pax_global_header00006660000000000000000000000064122536175540014524gustar00rootroot0000000000000052 comment=263f3a6e02c3bf263b0df97f921201d9dba35347 ZoneMinder-1.26.5/000077500000000000000000000000001225361755400136715ustar00rootroot00000000000000ZoneMinder-1.26.5/.gitignore000066400000000000000000000002211225361755400156540ustar00rootroot00000000000000configure config.h.in autom4te.cache aclocal.m4 depcomp install-sh missing mkinstalldirs stamp-h1 stamp-h.in scripts/ZoneMinder/blib Makefile.in ZoneMinder-1.26.5/.travis.yml000066400000000000000000000051731225361755400160100ustar00rootroot00000000000000language: cpp notifications: irc: "chat.freenode.net#zoneminder-dev" branches: except: - modern env: global: - LD_LIBRARY_PATH="/usr/local/lib:/opt/libjpeg-turbo/lib:$LD_LIBRARY_PATH" - DEB_HOST_GNU_TYPE=$(dpkg-architecture -qDEB_HOST_GNU_TYPE) - DEB_BUILD_GNU_TYPE=$(dpkg-architecture -qDEB_BUILD_GNU_TYPE) - CFLAGS="-DZM_FFMPEG_CVS -DHAVE_LIBCRYPTO -I/usr/local/include" - CXXFLAGS="$CFLAGS" matrix: - ZM_BUILDMETHOD=cmake - ZM_BUILDMETHOD=autotools compiler: - gcc before_install: - sudo apt-get update -qq - sudo apt-get install -y -qq zlib1g-dev apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm subversion automake autoconf cmake libjpeg-turbo8-dev apache2-mpm-prefork libapache2-mod-php5 php5-cli libtheora-dev libvorbis-dev libvpx-dev libx264-dev 2>&1 > /dev/null install: - git clone --depth=10 --branch=master git://source.ffmpeg.org/ffmpeg.git - cd ffmpeg - ./configure --enable-shared --enable-swscale --enable-gpl --enable-libx264 --enable-libvpx --enable-libvorbis --enable-libtheora - make -j `grep processor /proc/cpuinfo|wc -l` - sudo make install - sudo make install-libs before_script: - cd $TRAVIS_BUILD_DIR - if [ "$ZM_BUILDMETHOD" = "autotools" ]; then libtoolize --force; fi - if [ "$ZM_BUILDMETHOD" = "autotools" ]; then aclocal; fi - if [ "$ZM_BUILDMETHOD" = "autotools" ]; then autoheader; fi - if [ "$ZM_BUILDMETHOD" = "autotools" ]; then automake --force-missing --add-missing; fi - if [ "$ZM_BUILDMETHOD" = "autotools" ]; then autoconf; fi script: - if [ "$ZM_BUILDMETHOD" = "autotools" ]; then ./configure --prefix=/usr --with-libarch=lib/$DEB_HOST_GNU_TYPE --host=$DEB_HOST_GNU_TYPE --build=$DEB_BUILD_GNU_TYPE --with-mysql=/usr --with-ffmpeg=/usr --with-webdir=/usr/share/zoneminder/www --with-cgidir=/usr/libexec/zoneminder/cgi-bin --with-webuser=www-data --with-webgroup=www-data --enable-crashtrace=yes --disable-debug --enable-mmap=yes ZM_SSL_LIB=openssl; fi - if [ "$ZM_BUILDMETHOD" = "cmake" ]; then cmake -DCMAKE_INSTALL_PREFIX="/usr"; fi - make - sudo make install after_success: - if [ "$ZM_BUILDMETHOD" = "cmake" ]; then sudo ./zmlinkcontent.sh; fi - mysql -uroot -e "CREATE DATABASE IF NOT EXISTS zm" - mysql -uroot -e "GRANT ALL ON zm.* TO 'zmuser'@'localhost' IDENTIFIED BY 'zmpass'"; - mysql -uroot -e "FLUSH PRIVILEGES" - mysql -uzmuser -pzmpass < db/zm_create.sql - mysql -uzmuser -pzmpass zm < db/test.monitor.sql - sudo zmpkg.pl start ZoneMinder-1.26.5/AUTHORS000066400000000000000000000007271225361755400147470ustar00rootroot00000000000000ZoneMinder - A Linux based camera monitoring and analysis tool. This project was imagined and created by Philip Coombes in September 2002, shortly after being burglarised of his power tools. He can be contacted at philip.coombes@zoneminder.com In early 2013 after nearly two years of no development, the community reached out to Phil in an attempt to open up the project. With Phil's blessing, the code was migrated to github, and the community took over the project. ZoneMinder-1.26.5/BUGS000066400000000000000000000001061225361755400143510ustar00rootroot00000000000000Please see https://github.com/ZoneMinder/ZoneMinder/issues?state=open ZoneMinder-1.26.5/CMakeLists.txt000066400000000000000000000541541225361755400164420ustar00rootroot00000000000000# Main CMake file for the ZoneMinder project. # Created by mastertheknife (Kfir Itzhak) # The goal is to ease up the installation of zoneminder. # Our current installation method (using autotools) is outdated, slow and breaks now and then. # The CMake installation method will require no parameters at all, default should sufficient and reliable. # It will be still possible to install ZoneMinder using autotools, they don't conflict with each other. The cmake way is a complete re-write (different syntax) and aims to be identical to the autotools way, # by having options using the same name and leaving ZM totally unmodified, while providing exactly the same things that ZM expects (config.h, configuration in *.in files, etc). # # For more information and installation, see the INSTALL file # cmake_minimum_required (VERSION 2.6) project (zoneminder) set(zoneminder_VERSION "1.26.5") # CMake does not allow out-of-source build if CMakeCache.exists in the source folder. Abort and notify the user to save him from headache why it doesn't work. if((NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) AND (EXISTS "${CMAKE_SOURCE_DIR}/CMakeCache.txt")) message(FATAL_ERROR " You are attempting to do an out-of-source build, but a cmake cache file for an in-source build exists. Please delete the file CMakeCache.txt from the source folder to proceed.") endif((NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) AND (EXISTS "${CMAKE_SOURCE_DIR}/CMakeCache.txt")) # Default build type. To change the build type, use the CMAKE_BUILD_TYPE configuration option. if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type: Release or Debug" FORCE) endif(NOT CMAKE_BUILD_TYPE) # Can assist in troubleshooting #set(CMAKE_VERBOSE_MAKEFILE ON) #set(CMAKE_INSTALL_ALWAYS ON) # Default CLFAGS and CXXFLAGS: set(CMAKE_C_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2") set(CMAKE_CXX_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2") set(CMAKE_C_FLAGS_DEBUG "-Wall -D__STDC_CONSTANT_MACROS -g") set(CMAKE_CXX_FLAGS_DEBUG "-Wall -D__STDC_CONSTANT_MACROS -g") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") # Modules that we need: include (GNUInstallDirs) include (CheckIncludeFile) include (CheckIncludeFiles) include (CheckFunctionExists) include (CheckPrototypeDefinition_fixed) include (CheckTypeSize) include (CheckStructHasMember) # Configuration options mark_as_advanced(FORCE ZM_EXTRA_LIBS ZM_MYSQL_ENGINE ZM_NO_MMAP CMAKE_INSTALL_FULL_BINDIR ZM_PERL_SUBPREFIX ZM_PERL_USE_PATH ZM_TARGET_DISTRO) set(ZM_RUNDIR "/var/run/zm" CACHE PATH "Location of transient process files, default: /var/run/zm") set(ZM_TMPDIR "/tmp/zm" CACHE PATH "Location of temporary files, default: /tmp/zm") set(ZM_LOGDIR "/var/log/zm" CACHE PATH "Location of generated log files, default: /var/log/zm") set(ZM_WEBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/zoneminder/www" CACHE PATH "Location of the web files, default: /${CMAKE_INSTALL_DATADIR}/zoneminder/www") set(ZM_CGIDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH "Location of the cgi-bin files, default: /${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin") set(ZM_CONTENTDIR "/var/lib/zoneminder" CACHE PATH "Location of dynamic content (events and images), default: /var/lib/zoneminder") set(ZM_DB_HOST "localhost" CACHE STRING "Hostname where ZoneMinder database located, default: localhost") set(ZM_DB_NAME "zm" CACHE STRING "Name of ZoneMinder database, default: zm") set(ZM_DB_USER "zmuser" CACHE STRING "Name of ZoneMinder database user, default: zmuser") set(ZM_DB_PASS "zmpass" CACHE STRING "Password of ZoneMinder database user, default: zmpass") set(ZM_WEB_USER "" CACHE STRING "The user apache or the local web server runs on. Leave empty for automatic detection. If that fails, you can use this variable to force") set(ZM_WEB_GROUP "" CACHE STRING "The group apache or the local web server runs on, Leave empty to be the same as the web user") # Advanced set(ZM_EXTRA_LIBS "" CACHE STRING "A list of optional libraries, separated by semicolons, e.g. ssl;theora") set(ZM_MYSQL_ENGINE "InnoDB" CACHE STRING "MySQL engine to use with database, default: InnoDB") set(ZM_NO_MMAP "OFF" CACHE BOOL "Set to ON to not use mmap shared memory. Shouldn't be enabled unless you experience problems with the shared memory. default: OFF") set(ZM_NO_FFMPEG "OFF" CACHE BOOL "Set to ON to skip ffmpeg checks and force building ZM without ffmpeg. default: OFF") set(ZM_NO_X10 "OFF" CACHE BOOL "Set to ON to build ZoneMinder without X10 support. default: OFF") set(ZM_PERL_SUBPREFIX "${CMAKE_INSTALL_LIBDIR}/perl5" CACHE PATH "Use a different directory for the zm perl modules. NOTE: This is a subprefix, e.g. lib will be turned into /lib, default: /perl5") set(ZM_PERL_USE_PATH "${CMAKE_INSTALL_PREFIX}/${ZM_PERL_SUBPREFIX}" CACHE PATH "Override the include path for zm perl modules. Useful if you are moving the perl modules without using the ZM_PERL_SUBPREFIX option. default: /") set(ZM_TARGET_DISTRO "" CACHE STRING "Build ZoneMinder for a specific distribution. Currently, valid names are: f19, el6") # Required for certain checks to work set(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} stdio.h stdlib.h math.h signal.h) # Required for including headers from the this folder include_directories("${CMAKE_BINARY_DIR}") # This is required to enable searching in lib64 (if exists), do not change set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS ON) # System checks check_include_file("linux/videodev.h" HAVE_LINUX_VIDEODEV_H) check_include_file("linux/videodev2.h" HAVE_LINUX_VIDEODEV2_H) check_include_file("execinfo.h" HAVE_EXECINFO_H) check_include_file("ucontext.h" HAVE_UCONTEXT_H) check_include_file("sys/sendfile.h" HAVE_SYS_SENDFILE_H) check_include_file("sys/syscall.h" HAVE_SYS_SYSCALL_H) check_function_exists("syscall" HAVE_SYSCALL) check_function_exists("sendfile" HAVE_SENDFILE) check_function_exists("backtrace" HAVE_DECL_BACKTRACE) check_function_exists("backtrace_symbols" HAVE_DECL_BACKTRACE_SYMBOLS) check_function_exists("posix_memalign" HAVE_POSIX_MEMALIGN) check_type_size("siginfo_t" HAVE_SIGINFO_T) check_type_size("ucontext_t" HAVE_UCONTEXT_T) # *** LIBRARY CHECKS *** # zlib find_package(ZLIB) if(ZLIB_FOUND) set(HAVE_LIBZLIB 1) list(APPEND ZM_BIN_LIBS "${ZLIB_LIBRARIES}") include_directories("${ZLIB_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${ZLIB_INCLUDE_DIR}") check_include_file("zlib.h" HAVE_ZLIB_H) set(optlibsfound "${optlibsfound} zlib") else(ZLIB_FOUND) set(optlibsnotfound "${optlibsnotfound} zlib") endif(ZLIB_FOUND) # jpeg find_package(JPEG) if(JPEG_FOUND) set(HAVE_LIBJPEG 1) list(APPEND ZM_BIN_LIBS "${JPEG_LIBRARIES}") #link_directories(${JPEG_LIBRARY}) include_directories("${JPEG_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${JPEG_INCLUDE_DIR}") check_include_files("stdio.h;jpeglib.h" HAVE_JPEGLIB_H) if(NOT HAVE_JPEGLIB_H) message(FATAL_ERROR " zm requires libjpeg headers - check that libjpeg development packages are installed") endif(NOT HAVE_JPEGLIB_H) else(JPEG_FOUND) message(FATAL_ERROR "zm requires jpeg but it was not found on your system") endif(JPEG_FOUND) # OpenSSL find_package(OpenSSL) if(OPENSSL_FOUND) set(HAVE_LIBOPENSSL 1) set(HAVE_LIBCRYPTO 1) list(APPEND ZM_BIN_LIBS "${OPENSSL_LIBRARIES}") include_directories("${OPENSSL_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") check_include_file("openssl/md5.h" HAVE_OPENSSL_MD5_H) set(optlibsfound "${optlibsfound} OpenSSL") else(OPENSSL_FOUND) set(optlibsnotfound "${optlibsnotfound} OpenSSL") endif(OPENSSL_FOUND) # pthread (using find_library and find_path) find_library(PTHREAD_LIBRARIES pthread) if(PTHREAD_LIBRARIES) set(HAVE_LIBPTHREAD 1) list(APPEND ZM_BIN_LIBS "${PTHREAD_LIBRARIES}") find_path(PTHREAD_INCLUDE_DIR pthread.h) if(PTHREAD_INCLUDE_DIR) include_directories("${PTHREAD_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${PTHREAD_INCLUDE_DIR}") endif(PTHREAD_INCLUDE_DIR) mark_as_advanced(FORCE PTHREAD_LIBRARIES PTHREAD_INCLUDE_DIR) check_include_file("pthread.h" HAVE_PTHREAD_H) if(NOT HAVE_PTHREAD_H) message(FATAL_ERROR " zm requires pthread headers - check that pthread development packages are installed") endif(NOT HAVE_PTHREAD_H) else(PTHREAD_LIBRARIES) message(FATAL_ERROR "zm requires pthread but it was not found on your system") endif(PTHREAD_LIBRARIES) # pcre (using find_library and find_path) find_library(PCRE_LIBRARIES pcre) if(PCRE_LIBRARIES) set(HAVE_LIBPCRE 1) list(APPEND ZM_BIN_LIBS "${PCRE_LIBRARIES}") find_path(PCRE_INCLUDE_DIR pcre.h) if(PCRE_INCLUDE_DIR) include_directories("${PCRE_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${PCRE_INCLUDE_DIR}") endif(PCRE_INCLUDE_DIR) mark_as_advanced(FORCE PCRE_LIBRARIES PCRE_INCLUDE_DIR) check_include_file("pcre.h" HAVE_PCRE_H) set(optlibsfound "${optlibsfound} PCRE") else(PCRE_LIBRARIES) set(optlibsnotfound "${optlibsnotfound} PCRE") endif(PCRE_LIBRARIES) # gcrypt (using find_library and find_path) find_library(GCRYPT_LIBRARIES gcrypt) if(GCRYPT_LIBRARIES) set(HAVE_LIBGCRYPT 1) list(APPEND ZM_BIN_LIBS "${GCRYPT_LIBRARIES}") find_path(GCRYPT_INCLUDE_DIR gcrypt.h) if(GCRYPT_INCLUDE_DIR) include_directories("${GCRYPT_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${GCRYPT_INCLUDE_DIR}") endif(GCRYPT_INCLUDE_DIR) mark_as_advanced(FORCE GCRYPT_LIBRARIES GCRYPT_INCLUDE_DIR) check_include_file("gcrypt.h" HAVE_GCRYPT_H) set(optlibsfound "${optlibsfound} GCrypt") else(GCRYPT_LIBRARIES) set(optlibsnotfound "${optlibsnotfound} GCrypt") endif(GCRYPT_LIBRARIES) # gnutls (using find_library and find_path) 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/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) find_library(MYSQLCLIENT_LIBRARIES mysqlclient PATH_SUFFIXES mysql) if(MYSQLCLIENT_LIBRARIES) set(HAVE_LIBMYSQLCLIENT 1) list(APPEND ZM_BIN_LIBS "${MYSQLCLIENT_LIBRARIES}") find_path(MYSQLCLIENT_INCLUDE_DIR mysql/mysql.h) if(MYSQLCLIENT_INCLUDE_DIR) include_directories("${MYSQLCLIENT_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${MYSQLCLIENT_INCLUDE_DIR}") endif(MYSQLCLIENT_INCLUDE_DIR) mark_as_advanced(FORCE MYSQLCLIENT_LIBRARIES MYSQLCLIENT_INCLUDE_DIR) check_include_file("mysql/mysql.h" HAVE_MYSQL_H) if(NOT HAVE_MYSQL_H) message(FATAL_ERROR "zm requires MySQL headers - check that MySQL development packages are installed") endif(NOT HAVE_MYSQL_H) else(MYSQLCLIENT_LIBRARIES) message(FATAL_ERROR "zm requires mysqlclient but it was not found on your system") endif(MYSQLCLIENT_LIBRARIES) set(PATH_FFMPEG "") set(OPT_FFMPEG "no") # Do not check for ffmpeg if ZM_NO_FFMPEG is on if(NOT ZM_NO_FFMPEG) # avformat (using find_library and find_path) find_library(AVFORMAT_LIBRARIES avformat) if(AVFORMAT_LIBRARIES) set(HAVE_LIBAVFORMAT 1) list(APPEND ZM_BIN_LIBS "${AVFORMAT_LIBRARIES}") find_path(AVFORMAT_INCLUDE_DIR "libavformat/avformat.h") if(AVFORMAT_INCLUDE_DIR) include_directories("${AVFORMAT_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${AVFORMAT_INCLUDE_DIR}") endif(AVFORMAT_INCLUDE_DIR) mark_as_advanced(FORCE AVFORMAT_LIBRARIES AVFORMAT_INCLUDE_DIR) check_include_file("libavformat/avformat.h" HAVE_LIBAVFORMAT_AVFORMAT_H) set(optlibsfound "${optlibsfound} AVFormat") else(AVFORMAT_LIBRARIES) set(optlibsnotfound "${optlibsnotfound} AVFormat") endif(AVFORMAT_LIBRARIES) # avcodec (using find_library and find_path) find_library(AVCODEC_LIBRARIES avcodec) if(AVCODEC_LIBRARIES) set(HAVE_LIBAVCODEC 1) list(APPEND ZM_BIN_LIBS "${AVCODEC_LIBRARIES}") find_path(AVCODEC_INCLUDE_DIR "libavcodec/avcodec.h") if(AVCODEC_INCLUDE_DIR) include_directories("${AVCODEC_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${AVCODEC_INCLUDE_DIR}") endif(AVCODEC_INCLUDE_DIR) mark_as_advanced(FORCE AVCODEC_LIBRARIES AVCODEC_INCLUDE_DIR) check_include_file("libavcodec/avcodec.h" HAVE_LIBAVCODEC_AVCODEC_H) set(optlibsfound "${optlibsfound} AVCodec") else(AVCODEC_LIBRARIES) set(optlibsnotfound "${optlibsnotfound} AVCodec") endif(AVCODEC_LIBRARIES) # avdevice (using find_library and find_path) find_library(AVDEVICE_LIBRARIES avdevice) if(AVDEVICE_LIBRARIES) set(HAVE_LIBAVDEVICE 1) list(APPEND ZM_BIN_LIBS "${AVDEVICE_LIBRARIES}") find_path(AVDEVICE_INCLUDE_DIR "libavdevice/avdevice.h") if(AVDEVICE_INCLUDE_DIR) include_directories("${AVDEVICE_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${AVDEVICE_INCLUDE_DIR}") endif(AVDEVICE_INCLUDE_DIR) mark_as_advanced(FORCE AVDEVICE_LIBRARIES AVDEVICE_INCLUDE_DIR) check_include_file("libavdevice/avdevice.h" HAVE_LIBAVDEVICE_AVDEVICE_H) set(optlibsfound "${optlibsfound} AVDevice") else(AVDEVICE_LIBRARIES) set(optlibsnotfound "${optlibsnotfound} AVDevice") endif(AVDEVICE_LIBRARIES) # avutil (using find_library and find_path) find_library(AVUTIL_LIBRARIES avutil) if(AVUTIL_LIBRARIES) set(HAVE_LIBAVUTIL 1) list(APPEND ZM_BIN_LIBS "${AVUTIL_LIBRARIES}") find_path(AVUTIL_INCLUDE_DIR "libavutil/avutil.h") if(AVUTIL_INCLUDE_DIR) include_directories("${AVUTIL_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${AVUTIL_INCLUDE_DIR}") endif(AVUTIL_INCLUDE_DIR) mark_as_advanced(FORCE AVUTIL_LIBRARIES AVUTIL_INCLUDE_DIR) check_include_file("libavutil/avutil.h" HAVE_LIBAVUTIL_AVUTIL_H) check_include_file("libavutil/mathematics.h" HAVE_LIBAVUTIL_MATHEMATICS_H) set(optlibsfound "${optlibsfound} AVUtil") else(AVUTIL_LIBRARIES) set(optlibsnotfound "${optlibsnotfound} AVUtil") endif(AVUTIL_LIBRARIES) # swscale (using find_library and find_path) find_library(SWSCALE_LIBRARIES swscale) if(SWSCALE_LIBRARIES) set(HAVE_LIBSWSCALE 1) list(APPEND ZM_BIN_LIBS "${SWSCALE_LIBRARIES}") find_path(SWSCALE_INCLUDE_DIR "libswscale/swscale.h") if(SWSCALE_INCLUDE_DIR) include_directories("${SWSCALE_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${SWSCALE_INCLUDE_DIR}") endif(SWSCALE_INCLUDE_DIR) mark_as_advanced(FORCE SWSCALE_LIBRARIES SWSCALE_INCLUDE_DIR) check_include_file("libswscale/swscale.h" HAVE_LIBSWSCALE_SWSCALE_H) set(optlibsfound "${optlibsfound} SWScale") else(SWSCALE_LIBRARIES) set(optlibsnotfound "${optlibsnotfound} SWScale") endif(SWSCALE_LIBRARIES) # Find the path to the ffmpeg executable find_program(FFMPEG_EXECUTABLE ffmpeg PATH_SUFFIXES ffmpeg) if(FFMPEG_EXECUTABLE) set(PATH_FFMPEG "${FFMPEG_EXECUTABLE}") set(OPT_FFMPEG "yes") mark_as_advanced(FFMPEG_EXECUTABLE) endif(FFMPEG_EXECUTABLE) endif(NOT ZM_NO_FFMPEG) # *** END OF LIBRARY CHECKS *** # Check for gnutls or crypto if((NOT HAVE_LIBCRYPTO) AND (NOT HAVE_LIBGNUTLS)) message(FATAL_ERROR " zm requires crypto or gnutls but none were found on your system") endif((NOT HAVE_LIBCRYPTO) AND (NOT HAVE_LIBGNUTLS)) # Check for V4L header files and enable ZM_HAS_V4L, ZM_HAS_V4L1, ZM_HAS_V4L2 accordingly # Setting to zeros first is required because ZM uses #define for these set(ZM_HAS_V4L 0) set(ZM_HAS_V4L1 0) set(ZM_HAS_V4L2 0) if(HAVE_LINUX_VIDEODEV_H) set(ZM_HAS_V4L 1) set(ZM_HAS_V4L1 1) endif(HAVE_LINUX_VIDEODEV_H) if(HAVE_LINUX_VIDEODEV2_H) set(ZM_HAS_V4L 1) set(ZM_HAS_V4L2 1) endif(HAVE_LINUX_VIDEODEV2_H) if((NOT HAVE_LINUX_VIDEODEV_H) AND (NOT HAVE_LINUX_VIDEODEV2_H)) message(AUTHOR_WARNING " Video 4 Linux headers weren't found - Analog and USB camera support will not be available") endif((NOT HAVE_LINUX_VIDEODEV_H) AND (NOT HAVE_LINUX_VIDEODEV2_H)) # Check for PCRE and enable ZM_PCRE accordingly set(ZM_PCRE 0) if(HAVE_LIBPCRE AND HAVE_PCRE_H) set(ZM_PCRE 1) endif(HAVE_LIBPCRE AND HAVE_PCRE_H) # Check for mmap and enable in all components set(ZM_MEM_MAPPED 0) set(ENABLE_MMAP no) if(NOT ZM_NO_MMAP) set(ZM_MEM_MAPPED 1) set(ENABLE_MMAP yes) set(ZM_MMAP_PERLPACKAGE "Sys::Mmap") endif(NOT ZM_NO_MMAP) # Check for authenication functions if(HAVE_OPENSSL_MD5_H) set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}") set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") check_prototype_definition(MD5 "unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)" "NULL" "openssl/md5.h" HAVE_MD5_OPENSSL) 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) set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}") set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") check_prototype_definition(gnutls_fingerprint "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) endif(HAVE_GNUTLS_GNUTLS_H) if(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) set(HAVE_DECL_MD5 1) else(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) message(AUTHOR_WARNING " ZM requires a working MD5 function for hashed authenication but none were found - hashed authenication will not be available") endif(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) # 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 if(HAVE_LIBCRYPTO AND HAVE_OPENSSL_MD5_H AND HAVE_MD5_OPENSSL) set(HAVE_GCRYPT_H 0) set(HAVE_GNUTLS_OPENSSL_H 0) endif(HAVE_LIBCRYPTO AND HAVE_OPENSSL_MD5_H AND HAVE_MD5_OPENSSL) # Check for Perl find_package(Perl) if(NOT PERL_FOUND) message(FATAL_ERROR "zm requires Perl 5.6.0 or newer but it was not found on your system") endif(NOT PERL_FOUND) # Checking for perl modules requires FindPerlModules.cmake # Check all required modules at once # TODO: Add checking for the optional modules find_package(PerlModules COMPONENTS Sys::Syslog DBI DBD::mysql Getopt::Long Time::HiRes Date::Manip LWP::UserAgent ExtUtils::MakeMaker ${ZM_MMAP_PERLPACKAGE}) if(NOT PERLMODULES_FOUND) message(FATAL_ERROR "Not all required perl modules were found on your system") endif(NOT PERLMODULES_FOUND) # Attempt to check which user apache (or other web server) runs on by searching for a user beginning with apache or www and then cutting the user from the first matching user line if(ZM_WEB_USER STREQUAL "") # Check for a user matching ^apache and cut the username from the userline in the first match file(STRINGS "/etc/passwd" userline_apache REGEX "^apache") file(STRINGS "/etc/passwd" userline_www REGEX "^www") if(NOT (userline_apache STREQUAL "")) execute_process(COMMAND echo ${userline_apache} COMMAND cut -d: -f1 OUTPUT_VARIABLE ZM_WEB_USER OUTPUT_STRIP_TRAILING_WHITESPACE) elseif(NOT (userline_www STREQUAL "")) execute_process(COMMAND echo ${userline_www} COMMAND cut -d: -f1 OUTPUT_VARIABLE ZM_WEB_USER OUTPUT_STRIP_TRAILING_WHITESPACE) endif(NOT (userline_apache STREQUAL "")) message(STATUS "Detected web server user: ${ZM_WEB_USER}") endif(ZM_WEB_USER STREQUAL "") # Check if webgroup contains anything. If not, use the web user as the web group if(NOT ZM_WEB_GROUP) set(ZM_WEB_GROUP ${ZM_WEB_USER}) endif(NOT ZM_WEB_GROUP) message(STATUS "Using web user: ${ZM_WEB_USER}") message(STATUS "Using web group: ${ZM_WEB_GROUP}") # Some variables that zm expects set(ZM_PID "${ZM_RUNDIR}/zm.pid") set(ZM_CONFIG "/${CMAKE_INSTALL_SYSCONFDIR}/zm.conf") set(VERSION "${zoneminder_VERSION}") set(PKGDATADIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/zoneminder") set(BINDIR "${CMAKE_INSTALL_FULL_BINDIR}") set(LIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}") set(SYSCONFDIR "/${CMAKE_INSTALL_SYSCONFDIR}") set(WEB_PREFIX "${ZM_WEBDIR}") set(CGI_PREFIX "${ZM_CGIDIR}") set(WEB_USER "${ZM_WEB_USER}") set(WEB_GROUP "${ZM_WEB_GROUP}") set(ZM_DB_TYPE "mysql") set(EXTRA_PERL_LIB "use lib '${ZM_PERL_USE_PATH}';") # Reassign some variables if a target distro has been specified if((ZM_TARGET_DISTRO STREQUAL "f19") OR (ZM_TARGET_DISTRO STREQUAL "el6")) set(ZM_RUNDIR "/var/run/zoneminder") set(ZM_TMPDIR "/var/lib/zoneminder/temp") set(ZM_LOGDIR "/var/log/zoneminder") endif((ZM_TARGET_DISTRO STREQUAL "f19") OR (ZM_TARGET_DISTRO STREQUAL "el6")) # Generate files from the .in files configure_file(zm.conf.in "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" @ONLY) configure_file(zoneminder-config.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY) configure_file(zmconfgen.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmconfgen.pl" @ONLY) configure_file(zmlinkcontent.sh.in "${CMAKE_CURRENT_BINARY_DIR}/zmlinkcontent.sh" @ONLY) # Process subdirectories add_subdirectory(src) add_subdirectory(scripts) add_subdirectory(db) add_subdirectory(web) # Process misc subdirectories if(ZM_TARGET_DISTRO STREQUAL "f19") add_subdirectory(distros/fedora) elseif(ZM_TARGET_DISTRO STREQUAL "el6") add_subdirectory(distros/redhat) else(ZM_TARGET_DISTRO STREQUAL "el6") add_subdirectory(misc) endif(ZM_TARGET_DISTRO STREQUAL "f19") # Print optional libraries detection status message(STATUS "Optional libraries found:${optlibsfound}") message(STATUS "Optional libraries not found:${optlibsnotfound}") # Run ZM configuration generator message(STATUS "Running ZoneMinder configuration generator") execute_process(COMMAND perl ./zmconfgen.pl RESULT_VARIABLE zmconfgen_result) if(zmconfgen_result EQUAL 0) message(STATUS "ZoneMinder configuration generator completed successfully") else(zmconfgen_result EQUAL 0) message(FATAL_ERROR "ZoneMinder configuration generator failed. Exit code: ${zmconfgen_result}") endif(zmconfgen_result EQUAL 0) # Install zm.conf install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" DESTINATION "/${CMAKE_INSTALL_SYSCONFDIR}") # Uninstall target configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake) ZoneMinder-1.26.5/COPYING000066400000000000000000000431101225361755400147230ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ZoneMinder-1.26.5/ChangeLog000066400000000000000000000001331225361755400154400ustar00rootroot00000000000000This is too hard to maintain. See https://github.com/ZoneMinder/ZoneMinder/commits/master ZoneMinder-1.26.5/INSTALL000066400000000000000000000233151225361755400147260ustar00rootroot00000000000000Installing ZoneMinder with cmake -------------------------------- Starting with ZoneMinder 1.26.4, ZoneMinder can now be installed using cmake. This requires cmake version 2.6 or newer. cmake is an alternative to the autotools collection (libtool, autoconf, automake, autoheader and such). Its more recent and has many advantages, including, but not limited to: * One program (cmake) instead of multiple. (libtool, autoconf, automake, etc) * One file per directory (CMakeLists.txt) instead of multiple. (configure.ac, Makefile.am and sometimes more) * One syntax (cmake's syntax) instead of multiple. (bash and m4) * Generation of makefiles for many platforms, including Windows. * Newer than autotools and is being actively developed. * Generates colored makefiles with progress indicator. * Slightly faster because its based on C and not bash. * Lots of documentation, unlike autotools: http://www.cmake.org/cmake/help/cmake2.6docs.html At this point, its still possible to use autotools for the ZoneMinder project. Choosing cmake or autotools is now a matter of preference. Hopefully in the future, cmake will become the default way to install ZoneMinder. Important differences --------------------- * Unlike the autotools way, the cmake way does not require any options. It attempts to detect some things by its own (system directories, libarch, web user and group) and uses defaults for others (installation paths and such). * Unlike the autotools way, which links the binaries to a fixed list of libraries, the cmake way only links to libraries that it found on the system. If a library is not found, but required, a fatal error will be shown during the configuration step. * Unlike the autotools way, the cmake way does not modify the system in any way it shouldnt. It only does what its supposed to do: Install files to your system. Nothing else and nothing leaks out of the DESTDIR environment variable (if used). This means that depending on your configuration, there might be an extra required step after installation: to link WEB_PATH/events and WEB_PATH/images folders to the correct places. Configuration ------------- cmake by default does not require any parameters, but its possible to override the defaults with the options below. Configuration can be done in 4 ways: 1) As a command line parameter, e.g. cmake -DCMAKE_VERBOSE_MAKEFILE=ON . 2) Using cmake-gui 4) Providing cmake with an initial cache file with the -C option 4) By editing the cache file CMakeCache.txt (after it has been generated) - Not recommended Possible configuration options: ZM_RUNDIR Location of transient process files, default: /var/run/zm ZM_TMPDIR Location of temporary files, default: /tmp/zm ZM_LOGDIR Location of generated log files, default: /var/log/zm ZM_WEBDIR Location of the web files, default: /share/zoneminder/www ZM_CGIDIR Location of the cgi-bin files, default: /libexec/zoneminder/cgi-bin ZM_CONTENTDIR Location of dynamic content (events and images), default: /var/lib/zoneminder ZM_DB_HOST Hostname where ZoneMinder database located, default: localhost ZM_DB_NAME Name of ZoneMinder database, default: zm ZM_DB_USER Name of ZoneMinder database user, default: zmuser ZM_DB_PASS Password of ZoneMinder database user, default: zmpass ZM_WEB_USER The user apache or the local web server runs on. Leave empty for automatic detection. If that fails, you can use this variable to force ZM_WEB_GROUP The group apache or the local web server runs on, Leave empty to be the same as the web user Advanced: ZM_EXTRA_LIBS A list of optional libraries, separated by semicolons, e.g. ssl;theora ZM_MYSQL_ENGINE MySQL engine to use with database, default: InnoDB ZM_NO_MMAP Set to ON to not use mmap shared memory. Shouldn't be enabled unless you experience problems with the shared memory. default: OFF ZM_NO_FFMPEG Set to ON to skip ffmpeg checks and force building ZM without ffmpeg. default: OFF ZM_NO_X10 Set to ON to build ZoneMinder without X10 support. default: OFF ZM_PERL_SUBPREFIX Use a different directory for the zm perl modules. NOTE: This is a subprefix, e.g. lib will be turned into /lib, default: /perl5 ZM_PERL_USE_PATH Override the include path for zm perl modules. Useful if you are moving the perl modules without using the ZM_PERL_SUBPREFIX option. default: / Useful configuration options provided by cmake: CMAKE_VERBOSE_MAKEFILE - Set this to ON (default OFF) to see what cmake is doing. Very useful for troubleshooting. CMAKE_BUILD_TYPE - Set this to Debug (default Release) to build ZoneMinder with debugging enabled. CMAKE_INSTALL_PREFIX - Use this to change the prefix (default /usr/local). This option behaves like --prefix from autoconf. Package maintainers will probably want to set this to "/usr". Useful environment variables provided by cmake: CMAKE_INCLUDE_PATH - Use this to add to the include search path. CMAKE_LIBRARY_PATH - Use this to add to the library search path. CMAKE_PREFIX_PATH - Use this to add to both include and library search paths. /include will be added to the include search path and /lib to the library search path. Multiple paths can be specified, separated by a : character. For example: export CMAKE_PREFIX_PATH="/opt/libjpeg-turbo:/opt/ffmpeg-from-git" CFLAGS, CPPFLAGS and other environment variables: To append to the CFLAGS and CXXFLAGS, please use the CFLAGS and CXXFLAGS environment variables. Or use the CMAKE_C_FLAGS and CMAKE_CXX_FLAGS configuration options. To replace the CFLAGS and CXXFLAGS entirely: * For the Release build type: use CMAKE_C_FLAGS_RELEASE for the CFLAGS and CMAKE_CXX_FLAGS_RELEASE for the CXXFLAGS * For the Debug build type: use CMAKE_C_FLAGS_DEBUG for the CFLAGS and CMAKE_CXX_FLAGS_DEBUG for the CXXFLAGS Other important environment variables (such as LDFLAGS) are also supported. The DESTDIR environment variable is also supported, however it needs to be set before invoking make install. For example: DESTDIR=mydestdir make install For more information about DESTDIR, see: * http://www.gnu.org/prep/standards/html_node/DESTDIR.html Basic steps for installing ZoneMinder on a fresh system ------------------------------------------------------- 1) After installing all the required dependencies, in the project directory, run "cmake [extra options] ." This behaves like ./configure. It is also possible to supply configuration options, e.g. cmake -DZM_DB_PASS="mypass" . 2) Run "make" to compile ZoneMinder 3) Run "make install" (as root, or use sudo) to install ZoneMinder to your system. 4) Create a directory for the content and the necessary symlinks by running zmlinkcontent.sh with the directory you want to use. e.g. ./zmlinkcontent.sh /nfs/zm 5) Create a database for zoneminder, called "zm". 6) Create a user for the zoneminder database, called zmuser with password and full privileges to the "zm" database. NOTE: The database server, database name, user and password can be different and adjusted during configuration step with the options in this file, or by editing /etc/zm.conf 7) Populate the zoneminder database using the script zm_create.sql. This should be found in /share/zoneminder/db or in the project/db directory. 8) Create an apache virtual host for ZoneMinder. Make sure to use the same paths as ZM_WEBDIR and ZM_CGIDIR in /etc/zm.conf 9) Create other config if desired (e.g. rsyslog, logrotate and such). Some of this can be found in /share/zoneminder/misc or project/misc directory 10) Setup an init script for your system. Its also possible to use "zmpkg.pl start" and "zmpkg.pl stop" if you can't find a one. Basic steps for upgrading ZoneMinder ------------------------------------ 1) If you wish to use the same paths and configuration as the currently installed ZoneMinder, you need to provide cmake with options that match your current installation. You can provide those options in the command line to cmake, e.g. cmake -DZM_DB_PASS="blah" -DZM_WEBDIR="/usr/local/share/zoneminder/www" -DCMAKE_INSTALL_FULL_BINDIR="/usr/bin" . Or alternatively, for convenience, use the cmakecacheimport.sh script. This reads a zoneminder configuration file (zm.conf) and creates a cmake initial cache file called zm_conf.cmake, which you can then provide to cmake. For example: ./cmakecacheimport.sh /etc/zm.conf cmake -C zm_conf.cmake [extra options] . 2) Run "make" to compile ZoneMinder 3) Run "make install" (as root, or use sudo) to install ZoneMinder to your system. 4) Depending on your configuration: If the DIR_EVENTS and DIR_IMAGES options are set to default (pointing to web directory/events and web directory/images), You will need to update the symlinks in the web directory to the correct folders. e.g. web directory/events should point to the real events directory, and likewise for the images directory. You can use the zmlinkcontent.sh script for this. For example, if /var/lib/zoneminder is the folder that contains the "images" and "events" directories, you can use: ./zmlinkcontent.sh /var/lib/zoneminder By default, the content directory for new installations is /var/lib/zoneminder. This can be overridden in cmake with the ZM_CONTENTDIR option. e.g. cmake -DZM_CONTENTDIR="/some/big/storage/zm" . 5) Run zmupdate.pl to update the database layout to the new version. Uninstallation: --------------- By default, cmake does not have an uninstall target, however we have added a one. Simply run make uninstall (or DESTDIR=mydestdir make uninstall if a DESTDIR was used) and it will remove all the files that cmake installed. It's also possible to do this manually. The file install_manifest.txt contains the list of files installed to the system. This can be used in many ways to delete all files installed by cmake, such as: xargs rm < install_manifest.txt Contributions: -------------- Please visit our GitHub at http://github.com/ZoneMinder/ZoneMinder ZoneMinder-1.26.5/LICENSE000066400000000000000000000431101225361755400146750ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ZoneMinder-1.26.5/Makefile.am000066400000000000000000000023261225361755400157300ustar00rootroot00000000000000AUTOMAKE_OPTIONS = foreign # And these to the user and group of your webserver webuser = @WEB_USER@ webgroup = @WEB_GROUP@ sysconf_DATA = \ zm.conf SUBDIRS = \ src \ web \ scripts \ db \ misc EXTRA_DIST = \ zm.conf.in \ zmconfgen.pl.in # Yes, you are correct. This is a HACK! install-data-hook: ( cd $(DESTDIR)$(sysconfdir); chown $(webuser):$(webgroup) $(sysconf_DATA); chmod 600 $(sysconf_DATA) ) ( if ! test -e $(DESTDIR)$(ZM_RUNDIR); then mkdir -p $(DESTDIR)$(ZM_RUNDIR); fi; if test "$(DESTDIR)$(ZM_RUNDIR)" != "/var/run"; then chown $(webuser):$(webgroup) $(DESTDIR)$(ZM_RUNDIR); chmod u+w $(DESTDIR)$(ZM_RUNDIR); fi ) ( if ! test -e $(DESTDIR)$(ZM_TMPDIR); then mkdir -m 700 -p $(DESTDIR)$(ZM_TMPDIR); fi; if test "$(DESTDIR)$(ZM_TMPDIR)" != "/tmp"; then chown $(webuser):$(webgroup) $(DESTDIR)$(ZM_TMPDIR); chmod u+w $(DESTDIR)$(ZM_TMPDIR); fi ) uninstall-hook: @-( cd $(DESTDIR)$(webdir); rm -rf events graphics images sounds temp ) @-( if test "$(DESTDIR)$(ZM_RUNDIR)" != "/var/run"; then rm -rf $(DESTDIR)$(ZM_RUNDIR); fi ) @-( if test "$(DESTDIR)$(ZM_TMPDIR)" != "/tmp"; then rm -rf $(DESTDIR)$(ZM_TMPDIR); fi ) @-( if test "$(DESTDIR)$(ZM_LOGDIR)" != "/var/log"; then rm -rf $(DESTDIR)$(ZM_LOGDIR); fi ) ZoneMinder-1.26.5/NEWS000066400000000000000000000000301225361755400143610ustar00rootroot00000000000000Please see README file. ZoneMinder-1.26.5/README.md000066400000000000000000000143411225361755400151530ustar00rootroot00000000000000ZoneMinder ========== [![Build Status](https://travis-ci.org/ZoneMinder/ZoneMinder.png)](https://travis-ci.org/ZoneMinder/ZoneMinder) All documentation for ZoneMinder is now online at http://www.zoneminder.com/wiki/index.php/Documentation ## Overview ZoneMinder is an integrated set of applications which provide a complete surveillance solution allowing capture, analysis, recording and monitoring of any CCTV or security cameras attached to a Linux based machine. It is designed to run on distributions which support the Video For Linux (V4L) interface and has been tested with video cameras attached to BTTV cards, various USB cameras and also supports most IP network cameras. ## Requirements If you are installing ZoneMinder from a package, that package should provide all of the needed core components. ### Packages If you are compiling ZoneMinder from source, the below list contains the packages needed to get ZoneMinder built: #### Ubuntu A fresh build based on master branch running Ubuntu 1204 LTS. Will likely work for other versions as well. ```bash root@host:~# aptitude install -y apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm subversion automake autoconf libjpeg8-dev libjpeg8 apache2-mpm-prefork libapache2-mod-php5 php5-cli libphp-serialization-perl libgnutls-dev libjpeg8-dev libavcodec-dev libavformat-dev libswscale-dev libavutil-dev libv4l-dev libtool ffmpeg libnetpbm10-dev libavdevice-dev libmime-lite-perl dh-autoreconf dpatch; root@host:~# git clone https://github.com/ZoneMinder/ZoneMinder.git zoneminder; root@host:~# cd zoneminder; root@host:~# ln -s distros/ubuntu1204 debian; root@host:~# dpkg-checkbuilddeps; root@host:~# dpkg-buildpackage; ``` One level above you'll now find a deb package matching the architecture of the build host: ```bash root@host:~# ls -1 ~/zoneminder*; /root/zoneminder_1.26.4-1_amd64.changes /root/zoneminder_1.26.4-1_amd64.deb /root/zoneminder_1.26.4-1.dsc /root/zoneminder_1.26.4-1.tar.gz ``` The dpkg command itself does not resolve dependencies. That's what high-level interfaces like aptitude and apt-get are normally for. Unfortunately, unlike RPM, there's no easy way to install a separate deb package not contained with any repository. To overcome this "limitation" we'll use dpkg only to install the zoneminder package and apt-get to fetch all needed dependencies afterwards. Running dpkg-reconfigure in the end will ensure that the setup scripts e.g. for database provisioning were executed. ```bash root@host:~# dpkg -i /root/zoneminder_1.26.4-1_amd64.deb; apt-get install -f; root@host:~# dpkg-reconfigure zoneminder; ``` Alternatively you may also use gdebi to automatically resolve dependencies during installation: ```bash root@host:~# aptitude install -y gdebi; root@host:~# gdebi /root/zoneminder_1.26.4-1_amd64.deb; ``` ```bash sudo apt-get install apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev \ libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 \ libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm subversion automake autoconf libjpeg-turbo8-dev libjpeg-turbo8 \ apache2-mpm-prefork libapache2-mod-php5 php5-cli ``` #### Debian A fresh build based on master branch running Debian 7 (wheezy): ```bash root@host:~# aptitude install -y apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm subversion automake autoconf libjpeg8-dev libjpeg8 apache2-mpm-prefork libapache2-mod-php5 php5-cli libphp-serialization-perl libgnutls-dev libjpeg8-dev libavcodec-dev libavformat-dev libswscale-dev libavutil-dev libv4l-dev libtool ffmpeg libnetpbm10-dev libavdevice-dev libmime-lite-perl dh-autoreconf dpatch; root@host:~# git clone https://github.com/ZoneMinder/ZoneMinder.git zoneminder; root@host:~# cd zoneminder; root@host:~# ln -s distros/debian; root@host:~# dpkg-checkbuilddeps; root@host:~# dpkg-buildpackage; ``` One level above you'll now find a deb package matching the architecture of the build host: ```bash root@host:~# ls -1 ~/zoneminder*; /root/zoneminder_1.26.4-1_amd64.changes /root/zoneminder_1.26.4-1_amd64.deb /root/zoneminder_1.26.4-1.dsc /root/zoneminder_1.26.4-1.tar.gz ``` The dpkg command itself does not resolve dependencies. That's what high-level interfaces like aptitude and apt-get are normally for. Unfortunately, unlike RPM, there's no easy way to install a separate deb package not contained with any repository. To overcome this "limitation" we'll use dpkg only to install the zoneminder package and apt-get to fetch all needed dependencies afterwards. Running dpkg-reconfigure in the end will ensure that the setup scripts e.g. for database provisioning were executed. ```bash root@host:~# dpkg -i /root/zoneminder_1.26.4-1_amd64.deb; apt-get install -f; root@host:~# dpkg-reconfigure zoneminder; ``` Alternatively you may also use gdebi to automatically resolve dependencies during installation: ```bash root@host:~# aptitude install -y gdebi; root@host:~# gdebi /root/zoneminder_1.26.4-1_amd64.deb; ``` #### CentOS / RHEL Two additional repositories must be added before one can build zoneminder on CentOS or RHEL: 1. RepoForge (formerly RPMForge) http://repoforge.org/use/ 2. EPEL https://fedoraproject.org/wiki/EPEL Once those are added, install the following: ```bash sudo yum install automake bzip2-devel ffmpeg ffmpeg-devel gnutls-devel httpd libjpeg-turbo libjpeg-turbo-devel mysql-devel mysql-server pcre-devel \ perl-Archive-Tar perl-Archive-Zip perl-Convert-BinHex perl-Date-Manip perl-DBD-MySQL perl-DBI perl-Device-SerialPort perl-Email-Date-Format perl-IO-stringy \ perl-IO-Zlib perl-MailTools perl-MIME-Lite perl-MIME-tools perl-MIME-Types perl-Module-Load perl-Package-Constants perl-Sys-Mmap perl-Time-HiRes \ perl-TimeDate perl-YAML-Syck php php-cli php-mysql subversion x264 ``` ### ffmpeg This release of ZoneMinder has been tested on and works with ffmpeg version N-55540-g93f4277. ZoneMinder-1.26.5/TODO000066400000000000000000000000341225361755400143560ustar00rootroot00000000000000Please see README.md file. ZoneMinder-1.26.5/acinclude.m4000066400000000000000000000035671225361755400160750ustar00rootroot00000000000000AC_DEFUN([AC_DEFINE_DIR], [ prefix_NONE= exec_prefix_NONE= test "x$prefix" = xNONE && prefix_NONE=yes && prefix=$ac_default_prefix test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix=$prefix dnl In Autoconf 2.60, ${datadir} refers to ${datarootdir}, which in turn dnl refers to ${prefix}. Thus we have to use `eval' twice. eval ac_define_dir="\"[$]$2\"" eval ac_define_dir="\"$ac_define_dir\"" AC_SUBST($1, "$ac_define_dir") AC_DEFINE_UNQUOTED($1, "$ac_define_dir", [$3]) test "$prefix_NONE" && prefix=NONE test "$exec_prefix_NONE" && exec_prefix=NONE ]) AC_DEFUN([AC_PROG_PERL_VERSION],[dnl # Make sure we have perl if test -z "$PERL"; then AC_CHECK_PROG(PERL,perl,perl) fi # Check if version of Perl is sufficient ac_perl_version="$1" if test "x$PERL" != "x"; then AC_MSG_CHECKING(for perl version greater than or equal to $ac_perl_version) # NB: It would be nice to log the error if there is one, but we cannot rely # on autoconf internals $PERL -e "use $ac_perl_version;" > /dev/null 2>&1 if test $? -ne 0; then AC_MSG_RESULT(no); $3 else AC_MSG_RESULT(ok); $2 fi else AC_MSG_WARN(could not find perl) fi ])dnl AC_DEFUN([AC_PROG_PERL_MODULES],[dnl ac_perl_modules="$1" # Make sure we have perl if test -z "$PERL"; then AC_CHECK_PROG(PERL,perl,perl) fi if test "x$PERL" != x; then ac_perl_modules_failed=0 for ac_perl_module in $ac_perl_modules; do AC_MSG_CHECKING(for perl module $ac_perl_module) # Would be nice to log result here, but can't rely on autoconf internals $PERL "-M$ac_perl_module" -e exit > /dev/null 2>&1 if test $? -ne 0; then AC_MSG_RESULT(no); ac_perl_modules_failed=1 else AC_MSG_RESULT(ok); fi done # Run optional shell commands if test "$ac_perl_modules_failed" = 0; then : $2 else : $3 fi else AC_MSG_WARN(could not find perl) fi])dnl ZoneMinder-1.26.5/bootstrap.sh000077500000000000000000000000771225361755400162510ustar00rootroot00000000000000#!/bin/bash aclocal autoheader automake --add-missing autoconf ZoneMinder-1.26.5/cmake/000077500000000000000000000000001225361755400147515ustar00rootroot00000000000000ZoneMinder-1.26.5/cmake/Modules/000077500000000000000000000000001225361755400163615ustar00rootroot00000000000000ZoneMinder-1.26.5/cmake/Modules/CheckPrototypeDefinition.c.in000066400000000000000000000010021225361755400240770ustar00rootroot00000000000000@CHECK_PROTOTYPE_DEFINITION_HEADER@ static void cmakeRequireSymbol(int dummy, ...) { (void) dummy; } static void checkSymbol(void) { #ifndef @CHECK_PROTOTYPE_DEFINITION_SYMBOL@ cmakeRequireSymbol(0, &@CHECK_PROTOTYPE_DEFINITION_SYMBOL@); #endif } @CHECK_PROTOTYPE_DEFINITION_PROTO@ { return @CHECK_PROTOTYPE_DEFINITION_RETURN@; } #ifdef __CLASSIC_C__ int main() { int ac; char*av[]; #else int main(int ac, char *av[]) { #endif checkSymbol(); if (ac > 1000) { return *av[0]; } return 0; } ZoneMinder-1.26.5/cmake/Modules/CheckPrototypeDefinition.cmake000066400000000000000000000100541225361755400243370ustar00rootroot00000000000000# - Check if the protoype we expect is correct. # check_prototype_definition(FUNCTION PROTOTYPE RETURN HEADER VARIABLE) # FUNCTION - The name of the function (used to check if prototype exists) # PROTOTYPE- The prototype to check. # RETURN - The return value of the function. # HEADER - The header files required. # VARIABLE - The variable to store the result. # Example: # check_prototype_definition(getpwent_r # "struct passwd *getpwent_r(struct passwd *src, char *buf, int buflen)" # "NULL" # "unistd.h;pwd.h" # SOLARIS_GETPWENT_R) # The following variables may be set before calling this macro to # modify the way the check is run: # # CMAKE_REQUIRED_FLAGS = string of compile command line flags # CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) # CMAKE_REQUIRED_INCLUDES = list of include directories # CMAKE_REQUIRED_LIBRARIES = list of libraries to link #============================================================================= # Copyright 2005-2009 Kitware, Inc. # Copyright 2010-2011 Andreas Schneider # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) # get_filename_component(__check_proto_def_dir "${CMAKE_CURRENT_LIST_FILE}" PATH) function(CHECK_PROTOTYPE_DEFINITION _FUNCTION _PROTOTYPE _RETURN _HEADER _VARIABLE) if ("${_VARIABLE}" MATCHES "^${_VARIABLE}$") set(CHECK_PROTOTYPE_DEFINITION_CONTENT "/* */\n") set(CHECK_PROTOTYPE_DEFINITION_FLAGS ${CMAKE_REQUIRED_FLAGS}) if (CMAKE_REQUIRED_LIBRARIES) set(CHECK_PROTOTYPE_DEFINITION_LIBS LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) else() set(CHECK_PROTOTYPE_DEFINITION_LIBS) endif() if (CMAKE_REQUIRED_INCLUDES) set(CMAKE_SYMBOL_EXISTS_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") else() set(CMAKE_SYMBOL_EXISTS_INCLUDES) endif() foreach(_FILE ${_HEADER}) set(CHECK_PROTOTYPE_DEFINITION_HEADER "${CHECK_PROTOTYPE_DEFINITION_HEADER}#include <${_FILE}>\n") endforeach() set(CHECK_PROTOTYPE_DEFINITION_SYMBOL ${_FUNCTION}) set(CHECK_PROTOTYPE_DEFINITION_PROTO ${_PROTOTYPE}) set(CHECK_PROTOTYPE_DEFINITION_RETURN ${_RETURN}) configure_file("${__check_proto_def_dir}/CheckPrototypeDefinition.c.in" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c" @ONLY) file(READ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c _SOURCE) try_compile(${_VARIABLE} ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} ${CHECK_PROTOTYPE_DEFINITION_LIBS} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${CHECK_PROTOTYPE_DEFINITION_FLAGS} "${CMAKE_SYMBOL_EXISTS_INCLUDES}" OUTPUT_VARIABLE OUTPUT) if (${_VARIABLE}) set(${_VARIABLE} 1 CACHE INTERNAL "Have correct prototype for ${_FUNCTION}") message(STATUS "Checking prototype ${_FUNCTION} for ${_VARIABLE} - True") file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "Determining if the prototype ${_FUNCTION} exists for ${_VARIABLE} passed with the following output:\n" "${OUTPUT}\n\n") else () message(STATUS "Checking prototype ${_FUNCTION} for ${_VARIABLE} - False") set(${_VARIABLE} 0 CACHE INTERNAL "Have correct prototype for ${_FUNCTION}") file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "Determining if the prototype ${_FUNCTION} exists for ${_VARIABLE} failed with the following output:\n" "${OUTPUT}\n\n${_SOURCE}\n\n") endif () endif() endfunction() ZoneMinder-1.26.5/cmake/Modules/CheckPrototypeDefinition_fixed.cmake000066400000000000000000000100571225361755400255210ustar00rootroot00000000000000# - Check if the protoype we expect is correct. # check_prototype_definition(FUNCTION PROTOTYPE RETURN HEADER VARIABLE) # FUNCTION - The name of the function (used to check if prototype exists) # PROTOTYPE- The prototype to check. # RETURN - The return value of the function. # HEADER - The header files required. # VARIABLE - The variable to store the result. # Example: # check_prototype_definition(getpwent_r # "struct passwd *getpwent_r(struct passwd *src, char *buf, int buflen)" # "NULL" # "unistd.h;pwd.h" # SOLARIS_GETPWENT_R) # The following variables may be set before calling this macro to # modify the way the check is run: # # CMAKE_REQUIRED_FLAGS = string of compile command line flags # CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) # CMAKE_REQUIRED_INCLUDES = list of include directories # CMAKE_REQUIRED_LIBRARIES = list of libraries to link #============================================================================= # Copyright 2005-2009 Kitware, Inc. # Copyright 2010-2011 Andreas Schneider # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) # get_filename_component(__check_proto_def_dir "${CMAKE_CURRENT_LIST_FILE}" PATH) function(CHECK_PROTOTYPE_DEFINITION _FUNCTION _PROTOTYPE _RETURN _HEADER _VARIABLE) if ("${_VARIABLE}" MATCHES "^${_VARIABLE}$") set(CHECK_PROTOTYPE_DEFINITION_CONTENT "/* */\n") set(CHECK_PROTOTYPE_DEFINITION_FLAGS ${CMAKE_REQUIRED_FLAGS}) if (CMAKE_REQUIRED_LIBRARIES) set(CHECK_PROTOTYPE_DEFINITION_LIBS ${LINK_LIBRARIES} ${CMAKE_REQUIRED_LIBRARIES}) else() set(CHECK_PROTOTYPE_DEFINITION_LIBS) endif() if (CMAKE_REQUIRED_INCLUDES) set(CMAKE_SYMBOL_EXISTS_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") else() set(CMAKE_SYMBOL_EXISTS_INCLUDES) endif() foreach(_FILE ${_HEADER}) set(CHECK_PROTOTYPE_DEFINITION_HEADER "${CHECK_PROTOTYPE_DEFINITION_HEADER}#include <${_FILE}>\n") endforeach() set(CHECK_PROTOTYPE_DEFINITION_SYMBOL ${_FUNCTION}) set(CHECK_PROTOTYPE_DEFINITION_PROTO ${_PROTOTYPE}) set(CHECK_PROTOTYPE_DEFINITION_RETURN ${_RETURN}) configure_file("${__check_proto_def_dir}/CheckPrototypeDefinition.c.in" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c" @ONLY) file(READ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c _SOURCE) try_compile(${_VARIABLE} ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} ${CHECK_PROTOTYPE_DEFINITION_LIBS} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${CHECK_PROTOTYPE_DEFINITION_FLAGS} "${CMAKE_SYMBOL_EXISTS_INCLUDES}" OUTPUT_VARIABLE OUTPUT) if (${_VARIABLE}) set(${_VARIABLE} 1 CACHE INTERNAL "Have correct prototype for ${_FUNCTION}") message(STATUS "Checking prototype ${_FUNCTION} for ${_VARIABLE} - True") file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "Determining if the prototype ${_FUNCTION} exists for ${_VARIABLE} passed with the following output:\n" "${OUTPUT}\n\n") else () message(STATUS "Checking prototype ${_FUNCTION} for ${_VARIABLE} - False") set(${_VARIABLE} 0 CACHE INTERNAL "Have correct prototype for ${_FUNCTION}") file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "Determining if the prototype ${_FUNCTION} exists for ${_VARIABLE} failed with the following output:\n" "${OUTPUT}\n\n${_SOURCE}\n\n") endif () endif() endfunction() ZoneMinder-1.26.5/cmake/Modules/FindPerlModules.cmake000066400000000000000000000043221225361755400224200ustar00rootroot00000000000000# - try to find perl modules, passed as COMPONENTS # # Non-cache variable you might use in your CMakeLists.txt: # PERLMODULES_FOUND # # Requires these CMake modules: # FindPackageHandleStandardArgs (known included with CMake >=2.6.2) # # Original Author: # 2012 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright Iowa State University 2012. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) if(NOT PERL_FOUND) find_package(Perl QUIET) endif() set(_deps_check) if(PERL_FOUND) foreach(module ${PerlModules_FIND_COMPONENTS}) string(REPLACE "::" "/" modfilename "${module}.pm") string(REPLACE "::" "_" modvarname "PERLMODULES_${module}_MODULE") string(TOUPPER "${modvarname}" modvarname) list(APPEND _deps_check ${modvarname}) if(NOT ${modvarname}) if(NOT PerlModules_FIND_QUIETLY) message(STATUS "Checking for perl module ${module}") endif() execute_process(COMMAND "${PERL_EXECUTABLE}" "-e" "use ${module}; print \$INC{\"${modfilename}\"}" RESULT_VARIABLE result_code OUTPUT_VARIABLE filename ERROR_VARIABLE error_info OUTPUT_STRIP_TRAILING_WHITESPACE) if(result_code EQUAL 0) if(NOT PerlModules_FIND_QUIETLY) message(STATUS "Checking for perl module ${module} - found at ${filename}") endif() set(${modvarname} "${filename}" CACHE FILEPATH "Location found for module ${module}" FORCE) mark_as_advanced(${modvarname}) else() if(NOT PerlModules_FIND_QUIETLY) message(STATUS "Checking for perl module ${module} - failed") endif() set(${modvarname} "NOTFOUND" CACHE FILEPATH "No location found for module ${module}" FORCE) file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "Determining if the Perl module ${module} exists failed with the following error output:\n" "${error_info}\n\n") endif() endif() endforeach() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PerlModules DEFAULT_MSG PERL_FOUND ${_deps_check}) ZoneMinder-1.26.5/cmake/Modules/GNUInstallDirs.cmake000066400000000000000000000162661225361755400222000ustar00rootroot00000000000000# - Define GNU standard installation directories # Provides install directory variables as defined for GNU software: # http://www.gnu.org/prep/standards/html_node/Directory-Variables.html # Inclusion of this module defines the following variables: # CMAKE_INSTALL_ - destination for files of a given type # CMAKE_INSTALL_FULL_ - corresponding absolute path # where is one of: # BINDIR - user executables (bin) # SBINDIR - system admin executables (sbin) # LIBEXECDIR - program executables (libexec) # SYSCONFDIR - read-only single-machine data (etc) # SHAREDSTATEDIR - modifiable architecture-independent data (com) # LOCALSTATEDIR - modifiable single-machine data (var) # LIBDIR - object code libraries (lib or lib64 or lib/ on Debian) # INCLUDEDIR - C header files (include) # OLDINCLUDEDIR - C header files for non-gcc (/usr/include) # DATAROOTDIR - read-only architecture-independent data root (share) # DATADIR - read-only architecture-independent data (DATAROOTDIR) # INFODIR - info documentation (DATAROOTDIR/info) # LOCALEDIR - locale-dependent data (DATAROOTDIR/locale) # MANDIR - man documentation (DATAROOTDIR/man) # DOCDIR - documentation root (DATAROOTDIR/doc/PROJECT_NAME) # Each CMAKE_INSTALL_ value may be passed to the DESTINATION options of # install() commands for the corresponding file type. If the includer does # not define a value the above-shown default will be used and the value will # appear in the cache for editing by the user. # Each CMAKE_INSTALL_FULL_ value contains an absolute path constructed # from the corresponding destination by prepending (if necessary) the value # of CMAKE_INSTALL_PREFIX. #============================================================================= # Copyright 2011 Nikita Krupen'ko # Copyright 2011 Kitware, Inc. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) # Installation directories # if(NOT DEFINED CMAKE_INSTALL_BINDIR) set(CMAKE_INSTALL_BINDIR "bin" CACHE PATH "user executables (bin)") endif() if(NOT DEFINED CMAKE_INSTALL_SBINDIR) set(CMAKE_INSTALL_SBINDIR "sbin" CACHE PATH "system admin executables (sbin)") endif() if(NOT DEFINED CMAKE_INSTALL_LIBEXECDIR) set(CMAKE_INSTALL_LIBEXECDIR "libexec" CACHE PATH "program executables (libexec)") endif() if(NOT DEFINED CMAKE_INSTALL_SYSCONFDIR) set(CMAKE_INSTALL_SYSCONFDIR "etc" CACHE PATH "read-only single-machine data (etc)") endif() if(NOT DEFINED CMAKE_INSTALL_SHAREDSTATEDIR) set(CMAKE_INSTALL_SHAREDSTATEDIR "com" CACHE PATH "modifiable architecture-independent data (com)") endif() if(NOT DEFINED CMAKE_INSTALL_LOCALSTATEDIR) set(CMAKE_INSTALL_LOCALSTATEDIR "var" CACHE PATH "modifiable single-machine data (var)") endif() if(NOT DEFINED CMAKE_INSTALL_LIBDIR) set(_LIBDIR_DEFAULT "lib") # Override this default 'lib' with 'lib64' iff: # - we are on Linux system but NOT cross-compiling # - we are NOT on debian # - we are on a 64 bits system # reason is: amd64 ABI: http://www.x86-64.org/documentation/abi.pdf # For Debian with multiarch, use 'lib/${CMAKE_LIBRARY_ARCHITECTURE}' if # CMAKE_LIBRARY_ARCHITECTURE is set (which contains e.g. "i386-linux-gnu" # See http://wiki.debian.org/Multiarch if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT CMAKE_CROSSCOMPILING) if (EXISTS "/etc/debian_version") # is this a debian system ? if(CMAKE_LIBRARY_ARCHITECTURE) set(_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}") endif() else() # not debian, rely on CMAKE_SIZEOF_VOID_P: if(NOT DEFINED CMAKE_SIZEOF_VOID_P) message(AUTHOR_WARNING "Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. " "Please enable at least one language before including GNUInstallDirs.") else() if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") set(_LIBDIR_DEFAULT "lib64") endif() endif() endif() endif() set(CMAKE_INSTALL_LIBDIR "${_LIBDIR_DEFAULT}" CACHE PATH "object code libraries (${_LIBDIR_DEFAULT})") endif() if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR) set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "C header files (include)") endif() if(NOT DEFINED CMAKE_INSTALL_OLDINCLUDEDIR) set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "C header files for non-gcc (/usr/include)") endif() if(NOT DEFINED CMAKE_INSTALL_DATAROOTDIR) set(CMAKE_INSTALL_DATAROOTDIR "share" CACHE PATH "read-only architecture-independent data root (share)") endif() #----------------------------------------------------------------------------- # Values whose defaults are relative to DATAROOTDIR. Store empty values in # the cache and store the defaults in local variables if the cache values are # not set explicitly. This auto-updates the defaults as DATAROOTDIR changes. if(NOT CMAKE_INSTALL_DATADIR) set(CMAKE_INSTALL_DATADIR "" CACHE PATH "read-only architecture-independent data (DATAROOTDIR)") set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}") endif() if(NOT CMAKE_INSTALL_INFODIR) set(CMAKE_INSTALL_INFODIR "" CACHE PATH "info documentation (DATAROOTDIR/info)") set(CMAKE_INSTALL_INFODIR "${CMAKE_INSTALL_DATAROOTDIR}/info") endif() if(NOT CMAKE_INSTALL_LOCALEDIR) set(CMAKE_INSTALL_LOCALEDIR "" CACHE PATH "locale-dependent data (DATAROOTDIR/locale)") set(CMAKE_INSTALL_LOCALEDIR "${CMAKE_INSTALL_DATAROOTDIR}/locale") endif() if(NOT CMAKE_INSTALL_MANDIR) set(CMAKE_INSTALL_MANDIR "" CACHE PATH "man documentation (DATAROOTDIR/man)") set(CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_DATAROOTDIR}/man") endif() if(NOT CMAKE_INSTALL_DOCDIR) set(CMAKE_INSTALL_DOCDIR "" CACHE PATH "documentation root (DATAROOTDIR/doc/PROJECT_NAME)") set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}") endif() #----------------------------------------------------------------------------- mark_as_advanced( CMAKE_INSTALL_BINDIR CMAKE_INSTALL_SBINDIR CMAKE_INSTALL_LIBEXECDIR CMAKE_INSTALL_SYSCONFDIR CMAKE_INSTALL_SHAREDSTATEDIR CMAKE_INSTALL_LOCALSTATEDIR CMAKE_INSTALL_LIBDIR CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_OLDINCLUDEDIR CMAKE_INSTALL_DATAROOTDIR CMAKE_INSTALL_DATADIR CMAKE_INSTALL_INFODIR CMAKE_INSTALL_LOCALEDIR CMAKE_INSTALL_MANDIR CMAKE_INSTALL_DOCDIR ) # Result directories # foreach(dir BINDIR SBINDIR LIBEXECDIR SYSCONFDIR SHAREDSTATEDIR LOCALSTATEDIR LIBDIR INCLUDEDIR OLDINCLUDEDIR DATAROOTDIR DATADIR INFODIR LOCALEDIR MANDIR DOCDIR ) if(NOT IS_ABSOLUTE ${CMAKE_INSTALL_${dir}}) set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_${dir}}") else() set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_${dir}}") endif() endforeach() ZoneMinder-1.26.5/cmake/cmake_uninstall.cmake.in000066400000000000000000000020771225361755400215370ustar00rootroot00000000000000if(POLICY CMP0007) cmake_policy(SET CMP0007 OLD) endif(POLICY CMP0007) if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") list(REVERSE files) foreach (file ${files}) message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") if (EXISTS "$ENV{DESTDIR}${file}") execute_process( COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" OUTPUT_VARIABLE rm_out RESULT_VARIABLE rm_retval ) if(NOT ${rm_retval} EQUAL 0) message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") endif (NOT ${rm_retval} EQUAL 0) else (EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") endif (EXISTS "$ENV{DESTDIR}${file}") endforeach(file) ZoneMinder-1.26.5/cmakecacheimport.sh000077500000000000000000000054451225361755400175370ustar00rootroot00000000000000#!/bin/bash # The purpose of this file is to add entries from zm.conf to cmake's cache. echo "*** This bash script imports configuration from zm.conf into cmake initial cache file" echo "*** The file can be used with cmake like so: cmake -C zm_conf.cmake [extra cmake options] ." echo "*** Usage: ./cmakecacheimport.sh [PATH TO ZM.CONF]" echo "" # Check for too many prameters if [[ "$#" -gt "0" && "$#" -ne "1" ]]; then echo "Error: Too many parameters!" exit 50 fi # Check if zm.conf was supplied as an argument and that it exists if [ "$#" -eq "1" ]; then ZM_CONFIG="$1" if [ ! -f "$ZM_CONFIG" ]; then echo "The zoneminder configuration file $ZM_CONFIG does not exist!" exit 40 fi fi # Load zm.conf if [ -n "$ZM_CONFIG" ]; then echo "Using custom zm.conf $ZM_CONFIG" source "$ZM_CONFIG" elif [ -f "/etc/zm.conf" ]; then echo "Using system zm.conf" source "/etc/zm.conf" elif [ -f "zm.conf" ]; then echo "Using local zm.conf" source "zm.conf" else echo "Failed locating zoneminder configuration file (zm.conf)\nPlease specify the full path to the zoneminder configuration file" exit 45 fi # Create the file touch "zm_conf.cmake" if [ "$?" != "0" ]; then echo "Failed creating zm_conf.cmake in the current directory" exit 10 fi # Print some information #echo "Executables directory : $ZM_PATH_BIN" #echo "Libraries directory : $ZM_PATH_LIB" #echo "System config directory : $ZM_PATH_CONF" echo "Web directory : $ZM_PATH_WEB" echo "CGI directory : $ZM_PATH_CGI" echo "Web user : $ZM_WEB_USER" echo "Web group : $ZM_WEB_GROUP" echo "Database host : $ZM_DB_HOST" echo "Database name : $ZM_DB_NAME" echo "Database user : $ZM_DB_USER" echo "Database password : Not shown" CMPATH="CACHE PATH \"Imported by cmakecacheimport.sh\" FORCE" CMSTRING="CACHE STRING \"Imported by cmakecacheimport.sh\" FORCE" # Write echo "# This file was generated by cmakecacheimport.sh">zm_conf.cmake #echo "set(CMAKE_INSTALL_FULL_BINDIR \"$ZM_PATH_BIN\" $CMPATH)">>zm_conf.cmake #echo "set(CMAKE_INSTALL_FULL_LIBDIR \"$ZM_PATH_LIB\" $CMPATH)">>zm_conf.cmake #echo "set(CMAKE_INSTALL_FULL_SYSCONFDIR \"$ZM_PATH_CONF\" $CMPATH)">>zm_conf.cmake echo "set(ZM_WEBDIR \"$ZM_PATH_WEB\" $CMPATH)">>zm_conf.cmake echo "set(ZM_CGIDIR \"$ZM_PATH_CGI\" $CMPATH)">>zm_conf.cmake echo "set(ZM_WEB_USER \"$ZM_WEB_USER\" $CMSTRING)">>zm_conf.cmake echo "set(ZM_WEB_GROUP \"$ZM_WEB_GROUP\" $CMSTRING)">>zm_conf.cmake echo "set(ZM_DB_HOST \"$ZM_DB_HOST\" $CMSTRING)">>zm_conf.cmake echo "set(ZM_DB_NAME \"$ZM_DB_NAME\" $CMSTRING)">>zm_conf.cmake echo "set(ZM_DB_USER \"$ZM_DB_USER\" $CMSTRING)">>zm_conf.cmake echo "set(ZM_DB_PASS \"$ZM_DB_PASS\" $CMSTRING)">>zm_conf.cmake echo "" echo "Wrote zm_conf.cmake" echo "" echo "All done" ZoneMinder-1.26.5/configure.ac000066400000000000000000000453541225361755400161720ustar00rootroot00000000000000AC_PREREQ(2.59) AC_INIT(zm,1.26.5,[http://www.zoneminder.com/forums/ - Please check FAQ first],zoneminder,http://www.zoneminder.com/downloads.html) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR(src/zm.h) AC_CONFIG_HEADERS(config.h) AC_SUBST([AM_CXXFLAGS], [-D__STDC_CONSTANT_MACROS]) AC_SUBST(VERSION) AC_ARG_VAR(ZM_DB_HOST,[Hostname where ZoneMinder database located, default localhost]) AC_ARG_VAR(ZM_DB_NAME,[Name of ZoneMinder database, default zm]) AC_ARG_VAR(ZM_DB_USER,[Name of ZoneMinder database user, default zmuser]) AC_ARG_VAR(ZM_DB_PASS,[Password of ZoneMinder database user, default zmpass]) AC_ARG_VAR(ZM_SSL_LIB,[Library to use for ssl functions, default gnutls]) AC_ARG_VAR(ZM_MYSQL_ENGINE,[MySQL engine to use with database, default InnoDB]) AC_ARG_VAR(ZM_RUNDIR,[Location of transient process files, default /var/run/zm]) AC_ARG_VAR(ZM_TMPDIR,[Location of temporary files, default /tmp/zm]) AC_ARG_VAR(ZM_LOGDIR,[Location of generated log files, default /var/log/zm]) if test "$ZM_DB_TYPE" == ""; then AC_SUBST(ZM_DB_TYPE,[mysql]) fi if test "$ZM_DB_HOST" == ""; then AC_SUBST(ZM_DB_HOST,[localhost]) fi if test "$ZM_DB_NAME" == ""; then AC_SUBST(ZM_DB_NAME,[zm]) fi if test "$ZM_DB_USER" == ""; then AC_SUBST(ZM_DB_USER,[zmuser]) fi if test "$ZM_DB_PASS" == ""; then AC_SUBST(ZM_DB_PASS,[zmpass]) fi if test "$ZM_SSL_LIB" == ""; then AC_SUBST(ZM_SSL_LIB,gnutls) fi if test "$ZM_MYSQL_ENGINE" == ""; then AC_SUBST(ZM_MYSQL_ENGINE,InnoDB) fi if test "$ZM_RUNDIR" == ""; then AC_SUBST(ZM_RUNDIR,[/var/run/zm]) fi if test "$ZM_TMPDIR" == ""; then AC_SUBST(ZM_TMPDIR,[/tmp/zm]) fi if test "$ZM_LOGDIR" == ""; then AC_SUBST(ZM_LOGDIR,[/var/log/zm]) fi LIB_ARCH=lib AC_ARG_WITH(libarch, [ --with-libarch= architecture library path to use, default lib], [LIB_ARCH=$with_libarch], AC_MSG_WARN([You can call configure with the --with-libarch option. This tells configure where to find architecture specific libraries. The default of 'lib' is usually ok but 64 bit machines may require lib64. e.g. --with-libarch=lib or --with-libarch=lib64]) ) AC_SUBST(LIB_ARCH) MYSQL_PREFIX=/usr AC_ARG_WITH(mysql, [ --with-mysql= prefix of MySQL installation, default /usr], [MYSQL_PREFIX=$with_mysql], AC_MSG_WARN([You can call configure with the --with-mysql option. This tells configure where to find the MySql C library and headers if configure cannot locate them automatically. e.g. --with-mysql=/usr/local or --with-mysql=/usr]) ) AC_SUBST(MYSQL_PREFIX) MYSQL_LIBS="-L${MYSQL_PREFIX}/${LIB_ARCH}/mysql" MYSQL_CFLAGS="-I${MYSQL_PREFIX}/include" AC_SUBST(MYSQL_LIBS) AC_SUBST(MYSQL_CFLAGS) LDFLAGS="${MYSQL_LIBS} $LDFLAGS" FFMPEG_PREFIX=/usr AC_ARG_WITH(ffmpeg, [ --with-ffmpeg= prefix of ffmpeg root directory for libavcodec etc, default /usr], [FFMPEG_PREFIX=$with_ffmpeg], AC_MSG_WARN([You can call configure with the --with-ffmpeg option. This tells configure where to find the ffmpeg root directory within which are the libavcodec and libavformat files that can be used to build true MPEG streaming into ZoneMinder. Ensure that your copy of ffmpeg has installed libraries as well as binaries (use 'make installlib'). If you are using a local install of ffmpeg you may have to remove or rename a previous real installation as the headers and libraries from that will probably be picked up before your local copy. e.g. --with-ffmpeg=/usr/local]) ) AC_SUBST(FFMPEG_PREFIX) FFMPEG_LIBS="-L${FFMPEG_PREFIX}/${LIB_ARCH}" FFMPEG_CFLAGS="-I${FFMPEG_PREFIX}/include -D__STDC_CONSTANT_MACROS" AC_SUBST(FFMPEG_LIBS) AC_SUBST(FFMPEG_CFLAGS) LDFLAGS="${FFMPEG_LIBS} $LDFLAGS" CFLAGS="${FFMPEG_CFLAGS} $CFLAGS" CPPFLAGS="${FFMPEG_CFLAGS} $CPPFLAGS" EXTRA_LIBS= AC_ARG_WITH(extralibs, [ --with-extralibs="" string containing extra libraries to pass to link, default empty], [EXTRA_LIBS=$with_extralibs], AC_MSG_WARN([You can call configure with the --with-extralibs option. Ordinarily you will need to use this option only when your copy of ffmpeg has been built with support for additional formats and you would use this option to detail which additional libraries ffmpeg was built with so that it is able to link successfully with ZoneMinder. You will need to wrap this option in quotes if it contains any spaces. e.g. --with-extralibs="-lmp3lame"]) ) AC_SUBST(EXTRA_LIBS) LDFLAGS="$LDFLAGS ${EXTRA_LIBS}" AC_ARG_WITH(webdir, [ --with-webdir= prefix of web directory], [WEB_PREFIX=$with_webdir], AC_MSG_ERROR([You must call configure with the --with-webdir option. This tells configure where to install PHP and web files and scripts. e.g. --with-webdir=/var/www/html or --with-webdir=/www/vhtdocs/]) ) AC_SUBST(WEB_PREFIX) AC_ARG_WITH(cgidir, [ --with-cgidir= prefix of cgi directory], [CGI_PREFIX=$with_cgidir], AC_MSG_ERROR([You must call configure with the --with-cgidir option. This tells configure where to install cgi files and scripts. e.g. --with-cgidir=/var/www/cgi-bin or --with-webdir=/www/vhtdocs//cgi-bin]) ) AC_SUBST(CGI_PREFIX) WEB_USER=apache AC_ARG_WITH(webuser, [ --with-webuser= name of web user, default apache], [WEB_USER=$with_webuser], AC_MSG_WARN([You can call configure with the --with-webuser option. This tells configure what the user name of the web user is if it is not the default of 'apache'. e.g. --with-webuser=apache or --with-webuser=web]) ) AC_SUBST(WEB_USER) WEB_GROUP=apache AC_ARG_WITH(webgroup, [ --with-webgroup= name of web group, default apache], [WEB_GROUP=$with_webgroup], AC_MSG_WARN([You can call configure with the --with-webgroup option. This tells configure what the group name of the web group is if it is not the default of 'apache'. e.g. --with-webgroup=apache or --with-webgroup=web]) ) AC_SUBST(WEB_GROUP) WEB_HOST=zm.local AC_ARG_WITH(webhost, [ --with-webhost= name of web hostname, default zm.local], [WEB_HOST=$with_webhost], AC_MSG_WARN([You can call configure with the --with-webhost option. This tells configure what the host name is for name based virtual hosting. This is only used to populate the sample web/zmHttpd.conf file. e.g. --with-webhost=zm.localdomain]) ) AC_SUBST(WEB_HOST) ENABLE_DEBUG=yes AC_ARG_ENABLE(debug, [ --enable-debug= enable or disable debug, default enabled], [ENABLE_DEBUG=$enable_debug], AC_MSG_WARN([You can call configure with the --enable-debug= or --disable-debug option. This tells configure whether to compile ZoneMinder with debug included. Although debug is included by default it is not output unless explicitly switched on elsewhere. These checks may induce a small penalty on performance and if you are after squeezing the maximum possible performance out of ZoneMinder you may use this switch to prevent debug from being compiled in. e.g. --enable-debug=yes or --disable-debug]) ) if test "$ENABLE_DEBUG" != "yes"; then AC_DEFINE(ZM_DBG_OFF,1,"Whether debug is switched off and compiled out") fi ENABLE_MMAP=yes AC_ARG_ENABLE(mmap, [ --enable-mmap= enable or disabled mapped memory versus shared memory, default mapped], [ENABLE_MMAP=$enable_mmap], AC_MSG_WARN([You can call configure with the --enable-mmap= or --disable-mmap option. This tells configure whether to compile ZoneMinder with mmap support rather than IPC shared memory. This is a feature that uses memory mapped into files which all processes can share. Memory mapping requires less configuration and is more flexible than shared memory but may slow down your system unless the mapped files are configured to reside on a fast or RAM based filesystem which will normally be the case by default. e.g. --enable-mmap=yes or --disable-mmap]) ) if test "$ENABLE_MMAP" == "yes"; then AC_DEFINE(ZM_MEM_MAPPED,1,"Whether to use mapped rather than shared memory") else AC_DEFINE(ZM_MEM_MAPPED,0,"Whether to use mapped rather than shared memory") fi AC_SUBST(ENABLE_MMAP) # Compiler AC_LANG_CPLUSPLUS # Checks for programs. AC_PROG_CXX AC_PROG_CC AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_RANLIB AC_PROG_MAKE_SET # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL AC_C_CONST AC_TYPE_UID_T AC_C_INLINE AC_TYPE_MODE_T AC_TYPE_SIZE_T AC_HEADER_TIME AC_STRUCT_TM AC_TYPE_SIGNAL AC_CHECK_TYPES(siginfo_t,,,[#include ]) AC_CHECK_TYPES(ucontext_t,,,[#include ]) # Checks for library functions. AC_PROG_GCC_TRADITIONAL AC_FUNC_MALLOC AC_FUNC_MMAP AC_FUNC_SELECT_ARGTYPES AC_FUNC_STAT AC_FUNC_STRFTIME AC_FUNC_STRTOD AC_FUNC_VPRINTF AC_CHECK_FUNCS([gethostbyname gethostname gettimeofday memmove memset mkdir munmap posix_memalign putenv select sendfile socket sqrt strcasecmp strchr strcspn strerror strncasecmp strrchr strspn strstr strtol strtoull]) AC_CHECK_FUNCS([syscall sleep usleep ioctl ioctlsocket sigaction]) # Other programs AC_CHECK_PROG(OPT_FFMPEG,ffmpeg,yes,no) AC_PATH_PROG(PATH_FFMPEG,ffmpeg) AC_CHECK_PROG(OPT_NETPBM,pnmscale,yes,no) AC_PATH_PROG(PATH_NETPBM,pnmscale) if test "$OPT_NETPBM" == "yes"; then PATH_NETPBM=`dirname $PATH_NETPBM` fi # Checks for libraries. AC_CHECK_LIB(mysqlclient,mysql_init,,AC_MSG_ERROR(zm requires libmysqlclient.a)) AC_CHECK_LIB(jpeg,jpeg_start_compress,,AC_MSG_ERROR(zm requires libjpeg.a)) AC_CHECK_LIB(pthread,pthread_create,,AC_MSG_ERROR(zm requires libpthread.a)) AC_CHECK_LIB(dl,dlsym,,AC_MSG_ERROR(zm requires libdl.a)) if test "$ZM_SSL_LIB" == "openssl"; then AC_CHECK_HEADERS(openssl/md5.h,,AC_MSG_WARN(zm requires openssl/md5.h header to be installed for openssl),) AC_CHECK_LIB(crypto,MD5,,AC_MSG_WARN([libcrypto.a is required for authenticated streaming - use ZM_SSL_LIB option to select gnutls instead])) else AC_CHECK_HEADERS(gnutls/openssl.h,AC_SUBST(ZM_HAS_GNUTLS_OPENSSL,1),AC_SUBST(ZM_HAS_GNUTLS_OPENSSL,0),) AC_CHECK_HEADERS(gnutls/gnutls.h,AC_SUBST(ZM_HAS_GNUTLS,1),AC_SUBST(ZM_HAS_GNUTLS,0),) if test "$ZM_HAS_GNUTLS_OPENSSL" == "0" && test "$ZM_HAS_GNUTLS" == "0"; then AC_MSG_WARN(gnutls is required for authenticated streaming - use ZM_SSL_LIB option to select openssl instead) fi AC_CHECK_HEADERS(gcrypt.h,,AC_MSG_WARN(zm requires libgcrypt headers to be installed for gnutls),) AC_CHECK_LIB(gcrypt,gcry_check_version,,AC_MSG_WARN([libgcrypt.a is required for authenticated streaming - use ZM_SSL_LIB option to select openssl instead])) AC_CHECK_LIB(gnutls,gnutls_fingerprint,,AC_MSG_WARN([libgnutls.a is required for authenticated streaming - use ZM_SSL_LIB option to select openssl instead])) if test "$ZM_HAS_GNUTLS_OPENSSL" == "1"; then AC_CHECK_LIB(gnutls-openssl,MD5,,AC_MSG_WARN([libgnutls.a is required for authenticated streaming - use ZM_SSL_LIB option to select openssl instead])) fi fi AC_CHECK_LIB(pcre,pcre_compile,,AC_MSG_WARN(libpcre.a may be required for remote/network camera support)) AC_CHECK_LIB(z,zlibVersion) AC_CHECK_LIB(x264,x264_predict_16x16_init) AC_CHECK_LIB(avutil,av_malloc,,AC_MSG_WARN(libavutil.a may be required for MPEG streaming)) # Don't bother to warn about this one AC_CHECK_LIB(avcore,av_image_copy,,) AC_CHECK_LIB(avcodec,avcodec_version,,AC_MSG_WARN(libavcodec.a is required for MPEG streaming)) AC_CHECK_LIB(avformat,avformat_version,,AC_MSG_WARN(libavformat.a is required for MPEG streaming)) #AC_CHECK_LIB(avcodec,avcodec_open,,AC_MSG_WARN(libavcodec.a is required for MPEG streaming)) #AC_CHECK_LIB(avformat,av_new_stream,,AC_MSG_WARN(libavformat.a is required for MPEG streaming)) AC_CHECK_LIB(avdevice,avdevice_register_all,,AC_MSG_WARN(libavdevice.a may be required for MPEG streaming)) AC_CHECK_LIB(swscale,sws_scale,,,-lswscale) AC_CHECK_LIB(bz2,BZ2_bzCompress,,AC_MSG_WARN(zm requires libbz2.a for recent versions of ffmpeg)) AC_CHECK_LIB(z,compress,,) # Checks for header files. AC_FUNC_ALLOCA AC_HEADER_STDC AC_CHECK_HEADERS([fcntl.h limits.h memory.h stddef.h stdlib.h string.h strings.h sys/param.h sys/time.h syslog.h unistd.h values.h]) AC_CHECK_HEADERS([netdb.h netinet/in.h arpa/inet.h sys/ioctl.h sys/socket.h sys/un.h glob.h sys/sendfile.h]) AC_CHECK_HEADERS(execinfo.h,,,) AC_CHECK_HEADERS(ucontext.h,,,) AC_CHECK_HEADERS(sys/syscall.h,,,) AC_CHECK_HEADERS(pthread.h,,,) AC_CHECK_HEADERS(linux/videodev.h,AC_SUBST(ZM_HAS_V4L1,1),AC_SUBST(ZM_HAS_V4L1,0),) AC_CHECK_HEADERS(linux/videodev2.h,AC_SUBST(ZM_HAS_V4L2,1),AC_SUBST(ZM_HAS_V4L2,0),) if test "$ZM_HAS_V4L1" == "1" || test "$ZM_HAS_V4L2" == "1"; then AC_SUBST(ZM_HAS_V4L,1) else AC_SUBST(ZM_HAS_V4L,0) AC_MSG_WARN(zm requires Video4Linux or Video4Linux2 to be installed for analog or USB camera support) fi AC_CHECK_HEADERS(jpeglib.h,,AC_MSG_ERROR(zm requires libjpeg headers to be installed),) AC_CHECK_HEADERS(mysql/mysql.h,,AC_MSG_ERROR(zm requires MySQL headers - check that MySQL development packages are installed),) AC_LANG_PUSH([C]) AC_CHECK_HEADERS(libavutil/avutil.h,,,) AC_CHECK_HEADERS(libavcodec/avcodec.h,,,) AC_CHECK_HEADERS(libavformat/avformat.h,,,) AC_CHECK_HEADERS(libswscale/swscale.h,,,) AC_LANG_POP([C]) AC_CHECK_HEADERS(pcre/pcre.h,AC_SUBST(ZM_PCRE,"1"),,) AC_CHECK_HEADERS(pcre.h,AC_SUBST(ZM_PCRE,"1"),,) if test "$ENABLE_MMAP" == "yes"; then AC_CHECK_HEADERS(sys/mman.h,,,) AC_CHECK_HEADERS(fcntl.h,,,) else AC_CHECK_HEADERS(sys/ipc.h,,,) AC_CHECK_HEADERS(sys/shm.h,,,) fi AC_CHECK_HEADERS(zlib.h,,,) if test "$ZM_SSL_LIB" == "openssl"; then AC_CHECK_DECLS(MD5,,AC_MSG_ERROR([zm requires openssl/md5.h - use ZM_SSL_LIB option to select gnutls instead]),[#include #include ]) else if test "$ZM_HAS_GNUTLS_OPENSSL" == "1"; then AC_CHECK_DECLS(MD5,,AC_MSG_ERROR([zm requires gnutls/openssl.h - use ZM_SSL_LIB option to select openssl instead]),[#include #include ]) else AC_CHECK_DECLS(gnutls_fingerprint,,AC_MSG_ERROR([zm requires gnutls/gnutls.h - use ZM_SSL_LIB option to select openssl instead]),[#include #include ]) fi fi AC_CHECK_DECLS(backtrace,,,[#include ]) AC_CHECK_DECLS(backtrace_symbols,,,[#include ]) AC_SUBST(LDFLAGS) AC_PROG_PERL_VERSION(5.6.0) # Compulsory perl modules AC_PROG_PERL_MODULES(Sys::Syslog,,AC_MSG_ERROR(zm requires SYS:Syslog)) AC_PROG_PERL_MODULES(DBI,,AC_MSG_ERROR(zm requires DBI)) AC_PROG_PERL_MODULES(DBD::mysql,,AC_MSG_ERROR(zm requires DBD::mysql)) AC_PROG_PERL_MODULES(Getopt::Long,,AC_MSG_ERROR(zm requires Getopt::Long)) AC_PROG_PERL_MODULES(Time::HiRes,,AC_MSG_ERROR(zm requires Time::HiRes)) AC_PROG_PERL_MODULES(Date::Manip,,AC_MSG_ERROR(zm requires Date::Manip)) AC_PROG_PERL_MODULES(LWP::UserAgent,,AC_MSG_ERROR(zm requires LWP::UserAgent)) AC_PROG_PERL_MODULES(ExtUtils::MakeMaker,,AC_MSG_ERROR(zm requires ExtUtils::MakeMaker)) if test "$ENABLE_MMAP" == "yes"; then AC_PROG_PERL_MODULES(Sys::Mmap,,AC_MSG_ERROR(zm requires Sys::Mmap for mapped memory - set --enable-mmap=no to use IPC shared memory instead)) fi # Optional perl modules AC_PROG_PERL_MODULES(Module::Load,,AC_MSG_WARN(Module::Load is required for PTZ camera control)) AC_PROG_PERL_MODULES(Device::SerialPort,,AC_MSG_WARN(Device::SerialPort is required for RS232/RS485 PTZ camera control)) AC_PROG_PERL_MODULES(Net::FTP,,AC_MSG_WARN(Net::FTP is required for automatic event uploading using ftp)) AC_PROG_PERL_MODULES(Net::SFTP::Foreign,,AC_MSG_WARN(Net::SFTP::Foreign is required for automatic event uploading using sftp)) AC_PROG_PERL_MODULES(Expect,,AC_MSG_WARN(Expect is required for automatic event uploading using sftp)) AC_PROG_PERL_MODULES(Archive::Tar,,AC_MSG_WARN(Archive::Tar may be required for automatic event uploading)) AC_PROG_PERL_MODULES(Archive::Zip,,AC_MSG_WARN(Archive::Zip may be required for automatic event uploading)) AC_PROG_PERL_MODULES(Net::SMTP,,AC_MSG_WARN(Net::SMTP may be required for automatic event email notification)) AC_PROG_PERL_MODULES(MIME::Lite,,AC_MSG_WARN(MIME::Lite may be required for automatic event email notification)) AC_PROG_PERL_MODULES(MIME::Entity,,AC_MSG_WARN(MIME::Entity may be required for automatic event email notification)) AC_PROG_PERL_MODULES(X10::ActiveHome,,AC_MSG_WARN(X10::ActiveHome is required for X.10 support)) AC_DEFINE_DIR([BINDIR],[bindir],[Expanded binary directory]) AC_DEFINE_DIR([LIBDIR],[libdir],[Expanded library directory]) AC_DEFINE_DIR([DATADIR],[datadir],[Expanded data directory]) AC_SUBST(PKGDATADIR,"$DATADIR/$PACKAGE") AC_SUBST(ZM_PID,"$ZM_RUNDIR/zm.pid") AC_DEFINE_DIR([SYSCONFDIR],[sysconfdir],[Expanded configuration directory]) AC_SUBST(ZM_CONFIG,"$SYSCONFDIR/zm.conf") # Slight hack for non-standard perl install paths if test "$prefix" != "NONE"; then PERL_SITE_PREFIX=`perl -V:siteprefix | sed -e "s/.*='\(.*\)';/\1/"` PERL_SITE_LIB=`perl -V:installsitelib | sed -e "s/.*='\(.*\)';/\1/"` PERL_LIB_PATH=`echo $PERL_SITE_LIB | sed -e "s|^$PERL_SITE_PREFIX||"` EXTRA_PERL_LIB="use lib '$prefix$PERL_LIB_PATH'; # Include custom perl install path" PERL_MM_PARMS="PREFIX=$prefix" else EXTRA_PERL_LIB="# Include from system perl paths only" PERL_MM_PARMS= fi AC_SUBST(PERL_MM_PARMS) AC_SUBST(EXTRA_PERL_LIB) AC_CONFIG_FILES([Makefile zm.conf zmconfgen.pl db/Makefile db/zm_create.sql misc/Makefile misc/apache.conf misc/logrotate.conf misc/syslog.conf scripts/Makefile scripts/zm scripts/zmaudit.pl scripts/zmcontrol.pl scripts/zmdc.pl scripts/zmfilter.pl scripts/zmpkg.pl scripts/zmtrack.pl scripts/zmtrigger.pl scripts/zmupdate.pl scripts/zmvideo.pl scripts/zmwatch.pl scripts/zmx10.pl scripts/zmdbbackup scripts/zmdbrestore scripts/zmeventdump scripts/zmlogrotate.conf scripts/ZoneMinder/lib/ZoneMinder/Base.pm scripts/ZoneMinder/lib/ZoneMinder/Config.pm scripts/ZoneMinder/lib/ZoneMinder/Memory.pm scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm src/Makefile src/zm_config.h web/Makefile web/ajax/Makefile web/css/Makefile web/graphics/Makefile web/includes/Makefile web/includes/config.php web/js/Makefile web/lang/Makefile web/skins/Makefile web/skins/classic/Makefile web/skins/classic/ajax/Makefile web/skins/classic/css/Makefile web/skins/classic/graphics/Makefile web/skins/classic/includes/Makefile web/skins/classic/js/Makefile web/skins/classic/lang/Makefile web/skins/classic/views/Makefile web/skins/classic/views/css/Makefile web/skins/classic/views/js/Makefile web/skins/mobile/Makefile web/skins/mobile/ajax/Makefile web/skins/mobile/css/Makefile web/skins/mobile/graphics/Makefile web/skins/mobile/includes/Makefile web/skins/mobile/lang/Makefile web/skins/mobile/views/Makefile web/skins/mobile/views/css/Makefile web/tools/Makefile web/tools/mootools/Makefile web/views/Makefile web/skins/xml/Makefile web/skins/xml/views/Makefile web/skins/xml/includes/Makefile]) # Create the definitions for compilation and defaults for the database AC_CONFIG_COMMANDS([src/zm_config_defines.h],[perl ./zmconfgen.pl]) # Manually generate the perl Makefile maker AC_CONFIG_COMMANDS([scripts/ZoneMinder/Makefile],[(cd scripts/ZoneMinder; echo "perl Makefile.PL $PERL_MM_PARMS"; perl Makefile.PL $PERL_MM_PARMS)],[PERL_MM_PARMS=$PERL_MM_PARMS]) AC_OUTPUT ZoneMinder-1.26.5/db/000077500000000000000000000000001225361755400142565ustar00rootroot00000000000000ZoneMinder-1.26.5/db/CMakeLists.txt000066400000000000000000000010441225361755400170150ustar00rootroot00000000000000# CMakeLists.txt for the ZoneMinder database scripts # Create files from the .in files configure_file(zm_create.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" @ONLY) # Glob all database upgrade scripts file(GLOB dbfileslist RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "zm_update-*.sql") # Install the database upgrade scripts install(FILES ${dbfileslist} DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") # install zm_create.sql install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") ZoneMinder-1.26.5/db/Makefile.am000066400000000000000000000003421225361755400163110ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu zmdbdatadir = $(pkgdatadir)/db EXTRA_DIST = \ zm_create.sql.in \ $(dbupgrade_scripts) dist_zmdbdata_DATA = \ zm_create.sql \ $(dbupgrade_scripts) dbupgrade_scripts = $(wildcard zm_update-*.sql) ZoneMinder-1.26.5/db/test.monitor.sql000066400000000000000000000005341225361755400174460ustar00rootroot00000000000000INSERT INTO Monitors (Name, Type, Function, Enabled, Format, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation, Deinterlacing, Brightness, Contrast, Hue, Colour, RefBlendPerc) VALUES ('travis_test','Remote','Modect',1,255,'http','simple','50.79.143.149','21146','/mjpg/video.mjpg',1280,800,3,0,'0',0,-1,-1,-1,-1,12); ZoneMinder-1.26.5/db/zm_create.sql.in000066400000000000000000001204141225361755400173570ustar00rootroot00000000000000-- MySQL dump 10.13 Distrib 5.6.13, for Linux (i686) -- -- Host: localhost Database: @ZM_DB_NAME@ -- ------------------------------------------------------ -- Server version 5.6.13 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- -- Current Database: `@ZM_DB_NAME@` -- CREATE DATABASE /*!32312 IF NOT EXISTS*/ `@ZM_DB_NAME@`; USE `@ZM_DB_NAME@`; -- -- Table structure for table `Config` -- DROP TABLE IF EXISTS `Config`; CREATE TABLE `Config` ( `Id` smallint(5) unsigned NOT NULL default '0', `Name` varchar(32) NOT NULL default '', `Value` text NOT NULL, `Type` tinytext NOT NULL, `DefaultValue` text, `Hint` tinytext, `Pattern` tinytext, `Format` tinytext, `Prompt` tinytext, `Help` text, `Category` varchar(32) NOT NULL default '', `Readonly` tinyint(3) unsigned NOT NULL default '0', `Requires` text, PRIMARY KEY (`Name`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `ControlPresets` -- DROP TABLE IF EXISTS `ControlPresets`; CREATE TABLE `ControlPresets` ( `MonitorId` int(10) unsigned NOT NULL default '0', `Preset` int(10) unsigned NOT NULL default '0', `Label` varchar(64) NOT NULL default '', PRIMARY KEY (`MonitorId`,`Preset`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `Controls` -- DROP TABLE IF EXISTS `Controls`; CREATE TABLE `Controls` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', `Type` enum('Local','Remote','Ffmpeg') NOT NULL default 'Local', `Protocol` varchar(64) default NULL, `CanWake` tinyint(3) unsigned NOT NULL default '0', `CanSleep` tinyint(3) unsigned NOT NULL default '0', `CanReset` tinyint(3) unsigned NOT NULL default '0', `CanZoom` tinyint(3) unsigned NOT NULL default '0', `CanAutoZoom` tinyint(3) unsigned NOT NULL default '0', `CanZoomAbs` tinyint(3) unsigned NOT NULL default '0', `CanZoomRel` tinyint(3) unsigned NOT NULL default '0', `CanZoomCon` tinyint(3) unsigned NOT NULL default '0', `MinZoomRange` int(10) unsigned default NULL, `MaxZoomRange` int(10) unsigned default NULL, `MinZoomStep` int(10) unsigned default NULL, `MaxZoomStep` int(10) unsigned default NULL, `HasZoomSpeed` tinyint(3) unsigned NOT NULL default '0', `MinZoomSpeed` int(10) unsigned default NULL, `MaxZoomSpeed` int(10) unsigned default NULL, `CanFocus` tinyint(3) unsigned NOT NULL default '0', `CanAutoFocus` tinyint(3) unsigned NOT NULL default '0', `CanFocusAbs` tinyint(3) unsigned NOT NULL default '0', `CanFocusRel` tinyint(3) unsigned NOT NULL default '0', `CanFocusCon` tinyint(3) unsigned NOT NULL default '0', `MinFocusRange` int(10) unsigned default NULL, `MaxFocusRange` int(10) unsigned default NULL, `MinFocusStep` int(10) unsigned default NULL, `MaxFocusStep` int(10) unsigned default NULL, `HasFocusSpeed` tinyint(3) unsigned NOT NULL default '0', `MinFocusSpeed` int(10) unsigned default NULL, `MaxFocusSpeed` int(10) unsigned default NULL, `CanIris` tinyint(3) unsigned NOT NULL default '0', `CanAutoIris` tinyint(3) unsigned NOT NULL default '0', `CanIrisAbs` tinyint(3) unsigned NOT NULL default '0', `CanIrisRel` tinyint(3) unsigned NOT NULL default '0', `CanIrisCon` tinyint(3) unsigned NOT NULL default '0', `MinIrisRange` int(10) unsigned default NULL, `MaxIrisRange` int(10) unsigned default NULL, `MinIrisStep` int(10) unsigned default NULL, `MaxIrisStep` int(10) unsigned default NULL, `HasIrisSpeed` tinyint(3) unsigned NOT NULL default '0', `MinIrisSpeed` int(10) unsigned default NULL, `MaxIrisSpeed` int(10) unsigned default NULL, `CanGain` tinyint(3) unsigned NOT NULL default '0', `CanAutoGain` tinyint(3) unsigned NOT NULL default '0', `CanGainAbs` tinyint(3) unsigned NOT NULL default '0', `CanGainRel` tinyint(3) unsigned NOT NULL default '0', `CanGainCon` tinyint(3) unsigned NOT NULL default '0', `MinGainRange` int(10) unsigned default NULL, `MaxGainRange` int(10) unsigned default NULL, `MinGainStep` int(10) unsigned default NULL, `MaxGainStep` int(10) unsigned default NULL, `HasGainSpeed` tinyint(3) unsigned NOT NULL default '0', `MinGainSpeed` int(10) unsigned default NULL, `MaxGainSpeed` int(10) unsigned default NULL, `CanWhite` tinyint(3) unsigned NOT NULL default '0', `CanAutoWhite` tinyint(3) unsigned NOT NULL default '0', `CanWhiteAbs` tinyint(3) unsigned NOT NULL default '0', `CanWhiteRel` tinyint(3) unsigned NOT NULL default '0', `CanWhiteCon` tinyint(3) unsigned NOT NULL default '0', `MinWhiteRange` int(10) unsigned default NULL, `MaxWhiteRange` int(10) unsigned default NULL, `MinWhiteStep` int(10) unsigned default NULL, `MaxWhiteStep` int(10) unsigned default NULL, `HasWhiteSpeed` tinyint(3) unsigned NOT NULL default '0', `MinWhiteSpeed` int(10) unsigned default NULL, `MaxWhiteSpeed` int(10) unsigned default NULL, `HasPresets` tinyint(3) unsigned NOT NULL default '0', `NumPresets` tinyint(3) unsigned NOT NULL default '0', `HasHomePreset` tinyint(3) unsigned NOT NULL default '0', `CanSetPresets` tinyint(3) unsigned NOT NULL default '0', `CanMove` tinyint(3) unsigned NOT NULL default '0', `CanMoveDiag` tinyint(3) unsigned NOT NULL default '0', `CanMoveMap` tinyint(3) unsigned NOT NULL default '0', `CanMoveAbs` tinyint(3) unsigned NOT NULL default '0', `CanMoveRel` tinyint(3) unsigned NOT NULL default '0', `CanMoveCon` tinyint(3) unsigned NOT NULL default '0', `CanPan` tinyint(3) unsigned NOT NULL default '0', `MinPanRange` int(10) default NULL, `MaxPanRange` int(10) default NULL, `MinPanStep` int(10) default NULL, `MaxPanStep` int(10) default NULL, `HasPanSpeed` tinyint(3) unsigned NOT NULL default '0', `MinPanSpeed` int(10) default NULL, `MaxPanSpeed` int(10) default NULL, `HasTurboPan` tinyint(3) unsigned NOT NULL default '0', `TurboPanSpeed` int(10) default NULL, `CanTilt` tinyint(3) unsigned NOT NULL default '0', `MinTiltRange` int(10) default NULL, `MaxTiltRange` int(10) default NULL, `MinTiltStep` int(10) default NULL, `MaxTiltStep` int(10) default NULL, `HasTiltSpeed` tinyint(3) unsigned NOT NULL default '0', `MinTiltSpeed` int(10) default NULL, `MaxTiltSpeed` int(10) default NULL, `HasTurboTilt` tinyint(3) unsigned NOT NULL default '0', `TurboTiltSpeed` int(10) default NULL, `CanAutoScan` tinyint(3) unsigned NOT NULL default '0', `NumScanPaths` tinyint(3) unsigned NOT NULL default '0', PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `Devices` -- DROP TABLE IF EXISTS `Devices`; CREATE TABLE `Devices` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` tinytext NOT NULL, `Type` enum('X10') NOT NULL default 'X10', `KeyString` varchar(32) NOT NULL default '', PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `Events` -- DROP TABLE IF EXISTS `Events`; CREATE TABLE `Events` ( `Id` int(10) unsigned NOT NULL auto_increment, `MonitorId` int(10) unsigned NOT NULL default '0', `Name` varchar(64) NOT NULL default '', `Cause` varchar(32) NOT NULL default '', `StartTime` datetime default NULL, `EndTime` datetime default NULL, `Width` smallint(5) unsigned NOT NULL default '0', `Height` smallint(5) unsigned NOT NULL default '0', `Length` decimal(10,2) NOT NULL default '0.00', `Frames` int(10) unsigned default NULL, `AlarmFrames` int(10) unsigned default NULL, `TotScore` int(10) unsigned NOT NULL default '0', `AvgScore` smallint(5) unsigned default '0', `MaxScore` smallint(5) unsigned default '0', `Archived` tinyint(3) unsigned NOT NULL default '0', `Videoed` tinyint(3) unsigned NOT NULL default '0', `Uploaded` tinyint(3) unsigned NOT NULL default '0', `Emailed` tinyint(3) unsigned NOT NULL default '0', `Messaged` tinyint(3) unsigned NOT NULL default '0', `Executed` tinyint(3) unsigned NOT NULL default '0', `Notes` text, PRIMARY KEY (`Id`,`MonitorId`), KEY `MonitorId` (`MonitorId`), KEY `StartTime` (`StartTime`), KEY `Frames` (`Frames`), KEY `Archived` (`Archived`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `Filters` -- DROP TABLE IF EXISTS `Filters`; CREATE TABLE `Filters` ( `Name` varchar(64) NOT NULL default '', `Query` text NOT NULL, `AutoArchive` tinyint(3) unsigned NOT NULL default '0', `AutoVideo` tinyint(3) unsigned NOT NULL default '0', `AutoUpload` tinyint(3) unsigned NOT NULL default '0', `AutoEmail` tinyint(3) unsigned NOT NULL default '0', `AutoMessage` tinyint(3) unsigned NOT NULL default '0', `AutoExecute` tinyint(3) unsigned NOT NULL default '0', `AutoExecuteCmd` tinytext, `AutoDelete` tinyint(3) unsigned NOT NULL default '0', `Background` tinyint(1) unsigned NOT NULL default '0', PRIMARY KEY (`Name`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `Frames` -- DROP TABLE IF EXISTS `Frames`; CREATE TABLE `Frames` ( `EventId` int(10) unsigned NOT NULL default '0', `FrameId` int(10) unsigned NOT NULL default '0', `Type` enum('Normal','Bulk','Alarm') NOT NULL default 'Normal', `TimeStamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, `Delta` decimal(8,2) NOT NULL default '0.00', `Score` smallint(5) unsigned NOT NULL default '0', PRIMARY KEY (`EventId`,`FrameId`), KEY `Type` (`Type`), KEY `TimeStamp` (`TimeStamp`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `Groups` -- DROP TABLE IF EXISTS `Groups`; CREATE TABLE `Groups` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', `MonitorIds` tinytext NOT NULL, PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `Logs` -- CREATE TABLE `Logs` ( `TimeKey` decimal(16,6) NOT NULL, `Component` varchar(32) NOT NULL, `Pid` smallint(6) DEFAULT NULL, `Level` tinyint(3) NOT NULL, `Code` char(3) NOT NULL, `Message` text NOT NULL, `File` varchar(255) DEFAULT NULL, `Line` smallint(5) unsigned DEFAULT NULL, KEY `TimeKey` (`TimeKey`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `MonitorPresets` -- DROP TABLE IF EXISTS `MonitorPresets`; CREATE TABLE `MonitorPresets` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', `Type` enum('Local','Remote','File','Ffmpeg') NOT NULL default 'Local', `Device` tinytext, `Channel` tinytext, `Format` int(10) unsigned default NULL, `Protocol` varchar(16) default NULL, `Method` varchar(16) default NULL, `Host` varchar(64) default NULL, `Port` varchar(8) default NULL, `Path` varchar(255) default NULL, `SubPath` varchar(64) default NULL, `Width` smallint(5) unsigned default NULL, `Height` smallint(5) unsigned default NULL, `Palette` int(10) unsigned default NULL, `MaxFPS` decimal(5,2) default NULL, `Controllable` tinyint(3) unsigned NOT NULL default '0', `ControlId` varchar(16) default NULL, `ControlDevice` varchar(255) default NULL, `ControlAddress` varchar(255) default NULL, `DefaultRate` smallint(5) unsigned NOT NULL default '100', `DefaultScale` smallint(5) unsigned NOT NULL default '100', PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `Monitors` -- DROP TABLE IF EXISTS `Monitors`; CREATE TABLE `Monitors` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', `Type` enum('Local','Remote','File','Ffmpeg') NOT NULL default 'Local', `Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor', `Enabled` tinyint(3) unsigned NOT NULL default '1', `LinkedMonitors` varchar(255) NOT NULL default '', `Triggers` set('X10') NOT NULL default '', `Device` varchar(64) NOT NULL default '', `Channel` tinyint(3) unsigned NOT NULL default '0', `Format` int(10) unsigned NOT NULL default '0', `Protocol` varchar(16) NOT NULL default '', `Method` varchar(16) NOT NULL default '', `Host` varchar(64) NOT NULL default '', `Port` varchar(8) NOT NULL default '', `SubPath` varchar(64) NOT NULL default '', `Path` varchar(255) NOT NULL default '', `Width` smallint(5) unsigned NOT NULL default '0', `Height` smallint(5) unsigned NOT NULL default '0', `Colours` tinyint(3) unsigned NOT NULL default '1', `Palette` int(10) unsigned NOT NULL default '0', `Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0', `Deinterlacing` int(10) unsigned NOT NULL default '0', `Brightness` mediumint(7) NOT NULL default '-1', `Contrast` mediumint(7) NOT NULL default '-1', `Hue` mediumint(7) NOT NULL default '-1', `Colour` mediumint(7) NOT NULL default '-1', `EventPrefix` varchar(32) NOT NULL default 'Event-', `LabelFormat` varchar(64) NOT NULL default '%N - %y/%m/%d %H:%M:%S', `LabelX` smallint(5) unsigned NOT NULL default '0', `LabelY` smallint(5) unsigned NOT NULL default '0', `ImageBufferCount` smallint(5) unsigned NOT NULL default '100', `WarmupCount` smallint(5) unsigned NOT NULL default '25', `PreEventCount` smallint(5) unsigned NOT NULL default '10', `PostEventCount` smallint(5) unsigned NOT NULL default '10', `StreamReplayBuffer` int(10) unsigned NOT NULL default '1000', `AlarmFrameCount` smallint(5) unsigned NOT NULL default '1', `SectionLength` int(10) unsigned NOT NULL default '600', `FrameSkip` smallint(5) unsigned NOT NULL default '0', `MaxFPS` decimal(5,2) default NULL, `AlarmMaxFPS` decimal(5,2) default NULL, `FPSReportInterval` smallint(5) unsigned NOT NULL default '250', `RefBlendPerc` tinyint(3) unsigned NOT NULL default '6', `AlarmRefBlendPerc` tinyint(3) unsigned NOT NULL default '3', `Controllable` tinyint(3) unsigned NOT NULL default '0', `ControlId` int(10) unsigned NOT NULL default '0', `ControlDevice` varchar(255) default NULL, `ControlAddress` varchar(255) default NULL, `AutoStopTimeout` decimal(5,2) default NULL, `TrackMotion` tinyint(3) unsigned NOT NULL default '0', `TrackDelay` smallint(5) unsigned NOT NULL default '0', `ReturnLocation` tinyint(3) NOT NULL default '-1', `ReturnDelay` smallint(5) unsigned NOT NULL default '0', `DefaultView` enum('Events','Control') NOT NULL default 'Events', `DefaultRate` smallint(5) unsigned NOT NULL default '100', `DefaultScale` smallint(5) unsigned NOT NULL default '100', `SignalCheckColour` varchar(32) NOT NULL default '#0000BE', `WebColour` varchar(32) NOT NULL default 'red', `Sequence` smallint(5) unsigned default NULL, PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `States` -- DROP TABLE IF EXISTS `States`; CREATE TABLE `States` ( `Name` varchar(64) NOT NULL default '', `Definition` text NOT NULL, PRIMARY KEY (`Name`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `Stats` -- DROP TABLE IF EXISTS `Stats`; CREATE TABLE `Stats` ( `MonitorId` int(10) unsigned NOT NULL default '0', `ZoneId` int(10) unsigned NOT NULL default '0', `EventId` int(10) unsigned NOT NULL default '0', `FrameId` int(10) unsigned NOT NULL default '0', `PixelDiff` tinyint(3) unsigned NOT NULL default '0', `AlarmPixels` int(10) unsigned NOT NULL default '0', `FilterPixels` int(10) unsigned NOT NULL default '0', `BlobPixels` int(10) unsigned NOT NULL default '0', `Blobs` smallint(5) unsigned NOT NULL default '0', `MinBlobSize` int(10) unsigned NOT NULL default '0', `MaxBlobSize` int(10) unsigned NOT NULL default '0', `MinX` smallint(5) unsigned NOT NULL default '0', `MaxX` smallint(5) unsigned NOT NULL default '0', `MinY` smallint(5) unsigned NOT NULL default '0', `MaxY` smallint(5) unsigned NOT NULL default '0', `Score` smallint(5) unsigned NOT NULL default '0', KEY `EventId` (`EventId`), KEY `MonitorId` (`MonitorId`), KEY `ZoneId` (`ZoneId`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `TriggersX10` -- DROP TABLE IF EXISTS `TriggersX10`; CREATE TABLE `TriggersX10` ( `MonitorId` int(10) unsigned NOT NULL default '0', `Activation` varchar(32) default NULL, `AlarmInput` varchar(32) default NULL, `AlarmOutput` varchar(32) default NULL, PRIMARY KEY (`MonitorId`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `Users` -- DROP TABLE IF EXISTS `Users`; CREATE TABLE `Users` ( `Id` int(10) unsigned NOT NULL auto_increment, `Username` varchar(32) character set latin1 collate latin1_bin NOT NULL default '', `Password` varchar(64) NOT NULL default '', `Language` varchar(8) NOT NULL default '', `Enabled` tinyint(3) unsigned NOT NULL default '1', `Stream` enum('None','View') NOT NULL default 'None', `Events` enum('None','View','Edit') NOT NULL default 'None', `Control` enum('None','View','Edit') NOT NULL default 'None', `Monitors` enum('None','View','Edit') NOT NULL default 'None', `Devices` enum('None','View','Edit') NOT NULL default 'None', `System` enum('None','View','Edit') NOT NULL default 'None', `MaxBandwidth` varchar(16) NOT NULL default '', `MonitorIds` tinytext NOT NULL, PRIMARY KEY (`Id`), UNIQUE KEY `UC_Username` (`Username`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `ZonePresets` -- DROP TABLE IF EXISTS `ZonePresets`; CREATE TABLE `ZonePresets` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', `Type` enum('Active','Inclusive','Exclusive','Preclusive','Inactive') NOT NULL default 'Active', `Units` enum('Pixels','Percent') NOT NULL default 'Pixels', `CheckMethod` enum('AlarmedPixels','FilteredPixels','Blobs') NOT NULL default 'Blobs', `MinPixelThreshold` smallint(5) unsigned default NULL, `MaxPixelThreshold` smallint(5) unsigned default NULL, `MinAlarmPixels` int(10) unsigned default NULL, `MaxAlarmPixels` int(10) unsigned default NULL, `FilterX` tinyint(3) unsigned default NULL, `FilterY` tinyint(3) unsigned default NULL, `MinFilterPixels` int(10) unsigned default NULL, `MaxFilterPixels` int(10) unsigned default NULL, `MinBlobPixels` int(10) unsigned default NULL, `MaxBlobPixels` int(10) unsigned default NULL, `MinBlobs` smallint(5) unsigned default NULL, `MaxBlobs` smallint(5) unsigned default NULL, `OverloadFrames` smallint(5) unsigned NOT NULL default '0', PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `Zones` -- DROP TABLE IF EXISTS `Zones`; CREATE TABLE `Zones` ( `Id` int(10) unsigned NOT NULL auto_increment, `MonitorId` int(10) unsigned NOT NULL default '0', `Name` varchar(64) NOT NULL default '', `Type` enum('Active','Inclusive','Exclusive','Preclusive','Inactive') NOT NULL default 'Active', `Units` enum('Pixels','Percent') NOT NULL default 'Pixels', `NumCoords` tinyint(3) unsigned NOT NULL default '0', `Coords` tinytext NOT NULL, `Area` int(10) unsigned NOT NULL default '0', `AlarmRGB` int(10) unsigned default '0', `CheckMethod` enum('AlarmedPixels','FilteredPixels','Blobs') NOT NULL default 'Blobs', `MinPixelThreshold` smallint(5) unsigned default NULL, `MaxPixelThreshold` smallint(5) unsigned default NULL, `MinAlarmPixels` int(10) unsigned default NULL, `MaxAlarmPixels` int(10) unsigned default NULL, `FilterX` tinyint(3) unsigned default NULL, `FilterY` tinyint(3) unsigned default NULL, `MinFilterPixels` int(10) unsigned default NULL, `MaxFilterPixels` int(10) unsigned default NULL, `MinBlobPixels` int(10) unsigned default NULL, `MaxBlobPixels` int(10) unsigned default NULL, `MinBlobs` smallint(5) unsigned default NULL, `MaxBlobs` smallint(5) unsigned default NULL, `OverloadFrames` smallint(5) unsigned NOT NULL default '0', PRIMARY KEY (`Id`), KEY `MonitorId` (`MonitorId`) ) ENGINE=@ZM_MYSQL_ENGINE@; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; -- -- Initial data to be loaded into ZoneMinder database -- -- -- Create a default admin user. -- insert into Users VALUES (NULL,'admin',password('admin'),'',1,'View','Edit','Edit','Edit','Edit','Edit','',''); -- -- Add a sample filter to purge the oldest 5 events when the disk is 95% full, delete is disabled though -- insert into Filters values ('PurgeWhenFull','{"sort_field":"Id","terms":[{"val":0,"attr":"Archived","op":"="},{"cnj":"and","val":95,"attr":"DiskPercent","op":">="}],"limit":5,"sort_asc":1}',0,0,0,0,0,0,'',1,0); -- -- Add in some sample control protocol definitions -- insert into Controls values (1,'Pelco-D','Local','PelcoD',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); insert into Controls values (2,'Pelco-P','Local','PelcoP',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); insert into Controls values (3,'Sony VISCA','Local','Visca',1,1,0,1,0,0,0,1,0,16384,10,4000,1,1,6,1,1,1,0,1,0,1536,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,3,1,1,1,1,0,1,1,0,1,-15578,15578,100,10000,1,1,50,1,254,1,-7789,7789,100,5000,1,1,50,1,254,0,0); INSERT INTO Controls VALUES (4,'Axis API v2','Remote','AxisV2',0,0,0,1,0,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,1,1,1,1,1,0,1,0,1,-360,360,1,90,0,NULL,NULL,0,NULL,1,-360,360,1,90,0,NULL,NULL,0,NULL,0,0); insert into Controls values (5,'Panasonic IP','Remote','PanasonicIP',0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0); insert into Controls values (6,'Neu-Fusion NCS370','Remote','Ncs370',0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,24,1,0,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0); insert into Controls values (7,'AirLink SkyIPCam 7xx','Remote','SkyIPCam7xx',0,0,1,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,1,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0); insert into Controls values (8,'Pelco-D','Ffmpeg','PelcoD',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); insert into Controls values (9,'Pelco-P','Ffmpeg','PelcoP',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); INSERT INTO Controls VALUES (10,'Foscam FI8620','Ffmpeg','FI8620_Y2k',0,0,0,1,0,0,0,1,1,10,1,10,1,1,63,1,1,0,0,1,1,63,1,63,1,1,63,1,1,0,0,1,0,0,0,0,1,0,255,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,1,8,0,1,1,1,0,0,0,1,1,1,360,1,360,1,1,63,0,0,1,1,90,1,90,1,1,63,0,0,0,0); INSERT INTO Controls VALUES (11,'Foscam FI8608W','Ffmpeg','FI8608W_Y2k',1,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,1,8,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,128,0,0,1,0,0,0,0,1,1,128,0,0,0,0); INSERT INTO Controls VALUES (12,'Foscam FI9821W','Ffmpeg','FI9821W_Y2k',1,0,1,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,100,1,1,0,0,1,0,100,0,100,1,0,100,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,100,0,100,1,0,100,1,16,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0); INSERT INTO Controls VALUES (13,'Loftek Sentinel','Remote','LoftekSentinel',0,0,1,1,0,0,0,1,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,255,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,6,1,1,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); -- -- Add some monitor preset values -- INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, jpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, mpjpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, jpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg, B&W','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&color=0',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, mpjpeg, B&W','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&color=0',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,':',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, mpjpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,1,4,NULL,':',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, jpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,':',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,1,4,NULL,':',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,':',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, mpjpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,1,4,NULL,':',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, jpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,':',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,1,4,NULL,':',100,100); INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, unicast','Remote','rtsp','rtpUni',NULL,NULL,NULL,'',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, multicast','Remote','rtsp','rtpMulti',NULL,NULL,NULL,'',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP','Remote','rtsp','rtpRtsp',NULL,NULL,NULL,'',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP/HTTP','Remote',NULL,NULL,NULL,'rtsp','rtpRtspHttp','',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 640x480, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 640x480, jpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 640x480, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,':',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, jpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,':',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,1,5,NULL,':',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,':',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, jpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,':',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,1,5,NULL,':',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg, max 5 FPS','Remote','http','simple',NULL,NULL,NULL,'',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/GetData.cgi',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'VEO Observer, jpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Blue Net Video Server, jpeg','Remote','http','simple',NULL,NULL,NULL,'',80,'/cgi-bin/image.cgi?control=0&id=admin&passwd=admin',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,'ACTi IP, mpeg4, unicast','Remote',NULL,NULL,NULL,'rtsp','rtpUni','',7070,'','/track',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp:///axis-media/media.amp?videocodec=h264',NULL,NULL,NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Vivotek FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://:554/live.sdp',NULL,NULL,NULL,352,240,NULL,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Axis FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp:///axis-media/media.amp',NULL,NULL,NULL,640,480,NULL,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'ACTi TCM FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://admin:123456@:7070',NULL,NULL,NULL,320,240,NULL,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 320x240','Local','/dev/video','',255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 320x240, max 5 FPS','Local','/dev/video','',255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 640x480','Local','/dev/video','',255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 640x480, max 5 FPS','Local','/dev/video','',255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 320x240','Local','/dev/video','',45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 320x240, max 5 FPS','Local','/dev/video','',45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 640x480','Local','/dev/video','',45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 640x480, max 5 FPS','Local','/dev/video','',45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 320x240','Local','/dev/video','',0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 320x240, max 5 FPS','Local','/dev/video','',0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 640x480','Local','/dev/video','',0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 640x480, max 5 FPS','Local','/dev/video','',0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 320x240','Local','/dev/video','',1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 320x240, max 5 FPS','Local','/dev/video','',1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 640x480','Local','/dev/video','',1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 640x480, max 5 FPS','Local','/dev/video','',1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Remote ZoneMinder','Remote',NULL,NULL,NULL,'http','simple','',80,'/cgi-bin/nph-zms?mode=jpeg&monitor=&scale=100&maxfps=5&buffer=0',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Foscam FI8620 FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://:@:554/11',NULL,704,576,0,NULL,1,'10','','',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Foscam FI8608W FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://:@:554/11',NULL,640,480,0,NULL,1,'11','','',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Foscam FI9821W FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://:@:88/videoMain',NULL,1280,720,0,NULL,1,'12','','',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Loftek Sentinel PTZ, 640x480, mjpeg','Remote','http',0,0,NULL,NULL,'','80','/videostream.cgi?user=&pwd=&resolution=32&rate=11',NULL,640,480,4,NULL,1,'13','',':@',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Airlink 777W PTZ, 640x480, mjpeg','Remote','http',0,0,NULL,NULL,':@','80','/cgi/mjpg/mjpg.cgi',NULL,640,480,4,NULL,1,'7','',':@',100,100); -- -- Add some zone preset values -- INSERT INTO ZonePresets VALUES (1,'Fast, low sensitivity','Active','Percent','AlarmedPixels',60,NULL,20,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0); INSERT INTO ZonePresets VALUES (2,'Fast, medium sensitivity','Active','Percent','AlarmedPixels',40,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0); INSERT INTO ZonePresets VALUES (3,'Fast, high sensitivity','Active','Percent','AlarmedPixels',20,NULL,5,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0); INSERT INTO ZonePresets VALUES (4,'Best, low sensitivity','Active','Percent','Blobs',60,NULL,36,NULL,7,7,24,NULL,20,NULL,1,NULL,0); INSERT INTO ZonePresets VALUES (5,'Best, medium sensitivity','Active','Percent','Blobs',40,NULL,16,NULL,5,5,12,NULL,10,NULL,1,NULL,0); INSERT INTO ZonePresets VALUES (6,'Best, high sensitivity','Active','Percent','Blobs',20,NULL,8,NULL,3,3,6,NULL,5,NULL,1,NULL,0); -- -- Apply the initial configuration -- -- This section is autogenerated by zmconfgen.pl -- Do not edit this file as any changes will be overwritten -- ZoneMinder-1.26.5/db/zm_update-0.0.1.sql000066400000000000000000000062441225361755400174270ustar00rootroot00000000000000-- -- This updates a 0.0.1 database to 0.9.7 -- alter table Monitors modify Device tinyint unsigned NOT NULL default '0'; alter table Monitors modify Format tinyint unsigned NOT NULL; alter table Monitors drop column WarmUpCount; alter table Monitors drop column PreEventCount; alter table Monitors drop column PostEventCount; alter table Monitors modify LabelFormat varchar(32) not null default '%%s - %y/%m/%d %H:%M:%S'; alter table Monitors modify LabelX smallint unsigned not null; alter table Monitors modify LabelY smallint unsigned not null; alter table Monitors add column ImageBufferCount smallint unsigned NOT NULL default '100'; alter table Monitors add column WarmupCount smallint unsigned NOT NULL default '25'; alter table Monitors add column PreEventCount smallint unsigned NOT NULL default '10'; alter table Monitors add column PostEventCount smallint unsigned NOT NULL default '10'; alter table Monitors add column AlarmFrameCount smallint unsigned NOT NULL default '1'; alter table Monitors add column FPSReportInterval smallint unsigned NOT NULL default '250'; alter table Monitors add column RefBlendPerc tinyint unsigned NOT NULL default '10'; alter table Monitors add column X10Activation char(32); alter table Monitors add column X10AlarmInput char(32); alter table Monitors add column X10AlarmOutput char(32); alter table Monitors modify column Function enum('None','Passive','Active','X10') default 'Passive' not null; update Monitors set LabelFormat = '%%s - %y/%m/%d %H:%M:%S'; update Monitors set LabelX = 0; update Monitors set LabelY = Height-8; alter table Events add column TotScore int unsigned not null default 0 after AlarmFrames; update Events set TotScore = AlarmFrames * AvgScore where TotScore = 0; alter table Events modify column Archived tinyint unsigned not null default 0; alter table Events add column Uploaded tinyint unsigned not null default 0 after Archived; alter table Events add column LearnState char(1) default '' after Uploaded; CREATE TABLE Filters ( Id int(10) unsigned NOT NULL auto_increment, MonitorId int(10) unsigned NOT NULL default '0', Name varchar(64) NOT NULL default '', Query text NOT NULL, AutoDelete tinyint(4) unsigned NOT NULL default '0', AutoUpload tinyint(4) unsigned NOT NULL default '0', PRIMARY KEY (Id), UNIQUE KEY FilterIDX (MonitorId,Name) ) TYPE=MyISAM; CREATE TABLE Stats ( MonitorId int(10) unsigned NOT NULL default '0', ZoneId int(10) unsigned NOT NULL default '0', EventId int(10) unsigned NOT NULL default '0', FrameId int(10) unsigned NOT NULL default '0', AlarmPixels int(10) unsigned NOT NULL default '0', FilterPixels int(10) unsigned NOT NULL default '0', BlobPixels int(10) unsigned NOT NULL default '0', Blobs smallint(5) unsigned NOT NULL default '0', MinBlobSize smallint(5) unsigned NOT NULL default '0', MaxBlobSize smallint(5) unsigned NOT NULL default '0', MinX smallint(5) unsigned NOT NULL default '0', MaxX smallint(5) unsigned NOT NULL default '0', MinY smallint(5) unsigned NOT NULL default '0', MaxY smallint(5) unsigned NOT NULL default '0', Score smallint(5) unsigned NOT NULL default '0', KEY EventId (EventId), KEY MonitorId (MonitorId), KEY ZoneId (ZoneId) ) TYPE=MyISAM; -- Not a problem if this fails drop table Alarms; ZoneMinder-1.26.5/db/zm_update-0.9.10.sql000066400000000000000000000012211225361755400175060ustar00rootroot00000000000000-- -- This updates a 0.9.10 database to 0.9.11 -- alter table Monitors change column Colours Palette tinyint(3) unsigned NOT NULL default '1'; update Monitors set Palette = 1 where Palette = 8; update Monitors set Palette = 4 where Palette = 24; alter table Zones modify column Type enum('Active','Inclusive','Exclusive','Preclusive','Inactive') not null default 'Active'; alter table Filters add column AutoArchive tinyint unsigned not null default 0 after Query; -- These are optional, it just seemed a good time... optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-0.9.11.sql000066400000000000000000000005361225361755400175170ustar00rootroot00000000000000-- -- This updates a 0.9.11 database to 0.9.12 -- alter table Monitors add column Orientation enum('0','90','180','270') not null default '0' after Palette; -- These are optional, it just seemed a good time... optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-0.9.12.sql000066400000000000000000000016101225361755400175120ustar00rootroot00000000000000-- -- This updates a 0.9.12 database to 0.9.13 -- CREATE TABLE Users ( Id int(10) unsigned NOT NULL auto_increment, Username varchar(32) NOT NULL default '', Password varchar(32) NOT NULL default '', Enabled tinyint(3) unsigned NOT NULL default '1', Stream enum('None','View') NOT NULL default 'None', Events enum('None','View','Edit') NOT NULL default 'None', Monitors enum('None','View','Edit') NOT NULL default 'None', System enum('None','View','Edit') NOT NULL default 'None', MonitorIds tinytext, PRIMARY KEY (Id), UNIQUE KEY UC_Id (Id), UNIQUE KEY UC_Username (Username) ) TYPE=MyISAM; insert into Users values ('','admin',password('admin'),1,'View','Edit','Edit','Edit',NULL); -- -- These are optional, it just seemed a good time... optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-0.9.13.sql000066400000000000000000000012601225361755400175140ustar00rootroot00000000000000-- -- This updates a 0.9.13 database to 0.9.14 -- CREATE TABLE Config ( Id smallint(5) unsigned NOT NULL default '0', Name varchar(32) NOT NULL default '', Value text NOT NULL, Type tinytext NOT NULL, DefaultValue tinytext, Hint tinytext, Pattern tinytext, Format tinytext, Prompt tinytext, Help text, Category varchar(32) NOT NULL default '', Readonly tinyint(3) unsigned NOT NULL default '0', Requires text, PRIMARY KEY (Name), UNIQUE KEY UC_Name (Name) ) TYPE=MyISAM; -- -- These are optional, but we might as well optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-0.9.15.sql000066400000000000000000000041511225361755400175200ustar00rootroot00000000000000-- -- This updates a 0.9.15 database to 0.9.16 -- -- Make changes to Monitor table -- alter table Monitors change column Function OldFunction enum('None','Passive','Active','X10') NOT NULL default 'Passive'; alter table Monitors add column Function enum('None','Monitor','Modect','Record','Mocord') NOT NULL default 'Monitor'; alter table Monitors add column RunMode enum('Continuous','Triggered') NOT NULL default 'Continuous' after Function; alter table Monitors add column Triggers set('X10') NOT NULL after RunMode; alter table Monitors add column SectionLength int(10) unsigned not null default 600 after PostEventCount; alter table Monitors add column FrameSkip smallint unsigned not null default 0 after SectionLength; -- -- Update to reflect existing setup -- update Monitors set Function = 'Monitor' where OldFunction = 'Passive'; update Monitors set Function = 'Modect' where OldFunction = 'Active'; update Monitors set Function = 'Modect' where OldFunction = 'X10'; update Monitors set RunMode = 'Triggered' where OldFunction = 'X10'; update Monitors set Triggers = 'X10' where OldFunction = 'X10'; -- -- Create the X10 triggers table -- CREATE TABLE TriggersX10 ( MonitorId int(10) unsigned NOT NULL default '0', Activation varchar(32) default NULL, AlarmInput varchar(32) default NULL, AlarmOutput varchar(32) default NULL, PRIMARY KEY (MonitorId) ) TYPE=MyISAM; -- -- Update to reflect existing setup -- insert into TriggersX10 select Id, X10Activation, X10AlarmInput, X10AlarmOutput from Monitors where Function = 'X10'; -- -- Clean up temporary and unused columns -- alter table Monitors drop column OldFunction ; alter table Monitors drop column X10Activation ; alter table Monitors drop column X10AlarmInput ; alter table Monitors drop column X10AlarmOutput ; -- -- Table structure for table `States` -- CREATE TABLE States ( Name varchar(32) NOT NULL default '', Definition tinytext NOT NULL, PRIMARY KEY (Name) ) TYPE=MyISAM; -- -- These are optional, but we might as well -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-0.9.16.sql000066400000000000000000000005461225361755400175250ustar00rootroot00000000000000-- -- This updates a 0.9.16 database to 0.9.17 -- -- Make changes to Users table -- alter table Users add column Language varchar(8) not null default "" after Password; -- -- These are optional, but we might as well -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-0.9.7.sql000066400000000000000000000010461225361755400174410ustar00rootroot00000000000000-- -- This updates a 0.9.7 database to 0.9.8 -- alter table Filters modify column AutoDelete tinyint unsigned not null default 0; alter table Filters modify column AutoUpload tinyint unsigned not null default 0; alter table Filters add column AutoEmail tinyint unsigned not null default 0; alter table Filters add column AutoMessage tinyint unsigned not null default 0; alter table Events add column Emailed tinyint unsigned not null default 0 after Uploaded; alter table Events add column Messaged tinyint unsigned not null default 0 after Emailed; ZoneMinder-1.26.5/db/zm_update-0.9.8.sql000066400000000000000000000004641225361755400174450ustar00rootroot00000000000000-- -- This updates a 0.9.8 database to 0.9.9 -- update Monitors set Colours = Colours * 8; optimize table Events; alter table Events modify column Length numeric( 10, 2 ) not null default 0.00; optimize table Frames; alter table Frames add column Delta numeric( 8, 2 ) not null default 0.00 after TimeStamp; ZoneMinder-1.26.5/db/zm_update-0.9.9.sql000066400000000000000000000012211225361755400174360ustar00rootroot00000000000000-- -- This updates a 0.9.9 database to 0.9.10 -- alter table Monitors add column Type enum('Local','Remote') NOT NULL default 'Local' after Name; alter table Monitors add column Host varchar(64) default NULL after Format; alter table Monitors add column Port varchar(8) default '80' after Host; alter table Monitors add column Path varchar(255) default NULL after Port; alter table Monitors add column MaxFPS decimal( 5, 2) not null default 0.0 after AlarmFrameCount; alter table Monitors drop column AlarmFrameCount; optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.17.1.sql000066400000000000000000000007511225361755400175150ustar00rootroot00000000000000-- -- This updates a 1.17.1 database to 1.17.2 -- -- Make changes to Zones table -- alter table Zones change column AlarmThreshold MinPixelThreshold smallint unsigned; alter table Zones add column MaxPixelThreshold smallint unsigned after MinPixelThreshold; alter table Events drop column ImagePath; -- -- These are optional, but we might as well -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.17.2.sql000066400000000000000000000011141225361755400175100ustar00rootroot00000000000000-- -- This updates a 1.17.2 database to 1.18.0 -- -- Make changes to Filter table -- alter table Zones add column CheckMethod enum('AlarmedPixels','FilteredPixels','Blobs') NOT NULL default 'Blobs' after AlarmRGB; alter table Filters drop index FilterIDX; alter table Filters drop column MonitorId; update Filters set AutoArchive = 0, AutoDelete = 0, AutoUpload = 0, AutoEmail = 0, AutoMessage = 0; -- -- These are optional, but we might as well -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.18.0.sql000066400000000000000000000000771225361755400175160ustar00rootroot00000000000000-- -- There are no updates from a 1.18.0 database to 1.18.1 -- ZoneMinder-1.26.5/db/zm_update-1.18.1.sql000066400000000000000000000012051225361755400175110ustar00rootroot00000000000000-- -- This updates a 1.18.1 database to 1.19.0 -- -- Make changes to Frames table -- alter table Frames add column Type enum('Normal','Bulk','Alarm') NOT NULL default 'Normal' after FrameId; update Frames set Type = 'Alarm' where AlarmFrame = 1; alter table Frames drop column AlarmFrame; -- -- Make changes to Filters table update Filters set Name = concat( Name, '_', Id ); alter table Filters drop column Id; alter table Filters add primary key(Name); -- -- These are optional, but we might as well -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.19.0.sql000066400000000000000000000000771225361755400175170ustar00rootroot00000000000000-- -- There are no updates from a 1.19.0 database to 1.19.1 -- ZoneMinder-1.26.5/db/zm_update-1.19.1.sql000066400000000000000000000014131225361755400175130ustar00rootroot00000000000000-- -- This updates a 1.19.1 database to 1.19.2 -- -- Make changes to Events table -- alter table Events add column Executed tinyint(3) unsigned not null default 0 after Messaged; -- -- Make changes to Filters table -- alter table Filters add column AutoExecute tinytext default ''; -- -- Add in a sample filter to purge the oldest 5 events when the disk is 99% full, delete is disabled though -- insert into Filters values ('PurgeWhenFull','trms=2&obr1=&cbr1=&attr1=Archived&op1=&val1=0&cnj2=and&obr2=&cbr2=&attr2=DiskPercent&op2=>=&val2=99&sort_field=Id&sort_asc=1&limit=5',0,0,0,0,0,''); -- -- These are optional, but we might as well -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.19.2.sql000066400000000000000000000005331225361755400175160ustar00rootroot00000000000000-- -- This updates a 1.19.2 database to 1.19.3 -- -- Make changes to Users table -- alter table Users modify column Password varchar(64) not null default ''; -- -- These are optional, but we might as well -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.19.3.sql000066400000000000000000000000771225361755400175220ustar00rootroot00000000000000-- -- There are no updates from a 1.19.3 database to 1.19.4 -- ZoneMinder-1.26.5/db/zm_update-1.19.4.sql000066400000000000000000000015421225361755400175210ustar00rootroot00000000000000-- -- This updates a 1.19.4 database to 1.19.5 -- -- Make changes to Monitors table -- alter table Monitors add column EventPrefix varchar(32) not null default 'Event-' after Orientation; alter table Monitors add column AlarmFrameCount smallint(5) unsigned not null default '1' after PostEventCount; alter table Monitors add column Brightness mediumint(7) NOT NULL default '-1' after Orientation; alter table Monitors add column Contrast mediumint(7) NOT NULL default '-1' after Brightness; alter table Monitors add column Hue mediumint(7) NOT NULL default '-1' after Contrast; alter table Monitors add column Colour mediumint(7) NOT NULL default '-1' after Hue; -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.19.5.sql000066400000000000000000000024601225361755400175220ustar00rootroot00000000000000-- -- This updates a 1.19.5 database to 1.20.0 -- -- Create the Groups table -- CREATE TABLE Groups ( Id int(10) unsigned NOT NULL auto_increment, Name varchar(64) NOT NULL, MonitorIds tinytext NOT NULL, PRIMARY KEY (Id) ) TYPE=MyISAM; -- -- Make changes to Users table -- alter table Users modify MonitorIds tinytext not null default ''; -- -- Make changes to Monitors table -- alter table Monitors modify column Function enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor'; -- -- Make changes to Events table -- alter table Events add column Cause varchar(32) not null default '' after Name; alter table Events add column Notes tinytext after LearnState; -- -- Add a new index to the Events table -- alter table Events add index (Frames); -- -- Rationalise some of the name columns alter table Events modify column Name varchar(64) not null; alter table Filters modify column Name varchar(64) not null; alter table Monitors modify column Name varchar(64) not null; alter table States modify column Name varchar(64) not null; alter table Zones modify column Name varchar(64) not null; -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.20.0.sql000066400000000000000000000005541225361755400175070ustar00rootroot00000000000000-- -- This updates a 1.20.0 database to 1.20.1 -- -- Make changes to Users table -- alter table Users modify column Username varchar(32) BINARY NOT NULL default ''; -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.20.1.sql000066400000000000000000000166631225361755400175200ustar00rootroot00000000000000-- -- This updates a 1.20.0 database to 1.20.1 -- -- Make changes to Monitors table -- alter table Monitors add column Controllable tinyint(3) unsigned NOT NULL default '0'; alter table Monitors add column ControlId int(10) unsigned NOT NULL default '0'; alter table Monitors add column ControlDevice varchar(255) default NULL; alter table Monitors add column ControlAddress varchar(255) default NULL; alter table Monitors add column TrackMotion tinyint(3) unsigned NOT NULL default '0'; alter table Monitors add column TrackDelay smallint(5) unsigned NOT NULL default '0'; alter table Monitors add column ReturnLocation tinyint(3) NOT NULL default '-1'; alter table Monitors add column ReturnDelay smallint(5) unsigned NOT NULL default '0'; -- -- Add new table `Controls` -- CREATE TABLE Controls ( Id int(10) unsigned NOT NULL auto_increment, Name varchar(64) NOT NULL default '', Type enum('Local','Remote') NOT NULL default 'Local', Command varchar(255) default NULL, CanWake tinyint(3) unsigned NOT NULL default '0', CanSleep tinyint(3) unsigned NOT NULL default '0', CanReset tinyint(3) unsigned NOT NULL default '0', CanZoom tinyint(3) unsigned NOT NULL default '0', CanAutoZoom tinyint(3) unsigned NOT NULL default '0', CanZoomAbs tinyint(3) unsigned NOT NULL default '0', CanZoomRel tinyint(3) unsigned NOT NULL default '0', CanZoomCon tinyint(3) unsigned NOT NULL default '0', MinZoomRange int(10) unsigned default NULL, MaxZoomRange int(10) unsigned default NULL, MinZoomStep int(10) unsigned default NULL, MaxZoomStep int(10) unsigned default NULL, HasZoomSpeed tinyint(3) unsigned NOT NULL default '0', MinZoomSpeed int(10) unsigned default NULL, MaxZoomSpeed int(10) unsigned default NULL, CanFocus tinyint(3) unsigned NOT NULL default '0', CanAutoFocus tinyint(3) unsigned NOT NULL default '0', CanFocusAbs tinyint(3) unsigned NOT NULL default '0', CanFocusRel tinyint(3) unsigned NOT NULL default '0', CanFocusCon tinyint(3) unsigned NOT NULL default '0', MinFocusRange int(10) unsigned default NULL, MaxFocusRange int(10) unsigned default NULL, MinFocusStep int(10) unsigned default NULL, MaxFocusStep int(10) unsigned default NULL, HasFocusSpeed tinyint(3) unsigned NOT NULL default '0', MinFocusSpeed int(10) unsigned default NULL, MaxFocusSpeed int(10) unsigned default NULL, CanIris tinyint(3) unsigned NOT NULL default '0', CanAutoIris tinyint(3) unsigned NOT NULL default '0', CanIrisAbs tinyint(3) unsigned NOT NULL default '0', CanIrisRel tinyint(3) unsigned NOT NULL default '0', CanIrisCon tinyint(3) unsigned NOT NULL default '0', MinIrisRange int(10) unsigned default NULL, MaxIrisRange int(10) unsigned default NULL, MinIrisStep int(10) unsigned default NULL, MaxIrisStep int(10) unsigned default NULL, HasIrisSpeed tinyint(3) unsigned NOT NULL default '0', MinIrisSpeed int(10) unsigned default NULL, MaxIrisSpeed int(10) unsigned default NULL, CanGain tinyint(3) unsigned NOT NULL default '0', CanAutoGain tinyint(3) unsigned NOT NULL default '0', CanGainAbs tinyint(3) unsigned NOT NULL default '0', CanGainRel tinyint(3) unsigned NOT NULL default '0', CanGainCon tinyint(3) unsigned NOT NULL default '0', MinGainRange int(10) unsigned default NULL, MaxGainRange int(10) unsigned default NULL, MinGainStep int(10) unsigned default NULL, MaxGainStep int(10) unsigned default NULL, HasGainSpeed tinyint(3) unsigned NOT NULL default '0', MinGainSpeed int(10) unsigned default NULL, MaxGainSpeed int(10) unsigned default NULL, CanWhite tinyint(3) unsigned NOT NULL default '0', CanAutoWhite tinyint(3) unsigned NOT NULL default '0', CanWhiteAbs tinyint(3) unsigned NOT NULL default '0', CanWhiteRel tinyint(3) unsigned NOT NULL default '0', CanWhiteCon tinyint(3) unsigned NOT NULL default '0', MinWhiteRange int(10) unsigned default NULL, MaxWhiteRange int(10) unsigned default NULL, MinWhiteStep int(10) unsigned default NULL, MaxWhiteStep int(10) unsigned default NULL, HasWhiteSpeed tinyint(3) unsigned NOT NULL default '0', MinWhiteSpeed int(10) unsigned default NULL, MaxWhiteSpeed int(10) unsigned default NULL, HasPresets tinyint(3) unsigned NOT NULL default '0', NumPresets tinyint(3) unsigned NOT NULL default '0', HasHomePreset tinyint(3) unsigned NOT NULL default '0', CanSetPresets tinyint(3) unsigned NOT NULL default '0', CanMove tinyint(3) unsigned NOT NULL default '0', CanMoveDiag tinyint(3) unsigned NOT NULL default '0', CanMoveMap tinyint(3) unsigned NOT NULL default '0', CanMoveAbs tinyint(3) unsigned NOT NULL default '0', CanMoveRel tinyint(3) unsigned NOT NULL default '0', CanMoveCon tinyint(3) unsigned NOT NULL default '0', CanPan tinyint(3) unsigned NOT NULL default '0', MinPanRange int(10) default NULL, MaxPanRange int(10) default NULL, MinPanStep int(10) default NULL, MaxPanStep int(10) default NULL, HasPanSpeed tinyint(3) unsigned NOT NULL default '0', MinPanSpeed int(10) default NULL, MaxPanSpeed int(10) default NULL, HasTurboPan tinyint(3) unsigned NOT NULL default '0', TurboPanSpeed int(10) default NULL, CanTilt tinyint(3) unsigned NOT NULL default '0', MinTiltRange int(10) default NULL, MaxTiltRange int(10) default NULL, MinTiltStep int(10) default NULL, MaxTiltStep int(10) default NULL, HasTiltSpeed tinyint(3) unsigned NOT NULL default '0', MinTiltSpeed int(10) default NULL, MaxTiltSpeed int(10) default NULL, HasTurboTilt tinyint(3) unsigned NOT NULL default '0', TurboTiltSpeed int(10) default NULL, CanAutoScan tinyint(3) unsigned NOT NULL default '0', NumScanPaths tinyint(3) unsigned NOT NULL default '0', PRIMARY KEY (Id), UNIQUE KEY UC_Id (Id) ) TYPE=MyISAM; -- -- Some sample control protocol definitions -- insert into Controls values (1,'pelco-d','Local','/usr/local/bin/zmcontrol-pelco-d.pl',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); insert into Controls values (2,'visca','Local','/usr/local/bin/zmcontrol-visca.pl',1,1,0,1,0,0,0,1,0,16384,10,4000,1,1,6,1,1,1,0,1,0,1536,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,3,1,1,1,1,0,1,1,0,1,-15578,15578,100,10000,1,1,50,1,254,1,-7789,7789,100,5000,1,1,50,1,254,0,0); insert into Controls values (3,'KX-HCM10','Remote','/usr/local/bin/zmcontrol-kx-hcm10.pl',0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0); insert into Controls values (4,'pelco-d-full','Local','/usr/local/bin/zmcontrol-pelco-d.pl',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.21.0.sql000066400000000000000000000032511225361755400175050ustar00rootroot00000000000000-- -- This updates a 1.21.0 database to 1.21.1 -- -- Make changes to Monitors table -- alter table Monitors modify column Orientation enum('0','90','180','270','hori','vert') NOT NULL default '0'; alter table Monitors add column AutoStopTimeout decimal(5,2) default NULL after ControlAddress; -- -- Make changes to Stats table -- alter table Stats modify column MinBlobSize int(10) unsigned NOT NULL default '0'; alter table Stats modify column MaxBlobSize int(10) unsigned NOT NULL default '0'; -- -- Make changes to Zones table -- alter table Zones modify column MinBlobPixels int(10) unsigned default NULL; alter table Zones modify column MaxBlobPixels int(10) unsigned default NULL; -- -- Add in extra PTZ protocol -- insert into Controls values (0,'PELCO-P','Local','/usr/local/bin/zmcontrol-pelco-p.pl',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0); INSERT INTO Controls VALUES (0,'Axis API v2','Remote','/usr/local/bin/zmcontrol-axis-v2.pl',0,0,0,1,0,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,1,1,1,1,1,0,1,0,1,-360,360,1,90,0,NULL,NULL,0,NULL,1,-360,360,1,90,0,NULL,NULL,0,NULL,0,0); -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.21.1.sql000066400000000000000000000004201225361755400175010ustar00rootroot00000000000000-- -- This updates a 1.21.1 database to 1.21.2 -- -- No changes required -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.21.2.sql000066400000000000000000000004201225361755400175020ustar00rootroot00000000000000-- -- This updates a 1.21.2 database to 1.21.3 -- -- No changes required -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.21.3.sql000066400000000000000000000024051225361755400175100ustar00rootroot00000000000000-- -- This updates a 1.21.3 database to 1.21.4 -- alter table Monitors add column WebColour varchar(32) not null default 'red'; update Monitors set WebColour = concat( '#', hex(14*rand()),hex(15*rand()),hex(14*rand()),hex(15*rand()),hex(14*rand()),hex(15*rand()) ); alter table Monitors add column Sequence smallint unsigned; alter table Monitors modify column Device tinytext; update Monitors set Device = concat( "/dev/video", Device ); update Monitors set Device = NULL where Type = "Remote"; alter table Monitors add column DefaultScale smallint unsigned after ReturnDelay; alter table Monitors modify column Type enum('Local','Remote','File') NOT NULL default 'Local'; alter table Events add column Height smallint(5) unsigned not null default '0' after EndTime; alter table Events add column Width smallint(5) unsigned not null default '0' after EndTime; alter table Users add column Control enum('None','View','Edit') NOT NULL default 'None' after Events; update Users set Control = System; alter table Users add column MaxBandwidth varchar(16) not null default '' after System; -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.21.4.sql000066400000000000000000000274701225361755400175220ustar00rootroot00000000000000-- -- This updates a 1.21.4 database to 1.22.0 -- alter table Monitors change column RunMode Enabled tinyint(3) unsigned NOT NULL default '1'; alter table Monitors add column DefaultRate smallint unsigned not null default 100 after ReturnDelay; -- alter table Events add column Videoed tinyint unsigned not null default 0 after Archived; alter table Filters add column AutoVideo tinyint unsigned not null default 0 after AutoArchive; alter table Filters add column Temp tinyint unsigned not null default 0; update Filters set Temp = AutoDelete; alter table Filters drop column AutoDelete; alter table Filters change column Temp AutoDelete tinyint unsigned not null default 0; alter table Filters change column AutoExecute AutoExecuteCmd tinytext; alter table Filters add column AutoExecute tinyint unsigned not null default 0 after AutoMessage; update Filters set AutoExecute = if(isnull(AutoExecuteCmd)||AutoExecuteCmd='', 0, 1 ); -- alter table Zones add column NumCoords tinyint(3) unsigned NOT NULL default '0' after Units; alter table Zones add column Coords tinytext NOT NULL after NumCoords; alter table Zones add column Area int(10) unsigned not null default 0 after Coords; alter table Zones modify column AlarmRGB int(10) unsigned default '0'; alter table Zones add index MonitorId (MonitorId); -- insert into Controls values ('','Neu-Fusion NCS370','Remote','zmcontrol-ncs370.pl',0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,24,1,0,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0); -- -- Table structure for table `MonitorPresets` -- CREATE TABLE MonitorPresets ( Id int(10) unsigned NOT NULL auto_increment, Name varchar(64) NOT NULL, Type enum('Local','Remote','File') NOT NULL default 'Local', Device tinytext, Channel varchar(32) default NULL, Format varchar(32) default NULL, Host varchar(64) default NULL, Port varchar(8) default NULL, Path varchar(255) default NULL, Width smallint(5) unsigned default NULL, Height smallint(5) unsigned default NULL, Palette tinyint(3) unsigned default NULL, MaxFPS decimal(5,2) default NULL, Controllable tinyint(3) unsigned NOT NULL default '0', ControlId varchar(16) default NULL, ControlDevice varchar(255) default NULL, ControlAddress varchar(255) default NULL, DefaultRate smallint(5) unsigned NOT NULL default '100', DefaultScale smallint(5) unsigned NOT NULL default '100', PRIMARY KEY (Id) ) TYPE=MyISAM; -- -- Dumping data for table `MonitorPresets` -- INSERT INTO MonitorPresets VALUES ('','BTTV Video, PAL, 320x240','Local','/dev/video','','0',NULL,NULL,NULL,320,240,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video, PAL, 320x240, max 5 FPS','Local','/dev/video','','0',NULL,NULL,NULL,320,240,4,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video, PAL, 640x480','Local','/dev/video','','0',NULL,NULL,NULL,640,480,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video, PAL, 640x480, max 5 FPS','Local','/dev/video','','0',NULL,NULL,NULL,640,480,4,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video, NTSC, 320x240','Local','/dev/video','','1',NULL,NULL,NULL,320,240,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video, NTSC, 320x240, max 5 FPS','Local','/dev/video','','1',NULL,NULL,NULL,320,240,4,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video, NTSC, 640x480','Local','/dev/video','','1',NULL,NULL,NULL,640,480,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video, NTSC, 640x480, max 5 FPS','Local','/dev/video','','1',NULL,NULL,NULL,640,480,4,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP, 320x240, mpjpeg','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',320,240,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP, 320x240, mpjpeg, max 5 FPS','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',320,240,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP, 320x240, jpeg','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',320,240,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP, 320x240, jpeg, max 5 FPS','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',320,240,4,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP, 640x480, mpjpeg','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',640,480,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP, 640x480, mpjpeg, max 5 FPS','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',640,480,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP, 640x480, jpeg','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',640,480,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP, 640x480, jpeg, max 5 FPS','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',640,480,4,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP PTZ, 320x240, mpjpeg','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',320,240,4,NULL,1,4,NULL,':',100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP PTZ, 320x240, mpjpeg, max 5 FPS','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',320,240,4,NULL,1,4,NULL,':',100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP PTZ, 320x240, jpeg','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',320,240,4,NULL,1,4,NULL,':',100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP PTZ, 320x240, jpeg, max 5 FPS','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',320,240,4,5.0,1,4,NULL,':',100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP PTZ, 640x480, mpjpeg','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',640,480,4,NULL,1,4,NULL,':',100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP PTZ, 640x480, mpjpeg, max 5 FPS','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',640,480,4,NULL,1,4,NULL,':',100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP PTZ, 640x480, jpeg','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',640,480,4,NULL,1,4,NULL,':',100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP PTZ, 640x480, jpeg, max 5 FPS','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',640,480,4,5.0,1,4,NULL,':',100,100); INSERT INTO MonitorPresets VALUES ('','Panasonic IP, 320x240, mpjpeg','Remote',NULL,NULL,NULL,'',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',320,240,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Panasonic IP, 320x240, jpeg','Remote',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',320,240,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Panasonic IP, 320x240, jpeg, max 5 FPS','Remote',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',320,240,4,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Panasonic IP, 640x480, mpjpeg','Remote',NULL,NULL,NULL,'',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',640,480,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Panasonic IP, 640x480, jpeg','Remote',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',640,480,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Panasonic IP, 640x480, jpeg, max 5 FPS','Remote',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',640,480,4,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Panasonic IP PTZ, 320x240, mpjpeg','Remote',NULL,NULL,NULL,'',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',320,240,4,NULL,1,5,NULL,':',100,100); INSERT INTO MonitorPresets VALUES ('','Panasonic IP PTZ, 320x240, jpeg','Remote',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',320,240,4,NULL,1,5,NULL,':',100,100); INSERT INTO MonitorPresets VALUES ('','Panasonic IP PTZ, 320x240, jpeg, max 5 FPS','Remote',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',320,240,4,5.0,1,5,NULL,':',100,100); INSERT INTO MonitorPresets VALUES ('','Panasonic IP PTZ, 640x480, mpjpeg','Remote',NULL,NULL,NULL,'',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',640,480,4,NULL,1,5,NULL,':',100,100); INSERT INTO MonitorPresets VALUES ('','Panasonic IP PTZ, 640x480, jpeg','Remote',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',640,480,4,NULL,1,5,NULL,':',100,100); INSERT INTO MonitorPresets VALUES ('','Panasonic IP PTZ, 640x480, jpeg, max 5 FPS','Remote',NULL,NULL,NULL,'',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',640,480,4,5.0,1,5,NULL,':',100,100); -- -- Table structure for table `ZonePresets` -- CREATE TABLE ZonePresets ( Id int(10) unsigned NOT NULL auto_increment, Name varchar(64) NOT NULL default '', Type enum('Active','Inclusive','Exclusive','Preclusive','Inactive') NOT NULL default 'Active', Units enum('Pixels','Percent') NOT NULL default 'Pixels', CheckMethod enum('AlarmedPixels','FilteredPixels','Blobs') NOT NULL default 'Blobs', MinPixelThreshold smallint(5) unsigned default NULL, MaxPixelThreshold smallint(5) unsigned default NULL, MinAlarmPixels int(10) unsigned default NULL, MaxAlarmPixels int(10) unsigned default NULL, FilterX tinyint(3) unsigned default NULL, FilterY tinyint(3) unsigned default NULL, MinFilterPixels int(10) unsigned default NULL, MaxFilterPixels int(10) unsigned default NULL, MinBlobPixels int(10) unsigned default NULL, MaxBlobPixels int(10) unsigned default NULL, MinBlobs smallint(5) unsigned default NULL, MaxBlobs smallint(5) unsigned default NULL, PRIMARY KEY (Id), UNIQUE KEY UC_Id (Id) ) TYPE=MyISAM; -- -- Dumping data for table `ZonePresets` -- INSERT INTO ZonePresets VALUES (1,'Fast, low sensitivity','Active','Percent','AlarmedPixels',25,NULL,20,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); INSERT INTO ZonePresets VALUES (2,'Fast, medium sensitivity','Active','Percent','AlarmedPixels',15,NULL,10,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); INSERT INTO ZonePresets VALUES (3,'Fast, high sensitivity','Active','Percent','AlarmedPixels',10,NULL,5,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); INSERT INTO ZonePresets VALUES (4,'Best, low sensitivity','Active','Percent','Blobs',25,NULL,36,NULL,7,7,24,NULL,20,NULL,1,NULL); INSERT INTO ZonePresets VALUES (5,'Best, medium sensitivity','Active','Percent','Blobs',15,NULL,16,NULL,5,5,12,NULL,10,NULL,1,NULL); INSERT INTO ZonePresets VALUES (6,'Best, high sensitivity','Active','Percent','Blobs',10,NULL,8,NULL,3,3,6,NULL,5,NULL,1,NULL); -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.22.0.sql000066400000000000000000000046611225361755400175140ustar00rootroot00000000000000-- -- This updates a 1.22.0 database to 1.22.1 -- -- -- Add support for linked monitors -- alter table Monitors add column LinkedMonitors varchar(255) NOT NULL default '' after Enabled; -- -- Revise some defaults and sizes -- alter table Monitors modify column Device varchar(64) not null default ''; alter table Monitors modify column Host varchar(64) not null default ''; alter table Monitors modify column Port varchar(8) not null default ''; alter table Monitors modify column Path varchar(255) not null default ''; alter table Monitors modify column LabelX smallint(5) unsigned not null default 0; alter table Monitors modify column LabelY smallint(5) unsigned not null default 0; alter table Monitors modify column MaxFPS decimal(5,2) default NULL; update Monitors set MaxFPS = NULL where MaxFPS = 0.00; -- -- Add monitor specific alarm max FPS -- alter table Monitors add column AlarmMaxFPS decimal(5,2) default NULL after MaxFPS; -- -- Add average pixel difference to stats -- alter table Stats add column PixelDiff tinyint(3) unsigned NOT NULL default '0' after FrameId; -- -- Add some new monitor presets -- INSERT INTO MonitorPresets VALUES ('','Axis IP, 320x240, mpjpeg, B&W','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&color=0',320,240,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Axis IP, 640x480, mpjpeg, B&W','Remote',NULL,NULL,NULL,'',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&color=0',640,480,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Gadspot IP, jpeg, max 5 FPS','Remote',NULL,NULL,NULL,'',80,'/Jpeg/CamImg.jpg',NULL,NULL,4,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Gadspot IP, mpjpeg','Remote',NULL,NULL,NULL,'',80,'/GetData.cgi',NULL,NULL,4,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','Gadspot IP, mpjpeg','Remote',NULL,NULL,NULL,'',80,'/Jpeg/CamImg.jpg',NULL,NULL,4,5.0,0,NULL,NULL,NULL,100,100); -- -- Modify zone presets a bit -- UPDATE ZonePresets SET MinPixelThreshold = 60 WHERE Id = 1 OR Id = 4; UPDATE ZonePresets SET MinPixelThreshold = 40 WHERE Id = 2 OR Id = 5; UPDATE ZonePresets SET MinPixelThreshold = 20 WHERE Id = 3 OR Id = 6; -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.22.1.sql000066400000000000000000000011211225361755400175010ustar00rootroot00000000000000-- -- This updates a 1.22.1 database to 1.22.2 -- -- -- Add missing Zone Preset -- replace into ZonePresets values (6,'Best, high sensitivity','Active','Percent','Blobs',20,NULL,8,NULL,3,3,6,NULL,5,NULL,1,NULL); -- -- Remove redundant Zone columns -- alter table Zones drop column LoX; alter table Zones drop column HiX; alter table Zones drop column LoY; alter table Zones drop column HiY; -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.22.2.sql000066400000000000000000000035311225361755400175110ustar00rootroot00000000000000-- -- This updates a 1.22.2 database to 1.22.3 -- -- -- Add new Background column into Filters -- alter table Filters add column Background tinyint(1) unsigned not null default 0; -- -- Set the Background flag for any filters currently saved with Auto tasks -- update Filters set Background = 1 where (AutoArchive = 1 or AutoVideo = 1 or AutoUpload = 1 or AutoEmail = 1 or AutoMessage = 1 or AutoExecute = 1 or AutoDelete = 1); -- -- Add default view column into Monitors -- alter table Monitors add column DefaultView enum ('Events','Control') not null default 'Events' after ReturnDelay; alter table Monitors modify LabelFormat varchar(64) NOT NULL default '%%s - %y/%m/%d %H:%M:%S'; -- -- Add device permissions column into Users, set the permissions for existing users to -- be the same as for Monitors as a default -- alter table Users add column Devices enum('None','View','Edit') NOT NULL default 'None' after Monitors; update Users set Devices = Monitors; -- -- Increase size of Notes field in Events -- alter table Events modify column Notes text; -- -- Create new preset labels table -- CREATE TABLE `ControlPresets` ( `MonitorId` int(10) unsigned NOT NULL default '0', `Preset` int(10) unsigned NOT NULL default '0', `Label` varchar(64) NOT NULL default '', PRIMARY KEY (`MonitorId`,`Preset`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- -- Create new devices table -- CREATE TABLE `Devices` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` tinytext NOT NULL, `Type` enum('X10') NOT NULL default 'X10', `KeyString` varchar(32) NOT NULL default '', PRIMARY KEY (`Id`), UNIQUE KEY `UC_Id` (`Id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.22.3.sql000066400000000000000000000046711225361755400175200ustar00rootroot00000000000000-- -- This updates a 1.22.3 database to 1.23.0 -- -- -- Add a column for buffer replay streams -- alter table Monitors add column `StreamReplayBuffer` int(10) unsigned NOT NULL default '1000' after PostEventCount; -- -- Change the default timestamp format -- alter table Monitors modify column `LabelFormat` varchar(64) NOT NULL default '%N - %y/%m/%d %H:%M:%S'; -- -- Add a column for signal check colour -- alter table Monitors add column `SignalCheckColour` varchar(32) NOT NULL default '#0100BE' after DefaultScale; -- -- Increase the size of the run state definition column -- alter table States modify column Definition text; -- -- Add overload shutout to zones and presets -- alter table Zones add column OverloadFrames smallint(5) unsigned NOT NULL default '0' after MaxBlobs; alter table ZonePresets add column OverloadFrames smallint(5) unsigned NOT NULL default '0' after MaxBlobs; -- -- Change control command to protocol module -- alter table Controls add column Protocol varchar(32) after Command; update Controls set Protocol = "PelcoD" where Command like "%zmcontrol-pelco-d.pl"; update Controls set Protocol = "PelcoP" where Command like "%zmcontrol-pelco-p.pl"; update Controls set Protocol = "Visca" where Command like "%zmcontrol-visca.pl"; update Controls set Protocol = "PanasonicIP" where Command like "%zmcontrol-panasonic-ip.pl"; update Controls set Protocol = "AxisV2" where Command like "%zmcontrol-axis-v2.pl"; update Controls set Protocol = "Ncs370" where Command like "%zmcontrol-ncs370.pl"; update Controls set Protocol = "VclTP" where Command like "%zmcontrol-vcltp.pl"; alter table Controls drop column Command; -- -- Drop some duplicate/redundant indices -- alter table Config drop index `UC_Name`; alter table Controls drop index `UC_Id`; alter table Devices drop index `UC_Id`; alter table Events drop index `UC_Id`; alter table Frames drop index `UC_Id`; alter table Users drop index `UC_Id`; alter table ZonePresets drop index `UC_Id`; alter table Zones drop index `UC_Id`; alter table Events drop primary key; alter table Events add primary key ( `Id`, `MonitorId` ); alter table Events drop index `Id`; alter table Frames drop column `Id`; alter table Frames drop index `EventId`; alter table Frames add primary key ( `EventId`, `FrameId` ); -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.23.0.sql000066400000000000000000000005251225361755400175100ustar00rootroot00000000000000-- -- This updates a 1.23.0 database to 1.23.1 -- -- -- Change protocol field slightly -- alter table Controls modify Protocol varchar(64); -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.23.1.sql000066400000000000000000000013241225361755400175070ustar00rootroot00000000000000-- -- This updates a 1.23.1 database to 1.23.2 -- -- -- Rename and fix typo version of PurgeWhenFull -- update Filters set Name = "PurgeWhenFull" where Name = "xPurgeWhenFull"; update Filters set Query = 'a:4:{s:5:"terms";a:2:{i:0;a:3:{s:3:"val";s:1:"0";s:4:"attr";s:8:"Archived";s:2:"op";s:1:"=";}i:1;a:4:{s:3:"cnj";s:3:"and";s:3:"val";s:2:"95";s:4:"attr";s:11:"DiskPercent";s:2:"op";s:2:">=";}}s:10:"sort_field";s:2:"Id";s:8:"sort_asc";s:1:"1";s:5:"limit";s:2:"5";}' where Name = "PurgeWhenFull" and Query like "trms=%"; -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.23.2.sql000066400000000000000000000016101225361755400175060ustar00rootroot00000000000000-- -- This updates a 1.23.2 database to 1.23.3 -- -- -- Rename and fix typo version of PurgeWhenFull -- update Filters set Query = 'a:4:{s:5:"terms";a:2:{i:0;a:3:{s:3:"val";s:1:"0";s:4:"attr";s:8:"Archived";s:2:"op";s:1:"=";}i:1;a:4:{s:3:"cnj";s:3:"and";s:3:"val";s:2:"95";s:4:"attr";s:11:"DiskPercent";s:2:"op";s:2:">=";}}s:10:"sort_field";s:2:"Id";s:8:"sort_asc";s:1:"1";s:5:"limit";s:1:"5";}' where Name = "PurgeWhenFull" and Query = 'a:4:{s:5:"terms";a:2:{i:0;a:3:{s:3:"val";s:1:"0";s:4:"attr";s:8:"Archived";s:2:"op";s:1:"=";}i:1;a:4:{s:3:"cnj";s:3:"and";s:3:"val";s:2:"95";s:4:"attr";s:11:"DiskPercent";s:2:"op";s:2:">=";}}s:10:"sort_field";s:2:"Id";s:8:"sort_asc";s:1:"1";s:5:"limit";s:2:"5";}'; -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.23.3.sql000066400000000000000000000101211225361755400175040ustar00rootroot00000000000000-- -- This updates a 1.23.3 database to 1.24.0 -- -- -- Add protocol column for monitors -- alter table Monitors add column `Protocol` varchar(16) not null default '' after `Format`; alter table MonitorPresets add column `Protocol` varchar(16) default NULL after `Format`; update Monitors set Protocol = "http" where Type = "Remote"; update MonitorPresets set Protocol = "http" where Type = "Remote"; -- -- Add method column for monitors; -- alter table Monitors add column `Method` varchar(16) not null default '' after `Protocol`; alter table MonitorPresets add column `Method` varchar(16) default NULL after `Protocol`; update Monitors set Method = "simple" where Type = "Remote" and ( select Value from Config where Name = "ZM_NETCAM_REGEXPS" ) = 0; update Monitors set Method = "regexp" where Type = "Remote" and ( select Value from Config where Name = "ZM_NETCAM_REGEXPS" ) = 1; update MonitorPresets set Method = "simple" where Type = "Remote" and ( select Value from Config where Name = "ZM_NETCAM_REGEXPS" ) = 0; update MonitorPresets set Method = "regexp" where Type = "Remote" and ( select Value from Config where Name = "ZM_NETCAM_REGEXPS" ) = 1; -- -- Add subpath for remote RTSP monitors (only for now at least) -- alter table Monitors add column `SubPath` varchar(64) not null default '' after `Path`; alter table MonitorPresets add column `SubPath` varchar(64) default NULL after `Path`; -- -- Update Palette for new meaning as Colours for non-Local monitors -- update Monitors set Palette = 3 where Type != 'Local' and Palette = 4; update MonitorPresets set Palette = 3 where Type != 'Local' and Palette = 4; -- -- Update Method for Local monitors -- update Monitors set Method = "v4l1" where Type = 'Local'; -- -- Add monitor type for FFMPEG cameras -- alter table Monitors modify column `Type` enum('Local','Remote','File','Ffmpeg') NOT NULL default 'Local'; alter table MonitorPresets modify column `Type` enum('Local','Remote','File','Ffmpeg') NOT NULL default 'Local'; -- -- Fix columns to fit V4L2 formats and palettes -- alter table Monitors modify column `Format` int(10) unsigned NOT NULL default '0'; alter table Monitors modify column `Palette` int(10) unsigned NOT NULL default '0'; alter table MonitorPresets modify column `Channel` tinyint(3) unsigned default NULL; alter table MonitorPresets modify column `Format` int(10) unsigned default NULL; alter table MonitorPresets modify column `Palette` int(10) unsigned default NULL; -- -- Add in new MPEG presets -- insert into MonitorPresets values ('','Axis IP, mpeg4, unicast','Remote',NULL,NULL,NULL,'rtsp','rtpUni','',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); insert into MonitorPresets values ('','Axis IP, mpeg4, multicast','Remote',NULL,NULL,NULL,'rtsp','rtpMulti','',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); insert into MonitorPresets values ('','Axis IP, mpeg4, RTP/RTSP','Remote',NULL,NULL,NULL,'rtsp','rtpRtsp','',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); insert into MonitorPresets values ('','Axis IP, mpeg4, RTP/RTSP/HTTP','Remote',NULL,NULL,NULL,'rtsp','rtpRtspHttp','',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); insert into MonitorPresets values ('','ACTi IP, mpeg4, unicast','Remote',NULL,NULL,NULL,'rtsp','rtpUni','',7070,'','/track',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); -- -- Get rid of never used columnn Learn State -- alter table Events drop column LearnState; -- -- Update for new event close mode -- update Config set Value = "time" where Name = "ZM_EVENT_CLOSE_MODE" and ( select Value from ( select * from Config ) as TempConfig where Name = "ZM_FORCE_CLOSE_EVENTS" ) = 1; update Config set Value = "idle" where Name = "ZM_EVENT_CLOSE_MODE" and ( select Value from ( select * from Config ) as TempConfig where Name = "ZM_FORCE_CLOSE_EVENTS" ) = 0; -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.24.0.sql000066400000000000000000000004301225361755400175040ustar00rootroot00000000000000-- -- This updates a 1.24.0 database to 1.24.1 -- -- -- No database changes -- -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.24.1.sql000066400000000000000000000065151225361755400175170ustar00rootroot00000000000000-- -- This updates a 1.24.1 database to the next version -- -- -- Replace local presets with V4L versioned ones -- delete from MonitorPresets where Type = 'Local'; INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L2), PAL, 320x240','Local','/dev/video','',255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L2), PAL, 320x240, max 5 FPS','Local','/dev/video','',255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L2), PAL, 640x480','Local','/dev/video','',255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L2), PAL, 640x480, max 5 FPS','Local','/dev/video','',255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L2), NTSC, 320x240','Local','/dev/video','',45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L2), NTSC, 320x240, max 5 FPS','Local','/dev/video','',45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L2), NTSC, 640x480','Local','/dev/video','',45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L2), NTSC, 640x480, max 5 FPS','Local','/dev/video','',45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L1), PAL, 320x240','Local','/dev/video','',0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L1), PAL, 320x240, max 5 FPS','Local','/dev/video','',0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L1), PAL, 640x480','Local','/dev/video','',0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L1), PAL, 640x480, max 5 FPS','Local','/dev/video','',0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L1), NTSC, 320x240','Local','/dev/video','',1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L1), NTSC, 320x240, max 5 FPS','Local','/dev/video','',1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L1), NTSC, 640x480','Local','/dev/video','',1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES ('','BTTV Video (V4L1), NTSC, 640x480, max 5 FPS','Local','/dev/video','',1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100); -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.24.2.sql000066400000000000000000000024671225361755400175220ustar00rootroot00000000000000-- -- This updates a 1.24.2 database to the next version -- -- -- Add in remote ZoneMinder preset. -- INSERT INTO `MonitorPresets` VALUES ('','Axis FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp:///axis-media/media.amp?videocodec=h264',NULL,NULL,NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO `MonitorPresets` VALUES ('','Vivotek FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://:554/live.sdp',NULL,NULL,NULL,352,240,NULL,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO `MonitorPresets` VALUES ('','Axis FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp:///axis-media/media.amp',NULL,NULL,NULL,640,480,NULL,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO `MonitorPresets` VALUES ('','ACTi TCM FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://admin:123456@:7070',NULL,NULL,NULL,320,240,NULL,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO `MonitorPresets` VALUES ('','Remote ZoneMinder','Remote',NULL,NULL,NULL,'http','simple','',80,'/cgi-bin/nph-zms?mode=jpeg&monitor=&scale=100&maxfps=5&buffer=0',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.24.3.sql000066400000000000000000000004041225361755400175100ustar00rootroot00000000000000-- -- This updates a 1.24.3 database to the next version -- -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.24.4.sql000066400000000000000000000012761225361755400175210ustar00rootroot00000000000000-- -- This updates a 1.24.4 database to the next version -- -- -- Create Logs table -- TODO - defaults to MyISAM as not easy to import selected engine -- CREATE TABLE `Logs` ( `TimeKey` decimal(16,6) NOT NULL, `Component` varchar(32) NOT NULL, `Pid` smallint(6) DEFAULT NULL, `Level` tinyint(3) NOT NULL, `Code` char(3) NOT NULL, `Message` varchar(255) NOT NULL, `File` varchar(255) DEFAULT NULL, `Line` smallint(5) unsigned DEFAULT NULL, KEY `TimeKey` (`TimeKey`) ) ENGINE=MyISAM; -- -- These are optional, but we might as well do it now -- optimize table Frames; optimize table Events; optimize table Filters; optimize table Zones; optimize table Monitors; optimize table Stats; ZoneMinder-1.26.5/db/zm_update-1.26.0.sql000066400000000000000000000002761225361755400175160ustar00rootroot00000000000000ALTER TABLE `Monitors` ADD `Colours` TINYINT UNSIGNED NOT NULL DEFAULT '1' AFTER `Height`; ALTER TABLE `Monitors` ADD `Deinterlacing` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `Orientation`; ZoneMinder-1.26.5/db/zm_update-1.26.1.sql000066400000000000000000000001151225361755400175070ustar00rootroot00000000000000-- -- This updates a 1.26.0 database to 1.26.1 -- -- No changes required -- ZoneMinder-1.26.5/db/zm_update-1.26.2.sql000066400000000000000000000001151225361755400175100ustar00rootroot00000000000000-- -- This updates a 1.26.1 database to 1.26.2 -- -- No changes required -- ZoneMinder-1.26.5/db/zm_update-1.26.3.sql000066400000000000000000000001151225361755400175110ustar00rootroot00000000000000-- -- This updates a 1.26.2 database to 1.26.3 -- -- No changes required -- ZoneMinder-1.26.5/db/zm_update-1.26.5.sql000066400000000000000000000004361225361755400175210ustar00rootroot00000000000000-- -- This updates a 1.26.4 database to 1.26.5 -- -- -- Add AlarmRefBlendPerc field for controlling the reference image blend percent during alarm (see pull request #241) -- ALTER TABLE `Monitors` ADD `AlarmRefBlendPerc` TINYINT(3) UNSIGNED NOT NULL DEFAULT '3' AFTER `RefBlendPerc`; ZoneMinder-1.26.5/description-pak000066400000000000000000000001151225361755400167050ustar00rootroot00000000000000Zoneminder with kfir performances patches and fixes from UnixMedia (nextime) ZoneMinder-1.26.5/distros/000077500000000000000000000000001225361755400153605ustar00rootroot00000000000000ZoneMinder-1.26.5/distros/debian/000077500000000000000000000000001225361755400166025ustar00rootroot00000000000000ZoneMinder-1.26.5/distros/debian/README.Debian000066400000000000000000000036561225361755400206550ustar00rootroot00000000000000zoneminder for Debian --------------------- There is one manual step to get the web interface working. You need to link /etc/zm/apache.conf to /etc/apache2/conf.d/zoneminder.conf, then reload the apache config (i.e. /etc/init.d/apache2 reload) Changing the location for images and events ------------------------------------------- Zoneminder, in its upstream form, stores data in /usr/share/zoneminder/. This package modifies that by changing /usr/share/zoneminder/images and /usr/share/zoneminder/events to symlinks to directories under /var/cache/zoneminder. There are numerous places these could be put and ways to do it. But, at the moment, if you change this, an upgrade will fail with a warning about these locations having changed (the reason for this was that previously, an upgrade would silently revert the changes and cause event loss - refer bug #608793). If you do want to change the location, here are a couple of suggestions. (thanks to vagrant@freegeek.org): These lines in fstab could allow you to bind-mount an alternate location /dev/sdX1 /otherdrive ext3 defaults 0 2 /otherdrive/zoneminder/images /var/cache/zoneminder/images bind defaults 0 2 /otherdrive/zoneminder/events /var/cache/zoneminder/events bind defaults 0 2 or if you have a separate partition for each: /dev/sdX1 /var/cache/zoneminder/images ext3 defaults 0 2 /dev/sdX2 /var/cache/zoneminder/events ext3 defaults 0 2 -- Peter Howard , Sun, 16 Jan 2010 01:35:51 +1100 Access to /dev/video* --------------------- For cameras which require access to /dev/video*, zoneminder may need the www-data user added to the video group in order to see those cameras: adduser www-data video Note that all web applications running on the zoneminder server will then have access to all video devices on the system. -- Vagrant Cascadian Sun, 27 Mar 2011 13:06:56 -0700 ZoneMinder-1.26.5/distros/debian/apache.conf000066400000000000000000000003231225361755400206700ustar00rootroot00000000000000Alias /zm /usr/share/zoneminder php_flag register_globals off Options Indexes FollowSymLinks DirectoryIndex index.php ZoneMinder-1.26.5/distros/debian/changelog000066400000000000000000000010101225361755400204440ustar00rootroot00000000000000zoneminder (1.26.5-1) unstable; urgency=low * improvements to zmupdate.pl, cleanups -- Isaac Connor Thu, 03 Oct 2013 11:40:32 -0400 zoneminder (1.26.3-1) unstable; urgency=low * A 'minor' release focusing on performance improvement and bug fixes. -- Isaac Connor Sun, 22 Sep 2013 09:36:42 +0800 zoneminder (1.25.1-1) unstable; urgency=low * Initial Version. -- Isaac Connor Mon, 29 Apr 2013 12:38:00 -0400 ZoneMinder-1.26.5/distros/debian/compat000066400000000000000000000000021225361755400200000ustar00rootroot000000000000009 ZoneMinder-1.26.5/distros/debian/control000066400000000000000000000054201225361755400202060ustar00rootroot00000000000000Source: zoneminder Section: net Priority: optional Maintainer: Isaac Connor Build-Depends: debhelper (>= 7.0.50), autoconf, automake, dpatch, libphp-serialization-perl, libgnutls-dev, libmysqlclient-dev, libdbd-mysql-perl, libdate-manip-perl, libwww-perl, libjpeg8-dev, libpcre3-dev, libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libv4l-dev (>= 0.8.3), libbz2-dev, libtool, libsys-mmap-perl, ffmpeg, libnetpbm10-dev, libavdevice-dev, libdevice-serialport-perl, libpcre3, libarchive-zip-perl, libmime-lite-perl, libjpeg8, dh-autoreconf Standards-Version: 3.9.2 Package: zoneminder Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, apache2, libapache2-mod-php5, php5, php5-mysql, libphp-serialization-perl, libdate-manip-perl, libmime-lite-perl, libmime-lite-perl, mysql-client, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl, libpcre3, ffmpeg, rsyslog | system-log-daemon, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, netpbm, libavdevice53, libjpeg8, zip, libnet-sftp-foreign-perl Recommends: mysql-server Description: A video camera security and surveillance solution ZoneMinder is intended for use in single or multi-camera video security applications, including commercial or home CCTV, theft prevention and child or family member or home monitoring and other care scenarios. It supports capture, analysis, recording, and monitoring of video data coming from one or more video or network cameras attached to a Linux system. ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom cameras using a variety of protocols. It is suitable for use as a home video security system and for commercial or professional video security and surveillance. It can also be integrated into a home automation system via X.10 or other protocols. Package: zoneminder-dbg Architecture: any Depends: zoneminder (= ${binary:Version}), ${misc:Depends} Description: debugging syumbols for zoneminder. ZoneMinder is a video camera security and surveillance solution. ZoneMinder is intended for use in single or multi-camera video security applications, including commercial or home CCTV, theft prevention and child or family member or home monitoring and other care scenarios. It supports capture, analysis, recording, and monitoring of video data coming from one or more video or network cameras attached to a Linux system. ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom cameras using a variety of protocols. It is suitable for use as a home video security system and for commercial or professional video security and surveillance. It can also be integrated into a home automation system via X.10 or other protocols. ZoneMinder-1.26.5/distros/debian/copyright000066400000000000000000000015331225361755400205370ustar00rootroot00000000000000Copyright: Copyright 2002 Philip Coombes License: This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA On Debian GNU/Linux systems, the text of the GPL can be found in /usr/share/common-licenses/GPL. ZoneMinder-1.26.5/distros/debian/dirs000066400000000000000000000002001225361755400174560ustar00rootroot00000000000000var/log/zm var/lib/zm var/cache/zoneminder/events var/cache/zoneminder/images var/cache/zoneminder/temp usr/share/zoneminder/db ZoneMinder-1.26.5/distros/debian/docs000066400000000000000000000000121225361755400174460ustar00rootroot00000000000000README.md ZoneMinder-1.26.5/distros/debian/init.d000066400000000000000000000034321225361755400177140ustar00rootroot00000000000000#!/bin/sh ### BEGIN INIT INFO # Provides: zoneminder # Required-Start: $network $remote_fs $syslog # Required-Stop: $network $remote_fs $syslog # Should-Start: mysql # Should-Stop: mysql # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Control ZoneMinder as a Service ### END INIT INFO # description: Control ZoneMinder as a Service # chkconfig: 2345 20 20 # Source function library. #. /etc/rc.d/init.d/functions prog=ZoneMinder ZM_PATH_BIN="/usr/bin" RUNDIR=/var/run/zm TMPDIR=/tmp/zm command="$ZM_PATH_BIN/zmpkg.pl" start() { echo -n "Starting $prog: " mkdir -p $RUNDIR && chown www-data:www-data $RUNDIR mkdir -p $TMPDIR && chown www-data:www-data $TMPDIR zmfix -a $command start RETVAL=$? [ $RETVAL = 0 ] && echo success [ $RETVAL != 0 ] && echo failure echo [ $RETVAL = 0 ] && touch /var/lock/zm return $RETVAL } stop() { echo -n "Stopping $prog: " # # Why is this status check being done? # as $command stop returns 1 if zoneminder # is stopped, which will result in # this returning 1, which will stuff # dpkg when it tries to stop zoneminder before # uninstalling . . . # result=`$command status` if [ ! "$result" = "running" ]; then echo "Zoneminder already stopped" echo RETVAL=0 else $command stop RETVAL=$? [ $RETVAL = 0 ] && echo success [ $RETVAL != 0 ] && echo failure echo [ $RETVAL = 0 ] && rm -f /var/lock/zm fi } status() { result=`$command status` if [ "$result" = "running" ]; then echo "ZoneMinder is running" RETVAL=0 else echo "ZoneMinder is stopped" RETVAL=1 fi } case "$1" in 'start') start ;; 'stop') stop ;; 'restart' | 'force-reload') stop start ;; 'status') status ;; *) echo "Usage: $0 { start | stop | restart | status }" RETVAL=1 ;; esac exit $RETVAL ZoneMinder-1.26.5/distros/debian/postinst000066400000000000000000000057031225361755400204150ustar00rootroot00000000000000#! /bin/sh set -e VERSION=1.26.4 if [ "$1" = "configure" ]; then # # Get mysql started if it isn't # if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then invoke-rc.d mysql start fi if $(/etc/init.d/mysql status >/dev/null 2>&1); then mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload # test if database if already present... if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf echo 'grant lock tables, alter,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql fi # get old version from upgrade... OLD_ZM_VERSION=${2%-*} if [ -z "$OLD_ZM_VERSION" ]; then # fall back to getting version from database itself, which may not necessarily be accurate? OLD_ZM_VERSION=$(echo 'select Value from Config where Name = "ZM_DYN_CURR_VERSION";' | mysql --defaults-file=/etc/mysql/debian.cnf --skip-column-names zm ) fi if [ -n "$OLD_ZM_VERSION" ] && [ "$OLD_ZM_VERSION" != "$VERSION" ] ; then echo 'grant lock tables, create, alter on zm.* to 'zmuser'@localhost identified by "zmpass";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql # stop zoneminder before performing database upgrade. invoke-rc.d zoneminder stop || true zmupdate.pl --nointeractive --version $OLD_ZM_VERSION fi else echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' fi chown www-data:www-data /var/log/zm chown www-data:www-data /var/lib/zm/ if [ -z "$2" ]; then chown www-data:www-data -R /var/cache/zoneminder fi fi # Ensure zoneminder is stopped... if [ -x "/etc/init.d/zoneminder" ]; then if invoke-rc.d zoneminder status ; then invoke-rc.d zoneminder stop || exit $? fi fi if [ "$1" = "configure" ]; then if [ -z "$2" ]; then chown www-data:www-data /var/log/zm chown www-data:www-data /var/lib/zm/ chown www-data:www-data -R /var/cache/zoneminder else chown www-data:www-data /var/log/zm OLD_ZM_VERSION=${2%-*} if [ "$OLD_ZM_VERSION" != "$VERSION" ] ; then zmupdate.pl --version $OLD_ZM_VERSION fi fi fi #DEBHELPER# ZoneMinder-1.26.5/distros/debian/postrm000066400000000000000000000005411225361755400200510ustar00rootroot00000000000000#! /bin/sh # set -e # to be reinstated later if [ "$1" = "purge" ]; then echo 'delete from user where User="zmuser";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql echo 'delete from db where User="zmuser";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql mysqladmin --defaults-file=/etc/mysql/debian.cnf -f drop zm fi #DEBHELPER# ZoneMinder-1.26.5/distros/debian/preinst000077500000000000000000000013761225361755400202230ustar00rootroot00000000000000#!/bin/sh set -e abort=false if [ -L /usr/share/zoneminder/events ]; then l=$(readlink /usr/share/zoneminder/events) if [ "$l" != "/var/cache/zoneminder/events" ]; then abort=true fi fi if [ -L /usr/share/zoneminder/images ]; then l=$(readlink /usr/share/zoneminder/images ) if [ "$l" != "/var/cache/zoneminder/images" ]; then abort=true fi fi if [ "$abort" = "true" ]; then cat >&2 << EOF Aborting installation of zoneminder due to non-default symlinks in /usr/share/zoneminder for the images and/or events directory, which could result in loss of data. Please move your data in each of these directories to /var/cache/zoneminder before installing zoneminder from the package. EOF exit 1 fi #DEBHELPER# exit 0 ZoneMinder-1.26.5/distros/debian/rules000077500000000000000000000062731225361755400176720ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 UPSTREAM_VERSION := $(shell dpkg-parsechangelog | egrep '^Version:' | cut -f 2 -d ' ' | cut -d - -f 1) POSTINST_VERSION := $(shell egrep ^VERSION= debian/postinst | cut -d = -f 2) # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) CFLAGS = -Wall -g CPPFLAGS = -D__STDC_CONSTANT_MACROS CXXFLAGS = -DHAVE_LIBCRYPTO ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) CFLAGS += -O0 else CFLAGS += -O2 endif %: dh $@ --with autoreconf override_dh_auto_configure: CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --sysconfdir=/etc/zm --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info --with-mysql=/usr --with-webdir=/usr/share/zoneminder --with-ffmpeg=/usr --with-cgidir=/usr/lib/cgi-bin --with-webuser=www-data --with-webgroup=www-data --enable-crashtrace=no --enable-mmap=yes override_dh_clean: # check to make sure that postinst contains the correct upstream version [ $(UPSTREAM_VERSION) = $(POSTINST_VERSION) ] # Add here commands to clean up after the build process. [ ! -f Makefile ] || $(MAKE) distclean dh_clean override_dh_install: # Add here commands to install the package into debian/zm. $(MAKE) install DESTDIR=$(CURDIR)/debian/zoneminder RUNDIR=$(CURDIR)/debian/zoneminder/var/run ZM_RUNDIR=$(CURDIR)/debian/zoneminder/var/run install -D -m 0644 db/zm_create.sql $(CURDIR)/debian/zoneminder/usr/share/zoneminder/db install -D -m 0644 db/zm_update-*.sql $(CURDIR)/debian/zoneminder/usr/share/zoneminder/db install -D -m 0644 debian/apache.conf $(CURDIR)/debian/zoneminder/etc/zm # # NOTE: This is a short-term kludge; hopefully changes in the next # upstream version will render this unnecessary. rm -rf debian/zoneminder/usr/share/zoneminder/events rm -rf debian/zoneminder/usr/share/zoneminder/images rm -rf debian/zoneminder/usr/share/zoneminder/temp ln -s /var/cache/zoneminder/events debian/zoneminder/usr/share/zoneminder/ ln -s /var/cache/zoneminder/images debian/zoneminder/usr/share/zoneminder/ ln -s /var/cache/zoneminder/temp debian/zoneminder/usr/share/zoneminder/ # # This is a slightly lesser kludge; moving the cgi stuff to # /usr/share/zoneminder/cgi-bin breaks one set of behavior, # having it just in /usr/lib/cgi-bin breaks another bit of # behavior. # ln -s /usr/lib/cgi-bin debian/zoneminder/usr/share/zoneminder/ override_dh_fixperms: dh_fixperms chown root:root debian/zoneminder/etc/zm/zm.conf override_dh_auto_test: # do not run tests... .PHONY: override_dh_strip override_dh_strip: dh_strip --dbg-package=zoneminder-dbg ZoneMinder-1.26.5/distros/debian/watch000066400000000000000000000001211225361755400176250ustar00rootroot00000000000000version=3 http://www.zoneminder.com/downloads.html \ .*/ZoneMinder-(.*).tar.gz ZoneMinder-1.26.5/distros/fedora/000077500000000000000000000000001225361755400166205ustar00rootroot00000000000000ZoneMinder-1.26.5/distros/fedora/CMakeLists.txt000066400000000000000000000071171225361755400213660ustar00rootroot00000000000000# CMakeLists.txt for the Fedora Target Distro. # Download jscalendar & move files into position file(DOWNLOAD http://downloads.sourceforge.net/jscalendar/jscalendar-1.0.zip ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar-1.0.zip STATUS download_jsc) if(download_jsc EQUAL 0) message(STATUS "Jscalander successfully downloaded. Installing...") execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar.sh WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ERROR_VARIABLE unzip_jsc) message(STATUS "Status of jscalender script was: ${unzip_jsc}") else(download_jsc EQUAL 0) message(STATUS "Unable to download optional jscalander. Skipping...") endif(download_jsc EQUAL 0) # Create several empty folders file(MAKE_DIRECTORY sock swap zoneminder zoneminder-upload events images temp) # Install the empty folders #install(DIRECTORY run DESTINATION /var DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_WRITE GROUP_READ GROUP_EXECUTE WORLD_WRITE WORLD_READ WORLD_EXECUTE) install(DIRECTORY sock swap DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder DESTINATION /var/log DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder DESTINATION /run DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder-upload DESTINATION /var/spool DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY events images temp DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) # Create symlinks install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/events \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/events\")") install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/images \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/images\")") install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/temp \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/temp\")") # Fedora requires cambozola as a separate package so just link to it install(CODE "execute_process(COMMAND ln -sf ../../java/cambozola.jar \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/cambozola.jar\")") # Install auxillary files required to run zoneminder on Fedora install(FILES zoneminder.conf DESTINATION /etc/httpd/conf.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) install(FILES zoneminder.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) install(FILES zoneminder.tmpfiles DESTINATION /etc/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(FILES redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) # Install jscalendar if(unzip_jsc STREQUAL "") install(DIRECTORY jscalendar-1.0/ DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/tools/jscalendar) endif(unzip_jsc STREQUAL "") ZoneMinder-1.26.5/distros/fedora/README.Fedora000066400000000000000000000111141225361755400206750ustar00rootroot00000000000000New installs ============ 1. Unless you are already using the MySQL server or you are running it remotely you will need to ensure that the server is installed and secured: sudo yum install community-mysql-server sudo systemctl enable mysqld sudo systemctl start mysqld.service mysql_secure_installation NOTE: The Fedora team currently recommends mysql-community over mariadb 2. Using the password for the root account set during the previous step, you will need to create the ZoneMinder database, assuming your database server is local: mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql mysqladmin -uroot -p reload 3. The database needs a user. One is not created by default because this would introduce an obvious security issue. The following should set this up: mysql -u root -p grant select,insert,update,delete,alter on zm.* to 'zmuser'@localhost identified by 'zmpass'; Obviously, change at least zmpass to an actual, secure password or passphrase. You can change zmuser as well if you like. 4. Edit /etc/zm.conf and, at the bottom, change ZM_DB_PASS and perhaps ZM_DB_USER to match. 5. Edit /etc/php.ini, uncomment the date.timezone line, and add your local timezone. For whatever reason, 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 http://php.net/date.timezone 6. This package probably does not work with SELinux enabled at the moment. It may be necessary to disable SELinux for httpd, or even completely for ZoneMinder to function. This will be addressed in a later release. Run setenforce 0 for testing, and edit /etc/sysconfig/selinux to disable it at boot time. 7. IMPORTANT: Edit /etc/httpd/conf.d/zoneminder.conf and/or /etc/httpd/conf. The httpd.conf file included with this version of Fedora processes the conf.d folder after the default ScriptAlias directive in the httpd.conf file. Previously, the conf.d folder was processed before the default ScriptAlias directive. This causes a ScriptAlias overlap and breaks Zoneminder's streaming abilities. Reference: http://httpd.apache.org/docs/2.4/mod/mod_alias.html#order Bug Report: https://bugzilla.redhat.com/show_bug.cgi?id=973067 WORKAROUND #1 If you are running zoneminder on a dedicated server then the simplest solution may be to simply comment out the line in httpd.conf that reads: ScriptAlias /cgi-bin/ "/var/www/cgi-bin/" WORKAROUND #2 If you need both the default cgi-bin folder & the zoneminder cgi-bin folder then a solution might be to move the following line before the default ScriptAlias directive in the httpd.conf file: IncludeOptional conf.d/*.conf 8. Now start the web server: sudo systemctl enable httpd.service sudo systemctl start httpd.service 9. You should immediately visit http://localhost/zm and secure the system if it is network facing. To do this: a) click Options, then System. b) check OPT_USE_AUTH. c) set AUTH_HASH_SECRET to a random string. d) click Save and refresh the main browser window. e) You should be prompted to log in; the default username/password is admin/admin. f) Open Options again, choose the newly visible Users tab. g) click the admin user and set a password. h) enable OPT_CONTROL on the Ssytem tab to enable ptz camera control. 10. The zoneminder.service file fails at present but the zmpkg.pl script can be run as root to start zoneminder. sudo zmpkg.pl start To start zoneminder automatically, create /etc/rc.d/rc.local and place the following inside it: #!/bin/sh /usr/bin/zmpkg.pl start The rc.local file must be made executable. Upgrades ======== 1. Update /etc/zm.conf. Check for any new settings and update the version information. Comparing /etc/zm.conf and /etc/zm.conf.rpmnew should help to do this. 2. Add the mysql ALTER permission to the zmuser account: mysql -u root -p grant alter on zm.* to 'zmuser'@localhost identified by 'zmpass'; Since this is an upgrade, the assumption is that the zmuser account already has select, insert, update, and delete permission. 3. You will need to upgrade the ZoneMinder database as described in the manual. Only if the previous step was succesful, may you run zmupdate like so: sudo zmupdate.pl --version= If unsure then run it this way: sudo zmupdate.pl --user=root --pass= --version= ZoneMinder-1.26.5/distros/fedora/jscalendar.sh000077700000000000000000000000001225361755400254012../redhat/jscalendar.shustar00rootroot00000000000000ZoneMinder-1.26.5/distros/fedora/redalert.wav000066400000000000000000000000271225361755400211400ustar00rootroot00000000000000../redhat/redalert.wav ZoneMinder-1.26.5/distros/fedora/zoneminder-1.24.3-runlevel.patch000066400000000000000000000010221225361755400243630ustar00rootroot00000000000000diff -up ./scripts/zm.in.runlevel ./scripts/zm.in --- ./scripts/zm.in.runlevel 2010-11-28 15:22:05.000000000 -0600 +++ ./scripts/zm.in 2011-03-24 21:39:01.973010160 -0500 @@ -1,6 +1,6 @@ #!/bin/sh # description: ZoneMinder is the top Linux video camera security and surveillance solution. ZoneMinder is intended for use in single or multi-camera video security applications.Copyright: Philip Coombes, Corey DeLasaux 2003-2008 -# chkconfig: 2345 99 00 +# chkconfig: - 99 00 # processname: zmpkg.pl # Source function library. ZoneMinder-1.26.5/distros/fedora/zoneminder-1.24.4-installfix.patch000066400000000000000000000042141225361755400247130ustar00rootroot00000000000000diff -up ./Makefile.am.installfix ./Makefile.am --- ./Makefile.am.installfix 2011-06-19 15:51:14.000000000 -0500 +++ ./Makefile.am 2011-08-13 20:33:30.288587436 -0500 @@ -21,12 +21,12 @@ EXTRA_DIST = \ # Yes, you are correct. This is a HACK! install-data-hook: ( cd $(DESTDIR)$(sysconfdir); chown $(webuser):$(webgroup) $(sysconf_DATA); chmod 600 $(sysconf_DATA) ) - ( if ! test -e $(ZM_RUNDIR); then mkdir -p $(ZM_RUNDIR); fi; if test "$(ZM_RUNDIR)" != "/var/run"; then chown $(webuser):$(webgroup) $(ZM_RUNDIR); chmod u+w $(ZM_RUNDIR); fi ) - ( if ! test -e $(ZM_TMPDIR); then mkdir -m 700 -p $(ZM_TMPDIR); fi; if test "$(ZM_TMPDIR)" != "/tmp"; then chown $(webuser):$(webgroup) $(ZM_TMPDIR); chmod u+w $(ZM_TMPDIR); fi ) - ( if ! test -e $(ZM_LOGDIR); then mkdir -p $(ZM_LOGDIR); fi; if test "$(ZM_LOGDIR)" != "/var/log"; then chown $(webuser):$(webgroup) $(ZM_LOGDIR); chmod u+w $(ZM_LOGDIR); fi ) + ( if ! test -e $(DESTDIR)$(ZM_RUNDIR); then mkdir -p $(DESTDIR)$(ZM_RUNDIR); fi; if test "$(DESTDIR)$(ZM_RUNDIR)" != "/var/run"; then chown $(webuser):$(webgroup) $(DESTDIR)$(ZM_RUNDIR); chmod u+w $(DESTDIR)$(ZM_RUNDIR); fi ) + ( if ! test -e $(DESTDIR)$(ZM_TMPDIR); then mkdir -m 700 -p $(DESTDIR)$(ZM_TMPDIR); fi; if test "$(DESTDIR)$(ZM_TMPDIR)" != "/tmp"; then chown $(webuser):$(webgroup) $(DESTDIR)$(ZM_TMPDIR); chmod u+w $(DESTDIR)$(ZM_TMPDIR); fi ) + ( if ! test -e $(DESTDIR)$(ZM_LOGDIR); then mkdir -p $(DESTDIR)$(ZM_LOGDIR); fi; if test "$(DESTDIR)$(ZM_LOGDIR)" != "/var/log"; then chown $(webuser):$(webgroup) $(DESTDIR)$(ZM_LOGDIR); chmod u+w $(DESTDIR)$(ZM_LOGDIR); fi ) uninstall-hook: @-( cd $(DESTDIR)$(webdir); rm -rf events graphics images sounds temp ) - @-( if test "$(ZM_RUNDIR)" != "/var/run"; then rm -rf $(ZM_RUNDIR); fi ) - @-( if test "$(ZM_TMPDIR)" != "/tmp"; then rm -rf $(ZM_TMPDIR); fi ) - @-( if test "$(ZM_LOGDIR)" != "/var/log"; then rm -rf $(ZM_LOGDIR); fi ) + @-( if test "$(DESTDIR)$(ZM_RUNDIR)" != "/var/run"; then rm -rf $(DESTDIR)$(ZM_RUNDIR); fi ) + @-( if test "$(DESTDIR)$(ZM_TMPDIR)" != "/tmp"; then rm -rf $(DESTDIR)$(ZM_TMPDIR); fi ) + @-( if test "$(DESTDIR)$(ZM_LOGDIR)" != "/var/log"; then rm -rf $(DESTDIR)$(ZM_LOGDIR); fi ) ZoneMinder-1.26.5/distros/fedora/zoneminder-1.26.0-defaults.patch000066400000000000000000000117061225361755400243470ustar00rootroot00000000000000--- configure.ac 2013-08-15 11:44:10.000000000 -0500 +++ configure.ac.logdir 2013-08-17 09:20:07.326053328 -0500 @@ -46,7 +46,7 @@ AC_SUBST(ZM_TMPDIR,[/tmp/zm]) fi if test "$ZM_LOGDIR" == ""; then - AC_SUBST(ZM_LOGDIR,[/var/log/zm]) + AC_SUBST(ZM_LOGDIR,[/var/log/zoneminder]) fi LIB_ARCH=lib --- scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in 2013-08-01 18:14:45.175241378 -0500 +++ scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in.defaults 2013-08-07 18:57:42.525006149 -0500 @@ -187,7 +187,7 @@ }, { name => "ZM_PATH_ZMS", - default => "/cgi-bin/nph-zms", + default => "/cgi-bin/zm/nph-zms", description => "Web path to zms streaming server", help => "The ZoneMinder streaming server is required to send streamed images to your browser. It will be installed into the cgi-bin path given at configuration time. This option determines what the web path to the server is rather than the local path on your machine. Ordinarily the streaming server runs in parser-header mode however if you experience problems with streaming you can change this to non-parsed-header (nph) mode by changing 'zms' to 'nph-zms'.", type => $types{rel_path}, @@ -276,7 +276,7 @@ }, { name => "ZM_OPT_CAMBOZOLA", - default => "no", + default => "yes", description => "Is the (optional) cambozola java streaming client installed", help => "Cambozola is a handy low fat cheese flavoured Java applet that ZoneMinder uses to view image streams on browsers such as Internet Explorer that don't natively support this format. If you use this browser it is highly recommended to install this from http://www.charliemouse.com/code/cambozola/ however if it is not installed still images at a lower refresh rate can still be viewed.", type => $types{boolean}, @@ -526,7 +526,7 @@ }, { name => "ZM_LOG_DEBUG_FILE", - default => "@ZM_TMPDIR@/zm_debug.log+", + default => "/var/log/zoneminder/zm_debug_log+", description => "Where extra debug is output to", help => "This option allows you to specify a different target for debug output. All components have a default log file which will norally be in /tmp or /var/log and this is where debug will be written to if this value is empty. Adding a path here will temporarily redirect debug, and other logging output, to this file. This option is a simple filename and you are debugging several components then they will all try and write to the same file with undesirable consequences. Appending a '+' to the filename will cause the file to be created with a '.' suffix containing your process id. In this way debug from each run of a component is kept separate. This is the recommended setting as it will also prevent subsequent runs from overwriting the same log. You should ensure that permissions are set up to allow writing to the file and directory specified here.", requires => [ { name => "ZM_LOG_DEBUG", value => "yes" } ], @@ -623,7 +623,7 @@ }, { name => "ZM_PATH_SOCKS", - default => "@ZM_TMPDIR@", + default => "/var/lib/zoneminder/sock", description => "Path to the various Unix domain socket files that ZoneMinder uses", help => "ZoneMinder generally uses Unix domain sockets where possible. This reduces the need for port assignments and prevents external applications from possibly compromising the daemons. However each Unix socket requires a .sock file to be created. This option indicates where those socket files go.", type => $types{abs_path}, @@ -639,7 +639,7 @@ }, { name => "ZM_PATH_SWAP", - default => "@ZM_TMPDIR@", + default => "/dev/shm", description => "Path to location for temporary swap images used in streaming", help => "Buffered playback requires temporary swap images to be stored for each instance of the streaming daemons. This option determines where these images will be stored. The images will actually be stored in sub directories beneath this location and will be automatically cleaned up after a period of time.", type => $types{abs_path}, @@ -902,7 +902,7 @@ }, { name => "ZM_UPLOAD_FTP_LOC_DIR", - default => "@ZM_TMPDIR@", + default => "/var/spool/zoneminder-upload", description => "The local directory in which to create upload files", help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates the local directory that ZoneMinder should use for temporary upload files. These are files that are created from events, uploaded and then deleted.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], @@ -1258,7 +1258,7 @@ }, { name => "ZM_OPT_CONTROL", - default => "no", + default => "yes", description => "Support controllable (e.g. PTZ) cameras", help => "ZoneMinder includes limited support for controllable cameras. A number of sample protocols are included and others can easily be added. If you wish to control your cameras via ZoneMinder then select this option otherwise if you only have static cameras or use other control methods then leave this option off.", type => $types{boolean}, ZoneMinder-1.26.5/distros/fedora/zoneminder-1.26.3-dbinstall.patch000066400000000000000000000047461225361755400245250ustar00rootroot00000000000000--- configure.ac 2013-09-05 10:33:08.000000000 -0500 +++ configure.ac.dbinstall 2013-09-05 17:23:28.555553447 -0500 @@ -1,13 +1,11 @@ AC_PREREQ(2.59) -AC_INIT(zm,1.26.3,[http://www.zoneminder.com/forums/ - Please check FAQ first],ZoneMinder,http://www.zoneminder.com/downloads.html) +AC_INIT(zm,1.26.3,[http://www.zoneminder.com/forums/ - Please check FAQ first],zoneminder,http://www.zoneminder.com/downloads.html) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR(src/zm.h) AC_CONFIG_HEADERS(config.h) AC_SUBST([AM_CXXFLAGS], [-D__STDC_CONSTANT_MACROS]) -PATH_BUILD=`pwd` -AC_SUBST(PATH_BUILD) TIME_BUILD=`date +'%s'` AC_SUBST(TIME_BUILD) @@ -354,6 +352,8 @@ AC_PROG_PERL_MODULES(X10::ActiveHome,,AC AC_DEFINE_DIR([BINDIR],[bindir],[Expanded binary directory]) AC_DEFINE_DIR([LIBDIR],[libdir],[Expanded library directory]) +AC_DEFINE_DIR([DATADIR],[datadir],[Expanded data directory]) +AC_SUBST(PKGDATADIR,"$DATADIR/$PACKAGE") AC_SUBST(ZM_PID,"$ZM_RUNDIR/zm.pid") AC_DEFINE_DIR([SYSCONFDIR],[sysconfdir],[Expanded configuration directory]) AC_SUBST(ZM_CONFIG,"$SYSCONFDIR/zm.conf") diff -up ./db/Makefile.am.dbinstall ./db/Makefile.am --- ./db/Makefile.am.dbinstall 2009-10-14 04:42:46.000000000 -0500 +++ ./db/Makefile.am 2011-03-24 22:50:14.173912137 -0500 @@ -1,7 +1,16 @@ AUTOMAKE_OPTIONS = gnu +zmdbdatadir = $(pkgdatadir)/db + EXTRA_DIST = \ zm_create.sql.in \ + $(dbupgrade_scripts) + +dist_zmdbdata_DATA = \ + zm_create.sql \ + $(dbupgrade_scripts) + +dbupgrade_scripts = \ zm_update-0.0.1.sql \ zm_update-0.9.7.sql \ zm_update-0.9.8.sql \ diff -up ./scripts/zmupdate.pl.in.dbinstall ./scripts/zmupdate.pl.in --- ./scripts/zmupdate.pl.in.dbinstall 2011-08-27 15:44:05.335602405 -0500 +++ ./scripts/zmupdate.pl.in 2011-08-26 02:51:37.000000000 -0500 @@ -424,7 +424,7 @@ if ( $version ) } else { - $command .= ZM_PATH_BUILD."/db"; + $command .= ZM_PATH_DATA."/db"; } $command .= "/zm_update-".$version.".sql"; diff -up ./zm.conf.in.dbinstall ./zm.conf.in --- ./zm.conf.in.dbinstall 2008-07-25 04:48:16.000000000 -0500 +++ ./zm.conf.in 2011-03-24 22:50:14.175912077 -0500 @@ -12,8 +12,8 @@ # Current version of ZoneMinder ZM_VERSION=@VERSION@ -# Path to build directory, used mostly for finding DB upgrade scripts -ZM_PATH_BUILD=@PATH_BUILD@ +# Path to installed data directory, used mostly for finding DB upgrade scripts +ZM_PATH_DATA=@PKGDATADIR@ # Build time, used to record when to trigger various checks ZM_TIME_BUILD=@TIME_BUILD@ ZoneMinder-1.26.5/distros/fedora/zoneminder-1.26.3-noffmpeg.patch000066400000000000000000000035111225361755400243370ustar00rootroot00000000000000--- configure.ac 2013-09-10 12:42:56.000000000 -0500 +++ configure.ac.noffmpeg 2013-09-14 17:25:41.988388970 -0500 @@ -284,15 +284,15 @@ AC_CHECK_LIB(pcre,pcre_compile,,AC_MSG_WARN(libpcre.a may be required for remote/network camera support)) AC_CHECK_LIB(z,zlibVersion) AC_CHECK_LIB(x264,x264_predict_16x16_init) -AC_CHECK_LIB(avutil,av_malloc,,AC_MSG_WARN(libavutil.a may be required for MPEG streaming)) +dnl AC_CHECK_LIB(avutil,av_malloc,,AC_MSG_WARN(libavutil.a may be required for MPEG streaming)) # Don't bother to warn about this one -AC_CHECK_LIB(avcore,av_image_copy,,) -AC_CHECK_LIB(avcodec,avcodec_version,,AC_MSG_WARN(libavcodec.a is required for MPEG streaming)) -AC_CHECK_LIB(avformat,avformat_version,,AC_MSG_WARN(libavformat.a is required for MPEG streaming)) -#AC_CHECK_LIB(avcodec,avcodec_open,,AC_MSG_WARN(libavcodec.a is required for MPEG streaming)) -#AC_CHECK_LIB(avformat,av_new_stream,,AC_MSG_WARN(libavformat.a is required for MPEG streaming)) -AC_CHECK_LIB(avdevice,avdevice_register_all,,AC_MSG_WARN(libavdevice.a may be required for MPEG streaming)) -AC_CHECK_LIB(swscale,sws_scale,,,-lswscale) +dnl AC_CHECK_LIB(avcore,av_image_copy,,) +dnl AC_CHECK_LIB(avcodec,avcodec_version,,AC_MSG_WARN(libavcodec.a is required for MPEG streaming)) +dnl AC_CHECK_LIB(avformat,avformat_version,,AC_MSG_WARN(libavformat.a is required for MPEG streaming)) +dnl AC_CHECK_LIB(avcodec,avcodec_open,,AC_MSG_WARN(libavcodec.a is required for MPEG streaming)) +dnl AC_CHECK_LIB(avformat,av_new_stream,,AC_MSG_WARN(libavformat.a is required for MPEG streaming)) +dnl AC_CHECK_LIB(avdevice,avdevice_register_all,,AC_MSG_WARN(libavdevice.a may be required for MPEG streaming)) +dnl AC_CHECK_LIB(swscale,sws_scale,,,-lswscale) AC_CHECK_LIB(bz2,BZ2_bzCompress,,AC_MSG_WARN(zm requires libbz2.a for recent versions of ffmpeg)) AC_CHECK_LIB(z,compress,,) ZoneMinder-1.26.5/distros/fedora/zoneminder-1.26.4-dbinstall.patch000066400000000000000000000055341225361755400245220ustar00rootroot00000000000000--- configure.ac 2013-09-05 10:33:08.000000000 -0500 +++ configure.ac.dbinstall 2013-09-05 17:23:28.555553447 -0500 @@ -1,13 +1,11 @@ AC_PREREQ(2.59) -AC_INIT(zm,1.26.4,[http://www.zoneminder.com/forums/ - Please check FAQ first],ZoneMinder,http://www.zoneminder.com/downloads.html) +AC_INIT(zm,1.26.4,[http://www.zoneminder.com/forums/ - Please check FAQ first],zoneminder,http://www.zoneminder.com/downloads.html) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR(src/zm.h) AC_CONFIG_HEADERS(config.h) AC_SUBST([AM_CXXFLAGS], [-D__STDC_CONSTANT_MACROS]) -PATH_BUILD=`pwd` -AC_SUBST(PATH_BUILD) TIME_BUILD=`date +'%s'` AC_SUBST(TIME_BUILD) @@ -354,6 +352,8 @@ AC_PROG_PERL_MODULES(X10::ActiveHome,,AC AC_DEFINE_DIR([BINDIR],[bindir],[Expanded binary directory]) AC_DEFINE_DIR([LIBDIR],[libdir],[Expanded library directory]) +AC_DEFINE_DIR([DATADIR],[datadir],[Expanded data directory]) +AC_SUBST(PKGDATADIR,"$DATADIR/$PACKAGE") AC_SUBST(ZM_PID,"$ZM_RUNDIR/zm.pid") AC_DEFINE_DIR([SYSCONFDIR],[sysconfdir],[Expanded configuration directory]) AC_SUBST(ZM_CONFIG,"$SYSCONFDIR/zm.conf") diff -up ./db/Makefile.am.dbinstall ./db/Makefile.am --- ./db/Makefile.am.dbinstall 2009-10-14 04:42:46.000000000 -0500 +++ ./db/Makefile.am 2011-03-24 22:50:14.173912137 -0500 @@ -1,7 +1,16 @@ AUTOMAKE_OPTIONS = gnu +zmdbdatadir = $(pkgdatadir)/db + EXTRA_DIST = \ zm_create.sql.in \ + $(dbupgrade_scripts) + +dist_zmdbdata_DATA = \ + zm_create.sql \ + $(dbupgrade_scripts) + +dbupgrade_scripts = \ zm_update-0.0.1.sql \ zm_update-0.9.7.sql \ zm_update-0.9.8.sql \ diff -up ./scripts/zmupdate.pl.in.dbinstall ./scripts/zmupdate.pl.in --- scripts/zmupdate.pl.in 2013-10-05 14:46:16.000000000 -0500 +++ scripts/zmupdate.pl.in.dbinstall 2013-10-05 18:56:05.431045910 -0500 @@ -429,7 +429,7 @@ if ( $version ) } else { - $command .= ZM_PATH_BUILD."/db"; + $command .= ZM_PATH_DATA."/db"; } $command .= "/zm_update-".$version.".sql"; @@ -1030,7 +1030,7 @@ if ( $version ) if ( $version ge '1.26.0' ) { my @files; - $updateDir = ZM_PATH_BUILD."/db" if ! $updateDir; + $updateDir = ZM_PATH_DATA."/db" if ! $updateDir; opendir( my $dh, $updateDir ) || die "Can't open updateDir $!"; @files = sort grep { (!/^\./) && /^zm_update\-[\d\.]+\.sql$/ && -f "$updateDir/$_" } readdir($dh); closedir $dh; diff -up ./zm.conf.in.dbinstall ./zm.conf.in --- ./zm.conf.in.dbinstall 2008-07-25 04:48:16.000000000 -0500 +++ ./zm.conf.in 2011-03-24 22:50:14.175912077 -0500 @@ -12,8 +12,8 @@ # Current version of ZoneMinder ZM_VERSION=@VERSION@ -# Path to build directory, used mostly for finding DB upgrade scripts -ZM_PATH_BUILD=@PATH_BUILD@ +# Path to installed data directory, used mostly for finding DB upgrade scripts +ZM_PATH_DATA=@PKGDATADIR@ # Build time, used to record when to trigger various checks ZM_TIME_BUILD=@TIME_BUILD@ ZoneMinder-1.26.5/distros/fedora/zoneminder.cmake.f19.spec000066400000000000000000000330251225361755400233260ustar00rootroot00000000000000%define zmuid $(id -un) %define zmgid $(id -gn) %define zmuid_final apache %define zmgid_final apache Name: zoneminder Version: 1.26.4 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons # jscalendar is LGPL (any version): http://www.dynarch.com/projects/calendar/ # Mootools is inder the MIT license: http://mootools.net/ License: GPLv2+ and LGPLv2+ and MIT URL: http://www.zoneminder.com/ #Source: https://github.com/ZoneMinder/ZoneMinder/archive/v%{version}.tar.gz Source: ZoneMinder-%{version}.tar.gz Patch1: zoneminder-1.26.0-defaults.patch # Enable this patch to disable ffmpeg support #Patch2: zoneminder-1.26.3-noffmpeg.patch BuildRequires: cmake gnutls-devel systemd-units bzip2-devel BuildRequires: community-mysql-devel pcre-devel libjpeg-turbo-devel BuildRequires: perl(Archive::Tar) perl(Archive::Zip) BuildRequires: perl(Date::Manip) perl(DBD::mysql) BuildRequires: perl(ExtUtils::MakeMaker) perl(LWP::UserAgent) BuildRequires: perl(MIME::Entity) perl(MIME::Lite) BuildRequires: perl(PHP::Serialization) perl(Sys::Mmap) BuildRequires: perl(Time::HiRes) perl(Net::SFTP::Foreign) BuildRequires: perl(Expect) BuildRequires: gcc gcc-c++ # Comment out for no ffmpeg BuildRequires: ffmpeg-devel # Uncomment for X10 support #BuildRequires: perl(X10::ActiveHome) perl(Astro::SunTime) Requires: httpd php php-mysql cambozola Requires: libjpeg-turbo ffmpeg Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) Requires: perl(DBD::mysql) perl(Archive::Tar) perl(Archive::Zip) Requires: perl(MIME::Entity) perl(MIME::Lite) perl(Net::SMTP) perl(Net::FTP) Requires: perl(LWP::Protocol::https) Requires(post): systemd-units systemd-sysv Requires(post): /usr/bin/gpasswd Requires(post): /usr/bin/less Requires(preun): systemd-units Requires(postun): systemd-units %description ZoneMinder is a set of applications which is intended to provide a complete solution allowing you to capture, analyse, record and monitor any cameras you have attached to a Linux based machine. It is designed to run on kernels which support the Video For Linux (V4L) interface and has been tested with cameras attached to BTTV cards, various USB cameras and IP network cameras. It is designed to support as many cameras as you can attach to your computer without too much degradation of performance. %prep %setup -q -n ZoneMinder-%{version} %patch1 -p0 -b .defaults #%patch2 -p0 -b .noffmpeg %build %cmake -DZM_TARGET_DISTRO="f19" -DZM_NO_X10=ON -DZM_NO_FFMPEG=ON -DZM_PERL_SUBPREFIX=`x="%{perl_vendorlib}" ; echo ${x#"%{_prefix}"}` . make %{?_smp_mflags} %install export DESTDIR=%{buildroot} make install %post if [ $1 -eq 1 ] ; then # Initial installation /bin/systemctl daemon-reload >/dev/null 2>&1 || : fi # Allow zoneminder access to local video sources /usr/bin/gpasswd -a %zmuid_final video # Display the README for post installation instructions /usr/bin/less %{_docdir}/%{name}-%{version}/README.Fedora %preun if [ $1 -eq 0 ] ; then # Package removal, not upgrade /bin/systemctl --no-reload disable zoneminder.service > /dev/null 2>&1 || : /bin/systemctl stop zoneminder.service > /dev/null 2>&1 || : fi %postun /bin/systemctl daemon-reload >/dev/null 2>&1 || : if [ $1 -ge 1 ] ; then # Package upgrade, not uninstall /bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || : fi %triggerun -- zoneminder < 1.25.0-4 # Save the current service runlevel info # User must manually run systemd-sysv-convert --apply zoneminder # to migrate them to systemd targets /usr/bin/systemd-sysv-convert --save zoneminder >/dev/null 2>&1 ||: # Run these because the SysV package being removed won't do them /sbin/chkconfig --del zoneminder >/dev/null 2>&1 || : /bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || : %files %defattr(-,root,root,-) %doc AUTHORS COPYING README.md distros/fedora/README.Fedora distros/fedora/jscalendar-doc %config %attr(640,root,%{zmgid_final}) /etc/zm.conf %config(noreplace) %attr(644,root,root) /etc/httpd/conf.d/zoneminder.conf %config(noreplace) /etc/tmpfiles.d/zoneminder.conf %config(noreplace) /etc/logrotate.d/zoneminder %{_unitdir}/zoneminder.service %{_bindir}/zma %{_bindir}/zmaudit.pl %{_bindir}/zmc %{_bindir}/zmcontrol.pl %{_bindir}/zmdc.pl %{_bindir}/zmf %{_bindir}/zmfilter.pl %attr(4755,root,root) %{_bindir}/zmfix %{_bindir}/zmpkg.pl %{_bindir}/zmstreamer %{_bindir}/zmtrack.pl %{_bindir}/zmtrigger.pl %{_bindir}/zmu %{_bindir}/zmupdate.pl %{_bindir}/zmvideo.pl %{_bindir}/zmwatch.pl # Uncomment this for x10 support #%{_bindir}/zmx10.pl %{perl_vendorlib}/ZoneMinder* %{perl_vendorlib}/x86_64-linux-thread-multi/auto/ZoneMinder* #%{perl_archlib}/ZoneMinder* %{_mandir}/man*/* %dir %{_libexecdir}/zoneminder %{_libexecdir}/zoneminder/cgi-bin %dir %{_datadir}/zoneminder %{_datadir}/zoneminder/db %{_datadir}/zoneminder/www %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/events %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/images %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/sock %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/swap %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/temp %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/log/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/spool/zoneminder-upload %dir %attr(755,%{zmuid_final},%{zmgid_final}) /run/zoneminder %changelog * Mon Oct 07 2013 Andrew Bauer - 1.26.4 - Initial cmake build. * Sat Oct 05 2013 Andrew Bauer - 1.26.4 - Fedora specific path changes have been moved to zoneminder-1.26.0-defaults.patch - All files are now part of the zoneminder source tree. Update specfile accordingly. * Sat Sep 21 2013 Andrew Bauer - 1.26.3 - Initial rebuild for ZoneMinder 1.26.3 release. * Fri Feb 15 2013 Fedora Release Engineering - 1.25.0-13 - Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild * Mon Jan 21 2013 Adam Tkac - 1.25.0-12 - rebuild due to "jpeg8-ABI" feature drop * Mon Jan 7 2013 Remi Collet - 1.25.0-11 - fix configuration file for httpd 2.4, #871502 * Fri Dec 21 2012 Adam Tkac - 1.25.0-10 - rebuild against new libjpeg * Thu Aug 09 2012 Jason L Tibbitts III - 1.25.0-9 - Add patch to work around v4l2 api breakage in 3.5 kernel. * Sun Jul 22 2012 Fedora Release Engineering - 1.25.0-8 - Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild * Sat Jun 23 2012 Petr Pisar - 1.25.0-7 - Perl 5.16 rebuild * Wed Mar 21 2012 Jason L Tibbitts III - 1.25.0-6 - Fix stupid thinko in sql modifications. * Sat Feb 25 2012 Jason L Tibbitts III - 1.25.0-5 - Clean up macro usage. * Sat Feb 25 2012 Jason L Tibbitts III - 1.25.0-4 - Convert to systemd. - Add tmpfiles.d configuration since the initscript isn't around to create /run/zoneminder. - Remove some pointless executable permissions. - Add logrotate file. * Wed Feb 22 2012 Jason L Tibbitts III - 1.25.0-3 - Update README.Fedora to reference systemctl and mention timezone info in php.ini. - Add proper default for EYEZM_LOG_TO_FILE. * Thu Feb 09 2012 Jason L Tibbitts III - 1.25.0-2 - Rebuild for new pcre. * Thu Jan 19 2012 Jason L Tibbitts III - 1.25.0-1 - Update to 1.25.0 - Fix gcc4.7 build problems. - Drop gcc4.4 build fixes; for whatever reason they now break the build. - Clean up old patches. - Force setting of ZM_TMPDIR and ZM_RUNDIR. * Sat Jan 14 2012 Fedora Release Engineering - 1.24.4-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild * Thu Sep 15 2011 Jason L Tibbitts III - 1.24.4-3 - Re-add the dist-tag that somehow got lost. * Thu Sep 15 2011 Jason L Tibbitts III - 1.24.4-2 - Add patch for bug 711780 - fix syntax issue in Mapped.pm. - Undo that patch, and undo another which was the cause of the whole mess. - Fix up other patches so ZM_PATH_BUILD is both defined and useful. - Make sure database creation mods actually take. - Update Fedora-specific docs with some additional info. - Use bundled mootools (javascript, so no guideline violation). - Update download location. - Update the gcrypt patch to actually work. - Upstream changed the tarball without changing the version to patch a vulnerability, so redownload. * Sun Aug 14 2011 Jason L Tibbitts III - 1.24.4-1 - Initial attempt to upgrade to 1.24.4. - Add patch from BZ 460310 to build against libgcrypt instead of requiring the gnutls openssl libs. * Thu Jul 21 2011 Petr Sabata - 1.24.3-7.20110324svn3310 - Perl mass rebuild * Wed Jul 20 2011 Petr Sabata - 1.24.3-6.20110324svn3310 - Perl mass rebuild * Mon May 09 2011 Jason L Tibbitts III - 1.24.3-5.20110324svn3310 - Bump for gnutls update. * Thu Mar 24 2011 Jason L Tibbitts III - 1.24.3-4.20110324svn3310 - Update to latest 1.24.3 subversion. Turns out that what upstream was calling 1.24.3 is really just an occasionally updated devel snapshot. - Rebase various patches. * Wed Mar 23 2011 Dan Horák - 1.24.3-3 - rebuilt for mysql 5.5.10 (soname bump in libmysqlclient) * Tue Feb 08 2011 Fedora Release Engineering - 1.24.3-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild * Tue Jan 25 2011 Jason L Tibbitts III - 1.24.3-1 - Update to latest upstream version. - Rebase patches. - Initial incomplete attempt to disable v4l1 support. * Fri Jan 21 2011 Jason L Tibbitts III - 1.24.2-6 - Unbundle cambozola; instead link to the separately pacakged copy. - Remove BuildRoot:, %%clean and buildroot cleaning in %%install. - Git rid of mixed space/tab usage by removing all tabs. - Remove unnecessary Conflicts: line. - Attempt to force short_open_tag on for the code directories. - Move default location of sockets, swaps, logfiles and some temporary files to make more sense and allow things to work better with a future selinux policy. - Fix errors in README.Fedora. * Wed Jun 02 2010 Marcela Maslanova - 1.24.2-5 - Mass rebuild with perl-5.12.0 * Fri Dec 4 2009 Stepan Kasal - 1.24.2-4 - rebuild against perl 5.10.1 - use Perl vendorarch and archlib variables correctly * Mon Jul 27 2009 Fedora Release Engineering - 1.24.2-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild * Wed Jul 22 2009 Jason L Tibbitts III - 1.24.2-2 - Bump release since 1.24.2-1 was mistakenly tagged a few months ago. * Wed Jul 22 2009 Jason L Tibbitts III - 1.24.2-1 - Initial update to 1.24.2. - Rebase patches. - Update mootools download location. - Update to mootools 1.2.3. - Add additional dependencies for some optional features. * Sat Apr 11 2009 Martin Ebourne - 1.24.1-3 - Remove unused Sys::Mmap perl dependency RPM is finding * Sat Apr 11 2009 Martin Ebourne - 1.24.1-2 - Update gcc44 patch to disable -frepo, seems to be broken with gcc44 - Added noffmpeg patch to make building outside mock easier * Sat Mar 21 2009 Martin Ebourne - 1.24.1-1 - Patch for gcc 4.4 compilation errors - Upgrade to 1.24.1 * Wed Feb 25 2009 Fedora Release Engineering - 1.23.3-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild * Sat Jan 24 2009 Caolán McNamara - 1.23.3-3 - rebuild for dependencies * Mon Dec 15 2008 Martin Ebourne - 1.23.3-2 - Fix permissions on zm.conf * Fri Jul 11 2008 Jason L Tibbitts III - 1.23.3-1 - Initial attempt at packaging 1.23. * Tue Jul 1 2008 Martin Ebourne - 1.22.3-15 - Add perl module compat dependency, bz #453590 * Tue May 6 2008 Martin Ebourne - 1.22.3-14 - Remove default runlevel, bz #441315 * Mon Apr 28 2008 Jason L Tibbitts III - 1.22.3-13 - Backport patch for CVE-2008-1381 from 1.23.3 to 1.22.3. * Tue Feb 19 2008 Fedora Release Engineering - 1.22.3-12 - Autorebuild for GCC 4.3 * Thu Jan 3 2008 Martin Ebourne - 1.22.3-11 - Fix compilation on gcc 4.3 * Thu Dec 6 2007 Martin Ebourne - 1.22.3-10 - Rebuild for new openssl * Thu Aug 2 2007 Martin Ebourne - 1.22.3-8 - Fix licence tag * Thu Jul 12 2007 Martin Ebourne - 1.22.3-7 - Fixes from testing by Jitz including missing dependencies and database creation * Sat Jun 30 2007 Martin Ebourne - 1.22.3-6 - Disable crashtrace on ppc * Sat Jun 30 2007 Martin Ebourne - 1.22.3-5 - Fix uid for directories in /var/lib/zoneminder * Tue Jun 26 2007 Martin Ebourne - 1.22.3-4 - Added perl Archive::Tar dependency - Disabled web interface due to lack of access control on the event images * Sun Jun 10 2007 Martin Ebourne - 1.22.3-3 - Changes recommended in review by Jason Tibbitts * Mon Apr 2 2007 Martin Ebourne - 1.22.3-2 - Standardised on package name of zoneminder * Thu Dec 28 2006 Martin Ebourne - 1.22.3-1 - First version. Uses some parts from zm-1.20.1 by Corey DeLasaux and Serg Oskin ZoneMinder-1.26.5/distros/fedora/zoneminder.conf000066400000000000000000000027661225361755400216540ustar00rootroot00000000000000# The Zoneminder web interface has been disabled by default due to a small # security issue in the default install. # # When using Zoneminder's own authentication, recorded CCTV images are # accessible from the web directly without passing the authentication. This # means any attacker could see your CCTV images without a password. In order # to avoid this you can disable Zoneminder's authentication and configure # standard Apache authentication (see the Apache documentation for details on # this). # # If you still wish to use Zoneminder's own authentication, or have an # internal site which needs no authentication, you need to delete the line # marked below and restart Apache. Alias /zm "/usr/share/zoneminder/www" Options -Indexes +MultiViews +FollowSymLinks AllowOverride All # Apache 2.4 Require all granted # Apache 2.2 Order deny,allow Allow from all # The code unfortunately uses short tags in many places php_value short_open_tag 1 ScriptAlias /cgi-bin/zm "/usr/libexec/zoneminder/cgi-bin" AllowOverride All Options ExecCGI # Apache 2.4 Require all granted # Apache 2.2 Order deny,allow Allow from all ZoneMinder-1.26.5/distros/fedora/zoneminder.f19.spec000066400000000000000000000415011225361755400222450ustar00rootroot00000000000000%define cambrev 0.931 %define moorev 1.3.2 %define jscrev 1.0 %define zmuid $(id -un) %define zmgid $(id -gn) %define zmuid_final apache %define zmgid_final apache Name: zoneminder Version: 1.26.4 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons # jscalendar is LGPL (any version): http://www.dynarch.com/projects/calendar/ # Mootools is inder the MIT license: http://mootools.net/ License: GPLv2+ and LGPLv2+ and MIT URL: http://www.zoneminder.com/ #Source: https://github.com/ZoneMinder/ZoneMinder/archive/v%{version}.tar.gz Source: ZoneMinder-%{version}.tar.gz Source2: jscalendar-%{jscrev}.zip #Source2: http://downloads.sourceforge.net/jscalendar/jscalendar-%{jscrev}.zip # Need to unravel the proper mootools files to grab from upstream, since the # number of them keeps multiplying. In the meantime, rely on the ones bundled # with zoneminder. As these are javascript, there is no guideline violation # here. #Source3: http://mootools.net/download/get/mootools-core-%{moorev}-full-compat-yc.js #Patch1: zoneminder-1.26.4-dbinstall.patch Patch2: zoneminder-1.24.3-runlevel.patch Patch3: zoneminder-1.26.0-defaults.patch # Enable this patch to disable ffmpeg support #Patch4: zoneminder-1.26.3-noffmpeg.patch BuildRequires: automake gnutls-devel systemd-units BuildRequires: libtool bzip2-devel BuildRequires: community-mysql-devel pcre-devel libjpeg-turbo-devel BuildRequires: perl(Archive::Tar) perl(Archive::Zip) BuildRequires: perl(Date::Manip) perl(DBD::mysql) BuildRequires: perl(ExtUtils::MakeMaker) perl(LWP::UserAgent::Determined) BuildRequires: perl(MIME::Entity) perl(MIME::Lite) BuildRequires: perl(PHP::Serialization) perl(Sys::Mmap) BuildRequires: perl(Time::HiRes) perl(Net::SFTP::Foreign) BuildRequires: perl(Expect) perl(Sys::Syslog) BuildRequires: gcc gcc-c++ BuildRequires: autoconf autoconf-archive # Comment out for no ffmpeg BuildRequires: ffmpeg-devel # Uncomment for X10 support #BuildRequires: perl(X10::ActiveHome) perl(Astro::SunTime) Requires: httpd php php-mysql cambozola Requires: libjpeg-turbo ffmpeg Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) Requires: perl(DBD::mysql) perl(Archive::Tar) perl(Archive::Zip) Requires: perl(MIME::Entity) perl(MIME::Lite) perl(Net::SMTP) perl(Net::FTP) Requires: perl(LWP::Protocol::https) Requires(post): systemd-units systemd-sysv Requires(post): /usr/bin/gpasswd Requires(post): /usr/bin/less Requires(preun): systemd-units Requires(postun): systemd-units %description ZoneMinder is a set of applications which is intended to provide a complete solution allowing you to capture, analyse, record and monitor any cameras you have attached to a Linux based machine. It is designed to run on kernels which support the Video For Linux (V4L) interface and has been tested with cameras attached to BTTV cards, various USB cameras and IP network cameras. It is designed to support as many cameras as you can attach to your computer without too much degradation of performance. %prep %setup -q -n ZoneMinder-%{version} # Unpack jscalendar and move some files around %setup -q -D -T -a 2 -n ZoneMinder-%{version} mkdir jscalendar-doc pushd jscalendar-1.0 mv *html *php doc/* README ../jscalendar-doc rmdir doc popd #%patch1 -p0 -b .dbinstall %patch2 -p0 -b .runlevel %patch3 -p0 -b .defaults #%patch4 -p0 -b .noffmpeg chmod -x src/zm_event.cpp src/zm_user.h %build libtoolize --force aclocal autoheader automake --force-missing --add-missing autoconf OPTS="" %configure \ --disable-crashtrace \ --with-libarch=%{_lib} \ --with-mysql=%{_prefix} \ --with-ffmpeg=%{_prefix} \ --with-webdir=%{_datadir}/%{name}/www \ --with-cgidir=%{_libexecdir}/%{name}/cgi-bin \ --with-webuser=%{zmuid} \ --with-webgroup=%{zmgid} \ --enable-mmap=yes \ --disable-debug \ --with-webhost=zm.local \ ZM_SSL_LIB="gnutls" \ ZM_RUNDIR=/var/run/zoneminder \ ZM_TMPDIR=/var/lib/zoneminder/temp \ %ifarch x86_64 CXXFLAGS="-D__STDC_CONSTANT_MACROS -msse2" \ %else CXXFLAGS="-D__STDC_CONSTANT_MACROS" \ %endif --with-extralibs="" \ $OPTS make %{?_smp_mflags} %{__perl} -pi -e 's/(ZM_WEB_USER=).*$/${1}%{zmuid_final}/;' \ -e 's/(ZM_WEB_GROUP=).*$/${1}%{zmgid_final}/;' zm.conf %install install -d %{buildroot}/%{_localstatedir}/run make install DESTDIR=%{buildroot} \ INSTALLDIRS=vendor rm -rf %{buildroot}/%{perl_vendorarch} %{buildroot}/%{perl_archlib} # Comment out for x10 support rm -f %{buildroot}/%{_bindir}/zmx10.pl install -m 755 -d %{buildroot}/var/log/zoneminder for dir in events images temp do install -m 755 -d %{buildroot}/var/lib/zoneminder/$dir if [ -d %{buildroot}/%{_datadir}/zoneminder/www/$dir ]; then rmdir %{buildroot}/%{_datadir}/zoneminder/www/$dir fi ln -sf ../../../../var/lib/zoneminder/$dir %{buildroot}/%{_datadir}/zoneminder/www/$dir done install -m 755 -d %{buildroot}/var/lib/zoneminder/sock install -m 755 -d %{buildroot}/var/lib/zoneminder/swap install -m 755 -d %{buildroot}/var/spool/zoneminder-upload install -D -m 644 distros/fedora/zoneminder.conf %{buildroot}/etc/httpd/conf.d/zoneminder.conf install -D -m 755 distros/fedora/redalert.wav %{buildroot}/%{_datadir}/zoneminder/www/sounds/redalert.wav install -D -m 644 distros/fedora/zoneminder.service %{buildroot}/%{_unitdir}/zoneminder.service install -D -m 644 distros/fedora/zoneminder.logrotate %{buildroot}/etc/logrotate.d/zoneminder # Install jscalendar - this really should be in its own package install -d -m 755 %{buildroot}/%{_datadir}/%{name}/www/jscalendar cp -rp jscalendar-1.0/* %{buildroot}/%{_datadir}/zoneminder/www/jscalendar # Set up cambozola pushd %{buildroot}/%{_datadir}/zoneminder/www ln -s ../../java/cambozola.jar popd # Set up mootools pushd %{buildroot}/%{_datadir}/%{name}/www ln -f -s tools/mootools/mootools-core-%{moorev}-yc.js mootools-core.js ln -f -s tools/mootools/mootools-more-%{moorev}.1-yc.js mootools-more.js popd # Create an entry for tmpfiles.d install -D -m 755 distros/fedora/zoneminder.tmpfiles %{buildroot}/etc/tmpfiles.d/zoneminder.conf install -m 755 -d %{buildroot}/run/zoneminder %post if [ $1 -eq 1 ] ; then # Initial installation /bin/systemctl daemon-reload >/dev/null 2>&1 || : fi # Allow zoneminder access to local video sources /usr/bin/gpasswd -a %zmuid_final video # Display the README for post installation instructions /usr/bin/less %{_docdir}/%{name}-%{version}/README.Fedora %preun if [ $1 -eq 0 ] ; then # Package removal, not upgrade /bin/systemctl --no-reload disable zoneminder.service > /dev/null 2>&1 || : /bin/systemctl stop zoneminder.service > /dev/null 2>&1 || : fi %postun /bin/systemctl daemon-reload >/dev/null 2>&1 || : if [ $1 -ge 1 ] ; then # Package upgrade, not uninstall /bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || : fi %triggerun -- zoneminder < 1.25.0-4 # Save the current service runlevel info # User must manually run systemd-sysv-convert --apply zoneminder # to migrate them to systemd targets /usr/bin/systemd-sysv-convert --save zoneminder >/dev/null 2>&1 ||: # Run these because the SysV package being removed won't do them /sbin/chkconfig --del zoneminder >/dev/null 2>&1 || : /bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || : %files %defattr(-,root,root,-) %doc AUTHORS COPYING README.md distros/fedora/README.Fedora jscalendar-doc %config %attr(640,root,%{zmgid_final}) /etc/zm.conf %config(noreplace) %attr(644,root,root) /etc/httpd/conf.d/zoneminder.conf %config(noreplace) /etc/tmpfiles.d/zoneminder.conf %config(noreplace) /etc/logrotate.d/zoneminder %{_unitdir}/zoneminder.service %{_bindir}/zma %{_bindir}/zmaudit.pl %{_bindir}/zmc %{_bindir}/zmcontrol.pl %{_bindir}/zmdc.pl %{_bindir}/zmf %{_bindir}/zmfilter.pl %attr(4755,root,root) %{_bindir}/zmfix %{_bindir}/zmpkg.pl %{_bindir}/zmstreamer %{_bindir}/zmtrack.pl %{_bindir}/zmtrigger.pl %{_bindir}/zmu %{_bindir}/zmupdate.pl %{_bindir}/zmvideo.pl %{_bindir}/zmwatch.pl #%{_bindir}/zmx10.pl %{perl_vendorlib}/ZoneMinder* %{_mandir}/man*/* %dir %{_libexecdir}/zoneminder %{_libexecdir}/zoneminder/cgi-bin %dir %{_datadir}/zoneminder %{_datadir}/zoneminder/db %{_datadir}/zoneminder/www %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/events %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/images %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/sock %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/swap %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/temp %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/log/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/spool/zoneminder-upload %dir %attr(755,%{zmuid_final},%{zmgid_final}) /run/zoneminder %changelog * Sat Oct 05 2013 Andrew Bauer - 1.26.4 - Fedora specific path changes have been moved to zoneminder-1.26.0-defaults.patch - All files are now part of the zoneminder source tree. Update specfile accordingly. * Sat Sep 21 2013 Andrew Bauer - 1.26.3 - Initial rebuild for ZoneMinder 1.26.3 release. * Fri Feb 15 2013 Fedora Release Engineering - 1.25.0-13 - Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild * Mon Jan 21 2013 Adam Tkac - 1.25.0-12 - rebuild due to "jpeg8-ABI" feature drop * Mon Jan 7 2013 Remi Collet - 1.25.0-11 - fix configuration file for httpd 2.4, #871502 * Fri Dec 21 2012 Adam Tkac - 1.25.0-10 - rebuild against new libjpeg * Thu Aug 09 2012 Jason L Tibbitts III - 1.25.0-9 - Add patch to work around v4l2 api breakage in 3.5 kernel. * Sun Jul 22 2012 Fedora Release Engineering - 1.25.0-8 - Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild * Sat Jun 23 2012 Petr Pisar - 1.25.0-7 - Perl 5.16 rebuild * Wed Mar 21 2012 Jason L Tibbitts III - 1.25.0-6 - Fix stupid thinko in sql modifications. * Sat Feb 25 2012 Jason L Tibbitts III - 1.25.0-5 - Clean up macro usage. * Sat Feb 25 2012 Jason L Tibbitts III - 1.25.0-4 - Convert to systemd. - Add tmpfiles.d configuration since the initscript isn't around to create /run/zoneminder. - Remove some pointless executable permissions. - Add logrotate file. * Wed Feb 22 2012 Jason L Tibbitts III - 1.25.0-3 - Update README.Fedora to reference systemctl and mention timezone info in php.ini. - Add proper default for EYEZM_LOG_TO_FILE. * Thu Feb 09 2012 Jason L Tibbitts III - 1.25.0-2 - Rebuild for new pcre. * Thu Jan 19 2012 Jason L Tibbitts III - 1.25.0-1 - Update to 1.25.0 - Fix gcc4.7 build problems. - Drop gcc4.4 build fixes; for whatever reason they now break the build. - Clean up old patches. - Force setting of ZM_TMPDIR and ZM_RUNDIR. * Sat Jan 14 2012 Fedora Release Engineering - 1.24.4-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild * Thu Sep 15 2011 Jason L Tibbitts III - 1.24.4-3 - Re-add the dist-tag that somehow got lost. * Thu Sep 15 2011 Jason L Tibbitts III - 1.24.4-2 - Add patch for bug 711780 - fix syntax issue in Mapped.pm. - Undo that patch, and undo another which was the cause of the whole mess. - Fix up other patches so ZM_PATH_BUILD is both defined and useful. - Make sure database creation mods actually take. - Update Fedora-specific docs with some additional info. - Use bundled mootools (javascript, so no guideline violation). - Update download location. - Update the gcrypt patch to actually work. - Upstream changed the tarball without changing the version to patch a vulnerability, so redownload. * Sun Aug 14 2011 Jason L Tibbitts III - 1.24.4-1 - Initial attempt to upgrade to 1.24.4. - Add patch from BZ 460310 to build against libgcrypt instead of requiring the gnutls openssl libs. * Thu Jul 21 2011 Petr Sabata - 1.24.3-7.20110324svn3310 - Perl mass rebuild * Wed Jul 20 2011 Petr Sabata - 1.24.3-6.20110324svn3310 - Perl mass rebuild * Mon May 09 2011 Jason L Tibbitts III - 1.24.3-5.20110324svn3310 - Bump for gnutls update. * Thu Mar 24 2011 Jason L Tibbitts III - 1.24.3-4.20110324svn3310 - Update to latest 1.24.3 subversion. Turns out that what upstream was calling 1.24.3 is really just an occasionally updated devel snapshot. - Rebase various patches. * Wed Mar 23 2011 Dan Horák - 1.24.3-3 - rebuilt for mysql 5.5.10 (soname bump in libmysqlclient) * Tue Feb 08 2011 Fedora Release Engineering - 1.24.3-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild * Tue Jan 25 2011 Jason L Tibbitts III - 1.24.3-1 - Update to latest upstream version. - Rebase patches. - Initial incomplete attempt to disable v4l1 support. * Fri Jan 21 2011 Jason L Tibbitts III - 1.24.2-6 - Unbundle cambozola; instead link to the separately pacakged copy. - Remove BuildRoot:, %%clean and buildroot cleaning in %%install. - Git rid of mixed space/tab usage by removing all tabs. - Remove unnecessary Conflicts: line. - Attempt to force short_open_tag on for the code directories. - Move default location of sockets, swaps, logfiles and some temporary files to make more sense and allow things to work better with a future selinux policy. - Fix errors in README.Fedora. * Wed Jun 02 2010 Marcela Maslanova - 1.24.2-5 - Mass rebuild with perl-5.12.0 * Fri Dec 4 2009 Stepan Kasal - 1.24.2-4 - rebuild against perl 5.10.1 - use Perl vendorarch and archlib variables correctly * Mon Jul 27 2009 Fedora Release Engineering - 1.24.2-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild * Wed Jul 22 2009 Jason L Tibbitts III - 1.24.2-2 - Bump release since 1.24.2-1 was mistakenly tagged a few months ago. * Wed Jul 22 2009 Jason L Tibbitts III - 1.24.2-1 - Initial update to 1.24.2. - Rebase patches. - Update mootools download location. - Update to mootools 1.2.3. - Add additional dependencies for some optional features. * Sat Apr 11 2009 Martin Ebourne - 1.24.1-3 - Remove unused Sys::Mmap perl dependency RPM is finding * Sat Apr 11 2009 Martin Ebourne - 1.24.1-2 - Update gcc44 patch to disable -frepo, seems to be broken with gcc44 - Added noffmpeg patch to make building outside mock easier * Sat Mar 21 2009 Martin Ebourne - 1.24.1-1 - Patch for gcc 4.4 compilation errors - Upgrade to 1.24.1 * Wed Feb 25 2009 Fedora Release Engineering - 1.23.3-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild * Sat Jan 24 2009 Caolán McNamara - 1.23.3-3 - rebuild for dependencies * Mon Dec 15 2008 Martin Ebourne - 1.23.3-2 - Fix permissions on zm.conf * Fri Jul 11 2008 Jason L Tibbitts III - 1.23.3-1 - Initial attempt at packaging 1.23. * Tue Jul 1 2008 Martin Ebourne - 1.22.3-15 - Add perl module compat dependency, bz #453590 * Tue May 6 2008 Martin Ebourne - 1.22.3-14 - Remove default runlevel, bz #441315 * Mon Apr 28 2008 Jason L Tibbitts III - 1.22.3-13 - Backport patch for CVE-2008-1381 from 1.23.3 to 1.22.3. * Tue Feb 19 2008 Fedora Release Engineering - 1.22.3-12 - Autorebuild for GCC 4.3 * Thu Jan 3 2008 Martin Ebourne - 1.22.3-11 - Fix compilation on gcc 4.3 * Thu Dec 6 2007 Martin Ebourne - 1.22.3-10 - Rebuild for new openssl * Thu Aug 2 2007 Martin Ebourne - 1.22.3-8 - Fix licence tag * Thu Jul 12 2007 Martin Ebourne - 1.22.3-7 - Fixes from testing by Jitz including missing dependencies and database creation * Sat Jun 30 2007 Martin Ebourne - 1.22.3-6 - Disable crashtrace on ppc * Sat Jun 30 2007 Martin Ebourne - 1.22.3-5 - Fix uid for directories in /var/lib/zoneminder * Tue Jun 26 2007 Martin Ebourne - 1.22.3-4 - Added perl Archive::Tar dependency - Disabled web interface due to lack of access control on the event images * Sun Jun 10 2007 Martin Ebourne - 1.22.3-3 - Changes recommended in review by Jason Tibbitts * Mon Apr 2 2007 Martin Ebourne - 1.22.3-2 - Standardised on package name of zoneminder * Thu Dec 28 2006 Martin Ebourne - 1.22.3-1 - First version. Uses some parts from zm-1.20.1 by Corey DeLasaux and Serg Oskin ZoneMinder-1.26.5/distros/fedora/zoneminder.logrotate000066400000000000000000000002511225361755400227120ustar00rootroot00000000000000/var/log/zoneminder/*.log { missingok notifempty sharedscripts postrotate /usr/bin/zmpkg.pl logrot 2> /dev/null > /dev/null || : endscript } ZoneMinder-1.26.5/distros/fedora/zoneminder.service000066400000000000000000000003631225361755400223560ustar00rootroot00000000000000[Unit] Description=Video security and surveillance system After=mysqld.service [Service] Type=forking ExecStart=/usr/bin/zmpkg.pl start ExecReload=/usr/bin/zmpkg.pl reload PIDFile=/run/zoneminder/zm.pid [Install] WantedBy=multi-user.target ZoneMinder-1.26.5/distros/fedora/zoneminder.tmpfiles000066400000000000000000000000571225361755400225410ustar00rootroot00000000000000d /run/zoneminder 0755 apache apache ZoneMinder-1.26.5/distros/redhat/000077500000000000000000000000001225361755400166275ustar00rootroot00000000000000ZoneMinder-1.26.5/distros/redhat/CMakeLists.txt000066400000000000000000000103271225361755400213720ustar00rootroot00000000000000# CMakeLists.txt for the Redhat/CentOS Target Distro. # Create the zoneminder service file configure_file(zoneminder.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY) # Download jscalendar & move files into position file(DOWNLOAD http://softlayer-dal.dl.sourceforge.net/project/jscalendar/jscalendar/1.0/jscalendar-1.0.zip ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar-1.0.zip LOG jsc_log STATUS download_jsc) #message(STATUS "Log of jscalender script was: ${jsc_log}") if(download_jsc EQUAL 0) message(STATUS "Jscalander successfully downloaded. Installing...") execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar.sh WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ERROR_VARIABLE unzip_jsc) message(STATUS "Status of jscalender script was: ${unzip_jsc}") else(download_jsc EQUAL 0) message(STATUS "Unable to download optional jscalander. Skipping...") endif(download_jsc EQUAL 0) # Download cambozola & move files into position file(DOWNLOAD http://www.andywilcock.com/code/cambozola/cambozola-0.931.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/cambozola-0.931.tar.gz STATUS download_camb) if(download_camb EQUAL 0) message(STATUS "Cambozola successfully downloaded. Installing...") execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cambozola.sh WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ERROR_VARIABLE untar_camb) message(STATUS "Status of cambozola script was: ${untar_camb}") else(download_camb EQUAL 0) message(STATUS "Unable to download optional Cambozola. Skipping...") endif(download_camb EQUAL 0) # Create several empty folders file(MAKE_DIRECTORY sock swap zoneminder zoneminder-upload events images temp) # Install the empty folders #install(DIRECTORY run DESTINATION /var DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_WRITE GROUP_READ GROUP_EXECUTE WORLD_WRITE WORLD_READ WORLD_EXECUTE) install(DIRECTORY sock swap DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder DESTINATION /var/log DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder DESTINATION /run DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder-upload DESTINATION /var/spool DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY events images temp DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) # Create symlinks install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/events \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/events\")") install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/images \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/images\")") install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/temp \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/temp\")") # Install auxillary files required to run zoneminder on CentOS install(FILES zoneminder.conf DESTINATION /etc/httpd/conf.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) install(FILES zm-logrotate_d DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) install(FILES redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(FILES zoneminder.service DESTINATION /etc/rc.d/init.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) # Install jscalendar if(unzip_jsc STREQUAL "") install(DIRECTORY jscalendar-1.0/ DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/tools/jscalendar) endif(unzip_jsc STREQUAL "") # Install cambozola if(untar_camb STREQUAL "") install(FILES cambozola-0.931/dist/cambozola.jar DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www) endif(untar_camb STREQUAL "") ZoneMinder-1.26.5/distros/redhat/README.CentOS000066400000000000000000000054211225361755400206430ustar00rootroot00000000000000================================================================================ NEW INSTALLS ================================================================================ 1. Unless you are already using MySQL server, you need to ensure that the server is confired to start during boot and properly secured by running: sudo service mysqld start /usr/bin/mysql_secure_installation sudo chkconfig mysqld on 2. Using the password for the root account set during the previous step, you will need to create the ZoneMinder database: mysql -uroot -p mysql> create database zm; mysql> grant select,insert,update,delete,alter on zm.* to 'zmuser'@localhost identified by 'zmpass'; mysql> exit; mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql mysqladmin -uroot -p reload 3. If you have chosen to change the zoneminder mysql credentials to something other than zmuser/zmpass then you must now edit /etc/zm.conf. Change ZM_DB_USER and ZM_DB_PASS to the values you created in step 2. 4. IMPORTANT: Edit /etc/php.ini and put in the appropriate timezone for date.timezone! 5. The ZoneMinder web interface is disabled by default, you will need to edit this file to enable it: /etc/httpd/conf.d/zoneminder.conf HINT: Most users will want to simply delete the line that says "Deny from all". 6. Configure the web server to start automatically: sudo chkconfig httpd on sudo service httpd start 7. This package has automatically configured and installed an SELinux policy called local_zoneminder. A copy of this policy is in the documentation folder. 8. Finally, you may start the ZoneMinder service: sudo service zoneminder start Then point your web browser to http:///zm ================================================================================ UPGRADES ================================================================================ 1. Add the mysql ALTER permission to the zmuser account: mysql -u root -p grant alter on zm.* to 'zmuser'@localhost identified by 'zmpass'; Since this is an upgrade, the assumption is that the zmuser account exists and already has select, insert, update, and delete permission. 2. If you have previsouly changed the zoneminder mysql credentials to something other than zmuser/zmpass then you must now edit /etc/zm.conf. Change ZM_DB_USER and ZM_DB_PASS to their appropriate values. 3. You will need to upgrade the ZoneMinder database as described in the manual. Only if step 1 was succesfully applied, may you run zmupdate like so: sudo zmupdate.pl --version= If unsure then run it this way: sudo zmupdate.pl --user=root --pass= --version= ZoneMinder-1.26.5/distros/redhat/cambozola.sh000077500000000000000000000003051225361755400211330ustar00rootroot00000000000000#!/bin/bash tar -xvzf cambozola-0.931.tar.gz mkdir -v cambozola-doc cd cambozola-0.931 mv -v application.properties build.xml dist.sh *html LICENSE testPages/* ../cambozola-doc rmdir -v testPages ZoneMinder-1.26.5/distros/redhat/jscalendar.sh000077500000000000000000000002201225361755400212660ustar00rootroot00000000000000#!/bin/bash unzip -o jscalendar-1.0.zip mkdir -v jscalendar-doc cd jscalendar-1.0 mv -v *html *php doc/* README ../jscalendar-doc rmdir -v doc ZoneMinder-1.26.5/distros/redhat/local_zoneminder.te000066400000000000000000000062701225361755400225120ustar00rootroot00000000000000 module local_zoneminder 1.0; require { type unconfined_t; type kernel_t; type init_t; type auditd_t; type mysqld_t; type httpd_log_t; type syslogd_t; type httpd_t; type initrc_state_t; type initrc_t; type var_lib_t; type udev_t; type mysqld_safe_t; type sshd_t; type crond_t; type getty_t; type httpd_var_lib_t; type initrc_var_run_t; type tmpfs_t; type dhcpc_t; type v4l_device_t; type file_t; class sock_file write; class unix_stream_socket { read connectto }; class lnk_file { write getattr read lock unlink }; class dir search; class file { write getattr read lock unlink open }; class shm { unix_read unix_write associate read write getattr }; class chr_file getattr; } #============= httpd_t ============== allow httpd_t auditd_t:dir search; allow httpd_t auditd_t:file { read getattr open }; allow httpd_t crond_t:dir search; allow httpd_t crond_t:file { read getattr open }; allow httpd_t dhcpc_t:dir search; allow httpd_t dhcpc_t:file { read getattr open }; allow httpd_t getty_t:dir search; allow httpd_t getty_t:file { read getattr open }; allow httpd_t httpd_log_t:file write; allow httpd_t httpd_var_lib_t:lnk_file { write getattr read lock unlink }; allow httpd_t init_t:dir search; allow httpd_t init_t:file { read getattr open }; #!!!! The source type 'httpd_t' can write to a 'file' of the following types: # squirrelmail_spool_t, dirsrvadmin_config_t, httpd_lock_t, dirsrv_config_t, httpd_tmp_t, dirsrvadmin_tmp_t, httpd_cache_t, httpd_tmpfs_t, httpd_squirrelmail_t, dirsrv_var_log_t, zarafa_var_lib_t, dirsrv_var_run_t, httpd_var_lib_t, httpd_var_run_t, passenger_tmp_t, httpd_nutups_cgi_rw_content_t, httpd_apcupsd_cgi_rw_content_t, httpd_dspam_rw_content_t, httpd_mediawiki_rw_content_t, httpd_squid_rw_content_t, httpd_prewikka_rw_content_t, httpd_smokeping_cgi_rw_content_t, passenger_var_run_t, httpd_openshift_rw_content_t, httpd_dirsrvadmin_rw_content_t, httpd_w3c_validator_rw_content_t, httpd_user_rw_content_t, httpd_awstats_rw_content_t, httpdcontent, httpd_cobbler_rw_content_t, root_t, httpd_munin_rw_content_t, httpd_bugzilla_rw_content_t, httpd_cvs_rw_content_t, httpd_git_rw_content_t, httpd_sys_rw_content_t, httpd_sys_rw_content_t, httpd_nagios_rw_content_t allow httpd_t initrc_state_t:file { read write getattr unlink open }; allow httpd_t initrc_t:unix_stream_socket connectto; allow httpd_t initrc_t:shm { unix_read unix_write associate read write getattr }; allow httpd_t initrc_var_run_t:file { read lock open }; allow httpd_t kernel_t:dir search; allow httpd_t kernel_t:file { read getattr open }; allow httpd_t mysqld_safe_t:dir search; allow httpd_t mysqld_safe_t:file { read getattr open }; allow httpd_t mysqld_t:dir search; allow httpd_t mysqld_t:file { read getattr open }; allow httpd_t sshd_t:dir search; allow httpd_t sshd_t:file { read getattr open }; allow httpd_t syslogd_t:dir search; allow httpd_t syslogd_t:file { read getattr open }; allow httpd_t tmpfs_t:sock_file write; allow httpd_t udev_t:dir search; allow httpd_t udev_t:file { read getattr open }; allow httpd_t unconfined_t:dir search; allow httpd_t unconfined_t:file { read getattr open }; allow httpd_t var_lib_t:lnk_file { write getattr read lock unlink }; allow httpd_t v4l_device_t:chr_file getattr; ZoneMinder-1.26.5/distros/redhat/redalert.wav000077500000000000000000001305341225361755400211610ustar00rootroot00000000000000RIFFT±WAVEfmt "VD¬data0±   üÿþÿ úÿìÿòÿ.4Öÿ¯ÿÐÿSÔ%×úæÿ ÿÉþ)ÿòÿÀ7/»iÿàþwþ$þêýäý6þøþOB<CíþAþþ^þàþwÿ¨;²ä¬ ÿWþþ^þéþwÿÉÿËÿ—ÿjÿ‡ÿ8.ŽÀÓÿÃýü=ûfû†üXþtgËSÝ}|Oþ|ürûoûmü&þ+[ñÅûÑŠ`ÿvþàý¥ýÉýLþ&ÿ:Q%w)O) ÿ9þÖý×ýþyþéþpÿ!í\:pÿÐý¿ûPúïùÙúùüäÿéG_äô ÕýûRùíøîùüÿA ÝUOþËû×úHûÑüÝþÆ €@ìcªÿ3ÿ’þàýTý ý_ýþýþR–ÓÓ9£ÞèüGú"ù úèüýøÆLÞÿõû*ùô÷Søéù,ü”þ¸XSž7,¥åþOýJü'üÿü¤þ©€­é=ôx0ÿ\þþGþæþ×ÿ8GìüsƒuˆÈÿ ÿ þªü!ûýùñù€û¤þ¶’ - óê&þÚùööùõäö^ùØü¬0ÃåV0ï]þ`û´ù°ù0ûœý)÷Éèß$îÿ&‚´‘&°ÿwÿ®ÿ[WXBÒÌ_Ðþmýzü,ü¡üßýËÿ#{Ifl†fþÀûúzùÇù¨úàû^ý)ÿ1&}¬rSþ$üJûúûÉýéÿCƒô²­¢[Ýÿqÿnÿ ’Vcðþ?þþ\þÐþWÿóÿº­¥[ˆ û¦eÿþþþ5þyþ±þÍþÎþ½þ­þ´þæþNÿßÿyꜿÿþVýeü ümü†ýÿ²ô‘zWÔßÞ ÿ=ü4úšùÅúŒýDäL­ÑA!þÆú=ùãùGüZÿçª-vÿPþþ¬þ‹ÿ6mCçÿ! šÿÚþþý<ý±ü_üCüpüý]þM–¥ÖºS¶ÿëýýLý4þlÿ›•SàC|„Võf´öR:*&ÈîþûW÷¯ôñóPõføuü¶“«µ Z > +V‰û öGòWñó¡ø6ÿÙ û ‡ ÒÔü#ùb÷°÷£ùtüIÿ‡þê]:êâ5³ý¸ù£÷Bøqû¯¤")rþÿùøö[õVõñö ú3þ¥]nYFú{ÿ¨üçúúÛùÙùúÆúSüÑþêÝÉfhòþü§úû6ýy   ½ ÿ#ˆžàþÊüÂûüuýµÿí£,ÿ±ü)ú{øø3ùÄûAÿµú#õÿûjøTøæúÿ1=$þzúŸø'ù­ûÿº•ú­ÿ£ý¯üPý™ÿ&1¶ ² u Þ zgõû>ùÉørúý1Æ…o~ÿLúuöõ¼ö9ûã{h1ûÐ÷±÷¯ú7ÿžL_üú¼ù]ûñýT²Ò×ÿÜþsþÊþðÿáxUÔ " ‡ ¹&ñý¤ù§÷²øxü¶¤˜ Ÿ ÆÉü„ø#öëõ‰÷OúýzÎ5„«Ê@ÿµüòú¡úþû£þžÌY"ÉXþ°ü üãû\ûàù°÷çõöùÿß_“)ØM Ïåý÷»òÕñ·ôÂú^\ ª Þ MVÿšúÚ÷ãöôöV÷Ñ÷¶øú¯ýÞVù ¼ ô ’*Ûûölòññ õû‡ “ Ô æ@ÿ6÷'ñ®î2ð÷ô—ûšÝª !{ øý„÷-õ·ö}û® Ü Ÿ ¨´Èþ˜ýmüÆúÇø6÷÷³øü:²ÇhÞj:—üù³öAö<øJü7_I>týŒø^õçôe÷Xü¡¶ï õ 7 Kìý÷„ôö—û5 š æ §ê þûåûíý6€LÛÿÔýÚûRúuùuù‰úÐü•m¾8=¢6TþÌü0ûJùl÷ZöÕöù®üƒžˆy}¤¨Ãëþ9ù¤ô-óÛõ#üß Š0p écÿ%û±øú÷¾øÀú½ý14±âצýúÑø“ú¸þƒñ´È]ÿ;ýðüpýŽýŒü›úÉøjøUúdþvôz c íþÄÿSügúQúêûžþ‡¦9zvýýúËù*úøûØþYëÎ ¸l´ý.öðžíÄï-ö'ÿ]¬¾0^  ý¹öñï¨ïûòøÆýý-2  » Á ¶ˆãü@öŒñrð óiúÞv çê” =þúô:ðlðô‰ûr 5 ë mjþuöñàïâòÌøžÿ‰– » v <  Ð+|³ûø­öÒ÷û5ÿCnmA æ*ÓýþhùzôÖñ¬ò!÷þ€( y  „¨ûO÷7õ†õè÷©ûÅÿr]Ãûø“öá÷ûÿÿ»§µ¾âݤ‰»ÌòÜPÿÅþNÿ³° va ð F³ýõöìñfðüòªø-ÿ•àkeýwüïý¬Óæ“ÑüDùF÷9÷oøÇùrú|ú±ú üûþ7- ; f DšýÁÿ4þWþ•ÿ´%BŸ39Øçï”ýeøõ%ôö¿ù­ý\Làƒ½"Bü¡÷5õþõWùIý£ÿ3ÿcüîøÝöp÷”úÿ„³T¥¦îKÿ~ýSýÿ^FÇ '  q G ™®Cü÷Þôñô‡÷hûûþ† ŸÅ!Äùþôûøø½÷oùãý~ßú¯ÿè÷éð„ìºë­îÑôýd‹Ì ¥^ø—óðólùc dDþ… ê‰ü/ö ò¥ñ{òhô÷úæþÛª9 y Î H ùêñêì€ë±î„öö —aîOõ®éCå”éÖô¿[µõ ø#ûßö÷Aûüf ”×/ (üô{ðÑð¸ô%ú¶þ½ìÿ[ýåúDú:üLúl3 ÐÞÒû€ö‡óØóm÷Fý¯»æ ™ løÿ?ûíø±ùý„zª¬ño2}‰ÇE – 1 }ÿ4ö2îêÞìºôFÿ‹# ¸ ‚›ýt÷IõQ÷öûÝå³nÿ¿ýmü9ûúrùÌù£ûÿaÎ+ Š p øÔ,ýeøÍõOö&úÄÚœ(ö2û kn÷oìÕäsâüå°îfú•Ư-þ‹õ=ð{ïómù¨¼B ¨ 2Äþ¼ù2ö€ôëô†÷(üJß[ Åx ‹zúöôÚó{÷¨þG,º«…Ø™üÁðç‡â°ä:íù±¿ d¤ ¤­÷öï¥ìnî¢ôŠýÕù…õ6üoñêtèYíJ÷hjº“a%³þÕ÷+ôäôQúŽwœ.¯¯ Ûþó¸êìæÎç”ìäó"ü¨ : å _¡þ ÷2ðŒëVêDí6ôôý,ꂪBMùÿî3êÚì0ö[·˜ÿþ÷Qõm÷ˆý o†Ãt &Šõ5î¸ëîbó3ùý›ý ûùqøûœÑº  ’ ü¸ó­îsîvòø*þ–„ÕÍK/PÝ= T?lþ8û×ûØ 5D]üîòjîGïô&úãþ‹ßþ û÷%õ–özû€\ ¤ · b êùPò°íÙë3ìîñÃõ`ü®I êQY° ¹§ø>óçòo÷iÿáöÿÎ5Ç%¢ú…òîAíyîãïNðóïTðNóËùÞÒ 9–² úþ›ô¹î–ï‘ösæc ¨ ùò©ï§òÜù ¾ â%Ï â‚ù‘ò¼ðrõž òæ  è# þhônðÕñeöûGýèû‘÷4ò.îIí ðõôû<Uãòÿ·ÿÙÿ„ÿ9þ2üNú£ùëú@þ©÷ $L›Œ[\ÿçöªñŸñ'÷²H ¤où0oÿü÷qòï îSïÐòø÷´ý{Æ› ÿFø]ñ‘ì”ëäî™õ§ý‘FÖÈëýºøˆö±ø%ÿX²eZùrY´þÞú¡û ý ß í ò ó©=‚þmúiö=ó¤ñÝñ‰óþõ»ø´û ÿè/`ïjþ2õAìþæ›çèíN÷þÿ¹Qû»øûZ” «^SÜ:^öðFñ“ùqÓ€n ®€Æó çÈásâéÆóÈÿ h† .úÌò'ï‹ïó ø–üYÿ®ÿ¯ýôùTõ¼ð9íöë"î‘ô*ÿ{ ¬ #¦%Áˆ•ûñ•ècèŒñht³"+Q*u ¢žûüè ܰגÜÁèEøa)Éþ …ÛúÏô–ò£óXöûø‹úüúçúáúûÚúîùVø÷Ü÷ü?@à% wþ÷gôLö;ûŸjÇ&TM®­öáå½ÚSÙ}âSó< GŒ· ßûÀìAâƒÞ¦áê+õëÿ¹Þ ëÖÏúÃóœðSó"ü# ¥F ƒ"7J6pôWð~õ‚_0$ú ¯•þôàçÁàJàtå5î=øÉ — šþÆö”ðí îEñõô+÷óöúô2óµó÷ þ$ À  ß Øß¥EeáþÎþÈë s.›$.#Fy %úkêüßÝ áåë øWG ¯ wFþ,öøïììzíñJöûNý3ü'ø óŒïðsõÿu ÚÏ.bˆô8ìÿëõR^h(Š0m.#^gò¥êhé=íàó ûö„5)ÿ"úÇõCóIóÇõðù{þL©ý‡ö­ïÍêêhî÷Qq±ïßMüÅñ1ëYêâï>û¦ +)”0/ë#ˆƒü‰êiàdàhé÷­0¨< $~ù"ðþéèYê¬ðõùS læ÷¡éá–âüìÚýá_@"Nc–ÿïïÿå¹äÁì%üËm#)õ(êŸû¾ì&åÙå-íÊ÷T k ž ²ýeøvó5ïBìŒëôíºóü¶& ü a šÀø¦òDòpøjmÅùEîêlïÊýX)$ÿ/]0/$YåôßèÒDÓûÞmñNÞÑ%¨ €þHõ¿ïuí¦ì¬ë1ê~éšëÖñ—û1 l ^C$þbc ¡ IýN÷¨öŠû£«C^"@&s$ G Lú!çÑØ©ÓmÙ\èžû sz¥R÷Xì:åóá¿áðã7ècîçõžý8Ù Û ¥ N õ ¬  ¢÷Äü§ù–ù½üµÒ ,n§$n'œ#RÙÇò á¸ÖkÖþß2ðô0xØW¯÷¬ë›ánÚ!׵اß<ë+ù ‰É` (\ _q8 Ræö òvõÿ/ "6(x.L-œ$±ß$ï×Ý>ÒÏqÕ×ä úÙ; >&ÄuC÷³àšÐHÊàÍëØ¹ç¹ö=« NMÁ Î;ÿþÙô ¨ñȪö+ïMðŠúi 4{-²4€1U$DúçÛTØ[Þ©ê–ùN©¯Ç·÷èwÚ°ÑhÏßÓèÝ\ëÂùÔОRð ˆx¡ý«þ²ªN / V 6òjv •t $Ç!qÓÐõäêØZØpãS÷ß £Š& ö”ÞÍÊÅIǃÏÛ:çJòóû$ €«œË¢’«þìø–öÔ÷çû©0`щ$Ñ(§(¨"ìZ7÷êÝâÄâ{ébõÍg$÷!11Šé°ÑrÀ º˜¿˜Îôáô¢³ìˆZQ *S~÷ ÿîô»í$ìNñ¿üŽ À´,È5D6Q-(õè`ãÞæðÑûw™¢ýx qÒ'"íÚ2ÌuÆÉÒÞ(êÝôûý V?f  †þ)óëºè1íf÷úå–&ñ(±%„ØgûÊô½òšôÓøØýÑÅ ’;/§õAõFè1Þß×.Õ³ÕdÙ°àíënúõ ö¯° –ï•áü۹ߴëÝü@‰),-*i!O ðþ"ø^õýõ²ø'üÿÜp  &òxX Wìöôë1ãŠÝÛkÛxÞzäÒíQú‹¶I:G¿côdââÖtÕ&ß%ò( r!O2©8K3T$Ž»ýð"ë£ìYò\ùþÿ*ƒ p" ï 4þïðøçõ䢿Èé\ëxêøèLê,ñ–ýI ~«‰ œüÝï©è¾ç½ë|òQúÆ Irý‹e ‚ ÿ7ÿZûtþÿ‘ý•ý@•­ Æ Ð²÷÷ëÅãÁàÖâ´è”ðÑø Œ|Ö(ŽNDv'_–êþæþC5eÐþiýMõÃÃÓò"qCúñCí4ïFõuüˆX«þJø=òiïDñ ÷yþ ¸Ðýz÷ðô÷ ý’!¸Å € û§÷Œðí·íâñ;ø2ÿ¤4 $»—k€¸£Ëùôî|é±ê ñÔøÿý0þïùEôñ¯òýø³/üiõîòpö.ÿw âåÂ~÷÷µìóäˆâQæ®ïbü) ËN eý :–A ýŽ÷ÍñçíðíŸò¥úÌÇ +÷ÿçùöõ/öæ÷9ùúoûLþV< Ã&–©§Ò ŸûÂìÒà[ÛœÞjêÙû ÜÓ!EE] Ûþ§õïÝêÌè€éÍíÊõ ¨H ÊÞûûŸ÷~ôjòóñ4ôìù‰÷ {‡MeÐ Ïwj÷ê“Þ Ø×ÜÔëÁœ&¥(?ï ½úë â àïãìËöºÄ FÔ^ûóñÇöšÿ>È >ÿØøß÷þÌ þ –"¿ˆ œûŸêÚÞ)ÛEà–ì“ü¸ à‡Œ oüñ?é£åËå×èîTõ+þÂlº ( Ãþ!öèñ+óùKè• lI d E| –ñ§hnò¥áSØ Û×éìÿ@#ª$ l“òŸàÄÔ>ÐӛܗëŸýǨ { Ýþøù´ø»øù÷&ö÷ôÜö6ý2£ñú$ØW³ õÿ¦ò–å0݈Ý)è×ú!d(“#þç‰ÔÊ&ɈÑâà§óÏÎ}›¡óÎ túþÅùdôIïêë;ìµñqüÀ |÷$/*¾'K=÷Öó[ë¸èÁë7ó/ýpÐ~Y3ú†ú$ïãåàÈÝ¬Þ âçaï­ùñy|ü â•õÂéVäæåí!÷Òx cY Ðåí ”þ´ôïOï›õ› Q5þ«?û·î’æ{ä²ç€íŒòˆôcóYñÌñm÷†f: !Ó —úaèâÜhÛôã™óÍòz x#²ä¦ö íEë?òÝÿux¢·Þ|'ôÀì¦êCì]ïcòèôJ÷êù±ü+ÿ¾ –ò \b3 çìóGèõá=ãÔëù;¯G¦S aü¾ïêæåäëXúª X'å& ÷ ¨ûÃíæâä¥èÏîùô£ùŠüþ;d V† À ’úÂèéܾګâÔñu´ç* ‘ûê¤ÛlÔ7×äŸ÷e †2$Ý"ô7Ñ4ý6ö)ðõêç$ç§êšñyúˆÁ þ4 B$µ#j&„û‰éñÝåÜAçú³c!®)Ù%œÝÿçLÔãÊUÍéÙåëmýÒ QS ± ÷ l N •øì=â“Þ_âmììù×P.#ü$ô!¿@ ÿÌòeì$îæ÷û÷#Ž'À"p¦çîýÞˆÖËÖ6ÞéÑò‹øËùÎø ùý޼ 7±Ö¼ö„é÷álâêõ4› ûÜßïR‡Mýýëøðùì  ')á!aí§ò…éÏçÄë‡ñíôuóqíååáVâ©êé÷ϲdT ‹Çõ&ëùåýç ñÿ­nq!y‚òèDèAò”+š'Ò0 1à(¡£ tù:íçjçÙìEôûùûóöQïäç~äFç ïeúS ¦ þ ÷Šó½õ0ý²Î.Ç *û(öõÄ÷2ý5¼ Ý·a UŠÔ ó±þ+ûHú¥úúvöï×äÒÚÌÔùÕrßgïNg5 FþÓó#ð‘ô%ÿ ¼U};†ü·óñŽõV Ž!Ç!gÚŠ]÷‚ñfò7ù.z íW)ù)èÊ×…ËQÆÊ"×¢ëªÕ¿(Ë,“%7RäõïÚðÇø|‘ ^ ç˜!üÜúoÿ ·1±!`/¤‡ö ï)ïóõP ò $ ¨÷ké&ÜNÒÔÍðÏÍØCçËøû _}“¸mºGþ±úBûÿdc   ¤  : k a( Èïû;ó¸ïÓóÖþÄ ÛúÓØ€ïaÜ"Ï(ÊeÍוä`óå D¡sF L™ýxúúÆû•þìÛï «ñ¡"£)f,±(–! ƒ÷yä ØáÕíÞÇð@ë#°!ÓÏÿáç²ÒìÄFÁ'Ƚׅì`f"ÌâØÂüÄô¹ðYðºòïö{ük ’#"Ú,¿3 3T* úÿïç¦ÖjÑðÙ]í ÆÂ!èÁ ¯óÝÛÜÊÄÄXÊ9Ùæì%t<tƒ;PÆ&Ò u Ä ¹–üÇõHósö¿þ_ à" +//q+D¾ Öô»à±ÕY×¾äMøë @¶ Ãýè=Ô·ÈÉàÔÑèÿA„â!· âýwòËî'ô½b!í%Û%w>¥ù'ò;ñMöò Dg•› ÏýríÜ߃×DÕ&ØÐÝ’ãŸçíé-ìÁð'ù´cH}CõPìõê¸ñMþ- #"&3-Ô/}-¸%щ9øì©ç„ì>ùÑ Ï+!Ö°n¥í5ÛÆÎÁÉBËæÐ)Ø™ßQçvðýûm c:– o ÐøºéæáÆãðî£Üs'æ4¾:Ï7|,ôÒõµë‡ë õåù #Z Õó àKÒÇÌñÎÖbÞå éëKí¾ñ$ùf -– ‡ä÷ñ¤ïSõä~''8Aj>Œ/=Åû1åšÚsßáñ" ç!Ï-*§ûžè3יτÐÖüÛDàBãç´í°÷|?e÷ ¡ýãñ&èÛâhä…î:ùx3®FôL—BÜ(dƒåÂÐîÎ(à‹ýŸÕ.0 äçsÔZÎMÔà5ì^òòÙíJê`ëæòÛÿòÕ’"Ñ /ñóÚä[Ý'àmí¿†í1 @ÁAU5?²ÿ8åðÕvÖÆåRýxqç{ ‘÷%ãJÖÓÏ×£àúéÐñÌ÷qüB€aZ è õSöËýõéòSùÜ·Í"n&a F7pø&óyôÝù•ÿnÿòû]õŽïøë¥êwêüégèæ~äOåµé¿ñiü(kÒ #¢$D †ëTõJéçGñfš4 >ò7$ êïPà2ÞPèùôWò âåצÕÚáxè*ì€ì6ëíêöíŽõ›³= «,2|-=d…ïòÞœÜöêkM&@KED.kÚõ ãYÜÔàrìéùF iÞÿ¥ôèCÜÓûÍêÍýÒÜç\ñÿøþk ÷î!Ê.®4 /jˆðì§ßpâcõÖƒ1ÕG O#E"-ã Ýï—ÚkÒR×#åPõw²® øžìeâÚÕÑ4Ï¥ÐùÖ›âòV )øwÒµµ‡^ šÿEù‚úhbï-<>"Bi6©´ÿyæ¿Ù­Û è–ö9ÿòýEô¤çâÝæÙ‡ÚÜtÛŠØœÖ6Úæ«øÖ Î× œLb_Bó ÎT€‘øNÝ#"¯ Õý'òºì¹íŽò^÷÷ø)öçïKèBá°Û‡×§ÔµÓOÖ6ÞöëËý¦‚‰ ô¯=nøÞ÷Œÿ šå O#ô šäîù@V( Éüœø>ö<ôöðÚëæÀáZá‚å›ì*óqõkñ0èçÝ1ئÛé­þÏÈ$Î)y#ã° ÿÿþŒ›Wo ' à°šg#ý)úÌúÿÐBžQû>ëSÚnμËäÒäà›ð ý*éüõÅížé\êâðÍü* §t'§,z*¿"%¡³_]_ÈèB ­Êý£üþcÿ†ýÕöîëŽß]ÕRÐvѾ×Óàfêóú}JmŠÿÛõ9êÏá9âÒî•Ë#ð=M±L>¾&¹üüYõ¹÷÷˜ xßÈŸÄ  øîyåúÝ׹РÌËûÏÂÛ÷ì¤ÿfr¦˜$ï®ÞåÕø×ñäú(2+É=ŒG½FÂ;b)c_ ø÷¯ÿJ éåVÎ5ú*ðCéSä˜ß6ÚÕ¤Ò†ÕçÞIí”üwœ ¤òWàjÓÃЫÚfï× @#s5¶<½8v,Öç°B Æò'¿,Ëþâôí’æ~áÅݽÛ&Üêßvçò¥ýØZ lú=êgÚ%ÐgÏÙ¾ìøÊ /†8ï7Ä.T å–hþ‰ÿþw‚"’!b 1ýdðÜçÅã¸âââ4ãÚãÕåêTð©÷Nþ´âuûòfè¿ß…Ü)âÈñ³ª ö1Ä6ï-Ö‹@ü§û $ù)%_FòàäÞ—Ýìá2éÉñQúlŠ9Þÿoö­ëaãáà‚åÊïüýÕ Y »1/JK/ oñ ÀЇ‹ \'æ týAíâÞ ÖEÕáÜÆêû ÚX‰¯ø¹ç]ÙþÑ„Ô}á¶öŠß%°3Ï4b(¦r÷]âåÙŽá,÷R,«8˜5Y%Lüuñpðõgù“ø‹ñXç^ßÞôæöOÇÚwü ä)ÎPžŠÙÏø A÷#ö3øqüµuúºò¢í:í„ñÙ÷Nüíûrö€î(è¶æëXóŒüéPU b ®  âÌÅg|å ;± ñ4¡ éü´ïªåÒá¼äeìFõðûiþ£üønòYíŠéSçç é‘ïÿøÂņ‡Ü‘ }>¼üþú þÆŒU$ä +Úîï!äoáéæUðRøæúA÷ßïþèHæÞèÙî~ô„öô„ïíiðýú‰ Y`%f(#Ìð ûü¢óÝï óŽý !È0B7Á0ØÌÓêÃÚÑ×çàkðêþež€û²ï8æKâYäcêPñ~öÙøùüøyútþm¼ V¹„ 8yýàù´ùÏý[f˜]*Ý.|*90 ¶öbèÊâæ ï¬øgþ þwøÌðÖê?é‘ìNóäúõ9¬MÍüÁø€ôüðºï&ò¦ø÷d îÉdG · ã %ÍDà0ûùôàòjó9ôµó òéðSò÷RþU - 5¹Tü@ö/ñí'ìíðñÏø\[ MW“5B) $1›%,vC÷mëOâ:Þ¦à~éìöÞæ>û°- –ûØëõÝ•ÕÇÕ_ßÿï‰QœÖ Û ûtùrüÙ”« Ýû™ûï¦óKåzÜ`Ücåùô«´ÔVfÖú^ñdë&è¿ç€ê–ð`ùöe ¿ Eÿ¬ôîàï4ûA ýx,®-‚"$¶öîâ¾×ýÖdß¡íËý‘ ËtV ³> $ô…ç¨ÝÁØàÚíä¿õ§ $! ŸûåÒר#ç§½‡2;:3í'þrßtÉlÂEÌäãØž/º3t+ð‹ ãþ÷ëñ#ìäÛäÕlÙñçÊþ£Q&'³,þÍä:ÖÛØAì’ ~&Q96<›.”õ´×è½ÈAá]†% >¼EÛ:"_öëbßhßmçyðõéóéïßí:ñ¾ù†= HþIò±êóì¬úV~&³4Ü4“%¥ ©ëdÑÃ]Ä»ÔÐïÖT*à;¦?O5( EÂî—ß·Û<âÿîcü" šýö ðì—é‡è¬éÞîÐù… .m/ò73÷·BããÊÁËț߃þtT1|8Ñ19!Ø Òúnï(ìÐïm÷yÿþL5ÍüæôfíÐçå åÉéºñDýO p$q''!’KüNçþØÞÕðÞkñÍÊ@(˜*.#ÞúõÕë!êÅï•úmqä §þ÷ï7ã"ÛCÙÝÝ0èËö¬%Ø$'*Q%ö,ÿVçBÖ6ÒÝ2ó¨ Œ ³( $ˆ¸”úõÞõ:úeÿÅ‚ Ö  H(ÿâõ¯ìûå…ãæYíÐø,7q"€'ª!åcõ€Ù¥Å´Á¢ÐÉîäg1Ñ@=)‹ ºñæàïÝ€çEøp W2i˜ ÷ø÷´ïGéµä¢âräœë’øã c*-0z*ÿ,âëɪ½{ÂÙ‰üü"!@šIS;ëñ´Ñ4ƯÒÊðK8,û2—'lŸúÛê9äÀã·äjääjç2òrË+¼1Ä* (ÿçÓÕ.Ï–ÔKåIþ1?;3†’÷×7ÅHÉ–áiA$Æ5W4#1 •òüá¿Ù4Ø>Û&âíÒý×"4-²+¥÷Ëî­ÜÅÕGÛ²êPÿ—$þ-v.x%‡ýÊæáÕÓÏÅÖàè¼0¿%³(ú¹éùÄæ‡ÙÅÔ+Ù‘å(÷ç Jl!' éÉ¿ô'èä¼é÷> •$r&ë Ý~®ûGñ;êçvè·îdù`ázW!õ¸ã¬ÙõÚ*ç¨ù” Äk}4ö£ìéÁëîóçÿi œl!{"Y2Jýû&þAÔ°ý·ô íì°óíÅ 2M[øTèŸà€åÌô3B§açÿAñ&ç´ãç”ðlÿÆÊ#¼/ 0¦"3 òêââ`ð8U0^ 9ü©îÊé½írõšúú™õ–ò²õÿí 6nE =úíçéòûýÓü™pã!™oBöæì«í¥÷m}Jfö­ç„àMã~íaùI‚kþEùˆ÷BûÞý Ò Ý…ùÑëPã&å•ò2 `/¥3ª*€£ñ¿ê,ïùû; Tq@]Eî9àÌÚ%ß™êê÷¨ž6ûÇö°÷¢ý$H mü\ïAåôãî¾N\-ƒ7‘4Œ%B¯ø·èíãë›úR #ëWRÙî—ßbÙ}Ý`éøõž _u•ûûöòõu÷'ù ùòöÚôÍõ üt Þ%x#ÉTîþIû‚û9ýCÿ 79bYü³ö ñjíDí¬ðDöüûϯþsûõöëñFî¥îöô!„¡$ #§m Ž7úËúgc  •¨úÒòÝî…ïJóp÷šù-ù¤÷Ÿ÷!ûu J  Tÿ[ð!ã·ÝxãgóŠ(í)b"!¹û7ôäòMöåüž6 °ê fïø2ñØêÊåâÔà ä³íÂýâÀ g&µÈìdÖ-Îê×"ð¹ &·1ë.£!»3ú‘÷:ø²ùÑúýûKþ]E ÷ ¬ |FóçâõÔ¬ÎÔæ°¹8-†-&eü}ݤË+Ïqçâ +l;v6N Kî ç‹îæýD # û‰ðÂï½úç ÷.!}÷ÂÔ]¹±±¥Ãkë­CÖR¹Bã®åï½V²+ÈÌö:+GP¬Wû?¯ƒèãÌ}ÊLÞ#ü#ÇHx–ôZîö)gá ñðõÎN¶=³(Êåôw$ÓFaN 8 Cß(Â6ÂÊßü¾<êV¦Sú4D‘ÛˆÀ½wÎ(ë/zåGÕP úªsîúèÅÒD­¿sÏÑîü³1Æ<1º0ñ?ØãÓÅæA +1MµSŸBôõ‹Òý¾ñ¾ŠÏ8éýÃy4Œ!§ l—öIèVØnËcÇFÐÌår /,N*E…ýkåÕÚÀã´þ#]D|VRé8KçqÇK¹7¿bÕÜóuv&u.ä(¢È,íêÜÕ/ÖêÝlè·ñA÷Óø;øøõùþ̪@ ê ë¨%d/Œ3˜-äÉtë—×Î×Ð˜Þæóï 2!+.q.P TçÜÍOÂ7ɺ߀ü~6ðêùßà=ÑHÒhä”S ­7HB?Ê2!7ýÿ7ôUë©ägàúß[å¶ñ_ (s.û%’ò×KÇÉÕÛ®øaã#» Å í;Ñ ÄbÌéa8œPœSfA`!±þÓãçÖ@ØpãòqþètÓêÂnþLûøæõ—õT÷„úðý±ÿÿû`õŒíƒçÕæQîþ÷ü+¬<AŸ6+ÊÑãIÐ{Ë^ÖIíß +]%xY󛨓ÊϧäÏX–) #7 ÏñÜùÓäÚCì„ ÚÓÉúwYfPö©æ(ÝžÝéÆüYz%V,á#Ê $íÏ©½4À×rúT1)/¨µô¹ÔnÄ´ÉÁá@÷"i8S@=< 0c êø]æ!Ø/Òt×äç®ÿ5C*y0å(PúßéÉ$ÁÈßÝÀüûÓ-¡-bÒ÷?Õþ½NÑÕô–Z=mO2PŒA)(ß ¯ìGÖXˉÎ3ßÖøòO(Ù/Î(4qþºè£Ú³ÖRÜ­èÖ÷¤R”÷å…Õ9Ï Öôé¨%T=ÌIÎG$8>BÿIáÂÊ…ÁAÉáøþ(AóD±0ë êಹ9Ê_ìA2-Ž3 $\ÛâÇÇx»·¿wÑë´æß3Ø@¯$mþ×+ºP°k¼fÛ"».uMŸY¯PÖ5á½ë€ÏÂþÄçÕ‡ï. ø!é-+üjûÚèÀh¸ƃçð%:®OåKÊ/Æ×·Y«ù¶þÕ>+³Mô_Û]GÞ!ŸõÔ͹´¹°Gžã{ G+<§9Î#œÝöÀ´µ$¿ÛÁ˜'ç@EŒ3\¸æPè°ì³Ó˨ñÅf@ÓW]®PÉ3´ ÝãçÂb²·дõtæ7^?ð0/í[Îξ}Ân×åöª…0q:52Qíõ™Ñܶ#®»ÿÚz1vRƒa±[ØBÀÞñÑËŸ³ž¯ZÁJäE(2D÷=Ï!‰ùMÓ_¼V¼¨ÒF÷×:¡C(8uÎõ]ч·¶®¹PÔFú‹"GDX½YÉH)ïÜêÂTº¿Ä*Þþå)K'·\þ«çöÚ¤Üìå§–.4+/÷ÅØ”Á¸u¿ ×Òù©P?ËP”O <ˆ÷‰ØÈËÈuØæï&Ô  ûùœëçãLå?ï1ÿLj#æ036)0EnÿÚÜ#¿k°u·`Ô¶ÿŒ,MiW‘Io)[—àúÌrÊÖméý ’ F,óåã+Ù~×ÇáÛ÷œ3HcMë>èeøYÓ=»Ù¶>ÇŸçÙ<2˜H»L£>#Öã8Ï™ÇÌ̹ۋïåÉcÏýý7çÂÑÆñÊDâY„/„MvVXF "Ãõ‹Ð†¾“ÄûÞ¸%À;?AÓ7.%ù$ý§îäÜjÖ«Ô8Ù‚åøv 7j·žêÜÍ ½àÃWâ£Â>5[JZS;h :ן· ¶(ÒTÕ.=MÎR-Ag"Ž î¬æÍçªêUéáâ¶Û™Úä÷B =øKâ^ÇK½ Ì-ñ_ 8HRYÖL|'¸÷ÜÏ©¾šÉ)ëµz9õJßFB2åþ+ïoç[ãhÞO×;ÑõÑÞÙô”#<%>öïeÌ ¸U¾¼ß¹K>7V)O×,=þÕÖÆ3ÑÖñÛ:?H³Bõ. N„ñ6é$ä+߀Ù8ÕèÕ4Þší¾ÿÆ ]÷¿ôôߺӘ׹ìÑ W,H?J>ì)w vìÛéÝò/X-@C7¶ ‘œî£ÜÒúÎuÒÛüèø- ã >þ©ì‹ÛúÑÜÔÀäpýŒX+Ž3ü.x | /ü¤ñÀðÉùu k"0,:±8±*…lõ)Ú,ÇÊÀÈ_ÚòÖÔÕôíò·Ý–ÐÖІÞô» |x>IE)] v¯ ©-W2~&h ãÚÀî¯H·2Ôûñ¨)‹#Þ)Äf¾´Î¥íF,$ê(oT àý µP!v#lbd™y ²kyQáXÄQ·ÁÝÞŒ"È*ô úðÖ#Á‘ÁçÖäöÜk#æ"ˆ¨ gÀãÇ#¥"¡Âßzàuü¹ë/Û„ÐÐŒÚôì Û0‰ ©ûçE×IУÔcâ­ôÖšu¶fÁù³?"Y)å-˜-©&hYíŠÙ2ÎÊÎÛfîÊ"†ö€çaߪßJæÃîßôíöZöcöÐù —V¸‘aâw*l35Ž+'xüäâ±ÑIͰÕÀæâù6 ¥þ˜ðåsßÊà2çDï·õèøcùFùÖú-ÿ¾õ i×+$Á*©/m/&'ƒ¼ÆëøÝ8Û&ã9ñ¼þÍÙ‚úîÉåáãaéjóDý‹¤ù†ïóæƒãç7ñM a"÷/-8q913J&,pçôoìëlïàöïýÂôÙû3ôrìÞæá䱿rë­ñï÷+ýÇZg„ýîöPïê¥ëƒ÷×*‡E;TÑOk7ª¢ê5ÏÛÇ-ÕÓïÌ ‘ÁS„ù:ä—Ø·Ùîä•ó†þnüô–íÔìæñ^ù¤þÿòûNú"(Ï? MôGj/6 ­ä²ËGÇA×}óx©6 âô•àÏØà´ñ°žV ,ÿìéÜ3ØßíÓûþÞ ÷ ;! ,/ƒ:„:Z+ìRñàÚ¼Ôßß—õÏ n½ííßpߟë–þ”!<^éð âˆÛÞ•æˆðšøþ¼w»­,8e8c*þßôæàöÜÊé(Ëîø‡Þ©ÏôÒ\ç¨o'3!Cš÷4æ ßáNéÅðõ ÷šúH.$£2à6 -ÐfûÒä³Û?ãx÷a] ï×Í+Õ‚ì< ˆÿ&C ¦ô´å‚áÊæ³ðú%¡Bl qC"oç!§ô”ì8íõ“ÿIVO÷ÎìDç4éññýýšÖ . <üËõŸôù ± { 0 zÿ³ÿâ Ðã«û‡þœö‹ò–òñôÛöHö.ó¸ïïÅó†ýB û& ;ü¸ìâãç ÷*&À2Á.qæü€áJÓ7ØWîw &¤0†( ?÷ ãÜûá¹îú þúºóæï1óýÒ•<FùþñÜôzá„&ý+ö!f 7íQÕ9Ì ÖvïêX(:2Ž)øwø½ärÞ™åFôa9‰ûóð¯êAëûñüª4^¾øy »"ôòæ"ÝÏÙDߨíÿB ##eþdè×Ü4á¼óû ‹Y"×û‚ßVÍÌ×Ü4ú´]4@ð;+'Ìþ!ðHéç÷æ§äná(áèt÷9 X¢©¡þ-è`ÝYåôýÀ0.0÷—ðÊÀ´ÊºgÛ„ M:àWP[ E± ´ú-à×-Ý<êßô÷ó‡í¦í~öæNöÞö;ç$äšñ ¹"¤,¯ ˆ­ÛÀD¼ƒÔ3$2MU=^K‡$3ú@ÛZЫØWëžüè¹ÿœõ:îåï¶úqLj Éý=ìKâçàùžh!5š Né‹ÍûÄ¢Ö5þ-¬RL_éNh(ìúO×0É€Ògëd©ÈÜð1ádÞ‰èùŒa çø[ðÛð¿ùÝÐ %9òeß9ÖŠßfü€$5I¼[ÌSŽ3öÔÞ¹ÉáÍÓæ°!ë'éþ$á1ϨÎÇÝLôã$…møÎðŠî!ïÂî‰ëmç”çFñ6D"Q<ùIŠD®,Ê °ëÛlÞ”òo "š(æeéVÔòË.ÑYàZó6ÈFò {•ñ½âç×ÅÔ®Û…ì)ñ?0µ8;4í$„;þôœôþ‹  i8ô@èÓßSÜþÞSèøöÜ(ÁñØ#űÁ@ÑMðXm4¼CS?t*õè+êRø3 ÈÎ!Œ1S (ÿ=ùÛñFé‘âíá¶éø˜P …›òߨéÄuÁ9Ó.öÃŒ>SJÎ>à!æÿSæÞIè_þÅÛ$ú&ÀBd^û>úïûÓü;û/ø5ö÷ÉùDû¾÷ºí¡ßGÓQÏà×=ì¾ ~-7.`"jºüàð`ïÎ÷TT†Ö!PÊtòýñùÍùwûìüRý ýÍüuüúmõFìáuØm×áÚó šï!2" Iùpí!ìõÖŽ†é§&²– )bqýÖú÷ù”ú”ûYû…øÐòië·äjá_ã¸ê«õ ^ r GCýö ñPñnõ‹ý{U Ñ$%(víðü6öîôrø‹þ&4ŸùƒëãÝ,Õ‡Õuà§óX 73 ýñ©Þ}Ö$ÛÊê+×—# *ó(#‚+Y fÿ–øJõa÷çþ­] …þ³èÓ-ÇùË á uY.F*•ÇòÔBÂqÃ’ÖÈô_‘,o8ð7â.u"ªä S²û@óríÌí[öq2Å3oíúÖÃÍpÖ›í —5'ô>ØÇÆëÅ Õ`îŠ ïi.%5Ö5°1’(Ù1õðëßÙØß]ò ØR&ê ûöý´ïÈê°î™ö=ü0ûóCçkÝÚÐÞxéÖõ ½» tF +£6$9£-¼†õ ÛJÏBÖÛëÔíjÊ ÿÙýçz ¹ Ùïû×ÔÆ§ÂIÍ â«ú7 pÉa ; ˆ ÏU&D0¡.)KìÔÛ…Úsçuûë ¶¯ÉÊsÚ ^ Éô¯Õ¼Î²²¾–ÛYþÖ%å áõþ½³C ¢#êb yöÅéUç/î‘ùô û š Ü{ø¤CÙú%áßÈκû¼ÀÏ/íO 4 *&û/ÆÿîùÕþ r6 ß÷¥èæá³å)òªúA FŠ1c•÷ÜæHÕ;Ç&ÂÊãÞ©û†D)a+ºe ‡øšñ\øÔÕ“Gmî³ÞÅ݇ì«Èd(^&wRø+ò/óŠõDóêZÝXÔMÖÙåÍþ•È%’# ›ýIï{ð"5 (Ÿ%níKÑÊÞÍ·/üGÓB"ƒöÓpÇÂÕ!óù ìS zöâxÜ¥èûþ.Xu ùôë7î…4„.C+¶gæðÂŒ¸Ðr;aÀbÄ>p¯Ì#®Ä²Ôªÿ !ß+È ãøóÛø©Òaûí²ãùèØþ ‚2Ó1´^ç?»À§ º}îï0AeKtãVw8Ö´¨¡—½°ì[¾,Œ)ifwþ‡ ´¿´ò¸ãáâžóp.$A&?ç ÀO®´½Iëô%ÕUûfSR¤ _æ]º¬™½ ä‡ ³)k0ö#3Âúqñ|ó†üg @ ‡úìþŠúõópêà»ÙÊÜì>µ 4Â6&ýå\ÍìÈÙ½öÀ*-|ÈMó¿çòéñ÷r  ë$ù Kßùüà¡Í²ÅEÌnàýA/5ù)"PòN׬ÈïÊÝ+ù1W+²2÷*™nÿsêhßìáwñZ Ï!ý1µ2,!ÌûÚ~¼ì°ï½]àÌ ¯3fG™A€% þÙÃ<ÀÏöè™åS-ß1+Û{Ûð¥áµÞ‰êBd1Ä3Å äü=ÔK¶ª¯‡Ä=@tJB8h`èÉÉÕ¿LÊCá3úh À£d"Ç$8#&ë<ôå+äXô‘Ÿ(J1í!UþôÔ}¸ê¶rÒ‹, EÊ?ý³óþÌ߹澫ֽõ–_ $Ý b$ŸóöGþ ý¹H «»"?ÿèèQ×èÒ3ßµø¢+±0¥"+1åüÊÀ(Çݤù˜L#%Y¶ üЇ *JH”Iþ-ï1æ¢åÂì¨ø¹ÑÂ;ŒÌ CúÍèÙ2ѾҫÞþñ{amszk÷.øažì#é(§pèOÏÙÆ¬ÓDò °5BA¥6ØÑøVÜÍ^ÌÖäŸñµü”œ ÝmK T÷öTÿXW$ô-q&E qêžË¾ÇȦè-26WHÀC,ä æêõÓ4ÉÏÉÓAâÍô¹Û›@ ·õ¦ç¹çÇø.W0"<Ò/ü Ãßý¹v«ðº<ã¡F@sUdPÒ5”‡ìHÒ|ŨÅ<Ð.â:øAôü$x† ÜóRäýäøÕD3Ÿ>X0Ð Ûd³¸£À²ÛÑ>Zo[bEn †÷}Ô ¾a·‰ÀðÖõ'^-Û6:.b«øøámÝñîNß2“EÄ<%²ã÷²Ï˜nŸvÄ—ú–/šSf^¦PŸ1- \æÒÉܹý¸ÄÇÓãíl'Þ:.:r%"pçbÚ2å>Í)¿C¶Cñ%yó¾›ü•ú°ûáK\CyX"Uè=*ÒõjÕ£¿s¸¹Á(ÚFü½Ê6O<¶-‘Šô$ä…èH† h9>=Ø&+üÌð§ùœ»®Ö™0ÀGÓI9qlü)àÃÌ7ÅÊVÛÇóz )!1)$$ælPþº˜£&Z4¡2±ú ÓQ´Ð¨¿´UÔTýÙ"ñ98=%.Àx÷†ácÖÖ{ÝèWòeú(‚è <Ì ±(/ 08(ø)׳»•¯ä¸“ÖÛÿ 'Û>á?„+ DëÝÖMÒþÚ¶é¶öAýý6ù7öp÷þc Q$p.Ñ2b/#¢ÝôÉÚ¤ÆP¾TÅ3Û¿ú(2W8+VÞð¢ØêÏ7Ø¿ë8Ò æ 6ýsîßçð3™"¶9ÄA57''ç¥ÚõÙüàöénñ§÷$ÿs ©z= gòÑÝsÕIÞ«ôv „Ð;EæÕˆÚºöðE8VéKg*ÿáÙ2ÆØÆ¢ÖìÆŽßÀ·qý î~Õ®Æ{Êáâ7Q+Pœ+#üGÓ,¾@Áè×½÷J(v,># ³÷[ßCÌkÃxÈíÛ´ù./W4û$n'è'լ׺ï±Y4›C­:êö8ÕÆÍ©å©!Ý.š+¶¹ÿå ÒiÉßÌêÚõïp %#8{Fíaâ½æIúD.‚7¹+³ …è!ËòÁTÑóíü7s?é.B RçOʦ¾hÅ!ÙBñ(Çã` îþöXôÃûÚ 2§#üxµçÎÄÆÖטü·&TD H/·Ô¨´¯ÂÂÆåb û ò&Ò¼ Îÿñùûü•ßÚ¤j‚Âü³ïÝâëÚ Ý%ì)v#ã8(<¹(Ǫփ´¶© »@âF3k?a1È©ïÑÙ„Øœê&."2Û2<&yzúæç×ôÒ¶Ùèì› )(ƒ=?(ý™Ë+¦œÊ²›â®NC¶P?í0ÏÈÙ"ø™„2´Ë"̳àF(AÛBq*ŠþëÍÞ©‹Ÿî²‰Ý,]=UÐR9HÉç«Èw¼.Æ~âq²+C@¥>'¥Ý`ÈËÈäi /Í@º8üÄì±ÃիѬ¿ÅYîa¯=~O0L‘5 ëUËÉ»]Á¹ÚæÿV$;ó;ô&yä!ÓHØkñ0F.ð5\%Ž-×lµD¨[´ÝÕfà,²IòQÃDì&oÝÅ·½¿ÈIâgR0…1½#É ùø‹íEïòûc ’‚KîJÓ#ÁE¿IЖð_R9.LJ3b}è?ʼXÂ=×tóö Î&¯!ÜŒ <ýEÿU™ X ¤köîßœÊË¿áÆá j4 S¿[äK)€þØYÀ‚¹vÂoÖ¯ïéž'*%,€#Q˜;õÜòú-3 ÷<ñlÖ^ÀüºsÌØñÿH°]K[Cø—ذĕ¾•Åb×Bð b!¯-Ø,( : Ûû’ò†ódû B…ø4åÆÐ˜ÄÌÇ8Ü¡ýb#†CãU6VÆDé%ÐíÝ4Å1¼ÄÛù´Z)ý/.*E “þßõlñïaì†èìã à‡Þ¶àqç,óÉ ú,½=°Ds=M'‹ëãØÉÀÀÊ~äÄ!¢1y3ÿ(¼aö„ëeäÿßçÝhÞááè¢ï;÷îý¿ œä$.(&#ØÑÿ´é]Ù^ÔíÜñS w$¯5p:1o-(éÕxʯÉ~ÐfÛKçšò@ý·ÈüsL Xÿ=÷?ó¨òÀóNõŸ÷düQgó ,¦.¾%MùçàÝÏ+ÉNÌ ÖpâÁî2úKr±…!÷!Mg ñøBèaÞ+Þdç ÷§íù!Å%Ö#‡T… 1þcó1ê”ãàÅßâ‰æìì}õvD æ#â$5z ö¹áyÔ(ÓfÞóO ;! 0I5Ò0H$Œ6ÿîâÖ܋ݧá'æcé%ì;ñ‡û‰ Ì,s1©(#ú‰â´ÓÐ$ÖÖâKó\*.b=•Bš9t"+ã Í$ǞΘÝóë7ô.ölöû¶äF. 9È6™$ˆ¸çÌÍÿ¿¹ÀÄÎŒæl{ ß8H¥J*?L'å÷ç¯Î%Á×À}ËBÜÌí>üR? | û#ý+/)·ýdßþÄw¶¹wÍ+ï÷Q8NÏR.Fö+n ½è]Íø¼°¹ßÂGÕÙëžÄÙf+ê$û%„² BòþÓ£¼4µ4Â’á =3÷N;W¶J”-©îáAÄà³Å²¥¿rÖ9ñw È’ äq ¯ ŒÐ ’'ä!6 0êdÆ®×­ÅÇö·* UShX_6>…àÒº;¦&¤³²‡Í¥î÷K'2•-NN +û”ù·è ÷Á‰äËÃr³½jàäìCQc3gOÃ"`ï-Â1¥JªÁƶë÷$+7û34$Øcÿ§ø7üC ï ßüÞèWÖ^ΓÖî@È1ˆIÖP‰Eì)¶¿Ú¾·ä¢¢z¶ÜÛR.'CuA,4Qöƒë€ð»þ, [ uÿßétÖÐÜ.øvŒ:9LCL¡;Ãtû±×—¹}§§ »¼àìæ7`NDKV1t Uì8Ý?âqôâÌ ¿²ðݨ՞àÆû ß;L²J)9Fùùñ׻ϩ†§:·{×¶8+aHõP}C&®èhÚvÛ·åúðšö§ô|î²ê¢ïŒ5–IO°Aè"†ù®Ï°“¢“ª_Å\ëZ·03@t?Â17>k÷tìzæPãýàãÞÞìàðé˜ú#ö,óDÙR‰P€;{}éÿY¤kžé®_Ðù`28ÕB!? 1‘ @öÀæ3ÛÿÓrѱӠÚîå>õˆD3MEFNI73 4äʽ¡¦¥¶¸>Úÿ¥s0`7Â5i/4&—$ÏñgÙ4ĸ»¸æÆ¿ßþO:}O•Y«UB (õÂÊf«]Ÿp©µÅ!ëf)¿6¾8±3£+>"œ¢ñ°Ù Ã†µY´ÂßÜãÿ}$äCLX©]5R 7æÍÀo©6¥)´ýÐó×°'|3œ7j7£4B.t! ï‚Ï?µ¨Ñ¬Ãäå 2eM¤[1[ñKN/ÕÂÞ”¹¢ž¼¯PЋö Ö-£6)6 2 .â)®!û]ö¡Õ8· ¥d¦¿¼0ã?H9½U aÂZ‰EÂ%¤×Û@½Zª§(´ÁÎ|ðý*e9=ó8¶0(&³h]îÙÒá¹…«Œ®Åíêž¿<TvX™K3ÊþøÚßéËp¾;¹+¾ÝÍiæAzÞ2£=Ó>C8ê+ÁöPëºÐ¼º-°{¶ÏΟô’|A:TµRê>›ŒýŠàj͆Å}NJоݼììûY rÃ"±+21¼1y+û>žé5Ì+µã«sµNÒý£+pQäcÎ]IA >ëPÉ3¸¾¸†Æ–Ú î#ÿÄ ˆC(š.0b* ïð>ØÃٵȴ³Â„ßì_0þP_¢U!7‘ oâ ĉ·z¼F͘â¹öLÍœ +›2¯4/O!Ö ›ô)Ü)ÇM¹9¶ÇÀœÙ§ý¬%¥GèYÇV?±ƒñÀÐȽ¼¹“ÁµÐZã’÷” M!U3&?tAÂ83&( òÜØÅ&¹•·6ÂgÙ®úÿž@¬ShS¶?yuù“ÚCÈ«Ã_ÉÔߪê*÷V³p1ÛBŠI(A„)ù$á‘Àl¬ã¨’¶«Ò¦÷.c>aQôRìB%MâûË^Ã'ÇíÒSá}îtùÑG/8<^@q7 ÕþÙƒ¹`§«¨:¾Žã²•76Q²VHÆ*åtèÒÒIÉ‹ÊTÓ?àÑîý… Ì…! ( +N*!%Yñlñ×>Àp´¶¹wÑÿö~ JB SêNs85ôùׇÇnÄ;ÍfÞIó¿8è}ŽªÌ-Á»Äí¤×ׯ’Â¢Î‰é¶ ¤.ÿE}L@u%&RßBÅí¹‘¿ Ôµñ'g'ý1¼.ô ýšÿT÷Üö/ûCÿþ5÷ëß9ÙrÞð H(ª> F$:€ŠôÍÚ²~¬ ¼yÜðH'Ÿ=ÉBç7A" BósåCáDåÓí„öÈûFünùÔöø—ëª+Ñ,G }PèDËÔ¸½¶AÆÏãø)Ä'( +î­ÚJÓ‘×°ãGòŸþO±  Q Ìè÷`-Œ‰ðߨÁŽS»Ջó$Í2ÇE J?þ'' GìÇÔzÈŽÉõÖ‡ì8š!U"¥? t T ¸îû`ë»×ÏǦÂHÌÈã{&#ù:åE’BÞ2ºÿáä4ÑÈiË£ÚµñX 7($(x ±¬ ̽üàó–çك̗ÇÅÎøâ¦Â !;.IØG87NºäÜÐBÈ̺ÚKðAü$ð&Ï!÷òGûö¡êïÛ;Î~ÆLɧØÇòE´/âCøIÂ@Â*P ïdÖÈ,ÆbМã û>ƒ!)Ø' l –úòÛçˆÛöÏûÉlΫßåûø4:MKCK":ÔSûýÝËŠÅxÌzÜßðv *$Ä"oïy UýùÖí1àÓ/ÊØÉAÕ¨ìh o-,G/‘né(Éè¸E¼FÏ éˆ~ë=þ %_'B$ˆëócâØG×ÜßÉïâ8F(Š/Î*ÉÅäÃÏöƿ˭Úwí%þ¬ ˜œ á $"x6’òàóÔ`ÔÜÞbò÷ N#q5à;%3’,úà×ñÀcÙªö|›UÊe݇üùäïЦÇkÍ-âw¥#Í?íMI½0 LÞÓ¸ˆ£°£9¸MÚïÿ339¾4*»D ý?ô åàÕ{Ë6ËÏØ5ôh7<ÑTzYìFî ZñÛı¦7}¨<ÃuåR*#&6»?«@-:|-ÆWÜî½×mÄO¹ëº=ÌÀì 6AR_og†U}-#útɯ§l›?¤>¼=Û#ú½¬)ü8–BrEú?12øúþÚ3¿Ï­¬p¼ÖÝ? ­7kZ‰hW]O;u SÚϳŸ«ŸÉ°ÕÌ-í_ ¾&*:’E‹H C^5 œ“å©ÇW±‘©FµµÔì¯1ÃV|fƒ\8<ààó»k¦Ñ¡@¬Âßïþ8³5ùDEIÉBB3ÆáæëÌйU³ ¾Úî¢/DQð^5UH8Î…ìп$¸™¸<¿éÌãìl"9@‚RTPD'÷*ßûÁ{°w®ó½ÜÝÀÜ4bVzcJXÌ8Kè…Ìí¿¸¿eÆRÏÙuå·öG &;vG¶E‘5©÷]Ô‹¸gª ¯Èñ"áLVe4dLJD ó¯ÎGºT¶w¾ÉÌìÜIí7þÖ!§09v8Å-ÿÑÿIã¨Éô¸Ì¶{Æêæ¨ø;ùYÂb¶Sv1 Ýð¿ö²tµ7ÃøÖX쫺ã!Z-Ò3¤3[+s×å_Ë»¹>·ÇfçëÐ8Tm[…MN/· Læ+Ì„¾ß¼ŒÄKÒaãáõxD)4€9Ã5K'ÿ í:Ëò±ª—¸¨Û® À8YicXV(74zèÑÊxº¸iÁŽÒìæpú Ñ_ ¡(o/2,’ û–ÖM·(©´³Ög’5UV\ûKÌ+ð6ç"ҚȃÈìÎrÙ|æâô•±s)Í0°1r(º0òÎþ±â©L¼ZæIJÝb^í?º–ëØÏxÅpÉÕ3â_íWötþ Š{±#**Ý*0"« vîËã®÷¥ê·ýâ8RN÷jøgìGrJçIŬ·’¼Í=áæóÞ9Á$ß!F%'d%w í€É}ªKå«×>úOðu#yPY—"ÙçѺåT©¬¾òÛˆøCù&y*è*›)°&¨ ÊÂêäÆ^­¥Z´ÌÚYÆB7e;lŠVô+Æù°Íi±¼¨²BÈ>å0uÞ0Ò;B>?9>.D· ñÖ5½Ø¬Ô«.¾{âå=y\;dkSp/“_Ø#ºç¬±ƒÃßÝýÜê1k@JEÆ@þ3o ¾óëФ¸«q¬V¿Ùá À6ñSþ\àO¯0úØßüÀϰã°~¿™ØõöJ.9?F_D:¿(”Ýõ7ØH½P«^¨¿·ò×D„,.L¦YSRß8ó#ìçÉý³P®S¹òÑEò .?€¿ø×ÒL¶#ª¼±QËþïÛw32CÏDo<Š/·!nÍê²Ð⸩« °vÈ/ðF’C×XÌW(AâKîàÅ«W¤P³ÔþÞ%ÈA…LúF7$yDÔù»êØWÅO¹»Î×ïy×<›RxSà>܆í¶Äy©®¢/²ÔfÿÜ()FoQâJ8¥ w cûdï€ä¹Ø8ÍTÆ…É>Úi÷Ü;]NTNë9ÆÅêÃ…©£Ë± Òküž&åFCV‡R´>¹!ЖëܰÔÒ1Ó0Ö€ÝÔë Ÿº7 H­GZ4¼pè6ÃŽ«§ˆ¶ÕÕÅý‘%¼DÔTõRž@^##’ç‚ÕÅÍãÍWÒÙrâÎðŸ<ï7GF^2;Dã"½!¦ ¤Õ¶EÙãR*oGâTQ\>C"Üšéx×=ÎGÌJÏÖ=áòÜ#Ç:¿H³Fk2/5å6¿%§A£n´<ÖSÆ(žFüSeOÑ;|ÓéÉØÑ«ÏOÒ¼×°àðîgP–4EàFá6  îÿÇt­¦’³nÒÏúÿ"âAQmN<æŽèô×ÎÑTÓ÷Ø*à¥èô+„B-Ù;˜=ß.[ìUÉ鲝޾çÜ!B%¢>‰IëDJ3à#ÿôèþÚÞÕŒ×ÖÜGãiê±óý…q%4ô7Ê,ûNðaÎi·¤²ÛÁíàQì*CÞJBo,ÓaöÉâÈØÉ×ÉÜäëbñ øØ¶P+Ì-l#ˆ >îÑ&¾Û»¹ËÑéÛ.×CYHu<ª$<ÇîâÝŸ×vÚ—âûë.ôûç@ üì€!]9þ%çíÒŽÇ*ɬØJóóÑ/xB1F„:R#Y¶í}ۀҰѰրßDëÅù6 C0&7*A$;®ê:ÙFÏ€Î×èÿBz.<Á>æ4"´ ûöç~ÜTÖÔÈÖkàéñ  •/Ÿ1ï$§ yó3Þ™Ò>Ѫ׵âað *Ø!….T3F.´ éûý>ñòèãäÝÊÙÚ~âÕôø {&ƒ5à4$”_ëÕË*μ۪ïÉe-*O2/1u']¸¹üÜõ>òî¿æéÜ;ÕáÕÈâ’ú@ù+Ž3D*ðãøâPÕ<Ô¤ÜÝêûZ V…&ä+Y*Ÿ"H’ £ùñ[èŠÞU××màNò‰Y! vÄ'÷)íFçAådç‰îßú‚ W¡"‚#¶, ·“ÿìûàô™êÁàoÜÜàí‰ül S˜E6·T %Ñø ë6âÌãûð…ʳ&)$"n ÿXõlìØävàásæîö?ûÿe( ![ ¤¥îûÃãmÔÕÒå, H+.0*éXöE÷ÌëãBß0ákç˜î­óKöGù¬2µ»*8*™äü¸Þ€ËUËÞÕûþü+î0ó)~Ø ógöÒí1çã â²ã0çìßòNýý Ù*Z.­#b ªì‰ÒÇàÎÿæšÿ"4›6¡,8¬õémâ@á´ã6ç¬éÎêÊìLó æ)¹6ë3'¥ý¯ÚþÂ=¿jÐÜïœ.º;f:+-2» ñ äòÝ.ÞTâçê ë.íæô?36Aö<¢$2þùÕ¹¹³ƒÃår f.ÚBÀF;¶% ƒñeÞÕåÕ2Þíè'ñ­ôpõ†øòÖU,Å<=Ü'ÕÐÓµ°Û¤UµØÜ ý8 hÿƒõ¡ï}ì×êDê=ë¡îõºþ¡ Þs Ý#L=±ùäòÕeÕ{ã}û†%é(3  ýëñ1îmï›ñòºð7ðGóû»Öêâ xüókè?â‚ä9ð#Y%(#š ß÷.ê:ææêˆóìú(þ¼ý“üÏýÅN ˆQž ò+çòáÆåøòS‰%M%¦z£ðCäŒâ³é†ô’ýì `J  ™ Z ÍKú·ñdëëéwïwüjÏÉ)'Xþ˜å¬ÕÐÓ‡ß@ó3ÛX¸Ž ÖLþÌú–ùùÅ÷Ìô²ðqíöíçôöªt'm13.šÎ+ã2ÍñÅoÏæq¦Ä*ž,r" ÎþxñìÔíCó®÷ó÷GôDðñú åÜ-Ö0·$ú Úí‰ÓÞÄÕÅ¯Õ¦ï˜ %3ä3F(£Vÿaî,å|ãFæñéÌìð÷`)í4Ì3Á#l¡éKРîÄOÓzêw±Œ+ 1z+Å? =ö)èêà›ßÒá²åWëžôI©}*û7º8÷)gŒíäÐ_À¿¿êÍÿå\œä)˜/Œ*è• 0ø®é;áéÞ\áçRïiú“Èÿ'Ç1ü1Ã&Êþ÷ößÁÏ ËNÒãŸøä Ê%$*N Œûxî$æèâùãvèðû ±c&å-À+"¹ ôRáרÕâˆò~jÙ_ žÆ÷ÉîýèûæÂèîcö5E „: ×!Ú! ñÃæVãúæôïšû8™,ô » Úýmñ7çáÁáÖç´òy¿áC"â"Õjþïçäâ5è«óW y·}åCý´ó?ë åtâ¶ä|ì2ù®n€!â#¹¹•ŽñþæxâèãWê­ôÚaÀ}&!'×¼ þô¡ßVÑ—ÍÈÔ›ä<ùxj Ò+j.ƒ'¤’Tó_æààâ_çî^õ`þ° )1£- ÷üšÛ4Áÿµ.½!ÔìóX-ù:w;0e=ôïóàÚxÜUäïƒúò²¥ö'-­(ÇÚþ àrÆr¸»¯Ï®ï£08A…AÆ2€þçcØ ÔuØpâàîüûl I´$ÿ. 2h*­Ãù]Ú)Áåµ™¼Ô4öõ+67D¹AÚ0< üÉåŽØGÕÚ´ãÝî¿ù*Ôv$+±)šÔ†éKÍûº‚¹ËÑ뺂4lG¤FÂ3éÜöWß(Ô(ÕWÞcê;õ‰ýv U÷ˆ%ê$eJŒìçÓéÃ~¾ÑÍî;2°D•Dö1Óñ4Ø‚Ì~ÏßÜøí÷ü7z TÅ|® ‹ý{äŒÎбÆCÛFüå :>îJ´B%( -âÂÌ«ÈÔÂçjû 9ü¼>™!•—»õ!ÙÂf¹’Ć⪠Ú3WNLR5>¶WîÚ̾\ÄiÚöS Ò;æ†'Ÿßï¾ÙoÈû—ÍËçà 06I–N9=®ïíÊ̸5½€ÔÅôeì$à)$ÎöÐ Æ»ÿŸô.æØvÏÿÐÑÞ€÷¤2óDDF=4nê?Ç$µg¹ÒöEÕ0s7Š.‡H ‡úðñŠíáé¤ä_ÞvÚŠÝðê~‘ÿ9¬HEá-Ãcà+ÁË´³¾ïÚÖÿÍ!a71<…1Ø(kðâ—ÚàØÛ½ßãæBñ¶ÿõ”%ð5H=ñ6À!–ÞsÃô¸íšއ¡&>DÇ8² )¥çÉÓbÊYËvÔ…âŽòžã !,ÿ3W4*’Äö·×‰À>¹ÅÖâ A-áD¯I;ûÛ´Åt¾íÄ`ÕvêEÿÄ,(ü.ë1n.ä!¡ ±îþÑo¾–»bÌní»ž9¦NðNÊ:,˜ð+Îܸš´§ÀH؆ô‚z!Ò+«.W,†&EurüòåÆÐÀÃÅ×»öôù;$MJq3E1ç}ÅZ²â±2ÃÞà”ªÀ1†6ù/×")¾ûáðYåRÚ¡Ó-Ö%åyÿ™:vHÛB÷)°ÛÕ»­¡²dÊ#í1A- ;a9U+Sòrõ–ìeçeãîß-ßåqô½ w(Ë>ÝF§;`qöúÎ ³-ª;¶GÓùƒ§7úAÎ;)Ìëù?é à¹ÜìÜšßàåÿñ 4lBšA&/AûæÇÃE®u¬¿hák 6/F…I:à°þ£åØÖrÛKã–êÜñ ü‘ §:3”>È:B%±bÙ@¸Î¨…°êÍâø(%"FæRI„-' ?êÖHÐßÕÍà£ëAô.ü¢%Ã2—5t)˜Ýê È ±z­Ò¿ïãç7çO“Q=sáöÛlÎ Ñ|Ý{ìÊøQ„›‚\)|/F*ZgùÈ×8¼–¯û¶æÑúá$VF¬T¶K–.êXáÞÈÎÂjÍ­á7÷<X¦á ž&S)À$ÃãüßqÄBµ6·ZˤíÜ®9ZOŽP <>úðÓÏô¾xÁ¿Ó—í3ÐG!%*& &#؃î?ÒL»ü±©»IجÞ,TMYLw*äþר¾2»ŒÊÅä$bé!K'ª(Ð'Ø# NïjÔ›¾¶µr¾Ømþh&‡EÃRòIl-ž|ÞmÃS»HÆ1ÞÔùúö%›'J'(%„ Åø9ÜÂw³=·ÂÎçô– C™UxR;®í…ÌžºT»žÌ è9ÓÙ*9/,×%t¸0þéç,Ðe½ü¶ÂqÞ/Ô.M:X‡M%05à–ÁC³K·AË3é F%x7(>Ò9Y,úüþ#ä_Ëå¹µ‰ÀÃÛ†Æ(qGÄU¸P:‚ƒõúÕrÀ)¸¾ÐÐàìÁ ë)H>¶Eù>Á+áað¼Òs¼ƒ²Z¸nÎñš;ÀP;S‹Cü&PèåóÍÀI½Å8Ø$óÇ.nB¿GÔ<ã#‹4àCħ´é´yÅTã6»+EœO‚HC3#0øß Î?Æ­ÇÞÑÖãtûû]+h9D;²/ƒDú]Ûß ·¿» цòô7úIJƒ9œÿhå-ԇ͜Ð3Ûê0üf è¬%)Ì$z°Üë¼Òú¿zº¢ÆÎãU 02ÄLLSÁDå&ŠäÚЇÊ$ÐÞVðØbP!Èé 3þTîÝ͆áÆPÚßüÕ%MH1X¶O"2Q »åÂÎ"ÉÒ ãÕõjD B º© qù˜æÕ_ÈÄ…Ë’à8½&ÈFàV÷P…6_ëiѼȅÏÝß$óßI †$% ï¯ù5âQÍy¿$¼yÅÜÛý#E!YXhAÒ[óYÔ„ÆyÊÀÚxïBQè' #þ ×ÃnïÖzÀ–´·ÎÉ3ë- >¾Z•b¿R0BpßwÈãëÎÞáƒöZNØ Ü'“)B# ÓùËÛÀ¿-­!ªx¹¾Ùü¡1¬TÑd®]®A†,ñyÓnÆÉÉj؉ë×ý w"‚(Æ(Z à ò¹Óø™©0¬aÂÄèô~A(]—böPg.Õøâ4Î*ÊÜÓå•÷^DÃ>!Ö"CËýãÆÈ[´ð¬¹¶ÂÑ>ùF$#H€[†Y›Cû ü ßÐ>ÐüÛqíÿp kGEÆl%ï™ÖÀƒ²Í³RÆ­ç¬!7QÀW‰J{.º ïÂÜ8ØLßçìŸû؆a:~íÈ ôøËÞuÄA²¯b¿4ßÁ,úFƒPIÁ4Ó ÿê]ÞAÝlå˜óy×Â@ 8ûwä…Ë!·.¯)¹öÔ ü#Õ@öL[G©4 1Žñ&æNâså`î)û? åÀ""ã0àgéÛÎ踯¶ˆÏ ô7ÂD)B‰3VH ºúÞîëçæ¹éòÿ¨ ƒò!g› ‡õrÛ\ÃÄ´òµÜÈ|éÍE.@ÓAÃ6%¥üâöçíøçæoéºòÇ´—d&Ø$I†ˆä?Èü´‘±OÀ†Ý¦³b3¢9\5¾+ò F¬ ñüáíãà¢Ú¢ÞGí5Eh+ª0ö&6­î η±#¾¦Ù1ú¦¥%ç)2'˜#c"/"^Ô}ÿc铨-ÔÔÞkõpí&ˆ1,y8ú¬ÙÀPµ÷¼…ÔÜórð!è%s NýØ` K Z/ºçÖ–ÓâþöÄ054 $ºáßÁ÷±ÿ¶½ÍÌí/ Øa% <eì" ÿ ôÎÜÑ}Øcð¾ -Ñ9*1Øî(˪µ€´GÆ*ㆊ³râÈõ!ˆ"ÎïŠÚÒÚ ña)60ì ø÷×MÂV¼‡ÅƒØîSI IL&‹-)0>*F£Dé¥ÕÎpÕê/E!­2P4ë$ê é‘χÃÃÆAÕÇçÓ÷¸0 ,`Â*5n7‡,i¡÷½Û/ÊfÈ×êñ×C*6&0¨îü áªÏwÌÕãÎï$ø¤ýºÿ"ë2'<Í7“$Ùç¦ÎŽÄ[Ë‘àýsÈ+$/&#¹ 1ôÎá,ÚŽÜþã¿êúítï'ô™,Æ,±>ÅBw5ÄøÚÃÉeÉÞ×aï#ÛÚ"7•åóâéØäóâ>âyâœå|îvþ Ì(€7È:[1þ¶ï‹ßM؈ÚÝäCô‡GX䘅iúsñtéBâ:ÝoÝ æ2øx'(…7u9Æ-j ðWå}â¡ådìÊôNý£° Ú B ‡ èývõ1ëaá(ÜÝ߉îò§V3[:¡2YLÉñäã/ß1âÜéó©û‚uÏ Ê  Ï ¶^ú+í´àéÙ Ýwë^xL/8¤3$ÿ J÷×äâÙ¶×ÅÝëééø,ÈD§âè Dy÷Ýê)àÛƒÞ[ëÿ¥W&Õ/k/ð%âjÛîšÞÛÔðÓTÜì;ÿÓa#s!Úÿ øùæèäÛùÖVÜìêyþC§á$û$!>ù îZÚÌ3É‚ÔÂëJ¶!#1]3=)‘y ìtÝM×(ÚkäÞòö0¼á!'4)ð#aùýMâ0ÊÆ½Â_Ö õ6J.‹:@8q)ÑUúÉåpÙ÷Ö)Ý¿èö´Fj$ý+-V#Ê 3ðŒÒ#¾Ú¹gÇÏâ"k"h62<º3S ¤%ðTß%ØOÚóâkî9úã+)©*10û*àhü³ÜÃt·½òÓ`ôr¨1‚?=…+¥©ôÉßבڸåKòæû9l sO,61À'£gë¦ÈÓ±­®‚À âù J-CxF­7$ÔüJã8Ö ×ßáþï´û.³'°-ª(Lùö5Ö0½±³n¼²Ôêõð3”Aª@41°/û&ãöÔUÒrÙ¢æ{öøN%á.ÿ/Ñ%•}ô¢ØnÄù¼ŸÃ7Öhð þ&’9Aa;’)¬ôxÝÎÐãωٳê¸ÿøØ&¥12=&o7ò»ÕˆÁ»ñçÙö£+?9:;˜2\"ÑúêúÝ‹×Ù×àpðIÏã2b9 -dA,Hµ= #œàÛËRÈgÕ7íF°™%$¨´ Ìû#ð³èeä0áÔÝFÛÜÜÂæBû<|6ÃKòNu<«ÛîÍ5¾PÆ—à†+†'*ÕÿVí>äñä0ë¾ðéðJëNäãÂí[_$(@?M¦Dy'ÿ ÙÄ"Å}ÚGús/'ñ$žû§æ3ÜEÞtééöÁÿ`)úëòòmü¯ +é=‘@ /Á [èýÌ ÆbÕcó^À$##Z'ó^ÛâÑ]٘얿€>¬úŒõüÇ Ä ‚.é.É x Dòäkãƒî þ^ –ãÞ÷)è^ÞAÞdç§õ8D  t v }~ìÑ’WüÙ÷’ù¾þÐøü#óÄèŽá àÜä¬î±ú„: u  ݈¹ªßjØÿ 7¾‰Æ BiùîÝâÛÞÚcábítû˜÷þ ÷þxþÓÆYΚ‹øýãráy¬ôÈãæÔbÎìÓ”äÒú²Rò2 ±ÿÈö÷9ì ú¦£ ú~×ÇÕÿgúfñaæCÜ‘Ö8Ø9â»ò4âeK Sþ¤õhõ.þä N$'ò)$Š,ú ðëéPéÙç°äƒáiáçóÍ9ße ,ûSíÎèˆñtd!ï8ðCK=]&Êné©×>ÕêÞsì5õÂôí¼äuãØìaþtAdê7ðêß>ÞÓîÈ Þ0MKÏRÝCL#jüPÜḩÎ`Ýžîaùíù ó¬ë”êPòÔÿ> £Ý dútéàãä­ù–­:;Q#TÀ@z'ó€ÒtÄöÊdßöÛ‘ùí–çí—úò¢ Üôeã{ÝÔèqÇ',FTLl0ê è)ÒÙÍ`ØËéïø‘ÿÚüüôfî î¨ôiþ™Rùðê¿àÃâ…ô«¬3ƒKÍPú@‡!£ýUáqÔ³×HåGô;ýyý«÷ñLðcõÏýÆ"š÷%è ÛKÚªèØª&ºBONGj.M öîÜØ]àÞîˆüq&iÂù›ô¹òó§ôó…í‚åÊßfâ{ñ? ë+³E=O‡C±%êß´ÏÓ­å=þwbbÝ ùþê“ããµæoêFëNé èÏì}ûvz.›B›F°6{òKÖMÌúÖÁðÄw% -Ý#UôõÞ%Ó'ÒÀبá¦èùì£ñ?ûu # 8B´:"áÿ*àmΘÐÖäÒió0X2$0 WïØbÊÿÇÕÎÏÚèŒô〠¾+4À1L#å Úòšà,ÛÔã÷÷!ÿ,+,`N õî“Ö÷Å­ÀèÆÕÕ0é&ý_¡°)”.+ì ˜õå¹ÞCäœóóµ*¬/–*³= æ:ÍL¾V½ïÉ–ßë÷} n>'D+M)– *aý4ê¹Ý“ÜÌçQü0Ç(µ4á4Â(NÎõYÙšÃå¹c¾Jϵç¨$.(u0h0·'àUéÒדÒÜiò&ö)P;¶>Š3¥:ÿ‘áÆÉ™¼j¼ªÈÞÎ÷§m$L0À2a+‰gìÅØ€Ï'ÔeæÆù6ö@¤<*I õìFЕ½Ì¸fÂh×eò) r"ù.Ÿ1õ*±V =ô}ávÕËÓ(Þ9óƒ9)ÿ; Al62þÄÞNÇ”¼¦¿HÎ[äWýõ'¨2š2ê&¨÷SàWÒ£ÑöÝHó© e!k063L&Æ‹ø5ßËòÀCûÑOé à1Ó8Á1ÊÁKå©ÐìÉÉÒèQñ /‰6A3'ßõÿ…ë{Ú<Ï|Ë!Ð#Ý;ñv ý ¾1 6o+­­õèÚÞËÎÌVÜ¡ô;ê"+/ 2,†U |õá¥Ò„ÍÖÒáÁô„ 2R&p(– ï(úñädÖôÒÌÛ|îƒó,à3É/7!Þ …ñEÛêÍÿÌsØšì–N$å'"è|¦ñ\áŠ×׉àOò…Ná.Z6^2º"ô gíXÔéÅ}ÆæÕFïð U!€-.½$‡Ú÷ì/ÜëÑÑúÚbî^9 3!;\6`%  Æî•ÕDƹÄ5Ñ4è™°.«4Î/¨ ð Õïä×EÈÆùÒ™ì# Ù(ø:F> 3O .êY×UÍ#Í5Öªæ™ûa$02o(éø~ÛMƲ¿Ë:æ› Ï*E@“Dé7UZ9èôÕ¬ÍÏLØçÖø] !`(/-/(¤Ÿhå—ÎÇóÉûßÕ"³;vEC>n)e 9ñ£Ú ͑ɚÏgÝ}ðÑó=)L0¯,ÓÙÝé0ÑkÃÌÅùØ;øÌÈ6œDmA/ ùÕáÓÒ*Í-ÐZÚîéïüß~"ò-”/;%˜üòÓֿçÀôÏ+î½æ2tEëE­5Ø‚ý¹ä“ÔâÍWÏ×ÎãÄôÞŽ_.Ü5˜/’¾ú¬Ø ¿D·Åvåp5tMûP“@‘"32â2ÎéÅÈ÷ÑzáõA b!â2:2PV÷ Óm¹ ´ýÅNê˜]:N—L 9^¿ü%ä¯Ô"δÎéÔfàPñÛìõ0b932¹ú÷çÓIºz´ŸÅé×¥8ºLœK8U üªäL×èÒzÔ‘ÙÕá¢îÃ-È:38ä"ÎþÖ¸¯WÀàæê°=³QVMø42ÆóÍÞTÖ-× ÜráÝæûî©ü ì$°3~4U#ì×Üz¾Þ³ŒÂÍæ¥b<ÅP,Lc2gQíÈØ¬Ó<ÚUåÂîYôEø®þ/ F=&¶)œo-å+É­¼ÆâãÄ 3ÙI5Jd54òkÚÆÑÙÖ™ãñ§ú;U bŸ áÝ{ëÖÐçÁ½Æmà¸Y/@IkL¦8mëòÚÖÑTØOæ¡ó”û¯þ6ˆŒ`û#ÎSé̼ªÁ‚Ý“Ê1pMQ<<?ðHÔÊ.ÒÁãâõôÌä úLÚ µ 2íÌÏr½†¿=Øâà+ûIƒP¾=¦ñÔÉfÐçâ¿öƒ ë ü º„¨ÅëêÍÎS¾#ÂzÛÛ¥*ŽD Gè3#¤ï]Ø Ò¬ÚÑê&úº¼þ |XjfâþJãVË%ÁµÊÊæ» /ÈB @,)ªRè¾ÕÔøßbñ< \ ƒ®ÿWpø?Ü‹Æ9ÁÐÀð÷£5B¤8q_þXä$ثڴæôgþ ‡! Êï"9{ Qñ±ÕcÄAņÙäúÚQ6hÖâÝðó !=Zyœýq÷õ õBóaïœëì9ô?Ôn'™+¯  ïì‹ÖlÎx×èíýÀ(p#ÀfƒöËñ0ôù ûœ÷ðCë‘íúÿ š!Î+Š& GõÛÍÞÑæ.,)x'üô ìîö•ýÊÿ¼ûõÚñ®öÂßJ y¸ýÇçÝÚf܈ë©S:éþóôïõÄýýÙ½ý öˆòâöXÐxgZ ü·íØç½ì¹ù›¿é ùõ3ð@ñh÷æþñh'ýòú¦üÓò´ Æ  ýXöIô'ø‰æ )yu OÒõºî2íñ4øxÿK©[WÕopÝòÑü:÷Eõ»øí± «`í ™Óô"í ìñ%ùk%é™ÿ6‚Éz ‘ ÆÓý»÷nõWøšÿF‹^ã áÿõ°íYìAñúÜC• ÿRûkû€ÿƒV \ â–¾û“ùwü « ? \ÅþIó ëêÿðý ÕßN>ý¸õ?ô6ùÛÄ " íæüqù|ûé } m 0PôëÄèï£û[ {@• #ÿ¬óuîâñiüE «v¯Å÷còÆôKý;   ‚öíúêæñôþ< zöK÷1ìaêbó­:´å¢cþ¥ïqéŸí.ùÜI =á÷;ð˜ðù+n_·Wø’ë çÜïÓ%õ#%RKò®ç°èæòÛÿ L?ùô\ö[ÿØ 'Ù ýíýáòâoñp› *Ÿ'hlïìælê<õM¬ŠÍü÷?÷‡þƒ âÊ6ƒõKäôÛªáô $È-®'£ËüÃé^âÏçˆõÅ} Å e÷bóølûl“ ü.ébÝÊß5ñH ##_.ü'Î(øoãËÜ~å·÷Ð A¸\£øòõPÿ â–ùjêäâyè‚ú+q%°+°!, 8ñ6ÞHÙTãìö UÇ%™@öŒó/ø³ÿ9ï]ùKïSêMï{þà.$w*à!  WòíÜfÔƒÛ°îÆ»’±ƒ Çøø­÷Múüfù ò6ê—çÑî:¢(Ú.µ$ ï×øÍÅÖYí.°ð#_l=ÿUöwõ`ùXüúòê‘ègñcÉ+-.v/æ ÒdÎEÜIõ$ ¡#ý@ ~þNöôáôîôtòãîî…ó[”%!€(#»û+ä™Õ³Ó^Þgñx†Ç S!—X–óàçtà‹ßˆæâô—­Ç%-(L ææþYïå‡âÐä^ê˜ñûù¬]\‡pMŒ!í ݕׂߢò‚ “)¬'WÏ ù'í¦çPçËé3íñQö$þ²¿q¸xnòÄá?Û3âÁôN - >)Þ$»F˜ñYè1çLë–ð=ô4öÐø×þ ˜e&þ¥éOܶÜ]ë˜*&´%‰ðø›îÝëûì`î[î)îÝðþøM’› jØ{ïáñÝ[ç†ù¡ ´^"06 «úïòzîÏëÔéÚèêêð”üË Wä">!zûÿ ê?ÜjÚüåžú›, ´$YPbù‹óMñmï(ìKèõæ»ë8øW kB'a%èœý^åšÖ,×Áæòþ2$×$žª Ùþ5øa÷’ø]÷½ñ¡éä—æóqø +ó(áû@à¹Ñ£ÕhêFY ,}'ü›ö\òHö_û?û½óèá¹äyõ}}%˜/Ó&‹ Þí6ÕÎ.Ûjöõ( ,Š!„öþÄõ[ô|ö’ö{ñ«è‰áeâ­îmm-ª.?í çÛӪѺà]ú8ü$@(¨Hùæó+ðiëWå¤à†áëžüÂX#Ð*%8Rþ6ëáâFìÍú™[0ßeù)æú¬ìÑߌØ$Ú/åõöH }™ ²öj wùÆö˜ö)÷±÷ýøÊüGŒõÌï6ÛfÐ5Ô¯å9þyŒ „kQ|ü¸ú½ÿcë…û>ò)ðÔ÷çeð_”ìØ×дØ;î©Ïï ¤¡ ‚üOö ú8 wî=ý{ó$òEûô ýQý^ä•Ò¯ÏÝRõbwC *F¯öôòîùžÝ· Hýò˜ñ×ûj 6_¢÷ØÞøÏÈÑãyýn]]· Qüó(õ T;Èj²ú¯òXõ›¦[.ÕÕë•×VЩٞïž1‰ÿZò=îböÿ8T†û+öû +¨ \õ¤ß`ÒÔ¯ä^ýך· Ïú ïäî¦ù) J@”ý¯û…” K¿ 8þé,×üÐ÷ÙûîªR_ -ê ¾ú³òyôƒýçÐ ´ :Óþî ƒÍ~¼ñÞÓëÕæ'ý:ÐÓGù:÷àûÎ"lúÐýøýÍÏ ¿g“ •ø‚æÚ{Ùïã¾õ(Ï^‘O rnüõû@þŽ âÿ ÿÒ ?©õùê=ßÞMç@÷…®Õ ò þVüÿ.ûÞJþâú%ü û Ü›ó æü%ëœÞWÜ<å¥õ óat‹ýˆù û€ÿ ì`ÿwû8ûôe Í(Y­4ð¼ÞM×éÜ.ío˪` ×þâ÷×÷ý{¦7ÿzú©ú]4 §·7âýêßÛžÙoäÂ÷l ŽK[Ãqùˆ÷yüÅöHÿ“ød÷[þ? ³]Ò:=ëÙmӕܯðs3ôY Vþ:øDù‘þH©_ÿ¬ùx÷=üÿ†{a /÷ÝàÓþÒ·à¬ö“ j(" oý¼üoþPÿšý"úÐ÷¼ù¯  pœî/Þ × ÜYëGÿËvQ` R›ýÔý˜ÿZÿÎüùûžþÚQ PÍ—ö¿æbÜÜæøñ ïf!¬ kù¥ö-ù¼ýpL½½ ¯ w !høëééÞÜ+ãò v¶dl4ýèõ¡ó’õúù,ÿ2º’ %l n-øWìã á$æ“ñÈÿQ «r .‚ãþ.þ^ýküYünþ • ážô·éæã8å=í:ù?· ÃÈ G¯¤úâ( ™ŸU ÛûEö±ëäåÕç&ñ þc Ž\ +šýú<üÏ l - yL˜žî¯ûüïæâ°çô:f@šØªûíó›ò¿÷Ï jg¦þ C ?`Jÿ÷ø¾ð®èëãEå±írûq ²`cà\ûèð5íSñûM.¿—_ Qˆö—íwç¯äåHê¬òÚýï !hš‡Ž/ò¡èÞçíð9Sí B%è7,þ?î<äáUãRèîPôÇûEejC rúöéqáêäÖó Ý(÷&‰¹þôŠçuá á3å¯é]îTô"ýö‘² „Wôpæã ìþ5!‡%}<jýŒîìåããŒæ`ë¥ð ö5üž¡ @æ¿òýmòií¥ð¯ûñ ø É0VKô„è?ã$äé£ïYöýüð » üÒð%ëôíóø“o‰ ,!¡’ –ý×ïiåjß|Þüâ­ì.ú°s È™Õöõæî£îÅôÿ5 ‡â%cøGí½äŽàÙáªèÈóÕΠı$‰íü'ó¦î5ñBúïÓø·A …Öø&ñHëãæpägålëáöÊå*z7žðïç©éõ 7“¶ ¿þ–õðhí"ì‰ëìï˜õOÿ" ±¶Ÿ’LûÈñ)ïãôð˜mœ‰à*ü©ò‚î(ï/òíôö öÈöú,°¿ •ü !HûõÆõšü‚r*® þ‰òÛëðë5ñ$ø1ýžþ$ý<ûsûÍþ¸ )¤°ü†û‘ÿx à—ýý=ò1ìcì›ñÓøÒþ¨OIÿ“ý}ýÿ`åÍVÑÿøÿír³é© bÁöoí0éËêìðïø/CnPA7Gý1ùtöËö6û3” =gÏ5 õæø>ñïìÏëDíÞðqö­ý† ûÓ á‚ü[ôÓð‰óûC2Ù 0JãûFøãõÿó^òŠñ’òXöÏü–8 ¼ §ŒûUô|òe÷’! xNJÒý`ö™óžôæöù÷ûö+õõ„øµÿ/%f5þ…ôwðYôÿ~ s‘ þ?ôâïÄðôPøúÔù%ùú°ýYÓt “ åû#ö\õ¡úcû2)²uûó‚ïfð¼ó*÷Qù^ú•û1þXÉw ¿{Tþù~÷¦ú± p™Ýq ¡¶úö–ó³òŽòÿò˜ôø¼ýp0 ½ Í èjý—÷#öëùƒÚ ¥ß— ©?ýÏú/ùïöóðÀîŒñÙø³t uù 5êûšõYõRûÆ‚ û÷á ¸8Xÿìþžý©ùDóìì1êºíx÷(’ô} žóAì~îú ô¾P˜ùZ÷¶ú’þeþ§øùïÅéþêñôû S(þuíväKèþ÷ö 7¿$½Lõøò/÷ÙüÔþ û¬óÕí~îM÷(mKŠ ·üdê¿ß ãNô ì"é,3'õþLîÍèî6øâÿ4«ù©ñïîõž$²Œ¥ÙêƒÙö×°èÅb#>5N4Þ!¥Eî®á*ã îêù ÿ#ýIö òûõ| pV èñîÝ‹×ã³üâq."2Q$Ý xójäzâåê¶öúþoÇüùøÇù”S 9"Ô.ö•èYäèìÕÿ´v%Ž(é< ¡õÉç”äÑêvõ£þª›™þ‡ýs0 Ö å$øÀì¼çèì¼û7â'ß!W‚þ›ííäÊåƒí?÷ þÌÖvìW‚ I ÂÝùxï»é„ì@øW Œ²"[!EØöÜë:éßìqóù8ýßþV›>áÏúGòåí÷ðÅûù ׾Ň ô)íýí ôüúÿ ÿ‹ü¨úÃûçÿžn\ûòòëîùò<ÿ»ôË#b…Üü\í+æˆè›ñ9ü“lÈÑþ­ü‘ý)–~ÿßùˆóÚð^õ~¶Äq%†Š:ûêùáÉäÀï!ý< ¡ûúøúCüáüéúË÷Äöôú:=æ#‡ç ?÷MäÛßÞ˜íܱeöø–îÎì²ñOùNÿ…~ov]!«s ú+ãÏÔÕ7äZü׌!¸ Ùœþ˜ìhãåÐîûÍ¡ Ë ‰ •ÅNK,þÒéãÙmÕlßñôÿ Ï &ØÛÈï¤ßëÛ˜äÁôf×¼·Q† éþKðôâZÝÃã‚õ{ kþ%Ë.ðífÛÆÖ áíô¯ ~êØ± Ý®Up(øÙêâTãAðñO“$P!G÷¤àáÔ#Øtèöþ)¤‡’×öþÿ÷!î°åEä]í¬ÿĘ#½$J<ý„ã™Ó¸Ó4ãûžð@ \N ®ÆÿVûyóÁëBésï¯ýÕe*|þê^݉ÝêgýˆñBɲþ»þ¡ÿ¼ý{ø^ò™ïxóþ¶ *åÜðýàëXàfà3ìùþMˆ˜í/ Œÿ,ù÷ïö6ö%ôqò ôKûnh~% ¹öää@ÜÁàÊðäÁ Û™ÄúËöV÷Ðø"øåô¿ñ¬ò,ú‡Ø@‘ êô°âÌÚôàíòŒ üª#é;3$õUðò’öú‚ú,ù7ù‰ýf¡ÙUêLõéã¥Û áíò‹ —P'ú!ØWþUï‡é¹ìµô%ü®ÿ’ÿìþ-`1–WLò—ãDÞ³å°÷M –#®" Æû%ïºêöíTõ ü9Ö²& › + pKþžñaç€äjë¨ú »!)•  ûÚìæVè`ñü½{ . Ü ‚­7Jûòñlê—èòî®ü† V´ Y+ fû]ìä䓿ŽïÆûæú +[+ ÿ“ÿ/ú1õzñ]ð óÐùÀ @YãÐüOðFéäèþîjùßá} ÖýõvïGí5ï9õ`þÆÛ ›>p ùî©èé3ï>ùC` {´fäþäõ-ð%îEð#özþkÝÿ¶ð ³òûógíkìdð9ø¼P ³ " tþ÷"ó&òžôÆù[ì ¿4W ÃÎûiôªï/ïtó|ûôè ÆY< ¡tø?óAò&õeúÈà ü Q «TüIõÑïcîuòŠûõ¯Fv hþuófíîmôÀýSY ã ÷º(ÿþÒü•ú¸÷Ìõºö{û6A :¡0 .ÿŠõðÎð ÷Þÿ‘b ¢ ·¬þ1ýÐü4ü§úÁø!ø_úÔÿ ² Úƒú`ó1ñô¿û¡( ® {MFÉýìü¼üü§úùù“ûås¥ ý i ñhú7ôÌò„ö„ý® X Niý¿û%üHý²ýÒüiûû)ýÊd^ k ×ÿh÷ó3ô`ú‹ î " \Wÿâùž÷eø·úÌüªý¢ýóýÔÿy²f ® ÷‘ýCöòóßø|˜ @  Ùvü;ø¹öX÷ýøÑú¨üÛþÈChë ÃÉüþ2ùgõëôÜ÷)ýŒ– ×èÛÌýŽú“øø×øþú6þüƒÞN™7;ÿðûZúÖúùüÎÿ?Ž’/Úÿ¼þ½ý¾ü×ûoû üðýÝõMn+Ùý×ûíûìýÔJG„‡Nÿ¾ý9ý…ýþ;þþ½ýþ9ÿ<XšQwËÿ}ýŸü ý Í¢À& þü?üûüþùþpÿ«ÿ ÍÏœ®Ãeþ{ýèý¥ÿ Úð¡ÅþfüJû¨û#ýÿŠ^ƒK å¸Jƒÿ™þþ9þrÿin¯™ÀOþ‘üñûbü{ýºþÌÿ Sþ‹­}þ‰ü‰ûü4þA,âÇï?þTüÆû{üñýŠÿÒþÚFIÿÍý"ýcý§þŸ¦5#0ÿ\ý¤üôü þ‚ÿïÊÛØ6ÿ“ýpüDüJýWÿÞ6Û`ýiûtúÔúTü~þÌÃ|Ô"Oÿ¾ýÇüŸüMý¨þaVÛy;d\þüŽûpûGüßýØÿÀ-ب¹SÐÿ‹þÄý™ýþÝþøÿ³À(ôXÿ±ýqüþûŠüûýïÿÓ]¥>²ÿ‹þ þ~þgÿr8|7—âÿWÿÿÿ%ÿ+ÿ ÿ×þ³þÑþIÿ ÝvŸO²´ÿ³ÿöÿAX#ºÿWÿ7ÿvÿûÿˆ×¿G¤ÿÿÙþøþ`ÿêÿoØ<1ø ƒÿÿùþÿ\ÿ¯ÿñÿ2Lq“—\Øÿÿgþ÷ýþ²þÕÿ!1­i}: ÿEþþkþþþ‡ÿÛÿ_È0LÜÜÿ˜þ˜ýdýAþ ,Hÿ]ý£üýþtÿˆ J9íž 8ÿ]þÕý÷ýäþmR¤ê]€ÿëýýýÕýõþóz´°qêþþîý=ý<ý þ„ÿ<­g62± ÿæýAýBýÓýÆþãÿñÁ,‹œ†ÿ–þþ.þÞþíÿÖ*úfÇÿýþQþÜýÆý5þ3ÿ”úìMêeÿEþáý=þÿ¿)ÿ²<ÿbþÖýÛý„þ­ÿÿ…;OóþKþBþÂþ‚ÿ/‘ŸpŽÒ òf|ÿƒþãýîý±þêÿ"àß13]ÿ ÿUÿýÿ å­ŠÿBÿgÿáÿkÁÁ}#åÿÛÿöÿ¼ÿWÿþþßþÿ³ÿKÊÊ<>ÿ/þÅýüý¸þ²ÿ”6÷‘3ûÿëÿöÿ ÿÿùÿúÿ%éÿ“ÿ2ÿðþ÷þWÿõÿ•íÌ<ÿÿÿËÿðÓ0ÿÍý3ý˜ýËþI|þ¸çòÿ6ÿâþêþÿ=ÿAÿ@ÿqÿ9/vËFUÿœý²üâü þµÿ=]ITÿÒþØþ:ÿ¯ÿõÿ÷ÿ×ÿÔÿ(Û¶TP vþRýýÐý9ÿÁÛ4ÓkÿÿÙþÉþÍþüþvÿK_dõ¿«ïÿ þžü)üãüþʯ§`þ ?þ5ý2ý þIÿjI:4`§Ånƒ2ÿèý+ýXý{þ:ú;hþ5ýýØýDÿ¸³þ²–BãÿŠÿ ÿ˜þnþ»þuÿ_!t?¦ðÿfÿ9ÿkÿßÿfØ 8%í” ¡ÿ0ÿïþøþRÿæÿƒëìwªÿÍþ9þ2þËþÚÿìG Wsšÿîþwþ3þ,þ{þ6ÿT—•ß2¥©þæüüû@üŸý©ÿÀUM<Ñÿ þˆý³übüÑüþöÿÿ“'ÃzÿYýþûÃûŸü8þ ¢³,'É-YP#ÿþ2ýý—ýÖþd¿tOk+ÿYþ>þþÿ_ÿ”ÿÄÿ!É S‚ð°%ÿßýUý°ýµþäÿ¸ák®ÿÿÿÿ_q<ßÿUÿ1ÿuÿúÿ„äðºw-Öÿnÿýþ™þcþnþ½þ>ÿØÿu ü0h]"ÿþ¤ýÿýÿ¦÷û,×mÿTþÂý¸ý þþÿ·ÿqP<ðu 8ÿ‹ýüÆüôý¬ÿ?ü"Qÿ1ÿšÿ#Vñÿÿ;þìý‚þåÿ“Ô#_|þ<ýýöýhÿ¸V5Iÿ×þÿðÿÓ=äêÿÐþ9þ’þÔÿ}Í7xžþyý}ý•þ+wåU&üþjþ©þ…ÿ}÷PÿýþÿÕÿéåc/W"ïþþÇýþÄþ­ÿ„Cœ ‰ÿ,ÿþþõþÿ&ÿ_ÿ¼ÿJþ¶:PÓÎyÿ-þHý ýý¢þûÿ:Mø<Z“ÿÿÜþàþÿ)ÿ^ÿ¶ÿK ÆúiEÿyý;üíû¥ü%þöÿ“˜Øe{gkÿ´þWþQþ—þÿÄÿ‹X {|ôê‘ÿBþ]ý+ý»ýßþ<n,Y]‰±ÿïþ\þþ&þ§þ‰ÿ¤¶t›èqÿþOýVý,þƒÿàÊü¦ÒÿSÿDÿ‚ÿÒÿ bÌ&3Ïÿgþ5þ§þ¡ÿÓÒF*÷þ_þbþéþ±ÿkã'.öRCÿ þýõüËýÿ–X)¼:,DþýëüžýÍþðv¬²—H§¬ÿ~þyýýjý þMæêlN#AÿÌþ·þßþ%ÿ{ÿäÿaÝ38Ò ÿFþØýûý­þ¿ÿéÜ\NÀãúÿEÿïþÿfÿñÿxÝÙ_¤ÿ¼þÚýEý>ýæý$ÿ§Õï[ZA]ÿÜþÇþÿ~ÿ~ØÇX¼ÿÿ^þéýÎý#þÞþÙÿÛ©)ØIôÿiÿÿûþ%ÿÿôÿ^¢³DàÿwÿÿäþÜþ ÿjÿéÿná/SN%Ü|©ÿTÿÿ÷þåþßþëþÿqÿôÿ†ü%èR’ÿêþ•þ²þ9ÿÔ~ØÓoÀãÿÿEþ×ýÐý5þñþÞÿÊ„éãuµÍÿõþhþSþÊþ²ÿʺ09 ÿ[þ-þrþòþpÿÅÿ÷ÿ'zöØÈ:I=ÿqþ-þˆþeÿyhçÏ+."ÿLþÛýÚý8þÏþwÿè*â™Mëÿäÿôÿ!$íÿ¿ÿ•ÿ|ÿÿ ÿÕÿ7=íÿÅÿÆÿvþjWÏ{ÿ ÿàþêþÿÿ!ÿ ÿ>ÿ›ÿAÊÖï§ÿnþ¾ýèýñþˆ"1F¶ÿÛýnýÆý˜þxÿ 7èÿöÿQÖ=BÐUÿÿEÿ ·ÚUU9ÿbþ þ<þÊþsÿùÿ;8 ÞÿÌÿãÿ[’µÍëEW&ŸÒÿôþKþ þ:þ¾þ[ÿÝÿ,Rg}Œ}:ÆÿDÿðþÿÿz…R˜4@Úþþêý=þÚþ|ÿíÿ+:i·ÿÒDœÿ$ÿÿÿdHçžàvÿ#ÿÿóþÉþˆþTþdþÝþ·ÿ½šê]¥ ÆÿÒÿ ;>Öÿ½ÿæÿJºñ¹ ÿ8þÇýòý©þ¤ÿ‰5ðî Ù/5ÿ?þ³ýÙý´þøÿ,ÞÕ(.PÿÞþìþSÿÏÿ"8$8•koý(/ÿdþþIþýþæÿ³%$ÊMèÿÁÿ×ÿ :?âÿÁÿÕÿw··pÿoÿ„ÿÀÿñÿïÿ¯ÿNÿÿùþDÿÐÿnð9E"Þ…¦ÿ:ÿóþðþ>ÿÐÿ{E ¦rÿÿÿ#ÿcÿ¨ÿèÿ.‰üsľEdQÿXþÃý·ý,þñþÆÿvê%8.®*Žÿ ÿÖþÿÉÿº– ë=81ÿqþ#þBþ¦þÿnÿ™ÿªÿÃÿ}šÜ³FmÿÜþÂþ&ÿàÿ©6Pê%@ÿƒþ&þ;þ±þ[ÿå!ÚžXìÿßÿðÿ(%÷ÿ£ÿ:ÿàþºþáþUÿúÿ Ø_êÿ°ÿÈÿ' üé‘< ýÿ®ÿ$ÿ‰þ"þ,þÁþÀÿ߿ΠJÿãþòþ]ÿñÿ|á3,¤ ?ÿmþÓý­ýþûþ¢©=žÍÿÏÿúÿ!' ìÿîÿ u½Äk¾ÿòþQþþbþÿâÿš 2' ñ¯UÞÿèÿ)²ÿ0ÿÆþºþ0ÿØõX6ÿ5þ#þËþãÿø§ÅhÑCãÿ¬ÿ|ÿ4ÿÎþhþ6þgþÿ*Y(ÿþ`þLþ¯þTÿþÿ„Þ73îLNÿ#þ#ý­üüü þÿs26nÿÿûþ+ÿpÿ±ÿíÿ2‡à°3ÿŒþIþ…þ,ÿÃ--Ùi <ˆ¸£B´ÿ0ÿéþùþSÿËÿ+L$ÎÿvÿHÿ[ÿ§ÿIGøÿzÿÿÚþÿÓÿÁ˜ é:5*ÿeþþ8þ¹þhÿ§û£%œÿ&ÿäþêþ>ÿÏÿxK/· fÿÿöþ8ÿ™ÿáÿïÿÉÿ™ÿ–ÿßÿeí/ùTÿÚþ²þÿñÿÊJ<±óÿ_ÿ3ÿwÿûÿv­–SO“ªkØÿ)ÿ«þ¦þ1ÿ$)ØèL;ÿYþ7þ¸þŸÿŒ(Hýƒôÿ´ÿ:ÿÔþÈþ9ÿÅåWJ ÿAþõýEþÿâÿ¡"i¨¦nãÿ)þÒý,þ'ÿq–-5ÿRþ&þeþßþfÿæÿfõŽG># ÿTþ?þÌþ¾ÿ²IRØ`ÿõþíþ0ÿÿÙÿùÿùÿüÿ"sÖ½"ƒÿ'ÿ9ÿ¹ÿtZMMÿmþþý*þãþìÿñ¨êº>¥–ÿ4ÿêþÅþ×þ5ÿßÿ¶€úïVT5ÿPþëý$þåþðÿ÷¯ïº4˜âÿãÿ!"åÿÛÿýÿF•¨C­ÿÿÌþÛþJÿòÿš 'ö™;þÿëÿ÷ÿ üÿ üÿíÿùÿ,ÜÃ6•ÿÿÐþéþSÿîÿŒ0 ¢‡ÿÿñþÿTÿÌÿWÛ:[-µhÿùþèþ;ÿÔÿ}þ1 § œÿ1ÿëþÐþâþÿ€ÿøÿpÐÏt¡ÿYÿGÿtÿÜÿcá*"Å-‹ÿÿÚþûþbÿòÿ„ö,º(ˆÿÿÏþðþaÿûÿŽðò°bÍÿ„ÿ<ÿÿâþ÷þHÿËÿgõRd$ üÿdÿÿùþFÿÓÿuþKL š—ÿ/ÿëþØþúþTÿØÿpúSa—÷ÿrÿ5ÿXÿÐÿpû;¢ ‡ÿIÿZÿ¡ÿöÿ0;÷ÿÞÿäÿ$0÷ÿÕÿÖÿU¢Â™-¡ÿ)ÿðþÿ\ÿÅÿ6)êÿèÿýÿ"÷ÿáÿìÿ#|Ù ¹,ŽÿÿÖþôþ`ÿõÿ‰ñý²Tüÿ¸ÿÿzÿ|ÿ˜ÿÒÿ+”ô&³ †ÿÿìþÿcÿÂÿ&! úÿöÿýÿ  õÿñÿýÿ,(¼ÿyÿ[ÿ~ÿáÿdÛÅYðÿ£ÿzÿrÿ‚ÿ¨ÿãÿ4ã ¿A­ÿ(ÿÓþÃþùþfÿðÿ|ð8F®‹ÿÿåþÿmÿýÿˆê ÷¹iÂÿrÿ/ÿ ÿÿ]ÿÚÿoï1¼%‡ÿ ÿØþôþYÿëÿ†ÿ6´ ŠÿÿïþÿSÿ¶ÿ[†˜’r5ÛÿvÿÿõþÿlÿòÿtÆÍŠ©ÿaÿUÿÿÇÿ!þÿêÿðÿ *'ðÿŒÿ!ÿâþ÷þcÿ‘Õ²7™ÿÿæþÿbÿÉÿ1%îÿéÿ÷ÿ ôÿäÿëÿKˆ­¦n¹ÿwÿeÿ€ÿ¸ÿñÿ"üÿûÿ  þÿþÿ   ÿÿûÿþÿ ûÿìÿìÿ0#òÿ¬ÿsÿfÿ”ÿîÿR–£{6øÿÙÿßÿüÿ&ðÿèÿóÿ (óÿÂÿ—ÿÿŠÿ¯ÿâÿ)&õÿéÿóÿ #&êÿÔÿåÿ)ò#¡šÿXÿ]ÿšÿçÿ 2"ðÿðÿÿÿ þÿûÿÿÿ  ÿÿZoneMinder-1.26.5/distros/redhat/zm-init000066400000000000000000000020761225361755400201460ustar00rootroot00000000000000#!/bin/sh #$Id: zm-init,v 1.1 2005/04/19 00:49:53 hunter Exp $ # # Copyright (C) 2005 Serg Oskin # ZM_VERSION= ZM_CONFIG=/etc/zm.conf ZM_PATH=/usr/lib/zm if [ -f $ZM_CONFIG ]; then . $ZM_CONFIG else echo "ERROR: $ZM_CONFIG not found." exit 1 fi for n in ZM_DB_SERVER ZM_DB_NAME ZM_DB_USER ZM_DB_PASS; do eval "val=\$$n" if [ "$val" = "" ]; then echo "ERROR($ZM_CONFIG): $n should exist and be not empty." exit 1 fi done if [ "$ZM_DB_SERVER" = "localhost" ]; then ClientHost=localhost else ClientHost=`hostname` fi sql=/tmp/zm.crdb.sql echo "" >$sql chmod 600 $sql echo "CREATE DATABASE /*!32312 IF NOT EXISTS*/ $ZM_DB_NAME;" >>$sql echo "USE $ZM_DB_NAME;" >>$sql echo "GRANT all on $ZM_DB_NAME.* TO '$ZM_DB_USER'@'$ClientHost' IDENTIFIED BY '$ZM_DB_PASS';" >>$sql echo -n "Enter MySQL Administrator username: " read admin cat $sql | mysql -B -h $ZM_DB_SERVER -u $admin -p rm -f $sql cat /usr/lib/zm/init/zmschema.sql | mysql -h $ZM_DB_SERVER -u $ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME ( cd $ZM_PATH/init; perl $ZM_PATH/init/zmoptions -f $ZM_PATH/init/zmconfig.txt ) ZoneMinder-1.26.5/distros/redhat/zm-logrotate_d000066400000000000000000000001221225361755400214740ustar00rootroot00000000000000/var/log/zoneminder/*log { weekly notifempty missingok create 660 http http } ZoneMinder-1.26.5/distros/redhat/zoneminder-1.26.0-dbinstall.patch000066400000000000000000000047461225361755400245310ustar00rootroot00000000000000--- configure.ac 2013-09-05 10:33:08.000000000 -0500 +++ configure.ac.dbinstall 2013-09-05 17:23:28.555553447 -0500 @@ -1,13 +1,11 @@ AC_PREREQ(2.59) -AC_INIT(zm,1.26.0,[http://www.zoneminder.com/forums/ - Please check FAQ first],ZoneMinder,http://www.zoneminder.com/downloads.html) +AC_INIT(zm,1.26.0,[http://www.zoneminder.com/forums/ - Please check FAQ first],zoneminder,http://www.zoneminder.com/downloads.html) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR(src/zm.h) AC_CONFIG_HEADERS(config.h) AC_SUBST([AM_CXXFLAGS], [-D__STDC_CONSTANT_MACROS]) -PATH_BUILD=`pwd` -AC_SUBST(PATH_BUILD) TIME_BUILD=`date +'%s'` AC_SUBST(TIME_BUILD) @@ -354,6 +352,8 @@ AC_PROG_PERL_MODULES(X10::ActiveHome,,AC AC_DEFINE_DIR([BINDIR],[bindir],[Expanded binary directory]) AC_DEFINE_DIR([LIBDIR],[libdir],[Expanded library directory]) +AC_DEFINE_DIR([DATADIR],[datadir],[Expanded data directory]) +AC_SUBST(PKGDATADIR,"$DATADIR/$PACKAGE") AC_SUBST(ZM_PID,"$ZM_RUNDIR/zm.pid") AC_DEFINE_DIR([SYSCONFDIR],[sysconfdir],[Expanded configuration directory]) AC_SUBST(ZM_CONFIG,"$SYSCONFDIR/zm.conf") diff -up ./db/Makefile.am.dbinstall ./db/Makefile.am --- ./db/Makefile.am.dbinstall 2009-10-14 04:42:46.000000000 -0500 +++ ./db/Makefile.am 2011-03-24 22:50:14.173912137 -0500 @@ -1,7 +1,16 @@ AUTOMAKE_OPTIONS = gnu +zmdbdatadir = $(pkgdatadir)/db + EXTRA_DIST = \ zm_create.sql.in \ + $(dbupgrade_scripts) + +dist_zmdbdata_DATA = \ + zm_create.sql \ + $(dbupgrade_scripts) + +dbupgrade_scripts = \ zm_update-0.0.1.sql \ zm_update-0.9.7.sql \ zm_update-0.9.8.sql \ diff -up ./scripts/zmupdate.pl.in.dbinstall ./scripts/zmupdate.pl.in --- ./scripts/zmupdate.pl.in.dbinstall 2011-08-27 15:44:05.335602405 -0500 +++ ./scripts/zmupdate.pl.in 2011-08-26 02:51:37.000000000 -0500 @@ -424,7 +424,7 @@ if ( $version ) } else { - $command .= ZM_PATH_BUILD."/db"; + $command .= ZM_PATH_DATA."/db"; } $command .= "/zm_update-".$version.".sql"; diff -up ./zm.conf.in.dbinstall ./zm.conf.in --- ./zm.conf.in.dbinstall 2008-07-25 04:48:16.000000000 -0500 +++ ./zm.conf.in 2011-03-24 22:50:14.175912077 -0500 @@ -12,8 +12,8 @@ # Current version of ZoneMinder ZM_VERSION=@VERSION@ -# Path to build directory, used mostly for finding DB upgrade scripts -ZM_PATH_BUILD=@PATH_BUILD@ +# Path to installed data directory, used mostly for finding DB upgrade scripts +ZM_PATH_DATA=@PKGDATADIR@ # Build time, used to record when to trigger various checks ZM_TIME_BUILD=@TIME_BUILD@ ZoneMinder-1.26.5/distros/redhat/zoneminder-1.26.0-defaults.patch000066400000000000000000000117061225361755400243560ustar00rootroot00000000000000--- configure.ac 2013-08-15 11:44:10.000000000 -0500 +++ configure.ac.logdir 2013-08-17 09:20:07.326053328 -0500 @@ -46,7 +46,7 @@ AC_SUBST(ZM_TMPDIR,[/tmp/zm]) fi if test "$ZM_LOGDIR" == ""; then - AC_SUBST(ZM_LOGDIR,[/var/log/zm]) + AC_SUBST(ZM_LOGDIR,[/var/log/zoneminder]) fi LIB_ARCH=lib --- scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in 2013-08-01 18:14:45.175241378 -0500 +++ scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in.defaults 2013-08-07 18:57:42.525006149 -0500 @@ -187,7 +187,7 @@ }, { name => "ZM_PATH_ZMS", - default => "/cgi-bin/nph-zms", + default => "/cgi-bin/zm/nph-zms", description => "Web path to zms streaming server", help => "The ZoneMinder streaming server is required to send streamed images to your browser. It will be installed into the cgi-bin path given at configuration time. This option determines what the web path to the server is rather than the local path on your machine. Ordinarily the streaming server runs in parser-header mode however if you experience problems with streaming you can change this to non-parsed-header (nph) mode by changing 'zms' to 'nph-zms'.", type => $types{rel_path}, @@ -276,7 +276,7 @@ }, { name => "ZM_OPT_CAMBOZOLA", - default => "no", + default => "yes", description => "Is the (optional) cambozola java streaming client installed", help => "Cambozola is a handy low fat cheese flavoured Java applet that ZoneMinder uses to view image streams on browsers such as Internet Explorer that don't natively support this format. If you use this browser it is highly recommended to install this from http://www.charliemouse.com/code/cambozola/ however if it is not installed still images at a lower refresh rate can still be viewed.", type => $types{boolean}, @@ -526,7 +526,7 @@ }, { name => "ZM_LOG_DEBUG_FILE", - default => "@ZM_TMPDIR@/zm_debug.log+", + default => "/var/log/zoneminder/zm_debug_log+", description => "Where extra debug is output to", help => "This option allows you to specify a different target for debug output. All components have a default log file which will norally be in /tmp or /var/log and this is where debug will be written to if this value is empty. Adding a path here will temporarily redirect debug, and other logging output, to this file. This option is a simple filename and you are debugging several components then they will all try and write to the same file with undesirable consequences. Appending a '+' to the filename will cause the file to be created with a '.' suffix containing your process id. In this way debug from each run of a component is kept separate. This is the recommended setting as it will also prevent subsequent runs from overwriting the same log. You should ensure that permissions are set up to allow writing to the file and directory specified here.", requires => [ { name => "ZM_LOG_DEBUG", value => "yes" } ], @@ -623,7 +623,7 @@ }, { name => "ZM_PATH_SOCKS", - default => "@ZM_TMPDIR@", + default => "/var/lib/zoneminder/sock", description => "Path to the various Unix domain socket files that ZoneMinder uses", help => "ZoneMinder generally uses Unix domain sockets where possible. This reduces the need for port assignments and prevents external applications from possibly compromising the daemons. However each Unix socket requires a .sock file to be created. This option indicates where those socket files go.", type => $types{abs_path}, @@ -639,7 +639,7 @@ }, { name => "ZM_PATH_SWAP", - default => "@ZM_TMPDIR@", + default => "/dev/shm", description => "Path to location for temporary swap images used in streaming", help => "Buffered playback requires temporary swap images to be stored for each instance of the streaming daemons. This option determines where these images will be stored. The images will actually be stored in sub directories beneath this location and will be automatically cleaned up after a period of time.", type => $types{abs_path}, @@ -902,7 +902,7 @@ }, { name => "ZM_UPLOAD_FTP_LOC_DIR", - default => "@ZM_TMPDIR@", + default => "/var/spool/zoneminder-upload", description => "The local directory in which to create upload files", help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates the local directory that ZoneMinder should use for temporary upload files. These are files that are created from events, uploaded and then deleted.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], @@ -1258,7 +1258,7 @@ }, { name => "ZM_OPT_CONTROL", - default => "no", + default => "yes", description => "Support controllable (e.g. PTZ) cameras", help => "ZoneMinder includes limited support for controllable cameras. A number of sample protocols are included and others can easily be added. If you wish to control your cameras via ZoneMinder then select this option otherwise if you only have static cameras or use other control methods then leave this option off.", type => $types{boolean}, ZoneMinder-1.26.5/distros/redhat/zoneminder-runlevel.patch000066400000000000000000000010221225361755400236470ustar00rootroot00000000000000diff -up ./scripts/zm.in.runlevel ./scripts/zm.in --- ./scripts/zm.in.runlevel 2010-11-28 15:22:05.000000000 -0600 +++ ./scripts/zm.in 2011-03-24 21:39:01.973010160 -0500 @@ -1,6 +1,6 @@ #!/bin/sh # description: ZoneMinder is the top Linux video camera security and surveillance solution. ZoneMinder is intended for use in single or multi-camera video security applications.Copyright: Philip Coombes, Corey DeLasaux 2003-2008 -# chkconfig: 2345 99 00 +# chkconfig: - 99 00 # processname: zmpkg.pl # Source function library. ZoneMinder-1.26.5/distros/redhat/zoneminder.cmake.el6.spec000066400000000000000000000276711225361755400234360ustar00rootroot00000000000000%define zmuid $(id -un) %define zmgid $(id -gn) %define zmuid_final apache %define zmgid_final apache Name: zoneminder Version: 1.26.4 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons # jscalendar is LGPL (any version): http://www.dynarch.com/projects/calendar/ # Mootools is inder the MIT license: http://mootools.net/ # Cambozola is GPL: http://www.charliemouse.com/code/cambozola/ License: GPLv2+ and LGPLv2+ and MIT URL: http://www.zoneminder.com/ #Source0: https://github.com/ZoneMinder/ZoneMinder/archive/v%{version}.tar.gz Source0: ZoneMinder-%{version}.tar.gz Patch1: zoneminder-1.26.0-defaults.patch BuildRequires: cmake gnutls-devel bzip2-devel BuildRequires: mysql-devel pcre-devel libjpeg-turbo-devel BuildRequires: perl(Archive::Tar) perl(Archive::Zip) BuildRequires: perl(Date::Manip) perl(DBD::mysql) BuildRequires: perl(ExtUtils::MakeMaker) perl(LWP::UserAgent) BuildRequires: perl(MIME::Entity) perl(MIME::Lite) BuildRequires: perl(PHP::Serialization) perl(Sys::Mmap) BuildRequires: perl(Time::HiRes) perl(Net::SFTP::Foreign) BuildRequires: perl(Expect) perl(X10::ActiveHome) perl(Astro::SunTime) BuildRequires: ffmpeg-devel >= 0.4.9 Requires: httpd php php-mysql mysql-server libjpeg-turbo Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) Requires: perl(DBD::mysql) perl(Archive::Tar) perl(Archive::Zip) Requires: perl(MIME::Entity) perl(MIME::Lite) perl(Net::SMTP) perl(Net::FTP) Requires: ffmpeg >= 0.4.9 Requires(post): /sbin/chkconfig Requires(post): /usr/bin/checkmodule Requires(post): /usr/bin/semodule_package Requires(post): /usr/sbin/semodule Requires(post): /usr/bin/gpasswd Requires(post): /usr/bin/less Requires(preun): /sbin/chkconfig Requires(preun): /sbin/service Requires(preun): /usr/sbin/semodule Requires(postun): /sbin/service %description ZoneMinder is a set of applications which is intended to provide a complete solution allowing you to capture, analyse, record and monitor any cameras you have attached to a Linux based machine. It is designed to run on kernels which support the Video For Linux (V4L) interface and has been tested with cameras attached to BTTV cards, various USB cameras and IP network cameras. It is designed to support as many cameras as you can attach to your computer without too much degradation of performance. %prep %setup -q -n ZoneMinder-%{version} %patch1 -p0 -b .defaults %build # Have to override CMAKE_INSTALL_LIBDIR for cmake < 2.8.7 due to this bug: # https://bugzilla.redhat.com/show_bug.cgi?id=795542 %cmake -DZM_TARGET_DISTRO="el6" -DCMAKE_INSTALL_LIBDIR:PATH=%{_lib} -DZM_PERL_SUBPREFIX=`x="%{perl_vendorlib}" ; echo ${x#"%{_prefix}"}` . make %{?_smp_mflags} %install export DESTDIR=%{buildroot} make install %post /sbin/chkconfig --add zoneminder /sbin/chkconfig zoneminder on # Allow zoneminder access to local video sources echo /usr/bin/gpasswd -a apache video # Create and load zoneminder selinux policy module echo -e "\nCreating and installing a ZoneMinder SELinux policy module. Please wait.\n" /usr/bin/checkmodule -M -m -o %{_docdir}/%{name}-%{version}/local_zoneminder.mod %{_docdir}/%{name}-%{version}/local_zoneminder.te > /dev/null /usr/bin/semodule_package -o %{_docdir}/%{name}-%{version}/local_zoneminder.pp -m %{_docdir}/%{name}-%{version}/local_zoneminder.mod > /dev/null /usr/sbin/semodule -i %{_docdir}/%{name}-%{version}/local_zoneminder.pp > /dev/null # Display the README for post installation instructions /usr/bin/less %{_docdir}/%{name}-%{version}/README.CentOS %preun if [ $1 -eq 0 ]; then /sbin/service zoneminder stop > /dev/null 2>&1 || : /sbin/chkconfig --del zoneminder echo -e "\nRemoving ZoneMinder SELinux policy module. Please wait.\n" /usr/sbin/semodule -r local_zoneminder.pp fi %postun if [ $1 -ge 1 ]; then /sbin/service zoneminder condrestart > /dev/null 2>&1 || : fi # Remove the doc folder. rm -rf %{_docdir}/%{name}-%{version} %files %defattr(-,root,root,-) %doc AUTHORS BUGS ChangeLog COPYING LICENSE NEWS README.md distros/redhat/README.CentOS distros/redhat/jscalendar-doc %doc distros/redhat/cambozola-doc distros/redhat/local_zoneminder.te %config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm.conf %config(noreplace) %attr(644,root,root) %{_sysconfdir}/httpd/conf.d/zoneminder.conf %config(noreplace) /etc/logrotate.d/%{name} %attr(755,root,root) %{_initrddir}/zoneminder %{_bindir}/zma %{_bindir}/zmaudit.pl %{_bindir}/zmc %{_bindir}/zmcontrol.pl %{_bindir}/zmdc.pl %{_bindir}/zmf %{_bindir}/zmfilter.pl %attr(4755,root,root) %{_bindir}/zmfix %{_bindir}/zmpkg.pl %{_bindir}/zmstreamer %{_bindir}/zmtrack.pl %{_bindir}/zmtrigger.pl %{_bindir}/zmu %{_bindir}/zmupdate.pl %{_bindir}/zmvideo.pl %{_bindir}/zmwatch.pl %{_bindir}/zmx10.pl %{perl_vendorlib}/ZoneMinder* %{perl_vendorlib}/x86_64-linux-thread-multi/auto/ZoneMinder* %{_mandir}/man*/* %dir %{_libexecdir}/%{name} %{_libexecdir}/%{name}/cgi-bin %dir %{_datadir}/%{name} %{_datadir}/%{name}/db %{_datadir}/%{name}/www %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/lib/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/lib/zoneminder/events %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/lib/zoneminder/images %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/lib/zoneminder/sock %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/lib/zoneminder/swap %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/lib/zoneminder/temp %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/log/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/spool/zoneminder-upload %changelog * Sat Oct 19 2013 Andrew Bauer - 1.26.4 - Streamline the cmake build. Move much code into cmakelist.txt file. * Mon Oct 07 2013 Andrew Bauer - 1.26.4 - Initial cmake build. * Sun Oct 06 2013 Andrew Bauer - 1.26.4 - All files are now part of the zoneminder source tree. Update specfile accordingly. * Thu Sep 05 2013 Andrew Bauer - 1.26.0 - 1.26.0 Release - https://github.com/ZoneMinder/ZoneMinder/archive/v1.26.0.tar.gz * Sun Sep 01 2013 Andrew Bauer - 1.26.0-beta - Update SELinux policy module * Thu Aug 29 2013 Andrew Bauer - 1.26.0-beta - Third Beta release - https://github.com/ZoneMinder/ZoneMinder/tree/release-1.26 - Reduce number of uneeded dependencies by integrating cambozola into spec file * Thu Aug 15 2013 Andrew Bauer - 1.26.0-beta - Initial Beta release - https://github.com/ZoneMinder/ZoneMinder/tree/release-1.26 * Sun Aug 11 2013 Andrew Bauer - 1.25.0-kfirproper - Modified specfile to work with kfir-proper branch - https://github.com/ZoneMinder/ZoneMinder/tree/kfir-proper * Wed Aug 07 2013 Andrew Bauer - 1.25.0-2svn3827 - Move RHEL/CentOS specific defaults to a patch file - Add bzip2-devel as a build dependency - Default ZM_SSL_LIB back to gnutls. AUTH_RELAY = hashed didn't work with openssl. * Fri Aug 02 2013 Andrew Bauer - 1.25.0-1svn3827 - Update to latest 1.25.0 subversion. - Does not compile with modern versions of ffmpeg. Configure to work only with older versions. - Does not compile with gcc 4.7. Configure to build with gcc less than 4.7. * Thu Mar 24 2011 Jason L Tibbitts III - 1.24.3-4.20110324svn3310 - Update to latest 1.24.3 subversion. Turns out that what upstream was calling 1.24.3 is really just an occasionally updated devel snapshot. - Rebase various patches. * Wed Mar 23 2011 Dan Horák - 1.24.3-3 - rebuilt for mysql 5.5.10 (soname bump in libmysqlclient) * Tue Feb 08 2011 Fedora Release Engineering - 1.24.3-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild * Tue Jan 25 2011 Jason L Tibbitts III - 1.24.3-1 - Update to latest upstream version. - Rebase patches. - Initial incomplete attempt to disable v4l1 support. * Fri Jan 21 2011 Jason L Tibbitts III - 1.24.2-6 - Unbundle cambozola; instead link to the separately pacakged copy. - Remove BuildRoot:, %%clean and buildroot cleaning in %%install. - Git rid of mixed space/tab usage by removing all tabs. - Remove unnecessary Conflicts: line. - Attempt to force short_open_tag on for the code directories. - Move default location of sockets, swaps, logfiles and some temporary files to make more sense and allow things to work better with a future selinux policy. - Fix errors in README.CentOS. * Wed Jun 02 2010 Marcela Maslanova - 1.24.2-5 - Mass rebuild with perl-5.12.0 * Fri Dec 4 2009 Stepan Kasal - 1.24.2-4 - rebuild against perl 5.10.1 - use Perl vendorarch and archlib variables correctly * Mon Jul 27 2009 Fedora Release Engineering - 1.24.2-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild * Wed Jul 22 2009 Jason L Tibbitts III - 1.24.2-2 - Bump release since 1.24.2-1 was mistakenly tagged a few months ago. * Wed Jul 22 2009 Jason L Tibbitts III - 1.24.2-1 - Initial update to 1.24.2. - Rebase patches. - Update mootools download location. - Update to mootools 1.2.3. - Add additional dependencies for some optional features. * Sat Apr 11 2009 Martin Ebourne - 1.24.1-3 - Remove unused Sys::Mmap perl dependency RPM is finding * Sat Apr 11 2009 Martin Ebourne - 1.24.1-2 - Update gcc44 patch to disable -frepo, seems to be broken with gcc44 - Added noffmpeg patch to make building outside mock easier * Sat Mar 21 2009 Martin Ebourne - 1.24.1-1 - Patch for gcc 4.4 compilation errors - Upgrade to 1.24.1 * Wed Feb 25 2009 Fedora Release Engineering - 1.23.3-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild * Sat Jan 24 2009 Caolán McNamara - 1.23.3-3 - rebuild for dependencies * Mon Dec 15 2008 Martin Ebourne - 1.23.3-2 - Fix permissions on zm.conf * Fri Jul 11 2008 Jason L Tibbitts III - 1.23.3-1 - Initial attempt at packaging 1.23. * Tue Jul 1 2008 Martin Ebourne - 1.22.3-15 - Add perl module compat dependency, bz #453590 * Tue May 6 2008 Martin Ebourne - 1.22.3-14 - Remove default runlevel, bz #441315 * Mon Apr 28 2008 Jason L Tibbitts III - 1.22.3-13 - Backport patch for CVE-2008-1381 from 1.23.3 to 1.22.3. * Tue Feb 19 2008 Fedora Release Engineering - 1.22.3-12 - Autorebuild for GCC 4.3 * Thu Jan 3 2008 Martin Ebourne - 1.22.3-11 - Fix compilation on gcc 4.3 * Thu Dec 6 2007 Martin Ebourne - 1.22.3-10 - Rebuild for new openssl * Thu Aug 2 2007 Martin Ebourne - 1.22.3-8 - Fix licence tag * Thu Jul 12 2007 Martin Ebourne - 1.22.3-7 - Fixes from testing by Jitz including missing dependencies and database creation * Sat Jun 30 2007 Martin Ebourne - 1.22.3-6 - Disable crashtrace on ppc * Sat Jun 30 2007 Martin Ebourne - 1.22.3-5 - Fix uid for directories in /var/lib/zoneminder * Tue Jun 26 2007 Martin Ebourne - 1.22.3-4 - Added perl Archive::Tar dependency - Disabled web interface due to lack of access control on the event images * Sun Jun 10 2007 Martin Ebourne - 1.22.3-3 - Changes recommended in review by Jason Tibbitts * Mon Apr 2 2007 Martin Ebourne - 1.22.3-2 - Standardised on package name of zoneminder * Thu Dec 28 2006 Martin Ebourne - 1.22.3-1 - First version. Uses some parts from zm-1.20.1 by Corey DeLasaux and Serg Oskin ZoneMinder-1.26.5/distros/redhat/zoneminder.conf000066400000000000000000000022741225361755400216550ustar00rootroot00000000000000# The Zoneminder web interface has been disabled by default due to a small # security issue in the default install. # # When using Zoneminder's own authentication, recorded CCTV images are # accessible from the web directly without passing the authentication. This # means any attacker could see your CCTV images without a password. In order # to avoid this you can disable Zoneminder's authentication and configure # standard Apache authentication (see the Apache documentation for details on # this). # # If you still wish to use Zoneminder's own authentication, or have an # internal site which needs no authentication, you need to delete the line # marked below and restart Apache. Alias /zm "/usr/share/zoneminder/www" Options -Indexes MultiViews FollowSymLinks AllowOverride All Order allow,deny Allow from all # The code unfortunately uses short tags in many places php_value short_open_tag 1 Deny from all # DELETE THIS LINE ScriptAlias /cgi-bin/zm "/usr/libexec/zoneminder/cgi-bin" AllowOverride All Options ExecCGI Order allow,deny Allow from all ZoneMinder-1.26.5/distros/redhat/zoneminder.el6.spec000066400000000000000000000363371225361755400223560ustar00rootroot00000000000000%define cambrev 0.931 %define moorev 1.3.2 %define jscrev 1.0 %define zmuid $(id -un) %define zmgid $(id -gn) %define zmuid_final apache %define zmgid_final apache Name: zoneminder Version: 1.26.4 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons # jscalendar is LGPL (any version): http://www.dynarch.com/projects/calendar/ # Mootools is inder the MIT license: http://mootools.net/ # Cambozola is GPL: http://www.charliemouse.com/code/cambozola/ License: GPLv2+ and LGPLv2+ and MIT URL: http://www.zoneminder.com/ #Source0: https://github.com/ZoneMinder/ZoneMinder/archive/v%{version}.tar.gz Source0: ZoneMinder-%{version}.tar.gz Source1: jscalendar-%{jscrev}.zip #Source1: http://downloads.sourceforge.net/jscalendar/jscalendar-%{jscrev}.zip # Mootools is currently bundled in the zoneminder tarball #Source2: mootools-core-%{moorev}-full-compat-yc.js #Source2: http://mootools.net/download/get/mootools-core-%{moorev}-full-compat-yc.js Source3: cambozola-%{cambrev}.tar.gz #Source3: http://www.andywilcock.com/code/cambozola/cambozola-%{cambrev}.tar.gz #Patch1: zoneminder-1.26.4-dbinstall.patch Patch2: zoneminder-runlevel.patch #Patch3: zoneminder-1.25.0-installfix.patch Patch4: zoneminder-1.26.0-defaults.patch # BuildRoot is depreciated and ignored in EPEL6 #BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: automake gnutls-devel bzip2-devel libtool BuildRequires: mysql-devel pcre-devel libjpeg-turbo-devel BuildRequires: perl(Archive::Tar) perl(Archive::Zip) BuildRequires: perl(Date::Manip) perl(DBD::mysql) BuildRequires: perl(ExtUtils::MakeMaker) perl(LWP::UserAgent) BuildRequires: perl(MIME::Entity) perl(MIME::Lite) BuildRequires: perl(PHP::Serialization) perl(Sys::Mmap) BuildRequires: perl(Time::HiRes) perl(Net::SFTP::Foreign) BuildRequires: perl(Expect) perl(X10::ActiveHome) perl(Astro::SunTime) BuildRequires: ffmpeg-devel >= 0.4.9 Requires: httpd php php-mysql mysql-server libjpeg-turbo Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) Requires: perl(DBD::mysql) perl(Archive::Tar) perl(Archive::Zip) Requires: perl(MIME::Entity) perl(MIME::Lite) perl(Net::SMTP) perl(Net::FTP) Requires: ffmpeg >= 0.4.9 Requires(post): /sbin/chkconfig Requires(post): /usr/bin/checkmodule Requires(post): /usr/bin/semodule_package Requires(post): /usr/sbin/semodule Requires(post): /usr/bin/gpasswd Requires(post): /usr/bin/less Requires(preun): /sbin/chkconfig Requires(preun): /sbin/service Requires(preun): /usr/sbin/semodule Requires(postun): /sbin/service %description ZoneMinder is a set of applications which is intended to provide a complete solution allowing you to capture, analyse, record and monitor any cameras you have attached to a Linux based machine. It is designed to run on kernels which support the Video For Linux (V4L) interface and has been tested with cameras attached to BTTV cards, various USB cameras and IP network cameras. It is designed to support as many cameras as you can attach to your computer without too much degradation of performance. %prep %setup -q -n ZoneMinder-%{version} # Unpack jscalendar and move some files around %setup -q -D -T -a 1 -n ZoneMinder-%{version} mkdir jscalendar-doc pushd jscalendar-%{jscrev} mv *html *php doc/* README ../jscalendar-doc rmdir doc popd # Unpack Cambozola and move some files around %setup -q -D -T -a 3 -n ZoneMinder-%{version} mkdir cambozola-doc pushd cambozola-%{cambrev} mv application.properties build.xml dist.sh *html LICENSE testPages/* ../cambozola-doc rmdir testPages popd #%patch1 -p0 -b .dbinstall %patch2 -p0 -b .runlevel #%patch3 -p0 -b .installfix %patch4 -p0 %build libtoolize --force aclocal autoheader automake --force-missing --add-missing autoconf OPTS="" %ifnarch %{ix86} x86_64 OPTS="$OPTS --disable-crashtrace" %endif %configure \ --with-libarch=%{_lib} \ %ifarch %{ix86} %{x8664} --enable-crashtrace \ %else --disable-crashtrace \ %endif --with-mysql=%{_prefix} \ --with-ffmpeg=%{_prefix} \ --with-webdir=%{_datadir}/%{name}/www \ --with-cgidir=%{_libexecdir}/%{name}/cgi-bin \ --with-webuser=%{zmuid} \ --with-webgroup=%{zmgid} \ --enable-mmap=yes \ --disable-debug \ --with-webhost=zm.local \ ZM_SSL_LIB="gnutls" \ ZM_RUNDIR=/var/run/zoneminder \ ZM_TMPDIR=/var/lib/zoneminder/temp \ %ifarch x86_64 CXXFLAGS="-D__STDC_CONSTANT_MACROS -msse2" \ %else CXXFLAGS="-D__STDC_CONSTANT_MACROS" \ %endif --with-extralibs="" make %{?_smp_mflags} %{__perl} -pi -e 's/(ZM_WEB_USER=).*$/${1}%{zmuid_final}/;' \ -e 's/(ZM_WEB_GROUP=).*$/${1}%{zmgid_final}/;' zm.conf %install install -d %{buildroot}/%{_localstatedir}/run install -d %{buildroot}/etc/logrotate.d make install DESTDIR=%{buildroot} \ INSTALLDIRS=vendor rm -rf %{buildroot}/%{perl_vendorarch} %{buildroot}/%{perl_archlib} install -m 755 -d %{buildroot}/%{_localstatedir}/log/zoneminder for dir in events images temp do install -m 755 -d %{buildroot}/%{_localstatedir}/lib/zoneminder/$dir if [ -d %{buildroot}/%{_datadir}/zoneminder/www/$dir ]; then rmdir %{buildroot}/%{_datadir}/%{name}/www/$dir fi ln -sf ../../../..%{_localstatedir}/lib/zoneminder/$dir %{buildroot}/%{_datadir}/%{name}/www/$dir done install -m 755 -d %{buildroot}/%{_localstatedir}/lib/zoneminder/sock install -m 755 -d %{buildroot}/%{_localstatedir}/lib/zoneminder/swap install -m 755 -d %{buildroot}/%{_localstatedir}/spool/zoneminder-upload install -D -m 755 scripts/zm %{buildroot}/%{_initrddir}/zoneminder install -D -m 644 distros/redhat/zoneminder.conf %{buildroot}/%{_sysconfdir}/httpd/conf.d/zoneminder.conf install -D -m 755 distros/redhat/redalert.wav %{buildroot}/%{_datadir}/%{name}/www/sounds/redalert.wav install distros/redhat/zm-logrotate_d $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/%{name} # Install jscalendar install -d -m 755 %{buildroot}/%{_datadir}/%{name}/www/jscalendar cp -rp jscalendar-%{jscrev}/* %{buildroot}/%{_datadir}/%{name}/www/jscalendar # Install Cambozola cp -rp cambozola-%{cambrev}/dist/cambozola.jar %{buildroot}/%{_datadir}/%{name}/www/ rm -rf cambozola-%{cambrev} # Install mootools pushd %{buildroot}/%{_datadir}/%{name}/www #install -m 644 %{Source2} mootools-core-%{moorev}-full-compat-yc.js #ln -s mootools-core-%{moorev}-full-compat-yc.js mootools.js ln -f -s tools/mootools/mootools-core-%{moorev}-yc.js mootools-core.js ln -f -s tools/mootools/mootools-more-%{moorev}.1-yc.js mootools-more.js popd %post /sbin/chkconfig --add zoneminder /sbin/chkconfig zoneminder on # Allow zoneminder access to local video sources echo /usr/bin/gpasswd -a apache video # Create and load zoneminder selinux policy module echo -e "\nCreating and installing a ZoneMinder SELinux policy module. Please wait.\n" /usr/bin/checkmodule -M -m -o %{_docdir}/%{name}-%{version}/local_zoneminder.mod %{_docdir}/%{name}-%{version}/local_zoneminder.te > /dev/null /usr/bin/semodule_package -o %{_docdir}/%{name}-%{version}/local_zoneminder.pp -m %{_docdir}/%{name}-%{version}/local_zoneminder.mod > /dev/null /usr/sbin/semodule -i %{_docdir}/%{name}-%{version}/local_zoneminder.pp > /dev/null # Display the README for post installation instructions /usr/bin/less %{_docdir}/%{name}-%{version}/README.CentOS %preun if [ $1 -eq 0 ]; then /sbin/service zoneminder stop > /dev/null 2>&1 || : /sbin/chkconfig --del zoneminder echo -e "\nRemoving ZoneMinder SELinux policy module. Please wait.\n" /usr/sbin/semodule -r local_zoneminder.pp fi %postun if [ $1 -ge 1 ]; then /sbin/service zoneminder condrestart > /dev/null 2>&1 || : fi %files %defattr(-,root,root,-) %doc AUTHORS BUGS ChangeLog COPYING LICENSE NEWS README.md distros/redhat/README.CentOS jscalendar-doc cambozola-doc distros/redhat/local_zoneminder.te %config %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm.conf %config(noreplace) %attr(644,root,root) %{_sysconfdir}/httpd/conf.d/zoneminder.conf %config(noreplace) /etc/logrotate.d/%{name} %attr(755,root,root) %{_initrddir}/zoneminder %{_bindir}/zma %{_bindir}/zmaudit.pl %{_bindir}/zmc %{_bindir}/zmcontrol.pl %{_bindir}/zmdc.pl %{_bindir}/zmf %{_bindir}/zmfilter.pl %attr(4755,root,root) %{_bindir}/zmfix %{_bindir}/zmpkg.pl %{_bindir}/zmstreamer %{_bindir}/zmtrack.pl %{_bindir}/zmtrigger.pl %{_bindir}/zmu %{_bindir}/zmupdate.pl %{_bindir}/zmvideo.pl %{_bindir}/zmwatch.pl %{_bindir}/zmx10.pl %{perl_vendorlib}/ZoneMinder* %{_mandir}/man*/* %dir %{_libexecdir}/%{name} %{_libexecdir}/%{name}/cgi-bin %dir %{_datadir}/%{name} %{_datadir}/%{name}/db %{_datadir}/%{name}/www %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/lib/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/lib/zoneminder/events %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/lib/zoneminder/images %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/lib/zoneminder/sock %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/lib/zoneminder/swap %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/lib/zoneminder/temp %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/log/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/spool/zoneminder-upload %changelog * Sun Oct 06 2013 Andrew Bauer - 1.26.4 - All files are now part of the zoneminder source tree. Update specfile accordingly. * Thu Sep 05 2013 Andrew Bauer - 1.26.0 - 1.26.0 Release - https://github.com/ZoneMinder/ZoneMinder/archive/v1.26.0.tar.gz * Sun Sep 01 2013 Andrew Bauer - 1.26.0-beta - Update SELinux policy module * Thu Aug 29 2013 Andrew Bauer - 1.26.0-beta - Third Beta release - https://github.com/ZoneMinder/ZoneMinder/tree/release-1.26 - Reduce number of uneeded dependencies by integrating cambozola into spec file * Thu Aug 15 2013 Andrew Bauer - 1.26.0-beta - Initial Beta release - https://github.com/ZoneMinder/ZoneMinder/tree/release-1.26 * Sun Aug 11 2013 Andrew Bauer - 1.25.0-kfirproper - Modified specfile to work with kfir-proper branch - https://github.com/ZoneMinder/ZoneMinder/tree/kfir-proper * Wed Aug 07 2013 Andrew Bauer - 1.25.0-2svn3827 - Move RHEL/CentOS specific defaults to a patch file - Add bzip2-devel as a build dependency - Default ZM_SSL_LIB back to gnutls. AUTH_RELAY = hashed didn't work with openssl. * Fri Aug 02 2013 Andrew Bauer - 1.25.0-1svn3827 - Update to latest 1.25.0 subversion. - Does not compile with modern versions of ffmpeg. Configure to work only with older versions. - Does not compile with gcc 4.7. Configure to build with gcc less than 4.7. * Thu Mar 24 2011 Jason L Tibbitts III - 1.24.3-4.20110324svn3310 - Update to latest 1.24.3 subversion. Turns out that what upstream was calling 1.24.3 is really just an occasionally updated devel snapshot. - Rebase various patches. * Wed Mar 23 2011 Dan Horák - 1.24.3-3 - rebuilt for mysql 5.5.10 (soname bump in libmysqlclient) * Tue Feb 08 2011 Fedora Release Engineering - 1.24.3-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild * Tue Jan 25 2011 Jason L Tibbitts III - 1.24.3-1 - Update to latest upstream version. - Rebase patches. - Initial incomplete attempt to disable v4l1 support. * Fri Jan 21 2011 Jason L Tibbitts III - 1.24.2-6 - Unbundle cambozola; instead link to the separately pacakged copy. - Remove BuildRoot:, %%clean and buildroot cleaning in %%install. - Git rid of mixed space/tab usage by removing all tabs. - Remove unnecessary Conflicts: line. - Attempt to force short_open_tag on for the code directories. - Move default location of sockets, swaps, logfiles and some temporary files to make more sense and allow things to work better with a future selinux policy. - Fix errors in README.CentOS. * Wed Jun 02 2010 Marcela Maslanova - 1.24.2-5 - Mass rebuild with perl-5.12.0 * Fri Dec 4 2009 Stepan Kasal - 1.24.2-4 - rebuild against perl 5.10.1 - use Perl vendorarch and archlib variables correctly * Mon Jul 27 2009 Fedora Release Engineering - 1.24.2-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild * Wed Jul 22 2009 Jason L Tibbitts III - 1.24.2-2 - Bump release since 1.24.2-1 was mistakenly tagged a few months ago. * Wed Jul 22 2009 Jason L Tibbitts III - 1.24.2-1 - Initial update to 1.24.2. - Rebase patches. - Update mootools download location. - Update to mootools 1.2.3. - Add additional dependencies for some optional features. * Sat Apr 11 2009 Martin Ebourne - 1.24.1-3 - Remove unused Sys::Mmap perl dependency RPM is finding * Sat Apr 11 2009 Martin Ebourne - 1.24.1-2 - Update gcc44 patch to disable -frepo, seems to be broken with gcc44 - Added noffmpeg patch to make building outside mock easier * Sat Mar 21 2009 Martin Ebourne - 1.24.1-1 - Patch for gcc 4.4 compilation errors - Upgrade to 1.24.1 * Wed Feb 25 2009 Fedora Release Engineering - 1.23.3-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild * Sat Jan 24 2009 Caolán McNamara - 1.23.3-3 - rebuild for dependencies * Mon Dec 15 2008 Martin Ebourne - 1.23.3-2 - Fix permissions on zm.conf * Fri Jul 11 2008 Jason L Tibbitts III - 1.23.3-1 - Initial attempt at packaging 1.23. * Tue Jul 1 2008 Martin Ebourne - 1.22.3-15 - Add perl module compat dependency, bz #453590 * Tue May 6 2008 Martin Ebourne - 1.22.3-14 - Remove default runlevel, bz #441315 * Mon Apr 28 2008 Jason L Tibbitts III - 1.22.3-13 - Backport patch for CVE-2008-1381 from 1.23.3 to 1.22.3. * Tue Feb 19 2008 Fedora Release Engineering - 1.22.3-12 - Autorebuild for GCC 4.3 * Thu Jan 3 2008 Martin Ebourne - 1.22.3-11 - Fix compilation on gcc 4.3 * Thu Dec 6 2007 Martin Ebourne - 1.22.3-10 - Rebuild for new openssl * Thu Aug 2 2007 Martin Ebourne - 1.22.3-8 - Fix licence tag * Thu Jul 12 2007 Martin Ebourne - 1.22.3-7 - Fixes from testing by Jitz including missing dependencies and database creation * Sat Jun 30 2007 Martin Ebourne - 1.22.3-6 - Disable crashtrace on ppc * Sat Jun 30 2007 Martin Ebourne - 1.22.3-5 - Fix uid for directories in /var/lib/zoneminder * Tue Jun 26 2007 Martin Ebourne - 1.22.3-4 - Added perl Archive::Tar dependency - Disabled web interface due to lack of access control on the event images * Sun Jun 10 2007 Martin Ebourne - 1.22.3-3 - Changes recommended in review by Jason Tibbitts * Mon Apr 2 2007 Martin Ebourne - 1.22.3-2 - Standardised on package name of zoneminder * Thu Dec 28 2006 Martin Ebourne - 1.22.3-1 - First version. Uses some parts from zm-1.20.1 by Corey DeLasaux and Serg Oskin ZoneMinder-1.26.5/distros/redhat/zoneminder.in000077500000000000000000000050071225361755400213360ustar00rootroot00000000000000#!/bin/sh # description: ZoneMinder is the top Linux video camera security and surveillance solution. ZoneMinder is intended for use in single or multi-camera video security applications.Copyright: Philip Coombes, Corey DeLasaux 2003-2008 # chkconfig: - 99 00 # processname: zmpkg.pl # Source function library. . /etc/rc.d/init.d/functions prog=ZoneMinder ZM_CONFIG="@ZM_CONFIG@" pidfile="@ZM_RUNDIR@" LOCKFILE=/var/lock/subsys/zm loadconf() { if [ -f $ZM_CONFIG ]; then . $ZM_CONFIG else echo "ERROR: $ZM_CONFIG not found." return 1 fi } loadconf command="$ZM_PATH_BIN/zmpkg.pl" start() { # Commenting out as it is not needed. Leaving as a placeholder for future use. # zmupdate || return $? loadconf || return $? #Make sure the directory for our PID folder exists or create one. [ ! -d $pidfile ] \ && mkdir -m 774 $pidfile \ && chown $ZM_WEB_USER:$ZM_WEB_GROUP $pidfile #Make sure the folder for the socks file exists or create one GetPath="select Value from Config where Name='ZM_PATH_SOCKS'" dbHost=`echo $ZM_DB_HOST | cut -d: -f1` dbPort=`echo $ZM_DB_HOST | cut -d: -s -f2` if [ "$dbPort" = "" ] then ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$ZM_DB_HOST -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'` else ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$dbHost -P$dbPort -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'` fi [ ! -d $ZM_PATH_SOCK ] \ && mkdir -m 774 $ZM_PATH_SOCK \ && chown $ZM_WEB_USER:$ZM_WEB_GROUP $ZM_PATH_SOCK echo -n $"Starting $prog: " $command start RETVAL=$? [ $RETVAL = 0 ] && success || failure echo [ $RETVAL = 0 ] && touch $LOCKFILE return $RETVAL } stop() { loadconf echo -n $"Stopping $prog: " $command stop RETVAL=$? [ $RETVAL = 0 ] && success || failure echo [ $RETVAL = 0 ] && rm -f $LOCKFILE } zmstatus() { loadconf result=`$command status` if [ "$result" = "running" ]; then echo "ZoneMinder is running" $ZM_PATH_BIN/zmu -l RETVAL=0 else echo "ZoneMinder is stopped" RETVAL=1 fi } zmupdate() { if [ -x $ZM_PATH_BIN/zmupdate.pl ]; then $ZM_PATH_BIN/zmupdate.pl -f fi } case "$1" in 'start') start ;; 'stop') stop ;; 'restart') stop start ;; 'condrestart') loadconf result=`$ZM_PATH_BIN/zmdc.pl check` if [ "$result" = "running" ]; then $ZM_PATH_BIN/zmdc.pl shutdown > /dev/null rm -f $LOCKFILE start fi ;; 'status') status httpd status mysqld zmstatus ;; *) echo "Usage: $0 { start | stop | restart | condrestart | status }" RETVAL=1 ;; esac exit $RETVAL ZoneMinder-1.26.5/distros/ubuntu1204/000077500000000000000000000000001225361755400172115ustar00rootroot00000000000000ZoneMinder-1.26.5/distros/ubuntu1204/README.Debian000066400000000000000000000036561225361755400212640ustar00rootroot00000000000000zoneminder for Debian --------------------- There is one manual step to get the web interface working. You need to link /etc/zm/apache.conf to /etc/apache2/conf.d/zoneminder.conf, then reload the apache config (i.e. /etc/init.d/apache2 reload) Changing the location for images and events ------------------------------------------- Zoneminder, in its upstream form, stores data in /usr/share/zoneminder/. This package modifies that by changing /usr/share/zoneminder/images and /usr/share/zoneminder/events to symlinks to directories under /var/cache/zoneminder. There are numerous places these could be put and ways to do it. But, at the moment, if you change this, an upgrade will fail with a warning about these locations having changed (the reason for this was that previously, an upgrade would silently revert the changes and cause event loss - refer bug #608793). If you do want to change the location, here are a couple of suggestions. (thanks to vagrant@freegeek.org): These lines in fstab could allow you to bind-mount an alternate location /dev/sdX1 /otherdrive ext3 defaults 0 2 /otherdrive/zoneminder/images /var/cache/zoneminder/images bind defaults 0 2 /otherdrive/zoneminder/events /var/cache/zoneminder/events bind defaults 0 2 or if you have a separate partition for each: /dev/sdX1 /var/cache/zoneminder/images ext3 defaults 0 2 /dev/sdX2 /var/cache/zoneminder/events ext3 defaults 0 2 -- Peter Howard , Sun, 16 Jan 2010 01:35:51 +1100 Access to /dev/video* --------------------- For cameras which require access to /dev/video*, zoneminder may need the www-data user added to the video group in order to see those cameras: adduser www-data video Note that all web applications running on the zoneminder server will then have access to all video devices on the system. -- Vagrant Cascadian Sun, 27 Mar 2011 13:06:56 -0700 ZoneMinder-1.26.5/distros/ubuntu1204/apache.conf000066400000000000000000000003231225361755400212770ustar00rootroot00000000000000Alias /zm /usr/share/zoneminder php_flag register_globals off Options Indexes FollowSymLinks DirectoryIndex index.php ZoneMinder-1.26.5/distros/ubuntu1204/changelog000066400000000000000000000010101225361755400210530ustar00rootroot00000000000000zoneminder (1.26.5-1) precise; urgency=high * improvements to zmupdate.pl, cleanups -- Isaac Connor Thu, 03 Oct 2013 11:40:32 -0400 zoneminder (1.26.3-1) unstable; urgency=low * A 'minor' release focusing on performance improvement and bug fixes. -- Isaac Connor Sun, 22 Sep 2013 09:36:42 +0800 zoneminder (1.25.1-1) unstable; urgency=low * Initial Version. -- Isaac Connor Mon, 29 Apr 2013 12:38:00 -0400 ZoneMinder-1.26.5/distros/ubuntu1204/compat000066400000000000000000000000021225361755400204070ustar00rootroot000000000000009 ZoneMinder-1.26.5/distros/ubuntu1204/control000066400000000000000000000054161225361755400206220ustar00rootroot00000000000000Source: zoneminder Section: net Priority: optional Maintainer: Isaac Connor Build-Depends: debhelper (>= 7.0.50), autoconf, automake, dpatch, libphp-serialization-perl, libgnutls-dev, libmysqlclient-dev, libdbd-mysql-perl, libdate-manip-perl, libwww-perl, libjpeg8-dev, libpcre3-dev, libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libv4l-dev (>= 0.8.3), libbz2-dev, libtool, libsys-mmap-perl, ffmpeg, libnetpbm10-dev, libavdevice-dev, libdevice-serialport-perl, libpcre3, libarchive-zip-perl, libmime-lite-perl, libjpeg8, dh-autoreconf Standards-Version: 3.9.2 Package: zoneminder Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, apache2, libapache2-mod-php5, php5, php5-mysql, libphp-serialization-perl, libdate-manip-perl, libmime-lite-perl, libmime-lite-perl, mysql-client, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl, libpcre3, ffmpeg, rsyslog | system-log-daemon, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, netpbm, libavdevice53, libjpeg8, zip, libnet-sftp-foreign-perl SUggests: mysql-server Description: A video camera security and surveillance solution ZoneMinder is intended for use in single or multi-camera video security applications, including commercial or home CCTV, theft prevention and child or family member or home monitoring and other care scenarios. It supports capture, analysis, recording, and monitoring of video data coming from one or more video or network cameras attached to a Linux system. ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom cameras using a variety of protocols. It is suitable for use as a home video security system and for commercial or professional video security and surveillance. It can also be integrated into a home automation system via X.10 or other protocols. Package: zoneminder-dbg Architecture: any Depends: zoneminder (= ${binary:Version}), ${misc:Depends} Description: debugging syumbols for zoneminder. ZoneMinder is a video camera security and surveillance solution. ZoneMinder is intended for use in single or multi-camera video security applications, including commercial or home CCTV, theft prevention and child or family member or home monitoring and other care scenarios. It supports capture, analysis, recording, and monitoring of video data coming from one or more video or network cameras attached to a Linux system. ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom cameras using a variety of protocols. It is suitable for use as a home video security system and for commercial or professional video security and surveillance. It can also be integrated into a home automation system via X.10 or other protocols. ZoneMinder-1.26.5/distros/ubuntu1204/copyright000066400000000000000000000015331225361755400211460ustar00rootroot00000000000000Copyright: Copyright 2002 Philip Coombes License: This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA On Debian GNU/Linux systems, the text of the GPL can be found in /usr/share/common-licenses/GPL. ZoneMinder-1.26.5/distros/ubuntu1204/dirs000066400000000000000000000002001225361755400200650ustar00rootroot00000000000000var/log/zm var/lib/zm var/cache/zoneminder/events var/cache/zoneminder/images var/cache/zoneminder/temp usr/share/zoneminder/db ZoneMinder-1.26.5/distros/ubuntu1204/docs000066400000000000000000000000121225361755400200550ustar00rootroot00000000000000README.md ZoneMinder-1.26.5/distros/ubuntu1204/init.d000066400000000000000000000034321225361755400203230ustar00rootroot00000000000000#!/bin/sh ### BEGIN INIT INFO # Provides: zoneminder # Required-Start: $network $remote_fs $syslog # Required-Stop: $network $remote_fs $syslog # Should-Start: mysql # Should-Stop: mysql # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Control ZoneMinder as a Service ### END INIT INFO # description: Control ZoneMinder as a Service # chkconfig: 2345 20 20 # Source function library. #. /etc/rc.d/init.d/functions prog=ZoneMinder ZM_PATH_BIN="/usr/bin" RUNDIR=/var/run/zm TMPDIR=/tmp/zm command="$ZM_PATH_BIN/zmpkg.pl" start() { echo -n "Starting $prog: " mkdir -p $RUNDIR && chown www-data:www-data $RUNDIR mkdir -p $TMPDIR && chown www-data:www-data $TMPDIR zmfix -a $command start RETVAL=$? [ $RETVAL = 0 ] && echo success [ $RETVAL != 0 ] && echo failure echo [ $RETVAL = 0 ] && touch /var/lock/zm return $RETVAL } stop() { echo -n "Stopping $prog: " # # Why is this status check being done? # as $command stop returns 1 if zoneminder # is stopped, which will result in # this returning 1, which will stuff # dpkg when it tries to stop zoneminder before # uninstalling . . . # result=`$command status` if [ ! "$result" = "running" ]; then echo "Zoneminder already stopped" echo RETVAL=0 else $command stop RETVAL=$? [ $RETVAL = 0 ] && echo success [ $RETVAL != 0 ] && echo failure echo [ $RETVAL = 0 ] && rm -f /var/lock/zm fi } status() { result=`$command status` if [ "$result" = "running" ]; then echo "ZoneMinder is running" RETVAL=0 else echo "ZoneMinder is stopped" RETVAL=1 fi } case "$1" in 'start') start ;; 'stop') stop ;; 'restart' | 'force-reload') stop start ;; 'status') status ;; *) echo "Usage: $0 { start | stop | restart | status }" RETVAL=1 ;; esac exit $RETVAL ZoneMinder-1.26.5/distros/ubuntu1204/postinst000066400000000000000000000027501225361755400210230ustar00rootroot00000000000000#! /bin/sh set -e if [ "$1" = "configure" ]; then # # Get mysql started if it isn't # if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then invoke-rc.d mysql start fi if $(/etc/init.d/mysql status >/dev/null 2>&1); then mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload # test if database if already present... if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf echo 'grant lock tables, alter,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql fi invoke-rc.d zoneminder stop || true zmupdate.pl --nointeractive else echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' fi chown www-data:www-data /var/log/zm chown www-data:www-data /var/lib/zm/ if [ -z "$2" ]; then chown www-data:www-data -R /var/cache/zoneminder fi fi # Ensure zoneminder is stopped... if [ -x "/etc/init.d/zoneminder" ]; then if invoke-rc.d zoneminder status ; then invoke-rc.d zoneminder stop || exit $? fi fi if [ "$1" = "configure" ]; then if [ -z "$2" ]; then chown www-data:www-data /var/log/zm chown www-data:www-data /var/lib/zm/ chown www-data:www-data -R /var/cache/zoneminder else chown www-data:www-data /var/log/zm zmupdate.pl fi fi #DEBHELPER# ZoneMinder-1.26.5/distros/ubuntu1204/postrm000066400000000000000000000005411225361755400204600ustar00rootroot00000000000000#! /bin/sh # set -e # to be reinstated later if [ "$1" = "purge" ]; then echo 'delete from user where User="zmuser";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql echo 'delete from db where User="zmuser";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql mysqladmin --defaults-file=/etc/mysql/debian.cnf -f drop zm fi #DEBHELPER# ZoneMinder-1.26.5/distros/ubuntu1204/preinst000077500000000000000000000013761225361755400206320ustar00rootroot00000000000000#!/bin/sh set -e abort=false if [ -L /usr/share/zoneminder/events ]; then l=$(readlink /usr/share/zoneminder/events) if [ "$l" != "/var/cache/zoneminder/events" ]; then abort=true fi fi if [ -L /usr/share/zoneminder/images ]; then l=$(readlink /usr/share/zoneminder/images ) if [ "$l" != "/var/cache/zoneminder/images" ]; then abort=true fi fi if [ "$abort" = "true" ]; then cat >&2 << EOF Aborting installation of zoneminder due to non-default symlinks in /usr/share/zoneminder for the images and/or events directory, which could result in loss of data. Please move your data in each of these directories to /var/cache/zoneminder before installing zoneminder from the package. EOF exit 1 fi #DEBHELPER# exit 0 ZoneMinder-1.26.5/distros/ubuntu1204/rules000077500000000000000000000056141225361755400202770ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # These are used for cross-compiling and for saving the configure script # from having to guess our platform (since we know it already) DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) CFLAGS = -Wall -g CPPFLAGS = -D__STDC_CONSTANT_MACROS CXXFLAGS = -DHAVE_LIBCRYPTO ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) CFLAGS += -O0 else CFLAGS += -O2 endif %: dh $@ --with autoreconf override_dh_auto_configure: CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --sysconfdir=/etc/zm --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info --with-mysql=/usr --with-webdir=/usr/share/zoneminder --with-ffmpeg=/usr --with-cgidir=/usr/lib/cgi-bin --with-webuser=www-data --with-webgroup=www-data --enable-crashtrace=no --enable-mmap=yes override_dh_clean: # Add here commands to clean up after the build process. [ ! -f Makefile ] || $(MAKE) distclean dh_clean override_dh_install: # Add here commands to install the package into debian/zm. $(MAKE) install DESTDIR=$(CURDIR)/debian/zoneminder RUNDIR=$(CURDIR)/debian/zoneminder/var/run ZM_RUNDIR=$(CURDIR)/debian/zoneminder/var/run install -D -m 0644 db/zm_create.sql $(CURDIR)/debian/zoneminder/usr/share/zoneminder/db install -D -m 0644 db/zm_update-*.sql $(CURDIR)/debian/zoneminder/usr/share/zoneminder/db install -D -m 0644 debian/apache.conf $(CURDIR)/debian/zoneminder/etc/zm # # NOTE: This is a short-term kludge; hopefully changes in the next # upstream version will render this unnecessary. rm -rf debian/zoneminder/usr/share/zoneminder/events rm -rf debian/zoneminder/usr/share/zoneminder/images rm -rf debian/zoneminder/usr/share/zoneminder/temp ln -s /var/cache/zoneminder/events debian/zoneminder/usr/share/zoneminder/ ln -s /var/cache/zoneminder/images debian/zoneminder/usr/share/zoneminder/ ln -s /var/cache/zoneminder/temp debian/zoneminder/usr/share/zoneminder/ # # This is a slightly lesser kludge; moving the cgi stuff to # /usr/share/zoneminder/cgi-bin breaks one set of behavior, # having it just in /usr/lib/cgi-bin breaks another bit of # behavior. # ln -s /usr/lib/cgi-bin debian/zoneminder/usr/share/zoneminder/ override_dh_fixperms: dh_fixperms chown root:root debian/zoneminder/etc/zm/zm.conf override_dh_auto_test: # do not run tests... .PHONY: override_dh_strip override_dh_strip: dh_strip --dbg-package=zoneminder-dbg ZoneMinder-1.26.5/distros/ubuntu1204/watch000066400000000000000000000001211225361755400202340ustar00rootroot00000000000000version=3 http://www.zoneminder.com/downloads.html \ .*/ZoneMinder-(.*).tar.gz ZoneMinder-1.26.5/doc-pak/000077500000000000000000000000001225361755400152075ustar00rootroot00000000000000ZoneMinder-1.26.5/doc-pak/AUTHORS000066400000000000000000000003461225361755400162620ustar00rootroot00000000000000ZoneMinder - A Linux based camera monitoring and analysis tool. This project was imagined and created by Philip Coombes in September 2002, shortly after being burglarised of his power tools. mailto:philip.coombes@zoneminder.com ZoneMinder-1.26.5/doc-pak/BUGS000066400000000000000000000000301225361755400156630ustar00rootroot00000000000000Please see README file. ZoneMinder-1.26.5/doc-pak/COPYING000066400000000000000000000431101225361755400162410ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ZoneMinder-1.26.5/doc-pak/ChangeLog000066400000000000000000000000301225361755400167520ustar00rootroot00000000000000Please see README file. ZoneMinder-1.26.5/doc-pak/INSTALL000066400000000000000000000173631225361755400162520ustar00rootroot00000000000000Basic Installation ================== These are generic installation instructions. Please see http://www.zoneminder.com/wiki/index.php/Documentation for full details. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, a file `config.cache' that saves the results of its tests to speed up reconfiguring, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.in' is used to create `configure' by a program called `autoconf'. You only need `configure.in' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. You can give `configure' initial values for variables by setting them in the environment. Using a Bourne-compatible shell, you can do that on the command line like this: CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure Or on systems that have the `env' program, you can do it like this: env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not supports the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' can not figure out automatically, but needs to determine by the type of host the package will run on. Usually `configure' can figure that out, but if it prints a message saying it can not guess the host type, give it the `--host=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name with three fields: CPU-COMPANY-SYSTEM See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the host type. If you are building compiler tools for cross-compiling, you can also use the `--target=TYPE' option to select the type of system they will produce code for and the `--build=TYPE' option to select the type of system on which you are compiling the package. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Operation Controls ================== `configure' recognizes the following options to control how it operates. `--cache-file=FILE' Use and save the results of the tests in FILE instead of `./config.cache'. Set FILE to `/dev/null' to disable caching, for debugging `configure'. `--help' Print a summary of the options to `configure', and exit. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--version' Print the version of Autoconf used to generate the `configure' script, and exit. `configure' also accepts some other, not widely useful, options. ZoneMinder-1.26.5/doc-pak/NEWS000066400000000000000000000000301225361755400156770ustar00rootroot00000000000000Please see README file. ZoneMinder-1.26.5/doc-pak/README000066400000000000000000000001541225361755400160670ustar00rootroot00000000000000 All documentation for ZoneMinder is now online at http://www.zoneminder.com/wiki/index.php/Documentation ZoneMinder-1.26.5/doc-pak/TODO000066400000000000000000000000311225361755400156710ustar00rootroot00000000000000Please see README file. ZoneMinder-1.26.5/misc/000077500000000000000000000000001225361755400146245ustar00rootroot00000000000000ZoneMinder-1.26.5/misc/CMakeLists.txt000066400000000000000000000010721225361755400173640ustar00rootroot00000000000000# CMakeLists.txt for the ZoneMinder misc files # Create files from the .in files configure_file(apache.conf.in "${CMAKE_CURRENT_BINARY_DIR}/apache.conf" @ONLY) configure_file(logrotate.conf.in "${CMAKE_CURRENT_BINARY_DIR}/logrotate.conf" @ONLY) configure_file(syslog.conf.in "${CMAKE_CURRENT_BINARY_DIR}/syslog.conf" @ONLY) # Do not install the misc files by default #install(FILES "${CMAKE_CURRENT_BINARY_DIR}/apache.conf" "${CMAKE_CURRENT_BINARY_DIR}/logrotate.conf" "${CMAKE_CURRENT_BINARY_DIR}/syslog.conf" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/misc") ZoneMinder-1.26.5/misc/Makefile.am000066400000000000000000000001361225361755400166600ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu EXTRA_DIST = \ apache.conf.in \ logrotate.conf.in \ syslog.conf.in ZoneMinder-1.26.5/misc/apache.conf.in000066400000000000000000000026111225361755400173210ustar00rootroot00000000000000# # PLEASE NOTE THAT THIS FILE IS INTENDED FOR GUIDANCE ONLY AND MAY NOT BE APPROPRIATE FOR YOUR DISTRIBUTION # # Sample configuration file for running ZoneMinder as name based virtual host # Some values may need to manually adjusted to suit your setup # ServerName @WEB_HOST@ ServerAdmin webmaster@localhost DocumentRoot "@WEB_PREFIX@" Options FollowSymLinks AllowOverride All ScriptAlias /cgi-bin/ "@CGI_PREFIX@" Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch AllowOverride All # Use the first option to have Apache logs written to the general log # directory, or the second to have them written to the regular Apache # directory (you may have to change the path to that used on your system) ErrorLog @ZM_LOGDIR@/apache-error.log ErrorLog /var/log/httpd/zm-error.log # Possible values include: debug, info, notice, warn, error, crit, # alert, emerg. LogLevel warn # Use the first option to have Apache logs written to the general log # directory, or the second to have them written to the regular Apache # directory (you may have to change the path to that used on your system) CustomLog @ZM_LOGDIR@/apache-access.log combined CustomLog /var/log/httpd/zm-access.log combined ZoneMinder-1.26.5/misc/logrotate.conf.in000066400000000000000000000014411225361755400201000ustar00rootroot00000000000000# # PLEASE NOTE THAT THIS FILE IS INTENDED FOR GUIDANCE ONLY AND MAY NOT BE APPROPRIATE FOR YOUR DISTRIBUTION # # Sample logrotate file for ZoneMinder. Copy this to /etc/logrotate.d/zm or similar # if you wish your system to prevent your log files from growing too large. # You can remove the 'kill' of syslogd if you have another logrotate file that does # this but in that case you will want this file run first, so it will need to be # named something lexically ahead of the other logrotate file, for example 0zm to # ensure it is executed first. # @ZM_LOGDIR@/*.log { missingok notifempty sharedscripts postrotate /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true @BINDIR@/zmpkg.pl logrot 2> /dev/null > /dev/null || true endscript } ZoneMinder-1.26.5/misc/syslog.conf.in000066400000000000000000000020701225361755400174170ustar00rootroot00000000000000# # PLEASE NOTE THAT THIS FILE IS INTENDED FOR GUIDANCE ONLY AND MAY NOT BE APPROPRIATE FOR YOUR DISTRIBUTION # # Sample instruction to put in /etc/syslog.conf (or rsyslog.conf) to redirect # ZoneMinder syslog message to a separate file, apart from warnings and errors. # This is done by assigning ZoneMinder messages to one of the user logging facilities # which are local0-7. In this example local1 is used but if this is used by other # packages then this can be changed to another which is unused (e.g. local3). # # Save ZoneMinder messages to zm.log, this uses the local1 facility, local1.* @ZM_LOGDIR@/zm.log # You will need to edit the existing config line that directs to /var/log/messages # or /var/log/syslog and insert the 'local1.!*;local1.warning' directives. This # first excludes all local1 messages, and then re-enables local1 messages of warning # level or above. Remove the second part if you want no ZoneMinder messages to go # to your system logs at all. *.info;local1.!*;local1.warning;mail.none;news.none;authpriv.none;cron.none /var/log/messages ZoneMinder-1.26.5/scripts/000077500000000000000000000000001225361755400153605ustar00rootroot00000000000000ZoneMinder-1.26.5/scripts/CMakeLists.txt000066400000000000000000000040411225361755400201170ustar00rootroot00000000000000# CMakeLists.txt for the ZoneMinder perl scripts. # Process the perl modules subdirectory add_subdirectory(ZoneMinder) # Create files from the .in files configure_file(zmaudit.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" @ONLY) configure_file(zmcontrol.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" @ONLY) configure_file(zmdc.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" @ONLY) configure_file(zmfilter.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" @ONLY) configure_file(zmpkg.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" @ONLY) configure_file(zmtrack.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" @ONLY) configure_file(zmtrigger.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" @ONLY) configure_file(zmupdate.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" @ONLY) configure_file(zmvideo.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" @ONLY) configure_file(zmwatch.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" @ONLY) if(NOT ZM_NO_X10) configure_file(zmx10.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmx10.pl" @ONLY) endif(NOT ZM_NO_X10) #configure_file(zmdbbackup.in zmdbbackup @ONLY) #configure_file(zmdbrestore.in zmdbrestore @ONLY) configure_file(zm.in "${CMAKE_CURRENT_BINARY_DIR}/zm" @ONLY) #configure_file(zmeventdump.in zmeventdump @ONLY) # Install the perl scripts install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) if(NOT ZM_NO_X10) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmx10.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) endif(NOT ZM_NO_X10) ZoneMinder-1.26.5/scripts/Makefile.am000066400000000000000000000045161225361755400174220ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu # Ack! Nasty hack to get modules Makefile regenerated if wiped with make clean all-local: ZoneMinder/Makefile ZoneMinder/Makefile: ZoneMinder/Makefile.PL ( cd ZoneMinder; perl Makefile.PL ) bin_SCRIPTS = \ zmdc.pl \ zmaudit.pl \ zmfilter.pl \ zmtrigger.pl \ zmx10.pl \ zmwatch.pl \ zmpkg.pl \ zmupdate.pl \ zmvideo.pl \ zmcontrol.pl \ zmtrack.pl SUBDIRS = \ . \ ZoneMinder EXTRA_DIST = \ zmdc.pl.in \ zmaudit.pl.in \ zmfilter.pl.in \ zmtrigger.pl.in \ zmx10.pl.in \ zmwatch.pl.in \ zmpkg.pl.in \ zmupdate.pl.in \ zmvideo.pl.in \ zmcontrol.pl.in \ zmtrack.pl.in \ ZoneMinder/Makefile.PL \ ZoneMinder/README \ ZoneMinder/Changes \ ZoneMinder/META.yml \ ZoneMinder/MANIFEST \ ZoneMinder/t/ZoneMinder.t \ ZoneMinder/lib/ZoneMinder.pm \ ZoneMinder/lib/ZoneMinder/Base.pm.in \ ZoneMinder/lib/ZoneMinder/Config.pm.in \ ZoneMinder/lib/ZoneMinder/Logger.pm \ ZoneMinder/lib/ZoneMinder/General.pm \ ZoneMinder/lib/ZoneMinder/Database.pm \ ZoneMinder/lib/ZoneMinder/Memory.pm.in \ ZoneMinder/lib/ZoneMinder/Memory/Shared.pm \ ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm \ ZoneMinder/lib/ZoneMinder/ConfigAdmin.pm \ ZoneMinder/lib/ZoneMinder/ConfigData.pm.in \ ZoneMinder/lib/ZoneMinder/Control.pm \ ZoneMinder/lib/ZoneMinder/Control/PelcoD.pm \ ZoneMinder/lib/ZoneMinder/Control/AxisV2.pm \ ZoneMinder/lib/ZoneMinder/Control/PanasonicIP.pm \ ZoneMinder/lib/ZoneMinder/Control/Visca.pm \ ZoneMinder/lib/ZoneMinder/Control/Ncs370.pm \ ZoneMinder/lib/ZoneMinder/Control/mjpgStreamer.pm \ ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm \ ZoneMinder/lib/ZoneMinder/Control/FI8608W_Y2k.pm \ ZoneMinder/lib/ZoneMinder/Control/FI8620_Y2k.pm \ ZoneMinder/lib/ZoneMinder/Control/FI9821W_Y2k.pm \ ZoneMinder/lib/ZoneMinder/Control/LoftekSentinel.pm \ ZoneMinder/lib/ZoneMinder/Trigger/Channel.pm \ ZoneMinder/lib/ZoneMinder/Trigger/Channel/Handle.pm \ ZoneMinder/lib/ZoneMinder/Trigger/Channel/Spawning.pm \ ZoneMinder/lib/ZoneMinder/Trigger/Channel/Inet.pm \ ZoneMinder/lib/ZoneMinder/Trigger/Channel/Unix.pm \ ZoneMinder/lib/ZoneMinder/Trigger/Channel/File.pm \ ZoneMinder/lib/ZoneMinder/Trigger/Channel/Serial.pm \ ZoneMinder/lib/ZoneMinder/Trigger/Connection.pm \ ZoneMinder/lib/ZoneMinder/Trigger/Connection/Example.pm \ zm.in \ zmdbbackup.in \ zmdbrestore.in \ zmeventdump.in \ zmlogrotate.conf.in ZoneMinder-1.26.5/scripts/ZoneMinder/000077500000000000000000000000001225361755400174325ustar00rootroot00000000000000ZoneMinder-1.26.5/scripts/ZoneMinder/CMakeLists.txt000066400000000000000000000047611225361755400222020ustar00rootroot00000000000000# CMakeLists.txt for the ZoneMinder perl module. # If this is an out-of-source build, copy the files we need to the binary directory if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Changes" "${CMAKE_CURRENT_BINARY_DIR}/Changes") execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.PL" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.PL") execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/MANIFEST" "${CMAKE_CURRENT_BINARY_DIR}/MANIFEST") execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/META.yml" "${CMAKE_CURRENT_BINARY_DIR}/META.yml") execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/README" "${CMAKE_CURRENT_BINARY_DIR}/README") execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/t" "${CMAKE_CURRENT_BINARY_DIR}/t") execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/lib" "${CMAKE_CURRENT_BINARY_DIR}/lib") endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) # Create files from the .in files configure_file(lib/ZoneMinder/Base.pm.in "${CMAKE_CURRENT_BINARY_DIR}/lib/ZoneMinder/Base.pm" @ONLY) configure_file(lib/ZoneMinder/Config.pm.in "${CMAKE_CURRENT_BINARY_DIR}/lib/ZoneMinder/Config.pm" @ONLY) configure_file(lib/ZoneMinder/Memory.pm.in "${CMAKE_CURRENT_BINARY_DIR}/lib/ZoneMinder/Memory.pm" @ONLY) configure_file(lib/ZoneMinder/ConfigData.pm.in "${CMAKE_CURRENT_BINARY_DIR}/lib/ZoneMinder/ConfigData.pm" @ONLY) if(CMAKE_VERBOSE_MAKEFILE) set(MAKEMAKER_NOECHO_COMMAND "") else(CMAKE_VERBOSE_MAKEFILE) set(MAKEMAKER_NOECHO_COMMAND "NOECHO=\"1>/dev/null\"") endif(CMAKE_VERBOSE_MAKEFILE) # Add build target for the perl modules add_custom_target(zmperlmodules ALL perl Makefile.PL FIRST_MAKEFILE=MakefilePerl PREFIX="${CMAKE_CURRENT_BINARY_DIR}/output" LIB="${CMAKE_CURRENT_BINARY_DIR}/output/${ZM_PERL_SUBPREFIX}" INSTALLSITEMAN3DIR="${CMAKE_CURRENT_BINARY_DIR}/output/${CMAKE_INSTALL_MANDIR}/man3" ${MAKEMAKER_NOECHO_COMMAND} COMMAND make --makefile=MakefilePerl COMMAND make --makefile=MakefilePerl pure_install COMMENT "Building ZoneMinder perl modules") # Add install target for the perl modules install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/output/" DESTINATION "${CMAKE_INSTALL_PREFIX}") # Add additional files and directories to make clean set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "output;blib;pm_to_blib;MakefilePerl") ZoneMinder-1.26.5/scripts/ZoneMinder/Changes000066400000000000000000000002451225361755400207260ustar00rootroot00000000000000Revision history for Perl extension ZoneMinder. 0.01 Thu Dec 15 17:22:29 2005 - original version; created by h2xs 1.23 with options -XA -b 5.6.0 -n ZoneMinder ZoneMinder-1.26.5/scripts/ZoneMinder/MANIFEST000066400000000000000000000002241225361755400205610ustar00rootroot00000000000000Changes Makefile.PL MANIFEST README t/ZoneMinder.t lib/ZoneMinder.pm META.yml Module meta-data (added by MakeMaker) ZoneMinder-1.26.5/scripts/ZoneMinder/META.yml000066400000000000000000000004671225361755400207120ustar00rootroot00000000000000# http://module-build.sourceforge.net/META-spec.html #XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# name: ZoneMinder version: 1.23.2 version_from: lib/ZoneMinder/Base.pm installdirs: site requires: distribution_type: module generated_by: ExtUtils::MakeMaker version 6.17 ZoneMinder-1.26.5/scripts/ZoneMinder/Makefile.PL000066400000000000000000000065251225361755400214140ustar00rootroot00000000000000use 5.006; use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( NAME => 'ZoneMinder', VERSION_FROM => 'lib/ZoneMinder/Base.pm', # finds $VERSION PREREQ_PM => {}, # e.g., Module::Name => 1.1 PM => { 'lib/ZoneMinder.pm' => '$(INST_LIBDIR)/ZoneMinder.pm', 'lib/ZoneMinder/Base.pm' => '$(INST_LIBDIR)/ZoneMinder/Base.pm', 'lib/ZoneMinder/Config.pm' => '$(INST_LIBDIR)/ZoneMinder/Config.pm', 'lib/ZoneMinder/General.pm' => '$(INST_LIBDIR)/ZoneMinder/General.pm', 'lib/ZoneMinder/Database.pm' => '$(INST_LIBDIR)/ZoneMinder/Database.pm', 'lib/ZoneMinder/Logger.pm' => '$(INST_LIBDIR)/ZoneMinder/Logger.pm', 'lib/ZoneMinder/Memory.pm' => '$(INST_LIBDIR)/ZoneMinder/Memory.pm', 'lib/ZoneMinder/Memory/Shared.pm' => '$(INST_LIBDIR)/ZoneMinder/Memory/Shared.pm', 'lib/ZoneMinder/Memory/Mapped.pm' => '$(INST_LIBDIR)/ZoneMinder/Memory/Mapped.pm', 'lib/ZoneMinder/ConfigAdmin.pm' => '$(INST_LIBDIR)/ZoneMinder/ConfigAdmin.pm', 'lib/ZoneMinder/ConfigData.pm' => '$(INST_LIBDIR)/ZoneMinder/ConfigData.pm', 'lib/ZoneMinder/Control.pm' => '$(INST_LIBDIR)/ZoneMinder/Control.pm', 'lib/ZoneMinder/Control/PelcoD.pm' => '$(INST_LIBDIR)/ZoneMinder/Control/PelcoD.pm', 'lib/ZoneMinder/Control/AxisV2.pm' => '$(INST_LIBDIR)/ZoneMinder/Control/AxisV2.pm', 'lib/ZoneMinder/Control/PanasonicIP.pm' => '$(INST_LIBDIR)/ZoneMinder/Control/PanasonicIP.pm', 'lib/ZoneMinder/Control/Visca.pm' => '$(INST_LIBDIR)/ZoneMinder/Control/Visca.pm', 'lib/ZoneMinder/Control/Ncs370.pm' => '$(INST_LIBDIR)/ZoneMinder/Control/Ncs370.pm', 'lib/ZoneMinder/Control/mjpgStreamer.pm' => '$(INST_LIBDIR)/ZoneMinder/Control/mjpgStreamer.pm', 'lib/ZoneMinder/Control/SkyIPCam7xx.pm' => '$(INST_LIBDIR)/ZoneMinder/Control/SkyIPCam7xx.pm', 'lib/ZoneMinder/Control/FI8608W_Y2k.pm' => '$(INST_LIBDIR)/ZoneMinder/Control/FI8608W_Y2k.pm', 'lib/ZoneMinder/Control/FI8620_Y2k.pm' => '$(INST_LIBDIR)/ZoneMinder/Control/FI8620_Y2k.pm', 'lib/ZoneMinder/Control/FI9821W_Y2k.pm' => '$(INST_LIBDIR)/ZoneMinder/Control/FI9821W_Y2k.pm', 'lib/ZoneMinder/Control/LoftekSentinel.pm' => '$(INST_LIBDIR)/ZoneMinder/Control/LoftekSentinel.pm', 'lib/ZoneMinder/Trigger/Channel.pm' => '$(INST_LIBDIR)/ZoneMinder/Trigger/Channel.pm', 'lib/ZoneMinder/Trigger/Channel/Handle.pm' => '$(INST_LIBDIR)/ZoneMinder/Trigger/Channel/Handle.pm', 'lib/ZoneMinder/Trigger/Channel/Spawning.pm' => '$(INST_LIBDIR)/ZoneMinder/Trigger/Channel/Spawning.pm', 'lib/ZoneMinder/Trigger/Channel/Inet.pm' => '$(INST_LIBDIR)/ZoneMinder/Trigger/Channel/Inet.pm', 'lib/ZoneMinder/Trigger/Channel/Unix.pm' => '$(INST_LIBDIR)/ZoneMinder/Trigger/Channel/Unix.pm', 'lib/ZoneMinder/Trigger/Channel/File.pm' => '$(INST_LIBDIR)/ZoneMinder/Trigger/Channel/File.pm', 'lib/ZoneMinder/Trigger/Channel/Serial.pm' => '$(INST_LIBDIR)/ZoneMinder/Trigger/Channel/Serial.pm', 'lib/ZoneMinder/Trigger/Connection.pm' => '$(INST_LIBDIR)/ZoneMinder/Trigger/Connection.pm', 'lib/ZoneMinder/Trigger/Connection/Example.pm' => '$(INST_LIBDIR)/ZoneMinder/Trigger/Connection/Example.pm', }, ($] >= 5.005 ? ## Add these new keywords supported since 5.005 (ABSTRACT_FROM => 'lib/ZoneMinder.pm', # retrieve abstract from module AUTHOR => 'Philip Coombes ') : ()), ); ZoneMinder-1.26.5/scripts/ZoneMinder/README000066400000000000000000000022301225361755400203070ustar00rootroot00000000000000ZoneMinder version 0.01 ======================= The README is used to introduce the module and provide instructions on how to install the module, any machine dependencies it may have (for example C compilers and installed libraries) and any other information that should be provided before the module is installed. A README file is required for CPAN modules since CPAN extracts the README file from a module distribution so that people browsing the archive can use it get an idea of the modules uses. It is usually a good idea to provide version information here so that people can decide whether fixes for the module are worth downloading. INSTALLATION To install this module type the following: perl Makefile.PL make make test make install DEPENDENCIES This module requires these other modules and libraries: blah blah blah COPYRIGHT AND LICENCE Put the correct copyright and licence information here. Copyright (C) 2005 by Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. ZoneMinder-1.26.5/scripts/ZoneMinder/lib/000077500000000000000000000000001225361755400202005ustar00rootroot00000000000000ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder.pm000066400000000000000000000070521225361755400226140ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Common Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the common definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder; use 5.006; use strict; use warnings; require Exporter; use ZoneMinder::Base qw(:all); use ZoneMinder::Config qw(:all); use ZoneMinder::Logger qw(:all); use ZoneMinder::General qw(:all); use ZoneMinder::Database qw(:all); use ZoneMinder::Memory qw(:all); our @ISA = qw(Exporter ZoneMinder::Base ZoneMinder::Config ZoneMinder::Logger ZoneMinder::General ZoneMinder::Database ZoneMinder::Memory); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'base' => [ @ZoneMinder::Base::EXPORT_OK ], 'config' => [ @ZoneMinder::Config::EXPORT_OK ], 'debug' => [ @ZoneMinder::Logger::EXPORT_OK ], 'general' => [ @ZoneMinder::General::EXPORT_OK ], 'database' => [ @ZoneMinder::Database::EXPORT_OK ], 'memory' => [ @ZoneMinder::Memory::EXPORT_OK ], ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; our @EXPORT_OK = @{ $EXPORT_TAGS{'all'} }; our @EXPORT = ( @EXPORT_OK ); our $VERSION = $ZoneMinder::Base::VERSION; 1; __END__ =head1 NAME ZoneMinder - Container module for common ZoneMinder modules =head1 SYNOPSIS use ZoneMinder; =head1 DESCRIPTION This module is a convenience container module that uses the ZoneMinder::Base, ZoneMinder::Common, ZoneMinder::Logger, ZoneMinder::Database and ZoneMinder::Memory modules. It also exports by default all symbols provided by the 'all' tag of each of the modules. Thus 'use'ing this module is equivalent to the following use ZoneMinder::Base qw(:all); use ZoneMinder::Config qw(:all); use ZoneMinder::Logger qw(:all); use ZoneMinder::Database qw(:all); use ZoneMinder::Memory qw(:all); but is somewhat easier. =head2 EXPORT All symbols exported by the 'all' tag of each of the included modules. =head1 SEE ALSO ZoneMinder::Base, ZoneMinder::Common, ZoneMinder::Logger, ZoneMinder::Database, ZoneMinder::Memory http://www.zoneminder.com =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2005 by Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/000077500000000000000000000000001225361755400222525ustar00rootroot00000000000000ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Base.pm.in000066400000000000000000000050711225361755400240720ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Base Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the common definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::Base; use 5.006; use strict; use warnings; require Exporter; our @ISA = qw(Exporter); use constant ZM_VERSION => "@VERSION@"; # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'all' => [ qw(ZM_VERSION) ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw(); our $VERSION = "@VERSION@"; 1; __END__ =head1 NAME ZoneMinder::Base - Base perl module for ZoneMinder =head1 SYNOPSIS use ZoneMinder::Base; =head1 DESCRIPTION This module is the base module for the rest of the ZoneMinder modules. It is included by each of the other modules but serves no purpose other than to propagate the perl module version amongst the other modules. You will never need to use this module directly but if you write new ZoneMinder modules they should include it. =head2 EXPORT None by default. =head1 SEE ALSO http://www.zoneminder.com =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in000066400000000000000000000114321225361755400244230ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Config Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the common definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::Config; use 5.006; use strict; use warnings; require Exporter; require ZoneMinder::Base; our @ISA = qw(Exporter ZoneMinder::Base); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our @EXPORT_CONFIG; # Get populated by BEGIN our %EXPORT_TAGS = ( 'constants' => [ qw( ZM_PID ) ] ); push( @{$EXPORT_TAGS{config}}, @EXPORT_CONFIG ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw(); our $VERSION = $ZoneMinder::Base::VERSION; use constant ZM_PID => "@ZM_PID@"; # Path to the ZoneMinder run pid file use constant ZM_CONFIG => "@ZM_CONFIG@"; # Path to the ZoneMinder config file use Carp; # Load the config from the database into the symbol table BEGIN { no strict 'refs'; my $config_file = ZM_CONFIG; ( my $local_config_file = $config_file ) =~ s|^.*/|./|; if ( -s $local_config_file && -r $local_config_file ) { print( STDERR "Warning, overriding installed $local_config_file file with local copy\n" ); $config_file = $local_config_file; } open( CONFIG, "<".$config_file ) or croak( "Can't open config file '$config_file': $!" ); foreach my $str ( ) { next if ( $str =~ /^\s*$/ ); next if ( $str =~ /^\s*#/ ); my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*(.*?)\s*$/; if ( ! $name ) { print( STDERR "Warning, bad line in $config_file: $str\n" ); next; } # end if $name =~ tr/a-z/A-Z/; *{$name} = sub { $value }; push( @EXPORT_CONFIG, $name ); } close( CONFIG ); use DBI; my $dbh = DBI->connect( "DBI:mysql:database=".&ZM_DB_NAME.";host=".&ZM_DB_HOST, &ZM_DB_USER, &ZM_DB_PASS ); my $sql = "select * from Config"; my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or croak( "Can't execute: ".$sth->errstr() ); while( my $config = $sth->fetchrow_hashref() ) { *{$config->{Name}} = sub { $config->{Value} }; push( @EXPORT_CONFIG, $config->{Name} ); } $sth->finish(); $dbh->disconnect(); } 1; __END__ =head1 NAME ZoneMinder::Config - ZoneMinder configuration module. =head1 SYNOPSIS use ZoneMinder::Config qw(:all); =head1 DESCRIPTION The ZoneMinder::Config module is used to import the ZoneMinder configuration from the database. It will do this at compile time in a BEGIN block and require access to the zm.conf file either in the current directory or in its defined location in order to determine database access details, configuration from this file will also be included. If the :all or :config tags are used then this configuration is exported into the namespace of the calling program or module. Once the configuration has been imported then configuration variables are defined as constants and can be accessed directory by name, e.g. $lang = ZM_LANG_DEFAULT; =head2 EXPORT None by default. The :constants tag will export the ZM_PID constant which details the location of the zm.pid file The :config tag will export all configuration from the database as well as any from the zm.conf file The :all tag will export all above symbols. =head1 SEE ALSO http://www.zoneminder.com =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/ConfigAdmin.pm000066400000000000000000000155511225361755400247750ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Config Admin Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the debug definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::ConfigAdmin; use 5.006; use strict; use warnings; require Exporter; require ZoneMinder::Base; our @ISA = qw(Exporter ZoneMinder::Base); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'functions' => [ qw( loadConfigFromDB saveConfigToDB ) ] ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; our @EXPORT_OK = ( @{ $EXPORT_TAGS{'functions'} } ); our @EXPORT = qw(); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Configuration Administration # # ========================================================================== use ZoneMinder::Config qw(:all); use ZoneMinder::ConfigData qw(:all); use Carp; sub loadConfigFromDB { print( "Loading config from DB\n" ); my $dbh = DBI->connect( "DBI:mysql:database=".ZM_DB_NAME.";host=".ZM_DB_HOST, ZM_DB_USER, ZM_DB_PASS ); if ( !$dbh ) { print( "Error: unable to load options from database: $DBI::errstr\n" ); return( 0 ); } my $sql = "select * from Config"; my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or croak( "Can't execute: ".$sth->errstr() ); my $option_count = 0; while( my $config = $sth->fetchrow_hashref() ) { my ( $name, $value ) = ( $config->{Name}, $config->{Value} ); #print( "Name = '$name'\n" ); my $option = $options_hash{$name}; if ( !$option ) { warn( "No option '$name' found, removing" ); next; } #next if ( $option->{category} eq 'hidden' ); if ( defined($value) ) { if ( $option->{type} == $types{boolean} ) { $option->{value} = $value?"yes":"no"; } else { $option->{value} = $value; } } $option_count++;; } $sth->finish(); $dbh->disconnect(); return( $option_count ); } sub saveConfigToDB { print( "Saving config to DB\n" ); my $dbh = DBI->connect( "DBI:mysql:database=".ZM_DB_NAME.";host=".ZM_DB_HOST, ZM_DB_USER, ZM_DB_PASS ); if ( !$dbh ) { print( "Error: unable to save options to database: $DBI::errstr\n" ); return( 0 ); } my $sql = "delete from Config"; my $res = $dbh->do( $sql ) or croak( "Can't do '$sql': ".$dbh->errstr() ); $sql = "replace into Config set Id = ?, Name = ?, Value = ?, Type = ?, DefaultValue = ?, Hint = ?, Pattern = ?, Format = ?, Prompt = ?, Help = ?, Category = ?, Readonly = ?, Requires = ?"; my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); foreach my $option ( @options ) { #next if ( $option->{category} eq 'hidden' ); #print( $option->{name}."\n" ) if ( !$option->{category} ); $option->{db_type} = $option->{type}->{db_type}; $option->{db_hint} = $option->{type}->{hint}; $option->{db_pattern} = $option->{type}->{pattern}; $option->{db_format} = $option->{type}->{format}; if ( $option->{db_type} eq "boolean" ) { $option->{db_value} = ($option->{value} eq "yes")?"1":"0"; } else { $option->{db_value} = $option->{value}; } if ( my $requires = $option->{requires} ) { $option->{db_requires} = join( ";", map { my $value = $_->{value}; $value = ($value eq "yes")?1:0 if ( $options_hash{$_->{name}}->{db_type} eq "boolean" ); ( "$_->{name}=$value" ) } @$requires ); } else { } my $res = $sth->execute( $option->{id}, $option->{name}, $option->{db_value}, $option->{db_type}, $option->{default}, $option->{db_hint}, $option->{db_pattern}, $option->{db_format}, $option->{description}, $option->{help}, $option->{category}, $option->{readonly}?1:0, $option->{db_requires} ) or croak( "Can't execute: ".$sth->errstr() ); } $sth->finish(); $dbh->disconnect(); } 1; __END__ =head1 NAME ZoneMinder::ConfigAdmin - ZoneMinder Configuration Administration module =head1 SYNOPSIS use ZoneMinder::ConfigAdmin; use ZoneMinder::ConfigAdmin qw(:all); loadConfigFromDB(); saveConfigToDB(); =head1 DESCRIPTION The ZoneMinder:ConfigAdmin module contains the master definition of the ZoneMinder configuration options as well as helper methods. This module is intended for specialist confguration management and would not normally be used by end users. The configuration held in this module, which was previously in zmconfig.pl, includes the name, default value, description, help text, type and category for each option, as well as a number of additional fields in a small number of cases. =head1 METHODS =over 4 =item loadConfigFromDB (); Loads existing configuration from the database (if any) and merges it with the definitions held in this module. This results in the merging of any new configuration and the removal of any deprecated configuration while preserving the existing values of every else. =item saveConfigToDB (); Saves configuration held in memory to the database. The act of loading and saving configuration is a convenient way to ensure that the configuration held in the database corresponds with the most recent definitions and that all components are using the same set of configuration. =head2 EXPORT None by default. The :data tag will export the various configuration data structures The :functions tag will export the helper functions. The :all tag will export all above symbols. =head1 SEE ALSO http://www.zoneminder.com =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in000066400000000000000000004313441225361755400252250ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Config Data Module, $Date: 2011-01-20 18:49:42 +0000 (Thu, 20 Jan 2011) $, $Revision: 3230 $ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the debug definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::ConfigData; use 5.006; use strict; use warnings; require Exporter; require ZoneMinder::Base; our @ISA = qw(Exporter ZoneMinder::Base); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'data' => [ qw( %types @options %options_hash ) ] ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; our @EXPORT_OK = ( @{ $EXPORT_TAGS{'data'} } ); our @EXPORT = qw(); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Configuration Data # # ========================================================================== use Carp; our $configInitialised = 0; sub INIT { initialiseConfig(); } # Types our %types = ( string => { db_type=>"string", hint=>"string", pattern=>qr|^(.+)$|, format=>q( $1 ) }, alphanum => { db_type=>"string", hint=>"alphanumeric", pattern=>qr|^([a-zA-Z0-9-_]+)$|, format=>q( $1 ) }, text => { db_type=>"text", hint=>"free text", pattern=>qr|^(.+)$|, format=>q( $1 ) }, boolean => { db_type=>"boolean", hint=>"yes|no", pattern=>qr|^([yn])|i, check=>q( $1 ), format=>q( ($1 =~ /^y/) ? "yes" : "no" ) }, integer => { db_type=>"integer", hint=>"integer", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, decimal => { db_type=>"decimal", hint=>"decimal", pattern=>qr|^(\d+(?:\.\d+)?)$|, format=>q( $1 ) }, hexadecimal => { db_type=>"hexadecimal", hint=>"hexadecimal", pattern=>qr|^(?:0x)?([0-9a-f]{1,8})$|, format=>q( "0x".$1 ) }, tristate => { db_type=>"string", hint=>"auto|yes|no", pattern=>qr|^([ayn])|i, check=>q( $1 ), format=>q( ($1 =~ /^y/) ? "yes" : ($1 =~ /^n/ ? "no" : "auto" ) ) }, abs_path => { db_type=>"string", hint=>"/absolute/path/to/somewhere", pattern=>qr|^((?:/[^/]*)+?)/?$|, format=>q( $1 ) }, rel_path => { db_type=>"string", hint=>"relative/path/to/somewhere", pattern=>qr|^((?:[^/].*)?)/?$|, format=>q( $1 ) }, directory => { db_type=>"string", hint=>"directory", pattern=>qr|^([a-zA-Z0-9-_.]+)$|, format=>q( $1 ) }, file => { db_type=>"string", hint=>"filename", pattern=>qr|^([a-zA-Z0-9-_.]+)$|, format=>q( $1 ) }, hostname => { db_type=>"string", hint=>"host.your.domain", pattern=>qr|^([a-zA-Z0-9_.-]+)$|, format=>q( $1 ) }, url => { db_type=>"string", hint=>"http://host.your.domain/", pattern=>qr|^(?:http://)?(.+)$|, format=>q( "http://".$1 ) }, email => { db_type=>"string", hint=>"your.name\@your.domain", pattern=>qr|^([a-zA-Z0-9_.-]+)\@([a-zA-Z0-9_.-]+)$|, format=>q( $1\@$2 ) }, ); our @options = ( { name => "ZM_LANG_DEFAULT", default => "en_gb", description => "Default language used by web interface", help => "ZoneMinder allows the web interface to use languages other than English if the appropriate language file has been created and is present. This option allows you to change the default language that is used from the shipped language, British English, to another language", type => $types{string}, category => "system", }, { name => "ZM_OPT_USE_AUTH", default => "no", description => "Authenticate user logins to ZoneMinder", help => "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.", type => $types{boolean}, category => "system", }, { name => "ZM_AUTH_TYPE", default => "builtin", description => "What is used to authenticate ZoneMinder users", help => "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.", requires => [ { name=>"ZM_OPT_USE_AUTH", value=>"yes" } ], type => { db_type=>"string", hint=>"builtin|remote", pattern=>qr|^([br])|i, format=>q( $1 =~ /^b/ ? "builtin" : "remote" ) }, category => "system", }, { name => "ZM_AUTH_RELAY", default => "hashed", description => "Method used to relay authentication information", help => "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.", requires => [ { name=>"ZM_OPT_USE_AUTH", value=>"yes" } ], type => { db_type=>"string", hint=>"hashed|plain|none", pattern=>qr|^([hpn])|i, format=>q( ($1 =~ /^h/) ? "hashed" : ($1 =~ /^p/ ? "plain" : "none" ) ) }, category => "system", }, { name => "ZM_AUTH_HASH_SECRET", default => "...Change me to something unique...", description => "Secret for encoding hashed authentication information", help => "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.", requires => [ { name=>"ZM_OPT_USE_AUTH", value=>"yes" }, { name=>"ZM_AUTH_RELAY", value=>"hashed" } ], type => $types{string}, category => "system", }, { name => "ZM_AUTH_HASH_IPS", default => "yes", description => "Include IP addresses in the authentication hash", help => "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.", requires => [ { name=>"ZM_OPT_USE_AUTH", value=>"yes" }, { name=>"ZM_AUTH_RELAY", value=>"hashed" } ], type => $types{boolean}, category => "system", }, { name => "ZM_AUTH_HASH_LOGINS", default => "no", description => "Allow login by authentication hash", help => "The normal process for logging into ZoneMinder is via the login screen with username and password. In some circumstances it may be desirable to allow access directly to one or more pages, for instance from a third party application. If this option is enabled then adding an 'auth' parameter to any request will include a shortcut login bypassing the login screen, if not already logged in. As authentication hashes are time and, optionally, IP limited this can allow short-term access to ZoneMinder screens from other web pages etc. In order to use this the calling application will hae to generate the authentication hash itself and ensure it is valid. If you use this option you should ensure that you have modified the ZM_AUTH_HASH_SECRET to somethign unique to your system.", requires => [ { name=>"ZM_OPT_USE_AUTH", value=>"yes" }, { name=>"ZM_AUTH_RELAY", value=>"hashed" } ], type => $types{boolean}, category => "system", }, { name => "ZM_DIR_EVENTS", default => "events", description => "Directory where events are stored", help => "This is the path to the events directory where all the event images and other miscellaneous files are stored. It is normally given as a subdirectory of the web directory you have specified earlier however if disk space is tight it can reside on another partition in which case you should create a link from that area to the path you give here.", type => $types{directory}, category => "paths", }, { name => "ZM_USE_DEEP_STORAGE", default => "yes", description => "Use a deep filesystem hierarchy for events", help => "Traditionally ZoneMinder stores all events for a monitor in one directory for that monitor. This is simple and efficient except when you have very large amounts of events. Some filesystems are unable to store more than 32k files in one directory and even without this limitation, large numbers of files in a directory can slow creation and deletion of files. This option allows you to select an alternate method of storing events by year/month/day/hour/min/second which has the effect of separating events out into more directories, resulting in less per directory, and also making it easier to manually navigate to any events that may have happened at a particular time or date.", type => $types{boolean}, category => "paths", }, { name => "ZM_DIR_IMAGES", default => "images", description => "Directory where the images that the ZoneMinder client generates are stored", help => "ZoneMinder generates a myriad of images, mosty of which are associated with events. For those that aren't this is where they go.", type => $types{directory}, category => "paths", }, { name => "ZM_DIR_SOUNDS", default => "sounds", description => "Directory to the sounds that the ZoneMinder client can use", help => "ZoneMinder can optionally play a sound file when an alarm is detected. This indicates where (relative to the web root) to look for this file.", type => $types{directory}, category => "paths", }, { name => "ZM_PATH_ZMS", default => "/cgi-bin/nph-zms", description => "Web path to zms streaming server", help => "The ZoneMinder streaming server is required to send streamed images to your browser. It will be installed into the cgi-bin path given at configuration time. This option determines what the web path to the server is rather than the local path on your machine. Ordinarily the streaming server runs in parser-header mode however if you experience problems with streaming you can change this to non-parsed-header (nph) mode by changing 'zms' to 'nph-zms'.", type => $types{rel_path}, category => "paths", }, { name => "ZM_COLOUR_JPEG_FILES", default => "no", description => "Colourise greyscale JPEG files", help => "Cameras that capture in greyscale can write their captured images to jpeg files with a corresponding greyscale colour space. This saves a small amount of disk space over colour ones. However some tools such as ffmpeg either fail to work with this colour space or have to convert it beforehand. Setting this option to yes uses up a little more space but makes creation of MPEG files much faster.", type => $types{boolean}, category => "images", }, { name => "ZM_ADD_JPEG_COMMENTS", default => "no", description => "Add jpeg timestamp annotations as file header comments", help => "JPEG files may have a number of extra fields added to the file header. The comment field may have any kind of text added. This options allows you to have the same text that is used to annotate the image additionally included as a file header comment. If you archive event images to other locations this may help you locate images for particular events or times if you use software that can read comment headers.", type => $types{boolean}, category => "images", }, { name => "ZM_JPEG_FILE_QUALITY", default => "70", description => "Set the JPEG quality setting for the saved event files (1-100)", help => "When ZoneMinder detects an event it will save the images associated with that event to files. These files are in the JPEG format and can be viewed or streamed later. This option specifies what image quality should be used to save these files. A higher number means better quality but less compression so will take up more disk space and take longer to view over a slow connection. By contrast a low number means smaller, quicker to view, files but at the price of lower quality images. This setting applies to all images written except if the capture image has caused an alarm and the alarm file quality option is set at a higher value when that is used instead.", type => $types{integer}, category => "images", }, { name => "ZM_JPEG_ALARM_FILE_QUALITY", default => "0", description => "Set the JPEG quality setting for the saved event files during an alarm (1-100)", help => "This value is equivalent to the regular jpeg file quality setting above except that it only applies to images saved while in an alarm state and then only if this value is set to a higher quality setting than the ordinary file setting. If set to a lower value then it is ignored. Thus leaving it at the default of 0 effectively means to use the regular file quality setting for all saved images. This is to prevent acccidentally saving important images at a worse quality setting.", type => $types{integer}, category => "images", }, # Deprecated, now stream quality { name => "ZM_JPEG_IMAGE_QUALITY", default => "70", description => "Set the JPEG quality setting for the streamed 'live' images (1-100)", help => "When viewing a 'live' stream for a monitor ZoneMinder will grab an image from the buffer and encode it into JPEG format before sending it. This option specifies what image quality should be used to encode these images. A higher number means better quality but less compression so will take longer to view over a slow connection. By contrast a low number means quicker to view images but at the price of lower quality images. This option does not apply when viewing events or still images as these are usually just read from disk and so will be encoded at the quality specified by the previous options.", type => $types{integer}, category => "hidden", }, { name => "ZM_JPEG_STREAM_QUALITY", default => "70", description => "Set the JPEG quality setting for the streamed 'live' images (1-100)", help => "When viewing a 'live' stream for a monitor ZoneMinder will grab an image from the buffer and encode it into JPEG format before sending it. This option specifies what image quality should be used to encode these images. A higher number means better quality but less compression so will take longer to view over a slow connection. By contrast a low number means quicker to view images but at the price of lower quality images. This option does not apply when viewing events or still images as these are usually just read from disk and so will be encoded at the quality specified by the previous options.", type => $types{integer}, category => "images", }, { name => "ZM_MPEG_TIMED_FRAMES", default => "yes", description => "Tag video frames with a timestamp for more realistic streaming", help => "When using streamed MPEG based video, either for live monitor streams or events, ZoneMinder can send the streams in two ways. If this option is selected then the timestamp for each frame, taken from it's capture time, is included in the stream. This means that where the frame rate varies, for instance around an alarm, the stream will still maintain it's 'real' timing. If this option is not selected then an approximate frame rate is calculated and that is used to schedule frames instead. This option should be selected unless you encounter problems with your preferred streaming method.", type => $types{boolean}, category => "images", }, { name => "ZM_MPEG_LIVE_FORMAT", default => "swf", description => "What format 'live' video streams are played in", help => "When using MPEG mode ZoneMinder can output live video. However what formats are handled by the browser varies greatly between machines. This option allows you to specify a video format using a file extension format, so you would just enter the extension of the file type you would like and the rest is determined from that. The default of 'asf' works well under Windows with Windows Media Player but I'm currently not sure what, if anything, works on a Linux platform. If you find out please let me know! If this option is left blank then live streams will revert to being in motion jpeg format", type => $types{string}, category => "images", }, { name => "ZM_MPEG_REPLAY_FORMAT", default => "swf", description => "What format 'replay' video streams are played in", help => "When using MPEG mode ZoneMinder can replay events in encoded video format. However what formats are handled by the browser varies greatly between machines. This option allows you to specify a video format using a file extension format, so you would just enter the extension of the file type you would like and the rest is determined from that. The default of 'asf' works well under Windows with Windows Media Player and 'mpg', or 'avi' etc should work under Linux. If you know any more then please let me know! If this option is left blank then live streams will revert to being in motion jpeg format", type => $types{string}, category => "images", }, { name => "ZM_RAND_STREAM", default => "yes", description => "Add a random string to prevent caching of streams", help => "Some browsers can cache the streams used by ZoneMinder. In order to prevent his a harmless random string can be appended to the url to make each invocation of the stream appear unique.", type => $types{boolean}, category => "images", }, { name => "ZM_OPT_CAMBOZOLA", default => "no", description => "Is the (optional) cambozola java streaming client installed", help => "Cambozola is a handy low fat cheese flavoured Java applet that ZoneMinder uses to view image streams on browsers such as Internet Explorer that don't natively support this format. If you use this browser it is highly recommended to install this from http://www.charliemouse.com/code/cambozola/ however if it is not installed still images at a lower refresh rate can still be viewed.", type => $types{boolean}, category => "images", }, { name => "ZM_PATH_CAMBOZOLA", default => "cambozola.jar", description => "Web path to (optional) cambozola java streaming client", help => "Cambozola is a handy low fat cheese flavoured Java applet that ZoneMinder uses to view image streams on browsers such as Internet Explorer that don't natively support this format. If you use this browser it is highly recommended to install this from http://www.charliemouse.com/code/cambozola/ however if it is not installed still images at a lower refresh rate can still be viewed. Leave this as 'cambozola.jar' if cambozola is installed in the same directory as the ZoneMinder web client files.", requires => [ { name=>"ZM_OPT_CAMBOZOLA", value=>"yes" } ], type => $types{rel_path}, category => "images", }, { name => "ZM_RELOAD_CAMBOZOLA", default => "0", description => "After how many seconds should Cambozola be reloaded in live view", help => "Cambozola allows for the viewing of streaming MJPEG however it caches the entire stream into cache space on the computer, setting this to a number > 0 will cause it to automatically reload after that many seconds to avoid filling up a hard drive.", type => $types{integer}, category => "images", }, { name => "ZM_TIMESTAMP_ON_CAPTURE", default => "yes", description => "Timestamp images as soon as they are captured", help => "ZoneMinder can add a timestamp to images in two ways. The default method, when this option is set, is that each image is timestamped immediately when captured and so the image held in memory is marked right away. The second method does not timestamp the images until they are either saved as part of an event or accessed over the web. The timestamp used in both methods will contain the same time as this is preserved along with the image. The first method ensures that an image is timestamped regardless of any other circumstances but will result in all images being timestamped even those never saved or viewed. The second method necessitates that saved images are copied before being saved otherwise two timestamps perhaps at different scales may be applied. This has the (perhaps) desirable side effect that the timestamp is always applied at the same resolution so an image that has scaling applied will still have a legible and correctly scaled timestamp.", type => $types{boolean}, category => "config", }, { name => "ZM_CPU_EXTENSIONS", default => "yes", description => "Use advanced CPU extensions to increase performance", help => "When advanced processor extensions such as SSE2 or SSSE3 are available, ZoneMinder can use them, which should increase performance and reduce system load. Enabling this option on processors that do not support the advanced processors extensions used by ZoneMinder is harmless and will have no effect.", type => $types{boolean}, category => "config", }, { name => "ZM_FAST_IMAGE_BLENDS", default => "yes", description => "Use a fast algorithm to blend the reference image", help => "To detect alarms ZoneMinder needs to blend the captured image with the stored reference image to update it for comparison with the next image. The reference blend percentage specified for the monitor controls how much the new image affects the reference image. There are two methods that are available for this. If this option is set then fast calculation which does not use any multiplication or division is used. This calculation is extremely fast, however it limits the possible blend percentages to 50%, 25%, 12.5%, 6.25%, 3.25% and 1.5%. Any other blend percentage will be rounded to the nearest possible one. The alternative is to switch this option off and use standard blending instead, which is slower.", type => $types{boolean}, category => "config", }, { name => "ZM_OPT_ADAPTIVE_SKIP", default => "yes", description => "Should frame analysis try and be efficient in skipping frames", help => "In previous versions of ZoneMinder the analysis daemon would attempt to keep up with the capture daemon by processing the last captured frame on each pass. This would sometimes have the undesirable side-effect of missing a chunk of the initial activity that caused the alarm because the pre-alarm frames would all have to be written to disk and the database before processing the next frame, leading to some delay between the first and second event frames. Setting this option enables a newer adaptive algorithm where the analysis daemon attempts to process as many captured frames as possible, only skipping frames when in danger of the capture daemon overwriting yet to be processed frames. This skip is variable depending on the size of the ring buffer and the amount of space left in it. Enabling this option will give you much better coverage of the beginning of alarms whilst biasing out any skipped frames towards the middle or end of the event. However you should be aware that this will have the effect of making the analysis daemon run somewhat behind the capture daemon during events and for particularly fast rates of capture it is possible for the adaptive algorithm to be overwhelmed and not have time to react to a rapid build up of pending frames and thus for a buffer overrun condition to occur.", type => $types{boolean}, category => "config", }, { name => "ZM_MAX_SUSPEND_TIME", default => "30", description => "Maximum time that a monitor may have motion detection suspended", help => "ZoneMinder allows monitors to have motion detection to be suspended, for instance while panning a camera. Ordinarily this relies on the operator resuming motion detection afterwards as failure to do so can leave a monitor in a permanently suspended state. This setting allows you to set a maximum time which a camera may be suspended for before it automatically resumes motion detection. This time can be extended by subsequent suspend indications after the first so continuous camera movement will also occur while the monitor is suspended.", type => $types{integer}, category => "config", }, # Deprecated, really no longer necessary { name => "ZM_OPT_REMOTE_CAMERAS", default => "no", description => "Are you going to use remote/networked cameras", help => "ZoneMinder can work with both local cameras, ie. those attached physically to your computer and remote or network cameras. If you will be using networked cameras select this option.", type => $types{boolean}, category => "hidden", }, # Deprecated, now set on a per monitor basis using the Method field { name => "ZM_NETCAM_REGEXPS", default => "yes", description => "Use regular expression matching with network cameras", help => "Traditionally ZoneMinder has used complex regular regular expressions to handle the multitude of formats that network cameras produce. In versions from 1.21.1 the default is to use a simpler and faster built in pattern matching methodology. This works well with most networks cameras but if you have problems you can try the older, but more flexible, regular expression based method by selecting this option. Note, to use this method you must have libpcre installed on your system.", requires => [ { name => "ZM_OPT_REMOTE_CAMERAS", value => "yes" } ], type => $types{boolean}, category => "hidden", }, { name => "ZM_HTTP_VERSION", default => "1.0", description => "The version of HTTP that ZoneMinder will use to connect", help => "ZoneMinder can communicate with network cameras using either of the HTTP/1.1 or HTTP/1.0 standard. A server will normally fall back to the version it supports iwht no problem so this should usually by left at the default. However it can be changed to HTTP/1.0 if necessary to resolve particular issues.", type => { db_type=>"string", hint=>"1.1|1.0", pattern=>qr|^(1\.[01])$|, format=>q( $1?$1:"" ) }, category => "network", }, { name => "ZM_HTTP_UA", default => "ZoneMinder", description => "The user agent that ZoneMinder uses to identify itself", help => "When ZoneMinder communicates with remote cameras it will identify itself using this string and it's version number. This is normally sufficient, however if a particular cameras expects only to communicate with certain browsers then this can be changed to a different string identifying ZoneMinder as Internet Explorer or Netscape etc.", type => $types{string}, category => "network", }, { name => "ZM_HTTP_TIMEOUT", default => "2500", description => "How long ZoneMinder waits before giving up on images (milliseconds)", help => "When retrieving remote images ZoneMinder will wait for this length of time before deciding that an image is not going to arrive and taking steps to retry. This timeout is in milliseconds (1000 per second) and will apply to each part of an image if it is not sent in one whole chunk.", type => $types{integer}, category => "network", }, { name => "ZM_MIN_RTP_PORT", default => "40200", description => "Minimum port that ZoneMinder will listen for RTP traffic on", help => "When ZoneMinder communicates with MPEG4 capable cameras using RTP with the unicast method it must open ports for the camera to connect back to for control and streaming purposes. This setting specifies the minimum port number that ZoneMinder will use. Ordinarily two adjacent ports are used for each camera, one for control packets and one for data packets. This port should be set to an even number, you may also need to open up a hole in your firewall to allow cameras to connect back if you wish to use unicasting.", type => $types{integer}, category => "network", }, { name => "ZM_MAX_RTP_PORT", default => "40499", description => "Maximum port that ZoneMinder will listen for RTP traffic on", help => "When ZoneMinder communicates with MPEG4 capable cameras using RTP with the unicast method it must open ports for the camera to connect back to for control and streaming purposes. This setting specifies the maximum port number that ZoneMinder will use. Ordinarily two adjacent ports are used for each camera, one for control packets and one for data packets. This port should be set to an even number, you may also need to open up a hole in your firewall to allow cameras to connect back if you wish to use unicasting. You should also ensure that you have opened up at least two ports for each monitor that will be connecting to unicasting network cameras.", type => $types{integer}, category => "network", }, { name => "ZM_OPT_FFMPEG", default => "@OPT_FFMPEG@", description => "Is the ffmpeg video encoder/decoder installed", help => "ZoneMinder can optionally encode a series of video images into an MPEG encoded movie file for viewing, downloading or storage. This option allows you to specify whether you have the ffmpeg tools installed. Note that creating MPEG files can be fairly CPU and disk intensive and is not a required option as events can still be reviewed as video streams without it.", type => $types{boolean}, category => "images", }, { name => "ZM_PATH_FFMPEG", default => "@PATH_FFMPEG@", description => "Path to (optional) ffmpeg mpeg encoder", help => "This path should point to where ffmpeg has been installed.", requires => [ { name=>"ZM_OPT_FFMPEG", value=>"yes" } ], type => $types{abs_path}, category => "images", }, { name => "ZM_FFMPEG_INPUT_OPTIONS", default => "", description => "Additional input options to ffmpeg", help => "Ffmpeg can take many options on the command line to control the quality of video produced. This option allows you to specify your own set that apply to the input to ffmpeg (options that are given before the -i option). Check the ffmpeg documentation for a full list of options which may be used here.", requires => [ { name=>"ZM_OPT_FFMPEG", value=>"yes" } ], type => $types{string}, category => "images", }, { name => "ZM_FFMPEG_OUTPUT_OPTIONS", default => "-r 25", description => "Additional output options to ffmpeg", help => "Ffmpeg can take many options on the command line to control the quality of video produced. This option allows you to specify your own set that apply to the output from ffmpeg (options that are given after the -i option). Check the ffmpeg documentation for a full list of options which may be used here. The most common one will often be to force an output frame rate supported by the video encoder.", requires => [ { name=>"ZM_OPT_FFMPEG", value=>"yes" } ], type => $types{string}, category => "images", }, { name => "ZM_FFMPEG_FORMATS", default => "mpg mpeg wmv asf avi* mov swf 3gp**", description => "Formats to allow for ffmpeg video generation", help => "Ffmpeg can generate video in many different formats. This option allows you to list the ones you want to be able to select. As new formats are supported by ffmpeg you can add them here and be able to use them immediately. Adding a '*' after a format indicates that this will be the default format used for web video, adding '**' defines the default format for phone video.", requires => [ { name=>"ZM_OPT_FFMPEG", value=>"yes" } ], type => $types{string}, category => "images", }, { name => "ZM_LOG_LEVEL_SYSLOG", default => "0", description => "Save logging output to the system log", help => "ZoneMinder logging is now more more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output that goes to the system log. ZoneMinder binaries have always logged to the system log but now scripts and web logging is also included. To preserve the previous behaviour you should ensure this value is set to Info or Warning. This option controls the maximum level of logging that will be written, so Info includes Warnings and Errors etc. To disable entirely, set this option to None. You should use caution when setting this option to Debug as it can affect severely affect system performance. If you want debug you will also need to set a level and component below", type => { db_type=>"integer", hint=>"None=-5|Panic=-4|Fatal=-3|Error=-2|Warning=-1|Info=0|Debug=1", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, category => "logging", }, { name => "ZM_LOG_LEVEL_FILE", default => "-5", description => "Save logging output to component files", help => "ZoneMinder logging is now more more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output that goes to individual log files written by specific components. This is how logging worked previously and although useful for tracking down issues in specific components it also resulted in many disparate log files. To preserve this behaviour you should ensure this value is set to Info or Warning. This option controls the maximum level of logging that will be written, so Info includes Warnings and Errors etc. To disable entirely, set this option to None. You should use caution when setting this option to Debug as it can affect severely affect system performance though file output has less impact than the other options. If you want debug you will also need to set a level and component below", type => { db_type=>"integer", hint=>"None=-5|Panic=-4|Fatal=-3|Error=-2|Warning=-1|Info=0|Debug=1", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, category => "logging", }, { name => "ZM_LOG_LEVEL_WEBLOG", default => "-5", description => "Save logging output to the weblog", help => "ZoneMinder logging is now more more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output from the web interface that goes to the httpd error log. Note that only web logging from PHP and JavaScript files is included and so this option is really only useful for investigating specific issues with those components. This option controls the maximum level of logging that will be written, so Info includes Warnings and Errors etc. To disable entirely, set this option to None. You should use caution when setting this option to Debug as it can affect severely affect system performance. If you want debug you will also need to set a level and component below", type => { db_type=>"integer", hint=>"None=-5|Panic=-4|Fatal=-3|Error=-2|Warning=-1|Info=0|Debug=1", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, category => "logging", }, { name => "ZM_LOG_LEVEL_DATABASE", default => "0", description => "Save logging output to the database", help => "ZoneMinder logging is now more more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output that is written to the database. This is a new option which can make viewing logging output easier and more intuitive and also makes it easier to get an overall impression of how the system is performing. If you have a large or very busy system then it is possible that use of this option may slow your system down if the table becomes very large. Ensure you use the LOG_DATABASE_LIMIT option to keep the table to a manageable size. This option controls the maximum level of logging that will be written, so Info includes Warnings and Errors etc. To disable entirely, set this option to None. You should use caution when setting this option to Debug as it can affect severely affect system performance. If you want debug you will also need to set a level and component below", type => { db_type=>"integer", hint=>"None=-5|Panic=-4|Fatal=-3|Error=-2|Warning=-1|Info=0|Debug=1", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, category => "logging", }, { name => "ZM_LOG_DATABASE_LIMIT", default => "7 day", description => "Maximum number of log entries to retain", help => "If you are using database logging then it is possible to quickly build up a large number of entries in the Logs table. This option allows you to specify how many of these entries are kept. If you set this option to a number greater than zero then that number is used to determine the maximum number of rows, less than or equal to zero indicates no limit and is not recommended. You can also set this value to time values such as ' day' which will limit the log entries to those newer than that time. You can specify 'hour', 'day', 'week', 'month' and 'year', note that the values should be singular (no 's' at the end). The Logs table is pruned periodically so it is possible for more than the expected number of rows to be present briefly in the meantime.", type => $types{string}, category => "logging", }, { name => "ZM_LOG_DEBUG", default => "no", description => "Switch debugging on", help => "ZoneMinder components usually support debug logging available to help with diagnosing problems. Binary components have several levels of debug whereas more other components have only one. Normally this is disabled to minimise performance penalties and avoid filling logs too quickly. This option lets you switch on other options that allow you to configure additional debug information to be output. Components will pick up this instruction when they are restarted.", type => $types{boolean}, category => "logging", }, { name => "ZM_LOG_DEBUG_TARGET", default => "", description => "What components should have extra debug enabled", help => "There are three scopes of debug available. Leaving this option blank means that all components will use extra debug (not recommended). Setting this option to '_', e.g. _zmc, will limit extra debug to that component only. Setting this option to '__', e.g. '_zmc_m1' will limit extra debug to that instance of the component only. This is ordinarily what you probably want to do. To debug scripts use their names without the .pl extension, e.g. '_zmvideo' and to debug issues with the web interface use '_web'. You can specify multiple targets by separating them with '|' characters.", requires => [ { name => "ZM_LOG_DEBUG", value => "yes" } ], type => $types{string}, category => "logging", }, { name => "ZM_LOG_DEBUG_LEVEL", default => 1, description => "What level of extra debug should be enabled", help => "There are 9 levels of debug available, with higher numbers being more debug and level 0 being no debug. However not all levels are used by all components. Also if there is debug at a high level it is usually likely to be output at such a volume that it may obstruct normal operation. For this reason you should set the level carefully and cautiously until the degree of debug you wish to see is present. Scripts and the web interface only have one level so this is an on/off type option for them.", requires => [ { name => "ZM_LOG_DEBUG", value => "yes" } ], type => { db_type=>"integer", hint=>"1|2|3|4|5|6|7|8|9", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, category => "logging", }, { name => "ZM_LOG_DEBUG_FILE", default => "@ZM_TMPDIR@/zm_debug.log+", description => "Where extra debug is output to", help => "This option allows you to specify a different target for debug output. All components have a default log file which will norally be in /tmp or /var/log and this is where debug will be written to if this value is empty. Adding a path here will temporarily redirect debug, and other logging output, to this file. This option is a simple filename and you are debugging several components then they will all try and write to the same file with undesirable consequences. Appending a '+' to the filename will cause the file to be created with a '.' suffix containing your process id. In this way debug from each run of a component is kept separate. This is the recommended setting as it will also prevent subsequent runs from overwriting the same log. You should ensure that permissions are set up to allow writing to the file and directory specified here.", requires => [ { name => "ZM_LOG_DEBUG", value => "yes" } ], type => $types{string}, category => "logging", }, { name => "ZM_LOG_CHECK_PERIOD", default => "900", description => "Time period used when calculating overall system health", help => "When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to indicate what period of historical events are used in this calculation. This value is expressed in seconds and is ignored if LOG_LEVEL_DATABASE is set to None.", type => $types{integer}, category => "logging", }, { name => "ZM_LOG_ALERT_WAR_COUNT", default => "1", description => "Number of warnings indicating system alert state", help => "When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many warnings must have occurred within the defined time period to generate an overall system alert state. A value of zero means warnings are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None.", type => $types{integer}, category => "logging", }, { name => "ZM_LOG_ALERT_ERR_COUNT", default => "1", description => "Number of errors indicating system alert state", help => "When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many errors must have occurred within the defined time period to generate an overall system alert state. A value of zero means errors are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None.", type => $types{integer}, category => "logging", }, { name => "ZM_LOG_ALERT_FAT_COUNT", default => "0", description => "Number of fatal error indicating system alert state", help => "When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many fatal errors (including panics) must have occurred within the defined time period to generate an overall system alert state. A value of zero means fatal errors are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None.", type => $types{integer}, category => "logging", }, { name => "ZM_LOG_ALARM_WAR_COUNT", default => "100", description => "Number of warnings indicating system alarm state", help => "When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many warnings must have occurred within the defined time period to generate an overall system alarm state. A value of zero means warnings are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None.", type => $types{integer}, category => "logging", }, { name => "ZM_LOG_ALARM_ERR_COUNT", default => "10", description => "Number of errors indicating system alarm state", help => "When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many errors must have occurred within the defined time period to generate an overall system alarm state. A value of zero means errors are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None.", type => $types{integer}, category => "logging", }, { name => "ZM_LOG_ALARM_FAT_COUNT", default => "1", description => "Number of fatal error indicating system alarm state", help => "When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many fatal errors (including panics) must have occurred within the defined time period to generate an overall system alarm state. A value of zero means fatal errors are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None.", type => $types{integer}, category => "logging", }, { name => "ZM_RECORD_EVENT_STATS", default => "yes", description => "Record event statistical information, switch off if too slow", help => "This version of ZoneMinder records detailed information about events in the Stats table. This can help in profiling what the optimum settings are for Zones though this is tricky at present. However in future releases this will be done more easily and intuitively, especially with a large sample of events. The default option of 'yes' allows this information to be collected now in readiness for this but if you are concerned about performance you can switch this off in which case no Stats information will be saved.", type => $types{boolean}, category => "logging", }, { name => "ZM_RECORD_DIAG_IMAGES", default => "no", description => "Record intermediate alarm diagnostic images, can be very slow", help => "In addition to recording event statistics you can also record the intermediate diagnostic images that display the results of the various checks and processing that occur when trying to determine if an alarm event has taken place. There are several of these images generated for each frame and zone for each alarm or alert frame so this can have a massive impact on performance. Only switch this setting on for debug or analysis purposes and remember to switch it off again once no longer required.", type => $types{boolean}, category => "logging", }, { name => "ZM_DUMP_CORES", default => "no", description => "Create core files on unexpected process failure.", help => "When an unrecoverable error occurs in a ZoneMinder binary process is has traditionally been trapped and the details written to logs to aid in remote analysis. However in some cases it is easier to diagnose the error if a core file, which is a memory dump of the process at the time of the error, is created. This can be interactively analysed in the debugger and may reveal more or better information than that available from the logs. This option is recommended for advanced users only otherwise leave at the default. Note using this option to trigger core files will mean that there will be no indication in the binary logs that a process has died, they will just stop, however the zmdc log will still contain an entry. Also note that you may have to explicitly enable core file creation on your system via the 'ulimit -c' command or other means otherwise no file will be created regardless of the value of this option.", type => $types{boolean}, category => "logging", }, { name => "ZM_PATH_MAP", default => "/dev/shm", description => "Path to the mapped memory files that that ZoneMinder can use", help => "ZoneMinder has historically used IPC shared memory for shared data between processes. This has it's advantages and limitations. This version of ZoneMinder can use an alternate method, mapped memory, instead with can be enabled with the --enable--mmap directive to configure. This requires less system configuration and is generally more flexible. However it requires each shared data segment to map onto a filesystem file. This option indicates where those mapped files go. You should ensure that this location has sufficient space for these files and for the best performance it should be a tmpfs file system or ramdisk otherwise disk access may render this method slower than the regular shared memory one.", type => $types{abs_path}, category => "paths", }, { name => "ZM_PATH_SOCKS", default => "@ZM_TMPDIR@", description => "Path to the various Unix domain socket files that ZoneMinder uses", help => "ZoneMinder generally uses Unix domain sockets where possible. This reduces the need for port assignments and prevents external applications from possibly compromising the daemons. However each Unix socket requires a .sock file to be created. This option indicates where those socket files go.", type => $types{abs_path}, category => "paths", }, { name => "ZM_PATH_LOGS", default => "@ZM_LOGDIR@", description => "Path to the various logs that the ZoneMinder daemons generate", help => "There are various daemons that are used by ZoneMinder to perform various tasks. Most generate helpful log files and this is where they go. They can be deleted if not required for debugging.", type => $types{abs_path}, category => "paths", }, { name => "ZM_PATH_SWAP", default => "@ZM_TMPDIR@", description => "Path to location for temporary swap images used in streaming", help => "Buffered playback requires temporary swap images to be stored for each instance of the streaming daemons. This option determines where these images will be stored. The images will actually be stored in sub directories beneath this location and will be automatically cleaned up after a period of time.", type => $types{abs_path}, category => "paths", }, { name => "ZM_WEB_TITLE_PREFIX", default => "ZM", description => "The title prefix displayed on each window", help => "If you have more than one installation of ZoneMinder it can be helpful to display different titles for each one. Changing this option allows you to customise the window titles to include further information to aid identification.", type => $types{string}, category => "web", }, { name => "ZM_WEB_RESIZE_CONSOLE", default => "yes", description => "Should the console window resize itself to fit", help => "Traditionally the main ZoneMinder web console window has resized itself to shrink to a size small enough to list only the monitors that are actually present. This is intended to make the window more unobtrusize but may not be to everyones tastes, especially if opened in a tab in browsers which support this kind if layout. Switch this option off to have the console window size left to the users preference", type => $types{boolean}, category => "web", }, { name => "ZM_WEB_POPUP_ON_ALARM", default => "yes", description => "Should the monitor window jump to the top if an alarm occurs", help => "When viewing a live monitor stream you can specify whether you want the window to pop to the front if an alarm occurs when the window is minimised or behind another window. This is most useful if your monitors are over doors for example when they can pop up if someone comes to the doorway.", type => $types{boolean}, category => "web", }, { name => "ZM_OPT_X10", default => "no", description => "Support interfacing with X10 devices", help => "If you have an X10 Home Automation setup in your home you can use ZoneMinder to initiate or react to X10 signals if your computer has the appropriate interface controller. This option indicates whether X10 options will be available in the browser client.", type => $types{boolean}, category => "x10", }, { name => "ZM_X10_DEVICE", default => "/dev/ttyS0", description => "What device is your X10 controller connected on", requires => [ { name => "ZM_OPT_X10", value => "yes" } ], help => "If you have an X10 controller device (e.g. XM10U) connected to your computer this option details which port it is conected on, the default of /dev/ttyS0 maps to serial or com port 1.", type => $types{abs_path}, category => "x10", }, { name => "ZM_X10_HOUSE_CODE", default => "A", description => "What X10 house code should be used", requires => [ { name => "ZM_OPT_X10", value => "yes" } ], help => "X10 devices are grouped together by identifying them as all belonging to one House Code. This option details what that is. It should be a single letter between A and P.", type => { db_type=>"string", hint=>"A-P", pattern=>qr|^([A-P])|i, format=>q( uc($1) ) }, category => "x10", }, { name => "ZM_X10_DB_RELOAD_INTERVAL", default => "60", description => "How often (in seconds) the X10 daemon reloads the monitors from the database", requires => [ { name => "ZM_OPT_X10", value => "yes" } ], help => "The zmx10 daemon periodically checks the database to find out what X10 events trigger, or result from, alarms. This option determines how frequently this check occurs, unless you change this area frequently this can be a fairly large value.", type => $types{integer}, category => "x10", }, { name => "ZM_WEB_SOUND_ON_ALARM", default => "no", description => "Should the monitor window play a sound if an alarm occurs", help => "When viewing a live monitor stream you can specify whether you want the window to play a sound to alert you if an alarm occurs.", type => $types{boolean}, category => "web", }, { name => "ZM_WEB_ALARM_SOUND", default => "", description => "The sound to play on alarm, put this in the sounds directory", help => "You can specify a sound file to play if an alarm occurs whilst you are watching a live monitor stream. So long as your browser understands the format it does not need to be any particular type. This file should be placed in the sounds directory defined earlier.", type => $types{file}, requires => [ { name => "ZM_WEB_SOUND_ON_ALARM", value => "yes" } ], category => "web", }, { name => "ZM_WEB_COMPACT_MONTAGE", default => "no", description => "Compact the montage view by removing extra detail", help => "The montage view shows the output of all of your active monitors in one window. This include a small menu and status information for each one. This can increase the web traffic and make the window larger than may be desired. Setting this option on removes all this extraneous information and just displays the images.", type => $types{boolean}, category => "web", }, { name => "ZM_OPT_FAST_DELETE", default => "yes", description => "Delete only event database records for speed", help => "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. It is recommended that you set this option which means that the browser client only deletes the key entries in the events table, which means the events will no longer appear in the listing, and leaves the zmaudit daemon to clear up the rest later.", type => $types{boolean}, category => "system", }, { name => "ZM_STRICT_VIDEO_CONFIG", default => "yes", description => "Allow errors in setting video config to be fatal", help => "With some video devices errors can be reported in setting the various video attributes when in fact the operation was successful. Switching this option off will still allow these errors to be reported but will not cause them to kill the video capture daemon. Note however that doing this will cause all errors to be ignored including those which are genuine and which may cause the video capture to not function correctly. Use this option with caution.", type => $types{boolean}, category => "config", }, { name => "ZM_SIGNAL_CHECK_POINTS", default => "10", description => "How many points in a captured image to check for signal loss", help => "For locally attached video cameras ZoneMinder can check for signal loss by looking at a number of random points on each captured image. If all of these points are set to the same fixed colour then the camera is assumed to have lost signal. When this happens any open events are closed and a short one frame signal loss event is generated, as is another when the signal returns. This option defines how many points on each image to check. Note that this is a maximum, any points found to not have the check colour will abort any further checks so in most cases on a couple of points will actually be checked. Network and file based cameras are never checked.", type => $types{integer}, category => "config", }, { name => "ZM_V4L_MULTI_BUFFER", default => "yes", description => "Use more than one buffer for Video 4 Linux devices", help => "Performance when using Video 4 Linux devices is usually best if multiple buffers are used allowing the next image to be captured while the previous one is being processed. If you have multiple devices on a card sharing one input that requires switching then this approach can sometimes cause frames from one source to be mixed up with frames from another. Switching this option off prevents multi buffering resulting in slower but more stable image capture. This option is ignored for non-local cameras or if only one input is present on a capture chip. This option addresses a similar problem to the ZM_CAPTURES_PER_FRAME option and you should normally change the value of only one of the options at a time.", type => $types{boolean}, category => "config", }, { name => "ZM_CAPTURES_PER_FRAME", default => "1", description => "How many images are captured per returned frame, for shared local cameras", help => "If you are using cameras attached to a video capture card which forces multiple inputs to share one capture chip, it can sometimes produce images with interlaced frames reversed resulting in poor image quality and a distinctive comb edge appearance. Increasing this setting allows you to force additional image captures before one is selected as the captured frame. This allows the capture hardware to 'settle down' and produce better quality images at the price of lesser capture rates. This option has no effect on (a) network cameras, or (b) where multiple inputs do not share a capture chip. This option addresses a similar problem to the ZM_V4L_MULTI_BUFFER option and you should normally change the value of only one of the options at a time.", type => $types{integer}, category => "config", }, { name => "ZM_FILTER_RELOAD_DELAY", default => "300", description => "How often (in seconds) filters are reloaded in zmfilter", help => "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 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.", type => $types{integer}, category => "system", }, { name => "ZM_FILTER_EXECUTE_INTERVAL", default => "60", description => "How often (in seconds) to run automatic saved filters", help => "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.", type => $types{integer}, category => "system", }, { name => "ZM_OPT_UPLOAD", default => "no", description => "Should ZoneMinder support uploading events from filters", help => "In ZoneMinder you can create event filters that specify whether events that match certain criteria should be uploaded to a remote server for archiving. This option specifies whether this functionality should be available", type => $types{boolean}, category => "upload", }, { name => "ZM_UPLOAD_ARCH_FORMAT", default => "tar", description => "What format the uploaded events should be created in.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], help => "Uploaded events may be stored in either .tar or .zip format, this option specifies which. Note that to use this you will need to have the Archive::Tar and/or Archive::Zip perl modules installed.", type => { db_type=>"string", hint=>"tar|zip", pattern=>qr|^([tz])|i, format=>q( $1 =~ /^t/ ? "tar" : "zip" ) }, category => "upload", }, { name => "ZM_UPLOAD_ARCH_COMPRESS", default => "no", description => "Should archive files be compressed", help => "When the archive files are created they can be compressed. However in general since the images are compressed already this saves only a minimal amount of space versus utilising more CPU in their creation. Only enable if you have CPU to waste and are limited in disk space on your remote server or bandwidth.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{boolean}, category => "upload", }, { name => "ZM_UPLOAD_ARCH_ANALYSE", default => "no", description => "Include the analysis files in the archive", help => "When the archive files are created they can contain either just the captured frames or both the captured frames and, for frames that caused an alarm, the analysed image with the changed area highlighted. This option controls files are included. Only include analysed frames if you have a high bandwidth connection to the remote server or if you need help in figuring out what caused an alarm in the first place as archives with these files in can be considerably larger.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{boolean}, category => "upload", }, { name => "ZM_UPLOAD_PROTOCOL", default => "ftp", description => "What protocol to use to upload events", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], help => "ZoneMinder can upload events to a remote server using either FTP or SFTP. Regular FTP is widely supported but not necessarily very secure whereas SFTP (Secure FTP) runs over an ssh connection and so is encrypted and uses regular ssh ports. Note that to use this you will need to have the appropriate perl module, either Net::FTP or Net::SFTP installed depending on your choice.", type => { db_type=>"string", hint=>"ftp|sftp", pattern=>qr|^([tz])|i, format=>q( $1 =~ /^f/ ? "ftp" : "sftp" ) }, category => "upload", }, { name => "ZM_UPLOAD_FTP_HOST", default => "", description => "The remote server to upload to", help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates the name, or ip address, of the server to use.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{hostname}, category => "hidden", }, { name => "ZM_UPLOAD_HOST", default => "", description => "The remote server to upload events to", help => "You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the name, or ip address, of the server to use.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{hostname}, category => "upload", }, { name => "ZM_UPLOAD_PORT", default => "", description => "The port on the remote upload server, if not the default (SFTP only)", help => "You can use filters to instruct ZoneMinder to upload events to a remote server. If you are using the SFTP protocol then this option allows you to specify a particular port to use for connection. If this option is left blank then the default, port 22, is used. This option is ignored for FTP uploads.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{integer}, category => "upload", }, { name => "ZM_UPLOAD_FTP_USER", default => "", description => "Your ftp username", help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates the username that ZoneMinder should use to log in for ftp transfer.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{alphanum}, category => "hidden", }, { name => "ZM_UPLOAD_USER", default => "", description => "Remote server username", help => "You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the username that ZoneMinder should use to log in for transfer.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{alphanum}, category => "upload", }, { name => "ZM_UPLOAD_FTP_PASS", default => "", description => "Your ftp password", help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates the password that ZoneMinder should use to log in for ftp transfer.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{string}, category => "hidden", }, { name => "ZM_UPLOAD_PASS", default => "", description => "Remote server password", help => "You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the password that ZoneMinder should use to log in for transfer. If you are using certicate based logins for SFTP servers you can leave this option blank.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{string}, category => "upload", }, { name => "ZM_UPLOAD_FTP_LOC_DIR", default => "@ZM_TMPDIR@", description => "The local directory in which to create upload files", help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates the local directory that ZoneMinder should use for temporary upload files. These are files that are created from events, uploaded and then deleted.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{abs_path}, category => "hidden", }, { name => "ZM_UPLOAD_LOC_DIR", default => "@ZM_TMPDIR@", description => "The local directory in which to create upload files", help => "You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the local directory that ZoneMinder should use for temporary upload files. These are files that are created from events, uploaded and then deleted.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{abs_path}, category => "upload", }, { name => "ZM_UPLOAD_FTP_REM_DIR", default => "", description => "The remote directory to upload to", help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates the remote directory that ZoneMinder should use to upload event files to.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{rel_path}, category => "hidden", }, { name => "ZM_UPLOAD_REM_DIR", default => "", description => "The remote directory to upload to", help => "You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the remote directory that ZoneMinder should use to upload event files to.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{rel_path}, category => "upload", }, { name => "ZM_UPLOAD_FTP_TIMEOUT", default => "120", description => "How long to allow the transfer to take for each file", help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates the maximum ftp inactivity timeout (in seconds) that should be tolerated before ZoneMinder determines that the transfer has failed and closes down the connection.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{integer}, category => "hidden", }, { name => "ZM_UPLOAD_TIMEOUT", default => "120", description => "How long to allow the transfer to take for each file", help => "You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the maximum inactivity timeout (in seconds) that should be tolerated before ZoneMinder determines that the transfer has failed and closes down the connection.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{integer}, category => "upload", }, { name => "ZM_UPLOAD_FTP_PASSIVE", default => "yes", description => "Use passive ftp when uploading", help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates that ftp transfers should be done in passive mode. This uses a single connection for all ftp activity and, whilst slower than active transfers, is more robust and likely to work from behind filewalls. This option is ignored for SFTP transfers.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], help => "If your computer is behind a firewall or proxy you may need to set FTP to passive mode. In fact for simple transfers it makes little sense to do otherwise anyway but you can set this to 'No' if you wish.", type => $types{boolean}, category => "upload", }, { name => "ZM_UPLOAD_FTP_DEBUG", default => "no", description => "Switch ftp debugging on", help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. If you are having (or expecting) troubles with uploading events then setting this to 'yes' permits additional information to be included in the zmfilter log file.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{boolean}, category => "hidden", }, { name => "ZM_UPLOAD_DEBUG", default => "no", description => "Switch upload debugging on", help => "You can use filters to instruct ZoneMinder to upload events to a remote server. If you are having (or expecting) troubles with uploading events then setting this to 'yes' permits additional information to be generated by the underlying transfer modules and included in the logs.", requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], type => $types{boolean}, category => "upload", }, { name => "ZM_OPT_EMAIL", default => "no", description => "Should ZoneMinder email you details of events that match corresponding filters", help => "In ZoneMinder you can create event filters that specify whether events that match certain criteria should have their details emailed to you at a designated email address. This will allow you to be notified of events as soon as they occur and also to quickly view the events directly. This option specifies whether this functionality should be available. The email created with this option can be any size and is intended to be sent to a regular email reader rather than a mobile device.", type => $types{boolean}, category => "mail", }, { name => "ZM_EMAIL_ADDRESS", default => "", description => "The email address to send matching event details to", requires => [ { name => "ZM_OPT_EMAIL", value => "yes" } ], help => "This option is used to define the email address that any events that match the appropriate filters will be sent to.", type => $types{email}, category => "mail", }, { name => "ZM_EMAIL_TEXT", default => 'subject = "ZoneMinder: Alarm - %MN%-%EI% (%ESM% - %ESA% %EFA%)" body = " Hello, An alarm has been detected on your installation of the ZoneMinder. The details are as follows :- Monitor : %MN% Event Id : %EI% Length : %EL% Frames : %EF% (%EFA%) Scores : t%EST% m%ESM% a%ESA% This alarm was matched by the %FN% filter and can be viewed at %EPS% ZoneMinder"', description => "The text of the email used to send matching event details", requires => [ { name => "ZM_OPT_EMAIL", value => "yes" } ], help => "This option is used to define the content of the email that is sent for any events that match the appropriate filters.", type => $types{text}, category => "hidden", }, { name => "ZM_EMAIL_SUBJECT", default => "ZoneMinder: Alarm - %MN%-%EI% (%ESM% - %ESA% %EFA%)", description => "The subject of the email used to send matching event details", requires => [ { name => "ZM_OPT_EMAIL", value => "yes" } ], help => "This option is used to define the subject of the email that is sent for any events that match the appropriate filters.", type => $types{string}, category => "mail", }, { name => "ZM_EMAIL_BODY", default => " Hello, An alarm has been detected on your installation of the ZoneMinder. The details are as follows :- Monitor : %MN% Event Id : %EI% Length : %EL% Frames : %EF% (%EFA%) Scores : t%EST% m%ESM% a%ESA% This alarm was matched by the %FN% filter and can be viewed at %EPS% ZoneMinder", description => "The body of the email used to send matching event details", requires => [ { name => "ZM_OPT_EMAIL", value => "yes" } ], help => "This option is used to define the content of the email that is sent for any events that match the appropriate filters.", type => $types{text}, category => "mail", }, { name => "ZM_OPT_MESSAGE", default => "no", description => "Should ZoneMinder message you with details of events that match corresponding filters", help => "In ZoneMinder you can create event filters that specify whether events that match certain criteria should have their details sent to you at a designated short message email address. This will allow you to be notified of events as soon as they occur. This option specifies whether this functionality should be available. The email created by this option will be brief and is intended to be sent to an SMS gateway or a minimal mail reader such as a mobile device or phone rather than a regular email reader.", type => $types{boolean}, category => "mail", }, { name => "ZM_MESSAGE_ADDRESS", default => "", description => "The email address to send matching event details to", requires => [ { name => "ZM_OPT_MESSAGE", value => "yes" } ], help => "This option is used to define the short message email address that any events that match the appropriate filters will be sent to.", type => $types{email}, category => "mail", }, { name => "ZM_MESSAGE_TEXT", default => 'subject = "ZoneMinder: Alarm - %MN%-%EI%" body = "ZM alarm detected - %EL% secs, %EF%/%EFA% frames, t%EST%/m%ESM%/a%ESA% score."', description => "The text of the message used to send matching event details", requires => [ { name => "ZM_OPT_MESSAGE", value => "yes" } ], help => "This option is used to define the content of the message that is sent for any events that match the appropriate filters.", type => $types{text}, category => "hidden", }, { name => "ZM_MESSAGE_SUBJECT", default => "ZoneMinder: Alarm - %MN%-%EI%", description => "The subject of the message used to send matching event details", requires => [ { name => "ZM_OPT_MESSAGE", value => "yes" } ], help => "This option is used to define the subject of the message that is sent for any events that match the appropriate filters.", type => $types{string}, category => "mail", }, { name => "ZM_MESSAGE_BODY", default => "ZM alarm detected - %EL% secs, %EF%/%EFA% frames, t%EST%/m%ESM%/a%ESA% score.", description => "The body of the message used to send matching event details", requires => [ { name => "ZM_OPT_MESSAGE", value => "yes" } ], help => "This option is used to define the content of the message that is sent for any events that match the appropriate filters.", type => $types{text}, category => "mail", }, { name => "ZM_NEW_MAIL_MODULES", default => "no", description => "Use a newer perl method to send emails", requires => [ { name => "ZM_OPT_EMAIL", value => "yes" }, { name => "ZM_OPT_MESSAGE", value => "yes" } ], help => "Traditionally ZoneMinder has used the MIME::Entity perl module to construct and send notification emails and messages. Some people have reported problems with this module not being present at all or flexible enough for their needs. If you are one of those people this option allows you to select a new mailing method using MIME::Lite and Net::SMTP instead. This method was contributed by Ross Melin and should work for everyone but has not been extensively tested so currently is not selected by default.", type => $types{boolean}, category => "mail", }, { name => "ZM_EMAIL_HOST", default => "localhost", description => "The host address of your SMTP mail server", requires => [ { name => "ZM_OPT_EMAIL", value => "yes" }, { name => "ZM_OPT_MESSAGE", value => "yes" } ], help => "If you have chosen SMTP as the method by which to send notification emails or messages then this option allows you to choose which SMTP server to use to send them. The default of localhost may work if you have the sendmail, exim or a similar daemon running however you may wish to enter your ISP's SMTP mail server here.", type => $types{hostname}, category => "mail", }, { name => "ZM_FROM_EMAIL", default => "", description => "The email address you wish your event notifications to originate from", requires => [ { name => "ZM_OPT_EMAIL", value => "yes" }, { name => "ZM_OPT_MESSAGE", value => "yes" } ], help => "The emails or messages that will be sent to you informing you of events can appear to come from a designated email address to help you with mail filtering etc. An address of something like ZoneMinder\@your.domain is recommended.", type => $types{email}, category => "mail", }, { name => "ZM_URL", default => "", description => "The URL of your ZoneMinder installation", requires => [ { name => "ZM_OPT_EMAIL", value => "yes" }, { name => "ZM_OPT_MESSAGE", value => "yes" } ], help => "The emails or messages that will be sent to you informing you of events can include a link to the events themselves for easy viewing. If you intend to use this feature then set this option to the url of your installation as it would appear from where you read your email, e.g. http://host.your.domain/zm.php.", type => $types{url}, category => "mail", }, { name => "ZM_MAX_RESTART_DELAY", default => "600", description => "Maximum delay (in seconds) for daemon restart attempts.", help => "The zmdc (zm daemon control) process controls when processeses are started or stopped and will attempt to restart any that fail. If a daemon fails frequently then a delay is introduced between each restart attempt. If the daemon stills fails then this delay is increased to prevent extra load being placed on the system by continual restarts. This option controls what this maximum delay is.", type => $types{integer}, category => "system", }, { name => "ZM_WATCH_CHECK_INTERVAL", default => "10", description => "How often to check the capture daemons have not locked up", help => "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 indefinately). This option determines how often the daemons are checked.", type => $types{integer}, category => "system", }, { name => "ZM_WATCH_MAX_DELAY", default => "5", description => "The maximum delay allowed since the last captured image", help => "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 indefinately). 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.", type => $types{decimal}, category => "system", }, { name => "ZM_RUN_AUDIT", default => "yes", description => "Run zmaudit to check data consistency", help => "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. This is recommended 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 not have zmaudit running unconditionally and schedule occasional checks at other, more convenient, times.", type => $types{boolean}, category => "system", }, { name => "ZM_AUDIT_CHECK_INTERVAL", default => "900", description => "How often to check database and filesystem consistency", help => "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.", type => $types{integer}, category => "system", }, { name => "ZM_FORCED_ALARM_SCORE", default => "255", description => "Score to give forced alarms", help => "The 'zmu' utility can be used to force an alarm on a monitor rather than rely on the motion detection algorithms. This option determines what score to give these alarms to distinguish them from regular ones. It must be 255 or less.", type => $types{integer}, category => "config", }, { name => "ZM_BULK_FRAME_INTERVAL", default => "100", description => "How often a bulk frame should be written to the database", help => "Traditionally ZoneMinder writes an entry into the Frames database table for each frame that is captured and saved. This works well in motion detection scenarios but when in a DVR situation ('Record' or 'Mocord' mode) this results in a huge number of frame writes and a lot of database and disk bandwidth for very little additional information. Setting this to a non-zero value will enabled ZoneMinder to group these non-alarm frames into one 'bulk' frame entry which saves a lot of bandwidth and space. The only disadvantage of this is that timing information for individual frames is lost but in constant frame rate situations this is usually not significant. This setting is ignored in Modect mode and individual frames are still written if an alarm occurs in Mocord mode also.", type => $types{integer}, category => "config", }, { name => "ZM_EVENT_CLOSE_MODE", default => "idle", description => "When continuous events are closed.", help => "When a monitor is running in a continuous recording mode (Record or Mocord) events are usually closed after a fixed period of time (the section length). However in Mocord mode it is possible that motion detection may occur near the end of a section. This option controls what happens when an alarm occurs in Mocord mode. The 'time' setting means that the event will be closed at the end of the section regardless of alarm activity. The 'idle' setting means that the event will be closed at the end of the section if there is no alarm activity occuring at the time otherwise it will be closed once the alarm is over meaning the event may end up being longer than the normal section length. The 'alarm' setting means that if an alarm occurs during the event, the event will be closed once the alarm is over regardless of when this occurs. This has the effect of limiting the number of alarms to one per event and the events will be shorter than the section length if an alarm has occurred.", type => $types{boolean}, type => { db_type=>"string", hint=>"time|idle|alarm", pattern=>qr|^([tia])|i, format=>q( ($1 =~ /^t/) ? "time" : ($1 =~ /^i/ ? "idle" : "time" ) ) }, category => "config", }, # Deprecated, superseded by event close mode { name => "ZM_FORCE_CLOSE_EVENTS", default => "no", description => "Close events at section ends.", help => "When a monitor is running in a continuous recording mode (Record or Mocord) events are usually closed after a fixed period of time (the section length). However in Mocord mode it is possible that motion detection may occur near the end of a section and ordinarily this will prevent the event being closed until the motion has ceased. Switching this option on will force the event closed at the specified time regardless of any motion activity.", type => $types{boolean}, category => "hidden", }, { name => "ZM_CREATE_ANALYSIS_IMAGES", default => "yes", description => "Create analysed alarm images with motion outlined", help => "By default during an alarm ZoneMinder records both the raw captured image and one that has been analysed and had areas where motion was detected outlined. This can be very useful during zone configuration or in analysing why events occured. However it also incurs some overhead and in a stable system may no longer be necessary. This parameter allows you to switch the generation of these images off.", type => $types{boolean}, category => "config", }, { name => "ZM_WEIGHTED_ALARM_CENTRES", default => "no", description => "Use a weighted algorithm to calculate the centre of an alarm", help => "ZoneMinder will always calculate the centre point of an alarm in a zone to give some indication of where on the screen it is. This can be used by the experimental motion tracking feature or your own custom extensions. In the alarmed or filtered pixels mode this is a simple midpoint between the extents of the detected pxiesl. However in the blob method this can instead be calculated using weighted pixel locations to give more accurate positioning for irregularly shaped blobs. This method, while more precise is also slower and so is turned off by default.", type => $types{boolean}, category => "config", }, { name => "ZM_EVENT_IMAGE_DIGITS", default => "3", description => "How many significant digits are used in event image numbering", help => "As event images are captured they are stored to the filesystem with a numerical index. By default this index has three digits so the numbers start 001, 002 etc. This works works for most scenarios as events with more than 999 frames are rarely captured. However if you have extremely long events and use external applications then you may wish to increase this to ensure correct sorting of images in listings etc. Warning, increasing this value on a live system may render existing events unviewable as the event will have been saved with the previous scheme. Decreasing this value should have no ill effects.", type => $types{integer}, category => "config", }, { name => "ZM_DEFAULT_ASPECT_RATIO", default => "4:3", description => "The default width:height aspect ratio used in monitors", help => "When specifying the dimensions of monitors you can click a checkbox to ensure that the width stays in the correct ratio to the height, or vice versa. This setting allows you to indicate what the ratio of these settings should be. This should be specified in the format : and the default of 4:3 normally be acceptable but 11:9 is another common setting. If the checkbox is not clicked when specifying monitor dimensions this setting has no effect.", type => $types{string}, category => "config", }, { name => "ZM_USER_SELF_EDIT", default => "no", description => "Allow unprivileged users to change their details", help => "Ordinarily only users with system edit privilege are able to change users details. Switching this option on allows ordinary users to change their passwords and their language settings", type => $types{boolean}, category => "config", }, { name => "ZM_OPT_FRAME_SERVER", default => "no", description => "Should analysis farm out the writing of images to disk", #requires => [ { name => "ZM_OPT_ADAPTIVE_SKIP", value => "yes" } ], help => "In some circumstances it is possible for a slow disk to take so long writing images to disk that it causes the analysis daemon to fall behind especially during high frame rate events. Setting this option to yes enables a frame server daemon (zmf) which will be sent the images from the analysis daemon and will do the actual writing of images itself freeing up the analysis daemon to get on with other things. Should this transmission fail or other permanent or transient error occur, this function will fall back to the analysis daemon.", type => $types{boolean}, category => "system", }, { name => "ZM_FRAME_SOCKET_SIZE", default => "0", description => "Specify the frame server socket buffer size if non-standard", requires => [ { name => "ZM_OPT_FRAME_SERVER", value => "yes" } ], help => "For large captured images it is possible for the writes from the analysis daemon to the frame server to fail as the amount to be written exceeds the default buffer size. While the images are then written by the analysis daemon so no data is lost, it defeats the object of the frame server daemon in the first place. You can use this option to indicate that a larger buffer size should be used. Note that you may have to change the existing maximum socket buffer size on your system via sysctl (or in /proc/sys/net/core/wmem_max) to allow this new size to be set. Alternatively you can change the default buffer size on your system in the same way in which case that will be used with no change necessary in this option", type => $types{integer}, category => "system", }, { name => "ZM_OPT_CONTROL", default => "no", description => "Support controllable (e.g. PTZ) cameras", help => "ZoneMinder includes limited support for controllable cameras. A number of sample protocols are included and others can easily be added. If you wish to control your cameras via ZoneMinder then select this option otherwise if you only have static cameras or use other control methods then leave this option off.", type => $types{boolean}, category => "system", }, { name => "ZM_OPT_TRIGGERS", default => "no", description => "Interface external event triggers via socket or device files", help => "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.", type => $types{boolean}, category => "system", }, { name => "ZM_CHECK_FOR_UPDATES", default => "yes", description => "Check with zoneminder.com for updated versions", help => "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", type => $types{boolean}, category => "system", }, { name => "ZM_UPDATE_CHECK_PROXY", default => "", description => "Proxy url if required to access zoneminder.com", help => "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://:/", type => $types{string}, category => "system", }, { name => "ZM_SHM_KEY", default => "0x7a6d0000", description => "Shared memory root key to use", help => "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.", type => $types{hexadecimal}, category => "system", }, # Deprecated, really no longer necessary { name => "ZM_WEB_REFRESH_METHOD", default => "javascript", description => "What method windows should use to refresh themselves", help => "Many windows in Javascript need to refresh themselves to keep their information current. This option determines what method they should use to do this. Choosing 'javascript' means that each window will have a short JavaScript statement in with a timer to prompt the refresh. This is the most compatible method. Choosing 'http' means the refresh instruction is put in the HTTP header. This is a cleaner method but refreshes are interrupted or cancelled when a link in the window is clicked meaning that the window will no longer refresh and this would have to be done manually.", type => { db_type=>"string", hint=>"javascript|http", pattern=>qr|^([jh])|i, format=>q( $1 =~ /^j/ ? "javascript" : "http" ) }, category => "hidden", }, { name => "ZM_WEB_EVENT_SORT_FIELD", default => "DateTime", description => "Default field the event lists are sorted by", help => "Events in lists can be initially ordered in any way you want. This option controls what field is used to sort them. You can modify this ordering from filters or by clicking on headings in the lists themselves. Bear in mind however that the 'Prev' and 'Next' links, when scrolling through events, relate to the ordering in the lists and so not always to time based ordering.", type => { db_type=>"string", hint=>"Id|Name|Cause|MonitorName|DateTime|Length|Frames|AlarmFrames|TotScore|AvgScore|MaxScore", pattern=>qr|.|, format=>q( $1 ) }, category => "web", }, { name => "ZM_WEB_EVENT_SORT_ORDER", default => "asc", description => "Default order the event lists are sorted by", help => "Events in lists can be initially ordered in any way you want. This option controls what order (ascending or descending) is used to sort them. You can modify this ordering from filters or by clicking on headings in the lists themselves. Bear in mind however that the 'Prev' and 'Next' links, when scrolling through events, relate to the ordering in the lists and so not always to time based ordering.", type => { db_type=>"string", hint=>"asc|desc", pattern=>qr|^([ad])|i, format=>q( $1 =~ /^a/i ? "asc" : "desc" ) }, category => "web", }, { name => "ZM_WEB_EVENTS_PER_PAGE", default => "25", description => "How many events to list per page in paged mode", help => "In the event list view you can either list all events or just a page at a time. This option controls how many events are listed per page in paged mode and how often to repeat the column headers in non-paged mode.", type => $types{integer}, category => "web", }, { name => "ZM_WEB_LIST_THUMBS", default => "no", description => "Display mini-thumbnails of event images in event lists", help => "Ordinarily the event lists just display text details of the events to save space and time. By switching this option on you can also display small thumbnails to help you identify events of interest. The size of these thumbnails is controlled by the following two options.", type => $types{boolean}, category => "web", }, { name => "ZM_WEB_LIST_THUMB_WIDTH", default => "48", description => "The width of the thumbnails that appear in the event lists", help => "This options controls the width of the thumbnail images that appear in the event lists. It should be fairly small to fit in with the rest of the table. If you prefer you can specify a height instead in the next option but you should only use one of the width or height and the other option should be set to zero. If both width and height are specified then width will be used and height ignored.", type => $types{integer}, requires => [ { name => "ZM_WEB_LIST_THUMBS", value => "yes" } ], category => "web", }, { name => "ZM_WEB_LIST_THUMB_HEIGHT", default => "0", description => "The height of the thumbnails that appear in the event lists", help => "This options controls the height of the thumbnail images that appear in the event lists. It should be fairly small to fit in with the rest of the table. If you prefer you can specify a width instead in the previous option but you should only use one of the width or height and the other option should be set to zero. If both width and height are specified then width will be used and height ignored.", type => $types{integer}, requires => [ { name => "ZM_WEB_LIST_THUMBS", value => "yes" } ], category => "web", }, { name => "ZM_WEB_USE_OBJECT_TAGS", default => "yes", description => "Wrap embed in object tags for media content", help => "There are two methods of including media content in web pages. The most common way is use the EMBED tag which is able to give some indication of the type of content. However this is not a standard part of HTML. The official method is to use OBJECT tags which are able to give more information allowing the correct media viewers etc to be loaded. However these are less widely supported and content may be specifically tailored to a particular platform or player. This option controls whether media content is enclosed in EMBED tags only or whether, where appropriate, it is additionally wrapped in OBJECT tags. Currently OBJECT tags are only used in a limited number of circumstances but they may become more widespread in the future. It is suggested that you leave this option on unless you encounter problems playing some content.", type => $types{boolean}, category => "web", }, { name => "ZM_WEB_H_REFRESH_MAIN", default => "300", introduction => "There are now a number of options that are grouped into bandwidth categories, this allows you to configure the ZoneMinder client to work optimally over the various access methods you might to access the client.\n\nThe next few options control what happens when the client is running in 'high' bandwidth mode. You should set these options for when accessing the ZoneMinder client over a local network or high speed link. In most cases the default values will be suitable as a starting point.", description => "How often (in seconds) the main console window should refresh itself", help => "The main console window lists a general status and the event totals for all monitors. This is not a trivial task and should not be repeated too frequently or it may affect the performance of the rest of the system.", type => $types{integer}, category => "highband", }, { name => "ZM_WEB_H_REFRESH_CYCLE", default => "10", description => "How often (in seconds) the cycle watch window swaps to the next monitor", help => "The cycle watch window is a method of continuously cycling between images from all of your monitors. This option determines how often to refresh with a new image.", type => $types{integer}, category => "highband", }, { name => "ZM_WEB_H_REFRESH_IMAGE", default => "5", description => "How often (in seconds) the watched image is refreshed (if not streaming)", help => "The live images from a monitor can be viewed in either streamed or stills mode. This option determines how often a stills image is refreshed, it has no effect if streaming is selected.", type => $types{integer}, category => "highband", }, { name => "ZM_WEB_H_REFRESH_STATUS", default => "3", description => "How often (in seconds) the status refreshes itself in the watch window", help => "The monitor window is actually made from several frames. The one in the middle merely contains a monitor status which needs to refresh fairly frequently to give a true indication. This option determines that frequency.", type => $types{integer}, category => "highband", }, { name => "ZM_WEB_H_REFRESH_EVENTS", default => "30", description => "How often (in seconds) the event listing is refreshed in the watch window", help => "The monitor window is actually made from several frames. The lower framme contains a listing of the last few events for easy access. This option determines how often this is refreshed.", type => $types{integer}, category => "highband", }, { name => "ZM_WEB_H_CAN_STREAM", default => "auto", description => "Override the automatic detection of browser streaming capability", help => "If you know that your browser can handle image streams of the type 'multipart/x-mixed-replace' but ZoneMinder does not detect this correctly you can set this option to ensure that the stream is delivered with or without the use of the Cambozola plugin. Selecting 'yes' will tell ZoneMinder that your browser can handle the streams natively, 'no' means that it can't and so the plugin will be used while 'auto' lets ZoneMinder decide.", type => $types{tristate}, category => "highband", }, { name => "ZM_WEB_H_STREAM_METHOD", default => "jpeg", description => "Which method should be used to send video streams to your browser.", help => "ZoneMinder can be configured to use either mpeg encoded video or a series or still jpeg images when sending video streams. This option defines which is used. If you choose mpeg you should ensure that you have the appropriate plugins available on your browser whereas choosing jpeg will work natively on Mozilla and related browsers and with a Java applet on Internet Explorer", type => { db_type=>"string", hint=>"mpeg|jpeg", pattern=>qr|^([mj])|i, format=>q( $1 =~ /^m/ ? "mpeg" : "jpeg" ) }, category => "highband", }, { name => "ZM_WEB_H_DEFAULT_SCALE", default => "100", description => "What the default scaling factor applied to 'live' or 'event' views is (%)", help => "Normally ZoneMinder will display 'live' or 'event' streams in their native size. However if you have monitors with large dimensions or a slow link you may prefer to reduce this size, alternatively for small monitors you can enlarge it. This options lets you specify what the default scaling factor will be. It is expressed as a percentage so 100 is normal size, 200 is double size etc.", type => { db_type=>"integer", hint=>"25|33|50|75|100|150|200|300|400", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, category => "highband", }, { name => "ZM_WEB_H_DEFAULT_RATE", default => "100", description => "What the default replay rate factor applied to 'event' views is (%)", help => "Normally ZoneMinder will display 'event' streams at their native rate, i.e. as close to real-time as possible. However if you have long events it is often convenient to replay them at a faster rate for review. This option lets you specify what the default replay rate will be. It is expressed as a percentage so 100 is normal rate, 200 is double speed etc.", type => { db_type=>"integer", hint=>"25|50|100|150|200|400|1000|2500|5000|10000", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, category => "highband", }, { name => "ZM_WEB_H_VIDEO_BITRATE", default => "150000", description => "What the bitrate of the video encoded stream should be set to", help => "When encoding real video via the ffmpeg library a bit rate can be specified which roughly corresponds to the available bandwidth used for the stream. This setting effectively corresponds to a 'quality' setting for the video. A low value will result in a blocky image whereas a high value will produce a clearer view. Note that this setting does not control the frame rate of the video however the quality of the video produced is affected both by this setting and the frame rate that the video is produced at. A higher frame rate at a particular bit rate result in individual frames being at a lower quality.", type => $types{integer}, category => "highband", }, { name => "ZM_WEB_H_VIDEO_MAXFPS", default => "15", description => "What the maximum frame rate for streamed video should be", help => "When using streamed video the main control is the bitrate which determines how much data can be transmitted. However a lower bitrate at high frame rates results in a lower quality image. This option allows you to limit the maximum frame rate to ensure that video quality is maintained. An additional advantage is that encoding video at high frame rates is a processor intensive task when for the most part a very high frame rate offers little perceptible improvement over one that has a more manageable resource requirement. Note, this option is implemented as a cap beyond which binary reduction takes place. So if you have a device capturing at 15fps and set this option to 10fps then the video is not produced at 10fps, but rather at 7.5fps (15 divided by 2) as the final frame rate must be the original divided by a power of 2.", type => $types{integer}, category => "highband", }, { name => "ZM_WEB_H_SCALE_THUMBS", default => "no", description => "Scale thumbnails in events, bandwidth versus cpu in rescaling", help => "If unset, this option sends the whole image to the browser which resizes it in the window. If set the image is scaled down on the server before sending a reduced size image to the browser to conserve bandwidth at the cost of cpu on the server. Note that ZM can only perform the resizing if the appropriate PHP graphics functionality is installed. This is usually available in the php-gd package.", type => $types{boolean}, category => "highband", }, { name => "ZM_WEB_H_EVENTS_VIEW", default => "events", description => "What the default view of multiple events should be.", help => "Stored events can be viewed in either an events list format or in a timeline based one. This option sets the default view that will be used. Choosing one view here does not prevent the other view being used as it will always be selectable from whichever view is currently being used.", type => { db_type=>"string", hint=>"events|timeline", pattern=>qr|^([lt])|i, format=>q( $1 =~ /^e/ ? "events" : "timeline" ) }, category => "highband", }, { name => "ZM_WEB_H_SHOW_PROGRESS", default => "yes", description => "Show the progress of replay in event view.", help => "When viewing events an event navigation panel and progress bar is shown below the event itself. This allows you to jump to specific points in the event, but can can also dynamically update to display the current progress of the event replay itself. This progress is calculated from the actual event duration and is not directly linked to the replay itself, so on limited bandwidth connections may be out of step with the replay. This option allows you to turn off the progress display, whilst still keeping the navigation aspect, where bandwidth prevents it functioning effectively.", type => $types{boolean}, category => "highband", }, { name => "ZM_WEB_H_AJAX_TIMEOUT", default => "3000", description => "How long to wait for Ajax request responses (ms)", help => "The newer versions of the live feed and event views use Ajax to request information from the server and populate the views dynamically. This option allows you to specify a timeout if required after which requests are abandoned. A timeout may be necessary if requests would overwise hang such as on a slow connection. This would tend to consume a lot of browser memory and make the interface unresponsive. Ordinarily no requests should timeout so this setting should be set to a value greater than the slowest expected response. This value is in milliseconds but if set to zero then no timeout will be used.", type => $types{integer}, category => "highband", }, { name => "ZM_WEB_M_REFRESH_MAIN", default => "300", description => "How often (in seconds) the main console window should refresh itself", help => "The main console window lists a general status and the event totals for all monitors. This is not a trivial task and should not be repeated too frequently or it may affect the performance of the rest of the system.", type => $types{integer}, introduction => "The next few options control what happens when the client is running in 'medium' bandwidth mode. You should set these options for when accessing the ZoneMinder client over a slower cable or DSL link. In most cases the default values will be suitable as a starting point.", category => "medband", }, { name => "ZM_WEB_M_REFRESH_CYCLE", default => "20", description => "How often (in seconds) the cycle watch window swaps to the next monitor", help => "The cycle watch window is a method of continuously cycling between images from all of your monitors. This option determines how often to refresh with a new image.", type => $types{integer}, category => "medband", }, { name => "ZM_WEB_M_REFRESH_IMAGE", default => "10", description => "How often (in seconds) the watched image is refreshed (if not streaming)", help => "The live images from a monitor can be viewed in either streamed or stills mode. This option determines how often a stills image is refreshed, it has no effect if streaming is selected.", type => $types{integer}, category => "medband", }, { name => "ZM_WEB_M_REFRESH_STATUS", default => "5", description => "How often (in seconds) the status refreshes itself in the watch window", help => "The monitor window is actually made from several frames. The one in the middle merely contains a monitor status which needs to refresh fairly frequently to give a true indication. This option determines that frequency.", type => $types{integer}, category => "medband", }, { name => "ZM_WEB_M_REFRESH_EVENTS", default => "60", description => "How often (in seconds) the event listing is refreshed in the watch window", help => "The monitor window is actually made from several frames. The lower framme contains a listing of the last few events for easy access. This option determines how often this is refreshed.", type => $types{integer}, category => "medband", }, { name => "ZM_WEB_M_CAN_STREAM", default => "auto", description => "Override the automatic detection of browser streaming capability", help => "If you know that your browser can handle image streams of the type 'multipart/x-mixed-replace' but ZoneMinder does not detect this correctly you can set this option to ensure that the stream is delivered with or without the use of the Cambozola plugin. Selecting 'yes' will tell ZoneMinder that your browser can handle the streams natively, 'no' means that it can't and so the plugin will be used while 'auto' lets ZoneMinder decide.", type => $types{tristate}, category => "medband", }, { name => "ZM_WEB_M_STREAM_METHOD", default => "jpeg", description => "Which method should be used to send video streams to your browser.", help => "ZoneMinder can be configured to use either mpeg encoded video or a series or still jpeg images when sending video streams. This option defines which is used. If you choose mpeg you should ensure that you have the appropriate plugins available on your browser whereas choosing jpeg will work natively on Mozilla and related browsers and with a Java applet on Internet Explorer", type => { db_type=>"string", hint=>"mpeg|jpeg", pattern=>qr|^([mj])|i, format=>q( $1 =~ /^m/ ? "mpeg" : "jpeg" ) }, category => "medband", }, { name => "ZM_WEB_M_DEFAULT_SCALE", default => "100", description => "What the default scaling factor applied to 'live' or 'event' views is (%)", help => "Normally ZoneMinder will display 'live' or 'event' streams in their native size. However if you have monitors with large dimensions or a slow link you may prefer to reduce this size, alternatively for small monitors you can enlarge it. This options lets you specify what the default scaling factor will be. It is expressed as a percentage so 100 is normal size, 200 is double size etc.", type => { db_type=>"integer", hint=>"25|33|50|75|100|150|200|300|400", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, category => "medband", }, { name => "ZM_WEB_M_DEFAULT_RATE", default => "100", description => "What the default replay rate factor applied to 'event' views is (%)", help => "Normally ZoneMinder will display 'event' streams at their native rate, i.e. as close to real-time as possible. However if you have long events it is often convenient to replay them at a faster rate for review. This option lets you specify what the default replay rate will be. It is expressed as a percentage so 100 is normal rate, 200 is double speed etc.", type => { db_type=>"integer", hint=>"25|50|100|150|200|400|1000|2500|5000|10000", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, category => "medband", }, { name => "ZM_WEB_M_VIDEO_BITRATE", default => "75000", description => "What the bitrate of the video encoded stream should be set to", help => "When encoding real video via the ffmpeg library a bit rate can be specified which roughly corresponds to the available bandwidth used for the stream. This setting effectively corresponds to a 'quality' setting for the video. A low value will result in a blocky image whereas a high value will produce a clearer view. Note that this setting does not control the frame rate of the video however the quality of the video produced is affected both by this setting and the frame rate that the video is produced at. A higher frame rate at a particular bit rate result in individual frames being at a lower quality.", type => $types{integer}, category => "medband", }, { name => "ZM_WEB_M_VIDEO_MAXFPS", default => "10", description => "What the maximum frame rate for streamed video should be", help => "When using streamed video the main control is the bitrate which determines how much data can be transmitted. However a lower bitrate at high frame rates results in a lower quality image. This option allows you to limit the maximum frame rate to ensure that video quality is maintained. An additional advantage is that encoding video at high frame rates is a processor intensive task when for the most part a very high frame rate offers little perceptible improvement over one that has a more manageable resource requirement. Note, this option is implemented as a cap beyond which binary reduction takes place. So if you have a device capturing at 15fps and set this option to 10fps then the video is not produced at 10fps, but rather at 7.5fps (15 divided by 2) as the final frame rate must be the original divided by a power of 2.", type => $types{integer}, category => "medband", }, { name => "ZM_WEB_M_SCALE_THUMBS", default => "yes", description => "Scale thumbnails in events, bandwidth versus cpu in rescaling", help => "If unset, this option sends the whole image to the browser which resizes it in the window. If set the image is scaled down on the server before sending a reduced size image to the browser to conserve bandwidth at the cost of cpu on the server. Note that ZM can only perform the resizing if the appropriate PHP graphics functionality is installed. This is usually available in the php-gd package.", type => $types{boolean}, category => "medband", }, { name => "ZM_WEB_M_EVENTS_VIEW", default => "events", description => "What the default view of multiple events should be.", help => "Stored events can be viewed in either an events list format or in a timeline based one. This option sets the default view that will be used. Choosing one view here does not prevent the other view being used as it will always be selectable from whichever view is currently being used.", type => { db_type=>"string", hint=>"events|timeline", pattern=>qr|^([lt])|i, format=>q( $1 =~ /^e/ ? "events" : "timeline" ) }, category => "medband", }, { name => "ZM_WEB_M_SHOW_PROGRESS", default => "yes", description => "Show the progress of replay in event view.", help => "When viewing events an event navigation panel and progress bar is shown below the event itself. This allows you to jump to specific points in the event, but can can also dynamically update to display the current progress of the event replay itself. This progress is calculated from the actual event duration and is not directly linked to the replay itself, so on limited bandwidth connections may be out of step with the replay. This option allows you to turn off the progress display, whilst still keeping the navigation aspect, where bandwidth prevents it functioning effectively.", type => $types{boolean}, category => "medband", }, { name => "ZM_WEB_M_AJAX_TIMEOUT", default => "5000", description => "How long to wait for Ajax request responses (ms)", help => "The newer versions of the live feed and event views use Ajax to request information from the server and populate the views dynamically. This option allows you to specify a timeout if required after which requests are abandoned. A timeout may be necessary if requests would overwise hang such as on a slow connection. This would tend to consume a lot of browser memory and make the interface unresponsive. Ordinarily no requests should timeout so this setting should be set to a value greater than the slowest expected response. This value is in milliseconds but if set to zero then no timeout will be used.", type => $types{integer}, category => "medband", }, { name => "ZM_WEB_L_REFRESH_MAIN", default => "300", description => "How often (in seconds) the main console window should refresh itself", introduction => "The next few options control what happens when the client is running in 'low' bandwidth mode. You should set these options for when accessing the ZoneMinder client over a modem or slow link. In most cases the default values will be suitable as a starting point.", help => "The main console window lists a general status and the event totals for all monitors. This is not a trivial task and should not be repeated too frequently or it may affect the performance of the rest of the system.", type => $types{integer}, category => "lowband", }, { name => "ZM_WEB_L_REFRESH_CYCLE", default => "30", description => "How often (in seconds) the cycle watch window swaps to the next monitor", help => "The cycle watch window is a method of continuously cycling between images from all of your monitors. This option determines how often to refresh with a new image.", type => $types{integer}, category => "lowband", }, { name => "ZM_WEB_L_REFRESH_IMAGE", default => "15", description => "How often (in seconds) the watched image is refreshed (if not streaming)", help => "The live images from a monitor can be viewed in either streamed or stills mode. This option determines how often a stills image is refreshed, it has no effect if streaming is selected.", type => $types{integer}, category => "lowband", }, { name => "ZM_WEB_L_REFRESH_STATUS", default => "10", description => "How often (in seconds) the status refreshes itself in the watch window", help => "The monitor window is actually made from several frames. The one in the middle merely contains a monitor status which needs to refresh fairly frequently to give a true indication. This option determines that frequency.", type => $types{integer}, category => "lowband", }, { name => "ZM_WEB_L_REFRESH_EVENTS", default => "180", description => "How often (in seconds) the event listing is refreshed in the watch window", help => "The monitor window is actually made from several frames. The lower framme contains a listing of the last few events for easy access. This option determines how often this is refreshed.", type => $types{integer}, category => "lowband", }, { name => "ZM_WEB_L_CAN_STREAM", default => "auto", description => "Override the automatic detection of browser streaming capability", help => "If you know that your browser can handle image streams of the type 'multipart/x-mixed-replace' but ZoneMinder does not detect this correctly you can set this option to ensure that the stream is delivered with or without the use of the Cambozola plugin. Selecting 'yes' will tell ZoneMinder that your browser can handle the streams natively, 'no' means that it can't and so the plugin will be used while 'auto' lets ZoneMinder decide.", type => $types{tristate}, category => "lowband", }, { name => "ZM_WEB_L_STREAM_METHOD", default => "jpeg", description => "Which method should be used to send video streams to your browser.", help => "ZoneMinder can be configured to use either mpeg encoded video or a series or still jpeg images when sending video streams. This option defines which is used. If you choose mpeg you should ensure that you have the appropriate plugins available on your browser whereas choosing jpeg will work natively on Mozilla and related browsers and with a Java applet on Internet Explorer", type => { db_type=>"string", hint=>"mpeg|jpeg", pattern=>qr|^([mj])|i, format=>q( $1 =~ /^m/ ? "mpeg" : "jpeg" ) }, category => "lowband", }, { name => "ZM_WEB_L_DEFAULT_SCALE", default => "100", description => "What the default scaling factor applied to 'live' or 'event' views is (%)", help => "Normally ZoneMinder will display 'live' or 'event' streams in their native size. However if you have monitors with large dimensions or a slow link you may prefer to reduce this size, alternatively for small monitors you can enlarge it. This options lets you specify what the default scaling factor will be. It is expressed as a percentage so 100 is normal size, 200 is double size etc.", type => { db_type=>"integer", hint=>"25|33|50|75|100|150|200|300|400", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, category => "lowband", }, { name => "ZM_WEB_L_DEFAULT_RATE", default => "100", description => "What the default replay rate factor applied to 'event' views is (%)", help => "Normally ZoneMinder will display 'event' streams at their native rate, i.e. as close to real-time as possible. However if you have long events it is often convenient to replay them at a faster rate for review. This option lets you specify what the default replay rate will be. It is expressed as a percentage so 100 is normal rate, 200 is double speed etc.", type => { db_type=>"integer", hint=>"25|50|100|150|200|400|1000|2500|5000|10000", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, category => "lowband", }, { name => "ZM_WEB_L_VIDEO_BITRATE", default => "25000", description => "What the bitrate of the video encoded stream should be set to", help => "When encoding real video via the ffmpeg library a bit rate can be specified which roughly corresponds to the available bandwidth used for the stream. This setting effectively corresponds to a 'quality' setting for the video. A low value will result in a blocky image whereas a high value will produce a clearer view. Note that this setting does not control the frame rate of the video however the quality of the video produced is affected both by this setting and the frame rate that the video is produced at. A higher frame rate at a particular bit rate result in individual frames being at a lower quality.", type => $types{integer}, category => "lowband", }, { name => "ZM_WEB_L_VIDEO_MAXFPS", default => "5", description => "What the maximum frame rate for streamed video should be", help => "When using streamed video the main control is the bitrate which determines how much data can be transmitted. However a lower bitrate at high frame rates results in a lower quality image. This option allows you to limit the maximum frame rate to ensure that video quality is maintained. An additional advantage is that encoding video at high frame rates is a processor intensive task when for the most part a very high frame rate offers little perceptible improvement over one that has a more manageable resource requirement. Note, this option is implemented as a cap beyond which binary reduction takes place. So if you have a device capturing at 15fps and set this option to 10fps then the video is not produced at 10fps, but rather at 7.5fps (15 divided by 2) as the final frame rate must be the original divided by a power of 2.", type => $types{integer}, category => "lowband", }, { name => "ZM_WEB_L_SCALE_THUMBS", default => "yes", description => "Scale thumbnails in events, bandwidth versus cpu in rescaling", help => "If unset, this option sends the whole image to the browser which resizes it in the window. If set the image is scaled down on the server before sending a reduced size image to the browser to conserve bandwidth at the cost of cpu on the server. Note that ZM can only perform the resizing if the appropriate PHP graphics functionality is installed. This is usually available in the php-gd package.", type => $types{boolean}, category => "lowband", }, { name => "ZM_WEB_L_EVENTS_VIEW", default => "events", description => "What the default view of multiple events should be.", help => "Stored events can be viewed in either an events list format or in a timeline based one. This option sets the default view that will be used. Choosing one view here does not prevent the other view being used as it will always be selectable from whichever view is currently being used.", type => { db_type=>"string", hint=>"events|timeline", pattern=>qr|^([lt])|i, format=>q( $1 =~ /^e/ ? "events" : "timeline" ) }, category => "lowband", }, { name => "ZM_WEB_L_SHOW_PROGRESS", default => "no", description => "Show the progress of replay in event view.", help => "When viewing events an event navigation panel and progress bar is shown below the event itself. This allows you to jump to specific points in the event, but can can also dynamically update to display the current progress of the event replay itself. This progress is calculated from the actual event duration and is not directly linked to the replay itself, so on limited bandwidth connections may be out of step with the replay. This option allows you to turn off the progress display, whilst still keeping the navigation aspect, where bandwidth prevents it functioning effectively.", type => $types{boolean}, category => "lowband", }, { name => "ZM_WEB_L_AJAX_TIMEOUT", default => "10000", description => "How long to wait for Ajax request responses (ms)", help => "The newer versions of the live feed and event views use Ajax to request information from the server and populate the views dynamically. This option allows you to specify a timeout if required after which requests are abandoned. A timeout may be necessary if requests would overwise hang such as on a slow connection. This would tend to consume a lot of browser memory and make the interface unresponsive. Ordinarily no requests should timeout so this setting should be set to a value greater than the slowest expected response. This value is in milliseconds but if set to zero then no timeout will be used.", type => $types{integer}, category => "lowband", }, { name => "ZM_WEB_P_CAN_STREAM", default => "auto", description => "Override the automatic detection of browser streaming capability", help => "If you know that your browser can handle image streams of the type 'multipart/x-mixed-replace' but ZoneMinder does not detect this correctly you can set this option to ensure that the stream is delivered with or without the use of the Cambozola plugin. Selecting 'yes' will tell ZoneMinder that your browser can handle the streams natively, 'no' means that it can't and so the plugin will be used while 'auto' lets ZoneMinder decide.", type => $types{tristate}, category => "phoneband", }, { name => "ZM_WEB_P_STREAM_METHOD", default => "jpeg", description => "Which method should be used to send video streams to your browser.", help => "ZoneMinder can be configured to use either mpeg encoded video or a series or still jpeg images when sending video streams. This option defines which is used. If you choose mpeg you should ensure that you have the appropriate plugins available on your browser whereas choosing jpeg will work natively on Mozilla and related browsers and with a Java applet on Internet Explorer", type => { db_type=>"string", hint=>"mpeg|jpeg", pattern=>qr|^([mj])|i, format=>q( $1 =~ /^m/ ? "mpeg" : "jpeg" ) }, category => "phoneband", }, { name => "ZM_WEB_P_DEFAULT_SCALE", default => "100", description => "What the default scaling factor applied to 'live' or 'event' views is (%)", help => "Normally ZoneMinder will display 'live' or 'event' streams in their native size. However if you have monitors with large dimensions or a slow link you may prefer to reduce this size, alternatively for small monitors you can enlarge it. This options lets you specify what the default scaling factor will be. It is expressed as a percentage so 100 is normal size, 200 is double size etc.", type => { db_type=>"integer", hint=>"25|33|50|75|100|150|200|300|400", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, category => "phoneband", }, { name => "ZM_WEB_P_DEFAULT_RATE", default => "100", description => "What the default replay rate factor applied to 'event' views is (%)", help => "Normally ZoneMinder will display 'event' streams at their native rate, i.e. as close to real-time as possible. However if you have long events it is often convenient to replay them at a faster rate for review. This option lets you specify what the default replay rate will be. It is expressed as a percentage so 100 is normal rate, 200 is double speed etc.", type => { db_type=>"integer", hint=>"25|50|100|150|200|400|1000|2500|5000|10000", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, category => "phoneband", }, { name => "ZM_WEB_P_VIDEO_BITRATE", default => "8000", description => "What the bitrate of the video encoded stream should be set to", help => "When encoding real video via the ffmpeg library a bit rate can be specified which roughly corresponds to the available bandwidth used for the stream. This setting effectively corresponds to a 'quality' setting for the video. A low value will result in a blocky image whereas a high value will produce a clearer view. Note that this setting does not control the frame rate of the video however the quality of the video produced is affected both by this setting and the frame rate that the video is produced at. A higher frame rate at a particular bit rate result in individual frames being at a lower quality.", type => $types{integer}, category => "phoneband", }, { name => "ZM_WEB_P_VIDEO_MAXFPS", default => "5", description => "What the maximum frame rate for streamed video should be", help => "When using streamed video the main control is the bitrate which determines how much data can be transmitted. However a lower bitrate at high frame rates results in a lower quality image. This option allows you to limit the maximum frame rate to ensure that video quality is maintained. An additional advantage is that encoding video at high frame rates is a processor intensive task when for the most part a very high frame rate offers little perceptible improvement over one that has a more manageable resource requirement. Note, this option is implemented as a cap beyond which binary reduction takes place. So if you have a device capturing at 15fps and set this option to 10fps then the video is not produced at 10fps, but rather at 7.5fps (15 divided by 2) as the final frame rate must be the original divided by a power of 2.", type => $types{integer}, category => "phoneband", }, { name => "ZM_WEB_P_SCALE_THUMBS", default => "yes", description => "Scale thumbnails in events, bandwidth versus cpu in rescaling", help => "If unset, this option sends the whole image to the browser which resizes it in the window. If set the image is scaled down on the server before sending a reduced size image to the browser to conserve bandwidth at the cost of cpu on the server. Note that ZM can only perform the resizing if the appropriate PHP graphics functionality is installed. This is usually available in the php-gd package.", type => $types{boolean}, category => "phoneband", }, { name => "ZM_WEB_P_AJAX_TIMEOUT", default => "10000", description => "How long to wait for Ajax request responses (ms)", help => "The newer versions of the live feed and event views use Ajax to request information from the server and populate the views dynamically. This option allows you to specify a timeout if required after which requests are abandoned. A timeout may be necessary if requests would overwise hang such as on a slow connection. This would tend to consume a lot of browser memory and make the interface unresponsive. Ordinarily no requests should timeout so this setting should be set to a value greater than the slowest expected response. This value is in milliseconds but if set to zero then no timeout will be used.", type => $types{integer}, category => "phoneband", }, { name => "ZM_DYN_LAST_VERSION", default => "", description => "What the last version of ZoneMinder recorded from zoneminder.com is", help => "", type => $types{string}, readonly => 1, category => "dynamic", }, { name => "ZM_DYN_CURR_VERSION", default => "@VERSION@", description => "What the effective current version of ZoneMinder is, might be different from actual if versions ignored", help => "", type => $types{string}, readonly => 1, category => "dynamic", }, { name => "ZM_DYN_DB_VERSION", default => "@VERSION@", description => "What the version of the database is, from zmupdate", help => "", type => $types{string}, readonly => 1, category => "dynamic", }, { name => "ZM_DYN_LAST_CHECK", default => "", description => "When the last check for version from zoneminder.com was", help => "", type => $types{integer}, readonly => 1, category => "dynamic", }, { name => "ZM_DYN_NEXT_REMINDER", default => "", description => "When the earliest time to remind about versions will be", help => "", type => $types{string}, readonly => 1, category => "dynamic", }, { name => "ZM_DYN_DONATE_REMINDER_TIME", default => 0, description => "When the earliest time to remind about donations will be", help => "", type => $types{integer}, readonly => 1, category => "dynamic", }, { name => "ZM_DYN_SHOW_DONATE_REMINDER", default => "yes", description => "Remind about donations or not", help => "", type => $types{boolean}, readonly => 1, category => "dynamic", }, { name => "ZM_EYEZM_DEBUG", default => "no", description => "Switch additional debugging on for eyeZm Plugin", help => "Enable or Disable extra debugging from the eyeZm Plugin. Extra debugging information will be displayed in it's own file (EYEZM_LOG_TO_FILE is set), or your Apache error log", type => $types{boolean}, category => "eyeZm", }, { name => "ZM_EYEZM_LOG_TO_FILE", default => "yes", description => "When eyeZm Debugging is enabled, enabling this logs output to it's own file", help => "When EYEZM_DEBUG is on and EYEZM_LOG_TO_FILE is on, output generated from the eyeZm Plugin will go to it's own file. Otherwise it will go to the apache error log.", type => $types{boolean}, category => "eyeZm", }, { name => "ZM_EYEZM_LOG_FILE", default => "@ZM_LOGDIR@/zm_xml.log", description => "Default filename to use when logging eyeZm Output and EYEZM_LOG_TO_FILE is enabled", help => "This file will contain it's own output from the eyeZm Plugin when EYEZM_LOG_TO_FILE and EYEZM_DEBUG are both enabled", type => $types{string}, category => "eyeZm", }, { name => "ZM_EYEZM_EVENT_VCODEC", default => "mpeg4", description => "Default video-codec to use for encoding events", help => "The eyeZm Plugin calls FFMPEG externally to encode the captured images. If your FFMPEG is not built with support for H264, change this to MPEG-4. If using H264, please check http://www.eyezm.com for H264 requirements and that your eyeZm version supports H264 (v1.2+).", type => { db_type=>"string", hint=>"mpeg4|h264", pattern=>qr|^([mh])|i, format=>q( $1 =~ /^m/ ? "mpeg4" : "h264" ) }, category => "eyeZm", }, { name => "ZM_EYEZM_FEED_VCODEC", default => "mjpeg", description => "Default video-codec to use for streaming the live feed", help => "Determines whether the live stream is generated using native MJPEG streaming with ZoneMinder, or H264 using FFMPEG and HTML-5 streaming. If using H264, please check http://www.eyezm.com for H264 requirements and that your eyeZm version supports H264 (v1.2+). This is just a default parameter, and can be overridden with eyeZm.", type => { db_type=>"string", hint=>"mjpeg|h264", pattern=>qr|^([mh])|i, format=>q( $1 =~ /^m/ ? "mjpeg" : "h264" ) }, category => "eyeZm", }, { name => "ZM_EYEZM_H264_DEFAULT_BR", default => "96k", description => "Default bit-rate to use with FFMPEG for H264 streaming", help => "When using the eyeZm Plugin to stream H264 data, FFMPEG requires a bitrate to control the quality and bandwidth of the video. This should be specified in a format acceptable to FFMPEG. The default value is sufficient for most installations. This is just a default parameter, and can be overridden with eyeZm.", type => $types{string}, category => "eyeZm", }, { name => "ZM_EYEZM_H264_DEFAULT_EVBR", default => "128k", description => "Default bit-rate to use with FFMPEG for H264 event viewing", help => "When using the eyeZm Plugin to view events in H264, FFMPEG requires a bitrate to control the quality and bandwidth of the video. This should be specified in a format acceptable to FFMPEG. The default value is sufficient for most installations. This is just a default parameter, and can be overridden with eyeZm.", type => $types{string}, category => "eyeZm", }, { name => "ZM_EYEZM_H264_TIMEOUT", default => "20", description => "Timeout (sec) to wait for H264 stream to start before terminating", help => "The eyeZm Plugin will attempt to spawn an H264 stream when requested, and require that it complete within the timeout specified. If you have a slow system or find through the logs that the H264 stream is not starting because the timeout is expiring, even though FFMPEG is running, try increasing this value. If you have a fast system, decreasing this value can improve the responsiveness when there are issues starting H264 streams", type => $types{string}, category => "eyeZm", }, { name => "ZM_EYEZM_SEG_DURATION", default => "3", description => "Segment duration used for streaming using HTTP-5 Streaming protocol", help => "The HTTP-5 Live Streaming Protocol segments the input video stream into small chunks of a duration specified by this parameter. Increasing the segment duration will help with choppy connections on the other end, but will increase the latency in starting a stream.", type => $types{string}, category => "eyeZm", }, ); our %options_hash = map { ( $_->{name}, $_ ) } @options; # This function should never need to be called explicitly, except if # this module is 'require'd rather than 'use'd. See zmconfgen.pl. sub initialiseConfig { return if ( $configInitialised ); # Do some initial data munging to finish the data structures # Create option ids my $option_id = 0; foreach my $option ( @options ) { if ( defined($option->{default}) ) { $option->{value} = $option->{default} } else { $option->{value} = ''; } #next if ( $option->{category} eq 'hidden' ); $option->{id} = $option_id++; } $configInitialised = 1; } 1; __END__ =head1 NAME ZoneMinder::ConfigData - ZoneMinder Configuration Data module =head1 SYNOPSIS use ZoneMinder::ConfigData; use ZoneMinder::ConfigData qw(:all); loadConfigFromDB(); saveConfigToDB(); =head1 DESCRIPTION The ZoneMinder:ConfigData module contains the master definition of the ZoneMinder configuration options as well as helper methods. This module is intended for specialist confguration management and would not normally be used by end users. The configuration held in this module, which was previously in zmconfig.pl, includes the name, default value, description, help text, type and category for each option, as well as a number of additional fields in a small number of cases. =head1 METHODS =over 4 =item loadConfigFromDB (); Loads existing configuration from the database (if any) and merges it with the definitions held in this module. This results in the merging of any new configuration and the removal of any deprecated configuration while preserving the existing values of every else. =item saveConfigToDB (); Saves configuration held in memory to the database. The act of loading and saving configuration is a convenient way to ensure that the configuration held in the database corresponds with the most recent definitions and that all components are using the same set of configuration. =head2 EXPORT None by default. The :data tag will export the various configuration data structures The :functions tag will export the helper functions. The :all tag will export all above symbols. =head1 SEE ALSO http://www.zoneminder.com =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Control.pm000066400000000000000000000110551225361755400242320ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Base Control Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the base class definitions for the camera control # protocol implementations # package ZoneMinder::Control; use 5.006; use strict; use warnings; require ZoneMinder::Base; our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Base connection class # # ========================================================================== use ZoneMinder::Logger qw(:all); use ZoneMinder::Database qw(:all); our $AUTOLOAD; sub new { my $class = shift; my $id = shift; my $self = {}; $self->{name} = "PelcoD"; if ( !defined($id) ) { Fatal( "No monitor defined when invoking protocol ".$self->{name} ); } $self->{id} = $id; bless( $self, $class ); return $self; } sub DESTROY { } sub AUTOLOAD { my $self = shift; my $class = ref($self) || croak( "$self not object" ); my $name = $AUTOLOAD; $name =~ s/.*://; if ( exists($self->{$name}) ) { return( $self->{$name} ); } croak( "Can't access $name member of object of class $class" ); } sub getKey() { my $self = shift; return( $self->{id} ); } sub open { my $self = shift; Fatal( "No open method defined for protocol ".$self->{name} ); } sub close { my $self = shift; Fatal( "No close method defined for protocol ".$self->{name} ); } sub loadMonitor { my $self = shift; if ( !$self->{Monitor} ) { if ( !($self->{Monitor} = zmDbGetMonitor( $self->{id} )) ) { Fatal( "Monitor id ".$self->{id}." not found or not controllable" ); } if ( defined($self->{Monitor}->{AutoStopTimeout}) ) { # Convert to microseconds. $self->{Monitor}->{AutoStopTimeout} = int(1000000*$self->{Monitor}->{AutoStopTimeout}); } } } sub getParam { my $self = shift; my $params = shift; my $name = shift; my $default = shift; if ( defined($params->{$name}) ) { return( $params->{$name} ); } elsif ( defined($default) ) { return( $default ); } Fatal( "Missing mandatory parameter '$name'" ); } sub executeCommand { my $self = shift; my $params = shift; $self->loadMonitor(); my $command = $params->{command}; delete $params->{command}; #if ( !defined($self->{$command}) ) #{ #Fatal( "Unsupported command '$command'" ); #} &{$self->{$command}}( $self, $params ); } sub printMsg() { my $self = shift; Fatal( "No printMsg method defined for protocol ".$self->{name} ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Control/000077500000000000000000000000001225361755400236725ustar00rootroot00000000000000ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Control/AxisV2.pm000066400000000000000000000252701225361755400253520ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Axis version 2 API Control Protocol Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the implementation of the Axis V2 API camera control # protocol # package ZoneMinder::Control::AxisV2; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Control; our @ISA = qw(ZoneMinder::Control); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Axis V2 Control Protocol # # ========================================================================== use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); sub new { my $class = shift; my $id = shift; my $self = ZoneMinder::Control->new( $id ); bless( $self, $class ); srand( time() ); return $self; } our $AUTOLOAD; sub AUTOLOAD { my $self = shift; my $class = ref($self) || croak( "$self not object" ); my $name = $AUTOLOAD; $name =~ s/.*://; if ( exists($self->{$name}) ) { return( $self->{$name} ); } Fatal( "Can't access $name member of object of class $class" ); } sub open { my $self = shift; $self->loadMonitor(); use LWP::UserAgent; $self->{ua} = LWP::UserAgent->new; $self->{ua}->agent( "ZoneMinder Control Agent/".ZM_VERSION ); $self->{state} = 'open'; } sub close { my $self = shift; $self->{state} = 'closed'; } sub printMsg { my $self = shift; my $msg = shift; my $msg_len = length($msg); Debug( $msg."[".$msg_len."]" ); } sub sendCmd { my $self = shift; my $cmd = shift; my $result = undef; printMsg( $cmd, "Tx" ); #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 ); } sub cameraReset { my $self = shift; Debug( "Camera Reset" ); my $cmd = "/axis-cgi/admin/restart.cgi"; $self->sendCmd( $cmd ); } sub moveConUp { my $self = shift; Debug( "Move Up" ); my $cmd = "/axis-cgi/com/ptz.cgi?move=up"; $self->sendCmd( $cmd ); } sub moveConDown { my $self = shift; Debug( "Move Down" ); my $cmd = "/axis-cgi/com/ptz.cgi?move=down"; $self->sendCmd( $cmd ); } sub moveConLeft { my $self = shift; Debug( "Move Left" ); my $cmd = "/axis-cgi/com/ptz.cgi?move=left"; $self->sendCmd( $cmd ); } sub moveConRight { my $self = shift; Debug( "Move Right" ); my $cmd = "/axis-cgi/com/ptz.cgi?move=right"; $self->sendCmd( $cmd ); } sub moveConUpRight { my $self = shift; Debug( "Move Up/Right" ); my $cmd = "/axis-cgi/com/ptz.cgi?move=upright"; $self->sendCmd( $cmd ); } sub moveConUpLeft { my $self = shift; Debug( "Move Up/Left" ); my $cmd = "/axis-cgi/com/ptz.cgi?move=upleft"; $self->sendCmd( $cmd ); } sub moveConDownRight { my $self = shift; Debug( "Move Down/Right" ); my $cmd = "/axis-cgi/com/ptz.cgi?move=downright"; $self->sendCmd( $cmd ); } sub moveConDownLeft { my $self = shift; Debug( "Move Down/Left" ); my $cmd = "/axis-cgi/com/ptz.cgi?move=downleft"; $self->sendCmd( $cmd ); } sub moveMap { my $self = shift; my $params = shift; my $xcoord = $self->getParam( $params, 'xcoord' ); my $ycoord = $self->getParam( $params, '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}; $self->sendCmd( $cmd ); } sub moveRelUp { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'tiltstep' ); Debug( "Step Up $step" ); my $cmd = "/axis-cgi/com/ptz.cgi?rtilt=$step"; $self->sendCmd( $cmd ); } sub moveRelDown { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'tiltstep' ); Debug( "Step Down $step" ); my $cmd = "/axis-cgi/com/ptz.cgi?rtilt=-$step"; $self->sendCmd( $cmd ); } sub moveRelLeft { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'panstep' ); Debug( "Step Left $step" ); my $cmd = "/axis-cgi/com/ptz.cgi?rpan=-$step"; $self->sendCmd( $cmd ); } sub moveRelRight { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'panstep' ); Debug( "Step Right $step" ); my $cmd = "/axis-cgi/com/ptz.cgi?rpan=$step"; $self->sendCmd( $cmd ); } sub moveRelUpRight { my $self = shift; my $params = shift; my $panstep = $self->getParam( $params, 'panstep' ); my $tiltstep = $self->getParam( $params, 'tiltstep' ); Debug( "Step Up/Right $tiltstep/$panstep" ); my $cmd = "/axis-cgi/com/ptz.cgi?rpan=$panstep&rtilt=$tiltstep"; $self->sendCmd( $cmd ); } sub moveRelUpLeft { my $self = shift; my $params = shift; my $panstep = $self->getParam( $params, 'panstep' ); my $tiltstep = $self->getParam( $params, 'tiltstep' ); Debug( "Step Up/Left $tiltstep/$panstep" ); my $cmd = "/axis-cgi/com/ptz.cgi?rpan=-$panstep&rtilt=$tiltstep"; $self->sendCmd( $cmd ); } sub moveRelDownRight { my $self = shift; my $params = shift; my $panstep = $self->getParam( $params, 'panstep' ); my $tiltstep = $self->getParam( $params, 'tiltstep' ); Debug( "Step Down/Right $tiltstep/$panstep" ); my $cmd = "/axis-cgi/com/ptz.cgi?rpan=$panstep&rtilt=-$tiltstep"; $self->sendCmd( $cmd ); } sub moveRelDownLeft { my $self = shift; my $params = shift; my $panstep = $self->getParam( $params, 'panstep' ); my $tiltstep = $self->getParam( $params, 'tiltstep' ); Debug( "Step Down/Left $tiltstep/$panstep" ); my $cmd = "/axis-cgi/com/ptz.cgi?rpan=-$panstep&rtilt=-$tiltstep"; $self->sendCmd( $cmd ); } sub zoomRelTele { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'step' ); Debug( "Zoom Tele" ); my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=$step"; $self->sendCmd( $cmd ); } sub zoomRelWide { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'step' ); Debug( "Zoom Wide" ); my $cmd = "/axis-cgi/com/ptz.cgi?rzoom=-$step"; $self->sendCmd( $cmd ); } sub focusRelNear { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'step' ); Debug( "Focus Near" ); my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=-$step"; $self->sendCmd( $cmd ); } sub focusRelFar { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'step' ); Debug( "Focus Far" ); my $cmd = "/axis-cgi/com/ptz.cgi?rfocus=$step"; $self->sendCmd( $cmd ); } sub focusAuto { my $self = shift; Debug( "Focus Auto" ); my $cmd = "/axis-cgi/com/ptz.cgi?autofocus=on"; $self->sendCmd( $cmd ); } sub focusMan { my $self = shift; Debug( "Focus Manual" ); my $cmd = "/axis-cgi/com/ptz.cgi?autofocus=off"; $self->sendCmd( $cmd ); } sub irisRelOpen { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'step' ); Debug( "Iris Open" ); my $cmd = "/axis-cgi/com/ptz.cgi?riris=$step"; $self->sendCmd( $cmd ); } sub irisRelClose { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'step' ); Debug( "Iris Close" ); my $cmd = "/axis-cgi/com/ptz.cgi?riris=-$step"; $self->sendCmd( $cmd ); } sub irisAuto { my $self = shift; Debug( "Iris Auto" ); my $cmd = "/axis-cgi/com/ptz.cgi?autoiris=on"; $self->sendCmd( $cmd ); } sub irisMan { my $self = shift; Debug( "Iris Manual" ); my $cmd = "/axis-cgi/com/ptz.cgi?autoiris=off"; $self->sendCmd( $cmd ); } sub presetClear { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); Debug( "Clear Preset $preset" ); my $cmd = "/axis-cgi/com/ptz.cgi?removeserverpresetno=$preset"; $self->sendCmd( $cmd ); } sub presetSet { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); Debug( "Set Preset $preset" ); my $cmd = "/axis-cgi/com/ptz.cgi?setserverpresetno=$preset"; $self->sendCmd( $cmd ); } sub presetGoto { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); Debug( "Goto Preset $preset" ); my $cmd = "/axis-cgi/com/ptz.cgi?gotoserverpresetno=$preset"; $self->sendCmd( $cmd ); } sub presetHome { my $self = shift; Debug( "Home Preset" ); my $cmd = "/axis-cgi/com/ptz.cgi?move=home"; $self->sendCmd( $cmd ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8608W_Y2k.pm000066400000000000000000000556151225361755400257440ustar00rootroot00000000000000# V1.0 ==================================================================================== # # ZoneMinder FOSCAM version 1.0 API Control Protocol Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # V1.0 ==================================================================================== # # This module FI8608W_Y2k.pm contains the implementation of API camera control # For FOSCAM FI8608W PT Camera (This cam support only H264 streaming) # V1.0 Le 13 AOUT 2013 # If you wan't to contact me i understand French and English, precise ZoneMinder in subject # but i prefer via the ZoneMinder Forum # My name is Christophe DAPREMONT my email is christophe_y2k@yahoo.fr # # V1.0 ==================================================================================== package ZoneMinder::Control::FI8608W_Y2k; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Control; our @ISA = qw(ZoneMinder::Control); our $VERSION = $ZoneMinder::Base::VERSION; # =================================================================================================================================== # # FI8608W FOSCAM PT H264 Control Protocol # with Firmware version V3.2.1.1.1-20120815 (latest at 13/08/2013) # based with the latest doc from FOSCAM ( http://foscam.us/forum/cgi-api-sdk-for-mjpeg-h-264-camera-t2986.html ) # This IPCAM work under ZoneMinder V1.25 from alternative source of code # from this svn at https://svn.unixmedia.net/public/zum/trunk/zum/ # Many Thanks to "MASTERTHEKNIFE" for the excellent speed optimisation ( http://www.zoneminder.com/forums/viewtopic.php?f=9&t=17652 ) # And to "NEXTIME" for the recent source update and incredible plugins ( http://www.zoneminder.com/forums/viewtopic.php?f=9&t=20587 ) # And all people helping ZoneMinder dev. # # -FUNCTIONALITIES: # -Move camera in 8 direction with arrow, the speed of movement is function # of the position of your mouse on the arrow. # Extremity of arrow equal to fastest speed of movement # Close the base of arrow to lowest speed of movement # for diagonaly you can click before the begining of the arrow for low speed # In round center equal to stop to move and switch of latest OSD # -You can clic directly on the image that equal to click on arrow (for the left there is a bug in zoneminder speed is inverted) # -Zoom Tele switch ON InfraRed LED and stay to manual IR MODE # -Zoom Wide switch OFF InfraRed LED and stay to manual IR MODE # -Button WAKE switch to AUTO ON/OFF IR LED # -Button RESET to setup image at initial value # -8 Preset PTZ are implemented and functionnal # -This Script use for login "admin" this hardcoded and your password must setup in "Control Device" section # -This script is compatible with the basic authentification method used by mostly new camera based with hi3510 chipset # -AutoStop function is active and you must set up value (in sec exemple 0.7) under AutoStop section # or you can set up to 0 for disable it (in this case you need to click to the circle center for stop) # -"White In" to control Brightness, "auto" for restore the original value of Brightness # -"White Out" to control Contrast, "man" for restore the original value of Contrast # -"Iris Open" to control Saturation , "auto" for restore the original value of Saturation # -"Iris Close" to control Hue , "man" for restore the original value of Hue # -I use the OSD function of this cam for printing the command with the value # =================================================================================================================================== use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); # Set $osd to "off" if you wan't disabled OSD i need to place this variable in another script because # this script is reload at every command ,if i want the button on/off (Focus MAN) for OSD works... my $osd = "on"; sub new { my $class = shift; my $id = shift; my $self = ZoneMinder::Control->new( $id ); bless( $self, $class ); srand( time() ); return $self; } our $AUTOLOAD; sub AUTOLOAD { my $self = shift; my $class = ref($self) || croak( "$self not object" ); my $name = $AUTOLOAD; $name =~ s/.*://; if ( exists($self->{$name}) ) { return( $self->{$name} ); } Fatal( "Can't access $name member of object of class $class" ); } sub open { my $self = shift; $self->loadMonitor(); use LWP::UserAgent; $self->{ua} = LWP::UserAgent->new; $self->{ua}->agent( "ZoneMinder Control Agent/".ZM_VERSION ); $self->{state} = 'open'; } sub close { my $self = shift; $self->{state} = 'closed'; } sub printMsg { my $self = shift; my $msg = shift; my $msg_len = length($msg); Debug( $msg."[".$msg_len."]" ); } sub sendCmd { my $self = shift; my $cmd = shift; my $result = undef; printMsg( $cmd, "Tx" ); # I solve the authentification problem with recent Foscam # I use perl Basic Authentification method my $ua = LWP::UserAgent->new(); my $req = HTTP::Request->new( GET =>"http://".$self->{Monitor}->{ControlAddress}."/web/cgi-bin/hi3510/".$cmd ); $req->authorization_basic('admin', $self->{Monitor}->{ControlDevice} ); my $res = $self->{ua}->request($req); if ( $res->is_success ) { $result = !undef; } else { Error( "Error check failed: '".$res->status_line()."'" ); } return( $result ); } sub reset { my $self = shift; Debug( "Reset=Setup FI8608W" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Color RESET"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-brightness=0"; $self->sendCmd( $cmd ); my $cmd = "param.cgi?cmd=setimageattr&-contrast=37"; $self->sendCmd( $cmd ); my $cmd = "param.cgi?cmd=setimageattr&-hue=255"; $self->sendCmd( $cmd ); my $cmd = "param.cgi?cmd=setimageattr&-saturation=94"; $self->sendCmd( $cmd ); my $cmd = "param.cgi?cmd=setinfra&-status=auto"; $self->sendCmd( $cmd ); my $cmd = "param.cgi?cmd=setimageattr&-scene=auto"; $self->sendCmd( $cmd ); } sub moveStop { my $self = shift; Debug( "Move Stop" ); my $cmd = "ptzctrl.cgi?-step=0&-act=stop"; $self->sendCmd( $cmd ); my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=0&-name=."; $self->sendCmd( $cmd ); } sub autoStop { my $self = shift; my $autostop = shift; if( $autostop ) { Debug( "Auto Stop" ); usleep( $autostop ); my $cmd = "ptzctrl.cgi?-step=0&-act=stop"; $self->sendCmd( $cmd ); my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=0&-name=."; $self->sendCmd( $cmd ); } } sub moveConUp { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $tiltspeed > 100 ) { $tiltspeed = 128; } if ( $tiltspeed < 10 ) { $tiltspeed = 1; } Debug( "Move Up" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Move Up $tiltspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=up"; $self->sendCmd( $cmd ); $self->autoStop( int(10000*$tiltspeed) ); } sub moveConDown { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $tiltspeed > 100 ) { $tiltspeed = 128; } if ( $tiltspeed < 10 ) { $tiltspeed = 1; } Debug( "Move Down" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Move Down $tiltspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=down"; $self->sendCmd( $cmd ); $self->autoStop( int(10000*$tiltspeed) ); } sub moveConLeft { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $panspeed > 100 ) { $panspeed = 128; } if ( $panspeed < 10 ) { $panspeed = 1; } # Algorithme pour inverser la table de valeur de la flèche gauche, (for invert the value) 63 ==> 1 et 1 ==> 63 ... $panspeed = abs($panspeed - 128) + 1; Debug( "Move Left" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Move Left $panspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=left"; $self->sendCmd( $cmd ); $self->autoStop( int(10000*$panspeed) ); } sub moveConRight { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $panspeed > 100 ) { $panspeed = 128; } if ( $panspeed < 10 ) { $panspeed = 1; } Debug( "Move Right" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Move Right $panspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=right"; $self->sendCmd( $cmd ); $self->autoStop( int(10000*$panspeed) ); } sub moveConUpLeft { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $tiltspeed > 100 ) { $tiltspeed = 128; } if ( $tiltspeed < 10 ) { $tiltspeed = 1; } my $panspeed = $self->getParam( $params, 'panspeed' ); # Standardization for incorrect value in the database if ( $panspeed > 100 ) { $panspeed = 128; } if ( $panspeed < 10 ) { $panspeed = 1; } Debug( "Move Con Up Left" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Up $tiltspeed Left $panspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=up"; $self->sendCmd( $cmd ); $self->autoStop( int(10000*$tiltspeed) ); my $cmd = "ptzctrl.cgi?-step=0&-act=left"; $self->sendCmd( $cmd ); $self->autoStop( int(10000*$panspeed) ); } sub moveConUpRight { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $tiltspeed > 100 ) { $tiltspeed = 128; } if ( $tiltspeed < 10 ) { $tiltspeed = 1; } my $panspeed = $self->getParam( $params, 'panspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $panspeed > 100 ) { $panspeed = 128; } if ( $panspeed < 10 ) { $panspeed = 1; } Debug( "Move Con Up Right" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Up $tiltspeed Right $panspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=up"; $self->sendCmd( $cmd ); $self->autoStop( int(10000*$tiltspeed) ); my $cmd = "ptzctrl.cgi?-step=0&-act=right"; $self->sendCmd( $cmd ); $self->autoStop( int(10000*$panspeed) ); } sub moveConDownLeft { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $tiltspeed > 100 ) { $tiltspeed = 128; } if ( $tiltspeed < 10 ) { $tiltspeed = 1; } my $panspeed = $self->getParam( $params, 'panspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $panspeed > 100 ) { $panspeed = 128; } if ( $panspeed < 10 ) { $panspeed = 1; } Debug( "Move Con Down Left" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Down $tiltspeed Left $panspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=down"; $self->sendCmd( $cmd ); $self->autoStop( int(10000*$tiltspeed) ); my $cmd = "ptzctrl.cgi?-step=0&-act=left"; $self->sendCmd( $cmd ); $self->autoStop( int(10000*$panspeed) ); } sub moveConDownRight { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $tiltspeed > 100 ) { $tiltspeed = 128; } if ( $tiltspeed < 10 ) { $tiltspeed = 1; } my $panspeed = $self->getParam( $params, 'panspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $panspeed > 100 ) { $panspeed = 128; } if ( $panspeed < 10 ) { $panspeed = 1; } Debug( "Move Con Down Right" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Down $tiltspeed Right $panspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=down"; $self->sendCmd( $cmd ); $self->autoStop( int(10000*$tiltspeed) ); my $cmd = "ptzctrl.cgi?-step=0&-act=right"; $self->sendCmd( $cmd ); $self->autoStop( int(10000*$panspeed) ); } sub zoomConTele { my $self = shift; Debug( "Zoom-Tele=MANU IR LED ON" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Manual IR LED ON"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setinfra&-status=open"; $self->sendCmd( $cmd ); } sub zoomConWide { my $self = shift; Debug( "Zoom-Wide=MANU IR LED OFF" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Manual IR LED OFF"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setinfra&-status=close"; $self->sendCmd( $cmd ); } sub wake { my $self = shift; Debug( "Wake=AUTO IR LED" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Auto IR LED Mode"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setinfra&-status=auto"; $self->sendCmd( $cmd ); } sub whiteConIn { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $speed > 255 ) { $speed = 255; } if ( $speed < 0 ) { $speed = 0; } Debug( "White ConIn=brightness" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Brightness $speed"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-brightness=$speed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub whiteConOut { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $speed > 255 ) { $speed = 255; } if ( $speed < 0 ) { $speed = 0; } Debug( "White ConOut=Contrast" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Contrast $speed"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-contrast=$speed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub whiteAuto { my $self = shift; Debug( "White Auto=Brightness Reset" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Brightness Reset"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-brightness=0"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub whiteMan { my $self = shift; Debug( "White Manuel=Contrast Reset" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Contrast Reset"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-contrast=37"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub irisConOpen { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Standardization for incorrect value in the database if ( $speed > 255 ) { $speed = 255; } if ( $speed < 0 ) { $speed = 0; } Debug( "Iris ConOpen=Saturation" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Saturation $speed"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-saturation=$speed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub irisConClose { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Standardization for incorrect value in the database if ( $speed > 255 ) { $speed = 255; } if ( $speed < 0 ) { $speed = 0; } Debug( "Iris ConClose=Hue" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Hue $speed"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-hue=$speed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub irisAuto { my $self = shift; Debug( "Iris Auto=Saturation Reset" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Saturation Reset"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-saturation=94"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub irisMan { my $self = shift; Debug( "Iris Manuel=Hue Reset" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Hue Reset"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-hue=255"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub presetSet { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); if ( ( $preset >= 1 ) && ( $preset <= 8 ) ) { Debug( "Set Preset $preset" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=PresetSet $preset"; $self->sendCmd( $cmd ); } my $preset = $preset - 1; my $cmd = "preset.cgi?-act=set&-status=1&-number=$preset"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } } sub presetGoto { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); if ( ( $preset >= 1 ) && ( $preset <= 8 ) ) { Debug( "Goto Preset $preset" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=PresetGoto $preset"; $self->sendCmd( $cmd ); } my $preset = $preset - 1; my $cmd = "preset.cgi?-act=goto&-number=$preset"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 FI-8608W ZoneMinder::Database - Perl extension for FOSCAM FI-8608W by Christophe_Y2k =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8620_Y2k.pm000066400000000000000000000643241225361755400256040ustar00rootroot00000000000000# V1.1 ==================================================================================== # # ZoneMinder FOSCAM version 1.0 API Control Protocol Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # V1.1 ==================================================================================== # # This module FI8620_Y2k.pm contains the implementation of API camera control # For FOSCAM FI8620 Dome PTZ Camera (This cam support only H264 streaming) # V0.1b Le 01 JUIN 2013 # V0.2b Le 11 JUILLET 2013 # V0.5b Le 24 JUILLET 2013 # V0.6b Le 01 AOUT 2013 - # V1.0 Le 04 AOUT 2013 - production usable if you do not use preset ptz # V1.1 Le 11 AOUT 2013 - put a cosmetic update source code # If you wan't to contact me i understand French and English, precise ZoneMinder in subject # My name is Christophe DAPREMONT my email is christophe_y2k@yahoo.fr # # V1.1 ==================================================================================== package ZoneMinder::Control::FI8620_Y2k; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Control; our @ISA = qw(ZoneMinder::Control); our $VERSION = $ZoneMinder::Base::VERSION; # =================================================================================================================================== # # FI8620 FOSCAM Dome PTZ H264 Control Protocol # with Firmware version V3.2.2.2.1-20120815 (latest at 04/08/2013) # based with the latest buggy CGI doc from FOSCAM ( http://foscam.us/forum/cgi-api-sdk-for-mjpeg-h-264-camera-t2986.html ) # This IPCAM work under ZoneMinder V1.25 from alternative source of code # from this svn at https://svn.unixmedia.net/public/zum/trunk/zum/ # Many Thanks to "MASTERTHEKNIFE" for the excellent speed optimisation ( http://www.zoneminder.com/forums/viewtopic.php?f=9&t=17652 ) # And to "NEXTIME" for the recent source update and incredible plugins ( http://www.zoneminder.com/forums/viewtopic.php?f=9&t=20587 ) # And all people helping ZoneMinder dev. # # -FUNCTIONALITIES: # -Move camera in 8 direction with arrow, the speed of movement is function # of the position of your mouse on the arrow. # Extremity of arrow equal to fastest speed of movement # Close the base of arrow to lowest speed of movement # for diagonaly you can click before the begining of the arrow for low speed # In round center equal to stop to move # -You can clic directly on the image that equal to click on arrow (for the left there is a bug in zoneminder speed is inverted) # -Zoom Tele/Wide with time control to simulate speed because speed value do not work (buggy firmware or not implemented on this cam) # -Focus Near/Far with time control to simulate speed because speed value do not work (buggy firmware or not implemented on this cam) # -Autofocus is automatic when you move again or can be setting by autofocus button # -8 Preset PTZ are implemented but the firmware is buggy and that do not work # You Need to configure ZoneMinder PANSPEED & TILTSEPPED & ZOOMSPEED 1 to 63 by 1 step # -This Script use for login "admin" this hardcoded and your password must setup in "Control Device" section # -This script is compatible with the basic authentification method used by mostly new camera # -AutoStop function is active and you must set up value (in sec exemple 0.5) under AutoStop section # or you can set up to 0 for disable it but the camera never stop to move and trust me, she can move all the night... # (you need to click to the center arrow for stop) # -"White In" to control Brightness, "auto" for restore the original value of Brightness # -"White Out" to control Contrast, "man" for restore the original value of Contrast # -"Iris Open" to control Saturation , "auto" for restore the original value of Saturation # -"Iris Close" to control Hue , "man" for restore the original value of Hue # -Another cool stuff i use the OSD function of this cam for printing the command with the value # The button of Focus "Man" is for enable or disable OSD but that do not work ( it's my bug... i'm very very new with perl ) # =================================================================================================================================== use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); # Set $osd to "off" if you wan't disabled OSD i need to place this variable in another script because # this script is reload at every command ,if i want the button on/off (Focus MAN) for OSD works... my $osd = "on"; sub new { my $class = shift; my $id = shift; my $self = ZoneMinder::Control->new( $id ); bless( $self, $class ); srand( time() ); return $self; } our $AUTOLOAD; sub AUTOLOAD { my $self = shift; my $class = ref($self) || croak( "$self not object" ); my $name = $AUTOLOAD; $name =~ s/.*://; if ( exists($self->{$name}) ) { return( $self->{$name} ); } Fatal( "Can't access $name member of object of class $class" ); } sub open { my $self = shift; $self->loadMonitor(); use LWP::UserAgent; $self->{ua} = LWP::UserAgent->new; $self->{ua}->agent( "ZoneMinder Control Agent/".ZM_VERSION ); $self->{state} = 'open'; } sub close { my $self = shift; $self->{state} = 'closed'; } sub printMsg { my $self = shift; my $msg = shift; my $msg_len = length($msg); Debug( $msg."[".$msg_len."]" ); } sub sendCmd { my $self = shift; my $cmd = shift; my $result = undef; printMsg( $cmd, "Tx" ); # I solve the authentification problem with recent Foscam # I use perl Basic Authentification method my $ua = LWP::UserAgent->new(); my $req = HTTP::Request->new( GET =>"http://".$self->{Monitor}->{ControlAddress}."/web/cgi-bin/hi3510/".$cmd ); $req->authorization_basic('admin', $self->{Monitor}->{ControlDevice} ); my $res = $self->{ua}->request($req); if ( $res->is_success ) { $result = !undef; } else { Error( "Error check failed: '".$res->status_line()."'" ); } return( $result ); } sub moveStop { my $self = shift; Debug( "Move Stop" ); my $cmd = "ptzctrl.cgi?-step=0&-act=stop"; $self->sendCmd( $cmd ); my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=0&-name=."; $self->sendCmd( $cmd ); } sub autoStop { my $self = shift; my $autostop = shift; if( $autostop ) { Debug( "Auto Stop" ); usleep( $autostop ); my $cmd = "ptzctrl.cgi?-step=0&-act=stop"; $self->sendCmd( $cmd ); my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=0&-name=."; $self->sendCmd( $cmd ); } } sub moveConUp { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $tiltspeed > 59 ) { $tiltspeed = 63; } if ( $tiltspeed < 6 ) { $tiltspeed = 1; } Debug( "Move Up" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Move Up $tiltspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=up&-speed=$tiltspeed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub moveConDown { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $tiltspeed > 59 ) { $tiltspeed = 63; } if ( $tiltspeed < 6 ) { $tiltspeed = 1; } Debug( "Move Down" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Move Down $tiltspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=down&-speed=$tiltspeed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub moveConLeft { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $panspeed > 59 ) { $panspeed = 63; } if ( $panspeed < 6 ) { $panspeed = 1; } # Algorithme pour inverser la table de valeur de la flèche gauche, (for invert the value) 63 ==> 1 et 1 ==> 63 ... $panspeed = abs($panspeed - 63) + 1; Debug( "Move Left" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Move Left $panspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=left&-speed=$panspeed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub moveConRight { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $panspeed > 59 ) { $panspeed = 63; } if ( $panspeed < 6 ) { $panspeed = 1; } Debug( "Move Right" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Move Right $panspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=right&-speed=$panspeed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub moveConUpLeft { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $tiltspeed > 59 ) { $tiltspeed = 63; } if ( $tiltspeed < 6 ) { $tiltspeed = 1; } my $panspeed = $self->getParam( $params, 'panspeed' ); # Standardization for incorrect value in the database if ( $panspeed > 59 ) { $panspeed = 63; } if ( $panspeed < 6 ) { $panspeed = 1; } Debug( "Move Con Up Left" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Up $tiltspeed Left $panspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=up&-speed=$tiltspeed"; $self->sendCmd( $cmd ); my $cmd = "ptzctrl.cgi?-step=0&-act=left&-speed=$panspeed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub moveConUpRight { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $tiltspeed > 59 ) { $tiltspeed = 63; } if ( $tiltspeed < 6 ) { $tiltspeed = 1; } my $panspeed = $self->getParam( $params, 'panspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $panspeed > 59 ) { $panspeed = 63; } if ( $panspeed < 6 ) { $panspeed = 1; } Debug( "Move Con Up Right" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Up $tiltspeed Right $panspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=up&-speed=$tiltspeed"; $self->sendCmd( $cmd ); my $cmd = "ptzctrl.cgi?-step=0&-act=right&-speed=$panspeed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub moveConDownLeft { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $tiltspeed > 59 ) { $tiltspeed = 63; } if ( $tiltspeed < 6 ) { $tiltspeed = 1; } my $panspeed = $self->getParam( $params, 'panspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $panspeed > 59 ) { $panspeed = 63; } if ( $panspeed < 6 ) { $panspeed = 1; } Debug( "Move Con Down Left" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Down $tiltspeed Left $panspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=down&-speed=$tiltspeed"; $self->sendCmd( $cmd ); my $cmd = "ptzctrl.cgi?-step=0&-act=left&-speed=$panspeed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub moveConDownRight { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $tiltspeed > 59 ) { $tiltspeed = 63; } if ( $tiltspeed < 6 ) { $tiltspeed = 1; } my $panspeed = $self->getParam( $params, 'panspeed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $panspeed > 59 ) { $panspeed = 63; } if ( $panspeed < 6 ) { $panspeed = 1; } Debug( "Move Con Down Right" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Down $tiltspeed Right $panspeed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=down&-speed=$tiltspeed"; $self->sendCmd( $cmd ); my $cmd = "ptzctrl.cgi?-step=0&-act=right&-speed=$panspeed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub zoomConTele { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $speed > 59 ) { $speed = 63; } if ( $speed < 6 ) { $speed = 1; } Debug( "Zoom-Tele" ); # I use OSD Function to send the speed used for determining the time before stop the order if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Zoom Tele $speed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=zoomin"; $self->sendCmd( $cmd ); # The variable speed does not work with zoom setting, so I used to set the duration of the order # the result is identical $self->autoStop( int(10000*$speed) ); } sub zoomConWide { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $speed > 59 ) { $speed = 63; } if ( $speed < 6 ) { $speed = 1; } Debug( "Zoom-Wide" ); # I use the feature OSD (OnScreenDisplay). Variable speed as a basis for calculating the duration of the zoom order if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Zoom Wide $speed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=zoomout"; $self->sendCmd( $cmd ); # The variable speed does not work with zoom setting, so I used to set the duration of the order # the result is identical $self->autoStop( int(10000*$speed) ); } sub focusConNear { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $speed > 59 ) { $speed = 63; } if ( $speed < 6 ) { $speed = 1; } Debug( "Focus Near" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Focus Near $speed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=focusout&-speed=$speed"; $self->sendCmd( $cmd ); # The variable speed does not work with focus setting, so I used to set the duration of the order # the result is identical $self->autoStop( int(10000*$speed) ); } sub focusConFar { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $speed > 59 ) { $speed = 63; } if ( $speed < 6 ) { $speed = 1; } Debug( "Focus Far" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Focus Far $speed"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-step=0&-act=focusin&-speed=$speed"; $self->sendCmd( $cmd ); # The variable speed does not work with focus setting, so I used to set the duration of the order # the result is identical $self->autoStop( int(10000*$speed) ); } sub focusAuto { my $self = shift; Debug( "Focus Auto" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Focus Auto"; $self->sendCmd( $cmd ); } my $cmd = "ptzctrl.cgi?-act=auto&-step=1"; $self->sendCmd( $cmd ); } sub focusMan { my $self = shift; Debug( "Focus Manu=OSD ON OFF" ); if ( $osd eq "on" ) { $osd = "off"; my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=OSD $osd"; $self->sendCmd( $cmd ); $self->autoStop( int(1000000*0.5) ); } if ( $osd eq "off" ) { $osd = "on"; my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=OSD $osd"; $self->sendCmd( $cmd ); $self->autoStop( int(1000000*0.5) ); } } sub whiteConIn { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $speed > 255 ) { $speed = 255; } if ( $speed < 0 ) { $speed = 0; } Debug( "White ConIn=brightness" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Brightness $speed"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-brightness=$speed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub whiteConOut { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Standardization for incorrect possible value in the database, and for realise at low and high speed an more precise moving if ( $speed > 255 ) { $speed = 255; } if ( $speed < 0 ) { $speed = 0; } Debug( "White ConOut=Contrast" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Contrast $speed"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-contrast=$speed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub whiteAuto { my $self = shift; Debug( "White Auto=Brightness Reset" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Brightness Reset"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-brightness=120"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub whiteMan { my $self = shift; Debug( "White Manuel=Contrast Reset" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Contrast Reset"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-contrast=140"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub irisConOpen { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Standardization for incorrect value in the database if ( $speed > 255 ) { $speed = 255; } if ( $speed < 0 ) { $speed = 0; } Debug( "Iris ConOpen=Saturation" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Saturation $speed"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-saturation=$speed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub irisConClose { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Standardization for incorrect value in the database if ( $speed > 255 ) { $speed = 255; } if ( $speed < 0 ) { $speed = 0; } Debug( "Iris ConClose=Hue" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Hue $speed"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-hue=$speed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub irisAuto { my $self = shift; Debug( "Iris Auto=Saturation Reset" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Saturation Reset"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-saturation=150"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub irisMan { my $self = shift; Debug( "Iris Manuel=Hue Reset" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Hue Reset"; $self->sendCmd( $cmd ); } my $cmd = "param.cgi?cmd=setimageattr&-hue=255"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub presetSet { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); if ( ( $preset >= 1 ) && ( $preset <= 8 ) ) { Debug( "Clear Preset $preset" ); my $cmd = "preset.cgi?-act=set&-status=0&-number=$preset"; $self->sendCmd( $cmd ); Debug( "Set Preset $preset" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=PresetSet $preset"; $self->sendCmd( $cmd ); } my $cmd = "preset.cgi?-act=set&-status=1&-number=$preset"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } } sub presetGoto { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); if ( ( $preset >= 1 ) && ( $preset <= 8 ) ) { Debug( "Goto Preset $preset" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=PresetGoto $preset"; $self->sendCmd( $cmd ); } my $cmd = "preset.cgi?-act=goto&-number=$preset"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 FI8620 ZoneMinder::Database - Perl extension for FOSCAM FI8620 =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Control/FI9821W_Y2k.pm000066400000000000000000000530301225361755400257270ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder FOSCAM version 1.0 API Control Protocol Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================================= # # This module FI8620_Y2k.pm contains the implementation of API camera control # For FOSCAM FI8620 Dome PTZ Camera (This cam support only H264 streaming) # V1.0 Le 09 AOUT 2013 - production usable for the script but not for the camera "reboot itself" # If you wan't to contact me i understand French and English, precise ZoneMinder in subject # My name is Christophe DAPREMONT my email is christophe_y2k@yahoo.fr # # ========================================================================================= package ZoneMinder::Control::FI9821W_Y2k; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Control; our @ISA = qw(ZoneMinder::Control); our $VERSION = $ZoneMinder::Base::VERSION; # =================================================================================================================================== # # FI9821 FOSCAM PT H264 Control Protocol # with Firmware version V1.2.1.1 (latest at 09/08/2013) # based with the latest buggy CGI doc from FOSCAM ( http://foscam.us/forum/cgi-sdk-for-hd-camera-t6045.html ) # This IPCAM work under ZoneMinder V1.25 from alternative source of code # from this svn at https://svn.unixmedia.net/public/zum/trunk/zum/ # Many Thanks to "MASTERTHEKNIFE" for the excellent speed optimisation ( http://www.zoneminder.com/forums/viewtopic.php?f=9&t=17652 ) # And to "NEXTIME" for the recent source update and incredible plugins ( http://www.zoneminder.com/forums/viewtopic.php?f=9&t=20587 ) # And all people helping ZoneMinder dev. # # -FUNCTION: display on OSD # speed is progressive in function of where you click on arrow ========> # speed low=/ \=speed high # =================================================================================================================================== use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); # Set $osd to "off" if you wan't disabled OSD i need to place this variable in another script because # this script is reload at every command ,if i want the button on/off (Focus MAN) for OSD works... my $osd = "on"; sub new { my $class = shift; my $id = shift; my $self = ZoneMinder::Control->new( $id ); bless( $self, $class ); srand( time() ); return $self; } our $AUTOLOAD; sub AUTOLOAD { my $self = shift; my $class = ref($self) || croak( "$self not object" ); my $name = $AUTOLOAD; $name =~ s/.*://; if ( exists($self->{$name}) ) { return( $self->{$name} ); } Fatal( "Can't access $name member of object of class $class" ); } sub open { my $self = shift; $self->loadMonitor(); use LWP::UserAgent; $self->{ua} = LWP::UserAgent->new; $self->{ua}->agent( "ZoneMinder Control Agent/".ZM_VERSION ); $self->{state} = 'open'; } sub close { my $self = shift; $self->{state} = 'closed'; } sub printMsg { my $self = shift; my $msg = shift; my $msg_len = length($msg); Debug( $msg."[".$msg_len."]" ); } sub sendCmd { my $self = shift; my $cmd = shift; my $result = undef; printMsg( $cmd, "Tx" ); my $temps = time(); my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/cgi-bin/CGIProxy.fcgi?usr%3Dadmin%26pwd%3D".$self->{Monitor}->{ControlDevice}."%26cmd%3D".$cmd."%26".$temps ); my $res = $self->{ua}->request($req); if ( $res->is_success ) { $result = !undef; } else { Error( "Error check failed: '".$res->status_line()."'" ); } return( $result ); } sub reset { my $self = shift; Debug ( "Reset = setup camera FI9821W" ); # Setup OSD my $cmd = "setOSDSetting%26isEnableTimeStamp%3D0%26isEnableDevName%3D1%26dispPos%3D0%26isEnabledOSDMask%3D0"; $self->sendCmd( $cmd ); # Setup For Stream=0 Resolution=720p Bandwith=4M FPS=30 KeyFrameInterval/GOP=100 VBR=ON my $cmd = "setVideoStreamParam%26streamType%3D0%26resolution%3D0%26bitRate%3D4194304%26frameRate%3D30%26GOP%3D100%26isVBR%3D1"; $self->sendCmd( $cmd ); # Setup For Infrared AUTO my $cmd = "setInfraLedConfig%26Mode%3D1"; $self->sendCmd( $cmd ); # Reset image settings my $cmd = "resetImageSetting"; $self->sendCmd( $cmd ); } sub moveStop { my $self = shift; Debug( "Move Stop" ); my $cmd = "ptzStopRun"; $self->sendCmd( $cmd ); my $cmd = "setDevName%26devName%3D."; $self->sendCmd( $cmd ); my $cmd = "setOSDSetting%26isEnableDevName%3D1"; $self->sendCmd( $cmd ); } sub autoStop { my $self = shift; my $autostop = shift; if( $autostop ) { Debug( "Auto Stop" ); usleep( $autostop ); my $cmd = "ptzStopRun"; $self->sendCmd( $cmd ); } } sub moveConUp { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4 $tiltspeed = abs($tiltspeed - 4); # Normalisation en cas de valeur erronée dans la base de données if ( $tiltspeed > 4 ) { $tiltspeed = 4; } if ( $tiltspeed < 0 ) { $tiltspeed = 0; } Debug( "Move Up" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DMove Up $tiltspeed"; $self->sendCmd( $cmd ); } my $cmd = "setPTZSpeed%26speed%3D$tiltspeed"; $self->sendCmd( $cmd ); my $cmd = "ptzMoveUp"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub moveConDown { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4 $tiltspeed = abs($tiltspeed - 4); # Normalization if ( $tiltspeed > 4 ) { $tiltspeed = 4; } if ( $tiltspeed < 0 ) { $tiltspeed = 0; } Debug( "Move Down" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DMove Down $tiltspeed"; $self->sendCmd( $cmd ); } my $cmd = "setPTZSpeed%26speed%3D$tiltspeed"; $self->sendCmd( $cmd ); my $cmd = "ptzMoveDown"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub moveConLeft { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed' ); # Normalisation en cas de valeur erronée dans la base de données if ( $panspeed > 4 ) { $panspeed = 4; } if ( $panspeed < 0 ) { $panspeed = 0; } Debug( "Move Left" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DMove Left $panspeed"; $self->sendCmd( $cmd ); } my $cmd = "setPTZSpeed%26speed%3D$panspeed"; $self->sendCmd( $cmd ); my $cmd = "ptzMoveLeft"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub moveConRight { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed' ); # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4 $panspeed = abs($panspeed - 4); # Normalisation en cas de valeur erronée dans la base de données if ( $panspeed > 4 ) { $panspeed = 4; } if ( $panspeed < 0 ) { $panspeed = 0; } Debug( "Move Right" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DMove Right $panspeed"; $self->sendCmd( $cmd ); } my $cmd = "setPTZSpeed%26speed%3D$panspeed"; $self->sendCmd( $cmd ); my $cmd = "ptzMoveRight"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub moveConUpLeft { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4 $tiltspeed = abs($tiltspeed - 4); # Normalisation en cas de valeur erronée dans la base de données if ( $tiltspeed > 4 ) { $tiltspeed = 4; } if ( $tiltspeed < 0 ) { $tiltspeed = 0; } Debug( "Move Con Up Left" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DMove Up Left $tiltspeed"; $self->sendCmd( $cmd ); } my $cmd = "setPTZSpeed%26speed%3D$tiltspeed"; $self->sendCmd( $cmd ); my $cmd = "ptzMoveTopLeft"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub moveConUpRight { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4 $tiltspeed = abs($tiltspeed - 4); # Normalisation en cas de valeur erronée dans la base de données if ( $tiltspeed > 4 ) { $tiltspeed = 4; } if ( $tiltspeed < 0 ) { $tiltspeed = 0; } Debug( "Move Con Up Right" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DMove Up Right $tiltspeed"; $self->sendCmd( $cmd ); } my $cmd = "setPTZSpeed%26speed%3D$tiltspeed"; $self->sendCmd( $cmd ); my $cmd = "ptzMoveTopRight"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub moveConDownLeft { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4 $tiltspeed = abs($tiltspeed - 4); # Normalisation en cas de valeur erronée dans la base de données if ( $tiltspeed > 4 ) { $tiltspeed = 4; } if ( $tiltspeed < 0 ) { $tiltspeed = 0; } Debug( "Move Con Down Left" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DMove Down Left $tiltspeed"; $self->sendCmd( $cmd ); } my $cmd = "setPTZSpeed%26speed%3D$tiltspeed"; $self->sendCmd( $cmd ); my $cmd = "ptzMoveBottomLeft"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub moveConDownRight { my $self = shift; my $params = shift; my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4 $tiltspeed = abs($tiltspeed - 4); # Normalisation en cas de valeur erronée dans la base de données if ( $tiltspeed > 4 ) { $tiltspeed = 4; } if ( $tiltspeed < 0 ) { $tiltspeed = 0; } Debug( "Move Con Down Right" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DMove Down Right $tiltspeed"; $self->sendCmd( $cmd ); } my $cmd = "setPTZSpeed%26speed%3D$tiltspeed"; $self->sendCmd( $cmd ); my $cmd = "ptzMoveBottomRight"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub zoomConTele { my $self = shift; Debug( "Zoom-Tele=MANU IR LED ON" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DManual IR LED Switch ON"; $self->sendCmd( $cmd ); } my $cmd = "setInfraLedConfig%26mode%3D1"; $self->sendCmd( $cmd ); my $cmd = "openInfraLed"; $self->sendCmd( $cmd ); } sub zoomConWide { my $self = shift; Debug( "Zoom-Wide=MANU IR LED OFF" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DManual IR LED Switch OFF"; $self->sendCmd( $cmd ); } my $cmd = "setInfraLedConfig%26mode%3D1"; $self->sendCmd( $cmd ); my $cmd = "closeInfraLed"; $self->sendCmd( $cmd ); } sub wake { my $self = shift; Debug( "Wake=AUTO IR LED" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DAuto IR LED Mode"; $self->sendCmd( $cmd ); } my $cmd = "setInfraLedConfig%26mode%3D0"; $self->sendCmd( $cmd ); } sub focusConNear { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Normalisation en cas de valeur erronée dans la base de données if ( $speed > 100 ) { $speed = 100; } if ( $speed < 0 ) { $speed = 0; } Debug( "Focus Near=Sharpness" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DSharpness $speed"; $self->sendCmd( $cmd ); my $cmd = "setOSDSetting%26isEnableDevName%3D1"; $self->sendCmd( $cmd ); } my $cmd = "setSharpness%26sharpness%3D$speed"; $self->sendCmd( $cmd ); # La variable speed ne fonctionne pas en paramètre du focus, alors je l'utilise pour définir la durée de la commande # le résulat est identique $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub focusConFar { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Normalisation en cas de valeur erronée dans la base de données if ( $speed > 100 ) { $speed = 100; } if ( $speed < 0 ) { $speed = 0; } Debug( "Focus Far" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DSharpness $speed"; $self->sendCmd( $cmd ); } my $cmd = "setSharpness%26sharpness%3D$speed"; $self->sendCmd( $cmd ); # La variable speed ne fonctionne pas en paramètre du focus alors je l'utilise pour définir la durée de la commande # le résulat est identique $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub focusAuto { my $self = shift; Debug( "Focus Auto=Reset Sharpness" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DReset Sharpness"; $self->sendCmd( $cmd ); } my $cmd = "setSharpness%26sharpness%3D10"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub focusMan { my $self = shift; Debug( "Focus Manu=Reset Sharpness" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DFOSCAM FI9821W Script V1.0 By Christophe_y2k"; $self->sendCmd( $cmd ); } } sub whiteConIn { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Normalisation en cas de valeur erronée dans la base de données if ( $speed > 100 ) { $speed = 100; } if ( $speed < 0 ) { $speed = 0; } Debug( "White ConIn=brightness" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DBrightness $speed"; $self->sendCmd( $cmd ); } my $cmd = "setBrightness%26brightness%3D$speed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub whiteConOut { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Normalisation en cas de valeur erronée dans la base de données if ( $speed > 100 ) { $speed = 100; } if ( $speed < 0 ) { $speed = 0; } Debug( "White ConOut=Contrast" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DContrast $speed"; $self->sendCmd( $cmd ); } my $cmd = "setContrast%26constrast%3D$speed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub whiteAuto { my $self = shift; Debug( "White Auto=Brightness Reset" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DBrightness Reset"; $self->sendCmd( $cmd ); } my $cmd = "setBrightness%26brightness%3D50"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub whiteMan { my $self = shift; Debug( "White Manuel=Contrast Reset" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DContrast Reset"; $self->sendCmd( $cmd ); } my $cmd = "setContrast%26constrast%3D44"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub irisConOpen { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Normalisation en cas de valeur erronée dans la base de données if ( $speed > 100 ) { $speed = 100; } if ( $speed < 0 ) { $speed = 0; } Debug( "Iris ConOpen=Saturation" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DSaturation $speed"; $self->sendCmd( $cmd ); } my $cmd = "setSaturation%26saturation%3D$speed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub irisConClose { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed' ); # Normalisation en cas de valeur erronée dans la base de données if ( $speed > 100 ) { $speed = 100; } if ( $speed < 0 ) { $speed = 0; } Debug( "Iris ConClose=Hue" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DHue $speed"; $self->sendCmd( $cmd ); } my $cmd = "setHue%26hue%3D$speed"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub irisAuto { my $self = shift; Debug( "Iris Auto=Saturation Reset" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DSaturation Reset"; $self->sendCmd( $cmd ); } my $cmd = "setSaturation%26saturation%3D30"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub irisMan { my $self = shift; Debug( "Iris Manuel=Hue Reset" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DHue Reset"; $self->sendCmd( $cmd ); } my $cmd = "setHue%26hue%3D6"; $self->sendCmd( $cmd ); $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); } sub presetSet { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); if ( ( $preset >= 1 ) && ( $preset <= 16 ) ) { Debug( "Clear Preset $preset" ); my $cmd = "ptzDeletePresetPoint%26name%3D$preset"; $self->sendCmd( $cmd ); Debug( "Set Preset $preset" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DSet Preset $preset"; $self->sendCmd( $cmd ); } my $cmd = "ptzAddPresetPoint%26name%3D$preset"; $self->sendCmd( $cmd ); } } sub presetGoto { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); if ( ( $preset >= 1 ) && ( $preset <= 16 ) ) { Debug( "Goto Preset $preset" ); if ( $osd eq "on" ) { my $cmd = "setDevName%26devName%3DGoto Preset $preset"; $self->sendCmd( $cmd ); } my $cmd = "setPTZSpeed%26speed%3D0"; $self->sendCmd( $cmd ); my $cmd = "ptzGotoPresetPoint%26name%3D$preset"; $self->sendCmd( $cmd ); } } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 FI9821W ZoneMinder::Database - Perl extension for FOSCAM FI9821W =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Control/LoftekSentinel.pm000066400000000000000000000242441225361755400271640ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Loftek Sentinel IP Control Protocol Module, $Date: 2009-11-25 09:20:00 +0000 (Wed, 04 Nov 2009) $, $Revision: 0001 $ # Copyright (C) 2001-2008 Philip Coombes # Original modification for use with Foscam FI8908W IP Camera by Dave Harris # Updated by Ivan Francolin Martinez # Converted for use with Loftek Sentinal IP Camera by Andrew Bauer (knnniggett@users.sourceforge.net) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the implementation of the Loftek Sentinel IP camera control # protocol # package ZoneMinder::Control::LoftekSentinel; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Control; our @ISA = qw(ZoneMinder::Control); our $VERSION = $ZoneMinder::Base::VERSION; our %CamParams = (); # ========================================================================== # # Loftek Sentinel IP Control Protocol # # On ControlAddress use the format : # USERNAME:PASSWORD@ADDRESS:PORT # eg : admin:@10.1.2.1:80 # zoneminder:zonepass@10.0.100.1:40000 # # ========================================================================== use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); sub new { my $class = shift; my $id = shift; my $self = ZoneMinder::Control->new( $id ); my $logindetails = ""; bless( $self, $class ); srand( time() ); return $self; } our $AUTOLOAD; sub AUTOLOAD { my $self = shift; my $class = ref( ) || croak( "$self not object" ); my $name = $AUTOLOAD; $name =~ s/.*://; if ( exists($self->{$name}) ) { return( $self->{$name} ); } Fatal( "Can't access $name member of object of class $class" ); } sub open { my $self = shift; $self->loadMonitor(); use LWP::UserAgent; $self->{ua} = LWP::UserAgent->new; $self->{ua}->agent( "ZoneMinder Control Agent/".ZM_VERSION ); $self->{state} = 'open'; } sub close { my $self = shift; $self->{state} = 'closed'; } sub printMsg { my $self = shift; my $msg = shift; my $msg_len = length($msg); Debug( $msg."[".$msg_len."]" ); } sub sendCmd { my $self = shift; my $cmd = shift; my $result = undef; printMsg( $cmd, "Tx" ); my $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 ); } sub getCamParams { my $self = shift; my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/get_camera_params.cgi" ); my $res = $self->{ua}->request($req); if ( $res->is_success ) { # Parse results setting values in %FCParams my $content = $res->decoded_content; while ($content =~ s/var\s+([^=]+)=([^;]+);//ms) { $CamParams{$1} = $2; } } else { Error( "Error check failed:'".$res->status_line()."'" ); } } #autoStop #This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab sub autoStop { my $self = shift; my $stop_command = shift; my $autostop = shift; if( $stop_command && $autostop) { Debug( "Auto Stop" ); usleep( $autostop ); my $cmd = "decoder_control.cgi?command=".$stop_command; $self->sendCmd( $cmd ); } } # Reset the Camera sub reset { my $self = shift; Debug( "Camera Reset" ); my $cmd = "reboot.cgi?"; $self->sendCmd( $cmd ); } #Up Arrow sub moveConUp { my $self = shift; my $stop_command = "1"; Debug( "Move Up" ); my $cmd = "decoder_control.cgi?command=0"; $self->sendCmd( $cmd ); $self->autoStop( $stop_command, $self->{Monitor}->{AutoStopTimeout} ); } #Down Arrow sub moveConDown { my $self = shift; my $stop_command = "3"; Debug( "Move Down" ); my $cmd = "decoder_control.cgi?command=2"; $self->sendCmd( $cmd ); $self->autoStop( $stop_command, $self->{Monitor}->{AutoStopTimeout} ); } #Left Arrow sub moveConLeft { my $self = shift; my $stop_command = "5"; Debug( "Move Left" ); my $cmd = "decoder_control.cgi?command=4"; $self->sendCmd( $cmd ); $self->autoStop( $stop_command, $self->{Monitor}->{AutoStopTimeout} ); } #Right Arrow sub moveConRight { my $self = shift; my $stop_command = "7"; Debug( "Move Right" ); my $cmd = "decoder_control.cgi?command=6"; $self->sendCmd( $cmd ); $self->autoStop( $stop_command, $self->{Monitor}->{AutoStopTimeout} ); } #Zoom In sub zoomConTele { my $self = shift; my $stop_command = "17"; Debug( "Zoom Tele" ); my $cmd = "decoder_control.cgi?command=18"; $self->sendCmd( $cmd ); $self->autoStop( $stop_command, $self->{Monitor}->{AutoStopTimeout} ); } #Zoom Out sub zoomConWide { my $self = shift; my $stop_command = "19"; Debug( "Zoom Wide" ); my $cmd = "decoder_control.cgi?command=16"; $self->sendCmd( $cmd ); $self->autoStop( $stop_command, $self->{Monitor}->{AutoStopTimeout} ); } #Diagonally Up Right Arrow #This camera does not have builtin diagonal commands so we emulate them sub moveConUpRight { my $self = shift; Debug( "Move Diagonally Up Right" ); $self->moveConUp( ); $self->moveConRight( ); } #Diagonally Down Right Arrow #This camera does not have builtin diagonal commands so we emulate them sub moveConDownRight { my $self = shift; Debug( "Move Diagonally Down Right" ); $self->moveConDown( ); $self->moveConRight( ); } #Diagonally Up Left Arrow #This camera does not have builtin diagonal commands so we emulate them sub moveConUpLeft { my $self = shift; Debug( "Move Diagonally Up Left" ); $self->moveConUp( ); $self->moveConLeft( ); } #Diagonally Down Left Arrow #This camera does not have builtin diagonal commands so we emulate them sub moveConDownLeft { my $self = shift; Debug( "Move Diagonally Down Left" ); $self->moveConDown( ); $self->moveConLeft( ); } #Stop sub moveStop { my $self = shift; Debug( "Move Stop" ); my $cmd = "decoder_control.cgi?command=1"; $self->sendCmd( $cmd ); } #Set Camera Preset #Presets must be translated into values internal to the camera #Those values are: 30,32,34,36,38,40,42,44 for presets 1-8 respectively sub presetSet { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); Debug( "Set Preset $preset" ); if (( $preset >= 1 ) && ( $preset <= 8 )) { my $cmd = "decoder_control.cgi?command=".(($preset*2) + 28); $self->sendCmd( $cmd ); } } #Recall Camera Preset #Presets must be translated into values internal to the camera #Those values are: 31,33,35,37,39,41,43,45 for presets 1-8 respectively sub presetGoto { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); Debug( "Goto Preset $preset" ); if (( $preset >= 1 ) && ( $preset <= 8 )) { my $cmd = "decoder_control.cgi?command=".(($preset*2) + 29); $self->sendCmd( $cmd ); } if ( $preset == 9 ) { $self->horizontalPatrol(); } if ( $preset == 10 ) { $self->horizontalPatrolStop(); } } #Horizontal Patrol - Vertical Patrols are not supported sub horizontalPatrol { my $self = shift; Debug( "Horizontal Patrol" ); my $cmd = "decoder_control.cgi?command=20"; $self->sendCmd( $cmd ); } #Horizontal Patrol Stop sub horizontalPatrolStop { my $self = shift; Debug( "Horizontal Patrol Stop" ); my $cmd = "decoder_control.cgi?command=21"; $self->sendCmd( $cmd ); } # Increase Brightness sub irisAbsOpen { my $self = shift; my $params = shift; $self->getCamParams() unless($CamParams{'brightness'}); my $step = $self->getParam( $params, 'step' ); $CamParams{'brightness'} += $step; $CamParams{'brightness'} = 255 if ($CamParams{'brightness'} > 255); Debug( "Iris $CamParams{'brightness'}" ); my $cmd = "camera_control.cgi?param=1&value=".$CamParams{'brightness'}; $self->sendCmd( $cmd ); } # Decrease Brightness sub irisAbsClose { my $self = shift; my $params = shift; $self->getCamParams() unless($CamParams{'brightness'}); my $step = $self->getParam( $params, 'step' ); $CamParams{'brightness'} -= $step; $CamParams{'brightness'} = 0 if ($CamParams{'brightness'} < 0); Debug( "Iris $CamParams{'brightness'}" ); my $cmd = "camera_control.cgi?param=1&value=".$CamParams{'brightness'}; $self->sendCmd( $cmd ); } # Increase Contrast sub whiteAbsIn { my $self = shift; my $params = shift; $self->getCamParams() unless($CamParams{'contrast'}); my $step = $self->getParam( $params, 'step' ); $CamParams{'contrast'} += $step; $CamParams{'contrast'} = 6 if ($CamParams{'contrast'} > 6); Debug( "Iris $CamParams{'contrast'}" ); my $cmd = "camera_control.cgi?param=2&value=".$CamParams{'contrast'}; $self->sendCmd( $cmd ); } # Decrease Contrast sub whiteAbsOut { my $self = shift; my $params = shift; $self->getCamParams() unless($CamParams{'contrast'}); my $step = $self->getParam( $params, 'step' ); $CamParams{'contrast'} -= $step; $CamParams{'contrast'} = 0 if ($CamParams{'contrast'} < 0); Debug( "Iris $CamParams{'contrast'}" ); my $cmd = "camera_control.cgi?param=2&value=".$CamParams{'contrast'}; $self->sendCmd( $cmd ); } 1; ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Control/Ncs370.pm000066400000000000000000000121441225361755400252070ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Neu-Fusion Control Protocol Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the implementation of the Neu-Fusion NCS370 IP camera # control protocol # package ZoneMinder::Control::Ncs370; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Control; our @ISA = qw(ZoneMinder::Control); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Ncs370 IP Control Protocol # # ========================================================================== use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); sub new { my $class = shift; my $id = shift; my $self = ZoneMinder::Control->new( $id ); bless( $self, $class ); srand( time() ); return $self; } our $AUTOLOAD; sub AUTOLOAD { my $self = shift; my $class = ref($self) || croak( "$self not object" ); my $name = $AUTOLOAD; $name =~ s/.*://; if ( exists($self->{$name}) ) { return( $self->{$name} ); } Fatal( "Can't access $name member of object of class $class" ); } sub open { my $self = shift; $self->loadMonitor(); use LWP::UserAgent; $self->{ua} = LWP::UserAgent->new; $self->{ua}->agent( "ZoneMinder Control Agent/".ZM_VERSION ); $self->{state} = 'open'; } sub close { my $self = shift; $self->{state} = 'closed'; } sub printMsg { my $self = shift; my $msg = shift; my $msg_len = length($msg); Debug( $msg."[".$msg_len."]" ); } sub sendCmd { my $self = shift; my $cmd = shift; my $result = undef; printMsg( $cmd, "Tx" ); my $req = HTTP::Request->new( POST=>"http://".$self->{Monitor}->{ControlAddress}."/PANTILTCONTROL.CGI" ); my $res = $self->{ua}->request($req); if ( $res->is_success ) { $result = !undef; } else { Error( "Error check failed: '".$res->status_line()."'" ); } return( $result ); } sub moveConUp { my $self = shift; Debug( "Move Up" ); my $cmd = "PanSingleMoveDegree=1\nTiltSingleMoveDegree=1\nPanTiltSingleMove=1"; $self->sendCmd( $cmd ); } sub moveConDown { my $self = shift; Debug( "Move Down" ); my $cmd = "PanSingleMoveDegree=1\nTiltSingleMoveDegree=1\nPanTiltSingleMove=7"; $self->sendCmd( $cmd ); } sub moveConLeft { my $self = shift; Debug( "Move Left" ); my $cmd = "PanSingleMoveDegree=1\nTiltSingleMoveDegree=1\nPanTiltSingleMove=3"; $self->sendCmd( $cmd ); } sub moveConRight { my $self = shift; Debug( "Move Right" ); my $cmd = "PanSingleMoveDegree=1\nTiltSingleMoveDegree=1\nPanTiltSingleMove=5"; $self->sendCmd( $cmd ); } sub moveConUpRight { moveConUp(); moveConRight(); } sub moveConUpLeft { moveConUp(); moveConLeft(); } sub moveConDownRight { moveConDown(); moveConRight(); } sub moveConDownLeft { moveConDown(); moveConLeft(); } sub presetHome { my $self = shift; Debug( "Home Preset" ); my $cmd = "PanSingleMoveDegree=1\nTiltSingleMoveDegree=1\nPanTiltSingleMove=4"; $self->sendCmd( $cmd ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Control/PanasonicIP.pm000066400000000000000000000162401225361755400263770ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Panasonic IP Control Protocol Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the implementation of the Panasonic IP camera control # protocol # package ZoneMinder::Control::PanasonicIP; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Control; our @ISA = qw(ZoneMinder::Control); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Panasonic IP Control Protocol # # ========================================================================== use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); sub new { my $class = shift; my $id = shift; my $self = ZoneMinder::Control->new( $id ); bless( $self, $class ); srand( time() ); return $self; } our $AUTOLOAD; sub AUTOLOAD { my $self = shift; my $class = ref($self) || croak( "$self not object" ); my $name = $AUTOLOAD; $name =~ s/.*://; if ( exists($self->{$name}) ) { return( $self->{$name} ); } Fatal( "Can't access $name member of object of class $class" ); } sub open { my $self = shift; $self->loadMonitor(); use LWP::UserAgent; $self->{ua} = LWP::UserAgent->new; $self->{ua}->agent( "ZoneMinder Control Agent/".ZM_VERSION ); $self->{state} = 'open'; } sub close { my $self = shift; $self->{state} = 'closed'; } sub printMsg { my $self = shift; my $msg = shift; my $msg_len = length($msg); Debug( $msg."[".$msg_len."]" ); } sub sendCmd { my $self = shift; my $cmd = shift; my $result = undef; printMsg( $cmd, "Tx" ); my $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 ); } sub cameraReset { my $self = shift; Debug( "Camera Reset" ); my $cmd = "nphRestart?PAGE=Restart&Restart=OK"; $self->sendCmd( $cmd ); } sub moveConUp { my $self = shift; Debug( "Move Up" ); my $cmd = "nphControlCamera?Direction=TiltUp"; $self->sendCmd( $cmd ); } sub moveConDown { my $self = shift; Debug( "Move Down" ); my $cmd = "nphControlCamera?Direction=TiltDown"; $self->sendCmd( $cmd ); } sub moveConLeft { my $self = shift; Debug( "Move Left" ); my $cmd = "nphControlCamera?Direction=PanLeft"; $self->sendCmd( $cmd ); } sub moveConRight { my $self = shift; Debug( "Move Right" ); my $cmd = "nphControlCamera?Direction=PanRight"; $self->sendCmd( $cmd ); } sub moveMap { my $self = shift; my $params = shift; my $xcoord = $self->getParam( $params, 'xcoord' ); my $ycoord = $self->getParam( $params, 'ycoord' ); Debug( "Move Map to $xcoord,$ycoord" ); my $cmd = "nphControlCamera?Direction=Direct&NewPosition.x=$xcoord&NewPosition.y=$ycoord&Width=".$self->{Monitor}->{Width}."&Height=".$self->{Monitor}->{Height}; $self->sendCmd( $cmd ); } sub zoomConTele { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'step' ); Debug( "Zoom Tele" ); my $cmd = "nphControlCamera?Direction=ZoomTele"; $self->sendCmd( $cmd ); } sub zoomConWide { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'step' ); Debug( "Zoom Wide" ); my $cmd = "nphControlCamera?Direction=ZoomWide"; $self->sendCmd( $cmd ); } sub focusConNear { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'step' ); Debug( "Focus Near" ); my $cmd = "nphControlCamera?Direction=FocusNear"; $self->sendCmd( $cmd ); } sub focusConFar { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'step' ); Debug( "Focus Far" ); my $cmd = "nphControlCamera?Direction=FocusFar"; $self->sendCmd( $cmd ); } sub focusAuto { my $self = shift; Debug( "Focus Auto" ); my $cmd = "nphControlCamera?Direction=FocusAuto"; $self->sendCmd( $cmd ); } sub focusMan { my $self = shift; Debug( "Focus Manual" ); my $cmd = "/axis-cgi/com/ptz.cgi?autofocus=off"; $self->sendCmd( $cmd ); } sub presetClear { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); Debug( "Clear Preset $preset" ); my $cmd = "nphPresetNameCheck?Data=$preset"; $self->sendCmd( $cmd ); } sub presetSet { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); Debug( "Set Preset $preset" ); my $cmd = "nphPresetNameCheck?PresetName=$preset&Data=$preset"; $self->sendCmd( $cmd ); } sub presetGoto { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); Debug( "Goto Preset $preset" ); my $cmd = "nphControlCamera?Direction=Preset&PresetOperation=Move&Data=$preset"; $self->sendCmd( $cmd ); } sub presetHome { my $self = shift; Debug( "Home Preset" ); my $cmd = "nphControlCamera?Direction=HomePosition"; $self->sendCmd( $cmd ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Control/PelcoD.pm000066400000000000000000000447071225361755400254120ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Pelco-D Control Protocol Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the implementation of the Pelco-D camera control # protocol # package ZoneMinder::Control::PelcoD; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Control; our @ISA = qw(ZoneMinder::Control); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Pelco-D Control Protocol # # ========================================================================== use ZoneMinder::Logger qw(:all); use Time::HiRes qw( usleep ); use constant SYNC => 0xff; use constant COMMAND_GAP => 100000; # In ms sub new { my $class = shift; my $id = shift; my $self = ZoneMinder::Control->new( $id ); bless( $self, $class ); srand( time() ); return $self; } our $AUTOLOAD; sub AUTOLOAD { my $self = shift; my $class = ref($self) || croak( "$self not object" ); my $name = $AUTOLOAD; $name =~ s/.*://; if ( exists($self->{$name}) ) { return( $self->{$name} ); } Fatal( "Can't access $name member of object of class $class" ); } sub open { my $self = shift; $self->loadMonitor(); use Device::SerialPort; $self->{port} = new Device::SerialPort( $self->{Monitor}->{ControlDevice} ); $self->{port}->baudrate(2400); $self->{port}->databits(8); $self->{port}->parity('none'); $self->{port}->stopbits(1); $self->{port}->handshake('none'); $self->{port}->read_const_time(50); $self->{port}->read_char_time(10); $self->{state} = 'open'; } sub close { my $self = shift; $self->{state} = 'closed'; $self->{port}->close(); } sub printMsg { if ( logDebugging() ) { my $self = shift; my $msg = shift; my $prefix = shift || ""; $prefix = $prefix.": " if ( $prefix ); my $line_length = 16; my $msg_len = int(@$msg); my $msg_str = $prefix; for ( my $i = 0; $i < $msg_len; $i++ ) { if ( ($i > 0) && ($i%$line_length == 0) && ($i != ($msg_len-1)) ) { $msg_str .= sprintf( "\n%*s", length($prefix), "" ); } $msg_str .= sprintf( "%02x ", $msg->[$i] ); } $msg_str .= "[".$msg_len."]"; Debug( $msg_str ); } } sub sendCmd { my $self = shift; my $cmd = shift; my $ack = shift || 0; my $result = undef; my $checksum = 0x00; for ( my $i = 1; $i < int(@$cmd); $i++ ) { $checksum += $cmd->[$i]; $checksum &= 0xff; } push( @$cmd, $checksum ); $self->printMsg( $cmd, "Tx" ); my $id = $cmd->[0] & 0xf; my $tx_msg = pack( "C*", @$cmd ); #print( "Tx: ".length( $tx_msg )." bytes\n" ); my $n_bytes = $self->{port}->write( $tx_msg ); if ( !$n_bytes ) { Error( "Write failed: $!" ); } if ( $n_bytes != length($tx_msg) ) { Error( "Incomplete write, only ".$n_bytes." of ".length($tx_msg)." written: $!" ); } if ( $ack ) { Debug( "Waiting for ack" ); my $max_wait = 3; my $now = time(); while( 1 ) { my ( $count, $rx_msg ) = $self->{port}->read(4); if ( $count ) { #print( "Rx1: ".$count." bytes\n" ); my @resp = unpack( "C*", $rx_msg ); printMsg( \@resp, "Rx" ); if ( $resp[0] = 0x80 + ($id<<4) ) { if ( ($resp[1] & 0xf0) == 0x40 ) { my $socket = $resp[1] & 0x0f; Debug( "Got ack for socket $socket" ); $result = !undef; } else { Error( "Got bogus response" ); } last; } else { Error( "Got message for camera ".(($resp[0]-0x80)>>4) ); } } if ( (time() - $now) > $max_wait ) { Warning( "Response timeout" ); last; } } } } sub remoteReset { my $self = shift; Debug( "Remote Reset" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x0f, 0x00, 0x00 ); $self->sendCmd( \@msg ); } sub resetDefaults { my $self = shift; Debug( "Reset Defaults" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x29, 0x00, 0x00 ); $self->sendCmd( \@msg ); } sub cameraOff { my $self = shift; Debug( "Camera Off" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x08, 0x00, 0x00, 0x00 ); $self->sendCmd( \@msg ); } sub cameraOn { my $self = shift; Debug( "Camera On" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x88, 0x00, 0x00, 0x00 ); $self->sendCmd( \@msg ); } sub autoScan { my $self = shift; Debug( "Auto Scan" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x90, 0x00, 0x00, 0x00 ); $self->sendCmd( \@msg ); } sub manScan { my $self = shift; Debug( "Manual Scan" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x10, 0x00, 0x00, 0x00 ); $self->sendCmd( \@msg ); } sub stop { my $self = shift; Debug( "Stop" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x00, 0x00, 0x00 ); $self->sendCmd( \@msg ); } sub moveConUp { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'tiltspeed' ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Up" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x08, 0x00, $speed ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop( $params ); } } sub moveConDown { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'tiltspeed' ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Down" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x10, 0x00, $speed ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop(); } } sub moveConLeft { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'panspeed' ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Left" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x04, $speed, 0x00 ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop(); } } sub moveConRight { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'panspeed' ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Right" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x02, $speed, 0x00 ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop(); } } sub moveConUpLeft { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed', 0x3f ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x3f ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Up/Left" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x0c, $panspeed, $tiltspeed ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop(); } } sub moveConUpRight { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed', 0x3f ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x3f ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Up/Right" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x0a, $panspeed, $tiltspeed ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop(); } } sub moveConDownLeft { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed', 0x3f ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x3f ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Down/Left" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x14, $panspeed, $tiltspeed ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop(); } } sub moveConDownRight { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed', 0x3f ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x3f ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Down/Right" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x12, $panspeed, $tiltspeed ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop(); } } sub moveStop { my $self = shift; Debug( "Move Stop" ); $self->stop(); } sub flip180 { my $self = shift; Debug( "Flip 180" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x07, 0x00, 0x21 ); $self->sendCmd( \@msg ); } sub zeroPan { my $self = shift; Debug( "Zero Pan" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x07, 0x00, 0x22 ); $self->sendCmd( \@msg ); } sub _setZoomSpeed { my $self = shift; my $speed = shift; Debug( "Set Zoom Speed $speed" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x25, 0x00, $speed ); $self->sendCmd( \@msg ); } sub zoomStop { my $self = shift; Debug( "Zoom Stop" ); $self->stop(); $self->_setZoomSpeed( 0 ); } sub zoomConTele { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed', 0x01 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Zoom Tele" ); $self->_setZoomSpeed( $speed ); usleep( COMMAND_GAP ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x20, 0x00, 0x00 ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->zoomStop(); } } sub zoomConWide { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed', 0x01 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Zoom Wide" ); $self->_setZoomSpeed( $speed ); usleep( COMMAND_GAP ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x40, 0x00, 0x00 ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->zoomStop(); } } sub _setFocusSpeed { my $self = shift; my $speed = shift; Debug( "Set Focus Speed $speed" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x27, 0x00, $speed ); $self->sendCmd( \@msg ); } sub focusConNear { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed', 0x03 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Focus Near" ); $self->_setFocusSpeed( $speed ); usleep( COMMAND_GAP ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x01, 0x00, 0x00, 0x00 ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->_setFocusSpeed( 0 ); } } sub focusConFar { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed', 0x03 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Focus Far" ); $self->_setFocusSpeed( $speed ); usleep( COMMAND_GAP ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x80, 0x00, 0x00 ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->_setFocusSpeed( 0 ); } } sub focusStop { my $self = shift; Debug( "Focus Stop" ); $self->stop(); $self->_setFocusSpeed( 0 ); } sub focusAuto { my $self = shift; Debug( "Focus Auto" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x2b, 0x00, 0x00 ); $self->sendCmd( \@msg ); } sub focusMan { my $self = shift; Debug( "Focus Man" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x2b, 0x00, 0x02 ); $self->sendCmd( \@msg ); } sub _setIrisSpeed { my $self = shift; my $speed = shift; Debug( "Set Iris Speed $speed" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x27, 0x00, $speed ); $self->sendCmd( \@msg ); } sub irisConClose { my $self = shift; my $params = shift; my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Iris Close" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x04, 0x00, 0x00, 0x00 ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->_setIrisSpeed( 0 ); } } sub irisConOpen { my $self = shift; my $params = shift; my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Iris Open" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x02, 0x80, 0x00, 0x00 ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->_setIrisSpeed( 0 ); } } sub irisStop { my $self = shift; Debug( "Iris Stop" ); $self->stop(); $self->_setIrisSpeed( 0 ); } sub irisAuto { my $self = shift; Debug( "Iris Auto" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x2d, 0x00, 0x00 ); $self->sendCmd( \@msg ); } sub irisMan { my $self = shift; Debug( "Iris Man" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x2d, 0x00, 0x02 ); $self->sendCmd( \@msg ); } sub writeScreen { my $self = shift; my $params = shift; my $string = $self->getParam( $params, 'string' ); Debug( "Writing '$string' to screen" ); my @chars = unpack( "C*", $string ); for ( my $i = 0; $i < length($string); $i++ ) { my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x15, $i, $chars[$i] ); $self->sendCmd( \@msg ); usleep( COMMAND_GAP ); } } sub clearScreen { my $self = shift; Debug( "Clear Screen" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x17, 0x00, 0x00 ); $self->sendCmd( \@msg ); } sub clearPreset { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset', 1 ); Debug( "Clear Preset $preset" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x05, 0x00, $preset ); $self->sendCmd( \@msg ); } sub presetSet { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset', 1 ); Debug( "Set Preset $preset" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x03, 0x00, $preset ); $self->sendCmd( \@msg ); } sub presetGoto { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset', 1 ); Debug( "Goto Preset $preset" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x07, 0x00, $preset ); $self->sendCmd( \@msg ); } sub presetHome { my $self = shift; Debug( "Home Preset" ); my @msg = ( SYNC, $self->{Monitor}->{ControlAddress}, 0x00, 0x07, 0x00, 0x22 ); $self->sendCmd( \@msg ); } sub reset { my $self = shift; Debug( "Reset" ); $self->remoteReset(); $self->resetDefaults(); } sub wake { my $self = shift; Debug( "Wake" ); $self->cameraOn(); } sub sleep { my $self = shift; Debug( "Sleep" ); $self->cameraOff(); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Control/PelcoP.pm000066400000000000000000000451541225361755400254230ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Pelco-P Control Protocol Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the implementation of the Pelco-P camera control # protocol # package ZoneMinder::Control::PelcoD; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Control; our @ISA = qw(ZoneMinder::Control); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Pelco-P Control Protocol # # ========================================================================== use ZoneMinder::Logger qw(:all); use Time::HiRes qw( usleep ); use constant STX => 0xa0; use constant ETX => 0xaf; use constant COMMAND_GAP => 100000; # In ms sub new { my $class = shift; my $id = shift; my $self = ZoneMinder::Control->new( $id ); bless( $self, $class ); srand( time() ); return $self; } our $AUTOLOAD; sub AUTOLOAD { my $self = shift; my $class = ref($self) || croak( "$self not object" ); my $name = $AUTOLOAD; $name =~ s/.*://; if ( exists($self->{$name}) ) { return( $self->{$name} ); } Fatal( "Can't access $name member of object of class $class" ); } sub open { my $self = shift; $self->loadMonitor(); use Device::SerialPort; $self->{port} = new Device::SerialPort( $self->{Monitor}->{ControlDevice} ); $self->{port}->baudrate(4800); $self->{port}->databits(8); $self->{port}->parity('none'); $self->{port}->stopbits(1); $self->{port}->handshake('none'); $self->{port}->read_const_time(50); $self->{port}->read_char_time(10); $self->{state} = 'open'; } sub close { my $self = shift; $self->{state} = 'closed'; $self->{port}->close(); } sub printMsg { if ( logDebugging() ) { my $self = shift; my $msg = shift; my $prefix = shift || ""; $prefix = $prefix.": " if ( $prefix ); my $line_length = 16; my $msg_len = int(@$msg); my $msg_str = $prefix; for ( my $i = 0; $i < $msg_len; $i++ ) { if ( ($i > 0) && ($i%$line_length == 0) && ($i != ($msg_len-1)) ) { $msg_str .= sprintf( "\n%*s", length($prefix), "" ); } $msg_str .= sprintf( "%02x ", $msg->[$i] ); } $msg_str .= "[".$msg_len."]"; Debug( $msg_str ); } } sub sendCmd { my $self = shift; my $cmd = shift; my $ack = shift || 0; my $result = undef; my $checksum = 0x00; for ( my $i = 1; $i < int(@$cmd); $i++ ) { $checksum ^= $cmd->[$i]; } $checksum &= 0xff; push( @$cmd, $checksum ); $self->printMsg( $cmd, "Tx" ); my $id = $cmd->[0] & 0xf; my $tx_msg = pack( "C*", @$cmd ); #print( "Tx: ".length( $tx_msg )." bytes\n" ); my $n_bytes = $self->{port}->write( $tx_msg ); if ( !$n_bytes ) { Error( "Write failed: $!" ); } if ( $n_bytes != length($tx_msg) ) { Error( "Incomplete write, only ".$n_bytes." of ".length($tx_msg)." written: $!" ); } if ( $ack ) { Debug( "Waiting for ack" ); my $max_wait = 3; my $now = time(); while( 1 ) { my ( $count, $rx_msg ) = $self->{port}->read(4); if ( $count ) { #print( "Rx1: ".$count." bytes\n" ); my @resp = unpack( "C*", $rx_msg ); printMsg( \@resp, "Rx" ); if ( $resp[0] = 0x80 + ($id<<4) ) { if ( ($resp[1] & 0xf0) == 0x40 ) { my $socket = $resp[1] & 0x0f; Debug( "Got ack for socket $socket" ); $result = !undef; } else { Error( "Got bogus response" ); } last; } else { Error( "Got message for camera ".(($resp[0]-0x80)>>4) ); } } if ( (time() - $now) > $max_wait ) { Warning( "Response timeout" ); last; } } } } sub remoteReset { my $self = shift; Debug( "Remote Reset" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x0f, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); } sub resetDefaults { my $self = shift; Debug( "Reset Defaults" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x29, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); } sub cameraOff { my $self = shift; Debug( "Camera Off" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x08, 0x00, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); } sub cameraOn { my $self = shift; Debug( "Camera On" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x88, 0x00, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); } sub autoScan { my $self = shift; Debug( "Auto Scan" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x90, 0x00, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); } sub manScan { my $self = shift; Debug( "Manual Scan" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x10, 0x00, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); } sub stop { my $self = shift; Debug( "Stop" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x00, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); } sub moveConUp { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'tiltspeed' ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Up" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x08, 0x00, $speed, ETX ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop( $params ); } } sub moveConDown { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'tiltspeed' ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Down" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x10, 0x00, $speed, ETX ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop(); } } sub moveConLeft { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'panspeed' ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Left" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x04, $speed, 0x00, ETX ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop(); } } sub moveConRight { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'panspeed' ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Right" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x02, $speed, 0x00, ETX ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop(); } } sub moveConUpLeft { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed', 0x3f ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x3f ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Up/Left" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x0c, $panspeed, $tiltspeed, ETX ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop(); } } sub moveConUpRight { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed', 0x3f ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x3f ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Up/Right" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x0a, $panspeed, $tiltspeed, ETX ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop(); } } sub moveConDownLeft { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed', 0x3f ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x3f ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Down/Left" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x14, $panspeed, $tiltspeed, ETX ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop(); } } sub moveConDownRight { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed', 0x3f ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x3f ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Down/Right" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x12, $panspeed, $tiltspeed, ETX ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop(); } } sub moveStop { my $self = shift; Debug( "Move Stop" ); $self->stop(); } sub flip180 { my $self = shift; Debug( "Flip 180" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x07, 0x00, 0x21, ETX ); $self->sendCmd( \@msg ); } sub zeroPan { my $self = shift; Debug( "Zero Pan" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x07, 0x00, 0x22, ETX ); $self->sendCmd( \@msg ); } sub _setZoomSpeed { my $self = shift; my $speed = shift; Debug( "Set Zoom Speed $speed" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x25, 0x00, $speed, ETX ); $self->sendCmd( \@msg ); } sub zoomStop { my $self = shift; Debug( "Zoom Stop" ); $self->stop(); $self->_setZoomSpeed( 0 ); } sub zoomConTele { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed', 0x01 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Zoom Tele" ); $self->_setZoomSpeed( $speed ); usleep( COMMAND_GAP ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x20, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->zoomStop(); } } sub zoomConWide { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed', 0x01 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Zoom Wide" ); $self->_setZoomSpeed( $speed ); usleep( COMMAND_GAP ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x40, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->zoomStop(); } } sub _setFocusSpeed { my $self = shift; my $speed = shift; Debug( "Set Focus Speed $speed" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x27, 0x00, $speed, ETX ); $self->sendCmd( \@msg ); } sub focusConNear { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed', 0x03 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Focus Near" ); $self->_setFocusSpeed( $speed ); usleep( COMMAND_GAP ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x01, 0x00, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->_setFocusSpeed( 0 ); } } sub focusConFar { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed', 0x03 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Focus Far" ); $self->_setFocusSpeed( $speed ); usleep( COMMAND_GAP ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x80, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->_setFocusSpeed( 0 ); } } sub focusStop { my $self = shift; Debug( "Focus Stop" ); $self->stop(); $self->_setFocusSpeed( 0 ); } sub focusAuto { my $self = shift; Debug( "Focus Auto" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x2b, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); } sub focusMan { my $self = shift; Debug( "Focus Man" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x2b, 0x00, 0x02, ETX ); $self->sendCmd( \@msg ); } sub _setIrisSpeed { my $self = shift; my $speed = shift; Debug( "Set Iris Speed $speed" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x27, 0x00, $speed, ETX ); $self->sendCmd( \@msg ); } sub irisConClose { my $self = shift; my $params = shift; my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Iris Close" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x04, 0x00, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->_setIrisSpeed( 0 ); } } sub irisConOpen { my $self = shift; my $params = shift; my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Iris Open" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x02, 0x80, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->_setIrisSpeed( 0 ); } } sub irisStop { my $self = shift; Debug( "Iris Stop" ); $self->stop(); $self->_setIrisSpeed( 0 ); } sub irisAuto { my $self = shift; Debug( "Iris Auto" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x2d, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); } sub irisMan { my $self = shift; Debug( "Iris Man" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x2d, 0x00, 0x02, ETX ); $self->sendCmd( \@msg ); } sub writeScreen { my $self = shift; my $params = shift; my $string = $self->getParam( $params, 'string' ); Debug( "Writing '$string' to screen" ); my @chars = unpack( "C*", $string ); for ( my $i = 0; $i < length($string); $i++ ) { my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x15, $i, $chars[$i], ETX ); $self->sendCmd( \@msg ); usleep( COMMAND_GAP ); } } sub clearScreen { my $self = shift; Debug( "Clear Screen" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x17, 0x00, 0x00, ETX ); $self->sendCmd( \@msg ); } sub clearPreset { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset', 1 ); Debug( "Clear Preset $preset" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x05, 0x00, $preset, ETX ); $self->sendCmd( \@msg ); } sub presetSet { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset', 1 ); Debug( "Set Preset $preset" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x03, 0x00, $preset, ETX ); $self->sendCmd( \@msg ); } sub presetGoto { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset', 1 ); Debug( "Goto Preset $preset" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x07, 0x00, $preset, ETX ); $self->sendCmd( \@msg ); } sub presetHome { my $self = shift; Debug( "Home Preset" ); my @msg = ( STX, $self->{Monitor}->{ControlAddress}, 0x00, 0x07, 0x00, 0x22, ETX ); $self->sendCmd( \@msg ); } sub reset { my $self = shift; Debug( "Reset" ); $self->remoteReset(); $self->resetDefaults(); } sub wake { my $self = shift; Debug( "Wake" ); $self->cameraOn(); } sub sleep { my $self = shift; Debug( "Sleep" ); $self->cameraOff(); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm000066400000000000000000000165131225361755400263250ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Airlink SkyIPCam AICN747/AICN747W Control Protocol Module, $Date: 2008-09-13 17:30:29 +0000 (Sat, 13 Sept 2008) $, $Revision: 2229 $ # Copyright (C) 2008 Brian Rudy (brudyNO@SPAMpraecogito.com) # # 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 Airlink SkyIPCam # AICN747/AICN747W, TrendNet TV-IP410/TV-IP410W and other OEM versions of the # Fitivision CS-130A/CS-131A camera control protocol. # package ZoneMinder::Control::SkyIPCam7xx; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Control; our @ISA = qw(ZoneMinder::Control); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Airlink SkyIPCam AICN747/AICN747W Control Protocol # # ========================================================================== use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); sub new { my $class = shift; my $id = shift; my $self = ZoneMinder::Control->new( $id ); bless( $self, $class ); srand( time() ); return $self; } our $AUTOLOAD; sub AUTOLOAD { my $self = shift; my $class = ref($self) || croak( "$self not object" ); my $name = $AUTOLOAD; $name =~ s/.*://; if ( exists($self->{$name}) ) { return( $self->{$name} ); } Fatal( "Can't access $name member of object of class $class" ); } sub open { my $self = shift; $self->loadMonitor(); use LWP::UserAgent; $self->{ua} = LWP::UserAgent->new; $self->{ua}->agent( "ZoneMinder Control Agent/".ZM_VERSION ); $self->{state} = 'open'; } sub close { my $self = shift; $self->{state} = 'closed'; } sub printMsg { my $self = shift; my $msg = shift; my $msg_len = length($msg); Debug( $msg."[".$msg_len."]" ); } sub sendCmd { my $self = shift; my $cmd = shift; my $result = undef; printMsg( $cmd, "Tx" ); my $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 ); } sub reset { my $self = shift; Debug( "Camera Reset" ); my $cmd = "/admin/ptctl.cgi?move=reset"; $self->sendCmd( $cmd ); } sub moveMap { 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 $maxver = 8; my $maxhor = 30; my $horDir = "right"; my $verDir = "up"; my $horSteps = 0; my $verSteps = 0; # Horizontal movement if ($hor < 50) { # left $horSteps = ((50 - $hor) / 50) * $maxhor; $horDir = "left"; } elsif ($hor > 50) { # right $horSteps = (($hor - 50) / 50) * $maxhor; $horDir = "right"; } # Vertical movement if ($ver < 50) { # up $verSteps = ((50 - $ver) / 50) * $maxver; $verDir = "up"; } elsif ($ver > 50) { # down $verSteps = (($ver - 50) / 50) * $maxver; $verDir = "down"; } my $v = int($verSteps); my $h = int($horSteps); Debug( "Move Map to $xcoord,$ycoord, hor=$h $horDir, ver=$v $verDir"); my $cmd = "/cgi/admin/ptctrl.cgi?action=movedegree&Cmd=$horDir&Degree=$h"; $self->sendCmd( $cmd ); $cmd = "/cgi/admin/ptctrl.cgi?action=movedegree&Cmd=$verDir&Degree=$v"; $self->sendCmd( $cmd ); } sub moveRelUp { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'tiltstep' ); Debug( "Step Up $step" ); my $cmd = "/admin/ptctl.cgi?move=up"; $self->sendCmd( $cmd ); } sub moveRelDown { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'tiltstep' ); Debug( "Step Down $step" ); my $cmd = "/admin/ptctl.cgi?move=down"; $self->sendCmd( $cmd ); } sub moveRelLeft { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'panstep' ); Debug( "Step Left $step" ); my $cmd = "/admin/ptctl.cgi?move=left"; $self->sendCmd( $cmd ); } sub moveRelRight { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'panstep' ); Debug( "Step Right $step" ); my $cmd = "/admin/ptctl.cgi?move=right"; $self->sendCmd( $cmd ); } sub presetClear { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); Debug( "Clear Preset $preset" ); #my $cmd = "/axis-cgi/com/ptz.cgi?removeserverpresetno=$preset"; #$self->sendCmd( $cmd ); } sub presetSet { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); Debug( "Set Preset $preset" ); my $cmd = "/admin/ptctl.cgi?position=" . ($preset - 1) . "&positionname=zm$preset"; $self->sendCmd( $cmd ); } sub presetGoto { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset' ); Debug( "Goto Preset $preset" ); my $cmd = "/admin/ptctl.cgi?move=p" . ($preset - 1); $self->sendCmd( $cmd ); } sub presetHome { my $self = shift; Debug( "Home Preset" ); my $cmd = "/admin/ptctl.cgi?move=h"; $self->sendCmd( $cmd ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE Brian Rudy, EbrudyNO@SPAMpraecogito.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2008 by Brian Rudy This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Control/Visca.pm000066400000000000000000000471211225361755400253020ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Visca Control Protocol Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the implementation of the Visca camera control # protocol # package ZoneMinder::Control::Visca; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Control; our @ISA = qw(ZoneMinder::Control); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Visca Control Protocol # # ========================================================================== use ZoneMinder::Logger qw(:all); use Time::HiRes qw( usleep ); use constant SYNC => 0xff; use constant COMMAND_GAP => 100000; # In ms sub new { my $class = shift; my $id = shift; my $self = ZoneMinder::Control->new( $id ); bless( $self, $class ); srand( time() ); return $self; } our $AUTOLOAD; sub AUTOLOAD { my $self = shift; my $class = ref($self) || croak( "$self not object" ); my $name = $AUTOLOAD; $name =~ s/.*://; if ( exists($self->{$name}) ) { return( $self->{$name} ); } Fatal( "Can't access $name member of object of class $class" ); } sub open { my $self = shift; $self->loadMonitor(); use Device::SerialPort; $self->{port} = new Device::SerialPort( $self->{Monitor}->{ControlDevice} ); $self->{port}->baudrate(9600); $self->{port}->databits(8); $self->{port}->parity('none'); $self->{port}->stopbits(1); $self->{port}->handshake('rts'); $self->{port}->stty_echo(0); #$self->{port}->read_const_time(250); $self->{port}->read_char_time(2); $self->{state} = 'open'; } sub close { my $self = shift; $self->{state} = 'closed'; $self->{port}->close(); } sub printMsg { if ( logDebugging() ) { my $self = shift; my $msg = shift; my $prefix = shift || ""; $prefix = $prefix.": " if ( $prefix ); my $line_length = 16; my $msg_len = int(@$msg); my $msg_str = $prefix; for ( my $i = 0; $i < $msg_len; $i++ ) { if ( ($i > 0) && ($i%$line_length == 0) && ($i != ($msg_len-1)) ) { $msg_str .= sprintf( "\n%*s", length($prefix), "" ); } $msg_str .= sprintf( "%02x ", $msg->[$i] ); } $msg_str .= "[".$msg_len."]"; Debug( $msg_str ); } } sub sendCmd { my $self = shift; my $cmd = shift; my $ack = shift || 0; my $cmp = shift || 0; my $result = undef; $self->printMsg( $cmd, "Tx" ); my $id = $cmd->[0] & 0xf; my $tx_msg = pack( "C*", @$cmd ); #print( "Tx: ".length( $tx_msg )." bytes\n" ); my $n_bytes = $self->{port}->write( $tx_msg ); if ( !$n_bytes ) { Error( "Write failed: $!" ); } if ( $n_bytes != length($tx_msg) ) { Error( "Incomplete write, only ".$n_bytes." of ".length($tx_msg)." written: $!" ); } if ( $ack ) { Debug( "Waiting for ack" ); my $max_wait = 3; my $now = time(); while( 1 ) { my ( $count, $rx_msg ) = $self->{port}->read(4); if ( $count ) { #print( "Rx1: ".$count." bytes\n" ); my @resp = unpack( "C*", $rx_msg ); $self->printMsg( \@resp, "Rx" ); if ( $resp[0] = 0x80 + ($id<<4) ) { if ( ($resp[1] & 0xf0) == 0x40 ) { my $socket = $resp[1] & 0x0f; Debug( "Got ack for socket $socket" ); $result = !undef; } else { Error( "Got bogus response" ); } last; } else { Error( "Got message for camera ".(($resp[0]-0x80)>>4) ); } } if ( (time() - $now) > $max_wait ) { last; } } } if ( $cmp ) { Debug( "Waiting for command complete" ); my $max_wait = 10; my $now = time(); while( 1 ) { #print( "Waiting\n" ); my ( $count, $rx_msg ) = $self->{port}->read(16); if ( $count ) { #print( "Rx1: ".$count." bytes\n" ); my @resp = unpack( "C*", $rx_msg ); $self->printMsg( \@resp, "Rx" ); if ( $resp[0] = 0x80 + ($id<<4) ) { if ( ($resp[1] & 0xf0) == 0x50 ) { Debug( "Got command complete" ); $result = !undef; } else { Error( "Got bogus response" ); } last; } else { Error( "Got message for camera ".(($resp[0]-0x80)>>4) ); } } if ( (time() - $now) > $max_wait ) { last; } } } return( $result ); } sub cameraOff { my $self = shift; Debug( "Camera Off\n" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x04, 0x00, 0x0, SYNC ); $self->sendCmd( \@msg ); } sub cameraOn { my $self = shift; Debug( "Camera On\n" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x04, 0x00, 0x2, SYNC ); $self->sendCmd( \@msg ); } sub stop { my $self = shift; Debug( "Stop\n" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x01, 0x00, 0x00, 0x03, 0x03, SYNC ); $self->sendCmd( \@msg ); } sub moveConUp { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'tiltspeed', 0x40 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Up" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x01, 0x00, $speed, 0x03, 0x01, SYNC ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop( $params ); } } sub moveConDown { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'tiltspeed', 0x40 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Down" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x01, 0x00, $speed, 0x03, 0x02, SYNC ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop( $params ); } } sub movConLeft { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'panspeed', 0x40 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Left" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x01, $speed, 0x00, 0x01, 0x03, SYNC ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop( $params ); } } sub moveConRight { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'panspeed', 0x40 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Right" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x01, $speed, 0x00, 0x02, 0x03, SYNC ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop( $params ); } } sub moveUpLeft { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed', 0x40 ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x40 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Up/Left" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x01, $panspeed, $tiltspeed, 0x01, 0x01, SYNC ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop( $params ); } } sub moveUpRight { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed', 0x40 ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x40 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Up/Right" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x01, $panspeed, $tiltspeed, 0x02, 0x01, SYNC ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop( $params ); } } sub moveDownLeft { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed', 0x40 ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x40 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Down/Left" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x01, $panspeed, $tiltspeed, 0x01, 0x02, SYNC ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop( $params ); } } sub moveDownRight { my $self = shift; my $params = shift; my $panspeed = $self->getParam( $params, 'panspeed', 0x40 ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x40 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Move Down/Right" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x01, $panspeed, $tiltspeed, 0x02, 0x02, SYNC ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->stop( $params ); } } sub moveRelUp { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'tiltstep' ); my $speed = $self->getParam( $params, 'tiltspeed', 0x40 ); Debug( "Step Up" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x03, 0x00, $speed, 0x00, 0x00, 0x00, 0x00, ($step&0xf000)>>12, ($step&0x0f00)>>8, ($step&0x00f0)>>4, ($step&0x000f)>>0, SYNC ); $self->sendCmd( \@msg ); } sub moveRelDown { my $self = shift; my $params = shift; my $step = -$self->getParam( $params, 'tiltstep' ); my $speed = $self->getParam( $params, 'tiltspeed', 0x40 ); Debug( "Step Down" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x03, 0x00, $speed, 0x00, 0x00, 0x00, 0x00, ($step&0xf000)>>12, ($step&0x0f00)>>8, ($step&0x00f0)>>4, ($step&0x000f)>>0, SYNC ); $self->sendCmd( \@msg ); } sub moveRelLeft { my $self = shift; my $params = shift; my $step = -$self->getParam( $params, 'panstep' ); my $speed = $self->getParam( $params, 'panspeed', 0x40 ); Debug( "Step Left" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x03, $speed, 0x00, ($step&0xf000)>>12, ($step&0x0f00)>>8, ($step&0x00f0)>>4, ($step&0x000f)>>0, 0x00, 0x00, 0x00, 0x00, SYNC ); $self->sendCmd( \@msg ); } sub moveRelRight { my $self = shift; my $params = shift; my $step = $self->getParam( $params, 'panstep' ); my $speed = $self->getParam( $params, 'panspeed', 0x40 ); Debug( "Step Right" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x03, $speed, 0x00, ($step&0xf000)>>12, ($step&0x0f00)>>8, ($step&0x00f0)>>4, ($step&0x000f)>>0, 0x00, 0x00, 0x00, 0x00, SYNC ); $self->sendCmd( \@msg ); } sub moveRelUpLeft { my $self = shift; my $params = shift; my $panstep = -$self->getParam( $params, 'panstep' ); my $tiltstep = $self->getParam( $params, 'tiltstep' ); my $panspeed = $self->getParam( $params, 'panspeed', 0x40 ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x40 ); Debug( "Step Up/Left" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x03, $panspeed, $tiltspeed, ($panstep&0xf000)>>12, ($panstep&0x0f00)>>8, ($panstep&0x00f0)>>4, ($panstep&0x000f)>>0, ($tiltstep&0xf000)>>12, ($tiltstep&0x0f00)>>8, ($tiltstep&0x00f0)>>4, ($tiltstep&0x000f)>>0, SYNC ); $self->sendCmd( \@msg ); } sub moveRelUpRight { my $self = shift; my $params = shift; my $panstep = $self->getParam( $params, 'panstep' ); my $tiltstep = $self->getParam( $params, 'tiltstep' ); my $panspeed = $self->getParam( $params, 'panspeed', 0x40 ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x40 ); Debug( "Step Up/Right" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x03, $panspeed, $tiltspeed, ($panstep&0xf000)>>12, ($panstep&0x0f00)>>8, ($panstep&0x00f0)>>4, ($panstep&0x000f)>>0, ($tiltstep&0xf000)>>12, ($tiltstep&0x0f00)>>8, ($tiltstep&0x00f0)>>4, ($tiltstep&0x000f)>>0, SYNC ); $self->sendCmd( \@msg ); } sub moveRelDownLeft { my $self = shift; my $params = shift; my $panstep = -$self->getParam( $params, 'panstep' ); my $tiltstep = -$self->getParam( $params, 'tiltstep' ); my $panspeed = $self->getParam( $params, 'panspeed', 0x40 ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x40 ); Debug( "Step Down/Left" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x03, $panspeed, $tiltspeed, ($panstep&0xf000)>>12, ($panstep&0x0f00)>>8, ($panstep&0x00f0)>>4, ($panstep&0x000f)>>0, ($tiltstep&0xf000)>>12, ($tiltstep&0x0f00)>>8, ($tiltstep&0x00f0)>>4, ($tiltstep&0x000f)>>0, SYNC ); $self->sendCmd( \@msg ); } sub moveRelDownRight { my $self = shift; my $params = shift; my $panstep = $self->getParam( $params, 'panstep' ); my $tiltstep = -$self->getParam( $params, 'tiltstep' ); my $panspeed = $self->getParam( $params, 'panspeed', 0x40 ); my $tiltspeed = $self->getParam( $params, 'tiltspeed', 0x40 ); Debug( "Step Down/Right" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x03, $panspeed, $tiltspeed, ($panstep&0xf000)>>12, ($panstep&0x0f00)>>8, ($panstep&0x00f0)>>4, ($panstep&0x000f)>>0, ($tiltstep&0xf000)>>12, ($tiltstep&0x0f00)>>8, ($tiltstep&0x00f0)>>4, ($tiltstep&0x000f)>>0, SYNC ); $self->sendCmd( \@msg ); } sub zoomConTele { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed', 0x06 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Zoom Tele" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x04, 0x07, 0x20|$speed, SYNC ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->zoomStop(); } } sub zoomWide { my $self = shift; my $params = shift; my $speed = $self->getParam( $params, 'speed', 0x06 ); my $autostop = $self->getParam( $params, 'autostop', 0 ); Debug( "Zoom Wide" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x04, 0x07, 0x30|$speed, SYNC ); $self->sendCmd( \@msg ); if( $autostop && $self->{Monitor}->{AutoStopTimeout} ) { usleep( $self->{Monitor}->{AutoStopTimeout} ); $self->zoomStop(); } } sub zoomStop { my $self = shift; my $params = shift; Debug( "Zoom Stop" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x04, 0x07, 0x00, SYNC ); $self->sendCmd( \@msg ); } sub focusConNear { my $self = shift; my $params = shift; Debug( "Focus Near" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x04, 0x08, 0x03, SYNC ); $self->sendCmd( \@msg ); } sub focusConFar { my $self = shift; my $params = shift; Debug( "Focus Far" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x04, 0x08, 0x02, SYNC ); $self->sendCmd( \@msg ); } sub focusStop { my $self = shift; my $params = shift; Debug( "Focus Stop" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x04, 0x08, 0x00, SYNC ); $self->sendCmd( \@msg ); } sub focusAuto { my $self = shift; my $params = shift; Debug( "Focus Auto" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x04, 0x38, 0x02, SYNC ); $self->sendCmd( \@msg ); } sub focusMan { my $self = shift; my $params = shift; Debug( "Focus Man" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x04, 0x38, 0x03, SYNC ); $self->sendCmd( \@msg ); } sub presetClear { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset', 1 ); Debug( "Clear Preset $preset" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x04, 0x3f, 0x00, $preset, SYNC ); $self->sendCmd( \@msg ); } sub presetSet { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset', 1 ); Debug( "Set Preset $preset" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x04, 0x3f, 0x01, $preset, SYNC ); $self->sendCmd( \@msg ); } sub presetGoto { my $self = shift; my $params = shift; my $preset = $self->getParam( $params, 'preset', 1 ); Debug( "Goto Preset $preset" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x04, 0x3f, 0x02, $preset, SYNC ); $self->sendCmd( \@msg ); } sub presetHome { my $self = shift; my $params = shift; Debug( "Home Preset" ); my @msg = ( 0x80|$self->{Monitor}->{ControlAddress}, 0x01, 0x06, 0x04, SYNC ); $self->sendCmd( \@msg ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Control/mjpgStreamer.pm000066400000000000000000000122151225361755400266710ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder mjpg STreamer Control Protocol Module, $Date: 2007-11-04 17:30:29 +0000 (Sun, 04 Nov 2007) $, $Revision: 2229 $ # Copyright (C) 2003, 2004, 2005, 2006 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the implementation of the mjpg streamer camera control # protocol # package ZoneMinder::Control::mjpgStreamer; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Control; our @ISA = qw(ZoneMinder::Control); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # mjpgSTreamer Control Protocol # # ========================================================================== use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); sub new { my $class = shift; my $id = shift; my $self = ZoneMinder::Control->new( $id ); Debug( "Camera New" ); bless( $self, $class ); srand( time() ); return $self; } our $AUTOLOAD; sub AUTOLOAD { my $self = shift; my $class = ref($self) || croak( "$self not object" ); my $name = $AUTOLOAD; Debug( "Camera AUTOLOAD" ); $name =~ s/.*://; if ( exists($self->{$name}) ) { return( $self->{$name} ); } Fatal( "Can't access $name member of object of class $class" ); } sub open { my $self = shift; $self->loadMonitor(); Debug( "Camera open" ); use LWP::UserAgent; $self->{ua} = LWP::UserAgent->new; $self->{ua}->agent( "ZoneMinder Control Agent/".ZM_VERSION ); $self->{state} = 'open'; } sub close { my $self = shift; $self->{state} = 'closed'; } sub printMsg { my $self = shift; my $msg = shift; my $msg_len = length($msg); Debug( $msg."[".$msg_len."]" ); } sub sendCmd { my $self = shift; my $cmd = shift; my $result = undef; printMsg( $cmd, "Tx" ); my $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 ); } sub Up { my $self = shift; $self->moveConUp(); } sub Down { my $self = shift; $self->moveConDown(); } sub Left { my $self = shift; $self->moveConLeft(); } sub Right { my $self = shift; $self->moveConRight(); } sub reset { my $self = shift; $self->cameraReset(); } sub cameraReset { my $self = shift; Debug( "Camera Reset" ); my $cmd = "?action=command&command=reset_pan_tilt"; $self->sendCmd( $cmd ); } sub moveConUp { my $self = shift; Debug( "Move Up" ); my $cmd = "?action=command&command=tilt_minus"; $self->sendCmd( $cmd ); } sub moveConDown { my $self = shift; Debug( "Move Down" ); my $cmd = "?action=command&command=tilt_plus"; $self->sendCmd( $cmd ); } sub moveConLeft { my $self = shift; Debug( "Move Left" ); my $cmd = "?action=command&command=pan_plus"; $self->sendCmd( $cmd ); } sub moveConRight { my $self = shift; Debug( "Move Right" ); my $cmd = "?action=command&command=pan_minus"; $self->sendCmd( $cmd ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2005 by Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Database.pm000066400000000000000000000141221225361755400243140ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Database Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the common definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::Database; use 5.006; use strict; use warnings; require Exporter; require ZoneMinder::Base; our @ISA = qw(Exporter ZoneMinder::Base); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'functions' => [ qw( zmDbConnect zmDbDisconnect zmDbGetMonitors zmDbGetMonitor zmDbGetMonitorAndControl ) ] ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw(); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Database Access # # ========================================================================== use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); use Carp; our $dbh = undef; sub zmDbConnect( ;$ ) { my $force = shift; if ( $force ) { zmDbDisconnect(); } if ( !defined( $dbh ) ) { my ( $host, $port ) = ( ZM_DB_HOST =~ /^([^:]+)(?::(.+))?$/ ); if ( defined($port) ) { $dbh = DBI->connect( "DBI:mysql:database=".ZM_DB_NAME.";host=".$host.";port=".$port, ZM_DB_USER, ZM_DB_PASS ); } else { $dbh = DBI->connect( "DBI:mysql:database=".ZM_DB_NAME.";host=".ZM_DB_HOST, ZM_DB_USER, ZM_DB_PASS ); } $dbh->trace( 0 ); } return( $dbh ); } sub zmDbDisconnect() { if ( defined( $dbh ) ) { $dbh->disconnect(); $dbh = undef; } } use constant DB_MON_ALL => 0; # All monitors use constant DB_MON_CAPT => 1; # All monitors that are capturing use constant DB_MON_ACTIVE => 2; # All monitors that are active use constant DB_MON_MOTION => 3; # All monitors that are doing motion detection use constant DB_MON_RECORD => 4; # All monitors that are doing unconditional recording use constant DB_MON_PASSIVE => 5; # All monitors that are in nodect state sub zmDbGetMonitors( ;$ ) { zmDbConnect(); my $function = shift || DB_MON_ALL; my $sql = "select * from Monitors"; if ( $function ) { if ( $function == DB_MON_CAPT ) { $sql .= " where Function >= 'Monitor'"; } elsif ( $function == DB_MON_ACTIVE ) { $sql .= " where Function > 'Monitor'"; } elsif ( $function == DB_MON_MOTION ) { $sql .= " where Function = 'Modect' or Function = 'Mocord'"; } elsif ( $function == DB_MON_RECORD ) { $sql .= " where Function = 'Record' or Function = 'Mocord'"; } elsif ( $function == DB_MON_PASSIVE ) { $sql .= " where Function = 'Nodect'"; } } my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or croak( "Can't execute '$sql': ".$sth->errstr() ); my @monitors; while( my $monitor = $sth->fetchrow_hashref() ) { push( @monitors, $monitor ); } $sth->finish(); return( \@monitors ); } sub zmDbGetMonitor( $ ) { zmDbConnect(); my $id = shift; return( undef ) if ( !defined($id) ); my $sql = "select * from Monitors where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $id ) or croak( "Can't execute '$sql': ".$sth->errstr() ); my $monitor = $sth->fetchrow_hashref(); return( $monitor ); } sub zmDbGetMonitorAndControl( $ ) { zmDbConnect(); my $id = shift; return( undef ) if ( !defined($id) ); my $sql = "select C.*,M.*,C.Protocol from Monitors as M inner join Controls as C on (M.ControlId = C.Id) where M.Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $id ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); my $monitor = $sth->fetchrow_hashref(); return( $monitor ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/General.pm000066400000000000000000000536571225361755400242050ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder General Utility Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the common definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::General; use 5.006; use strict; use warnings; require Exporter; require ZoneMinder::Base; our @ISA = qw(Exporter ZoneMinder::Base); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'functions' => [ qw( executeShellCommand getCmdFormat runCommand setFileOwner getEventPath createEventPath createEvent deleteEventFiles makePath jsonEncode jsonDecode ) ] ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw(); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # General Utility Functions # # ========================================================================== use ZoneMinder::Config qw(:all); use ZoneMinder::Logger qw(:all); use ZoneMinder::Database qw(:all); use POSIX; # For running general shell commands sub executeShellCommand( $ ) { my $command = shift; my $output = qx( $command ); my $status = $? >> 8; if ( $status || logDebugging() ) { Debug( "Command: $command\n" ); chomp( $output ); Debug( "Output: $output\n" ); } return( $status ); } sub getCmdFormat() { Debug( "Testing valid shell syntax\n" ); my ( $name ) = getpwuid( $> ); if ( $name eq ZM_WEB_USER ) { Debug( "Running as '$name', su commands not needed\n" ); return( "" ); } my $null_command = "true"; my $prefix = "sudo -u ".ZM_WEB_USER." "; my $suffix = ""; my $command = $prefix.$null_command.$suffix; Debug( "Testing \"$command\"\n" ); $command .= " > /dev/null 2>&1"; my $output = qx($command); my $status = $? >> 8; if ( !$status ) { Debug( "Test ok, using format \"$prefix$suffix\"\n" ); return( $prefix, $suffix ); } else { chomp( $output ); Debug( "Test failed, '$output'\n" ); $prefix = "su ".ZM_WEB_USER." --shell=/bin/sh --command='"; $suffix = "'"; $command = $prefix.$null_command.$suffix; Debug( "Testing \"$command\"\n" ); my $output = qx($command); my $status = $? >> 8; if ( !$status ) { Debug( "Test ok, using format \"$prefix$suffix\"\n" ); return( $prefix, $suffix ); } else { chomp( $output ); Debug( "Test failed, '$output'\n" ); $prefix = "su ".ZM_WEB_USER." -c '"; $suffix = "'"; $command = $prefix.$null_command.$suffix; Debug( "Testing \"$command\"\n" ); $output = qx($command); $status = $? >> 8; if ( !$status ) { Debug( "Test ok, using format \"$prefix$suffix\"\n" ); return( $prefix, $suffix ); } else { chomp( $output ); Debug( "Test failed, '$output'\n" ); } } } Error( "Unable to find valid 'su' syntax\n" ); exit( -1 ); } our $testedShellSyntax = 0; our ( $cmdPrefix, $cmdSuffix ); # For running ZM daemons etc sub runCommand( $ ) { if ( !$testedShellSyntax ) { # Determine the appropriate syntax for the su command ( $cmdPrefix, $cmdSuffix ) = getCmdFormat(); $testedShellSyntax = !undef; } my $command = shift; $command = ZM_PATH_BIN."/".$command; if ( $cmdPrefix ) { $command = $cmdPrefix.$command.$cmdSuffix; } Debug( "Command: $command\n" ); my $output = qx($command); my $status = $? >> 8; chomp( $output ); if ( $status || logDebugging() ) { if ( $status ) { Error( "Unable to run \"$command\", output is \"$output\"\n" ); exit( -1 ); } else { Debug( "Output: $output\n" ); } } return( $output ); } sub getEventPath( $ ) { my $event = shift; my $event_path = ""; if ( ZM_USE_DEEP_STORAGE ) { $event_path = ZM_DIR_EVENTS.'/'.$event->{MonitorId}.'/'.strftime( "%y/%m/%d/%H/%M/%S", localtime($event->{Time}) ); } else { $event_path = ZM_DIR_EVENTS.'/'.$event->{MonitorId}.'/'.$event->{Id}; } $event_path = ZM_PATH_WEB.'/'.$event_path if ( index(ZM_DIR_EVENTS,'/') != 0 ); return( $event_path ); } sub createEventPath( $ ) { # # WARNING assumes running from events directory # my $event = shift; my $eventRootPath = (ZM_DIR_EVENTS=~m|/|)?ZM_DIR_EVENTS:(ZM_PATH_WEB.'/'.ZM_DIR_EVENTS); my $eventPath = $eventRootPath.'/'.$event->{MonitorId}; if ( ZM_USE_DEEP_STORAGE ) { my @startTime = localtime( $event->{StartTime} ); my @datetimeParts = (); $datetimeParts[0] = sprintf( "%02d", $startTime[5]-100 ); $datetimeParts[1] = sprintf( "%02d", $startTime[4]+1 ); $datetimeParts[2] = sprintf( "%02d", $startTime[3] ); $datetimeParts[3] = sprintf( "%02d", $startTime[2] ); $datetimeParts[4] = sprintf( "%02d", $startTime[1] ); $datetimeParts[5] = sprintf( "%02d", $startTime[0] ); my $datePath = join('/',@datetimeParts[0..2]); my $timePath = join('/',@datetimeParts[3..5]); makePath( $datePath, $eventPath ); $eventPath .= '/'.$datePath; # Create event id symlink my $idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} ); symlink( $timePath, $idFile ) or Fatal( "Can't symlink $idFile -> $eventPath: $!" ); makePath( $timePath, $eventPath ); $eventPath .= '/'.$timePath; setFileOwner( $idFile ); # Must come after directory has been created # Create empty id tag file $idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} ); open( ID_FP, ">$idFile" ) or Fatal( "Can't open $idFile: $!" ); close( ID_FP ); setFileOwner( $idFile ); } else { makePath( $event->{Id}, $eventPath ); $eventPath .= '/'.$event->{Id}; my $idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} ); open( ID_FP, ">$idFile" ) or Fatal( "Can't open $idFile: $!" ); close( ID_FP ); setFileOwner( $idFile ); } return( $eventPath ); } use Data::Dumper; our $_setFileOwner = undef; our ( $_ownerUid, $_ownerGid ); sub _checkProcessOwner() { if ( !defined($_setFileOwner) ) { my ( $processOwner ) = getpwuid( $> ); if ( $processOwner ne ZM_WEB_USER ) { # Not running as web user, so should be root in whch case chown the temporary directory ( my $ownerName, my $ownerPass, $_ownerUid, $_ownerGid ) = getpwnam( ZM_WEB_USER ) or Fatal( "Can't get user details for web user '".ZM_WEB_USER."': $!" ); $_setFileOwner = 1; } else { $_setFileOwner = 0; } } return( $_setFileOwner ); } sub setFileOwner( $ ) { my $file = shift; if ( _checkProcessOwner() ) { chown( $_ownerUid, $_ownerGid, $file ) or Fatal( "Can't change ownership of file '$file' to '".ZM_WEB_USER.":".ZM_WEB_GROUP."': $!" ); } } our $_hasImageInfo = undef; sub _checkForImageInfo() { if ( !defined($_hasImageInfo) ) { my $result = eval { require Image::Info; Image::Info->import(); }; $_hasImageInfo = $@?0:1; } return( $_hasImageInfo ); } sub createEvent( $;$ ) { my $event = shift; Debug( "Creating event" ); #print( Dumper( $event )."\n" ); _checkForImageInfo(); my $dbh = zmDbConnect(); if ( $event->{monitor} ) { $event->{MonitorId} = $event->{monitor}->{Id}; } elsif ( $event->{MonitorId} ) { my $sql = "select * from Monitors where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $event->{MonitorId} ) or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); $event->{monitor} = $sth->fetchrow_hashref() or Fatal( "Unable to create event, can't load monitor with id '".$event->{MonitorId}."'" ); $sth->finish(); } else { Fatal( "Unable to create event, no monitor or monitor id supplied" ); } $event->{Name} = "New Event" unless( $event->{Name} ); $event->{Frames} = int(@{$event->{frames}}); $event->{TotScore} = $event->{MaxScore} = 0; my $lastTimestamp = 0.0; foreach my $frame ( @{$event->{frames}} ) { if ( !$event->{Width} ) { if ( $_hasImageInfo ) { my $imageInfo = Image::Info::image_info( $frame->{imagePath} ); if ( $imageInfo->{error} ) { Error( "Unable to extract image info from '".$frame->{imagePath}."': ".$imageInfo->{error} ); } else { ( $event->{Width}, $event->{Height} ) = Image::Info::dim( $imageInfo ); } } } $frame->{Type} = $frame->{Score}>0?'Alarm':'Normal' unless( $frame->{Type} ); $frame->{Delta} = $lastTimestamp?($frame->{TimeStamp}-$lastTimestamp):0.0; $event->{StartTime} = $frame->{TimeStamp} unless ( $event->{StartTime} ); $event->{TotScore} += $frame->{Score}; $event->{MaxScore} = $frame->{Score} if ( $frame->{Score} > $event->{MaxScore} ); $event->{AlarmFrames}++ if ( $frame->{Type} eq 'Alarm' ); $event->{EndTime} = $frame->{TimeStamp}; $lastTimestamp = $frame->{TimeStamp}; } $event->{Width} = $event->{monitor}->{Width} unless( $event->{Width} ); $event->{Height} = $event->{monitor}->{Height} unless( $event->{Height} ); $event->{AvgScore} = $event->{TotScore}/int($event->{AlarmFrames}); $event->{Length} = $event->{EndTime} - $event->{StartTime}; my %formats = ( StartTime => 'from_unixtime(?)', EndTime => 'from_unixtime(?)', ); my ( @fields, @formats, @values ); while ( my ( $field, $value ) = each( %$event ) ) { next unless $field =~ /^[A-Z]/; push( @fields, $field ); push( @formats, ($formats{$field} or '?') ); push( @values, $event->{$field} ); } my $sql = "insert into Events (".join(',',@fields).") values (".join(',',@formats).")"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); my $res = $sth->execute( @values ) or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); $event->{Id} = $dbh->{mysql_insertid}; Info( "Created event ".$event->{Id} ); if ( $event->{EndTime} ) { $event->{Name} = $event->{monitor}->{EventPrefix}.$event->{Id} if ( $event->{Name} eq 'New Event' ); my $sql = "update Events set Name = ? where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $event->{Name}, $event->{Id} ) or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); } my $eventPath = createEventPath( $event ); my %frameFormats = ( TimeStamp => 'from_unixtime(?)', ); my $frameId = 1; foreach my $frame ( @{$event->{frames}} ) { $frame->{EventId} = $event->{Id}; $frame->{FrameId} = $frameId++; my ( @fields, @formats, @values ); while ( my ( $field, $value ) = each( %$frame ) ) { next unless $field =~ /^[A-Z]/; push( @fields, $field ); push( @formats, ($frameFormats{$field} or '?') ); push( @values, $frame->{$field} ); } my $sql = "insert into Frames (".join(',',@fields).") values (".join(',',@formats).")"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); my $res = $sth->execute( @values ) or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); #$frame->{FrameId} = $dbh->{mysql_insertid}; if ( $frame->{imagePath} ) { $frame->{capturePath} = sprintf( "%s/%0".ZM_EVENT_IMAGE_DIGITS."d-capture.jpg", $eventPath, $frame->{FrameId} ); rename( $frame->{imagePath}, $frame->{capturePath} ) or Fatal( "Can't copy ".$frame->{imagePath}." to ".$frame->{capturePath}.": $!" ); setFileOwner( $frame->{capturePath} ); if ( 0 && ZM_CREATE_ANALYSIS_IMAGES ) { $frame->{analysePath} = sprintf( "%s/%0".ZM_EVENT_IMAGE_DIGITS."d-analyse.jpg", $eventPath, $frame->{FrameId} ); link( $frame->{capturePath}, $frame->{analysePath} ) or Fatal( "Can't link ".$frame->{capturePath}." to ".$frame->{analysePath}.": $!" ); setFileOwner( $frame->{analysePath} ); } } } } sub addEventImage( $$ ) { my $event = shift; my $frame = shift; # TBD } sub updateEvent( $ ) { my $event = shift; if ( !$event->{EventId} ) { Error( "Unable to update event, no event id supplied" ); return( 0 ); } my $dbh = zmDbConnect(); $event->{Name} = $event->{monitor}->{EventPrefix}.$event->{Id} if ( $event->{Name} eq 'New Event' ); my %formats = ( StartTime => 'from_unixtime(?)', EndTime => 'from_unixtime(?)', ); my ( @values, @sets ); while ( my ( $field, $value ) = each( %$event ) ) { next if ( $field eq 'Id' ); push( @values, $event->{$field} ); push( @sets, $field." = ".($formats{$field} or '?') ); } my $sql = "update Events set ".join(',',@sets)." where Id = ?"; push( @values, $event->{Id} ); my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); my $res = $sth->execute( @values ) or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); } sub deleteEventFiles( $;$ ) { # # WARNING assumes running from events directory # my $event_id = shift; my $monitor_id = shift; $monitor_id = '*' if ( !defined($monitor_id) ); if ( ZM_USE_DEEP_STORAGE ) { my $link_path = $monitor_id."/*/*/*/.".$event_id; #Debug( "LP1:$link_path" ); my @links = glob($link_path); #Debug( "L:".$links[0].": $!" ); if ( @links ) { ( $link_path ) = ( $links[0] =~ /^(.*)$/ ); # De-taint #Debug( "LP2:$link_path" ); ( my $day_path = $link_path ) =~ s/\.\d+//; #Debug( "DP:$day_path" ); my $event_path = $day_path.readlink( $link_path ); ( $event_path ) = ( $event_path =~ /^(.*)$/ ); # De-taint #Debug( "EP:$event_path" ); my $command = "/bin/rm -rf ".$event_path; #Debug( "C:$command" ); executeShellCommand( $command ); unlink( $link_path ) or Error( "Unable to unlink '$link_path': $!" ); my @path_parts = split( /\//, $event_path ); for ( my $i = int(@path_parts)-2; $i >= 1; $i-- ) { my $delete_path = join( '/', @path_parts[0..$i] ); #Debug( "DP$i:$delete_path" ); my @has_files = glob( $delete_path."/*" ); #Debug( "HF1:".$has_files[0] ) if ( @has_files ); last if ( @has_files ); @has_files = glob( $delete_path."/.[0-9]*" ); #Debug( "HF2:".$has_files[0] ) if ( @has_files ); last if ( @has_files ); my $command = "/bin/rm -rf ".$delete_path; executeShellCommand( $command ); } } } else { my $command = "/bin/rm -rf $monitor_id/$event_id"; executeShellCommand( $command ); } } sub makePath( $;$ ) { my $path = shift; my $root = shift; $root = (( $path =~ m|^/| )?'':'.' ) unless( $root ); Debug( "Creating path '$path' in $root'\n" ); my @parts = split( '/', $path ); my $fullPath = $root; foreach my $dir ( @parts ) { $fullPath .= '/'.$dir; if ( !-d $fullPath ) { if ( -e $fullPath ) { Fatal( "Can't create '$fullPath', already exists as non directory" ); } else { Debug( "Creating '$fullPath'\n" ); mkdir( $fullPath, 0755 ) or Fatal( "Can't mkdir '$fullPath': $!" ); setFileOwner( $fullPath ); } } } return( $fullPath ); } our $testedJSON = 0; our $hasJSONAny = 0; sub _testJSON { return if ( $testedJSON ); my $result = eval { require JSON::Any; JSON::Any->import(); }; $testedJSON = 1; $hasJSONAny = 1 if ( $result ); } sub _getJSONType( $ ) { my $value = shift; return( 'null' ) unless( defined($value) ); return( 'integer' ) if ( $value =~ /^\d+$/ ); return( 'double' ) if ( $value =~ /^\d+$/ ); return( 'hash' ) if ( ref($value) eq 'HASH' ); return( 'array' ) if ( ref($value) eq 'ARRAY' ); return( 'string' ); } sub jsonEncode( $ ); sub jsonEncode( $ ) { my $value = shift; _testJSON(); if ( $hasJSONAny ) { my $string = eval { JSON::Any->objToJson( $value ) }; Fatal( "Unable to encode object to JSON: $@" ) unless( $string ); return( $string ); } my $type = _getJSONType($value); if ( $type eq 'integer' || $type eq 'double' ) { return( $value ); } elsif ( $type eq 'boolean' ) { return( $value?'true':'false' ); } elsif ( $type eq 'string' ) { $value =~ s|(["\\/])|\\$1|g; $value =~ s|\r?\n|\n|g; return( '"'.$value.'"' ); } elsif ( $type eq 'null' ) { return( 'null' ); } elsif ( $type eq 'array' ) { return( '['.join( ',', map { jsonEncode( $_ ) } @$value ).']' ); } elsif ( $type eq 'hash' ) { my $result = '{'; while ( my ( $subKey=>$subValue ) = each( %$value ) ) { $result .= ',' if ( $result ne '{' ); $result .= '"'.$subKey.'":'.jsonEncode( $subValue ); } return( $result.'}' ); } else { Fatal( "Unexpected type '$type'" ); } } sub jsonDecode( $ ) { my $value = shift; _testJSON(); if ( $hasJSONAny ) { my $object = eval { JSON::Any->jsonToObj( $value ) }; Fatal( "Unable to decode JSON string '$value': $@" ) unless( $object ); return( $object ); } my $comment = 0; my $unescape = 0; my $out = ''; my @chars = split( //, $value ); for ( my $i = 0; $i < @chars; $i++ ) { if ( !$comment ) { if ( $chars[$i] eq ':' ) { $out .= '=>'; } else { $out .= $chars[$i]; } } elsif ( !$unescape ) { if ( $chars[$i] eq '\\' ) { $unescape = 1; } else { $out .= $chars[$i]; } } else { if ( $chars[$i] ne '/' ) { $out .= '\\'; } $out .= $chars[$i]; $unescape = 0; } if ( $chars[$i] eq '"' ) { $comment = !$comment; } } $out =~ s/=>true/=>1/g; $out =~ s/=>false/=>0/g; $out =~ s/=>null/=>undef/g; $out =~ s/`/'/g; $out =~ s/qx/qq/g; ( $out ) = $out =~ m/^({.+})$/; # Detaint and check it's a valid object syntax my $result = eval $out; Fatal( $@ ) if ( $@ ); return( $result ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm000066400000000000000000000572451225361755400240440ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Logger Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the debug definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::Logger; use 5.006; use strict; use warnings; require Exporter; require ZoneMinder::Base; our @ISA = qw(Exporter ZoneMinder::Base); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'constants' => [ qw( DEBUG INFO WARNING ERROR FATAL PANIC NOLOG ) ], 'functions' => [ qw( logInit logReinit logTerm logSetSignal logClearSignal logDebugging logLevel logTermLevel logDatabaseLevel logFileLevel logSyslogLevel Mark Dump Debug Info Warning Error Fatal Panic ) ] ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw(); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Logger Facilities # # ========================================================================== use ZoneMinder::Config qw(:all); use DBI; use Carp; use POSIX; use IO::Handle; use Data::Dumper; use Time::HiRes qw/gettimeofday/; use Sys::Syslog; use constant { DEBUG => 1, INFO => 0, WARNING => -1, ERROR => -2, FATAL => -3, PANIC => -4, NOLOG => -5 }; our %codes = ( &DEBUG => "DBG", &INFO => "INF", &WARNING => "WAR", &ERROR => "ERR", &FATAL => "FAT", &PANIC => "PNC", &NOLOG => "OFF" ); our %priorities = ( &DEBUG => "debug", &INFO => "info", &WARNING => "warning", &ERROR => "err", &FATAL => "err", &PANIC => "err" ); our $logger; sub new { my $class = shift; my $this = {}; $this->{initialised} = undef; #$this->{id} = "zmundef"; ( $this->{id} ) = $0 =~ m|^(?:.*/)?([^/]+?)(?:\.[^/.]+)?$|; $this->{idRoot} = $this->{id}; $this->{idArgs} = ""; $this->{level} = INFO; $this->{termLevel} = NOLOG; $this->{databaseLevel} = NOLOG; $this->{fileLevel} = NOLOG; $this->{syslogLevel} = NOLOG; $this->{effectiveLevel} = INFO; $this->{autoFlush} = 1; $this->{hasTerm} = -t STDERR; ( $this->{fileName} = $0 ) =~ s|^.*/||; $this->{logPath} = ZM_PATH_LOGS; $this->{logFile} = $this->{logPath}."/".$this->{id}.".log"; $this->{trace} = 0; bless( $this, $class ); return $this; } sub BEGIN { # Fake the config variables that are used in case they are not defined yet # Only really necessary to support upgrade from previous version if ( !eval('defined(ZM_LOG_DEBUG)') ) { no strict 'subs'; no strict 'refs'; my %dbgConfig = ( ZM_LOG_LEVEL_DATABASE => 0, ZM_LOG_LEVEL_FILE => 0, ZM_LOG_LEVEL_SYSLOG => 0, ZM_LOG_DEBUG => 0, ZM_LOG_DEBUG_TARGET => "", ZM_LOG_DEBUG_LEVEL => 1, ZM_LOG_DEBUG_FILE => "" ); while ( my ( $name, $value ) = each( %dbgConfig ) ) { *{$name} = sub { $value }; } use strict 'subs'; use strict 'refs'; } } sub DESTROY { my $this = shift; $this->terminate(); } sub initialise( @ ) { my $this = shift; my %options = @_; $this->{id} = $options{id} if ( defined($options{id}) ); $this->{logPath} = $options{logPath} if ( defined($options{logPath}) ); my $tempLogFile; $tempLogFile = $this->{logPath}."/".$this->{id}.".log"; $tempLogFile = $options{logFile} if ( defined($options{logFile}) ); if ( my $logFile = $this->getTargettedEnv('LOG_FILE') ) { $tempLogFile = $logFile; } my $tempLevel = INFO; my $tempTermLevel = $this->{termLevel}; my $tempDatabaseLevel = $this->{databaseLevel}; my $tempFileLevel = $this->{fileLevel}; my $tempSyslogLevel = $this->{syslogLevel}; $tempTermLevel = $options{termLevel} if ( defined($options{termLevel}) ); if ( defined($options{databaseLevel}) ) { $tempDatabaseLevel = $options{databaseLevel}; } else { $tempDatabaseLevel = ZM_LOG_LEVEL_DATABASE; } if ( defined($options{fileLevel}) ) { $tempFileLevel = $options{fileLevel}; } else { $tempFileLevel = ZM_LOG_LEVEL_FILE; } if ( defined($options{syslogLevel}) ) { $tempSyslogLevel = $options{syslogLevel}; } else { $tempSyslogLevel = ZM_LOG_LEVEL_SYSLOG; } if ( defined($ENV{'LOG_PRINT'}) ) { $tempTermLevel = $ENV{'LOG_PRINT'}? DEBUG : NOLOG; } my $level; $tempLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL')) ); $tempTermLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_TERM')) ); $tempDatabaseLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_DATABASE')) ); $tempFileLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_FILE')) ); $tempSyslogLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_SYSLOG')) ); if ( ZM_LOG_DEBUG ) { foreach my $target ( split( /\|/, ZM_LOG_DEBUG_TARGET ) ) { if ( $target eq $this->{id} || $target eq "_".$this->{id} || $target eq $this->{idRoot} || $target eq "_".$this->{idRoot} || $target eq "" ) { if ( ZM_LOG_DEBUG_LEVEL > NOLOG ) { $tempLevel = $this->limit( ZM_LOG_DEBUG_LEVEL ); if ( ZM_LOG_DEBUG_FILE ne "" ) { $tempLogFile = ZM_LOG_DEBUG_FILE; $tempFileLevel = $tempLevel; } } } } } $this->logFile( $tempLogFile ); $this->termLevel( $tempTermLevel ); $this->databaseLevel( $tempDatabaseLevel ); $this->fileLevel( $tempFileLevel ); $this->syslogLevel( $tempSyslogLevel ); $this->level( $tempLevel ); $this->{trace} = $options{trace} if ( defined($options{trace}) ); $this->{autoFlush} = $ENV{'LOG_FLUSH'}?1:0 if ( defined($ENV{'LOG_FLUSH'}) ); $this->{initialised} = !undef; Debug( "LogOpts: level=".$codes{$this->{level}}."/".$codes{$this->{effectiveLevel}}.", screen=".$codes{$this->{termLevel}}.", database=".$codes{$this->{databaseLevel}}.", logfile=".$codes{$this->{fileLevel}}."->".$this->{logFile}.", syslog=".$codes{$this->{syslogLevel}} ); } sub terminate() { my $this = shift; return unless ( $this->{initialised} ); $this->syslogLevel( NOLOG ); $this->fileLevel( NOLOG ); $this->databaseLevel( NOLOG ); $this->termLevel( NOLOG ); } sub reinitialise() { my $this = shift; return unless ( $this->{initialised} ); # Bit of a nasty hack to reopen connections to log files and the DB my $syslogLevel = $this->syslogLevel(); $this->syslogLevel( NOLOG ); my $logfileLevel = $this->fileLevel(); $this->fileLevel( NOLOG ); my $databaseLevel = $this->databaseLevel(); $this->databaseLevel( NOLOG ); my $screenLevel = $this->termLevel(); $this->termLevel( NOLOG ); $this->syslogLevel( $syslogLevel ) if ( $syslogLevel > NOLOG ); $this->fileLevel( $logfileLevel ) if ( $logfileLevel > NOLOG ); $this->databaseLevel( $databaseLevel ) if ( $databaseLevel > NOLOG ); $this->databaseLevel( $databaseLevel ) if ( $databaseLevel > NOLOG ); } sub limit( $ ) { my $this = shift; my $level = shift; return( DEBUG ) if ( $level > DEBUG ); return( NOLOG ) if ( $level < NOLOG ); return( $level ); } sub getTargettedEnv( $ ) { my $this = shift; my $name = shift; my $envName = $name."_".$this->{id}; my $value; $value = $ENV{$envName} if ( defined($ENV{$envName}) ); if ( !defined($value) && $this->{id} ne $this->{idRoot} ) { $envName = $name."_".$this->{idRoot}; $value = $ENV{$envName} if ( defined($ENV{$envName}) ); } if ( !defined($value) ) { $value = $ENV{$name} if ( defined($ENV{$name}) ); } if ( defined($value) ) { ( $value ) = $value =~ m/(.*)/; } return( $value ); } sub fetch() { if ( !$logger ) { $logger = ZoneMinder::Logger->new(); $logger->initialise( 'syslogLevel'=>INFO, 'databaseLevel'=>INFO ); } return( $logger ); } sub id( ;$ ) { my $this = shift; my $id = shift; if ( defined($id) && $this->{id} ne $id ) { # Remove whitespace $id =~ s/\S//g; # Replace non-alphanum with underscore $id =~ s/[^a-zA-Z_]/_/g; if ( $this->{id} ne $id ) { $this->{id} = $this->{idRoot} = $id; if ( $id =~ /^([^_]+)_(.+)$/ ) { $this->{idRoot} = $1; $this->{idArgs} = $2; } } } return( $this->{id} ); } sub level( ;$ ) { my $this = shift; my $level = shift; if ( defined($level) ) { $this->{level} = $this->limit( $level ); $this->{effectiveLevel} = NOLOG; $this->{effectiveLevel} = $this->{termLevel} if ( $this->{termLevel} > $this->{effectiveLevel} ); $this->{effectiveLevel} = $this->{databaseLevel} if ( $this->{databaseLevel} > $this->{effectiveLevel} ); $this->{effectiveLevel} = $this->{fileLevel} if ( $this->{fileLevel} > $this->{effectiveLevel} ); $this->{effectiveLevel} = $this->{syslogLevel} if ( $this->{syslogLevel} > $this->{level} ); $this->{effectiveLevel} = $this->{level} if ( $this->{effectiveLevel} > $this->{level} ); } return( $this->{level} ); } sub debugOn() { my $this = shift; return( $this->{effectiveLevel} >= DEBUG ); } sub trace( ;$ ) { my $this = shift; $this->{trace} = $_[0] if ( @_ ); return( $this->{trace} ); } sub termLevel( ;$ ) { my $this = shift; my $termLevel = shift; if ( defined($termLevel) ) { $termLevel = NOLOG if ( !$this->{hasTerm} ); $termLevel = $this->limit( $termLevel ); if ( $this->{termLevel} != $termLevel ) { $this->{termLevel} = $termLevel; } } return( $this->{termLevel} ); } sub databaseLevel( ;$ ) { my $this = shift; my $databaseLevel = shift; if ( defined($databaseLevel) ) { $databaseLevel = $this->limit( $databaseLevel ); if ( $this->{databaseLevel} != $databaseLevel ) { if ( $databaseLevel > NOLOG && $this->{databaseLevel} <= NOLOG ) { if ( !$this->{dbh} ) { my ( $host, $port ) = ( ZM_DB_HOST =~ /^([^:]+)(?::(.+))?$/ ); if ( defined($port) ) { $this->{dbh} = DBI->connect( "DBI:mysql:database=".ZM_DB_NAME.";host=".$host.";port=".$port, ZM_DB_USER, ZM_DB_PASS ); } else { $this->{dbh} = DBI->connect( "DBI:mysql:database=".ZM_DB_NAME.";host=".ZM_DB_HOST, ZM_DB_USER, ZM_DB_PASS ); } if ( !$this->{dbh} ) { $databaseLevel = NOLOG; Error( "Unable to write log entries to DB, can't connect to database '".ZM_DB_NAME."' on host '".ZM_DB_HOST."'" ); } else { $this->{dbh}->{AutoCommit} = 1; Fatal( "Can't set AutoCommit on in database connection" ) unless( $this->{dbh}->{AutoCommit} ); $this->{dbh}->{mysql_auto_reconnect} = 1; Fatal( "Can't set mysql_auto_reconnect on in database connection" ) unless( $this->{dbh}->{mysql_auto_reconnect} ); $this->{dbh}->trace( 0 ); } } } elsif ( $databaseLevel <= NOLOG && $this->{databaseLevel} > NOLOG ) { if ( $this->{dbh} ) { $this->{dbh}->disconnect(); undef($this->{dbh}); } } $this->{databaseLevel} = $databaseLevel; } } return( $this->{databaseLevel} ); } sub fileLevel( ;$ ) { my $this = shift; my $fileLevel = shift; if ( defined($fileLevel) ) { $fileLevel = $this->limit($fileLevel); if ( $this->{fileLevel} != $fileLevel ) { $this->closeFile() if ( $this->{fileLevel} > NOLOG ); $this->{fileLevel} = $fileLevel; $this->openFile() if ( $this->{fileLevel} > NOLOG ); } } return( $this->{fileLevel} ); } sub syslogLevel( ;$ ) { my $this = shift; my $syslogLevel = shift; if ( defined($syslogLevel) ) { $syslogLevel = $this->limit($syslogLevel); if ( $this->{syslogLevel} != $syslogLevel ) { $this->closeSyslog() if ( $syslogLevel <= NOLOG && $this->{syslogLevel} > NOLOG ); $this->openSyslog() if ( $syslogLevel > NOLOG && $this->{syslogLevel} <= NOLOG ); $this->{syslogLevel} = $syslogLevel; } } return( $this->{syslogLevel} ); } sub openSyslog() { my $this = shift; openlog( $this->{id}, "pid", "local1" ); } sub closeSyslog() { my $this = shift; #closelog(); } sub logFile( $ ) { my $this = shift; my $logFile = shift; if ( $logFile =~ /^(.+)\+$/ ) { $this->{logFile} = $1.'.'.$$; } else { $this->{logFile} = $logFile; } } sub openFile() { my $this = shift; if ( open( LOGFILE, ">>".$this->{logFile} ) ) { LOGFILE->autoflush() if ( $this->{autoFlush} ); my $webUid = (getpwnam( ZM_WEB_USER ))[2]; my $webGid = (getgrnam( ZM_WEB_GROUP ))[2]; if ( $> == 0 ) { chown( $webUid, $webGid, $this->{logFile} ) or Fatal( "Can't change permissions on log file '".$this->{logFile}."': $!" ) } } else { $this->fileLevel( NOLOG ); Error( "Can't open log file '".$this->{logFile}."': $!" ); } } sub closeFile() { my $this = shift; close( LOGFILE ) if ( fileno(LOGFILE) ); } sub logPrint( $;$ ) { my $this = shift; my $level = shift; my $string = shift; if ( $level <= $this->{effectiveLevel} ) { $string =~ s/[\r\n]+$//g; my $code = $codes{$level}; my ($seconds, $microseconds) = gettimeofday(); my $message = sprintf( "%s.%06d %s[%d].%s [%s]", strftime( "%x %H:%M:%S", localtime( $seconds ) ), $microseconds, $this->{id}, $$, $code, $string ); if ( $this->{trace} ) { $message = Carp::shortmess( $message ); } else { $message = $message."\n"; } syslog( $priorities{$level}, $code." [%s]", $string ) if ( $level <= $this->{syslogLevel} ); print( LOGFILE $message ) if ( $level <= $this->{fileLevel} ); if ( $level <= $this->{databaseLevel} ) { my $sql = "insert into Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) values ( ?, ?, ?, ?, ?, ?, ?, NULL )"; $this->{sth} = $this->{dbh}->prepare_cached( $sql ); if ( !$this->{sth} ) { $this->{databaseLevel} = NOLOG; Fatal( "Can't prepare log entry '$sql': ".$this->{dbh}->errstr() ); } my $res = $this->{sth}->execute( $seconds+($microseconds/1000000.0), $this->{id}, $$, $level, $code, $string, $this->{fileName} ); if ( !$res ) { $this->{databaseLevel} = NOLOG; Fatal( "Can't execute log entry '$sql': ".$this->{sth}->errstr() ); } } print( STDERR $message ) if ( $level <= $this->{termLevel} ); } } sub logInit( ;@ ) { my %options = @_ ? @_ : (); $logger = ZoneMinder::Logger->new() if ( !$logger ); $logger->initialise( %options ); } sub logReinit() { fetch()->reinitialise(); } sub logTerm { return unless ( $logger ); $logger->terminate(); $logger = undef; } sub logHupHandler() { my $savedErrno = $!; return unless( $logger ); fetch()->reinitialise(); logSetSignal(); $! = $savedErrno; } sub logSetSignal() { $SIG{HUP} = \&logHupHandler; } sub logClearSignal() { $SIG{HUP} = 'DEFAULT'; } sub logLevel( ;$ ) { return( fetch()->level( @_ ) ); } sub logDebugging() { return( fetch()->debugOn() ); } sub logTermLevel( ;$ ) { return( fetch()->termLevel( @_ ) ); } sub logDatabaseLevel( ;$ ) { return( fetch()->databaseLevel( @_ ) ); } sub logFileLevel( ;$ ) { return( fetch()->fileLevel( @_ ) ); } sub logSyslogLevel( ;$ ) { return( fetch()->syslogLevel( @_ ) ); } sub Mark( ;$$ ) { my $level = shift; $level = DEBUG unless( defined($level) ); my $tag = "Mark"; fetch()->logPrint( $level, $tag ); } sub Dump( \$;$ ) { my $var = shift; my $label = shift; $label = "VAR" unless( defined($label) ); fetch()->logPrint( DEBUG, Data::Dumper->Dump( [ $var ], [ $label ] ) ); } sub Debug( @ ) { fetch()->logPrint( DEBUG, @_ ); } sub Info( @ ) { fetch()->logPrint( INFO, @_ ); } sub Warning( @ ) { fetch()->logPrint( WARNING, @_ ); } sub Error( @ ) { fetch()->logPrint( ERROR, @_ ); } sub Fatal( @ ) { fetch()->logPrint( FATAL, @_ ); exit( -1 ); } sub Panic( @ ) { fetch()->logPrint( PANIC, @_ ); confess( $_[0] ); } 1; __END__ =head1 NAME ZoneMinder::Logger - ZoneMinder Logger module =head1 SYNOPSIS use ZoneMinder::Logger; use ZoneMinder::Logger qw(:all); logInit( "myproc", DEBUG ); Debug( "This is what is happening" ); Info( "Something interesting is happening" ); Warning( "Something might be going wrong." ); Error( "Something has gone wrong!!" ); Fatal( "Something has gone badly wrong, gotta stop!!" ); Panic( "Something fundamental has gone wrong, die with stack trace ); =head1 DESCRIPTION The ZoneMinder:Logger module contains the common debug and error reporting routines used by the ZoneMinder scripts. To use debug in your scripts you need to include this module, and call logInit. Thereafter you can sprinkle Debug or Error calls etc throughout the code safe in the knowledge that they will be reported to your error log, and possibly the syslogger, in a meaningful and consistent format. Debug is discussed in terms of levels where 1 and above (currently only 1 for scripts) is considered debug, 0 is considered as informational, -1 is a warning, -2 is an error and -3 is a fatal error or panic. Where levels are mentioned below as thresholds the value given and anything with a lower level (ie. more serious) will be included. =head1 METHODS =over 4 =item logInit ( $id, %options ); Initialises the debug and prepares the logging for forthcoming operations. If not called explicitly it will be called by the first debug call in your script, but with default (and probably meaningless) options. The only compulsory arguments are $id which must be a string that will identify debug coming from this script in mixed logs. Other options may be provided as below, Option Default Description --------- --------- ----------- level INFO The initial debug level which defines which statements are output and which are ignored trace 0 Whether to use the Carp::shortmess format in debug statements to identify where the debug was emitted from termLevel NOLOG At what level debug is written to terminal standard error, 0 is no, 1 is yes, 2 is write only if terminal databaseLevel INFO At what level debug is written to the Log table in the database; fileLevel NOLOG At what level debug is written to a log file of the format of .log in the standard log directory. syslogLevel INFO At what level debug is written to syslog. To disable any of these action entirely set to NOLOG =item logTerm (); Used to end the debug session and close any logs etc. Not usually necessary. =item $id = logId ( [$id] ); =item $level = logLevel ( [$level] ); =item $trace = logTrace ( [$trace] ); =item $level = logLevel ( [$level] ); =item $termLevel = logTermLevel ( [$termLevel] ); =item $databaseLevel = logDatabaseLevel ( [$databaseLevel] ); =item $fileLevel = logFileLevel ( [$fileLevel] ); =item $syslogLevel = logSyslogLevel ( [$syslogLevel] ); These methods can be used to get and set the current settings as defined in logInit. =item Debug( $string ); This method will output a debug message if the current debug level permits it, otherwise does nothing. This message will be tagged with the DBG string in the logs. =item Info( $string ); This method will output an informational message if the current debug level permits it, otherwise does nothing. This message will be tagged with the INF string in the logs. =item Warning( $string ); This method will output a warning message if the current debug level permits it, otherwise does nothing. This message will be tagged with the WAR string in the logs. =item Error( $string ); This method will output an error message if the current debug level permits it, otherwise does nothing. This message will be tagged with the ERR string in the logs. =item Fatal( $string ); This method will output a fatal error message and then die if the current debug level permits it, otherwise does nothing. This message will be tagged with the FAT string in the logs. =item Panic( $string ); This method will output a panic error message and then die with a stack trace if the current debug level permits it, otherwise does nothing. This message will be tagged with the PNC string in the logs. =head2 EXPORT None by default. The :constants tag will export the debug constants which define the various levels of debug The :variables tag will export variables containing the current debug id and level The :functions tag will export the debug functions. This or :all is what you would normally use. The :all tag will export all above symbols. =head1 SEE ALSO Carp Sys::Syslog The ZoneMinder README file Troubleshooting section for an extended discussion on the use and configuration of syslog with ZoneMinder. http://www.zoneminder.com =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in000066400000000000000000000672541225361755400245030ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Memory Access Module, $Date: 2008-02-25 10:13:13 +0000 (Mon, 25 Feb 2008) $, $Revision: 2323 $ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the common definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::Memory; use 5.006; use strict; use warnings; require Exporter; require ZoneMinder::Base; our @ISA = qw(Exporter ZoneMinder::Base); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'constants' => [ qw( STATE_IDLE STATE_PREALARM STATE_ALARM STATE_ALERT STATE_TAPE ACTION_GET ACTION_SET ACTION_RELOAD ACTION_SUSPEND ACTION_RESUME TRIGGER_CANCEL TRIGGER_ON TRIGGER_OFF ) ], 'functions' => [ qw( zmMemVerify zmMemInvalidate zmMemRead zmMemWrite zmMemTidy zmGetMonitorState zmGetAlarmLocation zmIsAlarmed zmInAlarm zmHasAlarmed zmGetLastEvent zmGetLastWriteTime zmGetLastReadTime zmMonitorEnable zmMonitorDisable zmMonitorSuspend zmMonitorResume zmTriggerEventOn zmTriggerEventOff zmTriggerEventCancel zmTriggerShowtext ) ], ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw(); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Shared Memory Facilities # # ========================================================================== use ZoneMinder::Config qw(:all); use ZoneMinder::Logger qw(:all); use constant STATE_IDLE => 0; use constant STATE_PREALARM => 1; use constant STATE_ALARM => 2; use constant STATE_ALERT => 3; use constant STATE_TAPE => 4; use constant ACTION_GET => 1; use constant ACTION_SET => 2; use constant ACTION_RELOAD => 4; use constant ACTION_SUSPEND => 16; use constant ACTION_RESUME => 32; use constant TRIGGER_CANCEL => 0; use constant TRIGGER_ON => 1; use constant TRIGGER_OFF => 2; use Storable qw( freeze thaw ); if ( "@ENABLE_MMAP@" eq 'yes' ) # 'yes' if memory is mmapped { require ZoneMinder::Memory::Mapped; ZoneMinder::Memory::Mapped->import(); } else { require ZoneMinder::Memory::Shared; ZoneMinder::Memory::Shared->import(); } # Native architecture our $arch = int(3.2*length(~0)); our $native = $arch/8; our $mem_seq = 0; our $mem_data = { "shared_data" => { "type"=>"SharedData", "seq"=>$mem_seq++, "contents"=> { "size" => { "type"=>"uint32", "seq"=>$mem_seq++ }, "last_write_index" => { "type"=>"uint32", "seq"=>$mem_seq++ }, "last_read_index" => { "type"=>"uint32", "seq"=>$mem_seq++ }, "state" => { "type"=>"uint32", "seq"=>$mem_seq++ }, "last_event" => { "type"=>"uint32", "seq"=>$mem_seq++ }, "action" => { "type"=>"uint32", "seq"=>$mem_seq++ }, "brightness" => { "type"=>"int32", "seq"=>$mem_seq++ }, "hue" => { "type"=>"int32", "seq"=>$mem_seq++ }, "colour" => { "type"=>"int32", "seq"=>$mem_seq++ }, "contrast" => { "type"=>"int32", "seq"=>$mem_seq++ }, "alarm_x" => { "type"=>"int32", "seq"=>$mem_seq++ }, "alarm_y" => { "type"=>"int32", "seq"=>$mem_seq++ }, "valid" => { "type"=>"uint8", "seq"=>$mem_seq++ }, "active" => { "type"=>"uint8", "seq"=>$mem_seq++ }, "signal" => { "type"=>"uint8", "seq"=>$mem_seq++ }, "format" => { "type"=>"uint8", "seq"=>$mem_seq++ }, "imagesize" => { "type"=>"uint32", "seq"=>$mem_seq++ }, "epadding1" => { "type"=>"uint32", "seq"=>$mem_seq++ }, "epadding2" => { "type"=>"uint32", "seq"=>$mem_seq++ }, "last_write_time" => { "type"=>"time_t64", "seq"=>$mem_seq++ }, "last_read_time" => { "type"=>"time_t64", "seq"=>$mem_seq++ }, "control_state" => { "type"=>"uint8[256]", "seq"=>$mem_seq++ }, } }, "trigger_data" => { "type"=>"TriggerData", "seq"=>$mem_seq++, "contents"=> { "size" => { "type"=>"uint32", "seq"=>$mem_seq++ }, "trigger_state" => { "type"=>"uint32", "seq"=>$mem_seq++ }, "trigger_score" => { "type"=>"uint32", "seq"=>$mem_seq++ }, "padding" => { "type"=>"uint32", "seq"=>$mem_seq++ }, "trigger_cause" => { "type"=>"int8[32]", "seq"=>$mem_seq++ }, "trigger_text" => { "type"=>"int8[256]", "seq"=>$mem_seq++ }, "trigger_showtext" => { "type"=>"int8[256]", "seq"=>$mem_seq++ }, } }, "end" => { "seq"=>$mem_seq++, "size"=> 0 } }; our $mem_size = 0; our $mem_verified = {}; sub zmMemInit { my $offset = 0; foreach my $section_data ( sort { $a->{seq} <=> $b->{seq} } values( %$mem_data ) ) { $section_data->{offset} = $offset; $section_data->{align} = 0; if ( $section_data->{align} > 1 ) { my $rem = $offset % $section_data->{align}; if ( $rem > 0 ) { $offset += ($section_data->{align} - $rem); } } foreach my $member_data ( sort { $a->{seq} <=> $b->{seq} } values( %{$section_data->{contents}} ) ) { if ( $member_data->{type} eq "long" || $member_data->{type} eq "ulong" || $member_data->{type} eq "size_t") { $member_data->{size} = $member_data->{align} = $native; } elsif( $member_data->{type} eq "int64" || $member_data->{type} eq "uint64" || $member_data->{type} eq "time_t64") { $member_data->{size} = $member_data->{align} = 8; } elsif ( $member_data->{type} eq "int32" || $member_data->{type} eq "uint32" || $member_data->{type} eq "bool4" ) { $member_data->{size} = $member_data->{align} = 4; } elsif ($member_data->{type} eq "int16" || $member_data->{type} eq "uint16") { $member_data->{size} = $member_data->{align} = 2; } elsif ( $member_data->{type} eq "int8" || $member_data->{type} eq "uint8" || $member_data->{type} eq "bool1" ) { $member_data->{size} = $member_data->{align} = 1; } elsif ( $member_data->{type} =~ /^u?int8\[(\d+)\]$/ ) { $member_data->{size} = $1; $member_data->{align} = 1; } else { Fatal( "Unexpected type '".$member_data->{type}."' found in shared data definition." ); } if ( $member_data->{align} > 1 && ($offset%$member_data->{align}) > 0 ) { $offset += ($member_data->{align} - ($offset%$member_data->{align})); } $member_data->{offset} = $offset; $offset += $member_data->{size} } $section_data->{size} = $offset - $section_data->{offset}; } $mem_size = $offset; } &zmMemInit(); sub zmMemVerify( $ ) { my $monitor = shift; if ( !zmMemAttach( $monitor, $mem_size ) ) { return( undef ); } my $mem_key = zmMemKey( $monitor ); if ( !defined($mem_verified->{$mem_key}) ) { my $sd_size = zmMemRead( $monitor, "shared_data:size", 1 ); if ( $sd_size != $mem_data->{shared_data}->{size} ) { if ( $sd_size ) { Error( "Shared data size conflict in shared_data for monitor ".$monitor->{Name}.", expected ".$mem_data->{shared_data}->{size}.", got ".$sd_size ); } else { Debug( "Shared data size conflict in shared_data for monitor ".$monitor->{Name}.", expected ".$mem_data->{shared_data}->{size}.", got ".$sd_size ); } return( undef ); } my $td_size = zmMemRead( $monitor, "trigger_data:size", 1 ); if ( $td_size != $mem_data->{trigger_data}->{size} ) { if ( $td_size ) { Error( "Shared data size conflict in trigger_data for monitor ".$monitor->{Name}.", expected ".$mem_data->{triggger_data}->{size}.", got ".$td_size ); } else { Debug( "Shared data size conflict in trigger_data for monitor ".$monitor->{Name}.", expected ".$mem_data->{triggger_data}->{size}.", got ".$td_size ); } return( undef ); } $mem_verified->{$mem_key} = !undef; } return( !undef ); } sub zmMemRead( $$;$ ) { my $monitor = shift; my $fields = shift; my $nocheck = shift; if ( !($nocheck || zmMemVerify( $monitor )) ) { return( undef ); } if ( !ref($fields) ) { $fields = [ $fields ]; } my @values; foreach my $field ( @$fields ) { my ( $section, $element ) = split( /[\/:.]/, $field ); Fatal( "Invalid shared data selector '$field'" ) if ( !$section || !$element ); my $offset = $mem_data->{$section}->{contents}->{$element}->{offset}; my $type = $mem_data->{$section}->{contents}->{$element}->{type}; my $size = $mem_data->{$section}->{contents}->{$element}->{size}; my $data = zmMemGet( $monitor, $offset, $size ); if ( !defined($data) ) { Error( "Unable to read '$field' from memory for monitor ".$monitor->{Id} ); zmMemInvalidate( $monitor ); return( undef ); } my $value; if ( $type eq "long" ) { ( $value ) = unpack( "l!", $data ); } elsif ( $type eq "ulong" || $type eq "size_t" ) { ( $value ) = unpack( "L!", $data ); } elsif ( $type eq "int64" || $type eq "time_t64" ) { # The "q" type is only available on 64bit platforms, so use native. ( $value ) = unpack( "l!", $data ); } elsif ( $type eq "uint64" ) { # The "q" type is only available on 64bit platforms, so use native. ( $value ) = unpack( "L!", $data ); } elsif ( $type eq "int32" ) { ( $value ) = unpack( "l", $data ); } elsif ( $type eq "uint32" || $type eq "bool4" ) { ( $value ) = unpack( "L", $data ); } elsif ( $type eq "int16" ) { ( $value ) = unpack( "s", $data ); } elsif ( $type eq "uint16" ) { ( $value ) = unpack( "S", $data ); } elsif ( $type eq "int8" ) { ( $value ) = unpack( "c", $data ); } elsif ( $type eq "uint8" || $type eq "bool1" ) { ( $value ) = unpack( "C", $data ); } elsif ( $type =~ /^int8\[\d+\]$/ ) { ( $value ) = unpack( "Z".$size, $data ); } elsif ( $type =~ /^uint8\[\d+\]$/ ) { ( $value ) = unpack( "C".$size, $data ); } else { Fatal( "Unexpected type '".$type."' found for '".$field."'" ); } push( @values, $value ); } if ( wantarray() ) { return( @values ) } return( $values[0] ); } sub zmMemInvalidate( $ ) { my $monitor = shift; my $mem_key = zmMemKey($monitor); if ( $mem_key ) { delete $mem_verified->{$mem_key}; zmMemDetach( $monitor ); } } sub zmMemTidy() { zmMemClean(); } sub zmMemWrite( $$;$ ) { my $monitor = shift; my $field_values = shift; my $nocheck = shift; if ( !($nocheck || zmMemVerify( $monitor )) ) { return( undef ); } while ( my ( $field, $value ) = each( %$field_values ) ) { my ( $section, $element ) = split( /[\/:.]/, $field ); Fatal( "Invalid shared data selector '$field'" ) if ( !$section || !$element ); my $offset = $mem_data->{$section}->{contents}->{$element}->{offset}; my $type = $mem_data->{$section}->{contents}->{$element}->{type}; my $size = $mem_data->{$section}->{contents}->{$element}->{size}; my $data; if ( $type eq "long" ) { $data = pack( "l!", $value ); } elsif ( $type eq "ulong" || $type eq "size_t" ) { $data = pack( "L!", $value ); } elsif ( $type eq "int64" || $type eq "time_t64" ) { # The "q" type is only available on 64bit platforms, so use native. $data = pack( "l!", $value ); } elsif ( $type eq "uint64" ) { # The "q" type is only available on 64bit platforms, so use native. $data = pack( "L!", $value ); } elsif ( $type eq "int32" ) { $data = pack( "l", $value ); } elsif ( $type eq "uint32" || $type eq "bool4" ) { $data = pack( "L", $value ); } elsif ( $type eq "int16" ) { $data = pack( "s", $value ); } elsif ( $type eq "uint16" ) { $data = pack( "S", $value ); } elsif ( $type eq "int8" ) { $data = pack( "c", $value ); } elsif ( $type eq "uint8" || $type eq "bool1" ) { $data = pack( "C", $value ); } elsif ( $type =~ /^int8\[\d+\]$/ ) { $data = pack( "Z".$size, $value ); } elsif ( $type =~ /^uint8\[\d+\]$/ ) { $data = pack( "C".$size, $value ); } else { Fatal( "Unexpected type '".$type."' found for '".$field."'" ); } if ( !zmMemPut( $monitor, $offset, $size, $data ) ) { Error( "Unable to write '$value' to '$field' in memory for monitor ".$monitor->{Id} ); zmMemInvalidate( $monitor ); return( undef ); } } return( !undef ); } sub zmGetMonitorState( $ ) { my $monitor = shift; return( zmMemRead( $monitor, "shared_data:state" ) ); } sub zmGetAlarmLocation( $ ) { my $monitor = shift; return( zmMemRead( $monitor, [ "shared_data:alarm_x", "shared_data:alarm_y" ] ) ); } sub zmSetControlState( $$ ) { my $monitor = shift; my $control_state = shift; zmMemWrite( $monitor, { "shared_data:control_state" => $control_state } ); } sub zmGetControlState( $ ) { my $monitor = shift; return( zmMemRead( $monitor, "shared_data:control_state" ) ); } sub zmSaveControlState( $$ ) { my $monitor = shift; my $control_state = shift; zmSetControlState( $monitor, freeze( $control_state ) ); } sub zmRestoreControlState( $ ) { my $monitor = shift; return( thaw( zmGetControlState( $monitor ) ) ); } sub zmIsAlarmed( $ ) { my $monitor = shift; my $state = zmGetMonitorState( $monitor ); return( $state == STATE_ALARM ); } sub zmInAlarm( $ ) { my $monitor = shift; my $state = zmGetMonitorState( $monitor ); return( $state == STATE_ALARM || $state == STATE_ALERT ); } sub zmHasAlarmed( $$ ) { my $monitor = shift; my $last_event_id = shift; my ( $state, $last_event ) = zmMemRead( $monitor, [ "shared_data:state", "shared_data:last_event" ] ); if ( $state == STATE_ALARM || $state == STATE_ALERT ) { return( $last_event ); } elsif( $last_event != $last_event_id ) { return( $last_event ); } return( undef ); } sub zmGetLastEvent( $ ) { my $monitor = shift; return( zmMemRead( $monitor, "shared_data:last_event" ) ); } sub zmGetLastWriteTime( $ ) { my $monitor = shift; return( zmMemRead( $monitor, "shared_data:last_write_time" ) ); } sub zmGetLastReadTime( $ ) { my $monitor = shift; return( zmMemRead( $monitor, "shared_data:last_read_time" ) ); } sub zmGetMonitorActions( $ ) { my $monitor = shift; return( zmMemRead( $monitor, "shared_data:action" ) ); } sub zmMonitorEnable( $ ) { my $monitor = shift; my $action = zmMemRead( $monitor, "shared_data:action" ); $action |= ACTION_SUSPEND; zmMemWrite( $monitor, { "shared_data:action" => $action } ); } sub zmMonitorDisable( $ ) { my $monitor = shift; my $action = zmMemRead( $monitor, "shared_data:action" ); $action |= ACTION_RESUME; zmMemWrite( $monitor, { "shared_data:action" => $action } ); } sub zmMonitorSuspend( $ ) { my $monitor = shift; my $action = zmMemRead( $monitor, "shared_data:action" ); $action |= ACTION_SUSPEND; zmMemWrite( $monitor, { "shared_data:action" => $action } ); } sub zmMonitorResume( $ ) { my $monitor = shift; my $action = zmMemRead( $monitor, "shared_data:action" ); $action |= ACTION_RESUME; zmMemWrite( $monitor, { "shared_data:action" => $action } ); } sub zmGetTriggerState( $ ) { my $monitor = shift; return( zmMemRead( $monitor, "trigger_data:trigger_state" ) ); } sub zmTriggerEventOn( $$$;$$ ) { my $monitor = shift; my $score = shift; my $cause = shift; my $text = shift; my $showtext = shift; my $values = { "trigger_data:trigger_score" => $score, "trigger_data:trigger_cause" => $cause, }; $values->{"trigger_data:trigger_text"} = $text if ( defined($text) ); $values->{"trigger_data:trigger_showtext"} = $showtext if ( defined($showtext) ); $values->{"trigger_data:trigger_state"} = TRIGGER_ON; # Write state last so event not read incomplete zmMemWrite( $monitor, $values ); } sub zmTriggerEventOff( $ ) { my $monitor = shift; my $values = { "trigger_data:trigger_state" => TRIGGER_OFF, "trigger_data:trigger_score" => 0, "trigger_data:trigger_cause" => "", "trigger_data:trigger_text" => "", "trigger_data:trigger_showtext" => "", }; zmMemWrite( $monitor, $values ); } sub zmTriggerEventCancel( $ ) { my $monitor = shift; my $values = { "trigger_data:trigger_state" => TRIGGER_CANCEL, "trigger_data:trigger_score" => 0, "trigger_data:trigger_cause" => "", "trigger_data:trigger_text" => "", "trigger_data:trigger_showtext" => "", }; zmMemWrite( $monitor, $values ); } sub zmTriggerShowtext( $$ ) { my $monitor = shift; my $showtext = shift; my $values = { "trigger_data:trigger_showtext" => $showtext, }; zmMemWrite( $monitor, $values ); } 1; __END__ =head1 NAME ZoneMinder::MappedMem - ZoneMinder Mapped Memory access module =head1 SYNOPSIS use ZoneMinder::MappedMem; use ZoneMinder::MappedMem qw(:all); if ( zmMemVerify( $monitor ) ) { $state = zmGetMonitorState( $monitor ); if ( $state == STATE_ALARM ) { ... } } ( $lri, $lwi ) = zmMemRead( $monitor, [ "shared_data:last_read_index", "shared_data:last_write_index" ] ); zmMemWrite( $monitor, { "trigger_data:trigger_showtext" => "Some Text" } ); =head1 DESCRIPTION The ZoneMinder:MappedMem module contains methods for accessing and writing to mapped memory as well as helper methods for common operations. The core elements of ZoneMinder used mapped memory to allow multiple access to resources. Although ZoneMinder scripts have used this information before, up until now it was difficult to access and prone to errors. This module introduces a common API for mapped memory access (both reading and writing) making it a lot easier to customise scripts or even create your own. All the methods listed below require a 'monitor' parameter. This must be a reference to a hash with at least the 'Id' field set to the monitor id of the mapped memory you wish to access. Using database methods to select the monitor details will also return this kind of data. Some of the mapped memory methods will add and amend new fields to this hash. =over 4 =head1 METHODS =item zmMemVerify ( $monitor ); Verify that the mapped memory of the monitor given exists and is valid. It will return an undefined value if it is not valid. You should generally call this method first before using any of the other methods, but most of the remaining methods will also do so if the memory has not already been verified. =item zmMemInvalidate ( $monitor ); Following an error, reset the mapped memory ids and attempt to reverify on the next operation. This is mostly used when a mapped memory segment has gone away and been recreated with a different id. =item zmMemRead ( $monitor, $readspec ); This method is used to read data from mapped memory attached to the given monitor. The mapped memory will be verified if it has not already been. The 'readspec' must either be a string of the form "
:" or a reference to an array of strings of the same format. In the first case a single value is returned, in the latter case a list of values is return. Errors will cause undefined to be returned. The allowable sections and field names are described below. =item zmMemWrite ( $monitor, $writespec ); This method is used to write data to mapped memory attached to the given monitor. The mapped memory will be verified if it has not already been. The 'writespec' must be a reference to a hash with keys of the form "
:" and values as the data to be written. Errors will cause undefined to be returned, otherwise a non-undefined value will be returned. The allowable sections and field names are described below. =item $state = zmGetMonitorState ( $monitor ); Return the current state of the given monitor. This is an integer value and can be compared with the STATE constants given below. =item $event_id = zmGetLastEvent ( $monitor ); Return the event id of the last event that the monitor generated, or 0 if no event has been generated by the current monitor process. =item zmIsAlarmed ( $monitor ); Return 1 if the monitor given is currently in an alarm state, 0 otherwise. =item zmInAlarm ( $monitor ); Return 1 if the monitor given is currently in an alarm or alerted state, 0 otherwise. =item zmHasAlarmed ( $monitor ); Return 1 if the given monitor is in an alarm state, or has been in an alarm state since the last call to this method. =item ( $x, $y ) = zmGetAlarmLocation ( $monitor ); Return an x,y pair indicating the image co-ordinates of the centre of the last motion event generated by the given monitor. If no event has been generated by the current monitor process, or the alarm was not motion related, returns -1,-1. =item zmGetLastWriteTime ( $monitor ); Returns the time (in utc seconds) since the last image was captured by the given monitor and written to shared memory, or 0 otherwise. =item zmGetLastReadTime ( $monitor ); Returns the time (in utc seconds) since the last image was read from shared memory by the analysis daemon of the given monitor, or 0 otherwise or if the monitor is in monitor only mode. =item zmMonitorSuspend ( $monitor ); Suspend the given monitor from generating events caused by motion. This method can be used to prevent camera actions such as panning or zooming from causing events. If configured to do so, the monitor may automatically resume after a defined period. =item zmMonitorResume ( $monitor ); Allow the given monitor to resume generating events caused by motion. =item zmTriggerEventOn ( $monitor, $score, $cause [, $text, $showtext ] ); Trigger the given monitor to generate an event. You must supply an event score and a cause string indicating the reason for the event. You may also supply a text string containing further details about the event and a showtext string which may be included in the timestamp annotation on any images captured during the event, if configured to do so. =item zmTriggerEventOff ( $monitor ); Trigger the given monitor to not generate any events. This method does not cancel zmTriggerEventOn, but is exclusive to it. This method is intended to allow external triggers to prevent normal events being generated by monitors in the same way as zmMonitorSuspend but applies to all events and not just motion, and is intended for longer timescales than are appropriate for suspension. =item zmTriggerEventCancel ( $monitor ); Cancel any previous trigger on or off requests. This stops a triggered alarm if it exists from a previous 'on' and allows events to be generated once more following a previous 'off'. =item zmTriggerShowtext ( $monitor, $showtest ); Indicate that the given text should be displayed in the timestamp annotation on any images captured, if the format of the annotation string defined for the monitor permits. =head1 DATA The data fields in mapped memory that may be accessed are as follows. There are two main sections, shared_data which is general data and trigger_data which is used for event triggering. Whilst reading from these fields is harmless, extreme care must be taken when writing to mapped memory, especially in the shared_data section as this is normally written to only by monitor capture and analysis processes. shared_data The general mapped memory section size The size, in bytes, of this section valid Flag indicating whether this section has been initialised active Flag indicating whether this monitor is active (enabled/disabled) signal Flag indicating whether this monitor is reciving a valid signal state The current monitor state, see the STATE constants below last_write_index The last index, in the image buffer, that an image has been saved to last_read_index The last index, in the image buffer, that an image has been analysed from last_write_time The time (in utc seconds) when the last image was captured last_read_time The time (in utc seconds) when the last image was analysed last_event The id of the last event generated by the monitor analysis process, 0 if none action The monitor actions bitmask, see the ACTION constants below brightness Read/write location for the current monitor brightness hue Read/write location for the current monitor hue colour Read/write location for the current monitor colour contrast Read/write location for the current monitor contrast alarm_x Image x co-ordinate (from left) of the centre of the last motion event, -1 if none alarm_y Image y co-ordinate (from top) of the centre of the last motion event, -1 if none trigger_data The triggered event mapped memory section size The size, in bytes of this section trigger_state The current trigger state, see the TRIGGER constants below trigger_score The current triggered event score trigger_cause The current triggered event cause string trigger_text The current triggered event descriptive text string trigger_showtext The triggered text that will be displayed on captured image timestamps =head1 CONSTANTS The following constants are used by the methods above, but can also be used by user scripts if required. =item STATE_IDLE STATE_PREALARM STATE_ALARM STATE_ALERT STATE_TAPE These constants define the state of the monitor with respect to alarms and events. They are used in the shared_data:state field. =item ACTION_GET ACTION_SET ACTION_RELOAD ACTION_SUSPEND ACTION_RESUME These constants defines the various values that can exist in the shared_data:action field. This is a bitmask which when non-zero defines an action that an executing monitor process should take. ACTION_GET requires that the current values of brightness, contrast, colour and hue are taken from the camera and written to the equivalent mapped memory fields. ACTION_SET implies the reverse, that the values in mapped memory should be written to the camera. ACTION_RELOAD signal that the monitor process should reload itself from the database in case any settings have changed there. ACTION_SUSPEND signals that a monitor should stop exaiming images for motion, though other alarms may still occur. ACTION_RESUME sigansl that a monitor should resume motion detectiom. =item TRIGGER_CANCEL TRIGGER_ON TRIGGER_OFF These constants are used in the definition of external triggers. TRIGGER_CANCEL is used to indicated that any previous trigger settings should be cancelled, TRIGGER_ON signals that an alarm should be created (or continued)) as a result of the current trigger and TRIGGER_OFF signals that the trigger should prevent any alarms from being generated. See the trigger methods above for further details. =head1 EXPORT None by default. The :constants tag will export the mapped memory constants which mostly define enumerations for the variables held in memory The :functions tag will export the mapped memory access functions. The :all tag will export all above symbols. =head1 SEE ALSO http://www.zoneminder.com =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Memory/000077500000000000000000000000001225361755400235225ustar00rootroot00000000000000ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm000066400000000000000000000110231225361755400252630ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Mapped Memory Access Module, $Date: 2008-02-25 10:13:13 +0000 (Mon, 25 Feb 2008) $, $Revision: 2323 $ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the definitions and functions used when accessing mapped memory # package ZoneMinder::Memory::Mapped; use 5.006; use strict; use warnings; require Exporter; require ZoneMinder::Base; our @ISA = qw(Exporter ZoneMinder::Base); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'functions' => [ qw( zmMemKey zmMemAttach zmMemDetach zmMemGet zmMemPut zmMemClean ) ], ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = @EXPORT_OK; our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Mapped Memory Facilities # # ========================================================================== use ZoneMinder::Config qw(:all); use ZoneMinder::Logger qw(:all); use Sys::Mmap; sub zmMemKey( $ ) { my $monitor = shift; return( defined($monitor->{MMapAddr})?$monitor->{MMapAddr}:undef ); } sub zmMemAttach( $$ ) { my $monitor = shift; my $size = shift; if ( !defined($monitor->{MMapAddr}) ) { my $mmap_file = ZM_PATH_MAP."/zm.mmap.".$monitor->{Id}; if ( -s $mmap_file < $size ) { Error( sprintf( "Memory map file '%s' should have been %d but was instead %d", $mmap_file, $size, -s $mmap_file ) ); return ( undef ); } if ( !open( MMAP, "+<".$mmap_file ) ) { Error( sprintf( "Can't open memory map file '%s': $!\n", $mmap_file ) ); return( undef ); } my $mmap = undef; my $mmap_addr = mmap( $mmap, $size, PROT_READ|PROT_WRITE, MAP_SHARED, \*MMAP ); if ( !$mmap_addr || !$mmap ) { Error( sprintf( "Can't mmap to file '%s': $!\n", $mmap_file ) ); return( undef ); } $monitor->{MMapHandle} = \*MMAP; $monitor->{MMapAddr} = $mmap_addr; $monitor->{MMap} = \$mmap; } return( !undef ); } sub zmMemDetach( $ ) { my $monitor = shift; if ( $monitor->{MMap} ) { munmap( ${$monitor->{MMap}} ); delete $monitor->{MMap}; } if ( $monitor->{MMapAddr} ) { delete $monitor->{MMapAddr}; } if ( $monitor->{MMapHandle} ) { close( $monitor->{MMapHandle} ); delete $monitor->{MMapHandle}; } } sub zmMemGet( $$$ ) { my $monitor = shift; my $offset = shift; my $size = shift; my $mmap = $monitor->{MMap}; if ( !$mmap || !$$mmap ) { Error( sprintf( "Can't read from mapped memory for monitor '%d', gone away?", $monitor->{Id} ) ); return( undef ); } my $data = substr( $$mmap, $offset, $size ); return( $data ); } sub zmMemPut( $$$$ ) { my $monitor = shift; my $offset = shift; my $size = shift; my $data = shift; my $mmap = $monitor->{MMap}; if ( !$mmap || !$$mmap ) { Error( sprintf( "Can't write mapped memory for monitor '%d', gone away?", $monitor->{Id} ) ); return( undef ); } substr( $$mmap, $offset, $size ) = $data; return( !undef ); } sub zmMemClean { Debug( "Removing memory map files\n" ); my $mapPath = ZM_PATH_MAP."/zm.mmap.*"; foreach my $mapFile( glob( $mapPath ) ) { ( $mapFile ) = $mapFile =~ /^(.+)$/; Debug( "Removing memory map file '$mapFile'\n" ); unlink( $mapFile ); } } 1; __END__ ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Memory/Shared.pm000066400000000000000000000104331225361755400252670ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Shared Memory Access Module, $Date: 2007-08-29 19:11:09 +0100 (Wed, 29 Aug 2007) $, $Revision: 2175 $ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the common definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::Memory::Shared; use 5.006; use strict; use warnings; require Exporter; require ZoneMinder::Base; our @ISA = qw(Exporter ZoneMinder::Base); eval 'sub IPC_CREAT {0001000}' unless defined &IPC_CREAT; # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. # This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'functions' => [ qw( zmMemKey zmMemAttach zmMemDetach zmMemGet zmMemPut zmMemClean ) ], ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = @EXPORT_OK; our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Shared Memory Facilities # # ========================================================================== use ZoneMinder::Config qw(:all); use ZoneMinder::Logger qw(:all); sub zmMemKey( $ ) { my $monitor = shift; return( defined($monitor->{ShmKey})?$monitor->{ShmKey}:undef ); } sub zmMemAttach( $$ ) { my $monitor = shift; my $size = shift; if ( !defined($monitor->{ShmId}) ) { my $shm_key = (hex(ZM_SHM_KEY)&0xffff0000)|$monitor->{Id}; my $shm_id = shmget( $shm_key, $size, &IPC_CREAT | 0777 ); if ( !defined($shm_id) ) { Error( sprintf( "Can't get shared memory id '%x', %d: $!\n", $shm_key, $monitor->{Id} ) ); return( undef ); } $monitor->{ShmKey} = $shm_key; $monitor->{ShmId} = $shm_id; } return( !undef ); } sub zmMemDetach( $ ) { my $monitor = shift; delete $monitor->{ShmId}; } sub zmMemGet( $$$ ) { my $monitor = shift; my $offset = shift; my $size = shift; my $shm_key = $monitor->{ShmKey}; my $shm_id = $monitor->{ShmId}; my $data; if ( !shmread( $shm_id, $data, $offset, $size ) ) { Error( sprintf( "Can't read from shared memory '%x/%d': $!", $shm_key, $shm_id ) ); return( undef ); } return( $data ); } sub zmMemPut( $$$$ ) { my $monitor = shift; my $offset = shift; my $size = shift; my $data = shift; my $shm_key = $monitor->{ShmKey}; my $shm_id = $monitor->{ShmId}; if ( !shmwrite( $shm_id, $data, $offset, $size ) ) { Error( sprintf( "Can't write to shared memory '%x/%d': $!", $shm_key, $shm_id ) ); return( undef ); } return( !undef ); } sub zmMemClean { Debug( "Removing shared memory\n" ); # Find ZoneMinder shared memory my $command = "ipcs -m | grep '^".substr( sprintf( "0x%x", hex(ZM_SHM_KEY) ), 0, -2 )."'"; Debug( "Checking for shared memory with '$command'\n" ); open( CMD, "$command |" ) or Fatal( "Can't execute '$command': $!" ); while( ) { chomp; my ( $key, $id ) = split( /\s+/ ); if ( $id =~ /^(\d+)/ ) { $id = $1; $command = "ipcrm shm $id"; Debug( "Removing shared memory with '$command'\n" ); qx( $command ); } } close( CMD ); } 1; __END__ ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Trigger/000077500000000000000000000000001225361755400236555ustar00rootroot00000000000000ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel.pm000066400000000000000000000072511225361755400255700ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Trigger Channel Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the base class definition of the trigger channel # class tree # package ZoneMinder::Trigger::Channel; use 5.006; use strict; use warnings; require ZoneMinder::Base; our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Database Access # # ========================================================================== use ZoneMinder::Logger qw(:all); use Carp; our $AUTOLOAD; sub new { my $class = shift; my $self = {}; $self->{readable} = !undef; $self->{writeable} = !undef; $self->{selectable} = undef; $self->{state} = 'closed'; bless( $self, $class ); return $self; } sub clone { my $self = shift; my $clone = { %$self }; bless $clone, ref $self; } sub open() { my $self = shift; my $class = ref($self) or croak( "Can't get class for non object $self" ); croak( "Abstract base class method called for object of class $class" ); } sub close() { my $self = shift; my $class = ref($self) or croak( "Can't get class for non object $self" ); croak( "Abstract base class method called for object of class $class" ); } sub getState() { my $self = shift; return( $self->{state} ); } sub isOpen() { my $self = shift; return( $self->{state} eq "open" ); } sub isConnected() { my $self = shift; return( $self->{state} eq "connected" ); } sub DESTROY { } sub AUTOLOAD { my $self = shift; my $class = ref($self) || croak( "$self not object" ); my $name = $AUTOLOAD; $name =~ s/.*://; if ( !exists($self->{$name}) ) { croak( "Can't access $name member of object of class $class" ); } return( $self->{$name} ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/000077500000000000000000000000001225361755400252255ustar00rootroot00000000000000ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/File.pm000066400000000000000000000062121225361755400264430ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Trigger Channel Handle Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the class definition of the simple file based trigger # channel class # package ZoneMinder::Trigger::Channel::File; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Trigger::Channel::Handle; our @ISA = qw(ZoneMinder::Trigger::Channel::Handle); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Simple file based trigger channel # # ========================================================================== use ZoneMinder::Logger qw(:all); use Carp; use Fcntl; sub new { my $class = shift; my %params = @_; my $self = ZoneMinder::Trigger::Channel::Handle->new; $self->{path} = $params{path}; bless( $self, $class ); return $self; } sub open() { my $self = shift; local *sfh; #sysopen( *sfh, $conn->{path}, O_NONBLOCK|O_RDONLY ) or croak( "Can't sysopen: $!" ); #open( *sfh, "<".$conn->{path} ) or croak( "Can't open: $!" ); open( *sfh, "+<".$self->{path} ) or croak( "Can't open: $!" ); $self->{state} = 'open'; $self->{handle} = *sfh; } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Handle.pm000066400000000000000000000071751225361755400267700ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Trigger Channel Handle Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the class definition of the handle based trigger channel # class # package ZoneMinder::Trigger::Channel::Handle; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Trigger::Channel; our @ISA = qw(ZoneMinder::Trigger::Channel); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Base class for handle based trigger channels # # ========================================================================== use ZoneMinder::Logger qw(:all); use POSIX; sub new { my $class = shift; my $port = shift; my $self = ZoneMinder::Trigger::Channel->new(); $self->{handle} = undef; bless( $self, $class ); return $self; } sub spawns { return( undef ); } sub close() { my $self = shift; close( $self->{handle} ); $self->{state} = 'closed'; $self->{handle} = undef; } sub read() { my $self = shift; my $buffer; my $nbytes = sysread( $self->{handle}, $buffer, POSIX::BUFSIZ ); if ( !$nbytes ) { return( undef ); } Debug( "Read '$buffer' ($nbytes bytes)\n" ); return( $buffer ); } sub write() { my $self = shift; my $buffer = shift; my $nbytes = syswrite( $self->{handle}, $buffer ); if ( !defined( $nbytes) || $nbytes < length($buffer) ) { Error( "Unable to write buffer '".$buffer.", expected ".length($buffer)." bytes, sent ".$nbytes.": $!\n" ); return( undef ); } Debug( "Wrote '$buffer' ($nbytes bytes)\n" ); return( !undef ); } sub fileno() { my $self = shift; return( defined($self->{handle})?fileno($self->{handle}):-1 ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Inet.pm000066400000000000000000000071131225361755400264640ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Trigger Channel Handle Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the class definition of the tcp socket based trigger # channel class # package ZoneMinder::Trigger::Channel::Inet; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Trigger::Channel::Spawning; our @ISA = qw(ZoneMinder::Trigger::Channel::Spawning); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Internet (TCP) based trigger channel # # ========================================================================== use ZoneMinder::Logger qw(:all); use Carp; use Socket; sub new { my $class = shift; my %params = @_; my $self = ZoneMinder::Trigger::Channel::Spawning->new(); $self->{selectable} = !undef; $self->{port} = $params{port}; bless( $self, $class ); return $self; } sub open() { my $self = shift; local *sfh; my $saddr = sockaddr_in( $self->{port}, INADDR_ANY ); socket( *sfh, PF_INET, SOCK_STREAM, getprotobyname('tcp') ) or croak( "Can't open socket: $!" ); setsockopt( *sfh, SOL_SOCKET, SO_REUSEADDR, 1 ); bind( *sfh, $saddr ) or croak( "Can't bind: $!" ); listen( *sfh, SOMAXCONN ) or croak( "Can't listen: $!" ); $self->{state} = 'open'; $self->{handle} = *sfh; } sub _spawn( $ ) { my $self = shift; my $new_handle = shift; my $clone = $self->clone(); $clone->{handle} = $new_handle; $clone->{state} = 'connected'; return( $clone ); } sub accept() { my $self = shift; local *cfh; my $paddr = accept( *cfh, $self->{handle} ); return( $self->_spawn( *cfh ) ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Serial.pm000066400000000000000000000075371225361755400270160ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Trigger Channel Handle Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the class definition of the serial port trigger channel # class # package ZoneMinder::Trigger::Channel::Serial; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Trigger::Channel; our @ISA = qw(ZoneMinder::Trigger::Channel); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Serial access trigger channel # # ========================================================================== use ZoneMinder::Logger qw(:all); use Device::SerialPort; sub new { my $class = shift; my %params = @_; my $self = ZoneMinder::Trigger::Channel->new; $self->{path} = $params{path}; bless( $self, $class ); return $self; } sub open() { my $self = shift; my $device = new Device::SerialPort( $self->{path} ); $device->baudrate(9600); $device->databits(8); $device->parity('none'); $device->stopbits(1); $device->handshake('none'); $device->read_const_time(50); $device->read_char_time(10); $self->{device} = $device; $self->{state} = 'open'; $self->{state} = 'connected'; } sub close() { my $self = shift; $self->{device}->close(); $self->{state} = 'closed'; } sub read() { my $self = shift; my $buffer = $self->{device}->lookfor(); if ( !$buffer || !length($buffer) ) { return( undef ); } Debug( "Read '$buffer' (".length($buffer)." bytes)\n" ); return( $buffer ); } sub write() { my $self = shift; my $buffer = shift; my $nbytes = $self->{device}->write( $buffer ); $self->{device}->write_drain(); if ( !defined( $nbytes) || $nbytes < length($buffer) ) { Error( "Unable to write buffer '".$buffer.", expected ".length($buffer)." bytes, sent ".$nbytes.": $!\n" ); return( undef ); } Debug( "Wrote '$buffer' ($nbytes bytes)\n" ); return( !undef ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Spawning.pm000066400000000000000000000056501225361755400273570ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Trigger Channel Handle Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the class definition of the handle based trigger channel # classes that spawn new connections when connected. # package ZoneMinder::Trigger::Channel::Spawning; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Trigger::Channel::Handle; our @ISA = qw(ZoneMinder::Trigger::Channel::Handle); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Base class for handle based triggers that spawn new connections # # ========================================================================== use ZoneMinder::Logger qw(:all); sub new { my $class = shift; my $port = shift; my $self = ZoneMinder::Trigger::Channel::Handle->new(); $self->{spawns} = !undef; bless( $self, $class ); return $self; } sub spawns { return( !undef ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Unix.pm000066400000000000000000000070111225361755400265050ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Trigger Channel Handle Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the class definition of the unix socket based trigger # channel class # package ZoneMinder::Trigger::Channel::Unix; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Trigger::Channel::Spawning; our @ISA = qw(ZoneMinder::Trigger::Channel::Spawning); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Unix socket based trigger channel # # ========================================================================== use ZoneMinder::Logger qw(:all); use Carp; use Socket; sub new { my $class = shift; my %params = @_; my $self = ZoneMinder::Trigger::Channel->new; $self->{selectable} = !undef; $self->{path} = $params{path}; bless( $self, $class ); return $self; } sub open() { my $self = shift; local *sfh; unlink( $self->{path} ); my $saddr = sockaddr_un( $self->{path} ); socket( *sfh, PF_UNIX, SOCK_STREAM, 0 ) or croak( "Can't open socket: $!" ); bind( *sfh, $saddr ) or croak( "Can't bind: $!" ); listen( *sfh, SOMAXCONN ) or croak( "Can't listen: $!" ); $self->{handle} = *sfh; } sub _spawn( $ ) { my $self = shift; my $new_handle = shift; my $clone = $self->clone(); $clone->{handle} = $new_handle; $clone->{state} = 'connected'; return( $clone ); } sub accept() { my $self = shift; local *cfh; my $paddr = accept( *cfh, $self->{handle} ); return( $self->_spawn( *cfh ) ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Connection.pm000066400000000000000000000113431225361755400263140ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Trigger Connection Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains the base class definition of the trigger connection # class tree # package ZoneMinder::Trigger::Connection; use 5.006; use strict; use warnings; require ZoneMinder::Base; our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Base connection class # # ========================================================================== use ZoneMinder::Logger qw(:all); use Carp; our $AUTOLOAD; sub new { my $class = shift; my %params = @_; my $self = {}; $self->{name} = $params{name}; $self->{channel} = $params{channel}; $self->{input} = $params{mode} =~ /r/i; $self->{output} = $params{mode} =~ /w/i; bless( $self, $class ); return $self; } sub clone { my $self = shift; my $clone = { %$self }; bless $clone, ref $self; return( $clone ); } sub spawns { my $self = shift; return( $self->{channel}->spawns() ); } sub _spawn( $ ) { my $self = shift; my $new_channel = shift; my $clone = $self->clone(); $clone->{channel} = $new_channel; return( $clone ); } sub accept() { my $self = shift; my $new_channel = $self->{channel}->accept(); return( $self->_spawn( $new_channel ) ); } sub open() { my $self = shift; return( $self->{channel}->open() ); } sub close() { my $self = shift; return( $self->{channel}->close() ); } sub fileno() { my $self = shift; return( $self->{channel}->fileno() ); } sub isOpen() { my $self = shift; return( $self->{channel}->isOpen() ); } sub isConnected() { my $self = shift; return( $self->{channel}->isConnected() ); } sub canRead() { my $self = shift; return( $self->{input} && $self->isConnected() ); } sub canWrite() { my $self = shift; return( $self->{output} && $self->isConnected() ); } sub getMessages { my $self = shift; my $buffer = $self->{channel}->read(); return( undef ) if ( !defined($buffer) ); my @messages = split( /\r?\n/, $buffer ); return( \@messages ); } sub putMessages { my $self = shift; my $messages = shift; if ( @$messages ) { my $buffer = join( "\n", @$messages ); $buffer .= "\n"; if ( !$self->{channel}->write( $buffer ) ) { Error( "Unable to write buffer '".$buffer." to connection ".$self->{name}." (".$self->fileno().")\n" ); } } return( undef ); } sub timedActions { } sub DESTROY { } sub AUTOLOAD { my $self = shift; my $class = ref($self) || croak( "$self not object" ); my $name = $AUTOLOAD; $name =~ s/.*://; if ( exists($self->{$name}) ) { return( $self->{$name} ); } elsif ( defined($self->{channel}) ) { if ( exists($self->{channel}->{$name}) ) { return( $self->{channel}->{$name} ); } } croak( "Can't access $name member of object of class $class" ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Connection/000077500000000000000000000000001225361755400257545ustar00rootroot00000000000000ZoneMinder-1.26.5/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Connection/Example.pm000066400000000000000000000065321225361755400277130ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Trigger Channel Handle Module, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This module contains an example overriden trigger connection class # package ZoneMinder::Trigger::Connection::Example; use 5.006; use strict; use warnings; require ZoneMinder::Base; require ZoneMinder::Trigger::Connection; our @ISA = qw(ZoneMinder::Trigger::Connection); our $VERSION = $ZoneMinder::Base::VERSION; # ========================================================================== # # Example overridden connection class # # ========================================================================== use ZoneMinder::Logger qw(:all); sub new { my $class = shift; my $path = shift; my $self = ZoneMinder::Trigger::Connection->new( @_ ); bless( $self, $class ); return $self; } sub getMessages { my $self = shift; my $buffer = $self->{channel}->read(); return( undef ) if ( !defined($buffer) ); Debug( "Handling buffer '$buffer'\n" ); my @messages = grep { s/-/|/g; 1; } split( /\r?\n/, $buffer ); return( \@messages ); } sub putMessages { my $self = shift; my $messages = shift; if ( @$messages ) { my $buffer = join( "\n", grep{ s/\|/-/; 1; } @$messages ); $buffer .= "\n"; if ( !$self->{channel}->write( $buffer ) ) { Error( "Unable to write buffer '".$buffer." to connection ".$self->{name}." (".$self->fileno().")\n" ); } } return( undef ); } 1; __END__ # Below is stub documentation for your module. You'd better edit it! =head1 NAME ZoneMinder::Database - Perl extension for blah blah blah =head1 SYNOPSIS use ZoneMinder::Database; blah blah blah =head1 DESCRIPTION Stub documentation for ZoneMinder, created by h2xs. It looks like the author of the extension was negligent enough to leave the stub unedited. Blah blah blah. =head2 EXPORT None by default. =head1 SEE ALSO Mention other useful documentation such as the documentation of related modules or operating system documentation (such as man pages in UNIX), or any relevant external documentation such as RFCs or standards. If you have a mailing list set up for your module, mention it here. If you have a web site set up for your module, mention it here. =head1 AUTHOR Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, at your option, any later version of Perl 5 you may have available. =cut ZoneMinder-1.26.5/scripts/ZoneMinder/t/000077500000000000000000000000001225361755400176755ustar00rootroot00000000000000ZoneMinder-1.26.5/scripts/ZoneMinder/t/ZoneMinder.t000066400000000000000000000007731225361755400221430ustar00rootroot00000000000000# Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl ZoneMinder.t' ######################### # change 'tests => 1' to 'tests => last_test_to_print'; use Test; BEGIN { plan tests => 1 }; use ZoneMinder; ok(1); # If we made it this far, we're ok. ######################### # Insert your test code below, the Test::More module is use()ed here so read # its man page ( perldoc Test::More ) for help writing this test script. ZoneMinder-1.26.5/scripts/zm.in000077500000000000000000000050121225361755400163370ustar00rootroot00000000000000#!/bin/sh # description: ZoneMinder is the top Linux video camera security and surveillance solution. ZoneMinder is intended for use in single or multi-camera video security applications.Copyright: Philip Coombes, Corey DeLasaux 2003-2008 # chkconfig: 2345 99 00 # processname: zmpkg.pl # Source function library. . /etc/rc.d/init.d/functions prog=ZoneMinder ZM_CONFIG="@ZM_CONFIG@" pidfile="@ZM_RUNDIR@" LOCKFILE=/var/lock/subsys/zm loadconf() { if [ -f $ZM_CONFIG ]; then . $ZM_CONFIG else echo "ERROR: $ZM_CONFIG not found." return 1 fi } loadconf command="$ZM_PATH_BIN/zmpkg.pl" start() { # Commenting out as it is not needed. Leaving as a placeholder for future use. # zmupdate || return $? loadconf || return $? #Make sure the directory for our PID folder exists or create one. [ ! -d $pidfile ] \ && mkdir -m 774 $pidfile \ && chown $ZM_WEB_USER:$ZM_WEB_GROUP $pidfile #Make sure the folder for the socks file exists or create one GetPath="select Value from Config where Name='ZM_PATH_SOCKS'" dbHost=`echo $ZM_DB_HOST | cut -d: -f1` dbPort=`echo $ZM_DB_HOST | cut -d: -s -f2` if [ "$dbPort" = "" ] then ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$ZM_DB_HOST -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'` else ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$dbHost -P$dbPort -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'` fi [ ! -d $ZM_PATH_SOCK ] \ && mkdir -m 774 $ZM_PATH_SOCK \ && chown $ZM_WEB_USER:$ZM_WEB_GROUP $ZM_PATH_SOCK echo -n $"Starting $prog: " $command start RETVAL=$? [ $RETVAL = 0 ] && success || failure echo [ $RETVAL = 0 ] && touch $LOCKFILE return $RETVAL } stop() { loadconf echo -n $"Stopping $prog: " $command stop RETVAL=$? [ $RETVAL = 0 ] && success || failure echo [ $RETVAL = 0 ] && rm -f $LOCKFILE } zmstatus() { loadconf result=`$command status` if [ "$result" = "running" ]; then echo "ZoneMinder is running" $ZM_PATH_BIN/zmu -l RETVAL=0 else echo "ZoneMinder is stopped" RETVAL=1 fi } zmupdate() { if [ -x $ZM_PATH_BIN/zmupdate.pl ]; then $ZM_PATH_BIN/zmupdate.pl -f fi } case "$1" in 'start') start ;; 'stop') stop ;; 'restart') stop start ;; 'condrestart') loadconf result=`$ZM_PATH_BIN/zmdc.pl check` if [ "$result" = "running" ]; then $ZM_PATH_BIN/zmdc.pl shutdown > /dev/null rm -f $LOCKFILE start fi ;; 'status') status httpd status mysqld zmstatus ;; *) echo "Usage: $0 { start | stop | restart | condrestart | status }" RETVAL=1 ;; esac exit $RETVAL ZoneMinder-1.26.5/scripts/zmaudit.pl.in000066400000000000000000000501751225361755400200070ustar00rootroot00000000000000#!/usr/bin/perl -wT # # ========================================================================== # # ZoneMinder Audit Script, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This script checks for consistency between the event filesystem and # the database. If events are found in one and not the other they are # deleted (optionally). Additionally any monitor event directories that # do not correspond to a database monitor are similarly disposed of. # However monitors in the database that don't have a directory are left # alone as this is valid if they are newly created and have no events # yet. # use strict; use bytes; # ========================================================================== # # These are the elements you can edit to suit your installation # # ========================================================================== use constant MIN_AGE => 300; # Minimum age when we will delete anything use constant MAX_AGED_DIRS => 10; # Number of event dirs to check age on use constant RECOVER_TAG => "(r)"; # Tag to append to event name when recovered use constant RECOVER_TEXT => "Recovered."; # Text to append to event notes when recovered # ========================================================================== # # You shouldn't need to change anything from here downwards # # ========================================================================== @EXTRA_PERL_LIB@ use ZoneMinder; use DBI; use POSIX; use File::Find; use Time::HiRes qw/gettimeofday/; use Getopt::Long; use constant IMAGE_PATH => ZM_PATH_WEB.'/'.ZM_DIR_IMAGES; use constant EVENT_PATH => (ZM_DIR_EVENTS=~m|/|)?ZM_DIR_EVENTS:(ZM_PATH_WEB.'/'.ZM_DIR_EVENTS); $| = 1; $ENV{PATH} = '/bin:/usr/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; my $report = 0; my $interactive = 0; my $continuous = 0; sub usage { print( " Usage: zmaudit.pl [-r,-report|-i,-interactive] Parameters are :- -r, --report - Just report don't actually do anything -i, --interactive - Ask before applying any changes -c, --continuous - Run continuously "); exit( -1 ); } sub aud_print( $ ); sub confirm( ;$$ ); sub deleteSwapImage(); logInit(); logSetSignal(); if ( !GetOptions( 'report'=>\$report, 'interactive'=>\$interactive, 'continuous'=>\$continuous ) ) { usage(); } if ( ($report + $interactive + $continuous) > 1 ) { print( STDERR "Error, only option may be specified\n" ); usage(); } my $dbh = zmDbConnect(); chdir( EVENT_PATH ); my $max_image_age = 6/24; # 6 hours my $max_swap_age = 24/24; # 24 hours my $image_path = IMAGE_PATH; my $swap_image_path = ZM_PATH_SWAP; my $loop = 1; my $cleaned = 0; MAIN: while( $loop ) { my $db_monitors; my $monitorSelectSql = "select Id from Monitors order by Id"; my $monitorSelectSth = $dbh->prepare_cached( $monitorSelectSql ) or Fatal( "Can't prepare '$monitorSelectSql': ".$dbh->errstr() ); my $eventSelectSql = "select Id, (unix_timestamp() - unix_timestamp(StartTime)) as Age from Events where MonitorId = ? order by Id"; my $eventSelectSth = $dbh->prepare_cached( $eventSelectSql ) or Fatal( "Can't prepare '$eventSelectSql': ".$dbh->errstr() ); $cleaned = 0; my $res = $monitorSelectSth->execute() or Fatal( "Can't execute: ".$monitorSelectSth->errstr() ); while( my $monitor = $monitorSelectSth->fetchrow_hashref() ) { Debug( "Found database monitor '$monitor->{Id}'" ); my $db_events = $db_monitors->{$monitor->{Id}} = {}; my $res = $eventSelectSth->execute( $monitor->{Id} ) or Fatal( "Can't execute: ".$eventSelectSth->errstr() ); while ( my $event = $eventSelectSth->fetchrow_hashref() ) { $db_events->{$event->{Id}} = $event->{Age}; } Debug( "Got ".int(keys(%$db_events))." events\n" ); $eventSelectSth->finish(); } $monitorSelectSth->finish(); my $fs_monitors; foreach my $monitor ( <[0-9]*> ) { Debug( "Found filesystem monitor '$monitor'" ); my $fs_events = $fs_monitors->{$monitor} = {}; ( my $monitor_dir ) = ( $monitor =~ /^(.*)$/ ); # De-taint if ( ZM_USE_DEEP_STORAGE ) { foreach my $day_dir ( <$monitor_dir/*/*/*> ) { Debug( "Checking $day_dir" ); ( $day_dir ) = ( $day_dir =~ /^(.*)$/ ); # De-taint chdir( $day_dir ); opendir( DIR, "." ) or Fatal( "Can't open directory '$day_dir': $!" ); my @event_links = sort { $b <=> $a } grep { -l $_ } readdir( DIR ); closedir( DIR ); my $count = 0; foreach my $event_link ( @event_links ) { Debug( "Checking link $event_link" ); ( my $event = $event_link ) =~ s/^.*\.//; my $event_path = readlink( $event_link ); if ( $count++ > MAX_AGED_DIRS ) { $fs_events->{$event} = -1; } else { if ( !-e $event_path ) { aud_print( "Event link $day_dir/$event_link does not point to valid target" ); if ( confirm() ) { ( $event_link ) = ( $event_link =~ /^(.*)$/ ); # De-taint unlink( $event_link ); $cleaned = 1; } } else { $fs_events->{$event} = (time() - ($^T - ((-M $event_path) * 24*60*60))); } } } chdir( EVENT_PATH ); } } else { chdir( $monitor_dir ); opendir( DIR, "." ) or Fatal( "Can't open directory '$monitor_dir': $!" ); my @temp_events = sort { $b <=> $a } grep { -d $_ && $_ =~ /^\d+$/ } readdir( DIR ); closedir( DIR ); my $count = 0; foreach my $event ( @temp_events ) { if ( $count++ > MAX_AGED_DIRS ) { $fs_events->{$event} = -1; } else { $fs_events->{$event} = (time() - ($^T - ((-M $event) * 24*60*60))); } } chdir( EVENT_PATH ); } Debug( "Got ".int(keys(%$fs_events))." events\n" ); } redo MAIN if ( $cleaned ); $cleaned = 0; while ( my ( $fs_monitor, $fs_events ) = each(%$fs_monitors) ) { if ( my $db_events = $db_monitors->{$fs_monitor} ) { if ( $fs_events ) { while ( my ( $fs_event, $age ) = each(%$fs_events ) ) { if ( !defined($db_events->{$fs_event}) && ($age < 0 || ($age > MIN_AGE)) ) { aud_print( "Filesystem event '$fs_monitor/$fs_event' does not exist in database" ); if ( confirm() ) { deleteEventFiles( $fs_event, $fs_monitor ); $cleaned = 1; } } } } } else { aud_print( "Filesystem monitor '$fs_monitor' does not exist in database" ); if ( confirm() ) { my $command = "rm -rf $fs_monitor"; executeShellCommand( $command ); $cleaned = 1; } } } my $monitor_links; foreach my $link ( <*> ) { next if ( !-l $link ); next if ( -e $link ); aud_print( "Filesystem monitor link '$link' does not point to valid monitor directory" ); if ( confirm() ) { ( $link ) = ( $link =~ /^(.*)$/ ); # De-taint my $command = "rm $link"; executeShellCommand( $command ); $cleaned = 1; } } redo MAIN if ( $cleaned ); $cleaned = 0; my $deleteMonitorSql = "delete low_priority from Monitors where Id = ?"; my $deleteMonitorSth = $dbh->prepare_cached( $deleteMonitorSql ) or Fatal( "Can't prepare '$deleteMonitorSql': ".$dbh->errstr() ); my $deleteEventSql = "delete low_priority from Events where Id = ?"; my $deleteEventSth = $dbh->prepare_cached( $deleteEventSql ) or Fatal( "Can't prepare '$deleteEventSql': ".$dbh->errstr() ); my $deleteFramesSql = "delete low_priority from Frames where EventId = ?"; my $deleteFramesSth = $dbh->prepare_cached( $deleteFramesSql ) or Fatal( "Can't prepare '$deleteFramesSql': ".$dbh->errstr() ); my $deleteStatsSql = "delete low_priority from Stats where EventId = ?"; my $deleteStatsSth = $dbh->prepare_cached( $deleteStatsSql ) or Fatal( "Can't prepare '$deleteStatsSql': ".$dbh->errstr() ); while ( my ( $db_monitor, $db_events ) = each(%$db_monitors) ) { if ( my $fs_events = $fs_monitors->{$db_monitor} ) { if ( $db_events ) { while ( my ( $db_event, $age ) = each(%$db_events ) ) { if ( !defined($fs_events->{$db_event}) && ($age > MIN_AGE) ) { aud_print( "Database event '$db_monitor/$db_event' does not exist in filesystem" ); if ( confirm() ) { my $res = $deleteEventSth->execute( $db_event ) or Fatal( "Can't execute: ".$deleteEventSth->errstr() ); $res = $deleteFramesSth->execute( $db_event ) or Fatal( "Can't execute: ".$deleteFramesSth->errstr() ); $res = $deleteStatsSth->execute( $db_event ) or Fatal( "Can't execute: ".$deleteStatsSth->errstr() ); $cleaned = 1; } } } } } else { #aud_print( "Database monitor '$db_monitor' does not exist in filesystem" ); #if ( confirm() ) #{ # We don't actually do this in case it's new #my $res = $deleteMonitorSth->execute( $db_monitor ) or Fatal( "Can't execute: ".$deleteMonitorSth->errstr() ); #$cleaned = 1; #} } } redo MAIN if ( $cleaned ); # Remove orphaned events (with no monitor) $cleaned = 0; my $selectOrphanedEventsSql = "select Events.Id, Events.Name from Events left join Monitors on (Events.MonitorId = Monitors.Id) where isnull(Monitors.Id)"; my $selectOrphanedEventsSth = $dbh->prepare_cached( $selectOrphanedEventsSql ) or Fatal( "Can't prepare '$selectOrphanedEventsSql': ".$dbh->errstr() ); $res = $selectOrphanedEventsSth->execute() or Fatal( "Can't execute: ".$selectOrphanedEventsSth->errstr() ); while( my $event = $selectOrphanedEventsSth->fetchrow_hashref() ) { aud_print( "Found orphaned event with no monitor '$event->{Id}'" ); if ( confirm() ) { $res = $deleteEventSth->execute( $event->{Id} ) or Fatal( "Can't execute: ".$deleteEventSth->errstr() ); $cleaned = 1; } } $selectOrphanedEventsSth->finish(); redo MAIN if ( $cleaned ); # Remove empty events (with no frames) $cleaned = 0; my $selectEmptyEventsSql = "select * from Events as E left join Frames as F on (E.Id = F.EventId) where isnull(F.EventId) and now() - interval ".MIN_AGE." second > E.StartTime"; my $selectEmptyEventsSth = $dbh->prepare_cached( $selectEmptyEventsSql ) or Fatal( "Can't prepare '$selectEmptyEventsSql': ".$dbh->errstr() ); $res = $selectEmptyEventsSth->execute() or Fatal( "Can't execute: ".$selectEmptyEventsSth->errstr() ); while( my $event = $selectEmptyEventsSth->fetchrow_hashref() ) { aud_print( "Found empty event with no frame records '$event->{Id}'" ); if ( confirm() ) { $res = $deleteEventSth->execute( $event->{Id} ) or Fatal( "Can't execute: ".$deleteEventSth->errstr() ); $cleaned = 1; } } $selectEmptyEventsSth->finish(); redo MAIN if ( $cleaned ); # Remove orphaned frame records $cleaned = 0; my $selectOrphanedFramesSql = "select distinct EventId from Frames where EventId not in (select Id from Events)"; my $selectOrphanedFramesSth = $dbh->prepare_cached( $selectOrphanedFramesSql ) or Fatal( "Can't prepare '$selectOrphanedFramesSql': ".$dbh->errstr() ); $res = $selectOrphanedFramesSth->execute() or Fatal( "Can't execute: ".$selectOrphanedFramesSth->errstr() ); while( my $frame = $selectOrphanedFramesSth->fetchrow_hashref() ) { aud_print( "Found orphaned frame records for event '$frame->{EventId}'" ); if ( confirm() ) { $res = $deleteFramesSth->execute( $frame->{EventId} ) or Fatal( "Can't execute: ".$deleteFramesSth->errstr() ); $cleaned = 1; } } $selectOrphanedFramesSth->finish(); redo MAIN if ( $cleaned ); # Remove orphaned stats records $cleaned = 0; my $selectOrphanedStatsSql = "select distinct EventId from Stats where EventId not in (select Id from Events)"; my $selectOrphanedStatsSth = $dbh->prepare_cached( $selectOrphanedStatsSql ) or Fatal( "Can't prepare '$selectOrphanedStatsSql': ".$dbh->errstr() ); $res = $selectOrphanedStatsSth->execute() or Fatal( "Can't execute: ".$selectOrphanedStatsSth->errstr() ); while( my $stat = $selectOrphanedStatsSth->fetchrow_hashref() ) { aud_print( "Found orphaned statistic records for event '$stat->{EventId}'" ); if ( confirm() ) { $res = $deleteStatsSth->execute( $stat->{EventId} ) or Fatal( "Can't execute: ".$deleteStatsSth->errstr() ); $cleaned = 1; } } $selectOrphanedStatsSth->finish(); redo MAIN if ( $cleaned ); # New audit to close any events that were left open for longer than MIN_AGE seconds my $selectUnclosedEventsSql = "select E.Id, max(F.TimeStamp) as EndTime, unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartTime) as Length, max(F.FrameId) as Frames, count(if(F.Score>0,1,NULL)) as AlarmFrames, sum(F.Score) as TotScore, max(F.Score) as MaxScore, M.EventPrefix as Prefix from Events as E left join Monitors as M on E.MonitorId = M.Id inner join Frames as F on E.Id = F.EventId where isnull(E.Frames) or isnull(E.EndTime) group by E.Id having EndTime < (now() - interval ".MIN_AGE." second)"; my $selectUnclosedEventsSth = $dbh->prepare_cached( $selectUnclosedEventsSql ) or Fatal( "Can't prepare '$selectUnclosedEventsSql': ".$dbh->errstr() ); my $updateUnclosedEventsSql = "update low_priority Events set Name = ?, EndTime = ?, Length = ?, Frames = ?, AlarmFrames = ?, TotScore = ?, AvgScore = ?, MaxScore = ?, Notes = concat_ws( ' ', Notes, ? ) where Id = ?"; my $updateUnclosedEventsSth = $dbh->prepare_cached( $updateUnclosedEventsSql ) or Fatal( "Can't prepare '$updateUnclosedEventsSql': ".$dbh->errstr() ); $res = $selectUnclosedEventsSth->execute() or Fatal( "Can't execute: ".$selectUnclosedEventsSth->errstr() ); while( my $event = $selectUnclosedEventsSth->fetchrow_hashref() ) { aud_print( "Found open event '$event->{Id}'" ); if ( confirm( 'close', 'closing' ) ) { $res = $updateUnclosedEventsSth->execute( sprintf( "%s%d%s", $event->{Prefix}, $event->{Id}, RECOVER_TAG ), $event->{EndTime}, $event->{Length}, $event->{Frames}, $event->{AlarmFrames}, $event->{TotScore}, $event->{AlarmFrames}?int($event->{TotScore}/$event->{AlarmFrames}):0, $event->{MaxScore}, RECOVER_TEXT, $event->{Id} ) or Fatal( "Can't execute: ".$updateUnclosedEventsSth->errstr() ); } } $selectUnclosedEventsSth->finish(); # Now delete any old image files if ( my @old_files = grep { -M > $max_image_age } <$image_path/*.{jpg,gif,wbmp}> ) { aud_print( "Deleting ".int(@old_files)." old images\n" ); my $untainted_old_files = join( ";", @old_files ); ( $untainted_old_files ) = ( $untainted_old_files =~ /^(.*)$/ ); unlink( split( /;/, $untainted_old_files ) ); } # Now delete any old swap files ( my $swap_image_root ) = ( $swap_image_path =~ /^(.*)$/ ); # De-taint File::Find::find( { wanted=>\&deleteSwapImage, untaint=>1 }, $swap_image_root ); # Prune the Logs table if required if ( ZM_LOG_DATABASE_LIMIT ) { if ( ZM_LOG_DATABASE_LIMIT =~ /^\d+$/ ) { # Number of rows my $selectLogRowCountSql = "select count(*) as Rows from Logs"; my $selectLogRowCountSth = $dbh->prepare_cached( $selectLogRowCountSql ) or Fatal( "Can't prepare '$selectLogRowCountSql': ".$dbh->errstr() ); $res = $selectLogRowCountSth->execute() or Fatal( "Can't execute: ".$selectLogRowCountSth->errstr() ); my $row = $selectLogRowCountSth->fetchrow_hashref(); my $logRows = $row->{Rows}; $selectLogRowCountSth->finish(); if ( $logRows > ZM_LOG_DATABASE_LIMIT ) { my $deleteLogByRowsSql = "delete low_priority from Logs order by TimeKey asc limit ?"; my $deleteLogByRowsSth = $dbh->prepare_cached( $deleteLogByRowsSql ) or Fatal( "Can't prepare '$deleteLogByRowsSql': ".$dbh->errstr() ); $res = $deleteLogByRowsSth->execute( $logRows - ZM_LOG_DATABASE_LIMIT ) or Fatal( "Can't execute: ".$deleteLogByRowsSth->errstr() ); aud_print( "Deleted ".$deleteLogByRowsSth->rows()." log table entries by count\n" ) if ( $deleteLogByRowsSth->rows() ); } } else { # Time of record my $deleteLogByTimeSql = "delete low_priority from Logs where TimeKey < unix_timestamp(now() - interval ".ZM_LOG_DATABASE_LIMIT.")"; my $deleteLogByTimeSth = $dbh->prepare_cached( $deleteLogByTimeSql ) or Fatal( "Can't prepare '$deleteLogByTimeSql': ".$dbh->errstr() ); $res = $deleteLogByTimeSth->execute() or Fatal( "Can't execute: ".$deleteLogByTimeSth->errstr() ); aud_print( "Deleted ".$deleteLogByTimeSth->rows()." log table entries by time\n" ) if ( $deleteLogByTimeSth->rows() ); } } $loop = $continuous; sleep( ZM_AUDIT_CHECK_INTERVAL ) if ( $continuous ); }; exit( 0 ); sub aud_print( $ ) { my $string = shift; if ( !$continuous ) { print( $string ); } else { Info( $string ); } } sub confirm( ;$$ ) { my $prompt = shift || "delete"; my $action = shift || "deleting"; my $yesno = 0; if ( $report ) { print( "\n" ); } elsif ( $interactive ) { print( ", $prompt y/n: " ); my $char = <>; chomp( $char ); if ( $char eq 'q' ) { exit( 0 ); } if ( !$char ) { $char = 'y'; } $yesno = ( $char =~ /[yY]/ ); } else { if ( !$continuous ) { print( ", $action\n" ); } else { Info( $action ); } $yesno = 1; } return( $yesno ); } sub deleteSwapImage() { my $file = $_; if ( $file !~ /^zmswap-/ ) { return; } # Ignore directories if ( -d $file ) { return; } if ( -M $file > $max_swap_age ) { Debug( "Deleting $file" ); #unlink( $file ); } } ZoneMinder-1.26.5/scripts/zmcontrol.pl.in000066400000000000000000000142641225361755400203600ustar00rootroot00000000000000#!/usr/bin/perl -wT # # ========================================================================== # # ZoneMinder Control Script, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This script continuously monitors the recorded events for the given # monitor and applies any filters which would delete and/or upload # matching events # use strict; @EXTRA_PERL_LIB@ use ZoneMinder; use Getopt::Long; use POSIX qw/strftime EPIPE/; use Socket; use Data::Dumper; use Module::Load; use constant MAX_CONNECT_DELAY => 10; use constant MAX_COMMAND_WAIT => 1800; $| = 1; $ENV{PATH} = '/bin:/usr/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; sub Usage { print( " Usage: zmcontrol.pl --id --command= "); exit( -1 ); } logInit(); my $arg_string = join( " ", @ARGV ); my $id; my %options; if ( !GetOptions( 'id=i'=>\$id, 'command=s'=>\$options{command}, 'xcoord=i'=>\$options{xcoord}, 'ycoord=i'=>\$options{ycoord}, 'speed=i'=>\$options{speed}, 'step=i'=>\$options{step}, 'panspeed=i'=>\$options{panspeed}, 'tiltspeed=i'=>\$options{tiltspeed}, 'panstep=i'=>\$options{panstep}, 'tiltstep=i'=>\$options{tiltstep}, 'preset=i'=>\$options{preset}, 'autostop'=>\$options{autostop}, ) ) { Usage(); } if ( !$id || !$options{command} ) { print( STDERR "Please give a valid monitor id and command\n" ); Usage(); } ( $id ) = $id =~ /^(\w+)$/; Debug( $arg_string ); my $sock_file = ZM_PATH_SOCKS.'/zmcontrol-'.$id.'.sock'; socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); my $saddr = sockaddr_un( $sock_file ); my $server_up = connect( CLIENT, $saddr ); if ( !$server_up ) { # The server isn't there my $monitor = zmDbGetMonitorAndControl( $id ); if ( !$monitor ) { Fatal( "Unable to load control data for monitor $id" ); } my $protocol = $monitor->{Protocol}; if ( -x $protocol ) { # Protocol is actually a script! # Holdover from previous versions my $command .= $protocol.' '.$arg_string; Debug( $command."\n" ); my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { chomp( $output ); Debug( "Output: $output\n" ); } if ( $status ) { Error( "Command '$command' exited with status: $status\n" ); exit( $status ); } exit( 0 ); } Info( "Starting control server $id/$protocol" ); close( CLIENT ); if ( my $cpid = fork() ) { logReinit(); # Parent process just sleep and fall through socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or die( "Can't open socket: $!" ); my $attempts = 0; while (!connect( CLIENT, $saddr )) { $attempts++; Fatal( "Can't connect: $!" ) if ($attempts > MAX_CONNECT_DELAY); sleep(1); } } elsif ( defined($cpid) ) { close( STDOUT ); close( STDERR ); setpgrp(); logReinit(); Info( "Control server $id/$protocol starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() ) ); $0 = $0." --id $id"; load "ZoneMinder::Control::$protocol"; my $control = "ZoneMinder::Control::$protocol"->new( $id ); my $control_key = $control->getKey(); $control->loadMonitor(); $control->open(); socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); unlink( $sock_file ); bind( SERVER, $saddr ) or Fatal( "Can't bind: $!" ); listen( SERVER, SOMAXCONN ) or Fatal( "Can't listen: $!" ); my $rin = ''; vec( $rin, fileno(SERVER), 1 ) = 1; my $win = $rin; my $ein = $win; my $timeout = MAX_COMMAND_WAIT; while( 1 ) { my $nfound = select( my $rout = $rin, undef, undef, $timeout ); if ( $nfound > 0 ) { if ( vec( $rout, fileno(SERVER), 1 ) ) { my $paddr = accept( CLIENT, SERVER ); my $message = ; next if ( !$message ); my $params = jsonDecode( $message ); #Debug( Dumper( $params ) ); my $command = $params->{command}; $control->$command( $params ); close( CLIENT ); } else { Fatal( "Bogus descriptor" ); } } elsif ( $nfound < 0 ) { if ( $! == EPIPE ) { Error( "Can't select: $!" ); } else { Fatal( "Can't select: $!" ); } } else { #print( "Select timed out\n" ); last; } } Info( "Control server $id/$protocol exiting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() ) ); unlink( $sock_file ); $control->close(); exit( 0 ); } else { Fatal( "Can't fork: $!" ); } } # The server is there, connect to it #print( "Writing commands\n" ); CLIENT->autoflush(); my $message = jsonEncode( \%options ); print( CLIENT $message ); shutdown( CLIENT, 1 ); exit( 0 ); ZoneMinder-1.26.5/scripts/zmdbbackup.in000066400000000000000000000022241225361755400200320ustar00rootroot00000000000000#!/bin/bash #=============================================================================== # # FILE: zmdbbackup # # USAGE: ./zmdbbackup # # DESCRIPTION: Uses mysqldump to backup the config info in the zm DB # OPTIONS: --- None # REQUIREMENTS: --- mysqldump # BUGS: --- # NOTES: --- # AUTHOR: Ross Melin # COMPANY: # VERSION: 2.0 # CREATED: 05/26/2006 06:21:00 AM PDT # REVISION: --- #=============================================================================== # Edit these to suit your configuration ZM_CONFIG=@ZM_CONFIG@ source $ZM_CONFIG # ZM_VERSION in the config is now deprecated but will likely still exist in people's config files. This will override it. ZM_VERSION=@VERSION@ MYSQLDUMP=/usr/bin/mysqldump BACKUP_PATH=/var/lib/zm BACKUP_FILE=zm_backup.sql DUMPOPTS="--user=$ZM_DB_USER --password=$ZM_DB_PASS --opt" TABLES="Config Filters Groups Monitors States TriggersX10 Users Zones" OUTFILE="$BACKUP_PATH/$BACKUP_FILE" echo "-- -- Created by zm_db_backup for ZoneMinder Version $ZM_VERSION --" > $OUTFILE $MYSQLDUMP $DUMPOPTS zm $TABLES >> $OUTFILE exit 0 ZoneMinder-1.26.5/scripts/zmdbrestore.in000066400000000000000000000113731225361755400202550ustar00rootroot00000000000000#!/bin/bash #=============================================================================== # # FILE: zmdbrestore # # USAGE: ./zmdbrestore # # DESCRIPTION: Restore a ZoneMinder DB from a backup created by zm_db_backup # # OPTIONS: --- # REQUIREMENTS: --- # BUGS: --- # NOTES: --- # AUTHOR: (), # COMPANY: # VERSION: 1.0 # CREATED: 05/29/2006 04:45:06 PM PDT # REVISION: --- #=============================================================================== ZM_CONFIG=@ZM_CONFIG@ ZM_BACKUP=/var/lib/zm/zm_backup.sql EVENTS_DIR=events loadcfg() { if [ -f $ZM_CONFIG ]; then . $ZM_CONFIG else echo "ERROR: $ZM_CONFIG not found." exit 1 fi } chkcfg(){ for n in ZM_DB_HOST ZM_DB_NAME ZM_DB_USER ZM_DB_PASS; do eval "val=\$$n" if [ "$val" = "" ]; then echo "ERROR($ZM_CONFIG): $n should not be empty." echo "Enter a $n for ZM to use the Database." if [ "$n" = "ZM_DB_PASS" ]; then echo -n "Will not echo on screen $n : " stty -echo # Turns off screen echo. read newval stty echo # Restores screen echo. echo "" ### The following can be used to generate a random password # randstr newval 16 else echo -n "$n : " read newval fi cp $ZM_CONFIG /tmp/$$ && sed 's/^'$n='.*$/'$n=$newval'/g' /tmp/$$ >$ZM_CONFIG fi done if [ "$ZM_DB_HOST" = "localhost" ] then ClientHost=localhost else ClientHost=`hostname` fi } reloadcfg(){ loadcfg } chk_backup_ver(){ if [ -e $ZM_BACKUP ] then BACKUP_VER=$(cat $ZM_BACKUP | head -n 2 |tail -n 1 |cut -f 8 -d " ") else echo "$ZM_BACKUP doesn't exist" exit 1 fi if [ $BACKUP_VER != $ZM_VERSION ] then echo "$ZM_BACKUP is from version $BACKUP_VER" echo "ZoneMinder version is $ZM_VERSION" exit 1 fi } getmylogin(){ echo "Enter MySQL Administrator username" echo "(Default: root and password is blank)" echo -n "MySQL Admin: " read MYADMIN echo -n "Password: " read MYPASS if [ "X$MYPASS" != "X" ]; then MYPASS="-p$MYPASS"; fi echo "\q" |mysql -u $MYADMIN $MYPASS || exit 0 } checkfordb(){ if echo "show databases" |mysql -u $MYADMIN "$MYPASS" |grep zm then echo "A $ZM_DB_NAME database exists." while [ true ] do echo "Choose one of the following options:" echo "[D]rop the old database and reinitialize" echo "[E]xit and do nothing" read OPTION case $OPTION in "D"|"d") echo "drop database zm;"|mysql -u $MYADMIN $MYPASS return ;; "E"|"e") exit 0 ;; esac done fi } initdb(){ sql=/tmp/zm.crdb.sql echo "" >$sql chmod 600 $sql echo "CREATE DATABASE $ZM_DB_NAME;" >>$sql echo "USE $ZM_DB_NAME;" >>$sql echo "GRANT all on $ZM_DB_NAME.* TO '$ZM_DB_USER'@'$ClientHost' IDENTIFIED BY '$ZM_DB_PASS';" >>$sql cat $sql | mysql -B -h $ZM_DB_HOST -u $MYADMIN $MYPASS rm -f $sql cat $ZM_PATH_UPDATE/zm_create.sql | mysql -h $ZM_DB_HOST -u $ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME } restoredb(){ if [ -e $ZM_BACKUP ] then cat $ZM_BACKUP | mysql -h $ZM_DB_HOST -u $ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME else echo "$ZM_BACKUP doesn't exist" exit 1 fi } restore_events(){ for SQL in $(find $ZM_PATH_WEB/$EVENTS_DIR -name .sql) do cat $SQL | mysql -h $ZM_DB_HOST -u $ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME done } loadcfg chkcfg reloadcfg chk_backup_ver getmylogin checkfordb initdb restoredb restore_events exit 0 ZoneMinder-1.26.5/scripts/zmdc.pl.in000066400000000000000000000475201225361755400172670ustar00rootroot00000000000000#!/usr/bin/perl -wT # # ========================================================================== # # ZoneMinder Daemon Control Script, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This script is the gateway for controlling the various ZoneMinder # daemons. All starting, stopping and restarting goes through here. # On the first invocation it starts up a server which subsequently # records what's running and what's not. Other invocations just # connect to the server and pass instructions to it. # use strict; use bytes; # ========================================================================== # # User config # # ========================================================================== use constant MAX_CONNECT_DELAY => 10; # ========================================================================== # # Don't change anything from here on down # # ========================================================================== @EXTRA_PERL_LIB@ use ZoneMinder; use POSIX; use Socket; use IO::Handle; use Data::Dumper; use constant SOCK_FILE => ZM_PATH_SOCKS.'/zmdc.sock'; $| = 1; $ENV{PATH} = '/bin:/usr/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; my @daemons = ( 'zmc', 'zma', 'zmf', 'zmfilter.pl', 'zmaudit.pl', 'zmtrigger.pl', 'zmx10.pl', 'zmwatch.pl', 'zmupdate.pl', 'zmtrack.pl' ); sub Usage { print( " Usage: zmdc.pl [daemon [options]] Parameters are :- - One of 'startup|shutdown|status|check|logrot' or 'start|stop|restart|reload'. [daemon [options]] - Daemon name and options, required for second group of commands "); exit( -1 ); } my $command = shift @ARGV; if( !$command ) { print( STDERR "No command given\n" ); Usage(); } my $needs_daemon = $command !~ /(?:startup|shutdown|status|check|logrot)/; my $daemon = shift( @ARGV ); if( $needs_daemon && !$daemon ) { print( STDERR "No daemon given\n" ); Usage(); } my @args; my $daemon_patt = '('.join( '|', @daemons ).')'; if ( $needs_daemon ) { if ( $daemon =~ /^${daemon_patt}$/ ) { $daemon = $1; } else { print( STDERR "Invalid daemon '$daemon' specified" ); Usage(); } } foreach my $arg ( @ARGV ) { # Detaint arguments, if they look ok #if ( $arg =~ /^(-{0,2}[\w]+)/ ) if ( $arg =~ /^(-{0,2}[\w\/?&=.-]+)$/ ) { push( @args, $1 ); } else { print( STDERR "Bogus argument '$arg' found" ); exit( -1 ); } } socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); my $saddr = sockaddr_un( SOCK_FILE ); my $server_up = connect( CLIENT, $saddr ); if ( !$server_up ) { if ( $command eq "logrot" ) { exit(); } if ( $command eq "check" ) { print( "stopped\n" ); exit(); } elsif ( $command ne "startup" ) { print( "Unable to connect to server\n" ); exit( -1 ); } # The server isn't there print( "Starting server\n" ); close( CLIENT ); if ( my $cpid = fork() ) { logInit(); # Parent process just sleep and fall through socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); my $attempts = 0; while (!connect( CLIENT, $saddr )) { $attempts++; Fatal( "Can't connect: $!" ) if ($attempts > MAX_CONNECT_DELAY); sleep(1); } } elsif ( defined($cpid) ) { ZMServer::run(); } else { Fatal( "Can't fork: $!" ); } } if ( $command eq "check" && !$daemon ) { print( "running\n" ); exit(); } elsif ( $command eq "startup" ) { # Our work here is done exit() if ( !$server_up ); } # The server is there, connect to it #print( "Writing commands\n" ); CLIENT->autoflush(); my $message = "$command"; $message .= ";$daemon" if ( $daemon ); $message .= ";".join( ';', @args ) if ( @args ); print( CLIENT $message ); shutdown( CLIENT, 1 ); while ( my $line = ) { chomp( $line ); print( "$line\n" ); } # And we're done! close( CLIENT ); #print( "Finished writing, bye\n" ); exit; package ZMServer; use strict; use bytes; @EXTRA_PERL_LIB@ use ZoneMinder; use POSIX; use Socket; use IO::Handle; use Data::Dumper; our %cmd_hash; our %pid_hash; sub run { my $fd = 0; while( $fd < POSIX::sysconf( &POSIX::_SC_OPEN_MAX ) ) { POSIX::close( $fd++ ); } setpgrp(); logInit(); dPrint( ZoneMinder::Logger::INFO, "Server starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); if ( open( PID, ">".ZM_PID ) ) { print( PID $$ ); close( PID ); } killAll( 1 ); socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); unlink( main::SOCK_FILE ); bind( SERVER, $saddr ) or Fatal( "Can't bind: $!" ); listen( SERVER, SOMAXCONN ) or Fatal( "Can't listen: $!" ); $SIG{CHLD} = \&reaper; $SIG{INT} = \&shutdownAll; $SIG{TERM} = \&shutdownAll; $SIG{ABRT} = \&shutdownAll; $SIG{HUP} = \&logrot; my $rin = ''; vec( $rin, fileno(SERVER), 1 ) = 1; my $win = $rin; my $ein = $win; my $timeout = 0.1; while( 1 ) { my $nfound = select( my $rout = $rin, undef, undef, $timeout ); if ( $nfound > 0 ) { if ( vec( $rout, fileno(SERVER), 1 ) ) { my $paddr = accept( CLIENT, SERVER ); my $message = ; next if ( !$message ); my ( $command, $daemon, @args ) = split( /;/, $message ); if ( $command eq 'start' ) { start( $daemon, @args ); } elsif ( $command eq 'stop' ) { stop( $daemon, @args ); } elsif ( $command eq 'restart' ) { restart( $daemon, @args ); } elsif ( $command eq 'reload' ) { reload( $daemon, @args ); } elsif ( $command eq 'startup' ) { # Do nothing, this is all we're here for dPrint( ZoneMinder::Logger::WARNING, "Already running, ignoring command '$command'\n" ); } elsif ( $command eq 'shutdown' ) { shutdownAll(); } elsif ( $command eq 'check' ) { check( $daemon, @args ); } elsif ( $command eq 'status' ) { if ( $daemon ) { status( $daemon, @args ); } else { status(); } } elsif ( $command eq 'logrot' ) { logrot(); } else { dPrint( ZoneMinder::Logger::ERROR, "Invalid command '$command'\n" ); } close( CLIENT ); } else { Fatal( "Bogus descriptor" ); } } elsif ( $nfound < 0 ) { if ( $! == EINTR ) { # Dead child, will be reaped #print( "Probable dead child\n" ); # See if it needs to start up again restartPending(); } elsif ( $! == EPIPE ) { Error( "Can't select: $!" ); } else { Fatal( "Can't select: $!" ); } } else { #print( "Select timed out\n" ); restartPending(); } } dPrint( ZoneMinder::Logger::INFO, "Server exiting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); unlink( main::SOCK_FILE ); unlink( ZM_PID ); exit(); } sub cPrint { if ( fileno(CLIENT) ) { print CLIENT @_ } } sub dPrint { my $logLevel = shift; if ( fileno(CLIENT) ) { print CLIENT @_ } if ( $logLevel == ZoneMinder::Logger::DEBUG ) { Debug( @_ ); } elsif ( $logLevel == ZoneMinder::Logger::INFO ) { Info( @_ ); } elsif ( $logLevel == ZoneMinder::Logger::WARNING ) { Warning( @_ ); } elsif ( $logLevel == ZoneMinder::Logger::ERROR ) { Error( @_ ); } elsif ( $logLevel == ZoneMinder::Logger::FATAL ) { Fatal( @_ ); } } sub start { my $daemon = shift; my @args = @_; my $command = $daemon; $command .= ' '.join( ' ', ( @args ) ) if ( @args ); my $process = $cmd_hash{$command}; if ( !$process ) { # It's not running, or at least it's not been started by us $process = { daemon=>$daemon, args=>\@args, command=>$command, keepalive=>!undef }; } elsif ( $process->{pid} && $pid_hash{$process->{pid}} ) { dPrint( ZoneMinder::Logger::INFO, "'$process->{command}' already running at ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ).", pid = $process->{pid}\n" ); return(); } my $sigset = POSIX::SigSet->new; my $blockset = POSIX::SigSet->new( SIGCHLD ); sigprocmask( SIG_BLOCK, $blockset, $sigset ) or Fatal( "Can't block SIGCHLD: $!" ); if ( my $cpid = fork() ) { logReinit(); $process->{pid} = $cpid; $process->{started} = time(); delete( $process->{pending} ); dPrint( ZoneMinder::Logger::INFO, "'$command' starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ).", pid = $process->{pid}\n" ); $cmd_hash{$process->{command}} = $pid_hash{$cpid} = $process; sigprocmask( SIG_SETMASK, $sigset ) or Fatal( "Can't restore SIGCHLD: $!" ); } elsif ( defined($cpid ) ) { logReinit(); dPrint( ZoneMinder::Logger::INFO, "'".join( ' ', ( $daemon, @args ) )."' started at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); if ( $daemon =~ /^${daemon_patt}$/ ) { $daemon = ZM_PATH_BIN.'/'.$1; } else { Fatal( "Invalid daemon '$daemon' specified" ); } my @good_args; foreach my $arg ( @args ) { # Detaint arguments, if they look ok if ( $arg =~ /^(-{0,2}[\w\/?&=.-]+)$/ ) { push( @good_args, $1 ); } else { Fatal( "Bogus argument '$arg' found" ); } } logTerm(); my $fd = 0; while( $fd < POSIX::sysconf( &POSIX::_SC_OPEN_MAX ) ) { POSIX::close( $fd++ ); } # Child process $SIG{CHLD} = 'DEFAULT'; $SIG{INT} = 'DEFAULT'; $SIG{TERM} = 'DEFAULT'; $SIG{ABRT} = 'DEFAULT'; exec( $daemon, @good_args ) or Fatal( "Can't exec: $!" ); } else { Fatal( "Can't fork: $!" ); } } sub _stop { my $final = shift; my $daemon = shift; my @args = @_; my $command = $daemon; $command .= ' '.join( ' ', ( @args ) ) if ( @args ); my $process = $cmd_hash{$command}; if ( !$process ) { dPrint( ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'\n" ); return(); } elsif ( $process->{pending} ) { delete( $cmd_hash{$command} ); dPrint( ZoneMinder::Logger::INFO, "Command '$command' removed from pending list at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); return(); } my $cpid = $process->{pid}; if ( !$pid_hash{$cpid} ) { dPrint( ZoneMinder::Logger::ERROR, "No process with command of '$command' is running\n" ); return(); } dPrint( ZoneMinder::Logger::INFO, "'$daemon ".join( ' ', @args )."' stopping at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); $process->{keepalive} = !$final; kill( 'TERM', $cpid ); delete( $cmd_hash{$command} ); # Now check it has actually gone away, if not kill -9 it my $count = 0; while( $cpid && kill( 0, $cpid ) ) { if ( $count++ > 5 ) { kill( 'KILL', $cpid ); } sleep( 1 ); } } sub stop { _stop( 1, @_ ); } sub restart { my $daemon = shift; my @args = @_; my $command = $daemon; $command .= ' '.join( ' ', ( @args ) ) if ( @args ); my $process = $cmd_hash{$command}; if ( $process ) { if ( $process->{pid} ) { my $cpid = $process->{pid}; if ( defined($pid_hash{$cpid}) ) { _stop( 0, $daemon, @args ); return; } } } start( $daemon, @args ); } sub reload { my $daemon = shift; my @args = @_; my $command = $daemon; $command .= ' '.join( ' ', ( @args ) ) if ( @args ); my $process = $cmd_hash{$command}; if ( $process ) { if ( $process->{pid} ) { kill( 'HUP', $process->{pid} ); } } } sub logrot { logReinit(); foreach my $process ( values( %pid_hash ) ) { if ( $process->{pid} && $process->{command} =~ /^zm.*\.pl/ ) { kill( 'HUP', $process->{pid} ); } } } sub reaper { my $saved_status = $!; while ( (my $cpid = waitpid( -1, WNOHANG )) > 0 ) { my $status = $?; my $process = $pid_hash{$cpid}; delete( $pid_hash{$cpid} ); if ( !$process ) { dPrint( ZoneMinder::Logger::INFO, "Can't find child with pid of '$cpid'\n" ); next; } $process->{stopped} = time(); $process->{runtime} = ($process->{stopped}-$process->{started}); delete( $process->{pid} ); my $exit_status = $status>>8; my $exit_signal = $status&0xfe; my $core_dumped = $status&0x01; my $out_str = "'$process->{daemon} ".join( ' ', @{$process->{args}} )."' "; if ( $exit_signal ) { if ( $exit_signal == 15 || $exit_signal == 14 ) # TERM or ALRM { $out_str .= "exited"; } else { $out_str .= "crashed"; } $out_str .= ", signal $exit_signal"; } else { $out_str .= "exited "; if ( $exit_status ) { $out_str .= "abnormally, exit status $exit_status"; } else { $out_str .= "normally"; } } #print( ", core dumped" ) if ( $core_dumped ); $out_str .= "\n"; if ( $exit_status == 0 ) { Info( $out_str ); } else { Error( $out_str ); } if ( $process->{keepalive} ) { if ( !$process->{delay} || ($process->{runtime} > ZM_MAX_RESTART_DELAY) ) { #start( $process->{daemon}, @{$process->{args}} ); # Schedule for immediate restart $cmd_hash{$process->{command}} = $process; $process->{pending} = $process->{stopped}; $process->{delay} = 5; } else { $cmd_hash{$process->{command}} = $process; $process->{pending} = $process->{stopped}+$process->{delay}; $process->{delay} *= 2; # Limit the start delay to 15 minutes max if ( $process->{delay} > ZM_MAX_RESTART_DELAY ) { $process->{delay} = ZM_MAX_RESTART_DELAY; } } } } $SIG{CHLD} = \&reaper; $! = $saved_status; } sub restartPending { # Restart any pending processes foreach my $process ( values( %cmd_hash ) ) { if ( $process->{pending} && $process->{pending} <= time() ) { dPrint( ZoneMinder::Logger::INFO, "Starting pending process, $process->{command}\n" ); start( $process->{daemon}, @{$process->{args}} ); } } } sub shutdownAll { foreach my $process ( values( %pid_hash ) ) { stop( $process->{daemon}, @{$process->{args}} ); } killAll( 5 ); dPrint( ZoneMinder::Logger::INFO, "Server shutdown at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); unlink( main::SOCK_FILE ); unlink( ZM_PID ); close( CLIENT ); close( SERVER ); exit(); } sub check { my $daemon = shift; my @args = @_; my $command = $daemon; $command .= ' '.join( ' ', ( @args ) ) if ( @args ); my $process = $cmd_hash{$command}; if ( !$process ) { cPrint( "unknown\n" ); } elsif ( $process->{pending} ) { cPrint( "pending\n" ); } else { my $cpid = $process->{pid}; if ( !$pid_hash{$cpid} ) { cPrint( "stopped\n" ); } else { cPrint( "running\n" ); } } } sub status { my $daemon = shift; my @args = @_; if ( defined($daemon) ) { my $command = $daemon; $command .= ' '.join( ' ', ( @args ) ) if ( @args ); my $process = $cmd_hash{$command}; if ( !$process ) { dPrint( ZoneMinder::Logger::DEBUG, "'$command' not running\n" ); return(); } if ( $process->{pending} ) { dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending}) )."\n" ); } else { my $cpid = $process->{pid}; if ( !$pid_hash{$cpid} ) { dPrint( ZoneMinder::Logger::DEBUG, "'$command' not running\n" ); return(); } } dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' running since ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ).", pid = $process->{pid}" ); } else { foreach my $process ( values(%pid_hash) ) { my $out_str = "'$process->{command}' running since ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ).", pid = $process->{pid}"; $out_str .= ", valid" if ( kill( 0, $process->{pid} ) ); $out_str .= "\n"; dPrint( ZoneMinder::Logger::DEBUG, $out_str ); } foreach my $process ( values( %cmd_hash ) ) { if ( $process->{pending} ) { dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending}) )."\n" ); } } } } sub killAll { my $delay = shift; sleep( $delay ); foreach my $daemon ( @daemons ) { my $cmd = "killall --quiet --signal TERM $daemon"; Debug( $cmd ); qx( $cmd ); } sleep( $delay ); foreach my $daemon ( @daemons ) { my $cmd = "killall --quiet --signal KILL $daemon"; Debug( $cmd ); qx( $cmd ); } } ZoneMinder-1.26.5/scripts/zmeventdump.in000066400000000000000000000033651225361755400202750ustar00rootroot00000000000000#!/bin/bash #=============================================================================== # # FILE: zmeventdump # # USAGE: ./zmeventdump # # DESCRIPTION: Uses mysqldump to create a .sql file for individual zm # events to make Event table recovery possible by doing a # 'find' search in ZoneMinder the events directory # # OPTIONS: --- None # REQUIREMENTS: --- mysqldump # BUGS: --- # NOTES: --- # AUTHOR: Ross Melin # COMPANY: # VERSION: 3.0 # CREATED: 02/27/2008 05:39:00 PM PST # REVISION: --- Update for changed zmfilter and # ZM_USE_DEEP_STORAGE #=============================================================================== # Edit these to suit your configuration ZM_CONFIG=@ZM_CONFIG@ # ZM_VERSION in the config is now deprecated but will likely still exist in people's config files. This will override it. ZM_VERSION=@VERSION@ MYSQLDUMP=/usr/bin/mysqldump # The rest should not need editing # Get the mysql user and password source $ZM_CONFIG # zmfilter now passes the full path as an argument EVENT_PATH=$1 # Get the event id from a filename in the event directory EVENT_ID=$(ls $1/.[0-9]* | sed s:$1\/\.::) MYDUMPOPTS="--user=$ZM_DB_USER --password=$ZM_DB_PASS --skip-opt --compact --quick --no-create-info" # Dump the sql statements needed to reload the Events, Frames and Stats tables echo "-- ZM_DB_VERSION=$ZM_VERSION " > $EVENT_PATH/.sql $MYSQLDUMP $MYDUMPOPTS --where="Id=$EVENT_ID" zm Events >> $EVENT_PATH/.sql $MYSQLDUMP $MYDUMPOPTS --where="Eventid=$EVENT_ID" zm Frames >> $EVENT_PATH/.sql $MYSQLDUMP $MYDUMPOPTS --where="Eventid=$EVENT_ID" zm Stats >> $EVENT_PATH/.sql exit 0 ZoneMinder-1.26.5/scripts/zmfilter.pl.in000066400000000000000000001131021225361755400201540ustar00rootroot00000000000000#!/usr/bin/perl -wT # # ========================================================================== # # ZoneMinder Event Filter Script, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This script continuously monitors the recorded events for the given # monitor and applies any filters which would delete and/or upload # matching events # use strict; use bytes; # ========================================================================== # # These are the elements you can edit to suit your installation # # ========================================================================== use constant START_DELAY => 5; # How long to wait before starting # ========================================================================== # # You shouldn't need to change anything from here downwards # # ========================================================================== @EXTRA_PERL_LIB@ use ZoneMinder; use DBI; use POSIX; use Time::HiRes qw/gettimeofday/; use Date::Manip; use Getopt::Long; use Data::Dumper; use constant EVENT_PATH => (ZM_DIR_EVENTS=~m|/|)?ZM_DIR_EVENTS:(ZM_PATH_WEB.'/'.ZM_DIR_EVENTS); logInit(); logSetSignal(); if ( ZM_OPT_UPLOAD ) { # Comment these out if you don't have them and don't want to upload # or don't want to use that format if ( ZM_UPLOAD_ARCH_FORMAT eq "zip" ) { require Archive::Zip; import Archive::Zip qw( :ERROR_CODES :CONSTANTS ); } else { require Archive::Tar; } if ( ZM_UPLOAD_PROTOCOL eq "ftp" ) { require Net::FTP; } else { require Net::SFTP::Foreign; } } if ( ZM_OPT_EMAIL ) { if ( ZM_NEW_MAIL_MODULES ) { require MIME::Lite; require Net::SMTP; } else { require MIME::Entity; } } if ( ZM_OPT_MESSAGE ) { if ( ZM_NEW_MAIL_MODULES ) { require MIME::Lite; require Net::SMTP; } else { require MIME::Entity; } } $| = 1; $ENV{PATH} = '/bin:/usr/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; my $delay = ZM_FILTER_EXECUTE_INTERVAL; my $event_id = 0; my $filter_parm = ""; sub Usage { print( " Usage: zmfilter.pl [-f ,--filter=] Parameters are :- -f, --filter= - The name of a specific filter to run "); exit( -1 ); } # # More or less replicates the equivalent PHP function # sub strtotime { my $dt_str = shift; return( UnixDate( $dt_str, '%s' ) ); } # # More or less replicates the equivalent PHP function # sub str_repeat { my $string = shift; my $count = shift; return( ${string}x${count} ); } # Formats a date into MySQL format sub DateTimeToSQL { my $dt_str = shift; my $dt_val = strtotime( $dt_str ); if ( !$dt_val ) { Error( "Unable to parse date string '$dt_str'\n" ); return( undef ); } return( strftime( "%Y-%m-%d %H:%M:%S", localtime( $dt_val ) ) ); } if ( !GetOptions( 'filter=s'=>\$filter_parm ) ) { Usage(); } chdir( EVENT_PATH ); my $dbh = zmDbConnect(); if ( $filter_parm ) { Info( "Scanning for events using filter '$filter_parm'\n" ); } else { Info( "Scanning for events\n" ); } if ( !$filter_parm ) { sleep( START_DELAY ); } my $filters; my $last_action = 0; while( 1 ) { if ( (time() - $last_action) > ZM_FILTER_RELOAD_DELAY ) { Debug( "Reloading filters\n" ); $last_action = time(); $filters = getFilters( $filter_parm ); } foreach my $filter ( @$filters ) { checkFilter( $filter ); } last if ( $filter_parm ); Debug( "Sleeping for $delay seconds\n" ); sleep( $delay ); } sub getDiskPercent { my $command = "df ."; my $df = qx( $command ); my $space = -1; if ( $df =~ /\s(\d+)%/ms ) { $space = $1; } return( $space ); } sub getDiskBlocks { my $command = "df ."; my $df = qx( $command ); my $space = -1; if ( $df =~ /\s(\d+)\s+\d+\s+\d+%/ms ) { $space = $1; } return( $space ); } sub getLoad { my $command = "uptime ."; my $uptime = qx( $command ); my $load = -1; if ( $uptime =~ /load average:\s+([\d.]+)/ms ) { $load = $1; Info( "Load: $load" ); } return( $load ); } sub getFilters { my $filter_name = shift; my @filters; my $sql = "select * from Filters where"; if ( $filter_name ) { $sql .= " Name = ? and"; } else { $sql .= " Background = 1 and"; } $sql .= " (AutoArchive = 1 or AutoVideo = 1 or AutoUpload = 1 or AutoEmail = 1 or AutoMessage = 1 or AutoExecute = 1 or AutoDelete = 1) order by Name"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res; if ( $filter_name ) { $res = $sth->execute( $filter_name ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); } else { $res = $sth->execute() or Fatal( "Can't execute '$sql': ".$sth->errstr() ); } FILTER: while( my $db_filter = $sth->fetchrow_hashref() ) { Debug( "Found filter '$db_filter->{Name}'\n" ); my $filter_expr = jsonDecode( $db_filter->{Query} ); my $sql = "select E.Id,E.MonitorId,M.Name as MonitorName,M.DefaultRate,M.DefaultScale,E.Name,E.Cause,E.Notes,E.StartTime,unix_timestamp(E.StartTime) as Time,E.Length,E.Frames,E.AlarmFrames,E.TotScore,E.AvgScore,E.MaxScore,E.Archived,E.Videoed,E.Uploaded,E.Emailed,E.Messaged,E.Executed from Events as E inner join Monitors as M on M.Id = E.MonitorId where not isnull(E.EndTime)"; $db_filter->{Sql} = ''; if ( @{$filter_expr->{terms}} ) { for ( my $i = 0; $i < @{$filter_expr->{terms}}; $i++ ) { if ( exists($filter_expr->{terms}[$i]->{cnj}) ) { $db_filter->{Sql} .= " ".$filter_expr->{terms}[$i]->{cnj}." "; } if ( exists($filter_expr->{terms}[$i]->{obr}) ) { $db_filter->{Sql} .= " ".str_repeat( "(", $filter_expr->{terms}[$i]->{obr} )." "; } my $value = $filter_expr->{terms}[$i]->{val}; my @value_list; if ( $filter_expr->{terms}[$i]->{attr} ) { if ( $filter_expr->{terms}[$i]->{attr} =~ /^Monitor/ ) { my ( $temp_attr_name ) = $filter_expr->{terms}[$i]->{attr} =~ /^Monitor(.+)$/; $db_filter->{Sql} .= "M.".$temp_attr_name; } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DateTime' ) { $db_filter->{Sql} .= "E.StartTime"; } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Date' ) { $db_filter->{Sql} .= "to_days( E.StartTime )"; } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Time' ) { $db_filter->{Sql} .= "extract( hour_second from E.StartTime )"; } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Weekday' ) { $db_filter->{Sql} .= "weekday( E.StartTime )"; } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DiskPercent' ) { $db_filter->{Sql} .= "zmDiskPercent"; $db_filter->{HasDiskPercent} = !undef; } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DiskBlocks' ) { $db_filter->{Sql} .= "zmDiskBlocks"; $db_filter->{HasDiskBlocks} = !undef; } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'SystemLoad' ) { $db_filter->{Sql} .= "zmSystemLoad"; $db_filter->{HasSystemLoad} = !undef; } else { $db_filter->{Sql} .= "E.".$filter_expr->{terms}[$i]->{attr}; } ( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/; foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) { if ( $filter_expr->{terms}[$i]->{attr} =~ /^Monitor/ ) { $value = "'$temp_value'"; } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Name' || $filter_expr->{terms}[$i]->{attr} eq 'Cause' || $filter_expr->{terms}[$i]->{attr} eq 'Notes' ) { $value = "'$temp_value'"; } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DateTime' ) { $value = DateTimeToSQL( $temp_value ); if ( !$value ) { Error( "Error parsing date/time '$temp_value', skipping filter '$db_filter->{Name}'\n" ); next FILTER; } $value = "'$value'"; } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Date' ) { $value = DateTimeToSQL( $temp_value ); if ( !$value ) { Error( "Error parsing date/time '$temp_value', skipping filter '$db_filter->{Name}'\n" ); next FILTER; } $value = "to_days( '$value' )"; } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Time' ) { $value = DateTimeToSQL( $temp_value ); if ( !$value ) { Error( "Error parsing date/time '$temp_value', skipping filter '$db_filter->{Name}'\n" ); next FILTER; } $value = "extract( hour_second from '$value' )"; } else { $value = $temp_value; } push( @value_list, $value ); } } if ( $filter_expr->{terms}[$i]->{op} ) { if ( $filter_expr->{terms}[$i]->{op} eq '=~' ) { $db_filter->{Sql} .= " regexp $value"; } elsif ( $filter_expr->{terms}[$i]->{op} eq '!~' ) { $db_filter->{Sql} .= " not regexp $value"; } elsif ( $filter_expr->{terms}[$i]->{op} eq '=[]' ) { $db_filter->{Sql} .= " in (".join( ",", @value_list ).")"; } elsif ( $filter_expr->{terms}[$i]->{op} eq '!~' ) { $db_filter->{Sql} .= " not in (".join( ",", @value_list ).")"; } else { $db_filter->{Sql} .= " ".$filter_expr->{terms}[$i]->{op}." $value"; } } if ( exists($filter_expr->{terms}[$i]->{cbr}) ) { $db_filter->{Sql} .= " ".str_repeat( ")", $filter_expr->{terms}[$i]->{cbr} )." "; } } } if ( $db_filter->{Sql} ) { $sql .= " and ( ".$db_filter->{Sql}." )"; } my @auto_terms; if ( $db_filter->{AutoArchive} ) { push( @auto_terms, "E.Archived = 0" ) } if ( $db_filter->{AutoVideo} ) { push( @auto_terms, "E.Videoed = 0" ) } if ( $db_filter->{AutoUpload} ) { push( @auto_terms, "E.Uploaded = 0" ) } if ( $db_filter->{AutoEmail} ) { push( @auto_terms, "E.Emailed = 0" ) } if ( $db_filter->{AutoMessage} ) { push( @auto_terms, "E.Messaged = 0" ) } if ( $db_filter->{AutoExecute} ) { push( @auto_terms, "E.Executed = 0" ) } if ( @auto_terms ) { $sql .= " and ( ".join( " or ", @auto_terms )." )"; } if ( !$filter_expr->{sort_field} ) { $filter_expr->{sort_field} = 'StartTime'; $filter_expr->{sort_asc} = 0; } my $sort_column = ''; if ( $filter_expr->{sort_field} eq 'Id' ) { $sort_column = "E.Id"; } elsif ( $filter_expr->{sort_field} eq 'MonitorName' ) { $sort_column = "M.Name"; } elsif ( $filter_expr->{sort_field} eq 'Name' ) { $sort_column = "E.Name"; } elsif ( $filter_expr->{sort_field} eq 'StartTime' ) { $sort_column = "E.StartTime"; } elsif ( $filter_expr->{sort_field} eq 'Secs' ) { $sort_column = "E.Length"; } elsif ( $filter_expr->{sort_field} eq 'Frames' ) { $sort_column = "E.Frames"; } elsif ( $filter_expr->{sort_field} eq 'AlarmFrames' ) { $sort_column = "E.AlarmFrames"; } elsif ( $filter_expr->{sort_field} eq 'TotScore' ) { $sort_column = "E.TotScore"; } elsif ( $filter_expr->{sort_field} eq 'AvgScore' ) { $sort_column = "E.AvgScore"; } elsif ( $filter_expr->{sort_field} eq 'MaxScore' ) { $sort_column = "E.MaxScore"; } else { $sort_column = "E.StartTime"; } my $sort_order = $filter_expr->{sort_asc}?"asc":"desc"; $sql .= " order by ".$sort_column." ".$sort_order; if ( $filter_expr->{limit} ) { $sql .= " limit 0,".$filter_expr->{limit}; } Debug( "SQL:$sql\n" ); $db_filter->{Sql} = $sql; if ( $db_filter->{AutoExecute} ) { my $script = $db_filter->{AutoExecuteCmd}; $script =~ s/\s.*$//; if ( !-e $script ) { Error( "Auto execute script '$script' not found, skipping filter '$db_filter->{Name}'\n" ); next FILTER; } elsif ( !-x $script ) { Error( "Auto execute script '$script' not executable, skipping filter '$db_filter->{Name}'\n" ); next FILTER; } } push( @filters, $db_filter ); } $sth->finish(); return( \@filters ); } sub checkFilter { my $filter = shift; Debug( "Checking filter '$filter->{Name}'". ($filter->{AutoDelete}?", delete":""). ($filter->{AutoArchive}?", archive":""). ($filter->{AutoVideo}?", video":""). ($filter->{AutoUpload}?", upload":""). ($filter->{AutoEmail}?", email":""). ($filter->{AutoMessage}?", message":""). ($filter->{AutoExecute}?", execute":""). "\n" ); my $sql = $filter->{Sql}; if ( $filter->{HasDiskPercent} ) { my $disk_percent = getDiskPercent(); $sql =~ s/zmDiskPercent/$disk_percent/g; } if ( $filter->{HasDiskBlocks} ) { my $disk_blocks = getDiskBlocks(); $sql =~ s/zmDiskBlocks/$disk_blocks/g; } if ( $filter->{HasSystemLoad} ) { my $load = getLoad(); $sql =~ s/zmSystemLoad/$load/g; } my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute(); if ( !$res ) { Error( "Can't execute filter '$sql', ignoring: ".$sth->errstr() ); return; } while( my $event = $sth->fetchrow_hashref() ) { Debug( "Checking event $event->{Id}\n" ); my $delete_ok = !undef; if ( $filter->{AutoArchive} ) { Info( "Archiving event $event->{Id}\n" ); # Do it individually to avoid locking up the table for new events my $sql = "update Events set Archived = 1 where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); } if ( ZM_OPT_FFMPEG && $filter->{AutoVideo} ) { if ( !$event->{Videoed} ) { $delete_ok = undef if ( !generateVideo( $filter, $event ) ); } } if ( ZM_OPT_EMAIL && $filter->{AutoEmail} ) { if ( !$event->{Emailed} ) { $delete_ok = undef if ( !sendEmail( $filter, $event ) ); } } if ( ZM_OPT_MESSAGE && $filter->{AutoMessage} ) { if ( !$event->{Messaged} ) { $delete_ok = undef if ( !sendMessage( $filter, $event ) ); } } if ( ZM_OPT_UPLOAD && $filter->{AutoUpload} ) { if ( !$event->{Uploaded} ) { $delete_ok = undef if ( !uploadArchFile( $filter, $event ) ); } } if ( $filter->{AutoExecute} ) { if ( !$event->{Execute} ) { $delete_ok = undef if ( !executeCommand( $filter, $event ) ); } } if ( $filter->{AutoDelete} ) { if ( $delete_ok ) { Info( "Deleting event $event->{Id}\n" ); # Do it individually to avoid locking up the table for new events my $sql = "delete from Events where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); if ( !ZM_OPT_FAST_DELETE ) { my $sql = "delete from Frames where EventId = ?"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); $sql = "delete from Stats where EventId = ?"; $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); deleteEventFiles( $event->{Id}, $event->{MonitorId} ); } } else { Error( "Unable to delete event $event->{Id} as previous operations failed\n" ); } } } $sth->finish(); } sub generateVideo { my $filter = shift; my $event = shift; my $phone = shift; my $rate = $event->{DefaultRate}/100; my $scale = $event->{DefaultScale}/100; my $format; my @ffmpeg_formats = split( /\s+/, ZM_FFMPEG_FORMATS ); my $default_video_format; my $default_phone_format; foreach my $ffmpeg_format( @ffmpeg_formats ) { if ( $ffmpeg_format =~ /^(.+)\*\*$/ ) { $default_phone_format = $1; } elsif ( $ffmpeg_format =~ /^(.+)\*$/ ) { $default_video_format = $1; } } if ( $phone && $default_phone_format ) { $format = $default_phone_format; } elsif ( $default_video_format ) { $format = $default_video_format; } else { $format = $ffmpeg_formats[0]; } my $command = ZM_PATH_BIN."/zmvideo.pl -e ".$event->{Id}." -r ".$rate." -s ".$scale." -f ".$format; my $output = qx($command); chomp( $output ); my $status = $? >> 8; if ( $status || logDebugging() ) { Debug( "Output: $output\n" ); } if ( $status ) { Error( "Video generation '$command' failed with status: $status\n" ); if ( wantarray() ) { return( undef, undef ); } return( 0 ); } else { my $sql = "update Events set Videoed = 1 where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); if ( wantarray() ) { return( $format, sprintf( "%s/%s", getEventPath( $event ), $output ) ); } } return( 1 ); } sub uploadArchFile { my $filter = shift; my $event = shift; if ( !ZM_UPLOAD_HOST ) { Error( "Cannot upload archive as no upload host defined" ); return( 0 ); } my $archFile = $event->{MonitorName}.'-'.$event->{Id}; my $archImagePath = getEventPath( $event )."/".((ZM_UPLOAD_ARCH_ANALYSE)?'{*analyse,*capture}':'*capture').".jpg"; my @archImageFiles = glob($archImagePath); my $archLocPath; my $archError = 0; if ( ZM_UPLOAD_ARCH_FORMAT eq "zip" ) { $archFile .= '.zip'; $archLocPath = ZM_UPLOAD_LOC_DIR.'/'.$archFile; my $zip = Archive::Zip->new(); Info( "Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n" ); my $status = &AZ_OK; foreach my $imageFile ( @archImageFiles ) { Debug( "Adding $imageFile\n" ); my $member = $zip->addFile( $imageFile ); if ( !$member ) { Error( "Unable to add image file $imageFile to zip archive $archLocPath" ); $archError = 1; last; } $member->desiredCompressionMethod( (ZM_UPLOAD_ARCH_COMPRESS)?&COMPRESSION_DEFLATED:&COMPRESSION_STORED ); } if ( !$archError ) { $status = $zip->writeToFileNamed( $archLocPath ); if ( $archError = ($status != &AZ_OK) ) { Error( "Zip error: $status\n " ); } } else { Error( "Error adding images to zip archive $archLocPath, not writing" ); } } elsif ( ZM_UPLOAD_ARCH_FORMAT eq "tar" ) { if ( ZM_UPLOAD_ARCH_COMPRESS ) { $archFile .= '.tar.gz'; } else { $archFile .= '.tar'; } $archLocPath = ZM_UPLOAD_LOC_DIR.'/'.$archFile; Info( "Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n" ); if ( $archError = !Archive::Tar->create_archive( $archLocPath, ZM_UPLOAD_ARCH_COMPRESS, @archImageFiles ) ) { Error( "Tar error: ".Archive::Tar->error()."\n " ); } } if ( $archError ) { return( 0 ); } else { if ( ZM_UPLOAD_PROTOCOL eq "ftp" ) { Info( "Uploading to ".ZM_UPLOAD_HOST." using FTP\n" ); my $ftp = Net::FTP->new( ZM_UPLOAD_HOST, Timeout=>ZM_UPLOAD_TIMEOUT, Passive=>ZM_UPLOAD_FTP_PASSIVE, Debug=>ZM_UPLOAD_DEBUG ); if ( !$ftp ) { Error( "Can't create FTP connection: $@" ); return( 0 ); } $ftp->login( ZM_UPLOAD_USER, ZM_UPLOAD_PASS ) or Error( "FTP - Can't login" ); $ftp->binary() or Error( "FTP - Can't go binary" ); $ftp->cwd( ZM_UPLOAD_REM_DIR ) or Error( "FTP - Can't cwd" ) if ( ZM_UPLOAD_REM_DIR ); $ftp->put( $archLocPath ) or Error( "FTP - Can't upload '$archLocPath'" ); $ftp->quit() or Error( "FTP - Can't quit" ); } else { my $host = ZM_UPLOAD_HOST; $host .= ":".ZM_UPLOAD_PORT if ( ZM_UPLOAD_PORT ); Info( "Uploading to ".$host." using SFTP\n" ); my %sftpOptions = ( host=>ZM_UPLOAD_HOST, user=>ZM_UPLOAD_USER ); $sftpOptions{password} = ZM_UPLOAD_PASS if ( ZM_UPLOAD_PASS ); $sftpOptions{port} = ZM_UPLOAD_PORT if ( ZM_UPLOAD_PORT ); $sftpOptions{timeout} = ZM_UPLOAD_TIMEOUT if ( ZM_UPLOAD_TIMEOUT ); $sftpOptions{more} = [ '-o'=>'StrictHostKeyChecking=no' ]; $Net::SFTP::Foreign::debug = -1 if ( ZM_UPLOAD_DEBUG ); my $sftp = Net::SFTP::Foreign->new( ZM_UPLOAD_HOST, %sftpOptions ); if ( $sftp->error ) { Error( "Can't create SFTP connection: ".$sftp->error ); return( 0 ); } $sftp->setcwd( ZM_UPLOAD_REM_DIR ) or Error( "SFTP - Can't setcwd: ".$sftp->error ) if ( ZM_UPLOAD_REM_DIR ); $sftp->put( $archLocPath, $archFile ) or Error( "SFTP - Can't upload '$archLocPath': ".$sftp->error ); } unlink( $archLocPath ); my $sql = "update Events set Uploaded = 1 where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); } return( 1 ); } sub substituteTags { my $text = shift; my $filter = shift; my $event = shift; my $attachments_ref = shift; # First we'd better check what we need to get # We have a filter and an event, do we need any more # monitor information? my $need_monitor = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/; my $monitor = {}; if ( $need_monitor ) { my $db_now = strftime( "%Y-%m-%d %H:%M:%S", localtime() ); my $sql = "select M.Id, count(E.Id) as EventCount, count(if(E.Archived,1,NULL)) as ArchEventCount, count(if(E.StartTime>'$db_now' - INTERVAL 1 HOUR && E.Archived = 0,1,NULL)) as HourEventCount, count(if(E.StartTime>'$db_now' - INTERVAL 1 DAY && E.Archived = 0,1,NULL)) as DayEventCount, count(if(E.StartTime>'$db_now' - INTERVAL 7 DAY && E.Archived = 0,1,NULL)) as WeekEventCount, count(if(E.StartTime>'$db_now' - INTERVAL 1 MONTH && E.Archived = 0,1,NULL)) as MonthEventCount from Monitors as M left join Events as E on E.MonitorId = M.Id where MonitorId = ? group by E.MonitorId order by Id"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $event->{MonitorId} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); $monitor = $sth->fetchrow_hashref(); $sth->finish(); return() if ( !$monitor ); } # Do we need the image information too? my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM)%/; my $first_alarm_frame; my $max_alarm_frame; my $max_alarm_score = 0; if ( $need_images ) { my $sql = "select * from Frames where EventId = ? and Type = 'Alarm' order by FrameId"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); while( my $frame = $sth->fetchrow_hashref() ) { if ( !$first_alarm_frame ) { $first_alarm_frame = $frame; } if ( $frame->{Score} > $max_alarm_score ) { $max_alarm_frame = $frame; $max_alarm_score = $frame->{Score}; } } $sth->finish(); } my $url = ZM_URL; $text =~ s/%ZP%/$url/g; $text =~ s/%MN%/$event->{MonitorName}/g; $text =~ s/%MET%/$monitor->{EventCount}/g; $text =~ s/%MEH%/$monitor->{HourEventCount}/g; $text =~ s/%MED%/$monitor->{DayEventCount}/g; $text =~ s/%MEW%/$monitor->{WeekEventCount}/g; $text =~ s/%MEM%/$monitor->{MonthEventCount}/g; $text =~ s/%MEA%/$monitor->{ArchEventCount}/g; $text =~ s/%MP%/$url?view=watch&mid=$event->{MonitorId}/g; $text =~ s/%MPS%/$url?view=watchfeed&mid=$event->{MonitorId}&mode=stream/g; $text =~ s/%MPI%/$url?view=watchfeed&mid=$event->{MonitorId}&mode=still/g; $text =~ s/%EP%/$url?view=event&mid=$event->{MonitorId}&eid=$event->{Id}/g; $text =~ s/%EPS%/$url?view=event&mode=stream&mid=$event->{MonitorId}&eid=$event->{Id}/g; $text =~ s/%EPI%/$url?view=event&mode=still&mid=$event->{MonitorId}&eid=$event->{Id}/g; $text =~ s/%EI%/$event->{Id}/g; $text =~ s/%EN%/$event->{Name}/g; $text =~ s/%EC%/$event->{Cause}/g; $text =~ s/%ED%/$event->{Notes}/g; $text =~ s/%ET%/$event->{StartTime}/g; $text =~ s/%EL%/$event->{Length}/g; $text =~ s/%EF%/$event->{Frames}/g; $text =~ s/%EFA%/$event->{AlarmFrames}/g; $text =~ s/%EST%/$event->{TotScore}/g; $text =~ s/%ESA%/$event->{AvgScore}/g; $text =~ s/%ESM%/$event->{MaxScore}/g; if ( $first_alarm_frame ) { $text =~ s/%EPI1%/$url?view=frame&mid=$event->{MonitorId}&eid=$event->{Id}&fid=$first_alarm_frame->{FrameId}/g; $text =~ s/%EPIM%/$url?view=frame&mid=$event->{MonitorId}&eid=$event->{Id}&fid=$max_alarm_frame->{FrameId}/g; if ( $attachments_ref && $text =~ s/%EI1%//g ) { push( @$attachments_ref, { type=>"image/jpeg", path=>sprintf( "%s/%0".ZM_EVENT_IMAGE_DIGITS."d-capture.jpg", getEventPath( $event ), $first_alarm_frame->{FrameId} ) } ); } if ( $attachments_ref && $text =~ s/%EIM%//g ) { # Don't attach the same image twice if ( !@$attachments_ref || ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} ) ) { push( @$attachments_ref, { type=>"image/jpeg", path=>sprintf( "%s/%0".ZM_EVENT_IMAGE_DIGITS."d-capture.jpg", getEventPath( $event ), $max_alarm_frame->{FrameId} ) } ); } } } if ( $attachments_ref && ZM_OPT_FFMPEG ) { if ( $text =~ s/%EV%//g ) { my ( $format, $path ) = generateVideo( $filter, $event ); if ( !$format ) { return( undef ); } push( @$attachments_ref, { type=>"video/$format", path=>$path } ); } if ( $text =~ s/%EVM%//g ) { my ( $format, $path ) = generateVideo( $filter, $event, 1 ); if ( !$format ) { return( undef ); } push( @$attachments_ref, { type=>"video/$format", path=>$path } ); } } $text =~ s/%FN%/$filter->{Name}/g; ( my $filter_name = $filter->{Name} ) =~ s/ /+/g; $text =~ s/%FP%/$url?view=filter&mid=$event->{MonitorId}&filter_name=$filter_name/g; return( $text ); } sub sendEmail { my $filter = shift; my $event = shift; if ( !ZM_FROM_EMAIL ) { Error( "No 'from' email address defined, not sending email" ); return( 0 ); } if ( !ZM_EMAIL_ADDRESS ) { Error( "No email address defined, not sending email" ); return( 0 ); } Info( "Creating notification email\n" ); my $subject = substituteTags( ZM_EMAIL_SUBJECT, $filter, $event ); return( 0 ) if ( !$subject ); my @attachments; my $body = substituteTags( ZM_EMAIL_BODY, $filter, $event, \@attachments ); return( 0 ) if ( !$body ); Info( "Sending notification email '$subject'\n" ); eval { if ( ZM_NEW_MAIL_MODULES ) { ### Create the multipart container my $mail = MIME::Lite->new ( From => ZM_FROM_EMAIL, To => ZM_EMAIL_ADDRESS, Subject => $subject, Type => "multipart/mixed" ); ### Add the text message part $mail->attach ( Type => "TEXT", Data => $body ); ### Add the attachments foreach my $attachment ( @attachments ) { Info( "Attaching '$attachment->{path}\n" ); $mail->attach( Path => $attachment->{path}, Type => $attachment->{type}, Disposition => "attachment" ); } ### Send the Message MIME::Lite->send( "smtp", ZM_EMAIL_HOST, Timeout=>60 ); $mail->send(); } else { my $mail = MIME::Entity->build( From => ZM_FROM_EMAIL, To => ZM_EMAIL_ADDRESS, Subject => $subject, Type => (($body=~//)?'text/html':'text/plain'), Data => $body ); foreach my $attachment ( @attachments ) { Info( "Attaching '$attachment->{path}\n" ); $mail->attach( Path => $attachment->{path}, Type => $attachment->{type}, Encoding => "base64" ); } $mail->smtpsend( Host => ZM_EMAIL_HOST, MailFrom => ZM_FROM_EMAIL ); } }; if ( $@ ) { Error( "Can't send email: $@" ); return( 0 ); } else { Info( "Notification email sent\n" ); } my $sql = "update Events set Emailed = 1 where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); return( 1 ); } sub sendMessage { my $filter = shift; my $event = shift; if ( !ZM_FROM_EMAIL ) { Error( "No 'from' email address defined, not sending message" ); return( 0 ); } if ( !ZM_MESSAGE_ADDRESS ) { Error( "No message address defined, not sending message" ); return( 0 ); } Info( "Creating notification message\n" ); my $subject = substituteTags( ZM_MESSAGE_SUBJECT, $filter, $event ); return( 0 ) if ( !$subject ); my @attachments; my $body = substituteTags( ZM_MESSAGE_BODY, $filter, $event, \@attachments ); return( 0 ) if ( !$body ); Info( "Sending notification message '$subject'\n" ); eval { if ( ZM_NEW_MAIL_MODULES ) { ### Create the multipart container my $mail = MIME::Lite->new ( From => ZM_FROM_EMAIL, To => ZM_MESSAGE_ADDRESS, Subject => $subject, Type => "multipart/mixed" ); ### Add the text message part $mail->attach ( Type => "TEXT", Data => $body ); ### Add the attachments foreach my $attachment ( @attachments ) { Info( "Attaching '$attachment->{path}\n" ); $mail->attach( Path => $attachment->{path}, Type => $attachment->{type}, Disposition => "attachment" ); } ### Send the Message MIME::Lite->send( "smtp", ZM_EMAIL_HOST, Timeout=>60 ); $mail->send(); } else { my $mail = MIME::Entity->build( From => ZM_FROM_EMAIL, To => ZM_MESSAGE_ADDRESS, Subject => $subject, Type => (($body=~//)?'text/html':'text/plain'), Data => $body ); foreach my $attachment ( @attachments ) { Info( "Attaching '$attachment->{path}\n" ); $mail->attach( Path => $attachment->{path}, Type => $attachment->{type}, Encoding => "base64" ); } $mail->smtpsend( Host => ZM_EMAIL_HOST, MailFrom => ZM_FROM_EMAIL ); } }; if ( $@ ) { Error( "Can't send email: $@" ); return( 0 ); } else { Info( "Notification message sent\n" ); } my $sql = "update Events set Messaged = 1 where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); return( 1 ); } sub executeCommand { my $filter = shift; my $event = shift; my $event_path = getEventPath( $event ); my $command = $filter->{AutoExecuteCmd}; $command .= " $event_path"; Info( "Executing '$command'\n" ); my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { chomp( $output ); Debug( "Output: $output\n" ); } if ( $status ) { Error( "Command '$command' exited with status: $status\n" ); return( 0 ); } else { my $sql = "update Events set Executed = 1 where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); } return( 1 ); } ZoneMinder-1.26.5/scripts/zmlogrotate.conf.in000066400000000000000000000003501225361755400212010ustar00rootroot00000000000000# First the log files /var/log/zm/*log { weekly rotate 3 notifempty missingok } # Now the weekly db backup /var/lib/zm/zm_backup.sql { weekly rotate 3 missingok compress postrotate @BINDIR@/zmdbbackup endscript } ZoneMinder-1.26.5/scripts/zmpkg.pl.in000066400000000000000000000157721225361755400174660ustar00rootroot00000000000000#!/usr/bin/perl -wT # # ========================================================================== # # ZoneMinder Package Control Script, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This script is used to start and stop the ZoneMinder package primarily to # allow command line control for automatic restart on reboot (see zm script) # use strict; use bytes; # ========================================================================== # # Don't change anything below here # # ========================================================================== @EXTRA_PERL_LIB@ use ZoneMinder; use DBI; use POSIX; use Time::HiRes qw/gettimeofday/; # Detaint our environment $ENV{PATH} = '/bin:/usr/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; logInit(); my $command = $ARGV[0]; my $state; my $dbh = zmDbConnect(); if ( !$command || $command !~ /^(?:start|stop|restart|status|logrot)$/ ) { if ( $command ) { # Check to see if it's a valid run state my $sql = 'select * from States where Name = ?'; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $command ) or Fatal( "Can't execute: ".$sth->errstr() ); if ( $state = $sth->fetchrow_hashref() ) { $state->{Name} = $command; $state->{Definitions} = []; foreach( split( /,/, $state->{Definition} ) ) { my ( $id, $function, $enabled ) = split( /:/, $_ ); push( @{$state->{Definitions}}, { Id=>$id, Function=>$function, Enabled=>$enabled } ); } $command = 'state'; } else { $command = undef; } } if ( !$command ) { print( "Usage: zmpkg.pl \n" ); exit( -1 ); } } # Move to the right place chdir( ZM_PATH_WEB ) or Fatal( "Can't chdir to '".ZM_PATH_WEB."': $!" ); my $dbg_id = ""; Info( "Command: $command\n" ); my $retval = 0; if ( $command eq "state" ) { Info( "Updating DB: $state->{Name}\n" ); my $sql = "select * from Monitors order by Id asc"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); while( my $monitor = $sth->fetchrow_hashref() ) { foreach my $definition ( @{$state->{Definitions}} ) { if ( $monitor->{Id} =~ /^$definition->{Id}$/ ) { $monitor->{NewFunction} = $definition->{Function}; $monitor->{NewEnabled} = $definition->{Enabled}; } } #next if ( !$monitor->{NewFunction} ); $monitor->{NewFunction} = 'None' if ( !$monitor->{NewFunction} ); $monitor->{NewEnabled} = 0 if ( !$monitor->{NewEnabled} ); if ( $monitor->{Function} ne $monitor->{NewFunction} || $monitor->{Enabled} ne $monitor->{NewEnabled} ) { my $sql = "update Monitors set Function = ?, Enabled = ? where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $monitor->{NewFunction}, $monitor->{NewEnabled}, $monitor->{Id} ) or Fatal( "Can't execute: ".$sth->errstr() ); } } $sth->finish(); $command = "restart"; } if ( $command =~ /^(?:stop|restart)$/ ) { my $status = runCommand( "zmdc.pl check" ); if ( $status eq "running" ) { runCommand( "zmdc.pl shutdown" ); zmMemTidy(); } else { $retval = 1; } } #runCommand( "zmupdate.pl -f" ); if ( $command =~ /^(?:start|restart)$/ ) { my $status = runCommand( "zmdc.pl check" ); if ( $status eq "stopped" ) { if ( ZM_DYN_DB_VERSION && ZM_DYN_DB_VERSION ne ZM_VERSION ) { Fatal( "Version mismatch, system is version ".ZM_VERSION.", database is ".ZM_DYN_DB_VERSION.", please run zmupdate.pl to update." ); exit( -1 ); } # Recreate the temporary directory if it's been wiped if ( !-e "@ZM_TMPDIR@" ) { Debug( "Recreating temporary directory '@ZM_TMPDIR@'" ); mkdir( "@ZM_TMPDIR@", 0700 ) or Fatal( "Can't create missing temporary directory '@ZM_TMPDIR@': $!" ); my ( $runName ) = getpwuid( $> ); if ( $runName ne ZM_WEB_USER ) { # Not running as web user, so should be root in whch case chown the temporary directory my ( $webName, $webPass, $webUid, $webGid ) = getpwnam( ZM_WEB_USER ) or Fatal( "Can't get user details for web user '".ZM_WEB_USER."': $!" ); chown( $webUid, $webGid, "@ZM_TMPDIR@" ) or Fatal( "Can't change ownership of temporary directory '@ZM_TMPDIR@' to '".ZM_WEB_USER.":".ZM_WEB_GROUP."': $!" ); } } zmMemTidy(); runCommand( "zmfix" ); runCommand( "zmdc.pl startup" ); my $sql = "select * from Monitors"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); while( my $monitor = $sth->fetchrow_hashref() ) { if ( $monitor->{Function} ne 'None' ) { if ( $monitor->{Type} eq 'Local' ) { runCommand( "zmdc.pl start zmc -d $monitor->{Device}" ); } else { runCommand( "zmdc.pl start zmc -m $monitor->{Id}" ); } if ( $monitor->{Function} ne 'Monitor' ) { if ( ZM_OPT_FRAME_SERVER ) { runCommand( "zmdc.pl start zmf -m $monitor->{Id}" ); } runCommand( "zmdc.pl start zma -m $monitor->{Id}" ); } if ( ZM_OPT_CONTROL ) { if ( $monitor->{Function} eq 'Modect' || $monitor->{Function} eq 'Mocord' ) { if ( $monitor->{Controllable} && $monitor->{TrackMotion} ) { runCommand( "zmdc.pl start zmtrack.pl -m $monitor->{Id}" ); } } } } } $sth->finish(); # This is now started unconditionally runCommand( "zmdc.pl start zmfilter.pl" ); if ( ZM_RUN_AUDIT ) { runCommand( "zmdc.pl start zmaudit.pl -c" ); } if ( ZM_OPT_TRIGGERS ) { runCommand( "zmdc.pl start zmtrigger.pl" ); } if ( ZM_OPT_X10 ) { runCommand( "zmdc.pl start zmx10.pl -c start" ); } runCommand( "zmdc.pl start zmwatch.pl" ); if ( ZM_CHECK_FOR_UPDATES ) { runCommand( "zmdc.pl start zmupdate.pl -c" ); } } else { $retval = 1; } } if ( $command eq "status" ) { my $status = runCommand( "zmdc.pl check" ); print( STDOUT $status."\n" ); } if ( $command eq "logrot" ) { runCommand( "zmdc.pl logrot" ); } exit( $retval ); ZoneMinder-1.26.5/scripts/zmtrack.pl.in000066400000000000000000000117231225361755400200010ustar00rootroot00000000000000#!/usr/bin/perl -wT # # ========================================================================== # # ZoneMinder Experimental PTZ Tracking Script, $Date: 2009-06-08 10:11:56 +0100 (Mon, 08 Jun 2009) $, $Revision: 2908 $ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This script is used to trigger and cancel alarms from external sources # using an arbitrary text based format # use strict; use bytes; # ========================================================================== # # User config # # ========================================================================== use constant SLEEP_TIME => 10000; # In microseconds # ========================================================================== # # Don't change anything from here on down # # ========================================================================== @EXTRA_PERL_LIB@ use ZoneMinder; use DBI; use POSIX; use Data::Dumper; use Getopt::Long; use Time::HiRes qw( usleep ); $| = 1; $ENV{PATH} = '/bin:/usr/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; my $mid = 0; sub Usage { print( " Usage: zmtrack.pl -m ,--monitor=] Parameters are :- -m, --monitor= - Id of the monitor to track "); exit( -1 ); } if ( !GetOptions( 'monitor=s'=>\$mid ) ) { Usage(); } logInit(); logSetSignal(); my ( $detaint_mid ) = $mid =~ /^(\d+)$/; $mid = $detaint_mid; print( "Tracker daemon $mid (experimental) starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); my $dbh = zmDbConnect(); my $sql = "select C.*,M.* from Monitors as M left join Controls as C on M.ControlId = C.Id where M.Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $mid ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); my $monitor = $sth->fetchrow_hashref(); if ( !$monitor ) { print( "Can't find monitor '$mid'\n" ); exit( -1 ); } if ( !$monitor->{Controllable} ) { print( "Monitor '$mid' is not controllable\n" ); exit( -1 ); } if ( !$monitor->{TrackMotion} ) { print( "Monitor '$mid' is not configured to track motion\n" ); exit( -1 ); } if ( !$monitor->{CanMoveMap} ) { print( "Monitor '$mid' cannot move in map mode" ); if ( $monitor->{CanMoveRel} ) { print( ", falling back to pseudo map mode\n" ); } else { print( "\n" ); exit( -1 ); } } Debug( "Found monitor for id '$monitor'\n" ); exit( -1 ) if ( !zmMemVerify( $monitor ) ); sub Suspend { my $monitor = shift; zmMonitorSuspend( $monitor ); } sub Resume { my $monitor = shift; sleep( $monitor->{TrackDelay} ); zmMonitorResume( $monitor ); } sub Track { my $monitor = shift; my ( $x, $y ) = @_; my ( $detaint_x ) = $x =~ /^(\d+)$/; $x = $detaint_x; my ( $detaint_y ) = $y =~ /^(\d+)$/; $y = $detaint_y; my $ctrlCommand = ZM_PATH_BIN."/zmcontrol.pl -i ".$monitor->{Id}; $ctrlCommand .= " --command=".($monitor->{CanMoveMap}?"moveMap":"movePseudoMap")." --xcoord=$x --ycoord=$y"; executeShellCommand( $ctrlCommand ); } sub Return { my $monitor = shift; my $ctrlCommand = ZM_PATH_BIN."/zmcontrol.pl -i ".$monitor->{Id}; if ( $monitor->{ReturnLocation} > 0 ) { $ctrlCommand .= " --command=presetGoto --preset=".$monitor->{ReturnLocation}; } else { $ctrlCommand .= " --command=presetHome"; } executeShellCommand( $ctrlCommand ); } my $last_alarm = 0; if ( ($monitor->{ReturnLocation} >= 0) ) { Suspend( $monitor ); Return( $monitor ); Resume( $monitor ); } my $alarmed = undef; while( 1 ) { if ( zmIsAlarmed( $monitor ) ) { my ( $alarm_x, $alarm_y ) = zmGetAlarmLocation( $monitor ); if ( $alarm_x >= 0 && $alarm_y >= 0 ) { Debug( "Got alarm at $alarm_x, $alarm_y\n" ); Suspend( $monitor ); Track( $monitor, $alarm_x, $alarm_y ); Resume( $monitor ); $last_alarm = time(); $alarmed = !undef; } } else { if ( logDebugging() && $alarmed ) { print( "Left alarm state\n" ); $alarmed = undef; } if ( ($monitor->{ReturnLocation} >= 0) && ($last_alarm > 0) && ((time()-$last_alarm) > $monitor->{ReturnDelay}) ) { Debug( "Returning to location ".$monitor->{ReturnLocation}."\n" ); Suspend( $monitor ); Return( $monitor ); Resume( $monitor ); $last_alarm = 0; } } usleep( SLEEP_TIME ); } ZoneMinder-1.26.5/scripts/zmtrigger.pl.in000066400000000000000000000307061225361755400203420ustar00rootroot00000000000000#!/usr/bin/perl -wT # # ========================================================================== # # ZoneMinder External Trigger Script, $Date: 2008-07-25 10:48:16 +0100 (Fri, 25 Jul 2008) $, $Revision: 2612 $ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This script is used to trigger and cancel alarms from external connections # using an arbitrary text based format # # ========================================================================== use strict; use bytes; # ========================================================================== # # User config # # ========================================================================== use constant MAX_CONNECT_DELAY => 10; use constant MONITOR_RELOAD_INTERVAL => 300; use constant SELECT_TIMEOUT => 0.25; # ========================================================================== # # Channel/Connection Modules # # ========================================================================== @EXTRA_PERL_LIB@ use ZoneMinder; use ZoneMinder::Trigger::Channel::Inet; use ZoneMinder::Trigger::Channel::Unix; use ZoneMinder::Trigger::Channel::Serial; use ZoneMinder::Trigger::Connection; my @connections; push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan1", channel=>ZoneMinder::Trigger::Channel::Inet->new( port=>6802 ), mode=>"rw" ) ); push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan2", channel=>ZoneMinder::Trigger::Channel::Unix->new( path=>ZM_PATH_SOCKS.'/zmtrigger.sock' ), mode=>"rw" ) ); #push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan3", channel=>ZoneMinder::Trigger::Channel::File->new( path=>'/tmp/zmtrigger.out' ), mode=>"w" ) ); push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan4", channel=>ZoneMinder::Trigger::Channel::Serial->new( path=>'/dev/ttyS0' ), mode=>"rw" ) ); # ========================================================================== # # Don't change anything from here on down # # ========================================================================== use DBI; #use Socket; use Data::Dumper; use POSIX qw( EINTR ); use Time::HiRes qw( usleep ); $| = 1; $ENV{PATH} = '/bin:/usr/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; logInit(); logSetSignal(); Info( "Trigger daemon starting\n" ); my $dbh = zmDbConnect(); my $base_rin = ''; foreach my $connection ( @connections ) { Info( "Opening connection '$connection->{name}'\n" ); $connection->open(); } my @in_select_connections = grep { $_->input() && $_->selectable() } @connections; my @in_poll_connections = grep { $_->input() && !$_->selectable() } @connections; my @out_connections = grep { $_->output() } @connections; foreach my $connection ( @in_select_connections ) { vec( $base_rin, $connection->fileno(), 1 ) = 1; } my %spawned_connections; my %monitors; my $monitor_reload_time = 0; $! = undef; my $rin = ''; my $win = $rin; my $ein = $win; my $timeout = SELECT_TIMEOUT; my %actions; while( 1 ) { $rin = $base_rin; # Add the file descriptors of any spawned connections foreach my $fileno ( keys(%spawned_connections) ) { vec( $rin, $fileno, 1 ) = 1; } my $nfound = select( my $rout = $rin, undef, my $eout = $ein, $timeout ); if ( $nfound > 0 ) { Debug( "Got input from $nfound connections\n" ); foreach my $connection ( @in_select_connections ) { if ( vec( $rout, $connection->fileno(), 1 ) ) { Debug( "Got input from connection ".$connection->name()." (".$connection->fileno().")\n" ); if ( $connection->spawns() ) { my $new_connection = $connection->accept(); $spawned_connections{$new_connection->fileno()} = $new_connection; Debug( "Added new spawned connection (".$new_connection->fileno()."), ".int(keys(%spawned_connections))." spawned connections\n" ); } else { my $messages = $connection->getMessages(); if ( defined($messages) ) { foreach my $message ( @$messages ) { handleMessage( $connection, $message ); } } } } } foreach my $connection ( values(%spawned_connections) ) { if ( vec( $rout, $connection->fileno(), 1 ) ) { Debug( "Got input from spawned connection ".$connection->name()." (".$connection->fileno().")\n" ); my $messages = $connection->getMessages(); if ( defined($messages) ) { foreach my $message ( @$messages ) { handleMessage( $connection, $message ); } } else { delete( $spawned_connections{$connection->fileno()} ); Debug( "Removed spawned connection (".$connection->fileno()."), ".int(keys(%spawned_connections))." spawned connections\n" ); $connection->close(); } } } } elsif ( $nfound < 0 ) { if ( $! == EINTR ) { # Do nothing } else { Fatal( "Can't select: $!" ); } } # Check polled connections foreach my $connection ( @in_poll_connections ) { my $messages = $connection->getMessages(); if ( defined($messages) ) { foreach my $message ( @$messages ) { handleMessage( $connection, $message ); } } } # Check for alarms that might have happened my @out_messages; foreach my $monitor ( values(%monitors) ) { my ( $state, $last_event ) = zmMemRead( $monitor, [ "shared_data:state", "shared_data:last_event" ] ); #print( "$monitor->{Id}: S:$state, LE:$last_event\n" ); #print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}\n" ); if ( $state == STATE_ALARM || $state == STATE_ALERT ) # In alarm state { if ( !defined($monitor->{LastEvent}) || ($last_event != $monitor->{LastEvent}) ) # A new event { push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event ); } else # The same one as last time, so ignore it { # Do nothing } } elsif ( ($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE) || ($state == STATE_TAPE && $monitor->{LastState} != STATE_TAPE) ) # Out of alarm state { push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event ); } elsif ( defined($monitor->{LastEvent}) && ($last_event != $monitor->{LastEvent}) ) # We've missed a whole event { push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event ); push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event ); } $monitor->{LastState} = $state; $monitor->{LastEvent} = $last_event; } foreach my $connection ( @out_connections ) { if ( $connection->canWrite() ) { $connection->putMessages( \@out_messages ); } } foreach my $connection ( values(%spawned_connections) ) { if ( $connection->canWrite() ) { $connection->putMessages( \@out_messages ); } } Debug( "Checking for timed actions\n" ) if ( int(keys(%actions)) ); my $now = time(); foreach my $action_time ( sort( grep { $_ < $now } keys( %actions ) ) ) { Info( "Found actions expiring at $action_time\n" ); foreach my $action ( @{$actions{$action_time}} ) { my $connection = $action->{connection}; my $message = $action->{message}; Info( "Found action '$message'\n" ); handleMessage( $connection, $message ); } delete( $actions{$action_time} ); } # Allow connections to do their own timed actions foreach my $connection ( @connections ) { my $messages = $connection->timedActions(); if ( defined($messages) ) { foreach my $message ( @$messages ) { handleMessage( $connection, $message ); } } } foreach my $connection ( values(%spawned_connections) ) { my $messages = $connection->timedActions(); if ( defined($messages) ) { foreach my $message ( @$messages ) { handleMessage( $connection, $message ); } } } # If necessary reload monitors if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL ) { foreach my $monitor ( values(%monitors) ) { # Free up any used memory handle zmMemInvalidate( $monitor ); } loadMonitors(); } } Info( "Trigger daemon exiting\n" ); exit; sub loadMonitors { Debug( "Loading monitors\n" ); $monitor_reload_time = time(); my %new_monitors = (); my $sql = "select * from Monitors where find_in_set( Function, 'Modect,Mocord,Nodect' )"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); while( my $monitor = $sth->fetchrow_hashref() ) { next if ( !zmMemVerify( $monitor ) ); # Check shared memory ok if ( defined($monitors{$monitor->{Id}}->{LastState}) ) { $monitor->{LastState} = $monitors{$monitor->{Id}}->{LastState}; } else { $monitor->{LastState} = zmGetMonitorState( $monitor ); } if ( defined($monitors{$monitor->{Id}}->{LastEvent}) ) { $monitor->{LastEvent} = $monitors{$monitor->{Id}}->{LastEvent}; } else { $monitor->{LastEvent} = zmGetLastEvent( $monitor ); } $new_monitors{$monitor->{Id}} = $monitor; } %monitors = %new_monitors; } sub handleMessage { my $connection = shift; my $message = shift; my ( $id, $action, $score, $cause, $text, $showtext ) = split( /\|/, $message ); $score = 0 if ( !defined($score) ); $cause = "" if ( !defined($cause) ); $text = "" if ( !defined($text) ); my $monitor = $monitors{$id}; if ( !$monitor ) { Warning( "Can't find monitor '$id' for message '$message'\n" ); return; } Debug( "Found monitor for id '$id'\n" ); next if ( !zmMemVerify( $monitor ) ); Debug( "Handling action '$action'\n" ); if ( $action =~ /^(enable|disable)(?:\+(\d+))?$/ ) { my $state = $1; my $delay = $2; if ( $state eq "enable" ) { zmMonitorEnable( $monitor ); } else { zmMonitorDisable( $monitor ); } # Force a reload $monitor_reload_time = 0; Info( "Set monitor to $state\n" ); if ( $delay ) { my $action_time = time()+$delay; my $action_text = $id."|".(($state eq "enable")?"disable":"enable"); my $action_array = $actions{$action_time}; if ( !$action_array ) { $action_array = $actions{$action_time} = []; } push( @$action_array, { connection=>$connection, message=>$action_text } ); Debug( "Added timed event '$action_text', expires at $action_time (+$delay secs)\n" ); } } elsif ( $action =~ /^(on|off)(?:\+(\d+))?$/ ) { next if ( !$monitor->{Enabled} ); my $trigger = $1; my $delay = $2; my $trigger_data; if ( $trigger eq "on" ) { zmTriggerEventOn( $monitor, $score, $cause, $text ); zmTriggerShowtext( $monitor, $showtext ) if defined($showtext); Info( "Trigger '$trigger' '$cause'\n" ); } elsif ( $trigger eq "off" ) { my $last_event = zmGetLastEvent( $monitor ); zmTriggerEventOff( $monitor ); zmTriggerShowtext( $monitor, $showtext ) if defined($showtext); Info( "Trigger '$trigger'\n" ); # Wait til it's finished while( zmInAlarm( $monitor ) && ($last_event == zmGetLastEvent( $monitor )) ) { # Tenth of a second usleep( 100000 ); } zmTriggerEventCancel( $monitor ); } else { Info( "Trigger '$trigger'\n" ); zmTriggerEventCancel( $monitor ); } if ( $delay ) { my $action_time = time()+$delay; #my $action_text = $id."|cancel|0|".$cause."|".$text; my $action_text = $id."|cancel"; my $action_array = $actions{$action_time}; if ( !$action_array ) { $action_array = $actions{$action_time} = []; } push( @$action_array, { connection=>$connection, message=>$action_text } ); Debug( "Added timed event '$action_text', expires at $action_time (+$delay secs)\n" ); } } elsif( $action eq "cancel" ) { zmTriggerEventCancel( $monitor ); zmTriggerShowtext( $monitor, $showtext ) if defined($showtext); Info( "Cancelled event\n" ); } elsif( $action eq "show" ) { zmTriggerShowtext( $monitor, $showtext ); Info( "Updated show text to '$showtext'\n" ); } else { Error( "Unrecognised action '$action' in message '$message'\n" ); } } ZoneMinder-1.26.5/scripts/zmupdate.pl.in000066400000000000000000001243271225361755400201640ustar00rootroot00000000000000#!/usr/bin/perl -w # # ========================================================================== # # ZoneMinder Update Script, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This script just checks what the most recent release of ZoneMinder is # at the the moment. It will eventually be responsible for applying and # configuring upgrades etc, including on the fly upgrades. # use strict; use bytes; # ========================================================================== # # These are the elements you can edit to suit your installation # # ========================================================================== use constant CHECK_INTERVAL => (1*24*60*60); # Interval between version checks # ========================================================================== # # Don't change anything below here # # ========================================================================== @EXTRA_PERL_LIB@ use ZoneMinder::Base qw(:all); use ZoneMinder::Config qw(:all); use ZoneMinder::Logger qw(:all); use ZoneMinder::General qw(:all); use ZoneMinder::Database qw(:all); use ZoneMinder::ConfigAdmin qw( :functions ); use POSIX; use DBI; use Getopt::Long; use Data::Dumper; use constant EVENT_PATH => (ZM_DIR_EVENTS=~m|/|)?ZM_DIR_EVENTS:(ZM_PATH_WEB.'/'.ZM_DIR_EVENTS); $| = 1; $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; my $web_uid = (getpwnam( ZM_WEB_USER ))[2]; my $use_log = (($> == 0) || ($> == $web_uid)); logInit( toFile=>$use_log?DEBUG:NOLOG ); logSetSignal(); my $interactive = 1; my $check = 0; my $freshen = 0; my $rename = 0; my $zoneFix = 0; my $migrateEvents = 0; my $version = ''; my $dbUser = ZM_DB_USER; my $dbPass = ZM_DB_PASS; my $updateDir = ''; sub Usage { print( " Usage: zmupdate.pl <-c,--check|-f,--freshen|-v,--version=> [-u -p]> Parameters are :- -c, --check - Check for updated versions of ZoneMinder -f, --freshen - Freshen the configuration in the database. Equivalent of old zmconfig.pl -noi -v, --version= - Force upgrade to the current version from -u, --user= - Alternate DB user with privileges to alter DB -p, --pass= - Password of alternate DB user with privileges to alter DB -d,--dir= - Directory containing update files if not in default build location "); exit( -1 ); } if ( !GetOptions( 'check'=>\$check, 'freshen'=>\$freshen, 'rename'=>\$rename, 'zone-fix'=>\$zoneFix, 'migrate-events'=>\$migrateEvents, 'version=s'=>\$version, 'interactive!'=>\$interactive, 'user:s'=>\$dbUser, 'pass:s'=>\$dbPass, 'dir:s'=>\$updateDir ) ) { Usage(); } my $dbh = zmDbConnect(); *ZoneMinder::Database::ZM_DB_USER = sub { $dbUser } if ZoneMinder::Database::ZM_DB_USER ne $dbUser; *ZoneMinder::Database::ZM_DB_PASS = sub { $dbPass } if ZoneMinder::Database::ZM_DB_PASS ne $dbPass; if ( ! ($check || $freshen || $rename || $zoneFix || $migrateEvents || $version) ) { if ( ZM_DYN_DB_VERSION ) { $version = ZM_DYN_DB_VERSION; } else { print( STDERR "Please give a valid option\n" ); Usage(); } } if ( ($check + $freshen + $rename + $zoneFix + $migrateEvents + ($version?1:0)) > 1 ) { print( STDERR "Please give only one option\n" ); Usage(); } if ( $check && ZM_CHECK_FOR_UPDATES ) { print( "Update agent starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); my $currVersion = ZM_DYN_CURR_VERSION; my $lastVersion = ZM_DYN_LAST_VERSION; my $lastCheck = ZM_DYN_LAST_CHECK; if ( !$currVersion ) { $currVersion = ZM_VERSION; my $sql = "update Config set Value = ? where Name = 'ZM_DYN_CURR_VERSION'"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( "$currVersion" ) or die( "Can't execute: ".$sth->errstr() ); } while( 1 ) { my $now = time(); if ( !$lastVersion || !$lastCheck || (($now-$lastCheck) > CHECK_INTERVAL) ) { Info( "Checking for updates\n" ); use LWP::UserAgent; my $ua = LWP::UserAgent->new; $ua->agent( "ZoneMinder Update Agent/".ZM_VERSION ); if ( eval('defined(ZM_UPDATE_CHECK_PROXY)') ) { no strict 'subs'; if ( ZM_UPDATE_CHECK_PROXY ) { $ua->proxy( "http", ZM_UPDATE_CHECK_PROXY ); } use strict 'subs'; } my $req = HTTP::Request->new( GET=>'http://zoneminder.github.io/ZoneMinder/version.txt' ); my $res = $ua->request($req); if ( $res->is_success ) { $lastVersion = $res->content; chomp($lastVersion); $lastCheck = $now; Info( "Got version: '".$lastVersion."'\n" ); my $lv_sql = "update Config set Value = ? where Name = 'ZM_DYN_LAST_VERSION'"; my $lv_sth = $dbh->prepare_cached( $lv_sql ) or die( "Can't prepare '$lv_sql': ".$dbh->errstr() ); my $lv_res = $lv_sth->execute( $lastVersion ) or die( "Can't execute: ".$lv_sth->errstr() ); my $lc_sql = "update Config set Value = ? where Name = 'ZM_DYN_LAST_CHECK'"; my $lc_sth = $dbh->prepare_cached( $lc_sql ) or die( "Can't prepare '$lc_sql': ".$dbh->errstr() ); my $lc_res = $lc_sth->execute( $lastCheck ) or die( "Can't execute: ".$lc_sth->errstr() ); } else { Error( "Error check failed: '".$res->status_line()."'\n" ); } } sleep( 3600 ); } print( "Update agent exiting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); } if ( $rename ) { require File::Find; chdir( EVENT_PATH ); sub renameImage { my $file = $_; # Ignore directories if ( -d $file ) { print( "Checking directory '$file'\n" ); return; } if ( $file !~ /(capture|analyse)-(\d+)(\.jpg)/ ) { return; } my $newFile = "$2-$1$3"; print( "Renaming '$file' to '$newFile'\n" ); rename( $file, $newFile ) or warn( "Can't rename '$file' to '$newFile'" ); } File::Find::find( \&renameImage, '.' ); } if ( $zoneFix ) { my $sql = "select Z.*, M.Width as MonitorWidth, M.Height as MonitorHeight from Zones as Z inner join Monitors as M on Z.MonitorId = M.Id where Z.Units = 'Percent'"; 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 @zones; while( my $zone = $sth->fetchrow_hashref() ) { push( @zones, $zone ); } $sth->finish(); foreach my $zone ( @zones ) { my $zone_width = (($zone->{HiX}*$zone->{MonitorWidth})-($zone->{LoX}*$zone->{MonitorWidth}))/100; my $zone_height = (($zone->{HiY}*$zone->{MonitorHeight})-($zone->{LoY}*$zone->{MonitorHeight}))/100; my $zone_area = $zone_width * $zone_height; my $monitor_area = $zone->{MonitorWidth} * $zone->{MonitorHeight}; my $sql = "update Zones set MinAlarmPixels = ?, MaxAlarmPixels = ?, MinFilterPixels = ?, MaxFilterPixels = ?, MinBlobPixels = ?, MaxBlobPixels = ? where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( ($zone->{MinAlarmPixels}*$monitor_area)/$zone_area, ($zone->{MaxAlarmPixels}*$monitor_area)/$zone_area, ($zone->{MinFilterPixels}*$monitor_area)/$zone_area, ($zone->{MaxFilterPixels}*$monitor_area)/$zone_area, ($zone->{MinBlobPixels}*$monitor_area)/$zone_area, ($zone->{MaxBlobPixels}*$monitor_area)/$zone_area, $zone->{Id} ) or die( "Can't execute: ".$sth->errstr() ); } } if ( $migrateEvents ) { my $webUid = (getpwnam( ZM_WEB_USER ))[2]; my $webGid = (getgrnam( ZM_WEB_USER ))[2]; if ( !(($> == 0) || ($> == $webUid)) ) { print( "Error, migrating events can only be done as user root or ".ZM_WEB_USER.".\n" ); exit( -1 ); } # Run as web user/group $( = $webGid; $) = $webGid; $< = $webUid; $> = $webUid; print( "\nAbout to convert saved events to deep storage, please ensure that ZoneMinder is fully stopped before proceeding.\nThis process is not easily reversible. Are you sure you wish to proceed?\n\nPress 'y' to continue or 'n' to abort : " ); my $response = ; chomp( $response ); while ( $response !~ /^[yYnN]$/ ) { print( "Please press 'y' to continue or 'n' to abort only : " ); $response = ; chomp( $response ); } if ( $response =~ /^[yY]$/ ) { print( "Converting all events to deep storage.\n" ); chdir( ZM_PATH_WEB ); my $sql = "select *, unix_timestamp(StartTime) as UnixStartTime from Events"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute(); if ( !$res ) { Fatal( "Can't fetch Events: ".$sth->errstr() ); } while( my $event = $sth->fetchrow_hashref() ) { my $oldEventPath = ZM_DIR_EVENTS.'/'.$event->{MonitorId}.'/'.$event->{Id}; if ( !-d $oldEventPath ) { print( "Warning, can't find old event path '$oldEventPath', already converted?\n" ); next; } print( "Converting event ".$event->{Id}."\n" ); my $newDatePath = ZM_DIR_EVENTS.'/'.$event->{MonitorId}.'/'.strftime( "%y/%m/%d", localtime($event->{UnixStartTime}) ); my $newTimePath = strftime( "%H/%M/%S", localtime($event->{UnixStartTime}) ); my $newEventPath = $newDatePath.'/'.$newTimePath; ( my $truncEventPath = $newEventPath ) =~ s|/\d+$||; makePath( $truncEventPath, ZM_PATH_WEB ); my $idLink = $newDatePath.'/.'.$event->{Id}; symlink( $newTimePath, $idLink ) or die( "Can't symlink $newTimePath -> $idLink: $!" ); rename( $oldEventPath, $newEventPath ) or die( "Can't move $oldEventPath -> $newEventPath: $!" ); } print( "Updating configuration.\n" ); $sql = "update Config set Value = ? where Name = 'ZM_USE_DEEP_STORAGE'"; $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); $res = $sth->execute( 1 ) or die( "Can't execute: ".$sth->errstr() ); print( "All events converted.\n\n" ); } else { print( "Aborting event conversion.\n\n" ); } } if ( $freshen ) { print( "\nFreshening configuration in database\n" ); loadConfigFromDB(); saveConfigToDB(); } # Now check for MyISAM Tables my @MyISAM_Tables; my $sql = "SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='zm' AND engine = 'MyISAM'"; 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() ); while( my $dbTable = $sth->fetchrow() ) { push @MyISAM_Tables, $dbTable; } $sth->finish(); if ( @MyISAM_Tables ) { print( "\nPrevious versions of ZoneMinder used the MyISAM database engine.\nHowever, the recommended database engine is InnoDB.\n"); print( "\nHint: InnoDB tables are much less likely to be corrupted during an unclean shutdown.\n\nPress 'y' to convert your tables to InnoDB or 'n' to skip : "); my $response = ; chomp( $response ); if ( $response =~ /^[yY]$/ ) { print "\nConverting MyISAM tables to InnoDB. Please wait.\n"; foreach (@MyISAM_Tables) { my $sql = "ALTER TABLE $_ ENGINE = InnoDB"; 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() ); $sth->finish(); } } } if ( $version ) { my ( $detaint_version ) = $version =~ /^([\w.]+)$/; $version = $detaint_version; if ( ZM_VERSION eq $version ) { print( "\nDatabase already at version $version, update aborted.\n\n" ); exit( 0 ); } print( "\nInitiating database upgrade to version ".ZM_VERSION." from version $version\n" ); if ( $interactive ) { if ( ZM_DYN_DB_VERSION && ZM_DYN_DB_VERSION ne $version ) { print( "\nWARNING - You have specified an upgrade from version $version but the database version found is ".ZM_DYN_DB_VERSION.". Is this correct?\nPress enter to continue or ctrl-C to abort : " ); my $response = ; } print( "\nPlease ensure that ZoneMinder is stopped on your system prior to upgrading the database.\nPress enter to continue or ctrl-C to stop : " ); my $response = ; print( "\nDo you wish to take a backup of your database prior to upgrading?\nThis may result in a large file in @ZM_TMPDIR@ if you have a lot of events.\nPress 'y' for a backup or 'n' to continue : " ); $response = ; chomp( $response ); while ( $response !~ /^[yYnN]$/ ) { print( "Please press 'y' for a backup or 'n' to continue only : " ); $response = ; chomp( $response ); } if ( $response =~ /^[yY]$/ ) { my ( $host, $port ) = ( ZM_DB_HOST =~ /^([^:]+)(?::(.+))?$/ ); my $command = "mysqldump -h".$host; $command .= " -P".$port if defined($port); if ( $dbUser ) { $command .= " -u".$dbUser; if ( $dbPass ) { $command .= " -p".$dbPass; } } my $backup = "@ZM_TMPDIR@/".ZM_DB_NAME."-".$version.".dump"; $command .= " --add-drop-table --databases ".ZM_DB_NAME." > ".$backup; print( "Creating backup to $backup. This may take several minutes.\n" ); print( "Executing '$command'\n" ) if ( logDebugging() ); my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { chomp( $output ); print( "Output: $output\n" ); } if ( $status ) { die( "Command '$command' exited with status: $status\n" ); } else { print( "Database successfully backed up to $backup, proceeding to upgrade.\n" ); } } elsif ( $response !~ /^[nN]$/ ) { die( "Unexpected response '$response'" ); } } sub patchDB { my $dbh = shift; my $version = shift; my ( $host, $port ) = ( ZM_DB_HOST =~ /^([^:]+)(?::(.+))?$/ ); my $command = "mysql -h".$host; $command .= " -P".$port if defined($port); if ( $dbUser ) { $command .= " -u".$dbUser; if ( $dbPass ) { $command .= " -p".$dbPass; } } $command .= " ".ZM_DB_NAME." < "; if ( $updateDir ) { $command .= $updateDir; } else { $command .= ZM_PATH_DATA."/db"; } $command .= "/zm_update-".$version.".sql"; print( "Executing '$command'\n" ) if ( logDebugging() ); my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { chomp( $output ); print( "Output: $output\n" ); } if ( $status ) { die( "Command '$command' exited with status: $status\n" ); } else { print( "\nDatabase successfully upgraded from version $version.\n" ); 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 $res = $sth->execute( $version ) or die( "Can't execute: ".$sth->errstr() ); } } print( "\nUpgrading database to version ".ZM_VERSION."\n" ); # Update config first of all loadConfigFromDB(); saveConfigToDB(); my $cascade = undef; if ( $cascade || $version eq "1.19.0" ) { # Patch the database patchDB( $dbh, "1.19.0" ); $cascade = !undef; } if ( $cascade || $version eq "1.19.1" ) { # Patch the database patchDB( $dbh, "1.19.1"); $cascade = !undef; } if ( $cascade || $version eq "1.19.2" ) { # Patch the database patchDB( $dbh, "1.19.2" ); $cascade = !undef; } if ( $cascade || $version eq "1.19.3" ) { # Patch the database patchDB( $dbh, "1.19.3" ); $cascade = !undef; } if ( $cascade || $version eq "1.19.4" ) { # Rename the event directories and create a new symlink for the names chdir( EVENT_PATH ); my $sql = "select * from Monitors order by Id"; 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() ); while( my $monitor = $sth->fetchrow_hashref() ) { if ( -d $monitor->{Name} ) { rename( $monitor->{Name}, $monitor->{Id} ) or warn( "Can't rename existing monitor directory '$monitor->{Name}' to '$monitor->{Id}': $!" ); symlink( $monitor->{Id}, $monitor->{Name} ) or warn( "Can't symlink monitor directory '$monitor->{Id}' to '$monitor->{Name}': $!" ); } } $sth->finish(); # Patch the database patchDB( $dbh, "1.19.4" ); $cascade = !undef; } if ( $cascade || $version eq "1.19.5" ) { print( "\nThis version now only uses one database user.\nPlease ensure you have run zmconfig.pl and re-entered your database username and password prior to upgrading, or the upgrade will fail.\nPress enter to continue or ctrl-C to stop : " ); # Patch the database my $dummy = ; patchDB( $dbh, "1.19.5" ); $cascade = !undef; } if ( $cascade || $version eq "1.20.0" ) { # Patch the database patchDB( $dbh, "1.20.0" ); $cascade = !undef; } if ( $cascade || $version eq "1.20.1" ) { # Patch the database patchDB( $dbh, "1.20.1" ); $cascade = !undef; } if ( $cascade || $version eq "1.21.0" ) { # Patch the database patchDB( $dbh, "1.21.0" ); $cascade = !undef; } if ( $cascade || $version eq "1.21.1" ) { # Patch the database patchDB( $dbh, "1.21.1" ); $cascade = !undef; } if ( $cascade || $version eq "1.21.2" ) { # Patch the database patchDB( $dbh, "1.21.2" ); $cascade = !undef; } if ( $cascade || $version eq "1.21.3" ) { # Patch the database patchDB( $dbh, "1.21.3" ); # Add appropriate widths and heights to events { print( "Updating events. This may take a few minutes. Please wait.\n" ); my $sql = "select * from Monitors order by Id"; 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() ); while( my $monitor = $sth->fetchrow_hashref() ) { my $sql = "update Events set Width = ?, Height = ? where MonitorId = ?"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $monitor->{Width}, $monitor->{Height}, $monitor->{Id} ) or die( "Can't execute: ".$sth->errstr() ); } $sth->finish(); } # Add sequence numbers { print( "Updating monitor sequences. Please wait.\n" ); my $sql = "select * from Monitors order by Id"; 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 $sequence = 1; while( my $monitor = $sth->fetchrow_hashref() ) { my $sql = "update Monitors set Sequence = ? where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $sequence++, $monitor->{Id} ) or die( "Can't execute: ".$sth->errstr() ); } $sth->finish(); } # Update saved filters { print( "Updating saved filters. Please wait.\n" ); my $sql = "select * from Filters"; 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 @filters; while( my $filter = $sth->fetchrow_hashref() ) { push( @filters, $filter ); } $sth->finish(); $sql = "update Filters set Query = ? where Name = ?"; $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); foreach my $filter ( @filters ) { if ( $filter->{Query} =~ /op\d=&/ ) { ( my $newQuery = $filter->{Query} ) =~ s/(op\d=)&/$1=&/g; $res = $sth->execute( $newQuery, $filter->{Name} ) or die( "Can't execute: ".$sth->errstr() ); } } } $cascade = !undef; } if ( $cascade || $version eq "1.21.4" ) { # Patch the database patchDB( $dbh, "1.21.4" ); # Convert zones to new format { print( "Updating zones. Please wait.\n" ); # Get the existing zones from the DB my $sql = "select Z.*,M.Width,M.Height from Zones as Z inner join Monitors as M on (Z.MonitorId = M.Id)"; 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 @zones; while( my $zone = $sth->fetchrow_hashref() ) { push( @zones, $zone ); } $sth->finish(); no strict 'refs'; foreach my $zone ( @zones ) { # Create the coordinate strings if ( $zone->{Units} eq "Pixels" ) { my $sql = "update Zones set NumCoords = 4, Coords = concat( LoX,',',LoY,' ',HiX,',',LoY,' ',HiX,',',HiY,' ',LoX,',',HiY ), Area = round( ((HiX-LoX)+1)*((HiY-LoY)+1) ) where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $zone->{Id} ) or die( "Can't execute: ".$sth->errstr() ); } else { my $loX = ($zone->{LoX} * ($zone->{Width}-1) ) / 100; my $hiX = ($zone->{HiX} * ($zone->{Width}-1) ) / 100; my $loY = ($zone->{LoY} * ($zone->{Height}-1) ) / 100; my $hiY = ($zone->{HiY} * ($zone->{Height}-1) ) / 100; my $area = (($hiX-$loX)+1)*(($hiY-$loY)+1); my $sql = "update Zones set NumCoords = 4, Coords = concat( round(?),',',round(?),' ',round(?),',',round(?),' ',round(?),',',round(?),' ',round(?),',',round(?) ), Area = round(?), MinAlarmPixels = round(?), MaxAlarmPixels = round(?), MinFilterPixels = round(?), MaxFilterPixels = round(?), MinBlobPixels = round(?), MaxBlobPixels = round(?) where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $loX, $loY, $hiX, $loY, $hiX, $hiY, $loX, $hiY, $area, ($zone->{MinAlarmPixels}*$area)/100, ($zone->{MaxAlarmPixels}*$area)/100, ($zone->{MinFilterPixels}*$area)/100, ($zone->{MaxFilterPixels}*$area)/100, ($zone->{MinBlobPixels}*$area)/100, ($zone->{MaxBlobPixels}*$area)/100, $zone->{Id} ) or die( "Can't execute: ".$sth->errstr() ); } } } # Convert run states to new format { print( "Updating run states. Please wait.\n" ); # Get the existing zones from the DB my $sql = "select * from States"; 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 @states; while( my $state = $sth->fetchrow_hashref() ) { push( @states, $state ); } $sth->finish(); foreach my $state ( @states ) { my @new_defns; foreach my $defn ( split( /,/, $state->{Definition} ) ) { push( @new_defns, $defn.":1" ); } my $sql = "update States set Definition = ? where Name = ?"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( join( ',', @new_defns ), $state->{Name} ) or die( "Can't execute: ".$sth->errstr() ); } } $cascade = !undef; } if ( $cascade || $version eq "1.22.0" ) { # Patch the database patchDB( $dbh, "1.22.0" ); # Check for maximum FPS setting and update alarm max fps settings { print( "Updating monitors. Please wait.\n" ); if ( defined(&ZM_NO_MAX_FPS_ON_ALARM) && &ZM_NO_MAX_FPS_ON_ALARM ) { # Update the individual monitor settings to match the previous global one my $sql = "update Monitors set AlarmMaxFPS = NULL"; 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() ); } else { # Update the individual monitor settings to match the previous global one my $sql = "update Monitors set AlarmMaxFPS = MaxFPS"; 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() ); } } { print( "Updating mail configuration. Please wait.\n" ); my ( $sql, $sth, $res ); if ( defined(&ZM_EMAIL_TEXT) && &ZM_EMAIL_TEXT ) { my ( $email_subject, $email_body ) = ZM_EMAIL_TEXT =~ /subject\s*=\s*"([^\n]*)".*body\s*=\s*"(.*)"?$/ms; $sql = "replace into Config set Id = 0, Name = 'ZM_EMAIL_SUBJECT', Value = '".$email_subject."', Type = 'string', DefaultValue = 'ZoneMinder: Alarm - %MN%-%EI% (%ESM% - %ESA% %EFA%)', Hint = 'string', Pattern = '(?-xism:^(.+)\$)', Format = ' \$1 ', Prompt = 'The subject of the email used to send matching event details', Help = 'This option is used to define the subject of the email that is sent for any events that match the appropriate filters.', Category = 'mail', Readonly = '0', Requires = 'ZM_OPT_EMAIL=1'"; $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); $sql = "replace into Config set Id = 0, Name = 'ZM_EMAIL_BODY', Value = '".$email_body."', Hint = 'free text', Pattern = '(?-xism:^(.+)\$)', Format = ' \$1 ', Prompt = 'The body of the email used to send matching event details', Help = 'This option is used to define the content of the email that is sent for any events that match the appropriate filters.', Category = 'mail', Readonly = '0', Requires = 'ZM_OPT_EMAIL=1'"; $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); } if ( defined(&ZM_MESSAGE_TEXT) && &ZM_MESSAGE_TEXT ) { my ( $message_subject, $message_body ) = ZM_MESSAGE_TEXT =~ /subject\s*=\s*"([^\n]*)".*body\s*=\s*"(.*)"?$/ms; $sql = "replace into Config set Id = 0, Name = 'ZM_MESSAGE_SUBJECT', Value = '".$message_subject."', Type = 'string', DefaultValue = 'ZoneMinder: Alarm - %MN%-%EI%', Hint = 'string', Pattern = '(?-xism:^(.+)\$)', Format = ' \$1 ', Prompt = 'The subject of the message used to send matching event details', Help = 'This option is used to define the subject of the message that is sent for any events that match the appropriate filters.', Category = 'mail', Readonly = '0', Requires = 'ZM_OPT_MESSAGE=1'"; $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); $sql = "replace into Config set Id = 0, Name = 'ZM_MESSAGE_BODY', Value = '".$message_body."', Type = 'text', DefaultValue = 'ZM alarm detected - %ED% secs, %EF%/%EFA% frames, t%EST%/m%ESM%/a%ESA% score.', Hint = 'free text', Pattern = '(?-xism:^(.+)\$)', Format = ' \$1 ', Prompt = 'The body of the message used to send matching event details', Help = 'This option is used to define the content of the message that is sent for any events that match the appropriate filters.', Category = 'mail', Readonly = '0', Requires = 'ZM_OPT_MESSAGE=1'"; $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); } } $cascade = !undef; } if ( $cascade || $version eq "1.22.1" ) { # Patch the database patchDB( $dbh, "1.22.1" ); $cascade = !undef; } if ( $cascade || $version eq "1.22.2" ) { # Patch the database patchDB( $dbh, "1.22.2" ); $cascade = !undef; } if ( $cascade || $version eq "1.22.3" ) { # Patch the database patchDB( $dbh, "1.22.3" ); # Convert timestamp strings to new format { my $sql = "select * from Monitors"; 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 @db_monitors; while( my $db_monitor = $sth->fetchrow_hashref() ) { push( @db_monitors, $db_monitor ); } $sth->finish(); foreach my $db_monitor ( @db_monitors ) { if ( $db_monitor->{LabelFormat} =~ /\%\%s/ ) { $db_monitor->{LabelFormat} =~ s/\%\%s/%N/; $db_monitor->{LabelFormat} =~ s/\%\%s/%Q/; my $sql = "update Monitors set LabelFormat = ? where Id = ?"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $db_monitor->{LabelFormat}, $db_monitor->{Id} ) or die( "Can't execute: ".$sth->errstr() ); } } } # Convert filters to new format { my $sql = "select * from Filters"; 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 @dbFilters; while( my $dbFilter = $sth->fetchrow_hashref() ) { push( @dbFilters, $dbFilter ); } $sth->finish(); foreach my $dbFilter ( @dbFilters ) { my %filter_terms; foreach my $filter_parm ( split( /&/, $dbFilter->{Query} ) ) { my( $key, $value ) = split( /=/, $filter_parm, 2 ); if ( $key ) { $filter_terms{$key} = $value; } } my $filter = { 'terms' => [] }; for ( my $i = 1; $i <= $filter_terms{trms}; $i++ ) { my $term = {}; my $conjunction_name = "cnj$i"; my $obracket_name = "obr$i"; my $cbracket_name = "cbr$i"; my $attr_name = "attr$i"; my $op_name = "op$i"; my $value_name = "val$i"; $term->{cnj} = $filter_terms{$conjunction_name} if ( $filter_terms{$conjunction_name} ); $term->{obr} = $filter_terms{$obracket_name} if ( $filter_terms{$obracket_name} ); $term->{attr} = $filter_terms{$attr_name} if ( $filter_terms{$attr_name} ); $term->{val} = $filter_terms{$value_name} if ( defined($filter_terms{$value_name}) ); $term->{op} = $filter_terms{$op_name} if ( $filter_terms{$op_name} ); $term->{cbr} = $filter_terms{$cbracket_name} if ( $filter_terms{$cbracket_name} ); push( @{$filter->{terms}}, $term ); } $filter->{sort_field} = $filter_terms{sort_field} if ( $filter_terms{sort_field} ); $filter->{sort_asc} = $filter_terms{sort_asc} if ( $filter_terms{sort_asc} ); $filter->{limit} = $filter_terms{limit} if ( $filter_terms{limit} ); my $newQuery = 'a:'.int(keys(%$filter)).':{s:5:"terms";a:'.int(@{$filter->{terms}}).':{'; my $i = 0; foreach my $term ( @{$filter->{terms}} ) { $newQuery .= 'i:'.$i.';a:'.int(keys(%$term)).':{'; while ( my ( $key, $val ) = each( %$term ) ) { $newQuery .= 's:'.length($key).':"'.$key.'";'; $newQuery .= 's:'.length($val).':"'.$val.'";'; } $newQuery .= '}'; $i++; } $newQuery .= '}'; foreach my $field ( "sort_field", "sort_asc", "limit" ) { if ( defined($filter->{$field}) ) { $newQuery .= 's:'.length($field).':"'.$field.'";'; $newQuery .= 's:'.length($filter->{$field}).':"'.$filter->{$field}.'";'; } } $newQuery .= '}'; my $sql = "update Filters set Query = ? where Name = ?"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $newQuery, $dbFilter->{Name} ) or die( "Can't execute: ".$sth->errstr() ); } } # Update the stream quality setting to the old image quality ones { my $dbh = zmDbConnect(); my $sql = "update Config set Value = ? where Name = 'ZM_JPEG_STREAM_QUALITY'"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( ZM_JPEG_IMAGE_QUALITY ) or die( "Can't execute: ".$sth->errstr() ); } $cascade = !undef; } if ( $cascade || $version eq "1.23.0" ) { # Patch the database patchDB( $dbh, "1.23.0" ); $cascade = !undef; } if ( $cascade || $version eq "1.23.1" ) { # Patch the database patchDB( $dbh, "1.23.1" ); $cascade = !undef; } if ( $cascade || $version eq "1.23.2" ) { # Patch the database patchDB( $dbh, "1.23.2" ); $cascade = !undef; } if ( $cascade || $version eq "1.23.3" ) { # Patch the database patchDB( $dbh, "1.23.3" ); $cascade = !undef; } if ( $cascade || $version eq "1.24.0" ) { # Patch the database patchDB( $dbh, "1.24.0" ); $cascade = !undef; } if ( $cascade || $version eq "1.24.1" ) { # Patch the database patchDB( $dbh, "1.24.1" ); $cascade = !undef; } if ( $cascade || $version eq "1.24.2" ) { # Patch the database patchDB( $dbh, "1.24.2" ); $cascade = !undef; } if ( $cascade || $version eq "1.24.3" ) { my $result = eval { require PHP::Serialization; PHP::Serialization->import(); }; die( "Unable to perform upgrade from 1.24.3, PHP::Serialization module not found" ) if ( $result ); # Patch the database patchDB( $dbh, "1.24.3" ); # Convert filters to JSON from PHP format serialisation { print( "\nConverting filters from PHP to JSON format\n" ); my $sql = "select * from Filters"; 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 @dbFilters; while( my $dbFilter = $sth->fetchrow_hashref() ) { push( @dbFilters, $dbFilter ); } $sth->finish(); foreach my $dbFilter ( @dbFilters ) { print( " ".$dbFilter->{Name} ); eval { my $phpQuery = $dbFilter->{Query}; my $query = PHP::Serialization::unserialize( $phpQuery ); my $jsonQuery = jsonEncode( $query ); my $sql = "update Filters set Query = ? where Name = ?"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( $jsonQuery, $dbFilter->{Name} ) or die( "Can't execute: ".$sth->errstr() ); }; if ( $@ ) { print( " - failed, please check or report. Query is '".$dbFilter->{Query}."'\n" ); print( $@ ); } else { print( " - complete\n" ); } } print( "Conversion complete\n" ); } $cascade = !undef; } if ( $cascade || $version eq "1.24.4" ) { # Patch the database patchDB( $dbh, "1.24.4" ); # Copy the FTP specific values to the new general config my $fetchSql = "select * from Config where Name like 'ZM_UPLOAD_FTP_%'"; my $fetchSth = $dbh->prepare_cached( $fetchSql ) or die( "Can't prepare '$fetchSql': ".$dbh->errstr() ); my $updateSql = "update Config set Value = ? where Name = ?"; my $updateSth = $dbh->prepare_cached( $updateSql ) or die( "Can't prepare '$updateSql': ".$dbh->errstr() ); my $fetchRes = $fetchSth->execute() or die( "Can't execute: ".$fetchSth->errstr() ); while( my $config = $fetchSth->fetchrow_hashref() ) { ( my $name = $config->{Name} ) =~ s/_FTP_/_/; my $updateRes = $updateSth->execute( $config->{Value}, $name ) or die( "Can't execute: ".$updateSth->errstr() ); } $cascade = !undef; } if ( $cascade || $version lt "1.26.0" ) { my $sth = $dbh->prepare_cached( 'select * from Monitors LIMIT 0,1' ); die "Error: " . $dbh->errstr . "\n" unless ($sth); die "Error: " . $sth->errstr . "\n" unless ($sth->execute); my $columns = $sth->{'NAME'}; if ( ! grep(/^Colours$/, @$columns ) ) { $dbh->do(q{alter table Monitors add column `Colours` tinyint(3) unsigned NOT NULL default '1' after `Height`;}); } # end if if ( ! grep(/^Deinterlacing$/, @$columns ) ) { $dbh->do(q{alter table Monitors add column `Deinterlacing` INT unsigned NOT NULL default '0' after `Orientation`;}); } # end if $sth->finish(); $cascade = !undef; $version = '1.26.0'; } if ( $version ge '1.26.0' ) { my @files; $updateDir = ZM_PATH_DATA."/db" if ! $updateDir; opendir( my $dh, $updateDir ) || die "Can't open updateDir $!"; @files = sort grep { (!/^\./) && /^zm_update\-[\d\.]+\.sql$/ && -f "$updateDir/$_" } readdir($dh); closedir $dh; if ( ! @files ) { die "Should have found upgrade scripts at $updateDir\n"; } # end if $dbh->{'AutoCommit'} = 0; foreach my $patch ( @files ) { my ( $v ) = $patch =~ /^zm_update\-([\d\.]+)\.sql$/; if ( $v gt $version ) { print( "Upgrading DB to $v from $version\n" ); patchDB( $dbh, $v ); if ( $dbh->errstr() ) { $dbh->rollback(); die "Error: " . $dbh->errstr(). ". Rolled back.\n"; } # end if error } # end if } # end foreach patchfile $dbh->{'AutoCommit'} = 1; $cascade = !undef; } # end if if ( $cascade ) { my $installed_version = ZM_VERSION; my $sql = "update Config set Value = ? where Name = ?"; 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() ); $res = $sth->execute( "$installed_version", "ZM_DYN_CURR_VERSION" ) or die( "Can't execute: ".$sth->errstr() ); } else { zmDbDisconnect(); die( "Can't find upgrade from version '$version'" ); } print( "\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n" ); } zmDbDisconnect(); exit( 0 ); ZoneMinder-1.26.5/scripts/zmvideo.pl.in000066400000000000000000000152161225361755400200040ustar00rootroot00000000000000#!/usr/bin/perl -wT # # ========================================================================== # # ZoneMinder Video Creation Script, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This script is used to create MPEG videos of events for the web pages # or as email attachments. # use strict; use bytes; # ========================================================================== # # You shouldn't need to change anything from here downwards # # ========================================================================== @EXTRA_PERL_LIB@ use ZoneMinder; use DBI; use Data::Dumper; use POSIX qw(strftime); use Getopt::Long qw(:config no_ignore_case ); $| = 1; $ENV{PATH} = '/bin:/usr/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; logInit(); my $event_id; my $format = 'mpg'; my $rate = ''; my $scale = ''; my $fps = ''; my $size = ''; my $overwrite = 0; my @formats = split( /\s+/, ZM_FFMPEG_FORMATS ); for ( my $i = 0; $i < @formats; $i++ ) { if ( $i =~ /^(.+)\*$/ ) { $format = $formats[$i] = $1; } } sub Usage { print( " Usage: zmvideo.pl -e ,--event= [--format ] [--rate=] [--scale=] [--fps=] [--size=] [--overwrite] Parameters are :- -e, --event= - What event to create the video for -f, --format= - What format to create the video in, default is mpg. For ffmpeg only. -r, --rate= - Relative rate , 1 = realtime, 2 = double speed , 0.5 = half speed etc -s, --scale= - Scale, 1 = normal, 2 = double size, 0.5 = half size etc -F, --fps= - Absolute frame rate, in frames per second -S, --size= - Absolute video size, WxH or other specification supported by ffmpeg -o, --overwrite - Whether to overwrite an existing file, off by default. "); exit( -1 ); } if ( !GetOptions( 'event=i'=>\$event_id, 'format|f=s'=>\$format, 'rate|r=f'=>\$rate, 'scale|s=f'=>\$scale, 'fps|F=f'=>\$fps, 'size|S=s'=>\$size, 'overwrite'=>\$overwrite ) ) { Usage(); } if ( !$event_id || $event_id < 0 ) { print( STDERR "Please give a valid event id\n" ); Usage(); } if ( !ZM_OPT_FFMPEG ) { print( STDERR "Mpeg encoding is not currently enabled\n" ); exit(-1); } if ( !$rate && !$fps ) { $rate = 1; } if ( !$scale && !$size ) { $scale = 1; } if ( $rate && ($rate < 0.25 || $rate > 100) ) { print( STDERR "Rate is out of range, 0.25 >= rate <= 100\n" ); Usage(); } if ( $scale && ($scale < 0.25 || $scale > 4) ) { print( STDERR "Scale is out of range, 0.25 >= scale <= 4\n" ); Usage(); } if ( $fps && ($fps > 30) ) { print( STDERR "FPS is out of range, <= 30\n" ); Usage(); } my ( $detaint_format ) = $format =~ /^(\w+)$/; my ( $detaint_rate ) = $rate =~ /^(-?\d+(?:\.\d+)?)$/; my ( $detaint_scale ) = $scale =~ /^(-?\d+(?:\.\d+)?)$/; my ( $detaint_fps ) = $fps =~ /^(-?\d+(?:\.\d+)?)$/; my ( $detaint_size ) = $size =~ /^(\w+)$/; $format = $detaint_format; $rate = $detaint_rate; $scale = $detaint_scale; $fps = $detaint_fps; $size = $detaint_size; my $dbh = zmDbConnect(); my @filters; my $sql = "select max(F.Delta)-min(F.Delta) as FullLength, E.*, unix_timestamp(E.StartTime) as Time, M.Name as MonitorName, M.Width as MonitorWidth, M.Height as MonitorHeight, M.Palette from Frames as F inner join Events as E on F.EventId = E.Id inner join Monitors as M on E.MonitorId = M.Id where EventId = '$event_id' group by F.EventId"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); my $event = $sth->fetchrow_hashref(); $sth->finish(); my $event_path = getEventPath( $event ); chdir( $event_path ); ( my $video_name = $event->{Name} ) =~ s/\s/_/g; my @file_parts; if ( $rate ) { my $file_rate = $rate; $file_rate =~ s/\./_/; $file_rate =~ s/_00//; $file_rate =~ s/(_\d+)0+$/$1/; $file_rate = 'r'.$file_rate; push( @file_parts, $file_rate ); } elsif ( $fps ) { my $file_fps = $fps; $file_fps =~ s/\./_/; $file_fps =~ s/_00//; $file_fps =~ s/(_\d+)0+$/$1/; $file_fps = 'R'.$file_fps; push( @file_parts, $file_fps ); } if ( $scale ) { my $file_scale = $scale; $file_scale =~ s/\./_/; $file_scale =~ s/_00//; $file_scale =~ s/(_\d+)0+$/$1/; $file_scale = 's'.$file_scale; push( @file_parts, $file_scale ); } elsif ( $size ) { my $file_size = 'S'.$size; push( @file_parts, $file_size ); } my $video_file = "$video_name-".$file_parts[0]."-".$file_parts[1].".$format"; if ( $overwrite || !-s $video_file ) { Info( "Creating video file $video_file for event $event->{Id}\n" ); my $frame_rate = sprintf( "%.2f", $event->{Frames}/$event->{FullLength} ); if ( $rate ) { if ( $rate != 1.0 ) { $frame_rate *= $rate; } } elsif ( $fps ) { $frame_rate = $fps; } my $width = $event->{MonitorWidth}; my $height = $event->{MonitorHeight}; my $video_size = " ${width}x${height}"; if ( $scale ) { if ( $scale != 1.0 ) { $width = int($width*$scale); $height = int($height*$scale); $video_size = " ${width}x${height}"; } } elsif ( $size ) { $video_size = $size; } my $command = ZM_PATH_FFMPEG." -y -r $frame_rate ".ZM_FFMPEG_INPUT_OPTIONS." -i %0".ZM_EVENT_IMAGE_DIGITS."d-capture.jpg -s $video_size ".ZM_FFMPEG_OUTPUT_OPTIONS." '$video_file' > ffmpeg.log 2>&1"; Debug( $command."\n" ); my $output = qx($command); my $status = $? >> 8; if ( $status ) { Error( "Unable to generate video, check ".$event_path."/ffmpeg.log for details" ); exit( -1 ); } Info( "Finished $video_file\n" ); } else { Info( "Video file $video_file already exists for event $event->{Id}\n" ); } #print( STDOUT $event->{MonitorId}.'/'.$event->{Id}.'/'.$video_file."\n" ); print( STDOUT $video_file."\n" ); exit( 0 ); ZoneMinder-1.26.5/scripts/zmwatch.pl.in000066400000000000000000000123741225361755400200060ustar00rootroot00000000000000#!/usr/bin/perl -wT # # ========================================================================== # # ZoneMinder WatchDog Script, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This does some basic setup for ZoneMinder to run and then periodically # checks the fps output of the active daemons to check they haven't # locked up. If they have then they are killed and restarted # use strict; use bytes; # ========================================================================== # # These are the elements you can edit to suit your installation # # ========================================================================== use constant START_DELAY => 30; # To give everything else time to start # ========================================================================== # # Don't change anything below here # # ========================================================================== @EXTRA_PERL_LIB@ use ZoneMinder; use POSIX; use DBI; use Data::Dumper; $| = 1; $ENV{PATH} = '/bin:/usr/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; sub Usage { print( " Usage: zmwatch.pl "); exit( -1 ); } logInit(); logSetSignal(); Info( "Watchdog starting\n" ); Info( "Watchdog pausing for ".START_DELAY." seconds\n" ); sleep( START_DELAY ); my $dbh = zmDbConnect(); my $sql = "select * from Monitors"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); while( 1 ) { my $now = time(); my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); while( my $monitor = $sth->fetchrow_hashref() ) { if ( $monitor->{Function} ne 'None' ) { my $restart = 0; if ( zmMemVerify( $monitor ) && zmMemRead( $monitor, "shared_data:valid" ) ) { # Check we have got an image recently my $image_time = zmGetLastWriteTime( $monitor ); next if ( !defined($image_time) ); # Can't read from shared data next if ( !$image_time ); # We can't get the last capture time so can't be sure it's died. my $max_image_delay = ($monitor->{MaxFPS}&&($monitor->{MaxFPS}>0)&&($monitor->{MaxFPS}<1))?(3/$monitor->{MaxFPS}):ZM_WATCH_MAX_DELAY; my $image_delay = $now-$image_time; Debug( "Monitor $monitor->{Id} last captured $image_delay seconds ago, max is $max_image_delay\n" ); if ( $image_delay > $max_image_delay ) { Info( "Restarting capture daemon for ".$monitor->{Name}.", time since last capture $image_delay seconds ($now-$image_time)\n" ); $restart = 1; } } else { #Info( "Restarting capture daemon for ".$monitor->{Name}.", shared data not valid\n" ); #$restart = 1; } if ( $restart ) { my $command; if ( $monitor->{Type} eq 'Local' ) { $command = "zmdc.pl restart zmc -d $monitor->{Device}"; } else { $command = "zmdc.pl restart zmc -m $monitor->{Id}"; } runCommand( $command ); } elsif ( $monitor->{Function} ne 'Monitor' ) { if ( zmMemVerify( $monitor ) && zmMemRead( $monitor, "shared_data:valid" ) ) { # Check we have got an image recently my $image_time = zmGetLastReadTime( $monitor ); next if ( !defined($image_time) ); # Can't read from shared data next if ( !$image_time ); # We can't get the last capture time so can't be sure it's died. my $max_image_delay = ($monitor->{MaxFPS}&&($monitor->{MaxFPS}>0)&&($monitor->{MaxFPS}<1))?(3/$monitor->{MaxFPS}):ZM_WATCH_MAX_DELAY; my $image_delay = $now-$image_time; Debug( "Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay\n" ); if ( $image_delay > $max_image_delay ) { Info( "Restarting analysis daemon for ".$monitor->{Name}.", time since last analysis $image_delay seconds ($now-$image_time)\n" ); my $command = "zmdc.pl restart zma -m ".$monitor->{Id}; runCommand( $command ); } } } } # Prevent open handles building up if we have connect to shared memory zmMemInvalidate( $monitor ); } sleep( ZM_WATCH_CHECK_INTERVAL ); } Info( "Watchdog exiting\n" ); exit(); ZoneMinder-1.26.5/scripts/zmx10.pl.in000066400000000000000000000415201225361755400173030ustar00rootroot00000000000000#!/usr/bin/perl -wT # # ========================================================================== # # ZoneMinder X10 Control Script, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This script controls the monitoring of the X10 interface and the consequent # management of the ZM daemons based on the receipt of X10 signals. # use strict; use bytes; # ========================================================================== # # These are the elements you can edit to suit your installation # # ========================================================================== use constant CAUSE_STRING => "X10"; # What gets written as the cause of any events # ========================================================================== # # Don't change anything below here # # ========================================================================== @EXTRA_PERL_LIB@ use ZoneMinder; use POSIX; use Socket; use Getopt::Long; use Data::Dumper; use constant SOCK_FILE => ZM_PATH_SOCKS.'/zmx10.sock'; $| = 1; $ENV{PATH} = '/bin:/usr/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; sub Usage { print( " Usage: zmx10.pl -c ,--command= [-u ,--unit-code=] Parameters are :- -c , --command= - Command to issue, one of 'on','off','dim','bright','status','shutdown' -u , --unit-code= - Unit code to act on required for all commands except 'status' (optional) and 'shutdown' "); exit( -1 ); } logInit(); logSetSignal(); my $command; my $unit_code; if ( !GetOptions( 'command=s'=>\$command, 'unit-code=i'=>\$unit_code ) ) { Usage(); } die( "No command given" ) unless( $command ); die( "No unit code given" ) unless( $unit_code || ($command =~ /(?:start|status|shutdown)/) ); if ( $command eq "start" ) { X10Server::runServer(); exit(); } socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); my $saddr = sockaddr_un( SOCK_FILE ); if ( !connect( CLIENT, $saddr ) ) { # The server isn't there print( "Unable to connect, starting server\n" ); close( CLIENT ); if ( my $cpid = fork() ) { # Parent process just sleep and fall through sleep( 2 ); logReinit(); socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); connect( CLIENT, $saddr ) or Fatal( "Can't connect: $!" ); } elsif ( defined($cpid) ) { setpgrp(); logReinit(); X10Server::runServer(); } else { Fatal( "Can't fork: $!" ); } } # The server is there, connect to it #print( "Writing commands\n" ); CLIENT->autoflush(); my $message = "$command"; $message .= ";$unit_code" if ( $unit_code ); print( CLIENT $message ); shutdown( CLIENT, 1 ); while ( my $line = ) { chomp( $line ); print( "$line\n" ); } close( CLIENT ); #print( "Finished writing, bye\n" ); exit; # # ========================================================================== # # This is the X10 Server package # # ========================================================================== # package X10Server; use strict; use bytes; use ZoneMinder; use POSIX; use DBI; use Socket; use X10::ActiveHome; use Data::Dumper; our $dbh; our $x10; our %monitor_hash; our %device_hash; our %pending_tasks; sub runServer { Info( "X10 server starting\n" ); socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); unlink( main::SOCK_FILE ); my $saddr = sockaddr_un( main::SOCK_FILE ); bind( SERVER, $saddr ) or Fatal( "Can't bind: $!" ); listen( SERVER, SOMAXCONN ) or Fatal( "Can't listen: $!" ); $dbh = zmDbConnect(); $x10 = new X10::ActiveHome( port=>ZM_X10_DEVICE, house_code=>ZM_X10_HOUSE_CODE, debug=>0 ); loadTasks(); $x10->register_listener( \&x10listen ); my $rin = ''; vec( $rin, fileno(SERVER),1) = 1; vec( $rin, $x10->select_fds(),1) = 1; my $timeout = 0.2; #print( "F:".fileno(SERVER)."\n" ); my $reload = undef; my $reload_count = 0; my $reload_limit = &ZM_X10_DB_RELOAD_INTERVAL / $timeout; while( 1 ) { my $nfound = select( my $rout = $rin, undef, undef, $timeout ); #print( "Off select, NF:$nfound, ER:$!\n" ); #print( vec( $rout, fileno(SERVER),1)."\n" ); #print( vec( $rout, $x10->select_fds(),1)."\n" ); if ( $nfound > 0 ) { if ( vec( $rout, fileno(SERVER),1) ) { my $paddr = accept( CLIENT, SERVER ); my $message = ; my ( $command, $unit_code ) = split( /;/, $message ); my $device; if ( defined($unit_code) ) { if ( $unit_code < 1 || $unit_code > 16 ) { dPrint( ZoneMinder::Logger::ERROR, "Invalid unit code '$unit_code'\n" ); next; } $device = $device_hash{$unit_code}; if ( !$device ) { $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), status=>'unknown' }; } } my $result; if ( $command eq 'on' ) { $result = $device->{appliance}->on(); } elsif ( $command eq 'off' ) { $result = $device->{appliance}->off(); } #elsif ( $command eq 'dim' ) #{ #$result = $device->{appliance}->dim(); #} #elsif ( $command eq 'bright' ) #{ #$result = $device->{appliance}->bright(); #} elsif ( $command eq 'status' ) { if ( $device ) { dPrint( ZoneMinder::Logger::DEBUG, $unit_code." ".$device->{status}."\n" ); } else { foreach my $unit_code ( sort( keys(%device_hash) ) ) { my $device = $device_hash{$unit_code}; dPrint( ZoneMinder::Logger::DEBUG, $unit_code." ".$device->{status}."\n" ); } } } elsif ( $command eq 'shutdown' ) { last; } else { dPrint( ZoneMinder::Logger::ERROR, "Invalid command '$command'\n" ); } if ( defined($result) ) { if ( 1 || $result ) { $device->{status} = uc($command); dPrint( ZoneMinder::Logger::DEBUG, $device->{appliance}->address()." $command, ok\n" ); #x10listen( new X10::Event( sprintf("%s %s", $device->{appliance}->address, uc($command) ) ) ); } else { dPrint( ZoneMinder::Logger::ERROR, $device->{appliance}->address()." $command, failed\n" ); } } close( CLIENT ); } elsif ( vec( $rout, $x10->select_fds(),1) ) { $x10->handle_input(); } else { Fatal( "Bogus descriptor" ); } } elsif ( $nfound < 0 ) { if ( $! != EINTR ) { Fatal( "Can't select: $!" ); } } else { #print( "Select timed out\n" ); # Check for state changes foreach my $monitor_id ( sort(keys(%monitor_hash) ) ) { my $monitor = $monitor_hash{$monitor_id}; my $state = zmGetMonitorState( $monitor ); if ( !defined($state) ) { $reload = !undef; next; } if ( defined( $monitor->{LastState} ) ) { my $task_list; if ( ($state == STATE_ALARM || $state == STATE_ALERT) && ($monitor->{LastState} == STATE_IDLE || $monitor->{LastState} == STATE_TAPE) ) # Gone into alarm state { Debug( "Applying ON_list for $monitor_id\n" ); $task_list = $monitor->{"ON_list"}; } elsif ( ($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE) || ($state == STATE_TAPE && $monitor->{LastState} != STATE_TAPE) ) # Come out of alarm state { Debug( "Applying OFF_list for $monitor_id\n" ); $task_list = $monitor->{"OFF_list"}; } if ( $task_list ) { foreach my $task ( @$task_list ) { processTask( $task ); } } } $monitor->{LastState} = $state; } # Check for pending tasks my $now = time(); foreach my $activation_time ( sort(keys(%pending_tasks) ) ) { last if ( $activation_time > $now ); my $pending_list = $pending_tasks{$activation_time}; foreach my $task ( @$pending_list ) { processTask( $task ); } delete( $pending_tasks{$activation_time} ); } if ( $reload || ++$reload_count >= $reload_limit ) { loadTasks(); $reload = undef; $reload_count = 0; } } } Info( "X10 server exiting\n" ); close( SERVER ); exit(); } sub addToDeviceList { my $unit_code = shift; my $event = shift; my $monitor = shift; my $function = shift; my $limit = shift; Debug( "Adding to device list, uc:$unit_code, ev:$event, mo:".$monitor->{Id}.", fu:$function, li:$limit\n" ); my $device = $device_hash{$unit_code}; if ( !$device ) { $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), status=>'unknown' }; } my $task = { type=>"device", monitor=>$monitor, address=>$device->{appliance}->address(), function=>$function }; if ( $limit ) { $task->{limit} = $limit } my $task_list = $device->{$event."_list"}; if ( !$task_list ) { $task_list = $device->{$event."_list"} = []; } push( @$task_list, $task ); } sub addToMonitorList { my $monitor = shift; my $event = shift; my $unit_code = shift; my $function = shift; my $limit = shift; Debug( "Adding to monitor list, uc:$unit_code, ev:$event, mo:".$monitor->{Id}.", fu:$function, li:$limit\n" ); my $device = $device_hash{$unit_code}; if ( !$device ) { $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), status=>'unknown' }; } my $task = { type=>"monitor", device=>$device, id=>$monitor->{Id}, function=>$function }; if ( $limit ) { $task->{limit} = $limit; } my $task_list = $monitor->{$event."_list"}; if ( !$task_list ) { $task_list = $monitor->{$event."_list"} = []; } push( @$task_list, $task ); } sub loadTasks { %monitor_hash = (); Debug( "Loading tasks\n" ); # Clear out all old device task lists foreach my $unit_code ( sort( keys(%device_hash) ) ) { my $device = $device_hash{$unit_code}; $device->{ON_list} = []; $device->{OFF_list} = []; } my $sql = "select M.*,T.* from Monitors as M inner join TriggersX10 as T on (M.Id = T.MonitorId) where find_in_set( M.Function, 'Modect,Record,Mocord,Nodect' ) and M.Enabled = 1 and find_in_set( 'X10', M.Triggers )"; my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); while( my $monitor = $sth->fetchrow_hashref() ) { next if ( !zmMemVerify( $monitor ) ); # Check shared memory ok $monitor_hash{$monitor->{Id}} = $monitor; if ( $monitor->{Activation} ) { Debug( "$monitor->{Name} has active string '$monitor->{Activation}'\n" ); foreach my $code_string ( split( /,/, $monitor->{Activation} ) ) { #Debug( "Code string: $code_string\n" ); my ( $invert, $unit_code, $modifier, $limit ) = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); $limit = 0 if ( !$limit ); if ( $unit_code ) { if ( !$modifier || $modifier eq '+' ) { addToDeviceList( $unit_code, "ON", $monitor, !$invert?"start_active":"stop_active", $limit ); } if ( !$modifier || $modifier eq '-' ) { addToDeviceList( $unit_code, "OFF", $monitor, !$invert?"stop_active":"start_active", $limit ); } } } } if ( $monitor->{AlarmInput} ) { Debug( "$monitor->{Name} has alarm input string '$monitor->{AlarmInput}'\n" ); foreach my $code_string ( split( /,/, $monitor->{AlarmInput} ) ) { #Debug( "Code string: $code_string\n" ); my ( $invert, $unit_code, $modifier, $limit ) = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); $limit = 0 if ( !$limit ); if ( $unit_code ) { if ( !$modifier || $modifier eq '+' ) { addToDeviceList( $unit_code, "ON", $monitor, !$invert?"start_alarm":"stop_alarm", $limit ); } if ( !$modifier || $modifier eq '-' ) { addToDeviceList( $unit_code, "OFF", $monitor, !$invert?"stop_alarm":"start_alarm", $limit ); } } } } if ( $monitor->{AlarmOutput} ) { Debug( "$monitor->{Name} has alarm output string '$monitor->{AlarmOutput}'\n" ); foreach my $code_string ( split( /,/, $monitor->{AlarmOutput} ) ) { #Debug( "Code string: $code_string\n" ); my ( $invert, $unit_code, $modifier, $limit ) = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); $limit = 0 if ( !$limit ); if ( $unit_code ) { if ( !$modifier || $modifier eq '+' ) { addToMonitorList( $monitor, "ON", $unit_code, !$invert?"on":"off", $limit ); } if ( !$modifier || $modifier eq '-' ) { addToMonitorList( $monitor, "OFF", $unit_code, !$invert?"off":"on", $limit ); } } } } zmMemInvalidate( $monitor ); } } sub addPendingTask { my $task = shift; # Check whether we are just extending a previous pending task # and remove it if it's there foreach my $activation_time ( sort(keys(%pending_tasks) ) ) { my $pending_list = $pending_tasks{$activation_time}; my $new_pending_list = []; foreach my $pending_task ( @$pending_list ) { if ( $task->{type} ne $pending_task->{type} ) { push( @$new_pending_list, $pending_task ) } elsif ( $task->{type} eq "device" ) { if (( $task->{monitor}->{Id} != $pending_task->{monitor}->{Id} ) || ( $task->{function} ne $pending_task->{function} )) { push( @$new_pending_list, $pending_task ) } } elsif ( $task->{type} eq "monitor" ) { if (( $task->{device}->{appliance}->unit_code() != $pending_task->{device}->{appliance}->unit_code() ) || ( $task->{function} ne $pending_task->{function} )) { push( @$new_pending_list, $pending_task ) } } } if ( @$new_pending_list ) { $pending_tasks{$activation_time} = $new_pending_list; } else { delete( $pending_tasks{$activation_time} ); } } my $end_time = time() + $task->{limit}; my $pending_list = $pending_tasks{$end_time}; if ( !$pending_list ) { $pending_list = $pending_tasks{$end_time} = []; } my $pending_task; if ( $task->{type} eq "device" ) { $pending_task = { type=>$task->{type}, monitor=>$task->{monitor}, function=>$task->{function} }; $pending_task->{function} =~ s/start/stop/; } elsif ( $task->{type} eq "monitor" ) { $pending_task = { type=>$task->{type}, device=>$task->{device}, function=>$task->{function} }; $pending_task->{function} =~ s/on/off/; } push( @$pending_list, $pending_task ); } sub processTask { my $task = shift; if ( $task->{type} eq "device" ) { my ( $instruction, $class ) = ( $task->{function} =~ /^(.+)_(.+)$/ ); if ( $class eq "active" ) { if ( $instruction eq "start" ) { zmMonitorEnable( $task->{monitor} ); if ( $task->{limit} ) { addPendingTask( $task ); } } elsif( $instruction eq "stop" ) { zmMonitorDisable( $task->{monitor} ); } } elsif( $class eq "alarm" ) { if ( $instruction eq "start" ) { zmTriggerEventOn( $task->{monitor}, 0, main::CAUSE_STRING, $task->{address} ); if ( $task->{limit} ) { addPendingTask( $task ); } } elsif( $instruction eq "stop" ) { zmTriggerEventCancel( $task->{monitor} ); } } } elsif( $task->{type} eq "monitor" ) { if ( $task->{function} eq "on" ) { $task->{device}->{appliance}->on(); if ( $task->{limit} ) { addPendingTask( $task ); } } elsif ( $task->{function} eq "off" ) { $task->{device}->{appliance}->off(); } } } sub dPrint { my $dbg_level = shift; if ( fileno(CLIENT) ) { print CLIENT @_ } if ( $dbg_level == ZoneMinder::Logger::DEBUG ) { Debug( @_ ); } elsif ( $dbg_level == ZoneMinder::Logger::INFO ) { Info( @_ ); } elsif ( $dbg_level == ZoneMinder::Logger::WARNING ) { Warning( @_ ); } elsif ( $dbg_level == ZoneMinder::Logger::ERROR ) { Error( @_ ); } elsif ( $dbg_level == ZoneMinder::Logger::FATAL ) { Fatal( @_ ); } } sub x10listen { foreach my $event ( @_ ) { #print( Data::Dumper( $_ )."\n" ); if ( $event->house_code() eq ZM_X10_HOUSE_CODE ) { my $unit_code = $event->unit_code(); my $device = $device_hash{$unit_code}; if ( !$device ) { $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), status=>'unknown' }; } next if ( $event->func() !~ /(?:ON|OFF)/ ); $device->{status} = $event->func(); my $task_list = $device->{$event->func()."_list"}; if ( $task_list ) { foreach my $task ( @$task_list ) { processTask( $task ); } } } Info( "Got event - ".$event->as_string()."\n" ); } } 1; ZoneMinder-1.26.5/src/000077500000000000000000000000001225361755400144605ustar00rootroot00000000000000ZoneMinder-1.26.5/src/CMakeLists.txt000066400000000000000000000041271225361755400172240ustar00rootroot00000000000000# CMakeLists.txt for the ZoneMinder binaries # Create files from the .in files configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY) # Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc) set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_local_camera.cpp zm_monitor.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_zone.cpp) # A fix for cmake recompiling the source files for every target. add_library(zm STATIC ${ZM_BIN_SRC_FILES}) add_executable(zmc zmc.cpp) add_executable(zma zma.cpp) add_executable(zmu zmu.cpp) add_executable(zmf zmf.cpp) add_executable(zms zms.cpp) add_executable(nph-zms zms.cpp) add_executable(zmstreamer zmstreamer.cpp) add_executable(zmfix zmfix.cpp zm_config.cpp zm_regexp.cpp zm_logger.cpp zm_utils.cpp zm_db.cpp zm.cpp) target_link_libraries(zmc zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) target_link_libraries(zma zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) target_link_libraries(zmu zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) target_link_libraries(zmf zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) target_link_libraries(zms zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) target_link_libraries(nph-zms zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) target_link_libraries(zmstreamer zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) target_link_libraries(zmfix ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) install(TARGETS zmc zma zmu zmf zmstreamer zmfix RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(TARGETS zms nph-zms RUNTIME DESTINATION "${ZM_CGIDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) ZoneMinder-1.26.5/src/Makefile.am000066400000000000000000000047621225361755400165250ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu AM_CPPFLAGS = @MYSQL_CFLAGS@ @FFMPEG_CFLAGS@ -Wall -finline-functions -fomit-frame-pointer #AM_CXXFLAGS = -frepo CLEANFILES = *.rpo # This should be set to your CGI directory cgidir = @CGI_PREFIX@ # And these to the user and group of your webserver webuser = @WEB_USER@ webgroup = @WEB_GROUP@ bin_PROGRAMS = \ zmc \ zma \ zmu \ zms \ zmf \ zmstreamer \ zmfix zm_SOURCES = \ zm_box.cpp \ zm_buffer.cpp \ zm_camera.cpp \ zm_comms.cpp \ zm_config.cpp \ zm_coord.cpp \ zm.cpp \ zm_db.cpp \ zm_logger.cpp \ zm_event.cpp \ zm_exception.cpp \ zm_file_camera.cpp \ zm_ffmpeg_camera.cpp \ zm_image.cpp \ zm_jpeg.cpp \ zm_local_camera.cpp \ zm_monitor.cpp \ zm_ffmpeg.cpp \ zm_mpeg.cpp \ zm_poly.cpp \ zm_regexp.cpp \ zm_remote_camera.cpp \ zm_remote_camera_http.cpp \ zm_remote_camera_rtsp.cpp \ zm_rtp.cpp \ zm_rtp_ctrl.cpp \ zm_rtp_data.cpp \ zm_rtp_source.cpp \ zm_rtsp.cpp \ zm_sdp.cpp \ zm_signal.cpp \ zm_stream.cpp \ zm_thread.cpp \ zm_time.cpp \ zm_timer.cpp \ zm_user.cpp \ zm_utils.cpp \ zm_zone.cpp zmc_SOURCES = zmc.cpp $(zm_SOURCES) zma_SOURCES = zma.cpp $(zm_SOURCES) zms_SOURCES = zms.cpp $(zm_SOURCES) zmu_SOURCES = zmu.cpp $(zm_SOURCES) zmf_SOURCES = zmf.cpp $(zm_SOURCES) zmstreamer_SOURCES = zmstreamer.cpp $(zm_SOURCES) zmfix_SOURCES = zmfix.cpp zm_config.cpp zm_regexp.cpp zm_logger.cpp zm_utils.cpp zm_db.cpp zm.cpp noinst_HEADERS = \ jinclude.h \ zm_box.h \ zm_buffer.h \ zm_camera.h \ zm_comms.h \ zm_config_defines.h \ zm_config.h \ zm_coord.h \ zm_db.h \ zm_logger.h \ zm_event.h \ zm_exception.h \ zmf.h \ zm_file_camera.h \ zm_ffmpeg_camera.h \ zm_font.h \ zm_font.h \ zm.h \ zm_image.h \ zm_jpeg.h \ zm_local_camera.h \ zm_mem_utils.h \ zm_monitor.h \ zm_ffmpeg.h \ zm_mpeg.h \ zm_poly.h \ zm_regexp.h \ zm_remote_camera.h \ zm_remote_camera_http.h \ zm_remote_camera_rtsp.h \ zm_rgb.h \ zm_rtp_ctrl.h \ zm_rtp_data.h \ zm_rtp.h \ zm_rtp_source.h \ zm_rtsp.h \ zm_sdp.h \ zm_signal.h \ zm_stream.h \ zm_thread.h \ zm_time.h \ zm_timer.h \ zm_user.h \ zm_utils.h \ zm_zone.h EXTRA_DIST = \ zm_config.h.in \ zm_threaddata.cpp dist-hook: @( rm $(distdir)/zm_config.h ) # Yes, you are correct. This is a HACK! install-exec-hook: ( cd $(DESTDIR)@bindir@; mkdir -p $(DESTDIR)$(cgidir); mv zms $(DESTDIR)$(cgidir) ) ( cd $(DESTDIR)$(cgidir); chown $(webuser):$(webgroup) zms; ln -f zms nph-zms ) ( cd $(DESTDIR)@bindir@; chmod u+s zmfix ) uninstall-hook: ( cd $(DESTDIR)$(cgidir); rm -f zms nph-zms ) ZoneMinder-1.26.5/src/jinclude.h000066400000000000000000000062601225361755400164320ustar00rootroot00000000000000/* * jinclude.h * * Copyright (C) 2001-2008 Philip Coombes * This file is part of the Independent JPEG Group's software. * For conditions of distribution and use, see the accompanying README file. * * This file exists to provide a single place to fix any problems with * including the wrong system include files. (Common problems are taken * care of by the standard jconfig symbols, but on really weird systems * you may have to edit this file.) * * NOTE: this file is NOT intended to be included by applications using the * JPEG library. Most applications need only include jpeglib.h. */ /* Include auto-config file to find out which system include files we need. */ #include "jconfig.h" /* auto configuration options */ #define JCONFIG_INCLUDED /* so that jpeglib.h doesn't do it again */ /* * We need the NULL macro and size_t typedef. * On an ANSI-conforming system it is sufficient to include . * Otherwise, we get them from or ; we may have to * pull in as well. * Note that the core JPEG library does not require ; * only the default error handler and data source/destination modules do. * But we must pull it in because of the references to FILE in jpeglib.h. * You can remove those references if you want to compile without . */ #ifdef HAVE_STDDEF_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef NEED_SYS_TYPES_H #include #endif #include /* * We need memory copying and zeroing functions, plus strncpy(). * ANSI and System V implementations declare these in . * BSD doesn't have the mem() functions, but it does have bcopy()/bzero(). * Some systems may declare memset and memcpy in . * * NOTE: we assume the size parameters to these functions are of type size_t. * Change the casts in these macros if not! */ #ifdef NEED_BSD_STRINGS #include #define MEMZERO(target,size) bzero((void *)(target), (size_t)(size)) #define MEMCOPY(dest,src,size) bcopy((const void *)(src), (void *)(dest), (size_t)(size)) #else /* not BSD, assume ANSI/SysV string lib */ #include #define MEMZERO(target,size) memset((void *)(target), 0, (size_t)(size)) #define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (size_t)(size)) #endif /* * In ANSI C, and indeed any rational implementation, size_t is also the * type returned by sizeof(). However, it seems there are some irrational * implementations out there, in which sizeof() returns an int even though * size_t is defined as long or unsigned long. To ensure consistent results * we always use this SIZEOF() macro in place of using sizeof() directly. */ #define SIZEOF(object) ((size_t) sizeof(object)) /* * The modules that use fread() and fwrite() always invoke them through * these macros. On some systems you may need to twiddle the argument casts. * CAUTION: argument order is different from underlying functions! */ #define JFREAD(file,buf,sizeofbuf) \ ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) #define JFWRITE(file,buf,sizeofbuf) \ ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) ZoneMinder-1.26.5/src/zm.cpp000066400000000000000000000016241225361755400156150ustar00rootroot00000000000000// // ZoneMinder Core Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" /* This is our argv[0], we need it for backtrace */ const char* self = 0; ZoneMinder-1.26.5/src/zm.h000066400000000000000000000016421225361755400152620ustar00rootroot00000000000000// // ZoneMinder Core Interfaces, $Date$, $Revision$ // $Copyright$ // // 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. // #ifndef ZM_H #define ZM_H #include "zm_config.h" #include "zm_logger.h" #include extern const char* self; #endif // ZM_H ZoneMinder-1.26.5/src/zm_box.cpp000066400000000000000000000016131225361755400164630ustar00rootroot00000000000000// // ZoneMinder Box Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" #include "zm_box.h" // This section deliberately left blank ZoneMinder-1.26.5/src/zm_box.h000066400000000000000000000045531225361755400161360ustar00rootroot00000000000000// // ZoneMinder Box Class Interfaces, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_BOX_H #define ZM_BOX_H #include "zm.h" #include "zm_coord.h" #include // // Class used for storing a box, which is defined as a region // defined by two coordinates // class Box { private: Coord lo, hi; Coord size; public: inline Box() { } inline Box( int p_size ) : lo( 0, 0 ), hi ( p_size-1, p_size-1 ), size( Coord::Range( hi, lo ) ) { } inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { } inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { } inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { } inline const Coord &Lo() const { return( lo ); } inline int LoX() const { return( lo.X() ); } inline int LoY() const { return( lo.Y() ); } inline const Coord &Hi() const { return( hi ); } inline int HiX() const { return( hi.X() ); } inline int HiY() const { return( hi.Y() ); } inline const Coord &Size() const { return( size ); } inline int Width() const { return( size.X() ); } inline int Height() const { return( size.Y() ); } inline int Area() const { return( size.X()*size.Y() ); } inline const Coord Centre() const { int mid_x = int(round(lo.X()+(size.X()/2.0))); int mid_y = int(round(lo.Y()+(size.Y()/2.0))); return( Coord( mid_x, mid_y ) ); } inline bool Inside( const Coord &coord ) const { return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() ); } }; #endif // ZM_BOX_H ZoneMinder-1.26.5/src/zm_buffer.cpp000066400000000000000000000037611225361755400171520ustar00rootroot00000000000000/* * ZoneMinder flexible memory class implementation, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "zm.h" #include "zm_buffer.h" unsigned int Buffer::assign( const unsigned char *pStorage, unsigned int pSize ) { if ( mAllocation < pSize ) { delete[] mStorage; mAllocation = pSize; mHead = mStorage = new unsigned char[pSize]; } mSize = pSize; memcpy( mStorage, pStorage, mSize ); mHead = mStorage; mTail = mHead + mSize; return( mSize ); } unsigned int Buffer::expand( unsigned int count ) { int spare = mAllocation - mSize; int headSpace = mHead - mStorage; int tailSpace = spare - headSpace; int width = mTail - mHead; if ( spare > (int)count ) { if ( tailSpace < (int)count ) { memmove( mStorage, mHead, mSize ); mHead = mStorage; mTail = mHead + width; } } else { mAllocation += count; unsigned char *newStorage = new unsigned char[mAllocation]; if ( mStorage ) { memcpy( newStorage, mHead, mSize ); delete[] mStorage; } mStorage = newStorage; mHead = mStorage; mTail = mHead + width; } return( mSize ); } ZoneMinder-1.26.5/src/zm_buffer.h000066400000000000000000000135151225361755400166150ustar00rootroot00000000000000/* * ZoneMinder Flexible Memory Interface, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more demTails. * * 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. */ #ifndef ZM_BUFFER_H #define ZM_BUFFER_H #include "zm.h" #include class Buffer { protected: unsigned char *mStorage; unsigned int mAllocation; unsigned int mSize; unsigned char *mHead; unsigned char *mTail; public: Buffer() : mStorage( 0 ), mAllocation( 0 ), mSize( 0 ), mHead( 0 ), mTail( 0 ) { } Buffer( unsigned int pSize ) : mAllocation( pSize ), mSize( 0 ) { mHead = mStorage = new unsigned char[mAllocation]; mTail = mHead; } Buffer( const unsigned char *pStorage, unsigned int pSize ) : mAllocation( pSize ), mSize( pSize ) { mHead = mStorage = new unsigned char[mSize]; memcpy( mStorage, pStorage, mSize ); mTail = mHead + mSize; } Buffer( const Buffer &buffer ) : mAllocation( buffer.mSize ), mSize( buffer.mSize ) { mHead = mStorage = new unsigned char[mSize]; memcpy( mStorage, buffer.mHead, mSize ); mTail = mHead + mSize; } ~Buffer() { delete[] mStorage; } unsigned char *head() const { return( mHead ); } unsigned char *tail() const { return( mTail ); } unsigned int size() const { return( mSize ); } bool empty() const { return( mSize == 0 ); } unsigned int size( unsigned int pSize ) { if ( mSize < pSize ) { expand( pSize-mSize ); } return( mSize ); } //unsigned int Allocation() const { return( mAllocation ); } void clear() { mSize = 0; mHead = mTail = mStorage; } unsigned int assign( const unsigned char *pStorage, unsigned int pSize ); unsigned int assign( const Buffer &buffer ) { return( assign( buffer.mHead, buffer.mSize ) ); } // Trim from the front of the buffer unsigned int consume( unsigned int count ) { if ( count > mSize ) { Warning( "Attempt to consume %d bytes of buffer, size is only %d bytes", count, mSize ); count = mSize; } mHead += count; mSize -= count; tidy( 0 ); return( count ); } // Trim from the end of the buffer unsigned int shrink( unsigned int count ) { if ( count > mSize ) { Warning( "Attempt to shrink buffer by %d bytes, size is only %d bytes", count, mSize ); count = mSize; } mSize -= count; if ( mTail > (mHead + mSize) ) mTail = mHead + mSize; tidy( 0 ); return( count ); } // Add to the end of the buffer unsigned int expand( unsigned int count ); // Return pointer to the first pSize bytes and advance the head unsigned char *extract( unsigned int pSize ) { if ( pSize > mSize ) { Warning( "Attempt to extract %d bytes of buffer, size is only %d bytes", pSize, mSize ); pSize = mSize; } unsigned char *oldHead = mHead; mHead += pSize; mSize -= pSize; tidy( 0 ); return( oldHead ); } // Add bytes to the end of the buffer unsigned int append( const unsigned char *pStorage, unsigned int pSize ) { expand( pSize ); memcpy( mTail, pStorage, pSize ); mTail += pSize; mSize += pSize; return( mSize ); } unsigned int append( const char *pStorage, unsigned int pSize ) { return( append( (const unsigned char *)pStorage, pSize ) ); } unsigned int append( const Buffer &buffer ) { return( append( buffer.mHead, buffer.mSize ) ); } void tidy( bool level=0 ) { if ( mHead != mStorage ) { if ( mSize == 0 ) mHead = mTail = mStorage; else if ( level >= 1 ) { if ( (mHead-mStorage) > mSize ) { memcpy( mStorage, mHead, mSize ); mHead = mStorage; mTail = mHead + mSize; } else if ( level >= 2 ) { memmove( mStorage, mHead, mSize ); mHead = mStorage; mTail = mHead + mSize; } } } } Buffer &operator=( const Buffer &buffer ) { assign( buffer ); return( *this ); } Buffer &operator+=( const Buffer &buffer ) { append( buffer ); return( *this ); } Buffer &operator+=( unsigned int count ) { expand( count ); return( *this ); } Buffer &operator-=( unsigned int count ) { consume( count ); return( *this ); } operator unsigned char *() const { return( mHead ); } operator char *() const { return( (char *)mHead ); } unsigned char *operator+(int offset) const { return( (unsigned char *)(mHead+offset) ); } unsigned char operator[](int index) const { return( *(mHead+index) ); } operator int () const { return( (int)mSize ); } }; #endif // ZM_BUFFER_H ZoneMinder-1.26.5/src/zm_camera.cpp000066400000000000000000000036541225361755400171320ustar00rootroot00000000000000// // ZoneMinder Camera Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" #include "zm_camera.h" Camera::Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : id( p_id ), type( p_type ), width( p_width), height( p_height ), colours( p_colours ), subpixelorder( p_subpixelorder ), brightness( p_brightness ), hue( p_hue ), colour( p_colour ), contrast( p_contrast ), capture( p_capture ) { pixels = width * height; imagesize = pixels * colours; Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",id,width,height,colours,subpixelorder,capture); /* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */ if((colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB32) && (imagesize % 16) != 0) { Fatal("Image size is not multiples of 16"); } else if(colours == ZM_COLOUR_RGB24 && ((imagesize % 16) != 0 || (imagesize % 12) != 0)) { Fatal("Image size is not multiples of 12 and 16"); } } Camera::~Camera() { } ZoneMinder-1.26.5/src/zm_camera.h000066400000000000000000000052541225361755400165750ustar00rootroot00000000000000// // ZoneMinder Camera Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_CAMERA_H #define ZM_CAMERA_H #include #include #include "zm_image.h" // // Abstract base class for cameras. This is intended just to express // common attributes // class Camera { protected: typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC } SourceType; int id; SourceType type; unsigned int width; unsigned int height; unsigned int colours; unsigned int subpixelorder; unsigned int pixels; unsigned int imagesize; int brightness; int hue; int colour; int contrast; bool capture; public: Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); virtual ~Camera(); int getId() const { return( id ); } SourceType Type() const { return( type ); } bool IsLocal() const { return( type == LOCAL_SRC ); } bool IsRemote() const { return( type == REMOTE_SRC ); } bool IsFile() const { return( type == FILE_SRC ); } bool IsFfmpeg() const { return( type == FFMPEG_SRC ); } unsigned int Width() const { return( width ); } unsigned int Height() const { return( height ); } unsigned int Colours() const { return( colours ); } unsigned int SubpixelOrder() const { return( subpixelorder ); } unsigned int Pixels() const { return( pixels ); } unsigned int ImageSize() const { return( imagesize ); } virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); } virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); } virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); } virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); } bool CanCapture() const { return( capture ); } virtual int PrimeCapture() { return( 0 ); } virtual int PreCapture()=0; virtual int Capture( Image &image )=0; virtual int PostCapture()=0; }; #endif // ZM_CAMERA_H ZoneMinder-1.26.5/src/zm_comms.cpp000066400000000000000000000377671225361755400170340ustar00rootroot00000000000000// // ZoneMinder Communications Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm_comms.h" #include "zm_logger.h" #include #include #include //#include #include #include //#include #include #include //#include //#include #include int CommsBase::readV( int iovcnt, /* const void *, int, */ ... ) { va_list arg_ptr; //struct iovec iov[iovcnt]; struct iovec *iov = (struct iovec *)alloca( sizeof(struct iovec)*iovcnt ); va_start( arg_ptr, iovcnt ); for ( int i = 0; i < iovcnt; i++ ) { iov[i].iov_base = va_arg( arg_ptr, void * ); iov[i].iov_len = va_arg( arg_ptr, int ); } va_end( arg_ptr ); int nBytes = ::readv( mRd, iov, iovcnt ); if ( nBytes < 0 ) Debug( 1, "Readv of %d buffers max on rd %d failed: %s", iovcnt, mRd, strerror(errno) ); return( nBytes ); } int CommsBase::writeV( int iovcnt, /* const void *, int, */ ... ) { va_list arg_ptr; //struct iovec iov[iovcnt]; struct iovec *iov = (struct iovec *)alloca( sizeof(struct iovec)*iovcnt ); va_start( arg_ptr, iovcnt ); for ( int i = 0; i < iovcnt; i++ ) { iov[i].iov_base = va_arg( arg_ptr, void * ); iov[i].iov_len = va_arg( arg_ptr, int ); } va_end( arg_ptr ); ssize_t nBytes = ::writev( mWd, iov, iovcnt ); if ( nBytes < 0 ) Debug( 1, "Writev of %d buffers on wd %d failed: %s", iovcnt, mWd, strerror(errno) ); return( nBytes ); } bool Pipe::open() { if ( ::pipe( mFd ) < 0 ) { Error( "pipe(), errno = %d, error = %s", errno, strerror(errno) ); return( false ); } return( true ); } bool Pipe::close() { if ( mFd[0] > -1 ) ::close( mFd[0] ); mFd[0] = -1; if ( mFd[1] > -1 ) ::close( mFd[1] ); mFd[1] = -1; return( true ); } bool Pipe::setBlocking( bool blocking ) { int flags; /* Now set it for non-blocking I/O */ if ( (flags = fcntl( mFd[1], F_GETFL )) < 0 ) { Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); return( false ); } if ( blocking ) { flags &= ~O_NONBLOCK; } else { flags |= O_NONBLOCK; } if ( fcntl( mFd[1], F_SETFL, flags ) < 0 ) { Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); return( false ); } return( true ); } SockAddr::SockAddr( const struct sockaddr *addr ) : mAddr( addr ) { } SockAddr *SockAddr::newSockAddr( const struct sockaddr &addr, socklen_t len ) { if ( addr.sa_family == AF_INET && len == SockAddrInet::addrSize() ) { return( new SockAddrInet( (const struct sockaddr_in *)&addr ) ); } else if ( addr.sa_family == AF_UNIX && len == SockAddrUnix::addrSize() ) { return( new SockAddrUnix( (const struct sockaddr_un *)&addr ) ); } Error( "Unable to create new SockAddr from addr family %d with size %d", addr.sa_family, len ); return( 0 ); } SockAddr *SockAddr::newSockAddr( const SockAddr *addr ) { if ( !addr ) return( 0 ); if ( addr->getDomain() == AF_INET ) { return( new SockAddrInet( *(SockAddrInet *)addr ) ); } else if ( addr->getDomain() == AF_UNIX ) { return( new SockAddrUnix( *(SockAddrUnix *)addr ) ); } Error( "Unable to create new SockAddr from addr family %d", addr->getDomain() ); return( 0 ); } SockAddrInet::SockAddrInet() : SockAddr( (struct sockaddr *)&mAddrIn ) { } bool SockAddrInet::resolve( const char *host, const char *serv, const char *proto ) { memset( &mAddrIn, 0, sizeof(mAddrIn) ); struct hostent *hostent=0; if ( !(hostent = ::gethostbyname( host ) ) ) { Error( "gethostbyname( %s ), h_errno = %d", host, h_errno ); return( false ); } struct servent *servent=0; if ( !(servent = ::getservbyname( serv, proto ) ) ) { Error( "getservbyname( %s ), errno = %d, error = %s", serv, errno, strerror(errno) ); return( false ); } mAddrIn.sin_port = servent->s_port; mAddrIn.sin_family = AF_INET; mAddrIn.sin_addr.s_addr = ((struct in_addr *)(hostent->h_addr))->s_addr; return( true ); } bool SockAddrInet::resolve( const char *host, int port, const char *proto ) { memset( &mAddrIn, 0, sizeof(mAddrIn) ); struct hostent *hostent=0; if ( !(hostent = ::gethostbyname( host ) ) ) { Error( "gethostbyname( %s ), h_errno = %d", host, h_errno ); return( false ); } mAddrIn.sin_port = htons(port); mAddrIn.sin_family = AF_INET; mAddrIn.sin_addr.s_addr = ((struct in_addr *)(hostent->h_addr))->s_addr; return( true ); } bool SockAddrInet::resolve( const char *serv, const char *proto ) { memset( &mAddrIn, 0, sizeof(mAddrIn) ); struct servent *servent=0; if ( !(servent = ::getservbyname( serv, proto ) ) ) { Error( "getservbyname( %s ), errno = %d, error = %s", serv, errno, strerror(errno) ); return( false ); } mAddrIn.sin_port = servent->s_port; mAddrIn.sin_family = AF_INET; mAddrIn.sin_addr.s_addr = INADDR_ANY; return( true ); } bool SockAddrInet::resolve( int port, const char *proto ) { memset( &mAddrIn, 0, sizeof(mAddrIn) ); mAddrIn.sin_port = htons(port); mAddrIn.sin_family = AF_INET; mAddrIn.sin_addr.s_addr = INADDR_ANY; return( true ); } SockAddrUnix::SockAddrUnix() : SockAddr( (struct sockaddr *)&mAddrUn ) { } bool SockAddrUnix::resolve( const char *path, const char *proto ) { memset( &mAddrUn, 0, sizeof(mAddrUn) ); strncpy( mAddrUn.sun_path, path, sizeof(mAddrUn.sun_path) ); mAddrUn.sun_family = AF_UNIX; return( true ); } bool Socket::socket() { if ( mSd >= 0 ) return( true ); if ( (mSd = ::socket( getDomain(), getType(), 0 ) ) < 0 ) { Error( "socket(), errno = %d, error = %s", errno, strerror(errno) ); return( false ); } int val = 1; (void)::setsockopt( mSd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val) ); (void)::setsockopt( mSd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val) ); mState = DISCONNECTED; return( true ); } bool Socket::connect() { if ( !socket() ) return( false ); if ( ::connect( mSd, mRemoteAddr->getAddr(), getAddrSize() ) == -1 ) { Error( "connect(), errno = %d, error = %s", errno, strerror(errno) ); close(); return( false ); } mState = CONNECTED; return( true ); } bool Socket::bind() { if ( !socket() ) return( false ); if ( ::bind( mSd, mLocalAddr->getAddr(), getAddrSize() ) == -1 ) { Error( "bind(), errno = %d, error = %s", errno, strerror(errno) ); close(); return( false ); } return( true ); } bool Socket::listen() { if ( ::listen( mSd, SOMAXCONN ) == -1 ) { Error( "listen(), errno = %d, error = %s", errno, strerror(errno) ); close(); return( false ); } mState = LISTENING; return( true ); } bool Socket::accept() { struct sockaddr *rem_addr = mLocalAddr->getTempAddr(); socklen_t rem_addr_size = getAddrSize(); int newSd = -1; if ( (newSd = ::accept( mSd, rem_addr, &rem_addr_size )) == -1 ) { Error( "accept(), errno = %d, error = %s", errno, strerror(errno) ); close(); return( false ); } ::close( mSd ); mSd = newSd; mState = CONNECTED; return( true ); } bool Socket::accept( int &newSd ) { struct sockaddr *rem_addr = mLocalAddr->getTempAddr(); socklen_t rem_addr_size = getAddrSize(); newSd = -1; if ( (newSd = ::accept( mSd, rem_addr, &rem_addr_size )) == -1 ) { Error( "accept(), errno = %d, error = %s", errno, strerror(errno) ); close(); return( false ); } return( true ); } bool Socket::close() { if ( mSd > -1 ) ::close( mSd ); mSd = -1; mState = CLOSED; return( true ); } int Socket::bytesToRead() const { int bytes_to_read = 0; if ( ioctl( mSd, FIONREAD, &bytes_to_read ) < 0 ) { Error( "ioctl(), errno = %d, error = %s", errno, strerror(errno) ); return( -1 ); } return( bytes_to_read ); } bool Socket::getBlocking( bool &blocking ) { int flags; if ( (flags = fcntl( mSd, F_GETFL )) < 0 ) { Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); return( false ); } blocking = (flags & O_NONBLOCK); return( true ); } bool Socket::setBlocking( bool blocking ) { #if 0 // ioctl is apparently not recommended int ioctl_arg = !blocking; if ( ioctl( mSd, FIONBIO, &ioctl_arg ) < 0 ) { Error( "ioctl(), errno = %d, error = %s", errno, strerror(errno) ); return( false ); } return( true ); #endif int flags; /* Now set it for non-blocking I/O */ if ( (flags = fcntl( mSd, F_GETFL )) < 0 ) { Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); return( false ); } if ( blocking ) { flags &= ~O_NONBLOCK; } else { flags |= O_NONBLOCK; } if ( fcntl( mSd, F_SETFL, flags ) < 0 ) { Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); return( false ); } return( true ); } bool Socket::getSendBufferSize( int &buffersize ) const { socklen_t optlen = sizeof(buffersize); if ( getsockopt( mSd, SOL_SOCKET, SO_SNDBUF, &buffersize, &optlen ) < 0 ) { Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); return( -1 ); } return( buffersize ); } bool Socket::getRecvBufferSize( int &buffersize ) const { socklen_t optlen = sizeof(buffersize); if ( getsockopt( mSd, SOL_SOCKET, SO_RCVBUF, &buffersize, &optlen ) < 0 ) { Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); return( -1 ); } return( buffersize ); } bool Socket::setSendBufferSize( int buffersize ) { if ( setsockopt( mSd, SOL_SOCKET, SO_SNDBUF, (char *)&buffersize, sizeof(buffersize)) < 0 ) { Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); return( false ); } return( true ); } bool Socket::setRecvBufferSize( int buffersize ) { if ( setsockopt( mSd, SOL_SOCKET, SO_RCVBUF, (char *)&buffersize, sizeof(buffersize)) < 0 ) { Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); return( false ); } return( true ); } bool Socket::getRouting( bool &route ) const { int dontRoute; socklen_t optlen = sizeof(dontRoute); if ( getsockopt( mSd, SOL_SOCKET, SO_DONTROUTE, &dontRoute, &optlen ) < 0 ) { Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); return( false ); } route = !dontRoute; return( true ); } bool Socket::setRouting( bool route ) { int dontRoute = !route; if ( setsockopt( mSd, SOL_SOCKET, SO_DONTROUTE, (char *)&dontRoute, sizeof(dontRoute)) < 0 ) { Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); return( false ); } return( true ); } bool Socket::getNoDelay( bool &nodelay ) const { int int_nodelay; socklen_t optlen = sizeof(int_nodelay); if ( getsockopt( mSd, IPPROTO_TCP, TCP_NODELAY, &int_nodelay, &optlen ) < 0 ) { Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); return( false ); } nodelay = int_nodelay; return( true ); } bool Socket::setNoDelay( bool nodelay ) { int int_nodelay = nodelay; if ( setsockopt( mSd, IPPROTO_TCP, TCP_NODELAY, (char *)&int_nodelay, sizeof(int_nodelay)) < 0 ) { Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); return( false ); } return( true ); } bool TcpInetServer::listen() { return( Socket::listen() ); } bool TcpInetServer::accept() { return( Socket::accept() ); } bool TcpInetServer::accept( TcpInetSocket *&newSocket ) { int newSd = -1; newSocket = 0; if ( !Socket::accept( newSd ) ) return( false ); newSocket = new TcpInetSocket( *this, newSd ); return( true ); } bool TcpUnixServer::accept( TcpUnixSocket *&newSocket ) { int newSd = -1; newSocket = 0; if ( !Socket::accept( newSd ) ) return( false ); newSocket = new TcpUnixSocket( *this, newSd ); return( true ); } Select::Select() : mHasTimeout( false ), mMaxFd( -1 ) { } Select::Select( struct timeval timeout ) : mMaxFd( -1 ) { setTimeout( timeout ); } Select::Select( int timeout ) : mMaxFd( -1 ) { setTimeout( timeout ); } Select::Select( double timeout ) : mMaxFd( -1 ) { setTimeout( timeout ); } void Select::setTimeout( int timeout ) { mTimeout.tv_sec = timeout; mTimeout.tv_usec = 0; mHasTimeout = true; } void Select::setTimeout( double timeout ) { mTimeout.tv_sec = int(timeout); mTimeout.tv_usec = suseconds_t((timeout-mTimeout.tv_sec)*1000000.0); mHasTimeout = true; } void Select::setTimeout( struct timeval timeout ) { mTimeout = timeout; mHasTimeout = true; } void Select::clearTimeout() { mHasTimeout = false; } void Select::calcMaxFd() { mMaxFd = -1; for ( CommsSet::iterator iter = mReaders.begin(); iter != mReaders.end(); iter++ ) if ( (*iter)->getMaxDesc() > mMaxFd ) mMaxFd = (*iter)->getMaxDesc(); for ( CommsSet::iterator iter = mWriters.begin(); iter != mWriters.end(); iter++ ) if ( (*iter)->getMaxDesc() > mMaxFd ) mMaxFd = (*iter)->getMaxDesc(); } bool Select::addReader( CommsBase *comms ) { if ( !comms->isOpen() ) { Error( "Unable to add closed reader" ); return( false ); } std::pair result = mReaders.insert( comms ); if ( result.second ) if ( comms->getMaxDesc() > mMaxFd ) mMaxFd = comms->getMaxDesc(); return( result.second ); } bool Select::deleteReader( CommsBase *comms ) { if ( !comms->isOpen() ) { Error( "Unable to add closed reader" ); return( false ); } if ( mReaders.erase( comms ) ) { calcMaxFd(); return( true ); } return( false ); } void Select::clearReaders() { mReaders.clear(); mMaxFd = -1; } bool Select::addWriter( CommsBase *comms ) { std::pair result = mWriters.insert( comms ); if ( result.second ) if ( comms->getMaxDesc() > mMaxFd ) mMaxFd = comms->getMaxDesc(); return( result.second ); } bool Select::deleteWriter( CommsBase *comms ) { if ( mWriters.erase( comms ) ) { calcMaxFd(); return( true ); } return( false ); } void Select::clearWriters() { mWriters.clear(); mMaxFd = -1; } int Select::wait() { struct timeval tempTimeout = mTimeout; struct timeval *selectTimeout = mHasTimeout?&tempTimeout:NULL; fd_set rfds; fd_set wfds; mReadable.clear(); FD_ZERO(&rfds); for ( CommsSet::iterator iter = mReaders.begin(); iter != mReaders.end(); iter++ ) FD_SET((*iter)->getReadDesc(),&rfds); mWriteable.clear(); FD_ZERO(&wfds); for ( CommsSet::iterator iter = mWriters.begin(); iter != mWriters.end(); iter++ ) FD_SET((*iter)->getWriteDesc(),&wfds); int nFound = select( mMaxFd+1, &rfds, &wfds, NULL, selectTimeout ); if( nFound == 0 ) { Debug( 1, "Select timed out" ); } else if ( nFound < 0) { Error( "Select error: %s", strerror(errno) ); } else { for ( CommsSet::iterator iter = mReaders.begin(); iter != mReaders.end(); iter++ ) if ( FD_ISSET((*iter)->getReadDesc(),&rfds) ) mReadable.push_back( *iter ); for ( CommsSet::iterator iter = mWriters.begin(); iter != mWriters.end(); iter++ ) if ( FD_ISSET((*iter)->getWriteDesc(),&rfds) ) mWriteable.push_back( *iter ); } return( nFound ); } const Select::CommsList &Select::getReadable() const { return( mReadable ); } const Select::CommsList &Select::getWriteable() const { return( mWriteable ); } ZoneMinder-1.26.5/src/zm_comms.h000066400000000000000000000507461225361755400164710ustar00rootroot00000000000000// // ZoneMinder Communicatoions Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_COMMS_H #define ZM_COMMS_H #include "zm_exception.h" #include #include #include #include #include #include class CommsException : public Exception { public: CommsException( const std::string &message ) : Exception( message ) { } }; class CommsBase { protected: const int &mRd; const int &mWd; protected: CommsBase( int &rd, int &wd ) : mRd( rd ), mWd( wd ) { } virtual ~CommsBase() { } public: virtual bool close()=0; virtual bool isOpen() const=0; virtual bool isClosed() const=0; virtual bool setBlocking( bool blocking )=0; public: int getReadDesc() const { return( mRd ); } int getWriteDesc() const { return( mWd ); } int getMaxDesc() const { return( mRd>mWd?mRd:mWd ); } virtual int read( void *msg, int len ) { ssize_t nBytes = ::read( mRd, msg, len ); if ( nBytes < 0 ) Debug( 1, "Read of %d bytes max on rd %d failed: %s", len, mRd, strerror(errno) ); return( nBytes ); } virtual int write( const void *msg, int len ) { ssize_t nBytes = ::write( mWd, msg, len ); if ( nBytes < 0 ) Debug( 1, "Write of %d bytes on wd %d failed: %s", len, mWd, strerror(errno) ); return( nBytes ); } virtual int readV( const struct iovec *iov, int iovcnt ) { int nBytes = ::readv( mRd, iov, iovcnt ); if ( nBytes < 0 ) Debug( 1, "Readv of %d buffers max on rd %d failed: %s", iovcnt, mRd, strerror(errno) ); return( nBytes ); } virtual int writeV( const struct iovec *iov, int iovcnt ) { ssize_t nBytes = ::writev( mWd, iov, iovcnt ); if ( nBytes < 0 ) Debug( 1, "Writev of %d buffers on wd %d failed: %s", iovcnt, mWd, strerror(errno) ); return( nBytes ); } virtual int readV( int iovcnt, /* const void *msg1, int len1, */ ... ); virtual int writeV( int iovcnt, /* const void *msg1, int len1, */ ... ); }; class Pipe : public CommsBase { protected: int mFd[2]; public: Pipe() : CommsBase( mFd[0], mFd[1] ) { mFd[0] = -1; mFd[1] = -1; } ~Pipe() { close(); } public: bool open(); bool close(); bool isOpen() const { return( mFd[0] != -1 && mFd[1] != -1 ); } int getReadDesc() const { return( mFd[0] ); } int getWriteDesc() const { return( mFd[1] ); } bool setBlocking( bool blocking ); }; class SockAddr { private: const struct sockaddr *mAddr; public: SockAddr( const struct sockaddr *addr ); virtual ~SockAddr() { } static SockAddr *newSockAddr( const struct sockaddr &addr, socklen_t len ); static SockAddr *newSockAddr( const SockAddr *addr ); int getDomain() const { return( mAddr?mAddr->sa_family:AF_UNSPEC ); } const struct sockaddr *getAddr() const { return( mAddr ); } virtual socklen_t getAddrSize() const=0; virtual struct sockaddr *getTempAddr() const=0; }; class SockAddrInet : public SockAddr { private: struct sockaddr_in mAddrIn; struct sockaddr_in mTempAddrIn; public: SockAddrInet(); SockAddrInet( const SockAddrInet &addr ) : SockAddr( (const struct sockaddr *)&mAddrIn ), mAddrIn( addr.mAddrIn ) { } SockAddrInet( const struct sockaddr_in *addr ) : SockAddr( (const struct sockaddr *)&mAddrIn ), mAddrIn( *addr ) { } bool resolve( const char *host, const char *serv, const char *proto ); bool resolve( const char *host, int port, const char *proto ); bool resolve( const char *serv, const char *proto ); bool resolve( int port, const char *proto ); socklen_t getAddrSize() const { return( sizeof(mAddrIn) ); } struct sockaddr *getTempAddr() const { return( (sockaddr *)&mTempAddrIn ); } public: static socklen_t addrSize() { return( sizeof(sockaddr_in) ); } }; class SockAddrUnix : public SockAddr { private: struct sockaddr_un mAddrUn; struct sockaddr_un mTempAddrUn; public: SockAddrUnix(); SockAddrUnix( const SockAddrUnix &addr ) : SockAddr( (const struct sockaddr *)&mAddrUn ), mAddrUn( addr.mAddrUn ) { } SockAddrUnix( const struct sockaddr_un *addr ) : SockAddr( (const struct sockaddr *)&mAddrUn ), mAddrUn( *addr ) { } bool resolve( const char *path, const char *proto ); socklen_t getAddrSize() const { return( sizeof(mAddrUn) ); } struct sockaddr *getTempAddr() const { return( (sockaddr *)&mTempAddrUn ); } public: static socklen_t addrSize() { return( sizeof(sockaddr_un) ); } }; class Socket : public CommsBase { protected: typedef enum { CLOSED, DISCONNECTED, LISTENING, CONNECTED } State; protected: int mSd; State mState; SockAddr *mLocalAddr; SockAddr *mRemoteAddr; protected: Socket() : CommsBase( mSd, mSd ), mSd( -1 ), mState( CLOSED ), mLocalAddr( 0 ), mRemoteAddr( 0 ) { } Socket( const Socket &socket, int newSd ) : CommsBase( mSd, mSd ), mSd( newSd ), mState( CONNECTED ), mLocalAddr( 0 ), mRemoteAddr( 0 ) { if ( socket.mLocalAddr ) mLocalAddr = SockAddr::newSockAddr( mLocalAddr ); if ( socket.mRemoteAddr ) mRemoteAddr = SockAddr::newSockAddr( mRemoteAddr ); } virtual ~Socket() { close(); delete mLocalAddr; delete mRemoteAddr; } public: bool isOpen() const { return( !isClosed() ); } bool isClosed() const { return( mState == CLOSED ); } bool isDisconnected() const { return( mState == DISCONNECTED ); } bool isConnected() const { return( mState == CONNECTED ); } virtual bool close(); protected: bool isListening() const { return( mState == LISTENING ); } protected: virtual bool socket(); virtual bool bind(); protected: virtual bool connect(); virtual bool listen(); virtual bool accept(); virtual bool accept( int & ); public: virtual int send( const void *msg, int len ) const { ssize_t nBytes = ::send( mSd, msg, len, 0 ); if ( nBytes < 0 ) Debug( 1, "Send of %d bytes on sd %d failed: %s", len, mSd, strerror(errno) ); return( nBytes ); } virtual int recv( void *msg, int len ) const { ssize_t nBytes = ::recv( mSd, msg, len, 0 ); if ( nBytes < 0 ) Debug( 1, "Recv of %d bytes max on sd %d failed: %s", len, mSd, strerror(errno) ); return( nBytes ); } virtual int send( const std::string &msg ) const { ssize_t nBytes = ::send( mSd, msg.data(), msg.size(), 0 ); if ( nBytes < 0 ) Debug( 1, "Send of string '%s' (%zd bytes) on sd %d failed: %s", msg.c_str(), msg.size(), mSd, strerror(errno) ); return( nBytes ); } virtual int recv( std::string &msg ) const { char buffer[msg.capacity()]; int nBytes = 0; if ( (nBytes = ::recv( mSd, buffer, sizeof(buffer), 0 )) < 0 ) { Debug( 1, "Recv of %zd bytes max to string on sd %d failed: %s", sizeof(buffer), mSd, strerror(errno) ); return( nBytes ); } buffer[nBytes] = '\0'; msg = buffer; return( nBytes ); } virtual int recv( std::string &msg, size_t maxLen ) const { char buffer[maxLen]; int nBytes = 0; if ( (nBytes = ::recv( mSd, buffer, sizeof(buffer), 0 )) < 0 ) { Debug( 1, "Recv of %zd bytes max to string on sd %d failed: %s", maxLen, mSd, strerror(errno) ); return( nBytes ); } buffer[nBytes] = '\0'; msg = buffer; return( nBytes ); } virtual int bytesToRead() const; int getDesc() const { return( mSd ); } //virtual bool isOpen() const //{ //return( mSd != -1 ); //} virtual int getDomain() const=0; virtual int getType() const=0; virtual const char *getProtocol() const=0; const SockAddr *getLocalAddr() const { return( mLocalAddr ); } const SockAddr *getRemoteAddr() const { return( mRemoteAddr ); } virtual socklen_t getAddrSize() const=0; bool getBlocking( bool &blocking ); bool setBlocking( bool blocking ); bool getSendBufferSize( int & ) const; bool getRecvBufferSize( int & ) const; bool setSendBufferSize( int ); bool setRecvBufferSize( int ); bool getRouting( bool & ) const; bool setRouting( bool ); bool getNoDelay( bool & ) const; bool setNoDelay( bool ); }; class InetSocket : virtual public Socket { public: int getDomain() const { return( AF_INET ); } virtual socklen_t getAddrSize() const { return( SockAddrInet::addrSize() ); } protected: bool resolveLocal( const char *host, const char *serv, const char *proto ) { SockAddrInet *addr = new SockAddrInet; mLocalAddr = addr; return( addr->resolve( host, serv, proto ) ); } bool resolveLocal( const char *host, int port, const char *proto ) { SockAddrInet *addr = new SockAddrInet; mLocalAddr = addr; return( addr->resolve( host, port, proto ) ); } bool resolveLocal( const char *serv, const char *proto ) { SockAddrInet *addr = new SockAddrInet; mLocalAddr = addr; return( addr->resolve( serv, proto ) ); } bool resolveLocal( int port, const char *proto ) { SockAddrInet *addr = new SockAddrInet; mLocalAddr = addr; return( addr->resolve( port, proto ) ); } bool resolveRemote( const char *host, const char *serv, const char *proto ) { SockAddrInet *addr = new SockAddrInet; mRemoteAddr = addr; return( addr->resolve( host, serv, proto ) ); } bool resolveRemote( const char *host, int port, const char *proto ) { SockAddrInet *addr = new SockAddrInet; mRemoteAddr = addr; return( addr->resolve( host, port, proto ) ); } protected: bool bind( const SockAddrInet &addr ) { mLocalAddr = new SockAddrInet( addr ); return( Socket::bind() ); } bool bind( const char *host, const char *serv ) { if ( !resolveLocal( host, serv, getProtocol() ) ) return( false ); return( Socket::bind() ); } bool bind( const char *host, int port ) { if ( !resolveLocal( host, port, getProtocol() ) ) return( false ); return( Socket::bind() ); } bool bind( const char *serv ) { if ( !resolveLocal( serv, getProtocol() ) ) return( false ); return( Socket::bind() ); } bool bind( int port ) { if ( !resolveLocal( port, getProtocol() ) ) return( false ); return( Socket::bind() ); } bool connect( const SockAddrInet &addr ) { mRemoteAddr = new SockAddrInet( addr ); return( Socket::connect() ); } bool connect( const char *host, const char *serv ) { if ( !resolveRemote( host, serv, getProtocol() ) ) return( false ); return( Socket::connect() ); } bool connect( const char *host, int port ) { if ( !resolveRemote( host, port, getProtocol() ) ) return( false ); return( Socket::connect() ); } }; class UnixSocket : virtual public Socket { public: int getDomain() const { return( AF_UNIX ); } virtual socklen_t getAddrSize() const { return( SockAddrUnix::addrSize() ); } protected: bool resolveLocal( const char *serv, const char *proto ) { SockAddrUnix *addr = new SockAddrUnix; mLocalAddr = addr; return( addr->resolve( serv, proto ) ); } bool resolveRemote( const char *path, const char *proto ) { SockAddrUnix *addr = new SockAddrUnix; mRemoteAddr = addr; return( addr->resolve( path, proto ) ); } protected: bool bind( const char *path ) { if ( !UnixSocket::resolveLocal( path, getProtocol() ) ) return( false ); return( Socket::bind() ); } bool connect( const char *path ) { if ( !UnixSocket::resolveRemote( path, getProtocol() ) ) return( false ); return( Socket::connect() ); } }; class UdpSocket : virtual public Socket { public: int getType() const { return( SOCK_DGRAM ); } const char *getProtocol() const { return( "udp" ); } public: virtual int sendto( const void *msg, int len, const SockAddr *addr=0 ) const { ssize_t nBytes = ::sendto( mSd, msg, len, 0, addr?addr->getAddr():NULL, addr?addr->getAddrSize():0 ); if ( nBytes < 0 ) Debug( 1, "Sendto of %d bytes on sd %d failed: %s", len, mSd, strerror(errno) ); return( nBytes ); } virtual int recvfrom( void *msg, int len, SockAddr *addr=0 ) const { ssize_t nBytes = 0; if ( addr ) { struct sockaddr sockAddr; socklen_t sockLen; nBytes = ::recvfrom( mSd, msg, len, 0, &sockAddr, &sockLen ); if ( nBytes < 0 ) { Debug( 1, "Recvfrom of %d bytes max on sd %d (with address) failed: %s", len, mSd, strerror(errno) ); } else if ( sockLen ) { addr = SockAddr::newSockAddr( sockAddr, sockLen ); } } else { nBytes = ::recvfrom( mSd, msg, len, 0, NULL, 0 ); if ( nBytes < 0 ) Debug( 1, "Recvfrom of %d bytes max on sd %d (no address) failed: %s", len, mSd, strerror(errno) ); } return( nBytes ); } }; class UdpInetSocket : virtual public UdpSocket, virtual public InetSocket { public: bool bind( const SockAddrInet &addr ) { return( InetSocket::bind( addr ) ); } bool bind( const char *host, const char *serv ) { return( InetSocket::bind( host, serv ) ); } bool bind( const char *host, int port ) { return( InetSocket::bind( host, port ) ); } bool bind( const char *serv ) { return( InetSocket::bind( serv ) ); } bool bind( int port ) { return( InetSocket::bind( port ) ); } bool connect( const SockAddrInet &addr ) { return( InetSocket::connect( addr ) ); } bool connect( const char *host, const char *serv ) { return( InetSocket::connect( host, serv ) ); } bool connect( const char *host, int port ) { return( InetSocket::connect( host, port ) ); } }; class UdpUnixSocket : virtual public UdpSocket, virtual public UnixSocket { public: bool bind( const char *path ) { return( UnixSocket::bind( path ) ); } bool connect( const char *path ) { return( UnixSocket::connect( path ) ); } }; class UdpInetClient : public UdpInetSocket { protected: bool bind( const SockAddrInet &addr ) { return( UdpInetSocket::bind( addr ) ); } bool bind( const char *host, const char *serv ) { return( UdpInetSocket::bind( host, serv ) ); } bool bind( const char *host, int port ) { return( UdpInetSocket::bind( host, port ) ); } bool bind( const char *serv ) { return( UdpInetSocket::bind( serv ) ); } bool bind( int port ) { return( UdpInetSocket::bind( port ) ); } public: bool connect( const SockAddrInet &addr ) { return( UdpInetSocket::connect( addr ) ); } bool connect( const char *host, const char *serv ) { return( UdpInetSocket::connect( host, serv ) ); } bool connect( const char *host, int port ) { return( UdpInetSocket::connect( host, port ) ); } }; class UdpUnixClient : public UdpUnixSocket { public: bool bind( const char *path ) { return( UdpUnixSocket::bind( path ) ); } public: bool connect( const char *path ) { return( UdpUnixSocket::connect( path) ); } }; class UdpInetServer : public UdpInetSocket { public: bool bind( const SockAddrInet &addr ) { return( UdpInetSocket::bind( addr ) ); } bool bind( const char *host, const char *serv ) { return( UdpInetSocket::bind( host, serv ) ); } bool bind( const char *host, int port ) { return( UdpInetSocket::bind( host, port ) ); } bool bind( const char *serv ) { return( UdpInetSocket::bind( serv ) ); } bool bind( int port ) { return( UdpInetSocket::bind( port ) ); } protected: bool connect( const char *host, const char *serv ) { return( UdpInetSocket::connect( host, serv ) ); } bool connect( const char *host, int port ) { return( UdpInetSocket::connect( host, port ) ); } }; class UdpUnixServer : public UdpUnixSocket { public: bool bind( const char *path ) { return( UdpUnixSocket::bind( path ) ); } protected: bool connect( const char *path ) { return( UdpUnixSocket::connect( path ) ); } }; class TcpSocket : virtual public Socket { public: TcpSocket() { } TcpSocket( const TcpSocket &socket, int newSd ) : Socket( socket, newSd ) { } public: int getType() const { return( SOCK_STREAM ); } const char *getProtocol() const { return( "tcp" ); } }; class TcpInetSocket : virtual public TcpSocket, virtual public InetSocket { public: TcpInetSocket() { } TcpInetSocket( const TcpInetSocket &socket, int newSd ) : TcpSocket( socket, newSd ) { } }; class TcpUnixSocket : virtual public TcpSocket, virtual public UnixSocket { public: TcpUnixSocket() { } TcpUnixSocket( const TcpUnixSocket &socket, int newSd ) : TcpSocket( socket, newSd ) { } }; class TcpInetClient : public TcpInetSocket { public: bool connect( const char *host, const char *serv ) { return( TcpInetSocket::connect( host, serv ) ); } bool connect( const char *host, int port ) { return( TcpInetSocket::connect( host, port ) ); } }; class TcpUnixClient : public TcpUnixSocket { public: bool connect( const char *path ) { return( TcpUnixSocket::connect( path) ); } }; class TcpInetServer : public TcpInetSocket { public: bool bind( const char *host, const char *serv ) { return( TcpInetSocket::bind( host, serv ) ); } bool bind( const char *host, int port ) { return( TcpInetSocket::bind( host, port ) ); } bool bind( const char *serv ) { return( TcpInetSocket::bind( serv ) ); } bool bind( int port ) { return( TcpInetSocket::bind( port ) ); } public: bool isListening() const { return( Socket::isListening() ); } bool listen(); bool accept(); bool accept( TcpInetSocket *&newSocket ); }; class TcpUnixServer : public TcpUnixSocket { public: bool bind( const char *path ) { return( TcpUnixSocket::bind( path ) ); } public: bool isListening() const { return( Socket::isListening() ); } bool listen(); bool accept(); bool accept( TcpUnixSocket *&newSocket ); }; class Select { public: typedef std::set CommsSet; typedef std::vector CommsList; protected: CommsSet mReaders; CommsSet mWriters; CommsList mReadable; CommsList mWriteable; bool mHasTimeout; struct timeval mTimeout; int mMaxFd; public: Select(); Select( struct timeval timeout ); Select( int timeout ); Select( double timeout ); void setTimeout( int timeout ); void setTimeout( double timeout ); void setTimeout( struct timeval timeout ); void clearTimeout(); void calcMaxFd(); bool addReader( CommsBase *comms ); bool deleteReader( CommsBase *comms ); void clearReaders(); bool addWriter( CommsBase *comms ); bool deleteWriter( CommsBase *comms ); void clearWriters(); int wait(); const CommsList &getReadable() const; const CommsList &getWriteable() const; }; #endif // ZM_COMMS_H ZoneMinder-1.26.5/src/zm_config.cpp000066400000000000000000000153761225361755400171530ustar00rootroot00000000000000// // ZoneMinder Configuration Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" #include "zm_db.h" #include #include #include #include void zmLoadConfig() { FILE *cfg; char line[512]; char *val; if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL ) { Fatal( "Can't open %s: %s", ZM_CONFIG, strerror(errno) ); } while ( fgets( line, sizeof(line), cfg ) != NULL ) { char *line_ptr = line; // Trim off any cr/lf line endings int chomp_len = strcspn( line_ptr, "\r\n" ); line_ptr[chomp_len] = '\0'; // Remove leading white space int white_len = strspn( line_ptr, " \t" ); line_ptr += white_len; // Check for comment or empty line if ( *line_ptr == '\0' || *line_ptr == '#' ) continue; // Remove trailing white space char *temp_ptr = line_ptr+strlen(line_ptr)-1; while ( *temp_ptr == ' ' || *temp_ptr == '\t' ) { *temp_ptr-- = '\0'; temp_ptr--; } // Now look for the '=' in the middle of the line temp_ptr = strchr( line_ptr, '=' ); if ( !temp_ptr ) { Warning( "Invalid data in %s: '%s'", ZM_CONFIG, line ); continue; } // Assign the name and value parts char *name_ptr = line_ptr; char *val_ptr = temp_ptr+1; // Trim trailing space from the name part do { *temp_ptr = '\0'; temp_ptr--; } while ( *temp_ptr == ' ' || *temp_ptr == '\t' ); // Remove leading white space from the value part white_len = strspn( val_ptr, " \t" ); val_ptr += white_len; val = (char *)malloc( strlen(val_ptr)+1 ); strncpy( val, val_ptr, strlen(val_ptr)+1 ); if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 ) staticConfig.DB_HOST = val; else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 ) staticConfig.DB_NAME = val; else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 ) staticConfig.DB_USER = val; else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 ) staticConfig.DB_PASS = val; else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 ) staticConfig.PATH_WEB = val; else { // We ignore this now as there may be more parameters than the // c/c++ binaries are bothered about // Warning( "Invalid parameter '%s' in %s", name_ptr, ZM_CONFIG ); } } fclose( cfg); zmDbConnect(); config.Load(); config.Assign(); } StaticConfig staticConfig; ConfigItem::ConfigItem( const char *p_name, const char *p_value, const char *const p_type ) { name = new char[strlen(p_name)+1]; strcpy( name, p_name ); value = new char[strlen(p_value)+1]; strcpy( value, p_value ); type = new char[strlen(p_type)+1]; strcpy( type, p_type ); //Info( "Created new config item %s = %s (%s)\n", name, value, type ); accessed = false; } ConfigItem::~ConfigItem() { delete[] name; delete[] value; delete[] type; } void ConfigItem::ConvertValue() const { if ( !strcmp( type, "boolean" ) ) { cfg_type = CFG_BOOLEAN; cfg_value.boolean_value = (bool)strtol( value, 0, 0 ); } else if ( !strcmp( type, "integer" ) ) { cfg_type = CFG_INTEGER; cfg_value.integer_value = strtol( value, 0, 10 ); } else if ( !strcmp( type, "hexadecimal" ) ) { cfg_type = CFG_INTEGER; cfg_value.integer_value = strtol( value, 0, 16 ); } else if ( !strcmp( type, "decimal" ) ) { cfg_type = CFG_DECIMAL; cfg_value.decimal_value = strtod( value, 0 ); } else { cfg_type = CFG_STRING; cfg_value.string_value = value; } accessed = true; } bool ConfigItem::BooleanValue() const { if ( !accessed ) ConvertValue(); if ( cfg_type != CFG_BOOLEAN ) { Error( "Attempt to fetch boolean value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); exit( -1 ); } return( cfg_value.boolean_value ); } int ConfigItem::IntegerValue() const { if ( !accessed ) ConvertValue(); if ( cfg_type != CFG_INTEGER ) { Error( "Attempt to fetch integer value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); exit( -1 ); } return( cfg_value.integer_value ); } double ConfigItem::DecimalValue() const { if ( !accessed ) ConvertValue(); if ( cfg_type != CFG_DECIMAL ) { Error( "Attempt to fetch decimal value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); exit( -1 ); } return( cfg_value.decimal_value ); } const char *ConfigItem::StringValue() const { if ( !accessed ) ConvertValue(); if ( cfg_type != CFG_STRING ) { Error( "Attempt to fetch string value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); exit( -1 ); } return( cfg_value.string_value ); } Config::Config() { n_items = 0; items = 0; } Config::~Config() { if ( items ) { for ( int i = 0; i < n_items; i++ ) { delete items[i]; } delete[] items; } } void Config::Load() { static char sql[ZM_SQL_SML_BUFSIZ]; strncpy( sql, "select Name, Value, Type from Config order by Id", sizeof(sql) ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } MYSQL_RES *result = mysql_store_result( &dbconn ); if ( !result ) { Error( "Can't use query result: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } n_items = mysql_num_rows( result ); if ( n_items <= ZM_MAX_CFG_ID ) { Error( "Config mismatch, expected %d items, read %d. Try running 'zmupdate.pl -f' to reload config.", ZM_MAX_CFG_ID+1, n_items ); exit( -1 ); } items = new ConfigItem *[n_items]; for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { items[i] = new ConfigItem( dbrow[0], dbrow[1], dbrow[2] ); } mysql_free_result( result ); } void Config::Assign() { ZM_CFG_ASSIGN_LIST } const ConfigItem &Config::Item( int id ) { if ( !n_items ) { Load(); Assign(); } if ( id < 0 || id > ZM_MAX_CFG_ID ) { Error( "Attempt to access invalid config, id = %d. Try running 'zmupdate.pl -f' to reload config.", id ); exit( -1 ); } ConfigItem *item = items[id]; if ( !item ) { Error( "Can't find config item %d", id ); exit( -1 ); } return( *item ); } Config config; ZoneMinder-1.26.5/src/zm_config.h.in000066400000000000000000000073151225361755400172170ustar00rootroot00000000000000// // ZoneMinder Configuration, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_CONFIG_H #define ZM_CONFIG_H #include "config.h" #include "zm_config_defines.h" #include #define ZM_CONFIG "@ZM_CONFIG@" // Path to config file #define ZM_VERSION "@VERSION@" // ZoneMinder Version #define ZM_HAS_V4L1 @ZM_HAS_V4L1@ #define ZM_HAS_V4L2 @ZM_HAS_V4L2@ #define ZM_HAS_V4L @ZM_HAS_V4L@ #ifdef HAVE_LIBAVFORMAT #define ZM_HAS_FFMPEG 1 #endif // HAVE_LIBAVFORMAT #define ZM_MAX_IMAGE_WIDTH 2048 // The largest image we imagine ever handling #define ZM_MAX_IMAGE_HEIGHT 1536 // The largest image we imagine ever handling #define ZM_MAX_IMAGE_COLOURS 4 // The largest image we imagine ever handling #define ZM_MAX_IMAGE_DIM (ZM_MAX_IMAGE_WIDTH*ZM_MAX_IMAGE_HEIGHT) #define ZM_MAX_IMAGE_SIZE (ZM_MAX_IMAGE_DIM*ZM_MAX_IMAGE_COLOURS) #define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP #define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP #define ZM_SQL_BATCH_SIZE 50 // Limit the size of multi-row SQL statements #define ZM_SQL_SML_BUFSIZ 256 // Size of SQL buffer #define ZM_SQL_MED_BUFSIZ 1024 // Size of SQL buffer #define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer #define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer #define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle #define ZM_SAMPLE_RATE int(1000000/ZM_MAX_FPS) // A general nyquist sample frequency for delays etc #define ZM_SUSPENDED_RATE int(1000000/4) // A slower rate for when disabled etc extern void zmLoadConfig(); struct StaticConfig { std::string DB_HOST; std::string DB_NAME; std::string DB_USER; std::string DB_PASS; std::string PATH_WEB; }; extern StaticConfig staticConfig; class ConfigItem { private: char *name; char *value; char *type; mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type; mutable union { bool boolean_value; int integer_value; double decimal_value; char *string_value; } cfg_value; mutable bool accessed; public: ConfigItem( const char *p_name, const char *p_value, const char *const p_type ); ~ConfigItem(); void ConvertValue() const; bool BooleanValue() const; int IntegerValue() const; double DecimalValue() const; const char *StringValue() const; inline operator bool() const { return( BooleanValue() ); } inline operator int() const { return( IntegerValue() ); } inline operator double() const { return( DecimalValue() ); } inline operator const char *() const { return( StringValue() ); } }; class Config { public: ZM_CFG_DECLARE_LIST private: int n_items; ConfigItem **items; public: Config(); ~Config(); void Load(); void Assign(); const ConfigItem &Item( int id ); }; extern Config config; #endif // ZM_CONFIG_H ZoneMinder-1.26.5/src/zm_config_defines.h000066400000000000000000000650401225361755400203060ustar00rootroot00000000000000// The file is autogenerated by zmconfgen.pl // Do not edit this file as any changes will be overwritten #define ZM_LANG_DEFAULT 0 #define ZM_OPT_USE_AUTH 1 #define ZM_AUTH_TYPE 2 #define ZM_AUTH_RELAY 3 #define ZM_AUTH_HASH_SECRET 4 #define ZM_AUTH_HASH_IPS 5 #define ZM_AUTH_HASH_LOGINS 6 #define ZM_DIR_EVENTS 7 #define ZM_USE_DEEP_STORAGE 8 #define ZM_DIR_IMAGES 9 #define ZM_DIR_SOUNDS 10 #define ZM_PATH_ZMS 11 #define ZM_COLOUR_JPEG_FILES 12 #define ZM_ADD_JPEG_COMMENTS 13 #define ZM_JPEG_FILE_QUALITY 14 #define ZM_JPEG_ALARM_FILE_QUALITY 15 #define ZM_JPEG_IMAGE_QUALITY 16 #define ZM_JPEG_STREAM_QUALITY 17 #define ZM_MPEG_TIMED_FRAMES 18 #define ZM_MPEG_LIVE_FORMAT 19 #define ZM_MPEG_REPLAY_FORMAT 20 #define ZM_RAND_STREAM 21 #define ZM_OPT_CAMBOZOLA 22 #define ZM_PATH_CAMBOZOLA 23 #define ZM_RELOAD_CAMBOZOLA 24 #define ZM_TIMESTAMP_ON_CAPTURE 25 #define ZM_CPU_EXTENSIONS 26 #define ZM_FAST_IMAGE_BLENDS 27 #define ZM_OPT_ADAPTIVE_SKIP 28 #define ZM_BLEND_ALARMED_IMAGES 29 #define ZM_MAX_SUSPEND_TIME 30 #define ZM_OPT_REMOTE_CAMERAS 31 #define ZM_NETCAM_REGEXPS 32 #define ZM_HTTP_VERSION 33 #define ZM_HTTP_UA 34 #define ZM_HTTP_TIMEOUT 35 #define ZM_MIN_RTP_PORT 36 #define ZM_MAX_RTP_PORT 37 #define ZM_OPT_FFMPEG 38 #define ZM_PATH_FFMPEG 39 #define ZM_FFMPEG_INPUT_OPTIONS 40 #define ZM_FFMPEG_OUTPUT_OPTIONS 41 #define ZM_FFMPEG_FORMATS 42 #define ZM_LOG_LEVEL_SYSLOG 43 #define ZM_LOG_LEVEL_FILE 44 #define ZM_LOG_LEVEL_WEBLOG 45 #define ZM_LOG_LEVEL_DATABASE 46 #define ZM_LOG_DATABASE_LIMIT 47 #define ZM_LOG_DEBUG 48 #define ZM_LOG_DEBUG_TARGET 49 #define ZM_LOG_DEBUG_LEVEL 50 #define ZM_LOG_DEBUG_FILE 51 #define ZM_LOG_CHECK_PERIOD 52 #define ZM_LOG_ALERT_WAR_COUNT 53 #define ZM_LOG_ALERT_ERR_COUNT 54 #define ZM_LOG_ALERT_FAT_COUNT 55 #define ZM_LOG_ALARM_WAR_COUNT 56 #define ZM_LOG_ALARM_ERR_COUNT 57 #define ZM_LOG_ALARM_FAT_COUNT 58 #define ZM_RECORD_EVENT_STATS 59 #define ZM_RECORD_DIAG_IMAGES 60 #define ZM_DUMP_CORES 61 #define ZM_PATH_MAP 62 #define ZM_PATH_SOCKS 63 #define ZM_PATH_LOGS 64 #define ZM_PATH_SWAP 65 #define ZM_WEB_TITLE_PREFIX 66 #define ZM_WEB_RESIZE_CONSOLE 67 #define ZM_WEB_POPUP_ON_ALARM 68 #define ZM_OPT_X10 69 #define ZM_X10_DEVICE 70 #define ZM_X10_HOUSE_CODE 71 #define ZM_X10_DB_RELOAD_INTERVAL 72 #define ZM_WEB_SOUND_ON_ALARM 73 #define ZM_WEB_ALARM_SOUND 74 #define ZM_WEB_COMPACT_MONTAGE 75 #define ZM_OPT_FAST_DELETE 76 #define ZM_STRICT_VIDEO_CONFIG 77 #define ZM_SIGNAL_CHECK_POINTS 78 #define ZM_V4L_MULTI_BUFFER 79 #define ZM_CAPTURES_PER_FRAME 80 #define ZM_FILTER_RELOAD_DELAY 81 #define ZM_FILTER_EXECUTE_INTERVAL 82 #define ZM_OPT_UPLOAD 83 #define ZM_UPLOAD_ARCH_FORMAT 84 #define ZM_UPLOAD_ARCH_COMPRESS 85 #define ZM_UPLOAD_ARCH_ANALYSE 86 #define ZM_UPLOAD_PROTOCOL 87 #define ZM_UPLOAD_FTP_HOST 88 #define ZM_UPLOAD_HOST 89 #define ZM_UPLOAD_PORT 90 #define ZM_UPLOAD_FTP_USER 91 #define ZM_UPLOAD_USER 92 #define ZM_UPLOAD_FTP_PASS 93 #define ZM_UPLOAD_PASS 94 #define ZM_UPLOAD_FTP_LOC_DIR 95 #define ZM_UPLOAD_LOC_DIR 96 #define ZM_UPLOAD_FTP_REM_DIR 97 #define ZM_UPLOAD_REM_DIR 98 #define ZM_UPLOAD_FTP_TIMEOUT 99 #define ZM_UPLOAD_TIMEOUT 100 #define ZM_UPLOAD_FTP_PASSIVE 101 #define ZM_UPLOAD_FTP_DEBUG 102 #define ZM_UPLOAD_DEBUG 103 #define ZM_OPT_EMAIL 104 #define ZM_EMAIL_ADDRESS 105 #define ZM_EMAIL_TEXT 106 #define ZM_EMAIL_SUBJECT 107 #define ZM_EMAIL_BODY 108 #define ZM_OPT_MESSAGE 109 #define ZM_MESSAGE_ADDRESS 110 #define ZM_MESSAGE_TEXT 111 #define ZM_MESSAGE_SUBJECT 112 #define ZM_MESSAGE_BODY 113 #define ZM_NEW_MAIL_MODULES 114 #define ZM_EMAIL_HOST 115 #define ZM_FROM_EMAIL 116 #define ZM_URL 117 #define ZM_MAX_RESTART_DELAY 118 #define ZM_WATCH_CHECK_INTERVAL 119 #define ZM_WATCH_MAX_DELAY 120 #define ZM_RUN_AUDIT 121 #define ZM_AUDIT_CHECK_INTERVAL 122 #define ZM_FORCED_ALARM_SCORE 123 #define ZM_BULK_FRAME_INTERVAL 124 #define ZM_EVENT_CLOSE_MODE 125 #define ZM_FORCE_CLOSE_EVENTS 126 #define ZM_CREATE_ANALYSIS_IMAGES 127 #define ZM_WEIGHTED_ALARM_CENTRES 128 #define ZM_EVENT_IMAGE_DIGITS 129 #define ZM_DEFAULT_ASPECT_RATIO 130 #define ZM_USER_SELF_EDIT 131 #define ZM_OPT_FRAME_SERVER 132 #define ZM_FRAME_SOCKET_SIZE 133 #define ZM_OPT_CONTROL 134 #define ZM_OPT_TRIGGERS 135 #define ZM_CHECK_FOR_UPDATES 136 #define ZM_UPDATE_CHECK_PROXY 137 #define ZM_SHM_KEY 138 #define ZM_WEB_REFRESH_METHOD 139 #define ZM_WEB_EVENT_SORT_FIELD 140 #define ZM_WEB_EVENT_SORT_ORDER 141 #define ZM_WEB_EVENTS_PER_PAGE 142 #define ZM_WEB_LIST_THUMBS 143 #define ZM_WEB_LIST_THUMB_WIDTH 144 #define ZM_WEB_LIST_THUMB_HEIGHT 145 #define ZM_WEB_USE_OBJECT_TAGS 146 #define ZM_WEB_H_REFRESH_MAIN 147 #define ZM_WEB_H_REFRESH_CYCLE 148 #define ZM_WEB_H_REFRESH_IMAGE 149 #define ZM_WEB_H_REFRESH_STATUS 150 #define ZM_WEB_H_REFRESH_EVENTS 151 #define ZM_WEB_H_CAN_STREAM 152 #define ZM_WEB_H_STREAM_METHOD 153 #define ZM_WEB_H_DEFAULT_SCALE 154 #define ZM_WEB_H_DEFAULT_RATE 155 #define ZM_WEB_H_VIDEO_BITRATE 156 #define ZM_WEB_H_VIDEO_MAXFPS 157 #define ZM_WEB_H_SCALE_THUMBS 158 #define ZM_WEB_H_EVENTS_VIEW 159 #define ZM_WEB_H_SHOW_PROGRESS 160 #define ZM_WEB_H_AJAX_TIMEOUT 161 #define ZM_WEB_M_REFRESH_MAIN 162 #define ZM_WEB_M_REFRESH_CYCLE 163 #define ZM_WEB_M_REFRESH_IMAGE 164 #define ZM_WEB_M_REFRESH_STATUS 165 #define ZM_WEB_M_REFRESH_EVENTS 166 #define ZM_WEB_M_CAN_STREAM 167 #define ZM_WEB_M_STREAM_METHOD 168 #define ZM_WEB_M_DEFAULT_SCALE 169 #define ZM_WEB_M_DEFAULT_RATE 170 #define ZM_WEB_M_VIDEO_BITRATE 171 #define ZM_WEB_M_VIDEO_MAXFPS 172 #define ZM_WEB_M_SCALE_THUMBS 173 #define ZM_WEB_M_EVENTS_VIEW 174 #define ZM_WEB_M_SHOW_PROGRESS 175 #define ZM_WEB_M_AJAX_TIMEOUT 176 #define ZM_WEB_L_REFRESH_MAIN 177 #define ZM_WEB_L_REFRESH_CYCLE 178 #define ZM_WEB_L_REFRESH_IMAGE 179 #define ZM_WEB_L_REFRESH_STATUS 180 #define ZM_WEB_L_REFRESH_EVENTS 181 #define ZM_WEB_L_CAN_STREAM 182 #define ZM_WEB_L_STREAM_METHOD 183 #define ZM_WEB_L_DEFAULT_SCALE 184 #define ZM_WEB_L_DEFAULT_RATE 185 #define ZM_WEB_L_VIDEO_BITRATE 186 #define ZM_WEB_L_VIDEO_MAXFPS 187 #define ZM_WEB_L_SCALE_THUMBS 188 #define ZM_WEB_L_EVENTS_VIEW 189 #define ZM_WEB_L_SHOW_PROGRESS 190 #define ZM_WEB_L_AJAX_TIMEOUT 191 #define ZM_WEB_P_CAN_STREAM 192 #define ZM_WEB_P_STREAM_METHOD 193 #define ZM_WEB_P_DEFAULT_SCALE 194 #define ZM_WEB_P_DEFAULT_RATE 195 #define ZM_WEB_P_VIDEO_BITRATE 196 #define ZM_WEB_P_VIDEO_MAXFPS 197 #define ZM_WEB_P_SCALE_THUMBS 198 #define ZM_WEB_P_AJAX_TIMEOUT 199 #define ZM_DYN_LAST_VERSION 200 #define ZM_DYN_CURR_VERSION 201 #define ZM_DYN_DB_VERSION 202 #define ZM_DYN_LAST_CHECK 203 #define ZM_DYN_NEXT_REMINDER 204 #define ZM_DYN_DONATE_REMINDER_TIME 205 #define ZM_DYN_SHOW_DONATE_REMINDER 206 #define ZM_EYEZM_DEBUG 207 #define ZM_EYEZM_LOG_TO_FILE 208 #define ZM_EYEZM_LOG_FILE 209 #define ZM_EYEZM_EVENT_VCODEC 210 #define ZM_EYEZM_FEED_VCODEC 211 #define ZM_EYEZM_H264_DEFAULT_BR 212 #define ZM_EYEZM_H264_DEFAULT_EVBR 213 #define ZM_EYEZM_H264_TIMEOUT 214 #define ZM_EYEZM_SEG_DURATION 215 #define ZM_MAX_CFG_ID 215 #define ZM_CFG_DECLARE_LIST \ const char *lang_default;\ bool opt_use_auth;\ const char *auth_type;\ const char *auth_relay;\ const char *auth_hash_secret;\ bool auth_hash_ips;\ bool auth_hash_logins;\ const char *dir_events;\ bool use_deep_storage;\ const char *dir_images;\ const char *dir_sounds;\ const char *path_zms;\ bool colour_jpeg_files;\ bool add_jpeg_comments;\ int jpeg_file_quality;\ int jpeg_alarm_file_quality;\ int jpeg_image_quality;\ int jpeg_stream_quality;\ bool mpeg_timed_frames;\ const char *mpeg_live_format;\ const char *mpeg_replay_format;\ bool rand_stream;\ bool opt_cambozola;\ const char *path_cambozola;\ int reload_cambozola;\ bool timestamp_on_capture;\ bool cpu_extensions;\ bool fast_image_blends;\ bool opt_adaptive_skip;\ bool blend_alarmed_images;\ int max_suspend_time;\ bool opt_remote_cameras;\ bool netcam_regexps;\ const char *http_version;\ const char *http_ua;\ int http_timeout;\ int min_rtp_port;\ int max_rtp_port;\ bool opt_ffmpeg;\ const char *path_ffmpeg;\ const char *ffmpeg_input_options;\ const char *ffmpeg_output_options;\ const char *ffmpeg_formats;\ int log_level_syslog;\ int log_level_file;\ int log_level_weblog;\ int log_level_database;\ const char *log_database_limit;\ bool log_debug;\ const char *log_debug_target;\ int log_debug_level;\ const char *log_debug_file;\ int log_check_period;\ int log_alert_war_count;\ int log_alert_err_count;\ int log_alert_fat_count;\ int log_alarm_war_count;\ int log_alarm_err_count;\ int log_alarm_fat_count;\ bool record_event_stats;\ bool record_diag_images;\ bool dump_cores;\ const char *path_map;\ const char *path_socks;\ const char *path_logs;\ const char *path_swap;\ const char *web_title_prefix;\ bool web_resize_console;\ bool web_popup_on_alarm;\ bool opt_x10;\ const char *x10_device;\ const char *x10_house_code;\ int x10_db_reload_interval;\ bool web_sound_on_alarm;\ const char *web_alarm_sound;\ bool web_compact_montage;\ bool opt_fast_delete;\ bool strict_video_config;\ int signal_check_points;\ bool v4l_multi_buffer;\ int captures_per_frame;\ int filter_reload_delay;\ int filter_execute_interval;\ bool opt_upload;\ const char *upload_arch_format;\ bool upload_arch_compress;\ bool upload_arch_analyse;\ const char *upload_protocol;\ const char *upload_ftp_host;\ const char *upload_host;\ int upload_port;\ const char *upload_ftp_user;\ const char *upload_user;\ const char *upload_ftp_pass;\ const char *upload_pass;\ const char *upload_ftp_loc_dir;\ const char *upload_loc_dir;\ const char *upload_ftp_rem_dir;\ const char *upload_rem_dir;\ int upload_ftp_timeout;\ int upload_timeout;\ bool upload_ftp_passive;\ bool upload_ftp_debug;\ bool upload_debug;\ bool opt_email;\ const char *email_address;\ const char *email_text;\ const char *email_subject;\ const char *email_body;\ bool opt_message;\ const char *message_address;\ const char *message_text;\ const char *message_subject;\ const char *message_body;\ bool new_mail_modules;\ const char *email_host;\ const char *from_email;\ const char *url;\ int max_restart_delay;\ int watch_check_interval;\ double watch_max_delay;\ bool run_audit;\ int audit_check_interval;\ int forced_alarm_score;\ int bulk_frame_interval;\ const char *event_close_mode;\ bool force_close_events;\ bool create_analysis_images;\ bool weighted_alarm_centres;\ int event_image_digits;\ const char *default_aspect_ratio;\ bool user_self_edit;\ bool opt_frame_server;\ int frame_socket_size;\ bool opt_control;\ bool opt_triggers;\ bool check_for_updates;\ const char *update_check_proxy;\ int shm_key;\ const char *web_refresh_method;\ const char *web_event_sort_field;\ const char *web_event_sort_order;\ int web_events_per_page;\ bool web_list_thumbs;\ int web_list_thumb_width;\ int web_list_thumb_height;\ bool web_use_object_tags;\ int web_h_refresh_main;\ int web_h_refresh_cycle;\ int web_h_refresh_image;\ int web_h_refresh_status;\ int web_h_refresh_events;\ const char *web_h_can_stream;\ const char *web_h_stream_method;\ int web_h_default_scale;\ int web_h_default_rate;\ int web_h_video_bitrate;\ int web_h_video_maxfps;\ bool web_h_scale_thumbs;\ const char *web_h_events_view;\ bool web_h_show_progress;\ int web_h_ajax_timeout;\ int web_m_refresh_main;\ int web_m_refresh_cycle;\ int web_m_refresh_image;\ int web_m_refresh_status;\ int web_m_refresh_events;\ const char *web_m_can_stream;\ const char *web_m_stream_method;\ int web_m_default_scale;\ int web_m_default_rate;\ int web_m_video_bitrate;\ int web_m_video_maxfps;\ bool web_m_scale_thumbs;\ const char *web_m_events_view;\ bool web_m_show_progress;\ int web_m_ajax_timeout;\ int web_l_refresh_main;\ int web_l_refresh_cycle;\ int web_l_refresh_image;\ int web_l_refresh_status;\ int web_l_refresh_events;\ const char *web_l_can_stream;\ const char *web_l_stream_method;\ int web_l_default_scale;\ int web_l_default_rate;\ int web_l_video_bitrate;\ int web_l_video_maxfps;\ bool web_l_scale_thumbs;\ const char *web_l_events_view;\ bool web_l_show_progress;\ int web_l_ajax_timeout;\ const char *web_p_can_stream;\ const char *web_p_stream_method;\ int web_p_default_scale;\ int web_p_default_rate;\ int web_p_video_bitrate;\ int web_p_video_maxfps;\ bool web_p_scale_thumbs;\ int web_p_ajax_timeout;\ const char *dyn_last_version;\ const char *dyn_curr_version;\ const char *dyn_db_version;\ int dyn_last_check;\ const char *dyn_next_reminder;\ int dyn_donate_reminder_time;\ bool dyn_show_donate_reminder;\ bool eyezm_debug;\ bool eyezm_log_to_file;\ const char *eyezm_log_file;\ const char *eyezm_event_vcodec;\ const char *eyezm_feed_vcodec;\ const char *eyezm_h264_default_br;\ const char *eyezm_h264_default_evbr;\ const char *eyezm_h264_timeout;\ const char *eyezm_seg_duration;\ #define ZM_CFG_ASSIGN_LIST \ lang_default = (const char *)config.Item( ZM_LANG_DEFAULT );\ opt_use_auth = (bool)config.Item( ZM_OPT_USE_AUTH );\ auth_type = (const char *)config.Item( ZM_AUTH_TYPE );\ auth_relay = (const char *)config.Item( ZM_AUTH_RELAY );\ auth_hash_secret = (const char *)config.Item( ZM_AUTH_HASH_SECRET );\ auth_hash_ips = (bool)config.Item( ZM_AUTH_HASH_IPS );\ auth_hash_logins = (bool)config.Item( ZM_AUTH_HASH_LOGINS );\ dir_events = (const char *)config.Item( ZM_DIR_EVENTS );\ use_deep_storage = (bool)config.Item( ZM_USE_DEEP_STORAGE );\ dir_images = (const char *)config.Item( ZM_DIR_IMAGES );\ dir_sounds = (const char *)config.Item( ZM_DIR_SOUNDS );\ path_zms = (const char *)config.Item( ZM_PATH_ZMS );\ colour_jpeg_files = (bool)config.Item( ZM_COLOUR_JPEG_FILES );\ add_jpeg_comments = (bool)config.Item( ZM_ADD_JPEG_COMMENTS );\ jpeg_file_quality = (int)config.Item( ZM_JPEG_FILE_QUALITY );\ jpeg_alarm_file_quality = (int)config.Item( ZM_JPEG_ALARM_FILE_QUALITY );\ jpeg_image_quality = (int)config.Item( ZM_JPEG_IMAGE_QUALITY );\ jpeg_stream_quality = (int)config.Item( ZM_JPEG_STREAM_QUALITY );\ mpeg_timed_frames = (bool)config.Item( ZM_MPEG_TIMED_FRAMES );\ mpeg_live_format = (const char *)config.Item( ZM_MPEG_LIVE_FORMAT );\ mpeg_replay_format = (const char *)config.Item( ZM_MPEG_REPLAY_FORMAT );\ rand_stream = (bool)config.Item( ZM_RAND_STREAM );\ opt_cambozola = (bool)config.Item( ZM_OPT_CAMBOZOLA );\ path_cambozola = (const char *)config.Item( ZM_PATH_CAMBOZOLA );\ reload_cambozola = (int)config.Item( ZM_RELOAD_CAMBOZOLA );\ timestamp_on_capture = (bool)config.Item( ZM_TIMESTAMP_ON_CAPTURE );\ cpu_extensions = (bool)config.Item( ZM_CPU_EXTENSIONS );\ fast_image_blends = (bool)config.Item( ZM_FAST_IMAGE_BLENDS );\ opt_adaptive_skip = (bool)config.Item( ZM_OPT_ADAPTIVE_SKIP );\ blend_alarmed_images = (bool)config.Item( ZM_BLEND_ALARMED_IMAGES );\ max_suspend_time = (int)config.Item( ZM_MAX_SUSPEND_TIME );\ opt_remote_cameras = (bool)config.Item( ZM_OPT_REMOTE_CAMERAS );\ netcam_regexps = (bool)config.Item( ZM_NETCAM_REGEXPS );\ http_version = (const char *)config.Item( ZM_HTTP_VERSION );\ http_ua = (const char *)config.Item( ZM_HTTP_UA );\ http_timeout = (int)config.Item( ZM_HTTP_TIMEOUT );\ min_rtp_port = (int)config.Item( ZM_MIN_RTP_PORT );\ max_rtp_port = (int)config.Item( ZM_MAX_RTP_PORT );\ opt_ffmpeg = (bool)config.Item( ZM_OPT_FFMPEG );\ path_ffmpeg = (const char *)config.Item( ZM_PATH_FFMPEG );\ ffmpeg_input_options = (const char *)config.Item( ZM_FFMPEG_INPUT_OPTIONS );\ ffmpeg_output_options = (const char *)config.Item( ZM_FFMPEG_OUTPUT_OPTIONS );\ ffmpeg_formats = (const char *)config.Item( ZM_FFMPEG_FORMATS );\ log_level_syslog = (int)config.Item( ZM_LOG_LEVEL_SYSLOG );\ log_level_file = (int)config.Item( ZM_LOG_LEVEL_FILE );\ log_level_weblog = (int)config.Item( ZM_LOG_LEVEL_WEBLOG );\ log_level_database = (int)config.Item( ZM_LOG_LEVEL_DATABASE );\ log_database_limit = (const char *)config.Item( ZM_LOG_DATABASE_LIMIT );\ log_debug = (bool)config.Item( ZM_LOG_DEBUG );\ log_debug_target = (const char *)config.Item( ZM_LOG_DEBUG_TARGET );\ log_debug_level = (int)config.Item( ZM_LOG_DEBUG_LEVEL );\ log_debug_file = (const char *)config.Item( ZM_LOG_DEBUG_FILE );\ log_check_period = (int)config.Item( ZM_LOG_CHECK_PERIOD );\ log_alert_war_count = (int)config.Item( ZM_LOG_ALERT_WAR_COUNT );\ log_alert_err_count = (int)config.Item( ZM_LOG_ALERT_ERR_COUNT );\ log_alert_fat_count = (int)config.Item( ZM_LOG_ALERT_FAT_COUNT );\ log_alarm_war_count = (int)config.Item( ZM_LOG_ALARM_WAR_COUNT );\ log_alarm_err_count = (int)config.Item( ZM_LOG_ALARM_ERR_COUNT );\ log_alarm_fat_count = (int)config.Item( ZM_LOG_ALARM_FAT_COUNT );\ record_event_stats = (bool)config.Item( ZM_RECORD_EVENT_STATS );\ record_diag_images = (bool)config.Item( ZM_RECORD_DIAG_IMAGES );\ dump_cores = (bool)config.Item( ZM_DUMP_CORES );\ path_map = (const char *)config.Item( ZM_PATH_MAP );\ path_socks = (const char *)config.Item( ZM_PATH_SOCKS );\ path_logs = (const char *)config.Item( ZM_PATH_LOGS );\ path_swap = (const char *)config.Item( ZM_PATH_SWAP );\ web_title_prefix = (const char *)config.Item( ZM_WEB_TITLE_PREFIX );\ web_resize_console = (bool)config.Item( ZM_WEB_RESIZE_CONSOLE );\ web_popup_on_alarm = (bool)config.Item( ZM_WEB_POPUP_ON_ALARM );\ opt_x10 = (bool)config.Item( ZM_OPT_X10 );\ x10_device = (const char *)config.Item( ZM_X10_DEVICE );\ x10_house_code = (const char *)config.Item( ZM_X10_HOUSE_CODE );\ x10_db_reload_interval = (int)config.Item( ZM_X10_DB_RELOAD_INTERVAL );\ web_sound_on_alarm = (bool)config.Item( ZM_WEB_SOUND_ON_ALARM );\ web_alarm_sound = (const char *)config.Item( ZM_WEB_ALARM_SOUND );\ web_compact_montage = (bool)config.Item( ZM_WEB_COMPACT_MONTAGE );\ opt_fast_delete = (bool)config.Item( ZM_OPT_FAST_DELETE );\ strict_video_config = (bool)config.Item( ZM_STRICT_VIDEO_CONFIG );\ signal_check_points = (int)config.Item( ZM_SIGNAL_CHECK_POINTS );\ v4l_multi_buffer = (bool)config.Item( ZM_V4L_MULTI_BUFFER );\ captures_per_frame = (int)config.Item( ZM_CAPTURES_PER_FRAME );\ filter_reload_delay = (int)config.Item( ZM_FILTER_RELOAD_DELAY );\ filter_execute_interval = (int)config.Item( ZM_FILTER_EXECUTE_INTERVAL );\ opt_upload = (bool)config.Item( ZM_OPT_UPLOAD );\ upload_arch_format = (const char *)config.Item( ZM_UPLOAD_ARCH_FORMAT );\ upload_arch_compress = (bool)config.Item( ZM_UPLOAD_ARCH_COMPRESS );\ upload_arch_analyse = (bool)config.Item( ZM_UPLOAD_ARCH_ANALYSE );\ upload_protocol = (const char *)config.Item( ZM_UPLOAD_PROTOCOL );\ upload_ftp_host = (const char *)config.Item( ZM_UPLOAD_FTP_HOST );\ upload_host = (const char *)config.Item( ZM_UPLOAD_HOST );\ upload_port = (int)config.Item( ZM_UPLOAD_PORT );\ upload_ftp_user = (const char *)config.Item( ZM_UPLOAD_FTP_USER );\ upload_user = (const char *)config.Item( ZM_UPLOAD_USER );\ upload_ftp_pass = (const char *)config.Item( ZM_UPLOAD_FTP_PASS );\ upload_pass = (const char *)config.Item( ZM_UPLOAD_PASS );\ upload_ftp_loc_dir = (const char *)config.Item( ZM_UPLOAD_FTP_LOC_DIR );\ upload_loc_dir = (const char *)config.Item( ZM_UPLOAD_LOC_DIR );\ upload_ftp_rem_dir = (const char *)config.Item( ZM_UPLOAD_FTP_REM_DIR );\ upload_rem_dir = (const char *)config.Item( ZM_UPLOAD_REM_DIR );\ upload_ftp_timeout = (int)config.Item( ZM_UPLOAD_FTP_TIMEOUT );\ upload_timeout = (int)config.Item( ZM_UPLOAD_TIMEOUT );\ upload_ftp_passive = (bool)config.Item( ZM_UPLOAD_FTP_PASSIVE );\ upload_ftp_debug = (bool)config.Item( ZM_UPLOAD_FTP_DEBUG );\ upload_debug = (bool)config.Item( ZM_UPLOAD_DEBUG );\ opt_email = (bool)config.Item( ZM_OPT_EMAIL );\ email_address = (const char *)config.Item( ZM_EMAIL_ADDRESS );\ email_text = (const char *)config.Item( ZM_EMAIL_TEXT );\ email_subject = (const char *)config.Item( ZM_EMAIL_SUBJECT );\ email_body = (const char *)config.Item( ZM_EMAIL_BODY );\ opt_message = (bool)config.Item( ZM_OPT_MESSAGE );\ message_address = (const char *)config.Item( ZM_MESSAGE_ADDRESS );\ message_text = (const char *)config.Item( ZM_MESSAGE_TEXT );\ message_subject = (const char *)config.Item( ZM_MESSAGE_SUBJECT );\ message_body = (const char *)config.Item( ZM_MESSAGE_BODY );\ new_mail_modules = (bool)config.Item( ZM_NEW_MAIL_MODULES );\ email_host = (const char *)config.Item( ZM_EMAIL_HOST );\ from_email = (const char *)config.Item( ZM_FROM_EMAIL );\ url = (const char *)config.Item( ZM_URL );\ max_restart_delay = (int)config.Item( ZM_MAX_RESTART_DELAY );\ watch_check_interval = (int)config.Item( ZM_WATCH_CHECK_INTERVAL );\ watch_max_delay = (double) config.Item( ZM_WATCH_MAX_DELAY );\ run_audit = (bool)config.Item( ZM_RUN_AUDIT );\ audit_check_interval = (int)config.Item( ZM_AUDIT_CHECK_INTERVAL );\ forced_alarm_score = (int)config.Item( ZM_FORCED_ALARM_SCORE );\ bulk_frame_interval = (int)config.Item( ZM_BULK_FRAME_INTERVAL );\ event_close_mode = (const char *)config.Item( ZM_EVENT_CLOSE_MODE );\ force_close_events = (bool)config.Item( ZM_FORCE_CLOSE_EVENTS );\ create_analysis_images = (bool)config.Item( ZM_CREATE_ANALYSIS_IMAGES );\ weighted_alarm_centres = (bool)config.Item( ZM_WEIGHTED_ALARM_CENTRES );\ event_image_digits = (int)config.Item( ZM_EVENT_IMAGE_DIGITS );\ default_aspect_ratio = (const char *)config.Item( ZM_DEFAULT_ASPECT_RATIO );\ user_self_edit = (bool)config.Item( ZM_USER_SELF_EDIT );\ opt_frame_server = (bool)config.Item( ZM_OPT_FRAME_SERVER );\ frame_socket_size = (int)config.Item( ZM_FRAME_SOCKET_SIZE );\ opt_control = (bool)config.Item( ZM_OPT_CONTROL );\ opt_triggers = (bool)config.Item( ZM_OPT_TRIGGERS );\ check_for_updates = (bool)config.Item( ZM_CHECK_FOR_UPDATES );\ update_check_proxy = (const char *)config.Item( ZM_UPDATE_CHECK_PROXY );\ shm_key = (int)config.Item( ZM_SHM_KEY );\ web_refresh_method = (const char *)config.Item( ZM_WEB_REFRESH_METHOD );\ web_event_sort_field = (const char *)config.Item( ZM_WEB_EVENT_SORT_FIELD );\ web_event_sort_order = (const char *)config.Item( ZM_WEB_EVENT_SORT_ORDER );\ web_events_per_page = (int)config.Item( ZM_WEB_EVENTS_PER_PAGE );\ web_list_thumbs = (bool)config.Item( ZM_WEB_LIST_THUMBS );\ web_list_thumb_width = (int)config.Item( ZM_WEB_LIST_THUMB_WIDTH );\ web_list_thumb_height = (int)config.Item( ZM_WEB_LIST_THUMB_HEIGHT );\ web_use_object_tags = (bool)config.Item( ZM_WEB_USE_OBJECT_TAGS );\ web_h_refresh_main = (int)config.Item( ZM_WEB_H_REFRESH_MAIN );\ web_h_refresh_cycle = (int)config.Item( ZM_WEB_H_REFRESH_CYCLE );\ web_h_refresh_image = (int)config.Item( ZM_WEB_H_REFRESH_IMAGE );\ web_h_refresh_status = (int)config.Item( ZM_WEB_H_REFRESH_STATUS );\ web_h_refresh_events = (int)config.Item( ZM_WEB_H_REFRESH_EVENTS );\ web_h_can_stream = (const char *)config.Item( ZM_WEB_H_CAN_STREAM );\ web_h_stream_method = (const char *)config.Item( ZM_WEB_H_STREAM_METHOD );\ web_h_default_scale = (int)config.Item( ZM_WEB_H_DEFAULT_SCALE );\ web_h_default_rate = (int)config.Item( ZM_WEB_H_DEFAULT_RATE );\ web_h_video_bitrate = (int)config.Item( ZM_WEB_H_VIDEO_BITRATE );\ web_h_video_maxfps = (int)config.Item( ZM_WEB_H_VIDEO_MAXFPS );\ web_h_scale_thumbs = (bool)config.Item( ZM_WEB_H_SCALE_THUMBS );\ web_h_events_view = (const char *)config.Item( ZM_WEB_H_EVENTS_VIEW );\ web_h_show_progress = (bool)config.Item( ZM_WEB_H_SHOW_PROGRESS );\ web_h_ajax_timeout = (int)config.Item( ZM_WEB_H_AJAX_TIMEOUT );\ web_m_refresh_main = (int)config.Item( ZM_WEB_M_REFRESH_MAIN );\ web_m_refresh_cycle = (int)config.Item( ZM_WEB_M_REFRESH_CYCLE );\ web_m_refresh_image = (int)config.Item( ZM_WEB_M_REFRESH_IMAGE );\ web_m_refresh_status = (int)config.Item( ZM_WEB_M_REFRESH_STATUS );\ web_m_refresh_events = (int)config.Item( ZM_WEB_M_REFRESH_EVENTS );\ web_m_can_stream = (const char *)config.Item( ZM_WEB_M_CAN_STREAM );\ web_m_stream_method = (const char *)config.Item( ZM_WEB_M_STREAM_METHOD );\ web_m_default_scale = (int)config.Item( ZM_WEB_M_DEFAULT_SCALE );\ web_m_default_rate = (int)config.Item( ZM_WEB_M_DEFAULT_RATE );\ web_m_video_bitrate = (int)config.Item( ZM_WEB_M_VIDEO_BITRATE );\ web_m_video_maxfps = (int)config.Item( ZM_WEB_M_VIDEO_MAXFPS );\ web_m_scale_thumbs = (bool)config.Item( ZM_WEB_M_SCALE_THUMBS );\ web_m_events_view = (const char *)config.Item( ZM_WEB_M_EVENTS_VIEW );\ web_m_show_progress = (bool)config.Item( ZM_WEB_M_SHOW_PROGRESS );\ web_m_ajax_timeout = (int)config.Item( ZM_WEB_M_AJAX_TIMEOUT );\ web_l_refresh_main = (int)config.Item( ZM_WEB_L_REFRESH_MAIN );\ web_l_refresh_cycle = (int)config.Item( ZM_WEB_L_REFRESH_CYCLE );\ web_l_refresh_image = (int)config.Item( ZM_WEB_L_REFRESH_IMAGE );\ web_l_refresh_status = (int)config.Item( ZM_WEB_L_REFRESH_STATUS );\ web_l_refresh_events = (int)config.Item( ZM_WEB_L_REFRESH_EVENTS );\ web_l_can_stream = (const char *)config.Item( ZM_WEB_L_CAN_STREAM );\ web_l_stream_method = (const char *)config.Item( ZM_WEB_L_STREAM_METHOD );\ web_l_default_scale = (int)config.Item( ZM_WEB_L_DEFAULT_SCALE );\ web_l_default_rate = (int)config.Item( ZM_WEB_L_DEFAULT_RATE );\ web_l_video_bitrate = (int)config.Item( ZM_WEB_L_VIDEO_BITRATE );\ web_l_video_maxfps = (int)config.Item( ZM_WEB_L_VIDEO_MAXFPS );\ web_l_scale_thumbs = (bool)config.Item( ZM_WEB_L_SCALE_THUMBS );\ web_l_events_view = (const char *)config.Item( ZM_WEB_L_EVENTS_VIEW );\ web_l_show_progress = (bool)config.Item( ZM_WEB_L_SHOW_PROGRESS );\ web_l_ajax_timeout = (int)config.Item( ZM_WEB_L_AJAX_TIMEOUT );\ web_p_can_stream = (const char *)config.Item( ZM_WEB_P_CAN_STREAM );\ web_p_stream_method = (const char *)config.Item( ZM_WEB_P_STREAM_METHOD );\ web_p_default_scale = (int)config.Item( ZM_WEB_P_DEFAULT_SCALE );\ web_p_default_rate = (int)config.Item( ZM_WEB_P_DEFAULT_RATE );\ web_p_video_bitrate = (int)config.Item( ZM_WEB_P_VIDEO_BITRATE );\ web_p_video_maxfps = (int)config.Item( ZM_WEB_P_VIDEO_MAXFPS );\ web_p_scale_thumbs = (bool)config.Item( ZM_WEB_P_SCALE_THUMBS );\ web_p_ajax_timeout = (int)config.Item( ZM_WEB_P_AJAX_TIMEOUT );\ dyn_last_version = (const char *)config.Item( ZM_DYN_LAST_VERSION );\ dyn_curr_version = (const char *)config.Item( ZM_DYN_CURR_VERSION );\ dyn_db_version = (const char *)config.Item( ZM_DYN_DB_VERSION );\ dyn_last_check = (int)config.Item( ZM_DYN_LAST_CHECK );\ dyn_next_reminder = (const char *)config.Item( ZM_DYN_NEXT_REMINDER );\ dyn_donate_reminder_time = (int)config.Item( ZM_DYN_DONATE_REMINDER_TIME );\ dyn_show_donate_reminder = (bool)config.Item( ZM_DYN_SHOW_DONATE_REMINDER );\ eyezm_debug = (bool)config.Item( ZM_EYEZM_DEBUG );\ eyezm_log_to_file = (bool)config.Item( ZM_EYEZM_LOG_TO_FILE );\ eyezm_log_file = (const char *)config.Item( ZM_EYEZM_LOG_FILE );\ eyezm_event_vcodec = (const char *)config.Item( ZM_EYEZM_EVENT_VCODEC );\ eyezm_feed_vcodec = (const char *)config.Item( ZM_EYEZM_FEED_VCODEC );\ eyezm_h264_default_br = (const char *)config.Item( ZM_EYEZM_H264_DEFAULT_BR );\ eyezm_h264_default_evbr = (const char *)config.Item( ZM_EYEZM_H264_DEFAULT_EVBR );\ eyezm_h264_timeout = (const char *)config.Item( ZM_EYEZM_H264_TIMEOUT );\ eyezm_seg_duration = (const char *)config.Item( ZM_EYEZM_SEG_DURATION );\ ZoneMinder-1.26.5/src/zm_coord.cpp000066400000000000000000000016241225361755400170030ustar00rootroot00000000000000// // ZoneMinder Coordinate Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" #include "zm_coord.h" // This section deliberately left blank ZoneMinder-1.26.5/src/zm_coord.h000066400000000000000000000047001225361755400164460ustar00rootroot00000000000000// // ZoneMinder Coordinate Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_COORD_H #define ZM_COORD_H #include "zm.h" // // Class used for storing an x,y pair, i.e. a coordinate // class Coord { private: int x, y; public: inline Coord() : x(0), y(0) { } inline Coord( int p_x, int p_y ) : x(p_x), y(p_y) { } inline Coord( const Coord &p_coord ) : x(p_coord.x), y(p_coord.y) { } inline int &X() { return( x ); } inline const int &X() const { return( x ); } inline int &Y() { return( y ); } inline const int &Y() const { return( y ); } inline static Coord Range( const Coord &coord1, const Coord &coord2 ) { Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 ); return( result ); } inline bool operator==( const Coord &coord ) { return( x == coord.x && y == coord.y ); } inline bool operator!=( const Coord &coord ) { return( x != coord.x || y != coord.y ); } inline bool operator>( const Coord &coord ) { return( x > coord.x && y > coord.y ); } inline bool operator>=( const Coord &coord ) { return( !(operator<(coord)) ); } inline bool operator<( const Coord &coord ) { return( x < coord.x && y < coord.y ); } inline bool operator<=( const Coord &coord ) { return( !(operator>(coord)) ); } inline Coord &operator+=( const Coord &coord ) { x += coord.x; y += coord.y; return( *this ); } inline Coord &operator-=( const Coord &coord ) { x -= coord.x; y -= coord.y; return( *this ); } inline friend Coord operator+( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result += coord2; return( result ); } inline friend Coord operator-( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result -= coord2; return( result ); } }; #endif // ZM_COORD_H ZoneMinder-1.26.5/src/zm_db.cpp000066400000000000000000000044431225361755400162640ustar00rootroot00000000000000// // ZoneMinder MySQL Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include #include #include "zm.h" #include "zm_db.h" MYSQL dbconn; int zmDbConnected = false; void zmDbConnect() { if ( !mysql_init( &dbconn ) ) { Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } my_bool reconnect = 1; if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) ) Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) ); std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" ); if ( colonIndex != std::string::npos ) { std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex ); std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 ); if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) ) { Error( "Can't connect to server: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } } else { if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) ) { Error( "Can't connect to server: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } } if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) ) { Error( "Can't select database: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } zmDbConnected = true; } ZoneMinder-1.26.5/src/zm_db.h000066400000000000000000000020401225361755400157200ustar00rootroot00000000000000// // ZoneMinder Core Interfaces, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_DB_H #define ZM_DB_H #include #ifdef __cplusplus extern "C" { #endif extern MYSQL dbconn; extern int zmDbConnected; void zmDbConnect(); #ifdef __cplusplus } /* extern "C" */ #endif #endif // ZM_DB_H ZoneMinder-1.26.5/src/zm_event.cpp000066400000000000000000001370311225361755400170200ustar00rootroot00000000000000// // ZoneMinder Event Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include #include #include #include #include #include #include #include #include #include #include "zm.h" #include "zm_db.h" #include "zm_time.h" #include "zm_mpeg.h" #include "zm_signal.h" #include "zm_event.h" #include "zm_monitor.h" #include "zmf.h" #if HAVE_SYS_SENDFILE_H #include #endif //#define USE_PREPARED_SQL 1 bool Event::initialised = false; char Event::capture_file_format[PATH_MAX]; char Event::analyse_file_format[PATH_MAX]; char Event::general_file_format[PATH_MAX]; int Event::pre_alarm_count = 0; Event::PreAlarmData Event::pre_alarm_data[MAX_PRE_ALARM_FRAMES] = { { 0 } }; Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap ) : monitor( p_monitor ), start_time( p_start_time ), cause( p_cause ), noteSetMap( p_noteSetMap ) { if ( !initialised ) Initialise(); std::string notes; createNotes( notes ); bool untimedEvent = false; if ( !start_time.tv_sec ) { untimedEvent = true; gettimeofday( &start_time, 0 ); } static char sql[ZM_SQL_MED_BUFSIZ]; struct tm *stime = localtime( &start_time.tv_sec ); snprintf( sql, sizeof(sql), "insert into Events ( MonitorId, Name, StartTime, Width, Height, Cause, Notes ) values ( %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s' )", monitor->Id(), start_time.tv_sec, monitor->Width(), monitor->Height(), cause.c_str(), notes.c_str() ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't insert event: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } id = mysql_insert_id( &dbconn ); if ( untimedEvent ) { Warning( "Event %d has zero time, setting to current", id ); } end_time.tv_sec = 0; frames = 0; alarm_frames = 0; tot_score = 0; max_score = 0; if ( config.use_deep_storage ) { char *path_ptr = path; path_ptr += snprintf( path_ptr, sizeof(path), "%s/%d", config.dir_events, monitor->Id() ); int dt_parts[6]; dt_parts[0] = stime->tm_year-100; dt_parts[1] = stime->tm_mon+1; dt_parts[2] = stime->tm_mday; dt_parts[3] = stime->tm_hour; dt_parts[4] = stime->tm_min; dt_parts[5] = stime->tm_sec; char date_path[PATH_MAX] = ""; char time_path[PATH_MAX] = ""; char *time_path_ptr = time_path; for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) { path_ptr += snprintf( path_ptr, sizeof(path)-(path_ptr-path), "/%02d", dt_parts[i] ); struct stat statbuf; errno = 0; stat( path, &statbuf ); if ( errno == ENOENT || errno == ENOTDIR ) { if ( mkdir( path, 0755 ) ) { Fatal( "Can't mkdir %s: %s", path, strerror(errno)); } } if ( i == 2 ) strncpy( date_path, path, sizeof(date_path) ); else if ( i >= 3 ) time_path_ptr += snprintf( time_path_ptr, sizeof(time_path)-(time_path_ptr-time_path), "%s%02d", i>3?"/":"", dt_parts[i] ); } char id_file[PATH_MAX]; // Create event id symlink snprintf( id_file, sizeof(id_file), "%s/.%d", date_path, id ); if ( symlink( time_path, id_file ) < 0 ) Fatal( "Can't symlink %s -> %s: %s", id_file, path, strerror(errno)); // Create empty id tag file snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); if ( FILE *id_fp = fopen( id_file, "w" ) ) fclose( id_fp ); else Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); } else { snprintf( path, sizeof(path), "%s/%d/%d", config.dir_events, monitor->Id(), id ); struct stat statbuf; errno = 0; stat( path, &statbuf ); if ( errno == ENOENT || errno == ENOTDIR ) { if ( mkdir( path, 0755 ) ) { Error( "Can't mkdir %s: %s", path, strerror(errno)); } } char id_file[PATH_MAX]; // Create empty id tag file snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); if ( FILE *id_fp = fopen( id_file, "w" ) ) fclose( id_fp ); else Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); } last_db_frame = 0; } Event::~Event() { if ( frames > last_db_frame ) { struct DeltaTimeval delta_time; DELTA_TIMEVAL( delta_time, end_time, start_time, DT_PREC_2 ); Debug( 1, "Adding closing frame %d to DB", frames ); static char sql[ZM_SQL_SML_BUFSIZ]; snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld )", id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } } static char sql[ZM_SQL_MED_BUFSIZ]; struct DeltaTimeval delta_time; DELTA_TIMEVAL( delta_time, end_time, start_time, DT_PREC_2 ); snprintf( sql, sizeof(sql), "update Events set Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't update event: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } } void Event::createNotes( std::string ¬es ) { notes.clear(); for ( StringSetMap::const_iterator mapIter = noteSetMap.begin(); mapIter != noteSetMap.end(); mapIter++ ) { notes += mapIter->first; notes += ": "; const StringSet &stringSet = mapIter->second; for ( StringSet::const_iterator setIter = stringSet.begin(); setIter != stringSet.end(); setIter++ ) { if ( setIter != stringSet.begin() ) notes += ", "; notes += *setIter; } } } int Event::sd = -1; bool Event::OpenFrameSocket( int monitor_id ) { if ( sd > 0 ) { close( sd ); } sd = socket( AF_UNIX, SOCK_STREAM, 0); if ( sd < 0 ) { Error( "Can't create socket: %s", strerror(errno) ); return( false ); } int socket_buffer_size = config.frame_socket_size; if ( socket_buffer_size > 0 ) { if ( setsockopt( sd, SOL_SOCKET, SO_SNDBUF, &socket_buffer_size, sizeof(socket_buffer_size) ) < 0 ) { Error( "Can't get socket buffer size to %d, error = %s", socket_buffer_size, strerror(errno) ); close( sd ); sd = -1; return( false ); } } int flags; if ( (flags = fcntl( sd, F_GETFL )) < 0 ) { Error( "Can't get socket flags, error = %s", strerror(errno) ); close( sd ); sd = -1; return( false ); } flags |= O_NONBLOCK; if ( fcntl( sd, F_SETFL, flags ) < 0 ) { Error( "Can't set socket flags, error = %s", strerror(errno) ); close( sd ); sd = -1; return( false ); } char sock_path[PATH_MAX] = ""; snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id ); struct sockaddr_un addr; strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) ); addr.sun_family = AF_UNIX; if ( connect( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 ) { Warning( "Can't connect to frame server: %s", strerror(errno) ); close( sd ); sd = -1; return( false ); } Debug( 1, "Opened connection to frame server" ); return( true ); } bool Event::ValidateFrameSocket( int monitor_id ) { if ( sd < 0 ) { return( OpenFrameSocket( monitor_id ) ); } return( true ); } bool Event::SendFrameImage( const Image *image, bool alarm_frame ) { if ( !ValidateFrameSocket( monitor->Id() ) ) { return( false ); } static int jpg_buffer_size = 0; static unsigned char jpg_buffer[ZM_MAX_IMAGE_SIZE]; image->EncodeJpeg( jpg_buffer, &jpg_buffer_size, (alarm_frame&&(config.jpeg_alarm_file_quality>config.jpeg_file_quality))?config.jpeg_alarm_file_quality:config.jpeg_file_quality ); static FrameHeader frame_header; frame_header.event_id = id; if ( config.use_deep_storage ) frame_header.event_time = start_time.tv_sec; frame_header.frame_id = frames; frame_header.alarm_frame = alarm_frame; frame_header.image_length = jpg_buffer_size; struct iovec iovecs[2]; iovecs[0].iov_base = &frame_header; iovecs[0].iov_len = sizeof(frame_header); iovecs[1].iov_base = jpg_buffer; iovecs[1].iov_len = jpg_buffer_size; ssize_t writev_size = sizeof(frame_header)+jpg_buffer_size; ssize_t writev_result = writev( sd, iovecs, sizeof(iovecs)/sizeof(*iovecs)); if ( writev_result != writev_size ) { if ( writev_result < 0 ) { if ( errno == EAGAIN ) { Warning( "Blocking write detected" ); } else { Error( "Can't write frame: %s", strerror(errno) ); close( sd ); sd = -1; } } else { Error( "Incomplete frame write: %zd of %zd bytes written", writev_result, writev_size ); close( sd ); sd = -1; } return( false ); } Debug( 1, "Wrote frame image, %d bytes", jpg_buffer_size ); return( true ); } bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame ) { if ( config.timestamp_on_capture ) { if ( !config.opt_frame_server || !SendFrameImage( image, alarm_frame) ) { if ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) image->WriteJpeg( event_file, config.jpeg_alarm_file_quality ); else image->WriteJpeg( event_file ); } } else { Image ts_image( *image ); monitor->TimestampImage( &ts_image, ×tamp ); if ( !config.opt_frame_server || !SendFrameImage( &ts_image, alarm_frame) ) { if ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) ts_image.WriteJpeg( event_file, config.jpeg_alarm_file_quality ); else ts_image.WriteJpeg( event_file ); } } return( true ); } void Event::updateNotes( const StringSetMap &newNoteSetMap ) { bool update = false; //Info( "Checking notes, %d <> %d", noteSetMap.size(), newNoteSetMap.size() ); if ( newNoteSetMap.size() > 0 ) { if ( noteSetMap.size() == 0 ) { noteSetMap = newNoteSetMap; update = true; } else { for ( StringSetMap::const_iterator newNoteSetMapIter = newNoteSetMap.begin(); newNoteSetMapIter != newNoteSetMap.end(); newNoteSetMapIter++ ) { const std::string &newNoteGroup = newNoteSetMapIter->first; const StringSet &newNoteSet = newNoteSetMapIter->second; //Info( "Got %d new strings", newNoteSet.size() ); if ( newNoteSet.size() > 0 ) { StringSetMap::iterator noteSetMapIter = noteSetMap.find( newNoteGroup ); if ( noteSetMapIter == noteSetMap.end() ) { //Info( "Can't find note group %s, copying %d strings", newNoteGroup.c_str(), newNoteSet.size() ); noteSetMap.insert( StringSetMap::value_type( newNoteGroup, newNoteSet ) ); update = true; } else { StringSet ¬eSet = noteSetMapIter->second; //Info( "Found note group %s, got %d strings", newNoteGroup.c_str(), newNoteSet.size() ); for ( StringSet::const_iterator newNoteSetIter = newNoteSet.begin(); newNoteSetIter != newNoteSet.end(); newNoteSetIter++ ) { const std::string &newNote = *newNoteSetIter; StringSet::iterator noteSetIter = noteSet.find( newNote ); if ( noteSetIter == noteSet.end() ) { noteSet.insert( newNote ); update = true; } } } } } } } if ( update ) { std::string notes; createNotes( notes ); Debug( 2, "Updating notes for event %d, '%s'", id, notes.c_str() ); static char sql[ZM_SQL_MED_BUFSIZ]; #if USE_PREPARED_SQL static MYSQL_STMT *stmt = 0; char notesStr[ZM_SQL_MED_BUFSIZ] = ""; unsigned long notesLen = 0; if ( !stmt ) { const char *sql = "update Events set Notes = ? where Id = ?"; stmt = mysql_stmt_init( &dbconn ); if ( mysql_stmt_prepare( stmt, sql, strlen(sql) ) ) { Fatal( "Unable to prepare sql '%s': %s", sql, mysql_stmt_error(stmt) ); } /* Get the parameter count from the statement */ if ( mysql_stmt_param_count( stmt ) != 2 ) { Fatal( "Unexpected parameter count %ld in sql '%s'", mysql_stmt_param_count( stmt ), sql ); } MYSQL_BIND bind[2]; memset(bind, 0, sizeof(bind)); /* STRING PARAM */ bind[0].buffer_type = MYSQL_TYPE_STRING; bind[0].buffer = (char *)notesStr; bind[0].buffer_length = sizeof(notesStr); bind[0].is_null = 0; bind[0].length = ¬esLen; bind[1].buffer_type= MYSQL_TYPE_LONG; bind[1].buffer= (char *)&id; bind[1].is_null= 0; bind[1].length= 0; /* Bind the buffers */ if ( mysql_stmt_bind_param( stmt, bind ) ) { Fatal( "Unable to bind sql '%s': %s", sql, mysql_stmt_error(stmt) ); } } strncpy( notesStr, notes.c_str(), sizeof(notesStr) ); notesLen = notes.length(); if ( mysql_stmt_execute( stmt ) ) { Fatal( "Unable to execute sql '%s': %s", sql, mysql_stmt_error(stmt) ); } #else static char escapedNotes[ZM_SQL_MED_BUFSIZ]; mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() ); snprintf( sql, sizeof(sql), "update Events set Notes = '%s' where Id = %d", escapedNotes, id ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't insert event: %s", mysql_error( &dbconn ) ); } #endif } } void Event::AddFrames( int n_frames, Image **images, struct timeval **timestamps ) { for (int i = 0; i < n_frames; i += ZM_SQL_BATCH_SIZE) { AddFramesInternal(n_frames, i, images, timestamps); } } void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ) { static char sql[ZM_SQL_LGE_BUFSIZ]; strncpy( sql, "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ", sizeof(sql) ); int frameCount = 0; for ( int i = start_frame; i < n_frames && i - start_frame < ZM_SQL_BATCH_SIZE; i++ ) { if ( !timestamps[i]->tv_sec ) { Debug( 1, "Not adding pre-capture frame %d, zero timestamp", i ); continue; } frames++; static char event_file[PATH_MAX]; snprintf( event_file, sizeof(event_file), capture_file_format, path, frames ); Debug( 1, "Writing pre-capture frame %d", frames ); WriteFrameImage( images[i], *(timestamps[i]), event_file ); struct DeltaTimeval delta_time; DELTA_TIMEVAL( delta_time, *(timestamps[i]), start_time, DT_PREC_2 ); int sql_len = strlen(sql); snprintf( sql+sql_len, sizeof(sql)-sql_len, "( %d, %d, from_unixtime(%ld), %s%ld.%02ld ), ", id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); frameCount++; } if ( frameCount ) { Debug( 1, "Adding %d/%d frames to DB", frameCount, n_frames ); *(sql+strlen(sql)-2) = '\0'; if ( mysql_query( &dbconn, sql ) ) { Error( "Can't insert frames: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } last_db_frame = frames; } else { Debug( 1, "No valid pre-capture frames to add" ); } } void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *alarm_image ) { if ( !timestamp.tv_sec ) { Debug( 1, "Not adding new frame, zero timestamp" ); return; } frames++; static char event_file[PATH_MAX]; snprintf( event_file, sizeof(event_file), capture_file_format, path, frames ); Debug( 1, "Writing capture frame %d", frames ); WriteFrameImage( image, timestamp, event_file ); struct DeltaTimeval delta_time; DELTA_TIMEVAL( delta_time, timestamp, start_time, DT_PREC_2 ); bool db_frame = (score>=0) || ((frames%config.bulk_frame_interval)==0) || !frames; if ( db_frame ) { const char *frame_type = score>0?"Alarm":(score<0?"Bulk":"Normal"); Debug( 1, "Adding frame %d to DB", frames ); static char sql[ZM_SQL_MED_BUFSIZ]; snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) values ( %d, %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", id, frames, frame_type, timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } last_db_frame = frames; // We are writing a bulk frame if ( score < 0 ) { snprintf( sql, sizeof(sql), "update Events set Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't update event: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } } } end_time = timestamp; if ( score > 0 ) { alarm_frames++; tot_score += score; if ( score > (int)max_score ) max_score = score; if ( alarm_image ) { snprintf( event_file, sizeof(event_file), analyse_file_format, path, frames ); Debug( 1, "Writing analysis frame %d", frames ); WriteFrameImage( alarm_image, timestamp, event_file, true ); } } /* This makes viewing the diagnostic images impossible because it keeps deleting them if ( config.record_diag_images ) { char diag_glob[PATH_MAX] = ""; snprintf( diag_glob, sizeof(diag_glob), "%s/%d/diag-*.jpg", config.dir_events, monitor->Id() ); glob_t pglob; int glob_status = glob( diag_glob, 0, 0, &pglob ); if ( glob_status != 0 ) { if ( glob_status < 0 ) { Error( "Can't glob '%s': %s", diag_glob, strerror(errno) ); } else { Debug( 1, "Can't glob '%s': %d", diag_glob, glob_status ); } } else { char new_diag_path[PATH_MAX] = ""; for ( int i = 0; i < pglob.gl_pathc; i++ ) { char *diag_path = pglob.gl_pathv[i]; char *diag_file = strstr( diag_path, "diag-" ); if ( diag_file ) { snprintf( new_diag_path, sizeof(new_diag_path), general_file_format, path, frames, diag_file ); if ( rename( diag_path, new_diag_path ) < 0 ) { Error( "Can't rename '%s' to '%s': %s", diag_path, new_diag_path, strerror(errno) ); } } } } globfree( &pglob ); } */ } bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) { static char sql[ZM_SQL_SML_BUFSIZ]; snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %d and unix_timestamp( EndTime ) > %ld order by Id asc limit 1", monitor_id, event_time ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } MYSQL_RES *result = mysql_store_result( &dbconn ); if ( !result ) { Error( "Can't use query result: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } MYSQL_ROW dbrow = mysql_fetch_row( result ); if ( mysql_errno( &dbconn ) ) { Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } int init_event_id = atoi( dbrow[0] ); mysql_free_result( result ); loadEventData( init_event_id ); if ( event_time ) { curr_stream_time = event_time; curr_frame_id = 1; if ( event_time >= event_data->start_time ) { for (unsigned int i = 0; i < event_data->frame_count; i++ ) { //Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time ); if ( event_data->frames[i].timestamp >= event_time ) { curr_frame_id = i+1; Debug( 3, "Set cst:%.2f", curr_stream_time ); Debug( 3, "Set cfid:%d", curr_frame_id ); break; } } Debug( 3, "Skipping %ld frames", event_data->frame_count ); } } return( true ); } bool EventStream::loadInitialEventData( int init_event_id, int init_frame_id ) { loadEventData( init_event_id ); if ( init_frame_id ) { curr_stream_time = event_data->frames[init_frame_id-1].timestamp; curr_frame_id = init_frame_id; } else { curr_stream_time = event_data->start_time; } return( true ); } bool EventStream::loadEventData( int event_id ) { static char sql[ZM_SQL_MED_BUFSIZ]; snprintf( sql, sizeof(sql), "select M.Id, M.Name, E.Frames, unix_timestamp( StartTime ) as StartTimestamp, max(F.Delta)-min(F.Delta) as Duration from Events as E inner join Monitors as M on E.MonitorId = M.Id inner join Frames as F on E.Id = F.EventId where E.Id = %d group by E.Id", event_id ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } MYSQL_RES *result = mysql_store_result( &dbconn ); if ( !result ) { Error( "Can't use query result: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } if ( !mysql_num_rows( result ) ) { Fatal( "Unable to load event %d, not found in DB", event_id ); } MYSQL_ROW dbrow = mysql_fetch_row( result ); if ( mysql_errno( &dbconn ) ) { Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } delete event_data; event_data = new EventData; event_data->event_id = event_id; event_data->monitor_id = atoi( dbrow[0] ); event_data->start_time = atoi(dbrow[3]); if ( config.use_deep_storage ) { struct tm *event_time = localtime( &event_data->start_time ); if ( config.dir_events[0] == '/' ) snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", config.dir_events, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); else snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", staticConfig.PATH_WEB.c_str(), config.dir_events, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); } else { if ( config.dir_events[0] == '/' ) snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%ld", config.dir_events, event_data->monitor_id, event_data->event_id ); else snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%ld", staticConfig.PATH_WEB.c_str(), config.dir_events, event_data->monitor_id, event_data->event_id ); } event_data->frame_count = atoi(dbrow[2]); event_data->duration = atof(dbrow[4]); updateFrameRate( (double)event_data->frame_count/event_data->duration ); mysql_free_result( result ); snprintf( sql, sizeof(sql), "select FrameId, unix_timestamp( `TimeStamp` ), Delta from Frames where EventId = %d order by FrameId asc", event_id ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } result = mysql_store_result( &dbconn ); if ( !result ) { Error( "Can't use query result: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } event_data->n_frames = mysql_num_rows( result ); event_data->frames = new FrameData[event_data->frame_count]; int id, last_id = 0; time_t timestamp, last_timestamp = event_data->start_time; double delta, last_delta = 0.0; while ( ( dbrow = mysql_fetch_row( result ) ) ) { id = atoi(dbrow[0]); timestamp = atoi(dbrow[1]); delta = atof(dbrow[2]); int id_diff = id - last_id; double frame_delta = (delta-last_delta)/id_diff; if ( id_diff > 1 ) { for ( int i = last_id+1; i < id; i++ ) { event_data->frames[i-1].timestamp = (time_t)(last_timestamp + ((i-last_id)*frame_delta)); event_data->frames[i-1].offset = (time_t)(event_data->frames[i-1].timestamp-event_data->start_time); event_data->frames[i-1].delta = frame_delta; event_data->frames[i-1].in_db = false; } } event_data->frames[id-1].timestamp = timestamp; event_data->frames[id-1].offset = (time_t)(event_data->frames[id-1].timestamp-event_data->start_time); event_data->frames[id-1].delta = id>1?frame_delta:0.0; event_data->frames[id-1].in_db = true; last_id = id; last_delta = delta; last_timestamp = timestamp; } if ( mysql_errno( &dbconn ) ) { Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } //for ( int i = 0; i < 250; i++ ) //{ //Info( "%d -> %d @ %f (%d)", i+1, event_data->frames[i].timestamp, event_data->frames[i].delta, event_data->frames[i].in_db ); //} mysql_free_result( result ); if ( forceEventChange || mode == MODE_ALL_GAPLESS ) { if ( replay_rate > 0 ) curr_stream_time = event_data->frames[0].timestamp; else curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp; } Debug( 2, "Event:%ld, Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration ); return( true ); } void EventStream::processCommand( const CmdMsg *msg ) { Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ) // Check for incoming command switch( (MsgCommand)msg->msg_data[0] ) { case CMD_PAUSE : { Debug( 1, "Got PAUSE command" ); // Set paused flag paused = true; replay_rate = ZM_RATE_BASE; last_frame_sent = TV_2_FLOAT( now ); break; } case CMD_PLAY : { Debug( 1, "Got PLAY command" ); if ( paused ) { // Clear paused flag paused = false; } replay_rate = ZM_RATE_BASE; break; } case CMD_VARPLAY : { Debug( 1, "Got VARPLAY command" ); if ( paused ) { // Clear paused flag paused = false; } replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; break; } case CMD_STOP : { Debug( 1, "Got STOP command" ); // Clear paused flag paused = false; break; } case CMD_FASTFWD : { Debug( 1, "Got FAST FWD command" ); if ( paused ) { // Clear paused flag paused = false; } // Set play rate switch ( replay_rate ) { case 2 * ZM_RATE_BASE : replay_rate = 5 * ZM_RATE_BASE; break; case 5 * ZM_RATE_BASE : replay_rate = 10 * ZM_RATE_BASE; break; case 10 * ZM_RATE_BASE : replay_rate = 25 * ZM_RATE_BASE; break; case 25 * ZM_RATE_BASE : case 50 * ZM_RATE_BASE : replay_rate = 50 * ZM_RATE_BASE; break; default : replay_rate = 2 * ZM_RATE_BASE; break; } break; } case CMD_SLOWFWD : { Debug( 1, "Got SLOW FWD command" ); // Set paused flag paused = true; // Set play rate replay_rate = ZM_RATE_BASE; // Set step step = 1; break; } case CMD_SLOWREV : { Debug( 1, "Got SLOW REV command" ); // Set paused flag paused = true; // Set play rate replay_rate = ZM_RATE_BASE; // Set step step = -1; break; } case CMD_FASTREV : { Debug( 1, "Got FAST REV command" ); if ( paused ) { // Clear paused flag paused = false; } // Set play rate switch ( replay_rate ) { case -2 * ZM_RATE_BASE : replay_rate = -5 * ZM_RATE_BASE; break; case -5 * ZM_RATE_BASE : replay_rate = -10 * ZM_RATE_BASE; break; case -10 * ZM_RATE_BASE : replay_rate = -25 * ZM_RATE_BASE; break; case -25 * ZM_RATE_BASE : case -50 * ZM_RATE_BASE : replay_rate = -50 * ZM_RATE_BASE; break; default : replay_rate = -2 * ZM_RATE_BASE; break; } break; } case CMD_ZOOMIN : { x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; Debug( 1, "Got ZOOM IN command, to %d,%d", x, y ); switch ( zoom ) { case 100: zoom = 150; break; case 150: zoom = 200; break; case 200: zoom = 300; break; case 300: zoom = 400; break; case 400: default : zoom = 500; break; } break; } case CMD_ZOOMOUT : { Debug( 1, "Got ZOOM OUT command" ); switch ( zoom ) { case 500: zoom = 400; break; case 400: zoom = 300; break; case 300: zoom = 200; break; case 200: zoom = 150; break; case 150: default : zoom = 100; break; } break; } case CMD_PAN : { x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; Debug( 1, "Got PAN command, to %d,%d", x, y ); break; } case CMD_SCALE : { scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; Debug( 1, "Got SCALE command, to %d", scale ); break; } case CMD_PREV : { Debug( 1, "Got PREV command" ); if ( replay_rate >= 0 ) curr_frame_id = 0; else curr_frame_id = event_data->frame_count-1; paused = false; forceEventChange = true; break; } case CMD_NEXT : { Debug( 1, "Got NEXT command" ); if ( replay_rate >= 0 ) curr_frame_id = event_data->frame_count-1; else curr_frame_id = 0; paused = false; forceEventChange = true; break; } case CMD_SEEK : { int offset = ((unsigned char)msg->msg_data[1]<<24)|((unsigned char)msg->msg_data[2]<<16)|((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; curr_frame_id = (int)(event_data->frame_count*offset/event_data->duration); Debug( 1, "Got SEEK command, to %d (new cfid: %d)", offset, curr_frame_id ); break; } case CMD_QUERY : { Debug( 1, "Got QUERY command, sending STATUS" ); break; } default : { // Do nothing, for now } } struct { int event; int progress; int rate; int zoom; bool paused; } status_data; status_data.event = event_data->event_id; status_data.progress = (int)event_data->frames[curr_frame_id-1].offset; status_data.rate = replay_rate; status_data.zoom = zoom; status_data.paused = paused; Debug( 2, "E:%d, P:%d, p:%d R:%d, Z:%d", status_data.event, status_data.paused, status_data.progress, status_data.rate, status_data.zoom ); DataMsg status_msg; status_msg.msg_type = MSG_DATA_EVENT; memcpy( &status_msg.msg_data, &status_data, sizeof(status_msg.msg_data) ); if ( sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) ) < 0 ) { //if ( errno != EAGAIN ) { Error( "Can't sendto on sd %d: %s", sd, strerror(errno) ); exit( -1 ); } } updateFrameRate( (double)event_data->frame_count/event_data->duration ); } void EventStream::checkEventLoaded() { bool reload_event = false; static char sql[ZM_SQL_SML_BUFSIZ]; if ( curr_frame_id <= 0 ) { snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id < %ld order by Id desc limit 1", event_data->monitor_id, event_data->event_id ); reload_event = true; } else if ( (unsigned int)curr_frame_id > event_data->frame_count ) { snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id > %ld order by Id asc limit 1", event_data->monitor_id, event_data->event_id ); reload_event = true; } if ( reload_event ) { if ( forceEventChange || mode != MODE_SINGLE ) { //Info( "SQL:%s", sql ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } MYSQL_RES *result = mysql_store_result( &dbconn ); if ( !result ) { Error( "Can't use query result: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } MYSQL_ROW dbrow = mysql_fetch_row( result ); if ( mysql_errno( &dbconn ) ) { Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } if ( dbrow ) { int event_id = atoi(dbrow[0]); Debug( 1, "Loading new event %d", event_id ); loadEventData( event_id ); Debug( 2, "Current frame id = %d", curr_frame_id ); // When loading a new event, always set the current frame id to the first frame rather than the last // if ( curr_frame_id <= 0 ) // curr_frame_id = event_data->frame_count; // else curr_frame_id = 1; Debug( 2, "New frame id = %d", curr_frame_id ); } else { if ( curr_frame_id <= 0 ) curr_frame_id = 1; else curr_frame_id = event_data->frame_count; paused = true; } mysql_free_result( result ); forceEventChange = false; } else { if ( curr_frame_id <= 0 ) curr_frame_id = 1; else curr_frame_id = event_data->frame_count; paused = true; } } } bool EventStream::sendFrame( int delta_us ) { Debug( 2, "Sending frame %d", curr_frame_id ); static char filepath[PATH_MAX]; static struct stat filestat; FILE *fdj = NULL; snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id ); #if HAVE_LIBAVCODEC if ( type == STREAM_MPEG ) { Image image( filepath ); Image *send_image = prepareImage( &image ); if ( !vid_stream ) { vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() ); fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); vid_stream->OpenStream(); } /* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_us*1000 ); } else #endif // HAVE_LIBAVCODEC { static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; int img_buffer_size = 0; uint8_t *img_buffer = temp_img_buffer; bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); fprintf( stdout, "--ZoneMinderFrame\r\n" ); if ( type != STREAM_JPEG ) send_raw = false; if ( send_raw ) { fdj = fopen( filepath, "rb" ); if ( !fdj ) { Error( "Can't open %s: %s", filepath, strerror(errno) ); return( false ); } #if HAVE_SENDFILE if( fstat(fileno(fdj),&filestat) < 0 ) { Error( "Failed getting information about file %s: %s", filepath, strerror(errno) ); return( false ); } #else img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); #endif } else { Image image( filepath ); Image *send_image = prepareImage( &image ); switch( type ) { case STREAM_JPEG : send_image->EncodeJpeg( img_buffer, &img_buffer_size ); break; case STREAM_ZIP : #if HAVE_ZLIB_H unsigned long zip_buffer_size; send_image->Zip( img_buffer, &zip_buffer_size ); img_buffer_size = zip_buffer_size; break; #else Error("zlib is required for zipped images. Falling back to raw image"); type = STREAM_RAW; #endif // HAVE_ZLIB_H case STREAM_RAW : img_buffer = (uint8_t*)(send_image->Buffer()); img_buffer_size = send_image->Size(); break; default: Fatal( "Unexpected frame type %d", type ); break; } } switch( type ) { case STREAM_JPEG : fprintf( stdout, "Content-Type: image/jpeg\r\n" ); break; case STREAM_RAW : fprintf( stdout, "Content-Type: image/x-rgb\r\n" ); break; case STREAM_ZIP : fprintf( stdout, "Content-Type: image/x-rgbz\r\n" ); break; default : Fatal( "Unexpected frame type %d", type ); break; } if(send_raw) { #if HAVE_SENDFILE fprintf( stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size ); if(sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size) { /* sendfile() failed, use standard way instead */ img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); return( false ); } } #else fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); return( false ); } #endif fclose(fdj); /* Close the file handle */ } else { fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { Error( "Unable to send stream frame: %s", strerror(errno) ); return( false ); } } fprintf( stdout, "\r\n\r\n" ); fflush( stdout ); } last_frame_sent = TV_2_FLOAT( now ); return( true ); } void EventStream::runStream() { Event::Initialise(); openComms(); checkInitialised(); updateFrameRate( (double)event_data->frame_count/event_data->duration ); if ( type == STREAM_JPEG ) fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" ); if ( !event_data ) { sendTextFrame( "No event data found" ); exit( 0 ); } unsigned int delta_us = 0; while( !zm_terminate ) { gettimeofday( &now, NULL ); while(checkCommandQueue()); if ( step != 0 ) curr_frame_id += step; checkEventLoaded(); // Get current frame data FrameData *frame_data = &event_data->frames[curr_frame_id-1]; //Info( "cst:%.2f", curr_stream_time ); //Info( "cfid:%d", curr_frame_id ); //Info( "fdt:%d", frame_data->timestamp ); if ( !paused ) { bool in_event = true; double time_to_event = 0; if ( replay_rate > 0 ) { time_to_event = event_data->frames[0].timestamp - curr_stream_time; if ( time_to_event > 0 ) in_event = false; } else if ( replay_rate < 0 ) { time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp; if ( time_to_event > 0 ) in_event = false; } if ( !in_event ) { double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; if ( actual_delta_time > 1 ) { static char frame_text[64]; snprintf( frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event ); if ( !sendTextFrame( frame_text ) ) zm_terminate = true; } //else //{ usleep( STREAM_PAUSE_WAIT ); //curr_stream_time += (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000)); curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); //} continue; } } // Figure out if we should send this frame bool send_frame = false; if ( !paused ) { // If we are streaming and this frame is due to be sent if ( ((curr_frame_id-1)%frame_mod) == 0 ) { delta_us = (unsigned int)(frame_data->delta * 1000000); if ( effective_fps < base_fps ) delta_us = (unsigned int)((delta_us * base_fps)/effective_fps); send_frame = true; } } else if ( step != 0 ) { // We are paused and are just stepping forward or backward one frame step = 0; send_frame = true; } else { // We are paused, and doing nothing double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; if ( actual_delta_time > MAX_STREAM_DELAY ) { // Send keepalive Debug( 2, "Sending keepalive frame" ); send_frame = true; } } if ( send_frame ) if ( !sendFrame( delta_us ) ) zm_terminate = true; curr_stream_time = frame_data->timestamp; if ( !paused ) { curr_frame_id += replay_rate>0?1:-1; if ( send_frame && type != STREAM_MPEG ) { Debug( 3, "dUs: %d", delta_us ); usleep( delta_us ); } } else { usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) ); } } #if HAVE_LIBAVCODEC if ( type == STREAM_MPEG ) delete vid_stream; #endif // HAVE_LIBAVCODEC closeComms(); } ZoneMinder-1.26.5/src/zm_event.h000066400000000000000000000161221225361755400164620ustar00rootroot00000000000000// // ZoneMinder Core Interfaces, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_EVENT_H #define ZM_EVENT_H #include #include #include #include #include #include #include #include #include #include #include #include #include "zm.h" #include "zm_image.h" #include "zm_stream.h" class Zone; class Monitor; #define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored // // Class describing events, i.e. captured periods of activity. // class Event { friend class EventStream; protected: static bool initialised; static char capture_file_format[PATH_MAX]; static char analyse_file_format[PATH_MAX]; static char general_file_format[PATH_MAX]; protected: static int sd; public: typedef std::set StringSet; typedef std::map StringSetMap; protected: typedef enum { NORMAL, BULK, ALARM } FrameType; struct PreAlarmData { Image *image; struct timeval timestamp; unsigned int score; Image *alarm_frame; }; static int pre_alarm_count; static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES]; protected: unsigned int id; Monitor *monitor; struct timeval start_time; struct timeval end_time; std::string cause; StringSetMap noteSetMap; int frames; int alarm_frames; unsigned int tot_score; unsigned int max_score; char path[PATH_MAX]; protected: int last_db_frame; protected: static void Initialise() { if ( initialised ) return; snprintf( capture_file_format, sizeof(capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits ); snprintf( analyse_file_format, sizeof(analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits ); snprintf( general_file_format, sizeof(general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits ); initialised = true; } void createNotes( std::string ¬es ); public: static bool OpenFrameSocket( int ); static bool ValidateFrameSocket( int ); public: Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap ); ~Event(); int Id() const { return( id ); } const std::string &Cause() { return( cause ); } int Frames() const { return( frames ); } int AlarmFrames() const { return( alarm_frames ); } const struct timeval &StartTime() const { return( start_time ); } const struct timeval &EndTime() const { return( end_time ); } struct timeval &EndTime() { return( end_time ); } bool SendFrameImage( const Image *image, bool alarm_frame=false ); bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false ); void updateNotes( const StringSetMap &stringSetMap ); void AddFrames( int n_frames, Image **images, struct timeval **timestamps ); void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ); private: void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ); public: static const char *getSubPath( struct tm *time ) { static char subpath[PATH_MAX] = ""; snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec ); return( subpath ); } static const char *getSubPath( time_t *time ) { return( Event::getSubPath( localtime( time ) ) ); } public: static int PreAlarmCount() { return( pre_alarm_count ); } static void EmptyPreAlarmFrames() { if ( pre_alarm_count > 0 ) { for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ ) { delete pre_alarm_data[i].image; delete pre_alarm_data[i].alarm_frame; } memset( pre_alarm_data, 0, sizeof(pre_alarm_data) ); } pre_alarm_count = 0; } static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ) { pre_alarm_data[pre_alarm_count].image = new Image( *image ); pre_alarm_data[pre_alarm_count].timestamp = timestamp; pre_alarm_data[pre_alarm_count].score = score; if ( alarm_frame ) { pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame ); } pre_alarm_count++; } void SavePreAlarmFrames() { for ( int i = 0; i < pre_alarm_count; i++ ) { AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame ); } EmptyPreAlarmFrames(); } }; class EventStream : public StreamBase { public: typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode; protected: struct FrameData { //unsigned long id; time_t timestamp; time_t offset; double delta; bool in_db; }; struct EventData { unsigned long event_id; unsigned long monitor_id; unsigned long frame_count; time_t start_time; double duration; char path[PATH_MAX]; int n_frames; FrameData *frames; }; protected: static const int STREAM_PAUSE_WAIT = 250000; // Microseconds static const StreamMode DEFAULT_MODE = MODE_SINGLE; protected: StreamMode mode; bool forceEventChange; protected: int curr_frame_id; double curr_stream_time; EventData *event_data; protected: bool loadEventData( int event_id ); bool loadInitialEventData( int init_event_id, int init_frame_id ); bool loadInitialEventData( int monitor_id, time_t event_time ); void checkEventLoaded(); void processCommand( const CmdMsg *msg ); bool sendFrame( int delta_us ); public: EventStream() { mode = DEFAULT_MODE; forceEventChange = false; curr_frame_id = 0; curr_stream_time = 0.0; event_data = 0; } void setStreamStart( int init_event_id, int init_frame_id=0 ) { loadInitialEventData( init_event_id, init_frame_id ); loadMonitor( event_data->monitor_id ); } void setStreamStart( int monitor_id, time_t event_time ) { loadInitialEventData( monitor_id, event_time ); loadMonitor( monitor_id ); } void setStreamMode( StreamMode p_mode ) { mode = p_mode; } void runStream(); }; #endif // ZM_EVENT_H ZoneMinder-1.26.5/src/zm_exception.cpp000066400000000000000000000015501225361755400176710ustar00rootroot00000000000000// // ZoneMinder Exception Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm_exception.h" // Blank ZoneMinder-1.26.5/src/zm_exception.h000066400000000000000000000032371225361755400173420ustar00rootroot00000000000000// // ZoneMinder Exception Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_EXCEPTION_H #define ZM_EXCEPTION_H #include "zm.h" #include class Exception { protected: typedef enum { INFO, WARNING, ERROR, FATAL } Severity; protected: std::string mMessage; Severity mSeverity; public: Exception( const std::string &message, Severity severity=ERROR ) : mMessage( message ), mSeverity( severity ) { } public: const std::string &getMessage() const { return( mMessage ); } Severity getSeverity() const { return( mSeverity ); } bool isInfo() const { return( mSeverity == INFO ); } bool isWarning() const { return( mSeverity == WARNING ); } bool isError() const { return( mSeverity == ERROR ); } bool isFatal() const { return( mSeverity == FATAL ); } }; #endif // ZM_EXCEPTION_H ZoneMinder-1.26.5/src/zm_ffmpeg.cpp000066400000000000000000000151631225361755400171440ustar00rootroot00000000000000/* * ZoneMinder FFMPEG implementation, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "zm_ffmpeg.h" #include "zm_image.h" #include "zm_rgb.h" #if HAVE_LIBAVCODEC || HAVE_LIBAVUTIL || HAVE_LIBSWSCALE #if HAVE_LIBAVUTIL enum PixelFormat GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder) { enum PixelFormat pf; Debug(8,"Colours: %d SubpixelOrder: %d",p_colours,p_subpixelorder); switch(p_colours) { case ZM_COLOUR_RGB24: { if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) { /* BGR subpixel order */ pf = PIX_FMT_BGR24; } else { /* Assume RGB subpixel order */ pf = PIX_FMT_RGB24; } break; } case ZM_COLOUR_RGB32: { if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) { /* ARGB subpixel order */ pf = PIX_FMT_ARGB; } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) { /* ABGR subpixel order */ pf = PIX_FMT_ABGR; } else if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) { /* BGRA subpixel order */ pf = PIX_FMT_BGRA; } else { /* Assume RGBA subpixel order */ pf = PIX_FMT_RGBA; } break; } case ZM_COLOUR_GRAY8: pf = PIX_FMT_GRAY8; break; default: Panic("Unexpected colours: %d",p_colours); pf = PIX_FMT_GRAY8; /* Just to shush gcc variable may be unused warning */ break; } return pf; } #endif // HAVE_LIBAVUTIL #if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL SWScale::SWScale() : gotdefaults(false), swscale_ctx(NULL), input_avframe(NULL), output_avframe(NULL) { Debug(4,"SWScale object created"); /* Allocate AVFrame for the input */ input_avframe = avcodec_alloc_frame(); if(input_avframe == NULL) { Fatal("Failed allocating AVFrame for the input"); } /* Allocate AVFrame for the output */ output_avframe = avcodec_alloc_frame(); if(output_avframe == NULL) { Fatal("Failed allocating AVFrame for the output"); } } SWScale::~SWScale() { /* Free up everything */ av_free(input_avframe); input_avframe = NULL; av_free(output_avframe); output_avframe = NULL; if(swscale_ctx) { sws_freeContext(swscale_ctx); swscale_ctx = NULL; } Debug(4,"SWScale object destroyed"); } int SWScale::SetDefaults(enum PixelFormat in_pf, enum PixelFormat out_pf, unsigned int width, unsigned int height) { /* Assign the defaults */ default_input_pf = in_pf; default_output_pf = out_pf; default_width = width; default_height = height; gotdefaults = true; return 0; } int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum PixelFormat in_pf, enum PixelFormat out_pf, unsigned int width, unsigned int height) { /* Parameter checking */ if(in_buffer == NULL || out_buffer == NULL) { Error("NULL Input or output buffer"); return -1; } if(in_pf == 0 || out_pf == 0) { Error("Invalid input or output pixel formats"); return -2; } if(!width || !height) { Error("Invalid width or height"); return -3; } /* Warn if the input or output pixelformat is not supported */ if(!sws_isSupportedInput(in_pf)) { Warning("swscale does not support the input format: %c%c%c%c",(in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff)); } if(!sws_isSupportedOutput(out_pf)) { Warning("swscale does not support the output format: %c%c%c%c",(out_pf)&0xff,((out_pf>>8)&0xff),((out_pf>>16)&0xff),((out_pf>>24)&0xff)); } /* Check the buffer sizes */ size_t insize = avpicture_get_size(in_pf, width, height); if(insize != in_buffer_size) { Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size); return -4; } size_t outsize = avpicture_get_size(out_pf, width, height); if(outsize < out_buffer_size) { Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size); return -5; } /* Get the context */ swscale_ctx = sws_getCachedContext( NULL, width, height, in_pf, width, height, out_pf, 0, NULL, NULL, NULL ); if(swscale_ctx == NULL) { Error("Failed getting swscale context"); return -6; } /* Fill in the buffers */ if(!avpicture_fill( (AVPicture*)input_avframe, (uint8_t*)in_buffer, in_pf, width, height ) ) { Error("Failed filling input frame with input buffer"); return -7; } if(!avpicture_fill( (AVPicture*)output_avframe, out_buffer, out_pf, width, height ) ) { Error("Failed filling output frame with output buffer"); return -8; } /* Do the conversion */ if(!sws_scale(swscale_ctx, input_avframe->data, input_avframe->linesize, 0, height, output_avframe->data, output_avframe->linesize ) ) { Error("swscale conversion failed"); return -10; } return 0; } int SWScale::Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum PixelFormat in_pf, enum PixelFormat out_pf, unsigned int width, unsigned int height) { if(img->Width() != width) { Error("Source image width differs. Source: %d Output: %d",img->Width(), width); return -12; } if(img->Height() != height) { Error("Source image height differs. Source: %d Output: %d",img->Height(), height); return -13; } return Convert(img->Buffer(),img->Size(),out_buffer,out_buffer_size,in_pf,out_pf,width,height); } int SWScale::ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size) { if(!gotdefaults) { Error("Defaults are not set"); return -24; } return Convert(img,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height); } int SWScale::ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size) { if(!gotdefaults) { Error("Defaults are not set"); return -24; } return Convert(in_buffer,in_buffer_size,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height); } #endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL #endif // HAVE_LIBAVCODEC || HAVE_LIBAVUTIL || HAVE_LIBSWSCALE ZoneMinder-1.26.5/src/zm_ffmpeg.h000066400000000000000000000077411225361755400166140ustar00rootroot00000000000000/* * ZoneMinder FFMPEG Interface, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef ZM_FFMPEG_H #define ZM_FFMPEG_H #include #include "zm.h" #include "zm_image.h" #ifdef __cplusplus extern "C" { #endif // AVUTIL #if HAVE_LIBAVUTIL_AVUTIL_H #include #include #include #elif HAVE_FFMPEG_AVUTIL_H #include #include #include #endif // AVCODEC #if HAVE_LIBAVCODEC_AVCODEC_H #include #elif HAVE_FFMPEG_AVCODEC_H #include #endif // AVFORMAT #if HAVE_LIBAVFORMAT_AVFORMAT_H #include #elif HAVE_FFMPEG_AVFORMAT_H #include #endif // AVDEVICE #if HAVE_LIBAVDEVICE_AVDEVICE_H #include #elif HAVE_FFMPEG_AVDEVICE_H #include #endif // SWSCALE #if HAVE_LIBSWSCALE_SWSCALE_H #include #elif HAVE_FFMPEG_SWSCALE_H #include #endif #ifdef __cplusplus } #endif #if ( HAVE_LIBAVUTIL_AVUTIL_H || HAVE_LIBAVCODEC_AVCODEC_H || HAVE_LIBAVFORMAT_AVFORMAT_H || HAVE_LIBAVDEVICE_AVDEVICE_H ) #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0) #if defined(AVIO_WRONLY) #define AVIO_FLAG_WRITE AVIO_WRONLY #else #define AVIO_FLAG_WRITE URL_WRONLY #endif #endif #if FFMPEG_VERSION_INT == 0x000408 #define ZM_FFMPEG_048 1 #elif FFMPEG_VERSION_INT == 0x000409 #if LIBAVCODEC_VERSION_INT < ((50<<16)+(0<<8)+0) #define ZM_FFMPEG_049 1 #else // LIBAVCODEC_VERSION_INT #define ZM_FFMPEG_SVN 1 #endif // LIBAVCODEC_VERSION_INT #else // FFMPEG_VERSION_INT #define ZM_FFMPEG_SVN 1 #endif // FFMPEG_VERSION_INT /* Fix for not having SWS_CPU_CAPS_SSE2 defined */ #ifndef SWS_CPU_CAPS_SSE2 #define SWS_CPU_CAPS_SSE2 0x02000000 #endif #if HAVE_LIBAVUTIL enum PixelFormat GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder); #endif // HAVE_LIBAVUTIL /* SWScale wrapper class to make our life easier and reduce code reuse */ #if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL class SWScale { public: SWScale(); ~SWScale(); int SetDefaults(enum PixelFormat in_pf, enum PixelFormat out_pf, unsigned int width, unsigned int height); int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size); int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size); int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum PixelFormat in_pf, enum PixelFormat out_pf, unsigned int width, unsigned int height); int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum PixelFormat in_pf, enum PixelFormat out_pf, unsigned int width, unsigned int height); protected: bool gotdefaults; struct SwsContext* swscale_ctx; AVFrame* input_avframe; AVFrame* output_avframe; enum PixelFormat default_input_pf; enum PixelFormat default_output_pf; unsigned int default_width; unsigned int default_height; }; #endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL #endif // ( HAVE_LIBAVUTIL_AVUTIL_H || HAVE_LIBAVCODEC_AVCODEC_H || HAVE_LIBAVFORMAT_AVFORMAT_H || HAVE_LIBAVDEVICE_AVDEVICE_H ) #endif // ZM_FFMPEG_H ZoneMinder-1.26.5/src/zm_ffmpeg_camera.cpp000066400000000000000000000204731225361755400204540ustar00rootroot00000000000000// // ZoneMinder Ffmpeg Camera Class Implementation, $Date: 2009-01-16 12:18:50 +0000 (Fri, 16 Jan 2009) $, $Revision: 2713 $ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" #if HAVE_LIBAVFORMAT #include "zm_ffmpeg_camera.h" FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), mPath( p_path ) { if ( capture ) { Initialise(); } mFormatContext = NULL; mVideoStreamId = -1; mCodecContext = NULL; mCodec = NULL; mRawFrame = NULL; mFrame = NULL; frameCount = 0; #if HAVE_LIBSWSCALE mConvertContext = NULL; #endif /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ if(colours == ZM_COLOUR_RGB32) { subpixelorder = ZM_SUBPIX_ORDER_RGBA; imagePixFormat = PIX_FMT_RGBA; } else if(colours == ZM_COLOUR_RGB24) { subpixelorder = ZM_SUBPIX_ORDER_RGB; imagePixFormat = PIX_FMT_RGB24; } else if(colours == ZM_COLOUR_GRAY8) { subpixelorder = ZM_SUBPIX_ORDER_NONE; imagePixFormat = PIX_FMT_GRAY8; } else { Panic("Unexpected colours: %d",colours); } } FfmpegCamera::~FfmpegCamera() { av_freep( &mFrame ); av_freep( &mRawFrame ); #if HAVE_LIBSWSCALE if ( mConvertContext ) { sws_freeContext( mConvertContext ); mConvertContext = NULL; } #endif if ( mCodecContext ) { avcodec_close( mCodecContext ); mCodecContext = NULL; // Freed by av_close_input_file } if ( mFormatContext ) { #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0) av_close_input_file( mFormatContext ); #else avformat_close_input( &mFormatContext ); #endif mFormatContext = NULL; } if ( capture ) { Terminate(); } } void FfmpegCamera::Initialise() { if ( logDebugging() ) av_log_set_level( AV_LOG_DEBUG ); else av_log_set_level( AV_LOG_QUIET ); av_register_all(); } void FfmpegCamera::Terminate() { } int FfmpegCamera::PrimeCapture() { Info( "Priming capture from %s", mPath.c_str() ); // Open the input, not necessarily a file #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0) if ( av_open_input_file( &mFormatContext, mPath.c_str(), NULL, 0, NULL ) !=0 ) #else if ( avformat_open_input( &mFormatContext, mPath.c_str(), NULL, NULL ) !=0 ) #endif Fatal( "Unable to open input %s due to: %s", mPath.c_str(), strerror(errno) ); // Locate stream info from input #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0) if ( av_find_stream_info( mFormatContext ) < 0 ) #else if ( avformat_find_stream_info( mFormatContext, 0 ) < 0 ) #endif Fatal( "Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno) ); // Find first video stream present mVideoStreamId = -1; for (unsigned int i=0; i < mFormatContext->nb_streams; i++ ) { #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) #else if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) #endif { mVideoStreamId = i; break; } } if ( mVideoStreamId == -1 ) Fatal( "Unable to locate video stream in %s", mPath.c_str() ); mCodecContext = mFormatContext->streams[mVideoStreamId]->codec; // Try and get the codec from the codec context if ( (mCodec = avcodec_find_decoder( mCodecContext->codec_id )) == NULL ) Fatal( "Can't find codec for video stream from %s", mPath.c_str() ); // Open the codec #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 7, 0) if ( avcodec_open( mCodecContext, mCodec ) < 0 ) #else if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 ) #endif Fatal( "Unable to open codec for video stream from %s", mPath.c_str() ); // Allocate space for the native video frame mRawFrame = avcodec_alloc_frame(); // Allocate space for the converted video frame mFrame = avcodec_alloc_frame(); if(mRawFrame == NULL || mFrame == NULL) Fatal( "Unable to allocate frame for %s", mPath.c_str() ); int pSize = avpicture_get_size( imagePixFormat, width, height ); if( (unsigned int)pSize != imagesize) { Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); } #if HAVE_LIBSWSCALE if(!sws_isSupportedInput(mCodecContext->pix_fmt)) { Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff)); } if(!sws_isSupportedOutput(imagePixFormat)) { Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); } #else // HAVE_LIBSWSCALE Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" ); #endif // HAVE_LIBSWSCALE return( 0 ); } int FfmpegCamera::PreCapture() { // Nothing to do here return( 0 ); } int FfmpegCamera::Capture( Image &image ) { AVPacket packet; uint8_t* directbuffer; /* Request a writeable buffer of the target image */ directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); if(directbuffer == NULL) { Error("Failed requesting writeable buffer for the captured image."); return (-1); } int frameComplete = false; while ( !frameComplete ) { int avResult = av_read_frame( mFormatContext, &packet ); if ( avResult < 0 ) { Error( "Unable to read packet from stream %d: error %d", packet.stream_index, avResult ); return( -1 ); } Debug( 5, "Got packet from stream %d", packet.stream_index ); if ( packet.stream_index == mVideoStreamId ) { if ( avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ) < 0 ) Fatal( "Unable to decode frame at frame %d", frameCount ); Debug( 4, "Decoded video packet at frame %d", frameCount ); if ( frameComplete ) { Debug( 3, "Got frame %d", frameCount ); avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height); #if HAVE_LIBSWSCALE if(mConvertContext == NULL) { if(config.cpu_extensions && sseversion >= 20) { mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC | SWS_CPU_CAPS_SSE2, NULL, NULL, NULL ); } else { mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); } if(mConvertContext == NULL) Fatal( "Unable to create conversion context for %s", mPath.c_str() ); } if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 ) Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount ); #else // HAVE_LIBSWSCALE Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" ); #endif // HAVE_LIBSWSCALE frameCount++; } } av_free_packet( &packet ); } return (0); } int FfmpegCamera::PostCapture() { // Nothing to do here return( 0 ); } #endif // HAVE_LIBAVFORMAT ZoneMinder-1.26.5/src/zm_ffmpeg_camera.h000066400000000000000000000037701225361755400201220ustar00rootroot00000000000000// // ZoneMinder Ffmpeg Class Interface, $Date: 2008-07-25 10:33:23 +0100 (Fri, 25 Jul 2008) $, $Revision: 2611 $ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_FFMPEG_CAMERA_H #define ZM_FFMPEG_CAMERA_H #include "zm_camera.h" #include "zm_buffer.h" //#include "zm_utils.h" #include "zm_ffmpeg.h" // // Class representing 'remote' cameras, i.e. those which are // accessed over a network connection. // class FfmpegCamera : public Camera { protected: std::string mPath; int frameCount; #if HAVE_LIBAVFORMAT AVFormatContext *mFormatContext; int mVideoStreamId; AVCodecContext *mCodecContext; AVCodec *mCodec; AVFrame *mRawFrame; AVFrame *mFrame; PixelFormat imagePixFormat; #endif // HAVE_LIBAVFORMAT #if HAVE_LIBSWSCALE struct SwsContext *mConvertContext; #endif public: FfmpegCamera( int p_id, const std::string &path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); ~FfmpegCamera(); const std::string &Path() const { return( mPath ); } void Initialise(); void Terminate(); int PrimeCapture(); int PreCapture(); int Capture( Image &image ); int PostCapture(); }; #endif // ZM_FFMPEG_CAMERA_H ZoneMinder-1.26.5/src/zm_file_camera.cpp000066400000000000000000000042261225361755400201250ustar00rootroot00000000000000// // ZoneMinder File Camera Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include "zm.h" #include "zm_file_camera.h" FileCamera::FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : Camera( p_id, FILE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ) { strncpy( path, p_path, sizeof(path) ); if ( capture ) { Initialise(); } } FileCamera::~FileCamera() { if ( capture ) { Terminate(); } } void FileCamera::Initialise() { if ( !path[0] ) { Error( "No path specified for file image" ); exit( -1 ); } } void FileCamera::Terminate() { } int FileCamera::PreCapture() { struct stat statbuf; if ( stat( path, &statbuf ) < 0 ) { Error( "Can't stat %s: %s", path, strerror(errno) ); return( -1 ); } while ( (time( 0 ) - statbuf.st_mtime) < 1 ) { usleep( 100000 ); } return( 0 ); } int FileCamera::Capture( Image &image ) { return( image.ReadJpeg( path, colours, subpixelorder )?0:-1 ); } int FileCamera::PostCapture() { return( 0 ); } ZoneMinder-1.26.5/src/zm_file_camera.h000066400000000000000000000027641225361755400175770ustar00rootroot00000000000000// // ZoneMinder File Camera Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_FILE_CAMERA_H #define ZM_FILE_CAMERA_H #include "zm_camera.h" #include "zm_buffer.h" #include "zm_regexp.h" #include // // Class representing 'file' cameras, i.e. those which are // accessed over a network connection. // class FileCamera : public Camera { protected: char path[PATH_MAX]; public: FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); ~FileCamera(); const char *Path() const { return( path ); } void Initialise(); void Terminate(); int PreCapture(); int Capture( Image &image ); int PostCapture(); }; #endif // ZM_FILE_CAMERA_H ZoneMinder-1.26.5/src/zm_font.h000066400000000000000000002045211225361755400163110ustar00rootroot00000000000000/**********************************************/ /* */ /* Font file generated by rthelen */ /* */ /**********************************************/ static unsigned char fontdata[] = { /* 0 0x00 '^A' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 1 0x01 '^B' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 2 0x02 '^C' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 3 0x03 '^D' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 4 0x04 '^E' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 5 0x05 '^F' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 6 0x06 '^G' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 7 0x07 '^H' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 8 0x08 '^I' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 9 0x09 '^J' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 10 0x0a '^K' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 11 0x0b '^L' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 12 0x0c '^M' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 13 0x0d '^N' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 14 0x0e '^O' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 15 0x0f '^P' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 16 0x10 '^Q' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 17 0x11 '^R' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x28, /* 00 0 000 */ 0x54, /* 0 0 0 00 */ 0x38, /* 00 000 */ 0x54, /* 0 0 0 00 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 18 0x12 '^S' */ 0x04, /* 00000 00 */ 0x04, /* 00000 00 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x50, /* 0 0 0000 */ 0x50, /* 0 0 0000 */ 0x20, /* 00 00000 */ 0x20, /* 00 00000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 19 0x13 '^T' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x38, /* 00 000 */ 0x7c, /* 0 00 */ 0x38, /* 00 000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 20 0x14 '^U' */ 0x18, /* 000 000 */ 0x10, /* 000 0000 */ 0x28, /* 00 0 000 */ 0x7c, /* 0 00 */ 0x78, /* 0 000 */ 0x78, /* 0 000 */ 0x7c, /* 0 00 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 21 0x15 '^V' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 22 0x16 '^W' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 23 0x17 '^X' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 24 0x18 '^Y' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 25 0x19 '^Z' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 26 0x1a '^[' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 27 0x1b '^\' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 28 0x1c '^]' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 29 0x1d '^^' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 30 0x1e '^_' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 31 0x1f '^`' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 32 0x20 ' ' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 33 0x21 '!' */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 34 0x22 '"' */ 0x28, /* 00 0 000 */ 0x28, /* 00 0 000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 35 0x23 '#' */ 0x00, /* 00000000 */ 0x28, /* 00 0 000 */ 0x7c, /* 0 00 */ 0x28, /* 00 0 000 */ 0x7c, /* 0 00 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 36 0x24 '$' */ 0x10, /* 000 0000 */ 0x38, /* 00 000 */ 0x54, /* 0 0 0 00 */ 0x50, /* 0 0 0000 */ 0x38, /* 00 000 */ 0x14, /* 000 0 00 */ 0x54, /* 0 0 0 00 */ 0x38, /* 00 000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 37 0x25 '%' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x54, /* 0 0 0 00 */ 0x58, /* 0 0 000 */ 0x28, /* 00 0 000 */ 0x34, /* 00 0 00 */ 0x54, /* 0 0 0 00 */ 0x48, /* 0 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 38 0x26 '&' */ 0x00, /* 00000000 */ 0x30, /* 00 0000 */ 0x48, /* 0 00 000 */ 0x50, /* 0 0 0000 */ 0x20, /* 00 00000 */ 0x54, /* 0 0 0 00 */ 0x48, /* 0 00 000 */ 0x34, /* 00 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 39 0x27 ''' */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 40 0x28 '(' */ 0x04, /* 00000 00 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x08, /* 0000 000 */ 0x04, /* 00000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 41 0x29 ')' */ 0x20, /* 00 00000 */ 0x10, /* 000 0000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x20, /* 00 00000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 42 0x2a '*' */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x54, /* 0 0 0 00 */ 0x38, /* 00 000 */ 0x54, /* 0 0 0 00 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 43 0x2b '+' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x7c, /* 0 00 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 44 0x2c ',' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x30, /* 00 0000 */ 0x30, /* 00 0000 */ 0x10, /* 000 0000 */ 0x20, /* 00 00000 */ 0x00, /* 00000000 */ /* 45 0x2d '-' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 46 0x2e '.' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 000 000 */ 0x18, /* 000 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 47 0x2f '/' */ 0x04, /* 00000 00 */ 0x04, /* 00000 00 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x20, /* 00 00000 */ 0x20, /* 00 00000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x00, /* 00000000 */ /* 48 0x30 '0' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x4c, /* 0 00 00 */ 0x54, /* 0 0 0 00 */ 0x64, /* 0 00 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 49 0x31 '1' */ 0x00, /* 00000000 */ 0x08, /* 0000 000 */ 0x18, /* 000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x1c, /* 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 50 0x32 '2' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x04, /* 00000 00 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x20, /* 00 00000 */ 0x7c, /* 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 51 0x33 '3' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x04, /* 00000 00 */ 0x18, /* 000 000 */ 0x04, /* 00000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 52 0x34 '4' */ 0x00, /* 00000000 */ 0x08, /* 0000 000 */ 0x18, /* 000 000 */ 0x28, /* 00 0 000 */ 0x48, /* 0 00 000 */ 0x7c, /* 0 00 */ 0x08, /* 0000 000 */ 0x1c, /* 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 53 0x35 '5' */ 0x00, /* 00000000 */ 0x7c, /* 0 00 */ 0x40, /* 0 000000 */ 0x78, /* 0 000 */ 0x04, /* 00000 00 */ 0x04, /* 00000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 54 0x36 '6' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x40, /* 0 000000 */ 0x78, /* 0 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 55 0x37 '7' */ 0x00, /* 00000000 */ 0x7c, /* 0 00 */ 0x04, /* 00000 00 */ 0x04, /* 00000 00 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 56 0x38 '8' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 57 0x39 '9' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x3c, /* 00 00 */ 0x04, /* 00000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 58 0x3a ':' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x18, /* 000 000 */ 0x18, /* 000 000 */ 0x00, /* 00000000 */ 0x18, /* 000 000 */ 0x18, /* 000 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 59 0x3b ';' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x30, /* 00 0000 */ 0x30, /* 00 0000 */ 0x00, /* 00000000 */ 0x30, /* 00 0000 */ 0x30, /* 00 0000 */ 0x10, /* 000 0000 */ 0x20, /* 00 00000 */ 0x00, /* 00000000 */ /* 60 0x3c '<' */ 0x00, /* 00000000 */ 0x04, /* 00000 00 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x20, /* 00 00000 */ 0x10, /* 000 0000 */ 0x08, /* 0000 000 */ 0x04, /* 00000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 61 0x3d '=' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 0 00 */ 0x00, /* 00000000 */ 0x7c, /* 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 62 0x3e '>' */ 0x00, /* 00000000 */ 0x20, /* 00 00000 */ 0x10, /* 000 0000 */ 0x08, /* 0000 000 */ 0x04, /* 00000 00 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x20, /* 00 00000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 63 0x3f '?' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x04, /* 00000 00 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 64 0x40 '@' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x74, /* 0 0 00 */ 0x54, /* 0 0 0 00 */ 0x78, /* 0 000 */ 0x40, /* 0 000000 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 65 0x41 'A' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x7c, /* 0 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 66 0x42 'B' */ 0x00, /* 00000000 */ 0x78, /* 0 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x78, /* 0 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x78, /* 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 67 0x43 'C' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 68 0x44 'D' */ 0x00, /* 00000000 */ 0x78, /* 0 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x78, /* 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 69 0x45 'E' */ 0x00, /* 00000000 */ 0x7c, /* 0 00 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x78, /* 0 000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x7c, /* 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 70 0x46 'F' */ 0x00, /* 00000000 */ 0x7c, /* 0 00 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x78, /* 0 000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 71 0x47 'G' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x40, /* 0 000000 */ 0x4c, /* 0 00 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 72 0x48 'H' */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x7c, /* 0 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 73 0x49 'I' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 74 0x4a 'J' */ 0x00, /* 00000000 */ 0x04, /* 00000 00 */ 0x04, /* 00000 00 */ 0x04, /* 00000 00 */ 0x04, /* 00000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 75 0x4b 'K' */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x48, /* 0 00 000 */ 0x50, /* 0 0 0000 */ 0x60, /* 0 00000 */ 0x50, /* 0 0 0000 */ 0x48, /* 0 00 000 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 76 0x4c 'L' */ 0x00, /* 00000000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x7c, /* 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 77 0x4d 'M' */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x6c, /* 0 0 00 */ 0x54, /* 0 0 0 00 */ 0x54, /* 0 0 0 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 78 0x4e 'N' */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x64, /* 0 00 00 */ 0x54, /* 0 0 0 00 */ 0x4c, /* 0 00 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 79 0x4f 'O' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 80 0x50 'P' */ 0x00, /* 00000000 */ 0x78, /* 0 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x78, /* 0 000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 81 0x51 'Q' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x54, /* 0 0 0 00 */ 0x38, /* 00 000 */ 0x04, /* 00000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 82 0x52 'R' */ 0x00, /* 00000000 */ 0x78, /* 0 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x78, /* 0 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 83 0x53 'S' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x40, /* 0 000000 */ 0x38, /* 00 000 */ 0x04, /* 00000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 84 0x54 'T' */ 0x00, /* 00000000 */ 0x7c, /* 0 00 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 85 0x55 'U' */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 86 0x56 'V' */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x28, /* 00 0 000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 87 0x57 'W' */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x54, /* 0 0 0 00 */ 0x54, /* 0 0 0 00 */ 0x6c, /* 0 0 00 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 88 0x58 'X' */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x28, /* 00 0 000 */ 0x10, /* 000 0000 */ 0x28, /* 00 0 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 89 0x59 'Y' */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x28, /* 00 0 000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 90 0x5a 'Z' */ 0x00, /* 00000000 */ 0x7c, /* 0 00 */ 0x04, /* 00000 00 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x20, /* 00 00000 */ 0x40, /* 0 000000 */ 0x7c, /* 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 91 0x5b '[' */ 0x0c, /* 0000 00 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x0c, /* 0000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 92 0x5c '\' */ 0x20, /* 00 00000 */ 0x20, /* 00 00000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x04, /* 00000 00 */ 0x04, /* 00000 00 */ 0x02, /* 000000 0 */ 0x02, /* 000000 0 */ 0x00, /* 00000000 */ /* 93 0x5d ']' */ 0x30, /* 00 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x30, /* 00 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 94 0x5e '^' */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x28, /* 00 0 000 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 95 0x5f '_' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7e, /* 0 0 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 96 0x60 '`' */ 0x20, /* 00 00000 */ 0x10, /* 000 0000 */ 0x08, /* 0000 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 97 0x61 'a' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x4c, /* 0 00 00 */ 0x34, /* 00 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 98 0x62 'b' */ 0x00, /* 00000000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x78, /* 0 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x78, /* 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 99 0x63 'c' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x40, /* 0 000000 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 100 0x64 'd' */ 0x00, /* 00000000 */ 0x04, /* 00000 00 */ 0x04, /* 00000 00 */ 0x3c, /* 00 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 101 0x65 'e' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x7c, /* 0 00 */ 0x40, /* 0 000000 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 102 0x66 'f' */ 0x00, /* 00000000 */ 0x0c, /* 0000 00 */ 0x10, /* 000 0000 */ 0x38, /* 00 000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 103 0x67 'g' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x3c, /* 00 00 */ 0x04, /* 00000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ /* 104 0x68 'h' */ 0x00, /* 00000000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x78, /* 0 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 105 0x69 'i' */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x30, /* 00 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 106 0x6a 'j' */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x30, /* 00 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x60, /* 0 00000 */ 0x00, /* 00000000 */ /* 107 0x6b 'k' */ 0x00, /* 00000000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x48, /* 0 00 000 */ 0x50, /* 0 0 0000 */ 0x70, /* 0 0000 */ 0x48, /* 0 00 000 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 108 0x6c 'l' */ 0x00, /* 00000000 */ 0x30, /* 00 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 109 0x6d 'm' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x78, /* 0 000 */ 0x54, /* 0 0 0 00 */ 0x54, /* 0 0 0 00 */ 0x54, /* 0 0 0 00 */ 0x54, /* 0 0 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 110 0x6e 'n' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x58, /* 0 0 000 */ 0x64, /* 0 00 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 111 0x6f 'o' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 112 0x70 'p' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x78, /* 0 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x78, /* 0 000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x00, /* 00000000 */ /* 113 0x71 'q' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x3c, /* 00 00 */ 0x04, /* 00000 00 */ 0x04, /* 00000 00 */ 0x00, /* 00000000 */ /* 114 0x72 'r' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x58, /* 0 0 000 */ 0x64, /* 0 00 00 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 115 0x73 's' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x40, /* 0 000000 */ 0x38, /* 00 000 */ 0x04, /* 00000 00 */ 0x78, /* 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 116 0x74 't' */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x38, /* 00 000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x0c, /* 0000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 117 0x75 'u' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x4c, /* 0 00 00 */ 0x34, /* 00 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 118 0x76 'v' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x28, /* 00 0 000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 119 0x77 'w' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x54, /* 0 0 0 00 */ 0x54, /* 0 0 0 00 */ 0x54, /* 0 0 0 00 */ 0x54, /* 0 0 0 00 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 120 0x78 'x' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x28, /* 00 0 000 */ 0x10, /* 000 0000 */ 0x28, /* 00 0 000 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 121 0x79 'y' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x3c, /* 00 00 */ 0x04, /* 00000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ /* 122 0x7a 'z' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 0 00 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x20, /* 00 00000 */ 0x7c, /* 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 123 0x7b '{' */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x04, /* 00000 00 */ 0x00, /* 00000000 */ /* 124 0x7c '|' */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 125 0x7d '}' */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x20, /* 00 00000 */ 0x00, /* 00000000 */ /* 126 0x7e '~' */ 0x00, /* 00000000 */ 0x34, /* 00 0 00 */ 0x58, /* 0 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 127 0x7f '^?' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 128 0x80 '\200' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x7c, /* 0 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 129 0x81 '\201' */ 0x28, /* 00 0 000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x7c, /* 0 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 130 0x82 '\202' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 131 0x83 '\203' */ 0x10, /* 000 0000 */ 0x7c, /* 0 00 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x78, /* 0 000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x7c, /* 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 132 0x84 '\204' */ 0x58, /* 0 0 000 */ 0x44, /* 0 000 00 */ 0x64, /* 0 00 00 */ 0x54, /* 0 0 0 00 */ 0x4c, /* 0 00 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 133 0x85 '\205' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 134 0x86 '\206' */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 135 0x87 '\207' */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x4c, /* 0 00 00 */ 0x34, /* 00 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 136 0x88 '\210' */ 0x10, /* 000 0000 */ 0x08, /* 0000 000 */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x4c, /* 0 00 00 */ 0x34, /* 00 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 137 0x89 '\211' */ 0x10, /* 000 0000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x4c, /* 0 00 00 */ 0x34, /* 00 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 138 0x8a '\212' */ 0x00, /* 00000000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x4c, /* 0 00 00 */ 0x34, /* 00 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 139 0x8b '\213' */ 0x34, /* 00 0 00 */ 0x58, /* 0 0 000 */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x4c, /* 0 00 00 */ 0x34, /* 00 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 140 0x8c '\214' */ 0x18, /* 000 000 */ 0x24, /* 00 00 00 */ 0x18, /* 000 000 */ 0x3c, /* 00 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x4c, /* 0 00 00 */ 0x34, /* 00 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 141 0x8d '\215' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x3c, /* 00 00 */ 0x10, /* 000 0000 */ 0x20, /* 00 00000 */ 0x00, /* 00000000 */ /* 142 0x8e '\216' */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x7c, /* 0 00 */ 0x40, /* 0 000000 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 143 0x8f '\217' */ 0x20, /* 00 00000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x7c, /* 0 00 */ 0x40, /* 0 000000 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 144 0x90 '\220' */ 0x10, /* 000 0000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x7c, /* 0 00 */ 0x40, /* 0 000000 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 145 0x91 '\221' */ 0x00, /* 00000000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x7c, /* 0 00 */ 0x40, /* 0 000000 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 146 0x92 '\222' */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 147 0x93 '\223' */ 0x20, /* 00 00000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 148 0x94 '\224' */ 0x10, /* 000 0000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 149 0x95 '\225' */ 0x00, /* 00000000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 150 0x96 '\226' */ 0x34, /* 00 0 00 */ 0x58, /* 0 0 000 */ 0x00, /* 00000000 */ 0x58, /* 0 0 000 */ 0x64, /* 0 00 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 151 0x97 '\227' */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 152 0x98 '\230' */ 0x20, /* 00 00000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 153 0x99 '\231' */ 0x10, /* 000 0000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 154 0x9a '\232' */ 0x00, /* 00000000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 155 0x9b '\233' */ 0x34, /* 00 0 00 */ 0x58, /* 0 0 000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 156 0x9c '\234' */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x4c, /* 0 00 00 */ 0x34, /* 00 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 157 0x9d '\235' */ 0x20, /* 00 00000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x4c, /* 0 00 00 */ 0x34, /* 00 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 158 0x9e '\236' */ 0x10, /* 000 0000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x4c, /* 0 00 00 */ 0x34, /* 00 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 159 0x9f '\237' */ 0x00, /* 00000000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x4c, /* 0 00 00 */ 0x34, /* 00 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 160 0xa0 '\240' */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x38, /* 00 000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 161 0xa1 '\241' */ 0x18, /* 000 000 */ 0x24, /* 00 00 00 */ 0x24, /* 00 00 00 */ 0x18, /* 000 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 162 0xa2 '\242' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x38, /* 00 000 */ 0x54, /* 0 0 0 00 */ 0x50, /* 0 0 0000 */ 0x54, /* 0 0 0 00 */ 0x38, /* 00 000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 163 0xa3 '\243' */ 0x30, /* 00 0000 */ 0x48, /* 0 00 000 */ 0x40, /* 0 000000 */ 0x70, /* 0 0000 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x44, /* 0 000 00 */ 0x78, /* 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 164 0xa4 '\244' */ 0x44, /* 0 000 00 */ 0x24, /* 00 00 00 */ 0x50, /* 0 0 0000 */ 0x48, /* 0 00 000 */ 0x24, /* 00 00 00 */ 0x14, /* 000 0 00 */ 0x48, /* 0 00 000 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 165 0xa5 '\245' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x7c, /* 0 00 */ 0x7c, /* 0 00 */ 0x7c, /* 0 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 166 0xa6 '\246' */ 0x3c, /* 00 00 */ 0x54, /* 0 0 0 00 */ 0x54, /* 0 0 0 00 */ 0x54, /* 0 0 0 00 */ 0x3c, /* 00 00 */ 0x14, /* 000 0 00 */ 0x14, /* 000 0 00 */ 0x14, /* 000 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 167 0xa7 '\247' */ 0x18, /* 000 000 */ 0x24, /* 00 00 00 */ 0x44, /* 0 000 00 */ 0x48, /* 0 00 000 */ 0x48, /* 0 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x58, /* 0 0 000 */ 0x40, /* 0 000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 168 0xa8 '\250' */ 0x00, /* 00000000 */ 0x70, /* 0 0000 */ 0x08, /* 0000 000 */ 0x64, /* 0 00 00 */ 0x54, /* 0 0 0 00 */ 0x64, /* 0 00 00 */ 0x58, /* 0 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 169 0xa9 '\251' */ 0x00, /* 00000000 */ 0x70, /* 0 0000 */ 0x08, /* 0000 000 */ 0x34, /* 00 0 00 */ 0x44, /* 0 000 00 */ 0x34, /* 00 0 00 */ 0x08, /* 0000 000 */ 0x70, /* 0 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 170 0xaa '\252' */ 0x00, /* 00000000 */ 0x7a, /* 0 0 0 */ 0x2e, /* 00 0 0 */ 0x2e, /* 00 0 0 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 171 0xab '\253' */ 0x00, /* 00000000 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 172 0xac '\254' */ 0x00, /* 00000000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 173 0xad '\255' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x08, /* 0000 000 */ 0x7c, /* 0 00 */ 0x10, /* 000 0000 */ 0x7c, /* 0 00 */ 0x20, /* 00 00000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 174 0xae '\256' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x50, /* 0 0 0000 */ 0x50, /* 0 0 0000 */ 0x78, /* 0 000 */ 0x50, /* 0 0 0000 */ 0x50, /* 0 0 0000 */ 0x5c, /* 0 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 175 0xaf '\257' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x4c, /* 0 00 00 */ 0x54, /* 0 0 0 00 */ 0x64, /* 0 00 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 176 0xb0 '\260' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x6c, /* 0 0 00 */ 0x54, /* 0 0 0 00 */ 0x6c, /* 0 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 177 0xb1 '\261' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x7c, /* 0 00 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x7c, /* 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 178 0xb2 '\262' */ 0x00, /* 00000000 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x20, /* 00 00000 */ 0x10, /* 000 0000 */ 0x08, /* 0000 000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 179 0xb3 '\263' */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x08, /* 0000 000 */ 0x04, /* 00000 00 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x1c, /* 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 180 0xb4 '\264' */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x28, /* 00 0 000 */ 0x7c, /* 0 00 */ 0x10, /* 000 0000 */ 0x7c, /* 0 00 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 181 0xb5 '\265' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x48, /* 0 00 000 */ 0x48, /* 0 00 000 */ 0x48, /* 0 00 000 */ 0x48, /* 0 00 000 */ 0x74, /* 0 0 00 */ 0x40, /* 0 000000 */ 0x40, /* 0 000000 */ 0x00, /* 00000000 */ /* 182 0xb6 '\266' */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x08, /* 0000 000 */ 0x0c, /* 0000 00 */ 0x14, /* 000 0 00 */ 0x24, /* 00 00 00 */ 0x24, /* 00 00 00 */ 0x18, /* 000 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 183 0xb7 '\267' */ 0x00, /* 00000000 */ 0x7c, /* 0 00 */ 0x24, /* 00 00 00 */ 0x10, /* 000 0000 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x24, /* 00 00 00 */ 0x7c, /* 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 184 0xb8 '\270' */ 0x00, /* 00000000 */ 0x7c, /* 0 00 */ 0x28, /* 00 0 000 */ 0x28, /* 00 0 000 */ 0x28, /* 00 0 000 */ 0x28, /* 00 0 000 */ 0x28, /* 00 0 000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 185 0xb9 '\271' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 0 00 */ 0x28, /* 00 0 000 */ 0x28, /* 00 0 000 */ 0x28, /* 00 0 000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 186 0xba '\272' */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x60, /* 0 00000 */ 0x00, /* 00000000 */ /* 187 0xbb '\273' */ 0x00, /* 00000000 */ 0x1c, /* 000 00 */ 0x24, /* 00 00 00 */ 0x24, /* 00 00 00 */ 0x1c, /* 000 00 */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 188 0xbc '\274' */ 0x00, /* 00000000 */ 0x18, /* 000 000 */ 0x24, /* 00 00 00 */ 0x24, /* 00 00 00 */ 0x18, /* 000 000 */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 189 0xbd '\275' */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x28, /* 00 0 000 */ 0x6c, /* 0 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 190 0xbe '\276' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x54, /* 0 0 0 00 */ 0x5c, /* 0 0 00 */ 0x50, /* 0 0 0000 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 191 0xbf '\277' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x4c, /* 0 00 00 */ 0x54, /* 0 0 0 00 */ 0x64, /* 0 00 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 192 0xc0 '\300' */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x20, /* 00 00000 */ 0x40, /* 0 000000 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 193 0xc1 '\301' */ 0x00, /* 00000000 */ 0x08, /* 0000 000 */ 0x00, /* 00000000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x08, /* 0000 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 194 0xc2 '\302' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7c, /* 0 00 */ 0x04, /* 00000 00 */ 0x04, /* 00000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 195 0xc3 '\303' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x0c, /* 0000 00 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x50, /* 0 0 0000 */ 0x20, /* 00 00000 */ 0x20, /* 00 00000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 196 0xc4 '\304' */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x38, /* 00 000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x60, /* 0 00000 */ 0x00, /* 00000000 */ /* 197 0xc5 '\305' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x04, /* 00000 00 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x40, /* 0 000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 198 0xc6 '\306' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x10, /* 000 0000 */ 0x28, /* 00 0 000 */ 0x28, /* 00 0 000 */ 0x44, /* 0 000 00 */ 0x7c, /* 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 199 0xc7 '\307' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x24, /* 00 00 00 */ 0x48, /* 0 00 000 */ 0x48, /* 0 00 000 */ 0x24, /* 00 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 200 0xc8 '\310' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x48, /* 0 00 000 */ 0x24, /* 00 00 00 */ 0x24, /* 00 00 00 */ 0x48, /* 0 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 201 0xc9 '\311' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x54, /* 0 0 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 202 0xca '\312' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 203 0xcb '\313' */ 0x10, /* 000 0000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x7c, /* 0 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 204 0xcc '\314' */ 0x58, /* 0 0 000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x7c, /* 0 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 205 0xcd '\315' */ 0x58, /* 0 0 000 */ 0x38, /* 00 000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 206 0xce '\316' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x50, /* 0 0 0000 */ 0x50, /* 0 0 0000 */ 0x58, /* 0 0 000 */ 0x50, /* 0 0 0000 */ 0x50, /* 0 0 0000 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 207 0xcf '\317' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x28, /* 00 0 000 */ 0x54, /* 0 0 0 00 */ 0x5c, /* 0 0 00 */ 0x50, /* 0 0 0000 */ 0x2c, /* 00 0 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 208 0xd0 '\320' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 209 0xd1 '\321' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7e, /* 0 0 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 210 0xd2 '\322' */ 0x00, /* 00000000 */ 0x14, /* 000 0 00 */ 0x28, /* 00 0 000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 211 0xd3 '\323' */ 0x00, /* 00000000 */ 0x14, /* 000 0 00 */ 0x14, /* 000 0 00 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 212 0xd4 '\324' */ 0x00, /* 00000000 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x18, /* 000 000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 213 0xd5 '\325' */ 0x00, /* 00000000 */ 0x18, /* 000 000 */ 0x08, /* 0000 000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 214 0xd6 '\326' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x7c, /* 0 00 */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 215 0xd7 '\327' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x10, /* 000 0000 */ 0x28, /* 00 0 000 */ 0x44, /* 0 000 00 */ 0x28, /* 00 0 000 */ 0x10, /* 000 0000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 216 0xd8 '\330' */ 0x00, /* 00000000 */ 0x28, /* 00 0 000 */ 0x00, /* 00000000 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x44, /* 0 000 00 */ 0x3c, /* 00 00 */ 0x04, /* 00000 00 */ 0x38, /* 00 000 */ 0x00, /* 00000000 */ /* 217 0xd9 '\331' */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x7e, /* 0 0 */ 0x00, /* 00000000 */ 0x7e, /* 0 0 */ 0x00, /* 00000000 */ 0x7e, /* 0 0 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 218 0xda '\332' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 219 0xdb '\333' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 220 0xdc '\334' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 221 0xdd '\335' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 222 0xde '\336' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 223 0xdf '\337' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 224 0xe0 '\340' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 225 0xe1 '\341' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 226 0xe2 '\342' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 227 0xe3 '\343' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 228 0xe4 '\344' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 229 0xe5 '\345' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 230 0xe6 '\346' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 231 0xe7 '\347' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 232 0xe8 '\350' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 233 0xe9 '\351' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 234 0xea '\352' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 235 0xeb '\353' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 236 0xec '\354' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 237 0xed '\355' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 238 0xee '\356' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 239 0xef '\357' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 240 0xf0 '\360' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 241 0xf1 '\361' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 242 0xf2 '\362' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 243 0xf3 '\363' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 244 0xf4 '\364' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 245 0xf5 '\365' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 246 0xf6 '\366' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 247 0xf7 '\367' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 248 0xf8 '\370' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 249 0xf9 '\371' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 250 0xfa '\372' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 251 0xfb '\373' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 252 0xfc '\374' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 253 0xfd '\375' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 254 0xfe '\376' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ /* 255 0xff '\377' */ 0x00, /* 00000000 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x3c, /* 00 00 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ 0x00, /* 00000000 */ }; ZoneMinder-1.26.5/src/zm_image.cpp000066400000000000000000005050271225361755400167650ustar00rootroot00000000000000// // ZoneMinder Image Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" #include "zm_font.h" #include "zm_image.h" #include "zm_utils.h" #include "zm_rgb.h" #include #include bool Image::initialised = false; static unsigned char *y_table; static signed char *uv_table; static short *r_v_table; static short *g_v_table; static short *g_u_table; static short *b_u_table; __attribute__((aligned(16))) static const uint8_t movemask[16] = {0,4,8,12,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; jpeg_compress_struct *Image::jpg_ccinfo[101] = { 0 }; jpeg_decompress_struct *Image::jpg_dcinfo = 0; struct zm_error_mgr Image::jpg_err; /* Pointer to blend function. */ static blend_fptr_t fptr_blend; /* Pointer to delta8 functions */ static delta_fptr_t fptr_delta8_rgb; static delta_fptr_t fptr_delta8_bgr; static delta_fptr_t fptr_delta8_rgba; static delta_fptr_t fptr_delta8_bgra; static delta_fptr_t fptr_delta8_argb; static delta_fptr_t fptr_delta8_abgr; static delta_fptr_t fptr_delta8_gray8; /* Pointers to deinterlace_4field functions */ static deinterlace_4field_fptr_t fptr_deinterlace_4field_rgba; static deinterlace_4field_fptr_t fptr_deinterlace_4field_bgra; static deinterlace_4field_fptr_t fptr_deinterlace_4field_argb; static deinterlace_4field_fptr_t fptr_deinterlace_4field_abgr; static deinterlace_4field_fptr_t fptr_deinterlace_4field_gray8; /* Pointer to image buffer memory copy function */ imgbufcpy_fptr_t fptr_imgbufcpy; Image::Image() { if ( !initialised ) Initialise(); width = 0; height = 0; pixels = 0; colours = 0; subpixelorder = 0; size = 0; allocation = 0; buffer = 0; buffertype = 0; holdbuffer = 0; text[0] = '\0'; } Image::Image( const char *filename ) { if ( !initialised ) Initialise(); width = 0; height = 0; pixels = 0; colours = 0; subpixelorder = 0; size = 0; allocation = 0; buffer = 0; buffertype = 0; holdbuffer = 0; ReadJpeg( filename, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB); text[0] = '\0'; } Image::Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer ) { if ( !initialised ) Initialise(); width = p_width; height = p_height; pixels = width*height; colours = p_colours; subpixelorder = p_subpixelorder; size = (width*height)*colours; buffer = 0; holdbuffer = 0; if ( p_buffer ) { allocation = size; buffertype = ZM_BUFTYPE_DONTFREE; buffer = p_buffer; } else { AllocImgBuffer(size); } text[0] = '\0'; } Image::Image( const Image &p_image ) { if ( !initialised ) Initialise(); width = p_image.width; height = p_image.height; pixels = p_image.pixels; colours = p_image.colours; subpixelorder = p_image.subpixelorder; size = allocation = p_image.size; buffer = 0; holdbuffer = 0; AllocImgBuffer(allocation); (*fptr_imgbufcpy)(buffer, p_image.buffer, size); strncpy( text, p_image.text, sizeof(text) ); } Image::~Image() { DumpImgBuffer(); } void Image::Initialise() { /* Assign the blend pointer to function */ if(config.fast_image_blends) { if(config.cpu_extensions && sseversion >= 20) { fptr_blend = &sse2_fastblend; /* SSE2 fast blend */ Debug(2,"Blend: Using SSE2 fast blend function"); } else { fptr_blend = &std_fastblend; /* standard fast blend */ Debug(2,"Blend: Using fast blend function"); } } else { fptr_blend = &std_blend; Debug(2,"Blend: Using standard blend function"); } __attribute__((aligned(16))) uint8_t blend1[16] = {142,255,159,91,88,227,0,52,37,80,152,97,104,252,90,82}; __attribute__((aligned(16))) uint8_t blend2[16] = {129,56,136,96,119,149,94,29,96,176,1,144,230,203,111,172}; __attribute__((aligned(16))) uint8_t blendres[16]; __attribute__((aligned(16))) uint8_t blendexp[16] = {141,231,157,92,91,217,11,49,45,92,133,103,119,246,92,93}; /* Expected results for 12.5% blend */ (*fptr_blend)(blend1,blend2,blendres,16,12.5); /* Compare results with expected results */ for(int i=0;i<16;i++) { if(abs(blendexp[i] - blendres[i]) > 3) { Panic("Blend function failed self-test: Results differ from the expected results"); } } fptr_delta8_rgb = &std_delta8_rgb; fptr_delta8_bgr = &std_delta8_bgr; /* Assign the delta functions */ if(config.cpu_extensions) { if(sseversion >= 35) { /* SSSE3 available */ fptr_delta8_rgba = &ssse3_delta8_rgba; fptr_delta8_bgra = &ssse3_delta8_bgra; fptr_delta8_argb = &ssse3_delta8_argb; fptr_delta8_abgr = &ssse3_delta8_abgr; fptr_delta8_gray8 = &sse2_delta8_gray8; Debug(2,"Delta: Using SSSE3 delta functions"); } else if(sseversion >= 20) { /* SSE2 available */ fptr_delta8_rgba = &sse2_delta8_rgba; fptr_delta8_bgra = &sse2_delta8_bgra; fptr_delta8_argb = &sse2_delta8_argb; fptr_delta8_abgr = &sse2_delta8_abgr; /* ** On some systems, the 4 SSE2 algorithms above might be a little slower than ** the standard algorithms, especially on early Pentium 4 processors. ** In that case, comment out the 4 lines above and uncomment the 4 lines below */ // fptr_delta8_rgba = &std_delta8_rgba; // fptr_delta8_bgra = &std_delta8_bgra; // fptr_delta8_argb = &std_delta8_argb; // fptr_delta8_abgr = &std_delta8_abgr; fptr_delta8_gray8 = &sse2_delta8_gray8; Debug(2,"Delta: Using SSE2 delta functions"); } else { /* No suitable SSE version available */ fptr_delta8_rgba = &std_delta8_rgba; fptr_delta8_bgra = &std_delta8_bgra; fptr_delta8_argb = &std_delta8_argb; fptr_delta8_abgr = &std_delta8_abgr; fptr_delta8_gray8 = &std_delta8_gray8; Debug(2,"Delta: Using standard delta functions"); } } else { /* CPU extensions disabled */ fptr_delta8_rgba = &std_delta8_rgba; fptr_delta8_bgra = &std_delta8_bgra; fptr_delta8_argb = &std_delta8_argb; fptr_delta8_abgr = &std_delta8_abgr; fptr_delta8_gray8 = &std_delta8_gray8; Debug(2,"Delta: CPU extensions disabled, using standard delta functions"); } /* Use SSSE3 deinterlace functions? */ if(config.cpu_extensions && sseversion >= 35) { fptr_deinterlace_4field_rgba = &ssse3_deinterlace_4field_rgba; fptr_deinterlace_4field_bgra = &ssse3_deinterlace_4field_bgra; fptr_deinterlace_4field_argb = &ssse3_deinterlace_4field_argb; fptr_deinterlace_4field_abgr = &ssse3_deinterlace_4field_abgr; fptr_deinterlace_4field_gray8 = &ssse3_deinterlace_4field_gray8; Debug(2,"Deinterlace: Using SSSE3 delta functions"); } else { fptr_deinterlace_4field_rgba = &std_deinterlace_4field_rgba; fptr_deinterlace_4field_bgra = &std_deinterlace_4field_bgra; fptr_deinterlace_4field_argb = &std_deinterlace_4field_argb; fptr_deinterlace_4field_abgr = &std_deinterlace_4field_abgr; fptr_deinterlace_4field_gray8 = &std_deinterlace_4field_gray8; Debug(2,"Deinterlace: Using standard delta functions"); } /* Use SSE2 aligned memory copy? */ if(config.cpu_extensions && sseversion >= 20) { fptr_imgbufcpy = &sse2_aligned_memcpy; Debug(2,"Image buffer copy: Using SSE2 aligned memcpy"); } else { fptr_imgbufcpy = &memcpy; Debug(2,"Image buffer copy: Using standard memcpy"); } /* Code below relocated from zm_local_camera */ Debug( 3, "Setting up static colour tables" ); y_table = new unsigned char[256]; for ( int i = 0; i <= 255; i++ ) { unsigned char c = i; if ( c <= 16 ) y_table[c] = 0; else if ( c >= 235 ) y_table[c] = 255; else y_table[c] = (255*(c-16))/219; } uv_table = new signed char[256]; for ( int i = 0; i <= 255; i++ ) { unsigned char c = i; if ( c <= 16 ) uv_table[c] = -127; else if ( c >= 240 ) uv_table[c] = 127; else uv_table[c] = (127*(c-128))/112; } r_v_table = new short[255]; g_v_table = new short[255]; g_u_table = new short[255]; b_u_table = new short[255]; for ( int i = 0; i < 255; i++ ) { r_v_table[i] = (1402*(i-128))/1000; g_u_table[i] = (344*(i-128))/1000; g_v_table[i] = (714*(i-128))/1000; b_u_table[i] = (1772*(i-128))/1000; } initialised = true; } /* Requests a writeable buffer to the image. This is safer than buffer() because this way we can gurantee that a buffer of required size exists */ uint8_t* Image::WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder) { unsigned int newsize; if(p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32) { Error("WriteBuffer called with unexpected colours: %d",p_colours); return NULL; } if(!p_height || !p_width) { Error("WriteBuffer called with invaid width or height: %d %d",p_width,p_height); return NULL; } if(p_width != width || p_height != height || p_colours != colours || p_subpixelorder != subpixelorder) { newsize = (p_width * p_height) * p_colours; if(buffer == NULL) { AllocImgBuffer(newsize); } else { if(allocation < newsize) { if(holdbuffer) { Error("Held buffer is undersized for requested buffer"); return NULL; } else { /* Replace buffer with a bigger one */ DumpImgBuffer(); AllocImgBuffer(newsize); } } } width = p_width; height = p_height; colours = p_colours; subpixelorder = p_subpixelorder; pixels = height*width; size = newsize; } return buffer; } /* Assign an existing buffer to the image instead of copying from a source buffer. The goal is to reduce the amount of memory copying and increase efficiency and buffer reusing. */ void Image::AssignDirect( const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, uint8_t *new_buffer, const size_t buffer_size, const int p_buffertype) { if(new_buffer == NULL) { Error("Attempt to directly assign buffer from a NULL pointer"); return; } if(buffer_size < (unsigned int)((p_width*p_height)*p_colours)) { Error("Attempt to directly assign buffer from an undersized buffer of size: %zu",buffer_size); return; } if(!p_height || !p_width) { Error("Attempt to directly assign buffer with invalid width or height: %d %d",p_width,p_height); return; } if(p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32) { Error("Attempt to directly assign buffer with unexpected colours per pixel: %d",p_colours); return; } if(holdbuffer && buffer) { if((unsigned int)((p_height*p_width)*p_colours) > allocation) { Error("Held buffer is undersized for assigned buffer"); return; } else { width = p_width; height = p_height; colours = p_colours; subpixelorder = p_subpixelorder; pixels = height*width; size = pixels*colours; /* Copy into the held buffer */ if(new_buffer != buffer) (*fptr_imgbufcpy)(buffer, new_buffer, size); /* Free the new buffer */ DumpBuffer(new_buffer, p_buffertype); } } else { /* Free an existing buffer if any */ DumpImgBuffer(); width = p_width; height = p_height; colours = p_colours; subpixelorder = p_subpixelorder; pixels = height*width; size = pixels*colours; allocation = buffer_size; buffertype = p_buffertype; buffer = new_buffer; } } void Image::Assign(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, const uint8_t* new_buffer, const size_t buffer_size) { unsigned int new_size = (p_width * p_height) * p_colours; if(new_buffer == NULL) { Error("Attempt to assign buffer from a NULL pointer"); return; } if(buffer_size < new_size) { Error("Attempt to assign buffer from an undersized buffer of size: %zu",buffer_size); return; } if(!p_height || !p_width) { Error("Attempt to assign buffer with invalid width or height: %d %d",p_width,p_height); return; } if(p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32) { Error("Attempt to assign buffer with unexpected colours per pixel: %d",p_colours); return; } if ( !buffer || p_width != width || p_height != height || p_colours != colours || p_subpixelorder != subpixelorder) { if (holdbuffer && buffer) { if (new_size > allocation) { Error("Held buffer is undersized for assigned buffer"); return; } } else { if(new_size > allocation || !buffer) { DumpImgBuffer(); AllocImgBuffer(new_size); } } width = p_width; height = p_height; pixels = width*height; colours = p_colours; subpixelorder = p_subpixelorder; size = new_size; } if(new_buffer != buffer) (*fptr_imgbufcpy)(buffer, new_buffer, size); } void Image::Assign( const Image &image ) { unsigned int new_size = (image.width * image.height) * image.colours; if(image.buffer == NULL) { Error("Attempt to assign image with an empty buffer"); return; } if(image.colours != ZM_COLOUR_GRAY8 && image.colours != ZM_COLOUR_RGB24 && image.colours != ZM_COLOUR_RGB32) { Error("Attempt to assign image with unexpected colours per pixel: %d",image.colours); return; } if ( !buffer || image.width != width || image.height != height || image.colours != colours || image.subpixelorder != subpixelorder) { if (holdbuffer && buffer) { if (new_size > allocation) { Error("Held buffer is undersized for assigned buffer"); return; } } else { if(new_size > allocation || !buffer) { DumpImgBuffer(); AllocImgBuffer(new_size); } } width = image.width; height = image.height; pixels = width*height; colours = image.colours; subpixelorder = image.subpixelorder; size = new_size; } if(image.buffer != buffer) (*fptr_imgbufcpy)(buffer, image.buffer, size); } Image *Image::HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits ) { if ( colours != ZM_COLOUR_GRAY8 ) { Panic( "Attempt to highlight image edges when colours = %d", colours ); } /* Convert the colour's RGBA subpixel order into the image's subpixel order */ colour = rgb_convert(colour,p_subpixelorder); /* Create a new image of the target format */ Image *high_image = new Image( width, height, p_colours, p_subpixelorder ); uint8_t* high_buff = high_image->WriteBuffer(width, height, p_colours, p_subpixelorder); /* Set image to all black */ high_image->Clear(); unsigned int lo_x = limits?limits->Lo().X():0; unsigned int lo_y = limits?limits->Lo().Y():0; unsigned int hi_x = limits?limits->Hi().X():width-1; unsigned int hi_y = limits?limits->Hi().Y():height-1; if ( p_colours == ZM_COLOUR_GRAY8 ) { for ( unsigned int y = lo_y; y <= hi_y; y++ ) { const uint8_t* p = buffer + (y * width) + lo_x; uint8_t* phigh = high_buff + (y * width) + lo_x; for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh++ ) { bool edge = false; if ( *p ) { if ( !edge && x > 0 && !*(p-1) ) edge = true; if ( !edge && x < (width-1) && !*(p+1) ) edge = true; if ( !edge && y > 0 && !*(p-width) ) edge = true; if ( !edge && y < (height-1) && !*(p+width) ) edge = true; } if ( edge ) { *phigh = colour; } } } } else if ( p_colours == ZM_COLOUR_RGB24 ) { for ( unsigned int y = lo_y; y <= hi_y; y++ ) { const uint8_t* p = buffer + (y * width) + lo_x; uint8_t* phigh = high_buff + (((y * width) + lo_x) * 3); for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh += 3 ) { bool edge = false; if ( *p ) { if ( !edge && x > 0 && !*(p-1) ) edge = true; if ( !edge && x < (width-1) && !*(p+1) ) edge = true; if ( !edge && y > 0 && !*(p-width) ) edge = true; if ( !edge && y < (height-1) && !*(p+width) ) edge = true; } if ( edge ) { RED_PTR_RGBA(phigh) = RED_VAL_RGBA(colour); GREEN_PTR_RGBA(phigh) = GREEN_VAL_RGBA(colour); BLUE_PTR_RGBA(phigh) = BLUE_VAL_RGBA(colour); } } } } else if ( p_colours == ZM_COLOUR_RGB32 ) { for ( unsigned int y = lo_y; y <= hi_y; y++ ) { const uint8_t* p = buffer + (y * width) + lo_x; Rgb* phigh = (Rgb*)(high_buff + (((y * width) + lo_x) * 4)); for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh++ ) { bool edge = false; if ( *p ) { if ( !edge && x > 0 && !*(p-1) ) edge = true; if ( !edge && x < (width-1) && !*(p+1) ) edge = true; if ( !edge && y > 0 && !*(p-width) ) edge = true; if ( !edge && y < (height-1) && !*(p+width) ) edge = true; } if ( edge ) { *phigh = colour; } } } } return( high_image ); } bool Image::ReadRaw( const char *filename ) { FILE *infile; if ( (infile = fopen( filename, "rb" )) == NULL ) { Error( "Can't open %s: %s", filename, strerror(errno) ); return( false ); } struct stat statbuf; if ( fstat( fileno(infile), &statbuf ) < 0 ) { Error( "Can't fstat %s: %s", filename, strerror(errno) ); return( false ); } if ( statbuf.st_size != size ) { Error( "Raw file size mismatch, expected %d bytes, found %ld", size, statbuf.st_size ); return( false ); } if ( fread( buffer, size, 1, infile ) < 1 ) { Fatal( "Unable to read from '%s': %s", filename, strerror(errno) ); return( false ); } fclose( infile ); return( true ); } bool Image::WriteRaw( const char *filename ) const { FILE *outfile; if ( (outfile = fopen( filename, "wb" )) == NULL ) { Error( "Can't open %s: %s", filename, strerror(errno) ); return( false ); } if ( fwrite( buffer, size, 1, outfile ) != 1 ) { Error( "Unable to write to '%s': %s", filename, strerror(errno) ); return( false ); } fclose( outfile ); return( true ); } bool Image::ReadJpeg( const char *filename, unsigned int p_colours, unsigned int p_subpixelorder) { unsigned int new_width, new_height, new_colours, new_subpixelorder; struct jpeg_decompress_struct *cinfo = jpg_dcinfo; if ( !cinfo ) { cinfo = jpg_dcinfo = new jpeg_decompress_struct; cinfo->err = jpeg_std_error( &jpg_err.pub ); jpg_err.pub.error_exit = zm_jpeg_error_exit; jpg_err.pub.emit_message = zm_jpeg_emit_message; jpeg_create_decompress( cinfo ); } FILE *infile; if ( (infile = fopen( filename, "rb" )) == NULL ) { Error( "Can't open %s: %s", filename, strerror(errno) ); return( false ); } if ( setjmp( jpg_err.setjmp_buffer ) ) { jpeg_abort_decompress( cinfo ); fclose( infile ); return( false ); } jpeg_stdio_src( cinfo, infile ); jpeg_read_header( cinfo, TRUE ); if ( cinfo->num_components != 1 && cinfo->num_components != 3 ) { Error( "Unexpected colours when reading jpeg image: %d", colours ); jpeg_abort_decompress( cinfo ); fclose( infile ); return( false ); } /* Check if the image has at least one huffman table defined. If not, use the standard ones */ /* This is required for the MJPEG capture palette of USB devices */ if(cinfo->dc_huff_tbl_ptrs[0] == NULL) { zm_use_std_huff_tables(cinfo); } new_width = cinfo->image_width; new_height = cinfo->image_height; if ( width != new_width || height != new_height ) { Debug(9,"Image dimensions differ. Old: %ux%u New: %ux%u",width,height,new_width,new_height); } switch(p_colours) { case ZM_COLOUR_GRAY8: { cinfo->out_color_space = JCS_GRAYSCALE; new_colours = ZM_COLOUR_GRAY8; new_subpixelorder = ZM_SUBPIX_ORDER_NONE; break; } case ZM_COLOUR_RGB32: { #ifdef JCS_EXTENSIONS new_colours = ZM_COLOUR_RGB32; if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) { cinfo->out_color_space = JCS_EXT_BGRX; new_subpixelorder = ZM_SUBPIX_ORDER_BGRA; } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) { cinfo->out_color_space = JCS_EXT_XRGB; new_subpixelorder = ZM_SUBPIX_ORDER_ARGB; } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) { cinfo->out_color_space = JCS_EXT_XBGR; new_subpixelorder = ZM_SUBPIX_ORDER_ABGR; } else { /* Assume RGBA */ cinfo->out_color_space = JCS_EXT_RGBX; new_subpixelorder = ZM_SUBPIX_ORDER_RGBA; } break; #else Warning("libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead."); #endif } case ZM_COLOUR_RGB24: default: { new_colours = ZM_COLOUR_RGB24; if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) { #ifdef JCS_EXTENSIONS cinfo->out_color_space = JCS_EXT_BGR; new_subpixelorder = ZM_SUBPIX_ORDER_BGR; #else Warning("libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead."); cinfo->out_color_space = JCS_RGB; new_subpixelorder = ZM_SUBPIX_ORDER_RGB; #endif } else { /* Assume RGB */ /* #ifdef JCS_EXTENSIONS cinfo->out_color_space = JCS_EXT_RGB; #else cinfo->out_color_space = JCS_RGB; #endif */ cinfo->out_color_space = JCS_RGB; new_subpixelorder = ZM_SUBPIX_ORDER_RGB; } break; } } if(WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == NULL) { Error("Failed requesting writeable buffer for reading JPEG image."); jpeg_abort_decompress( cinfo ); fclose( infile ); return( false ); } jpeg_start_decompress( cinfo ); JSAMPROW row_pointer; /* pointer to a single row */ int row_stride = width * colours; /* physical row width in buffer */ while ( cinfo->output_scanline < cinfo->output_height ) { row_pointer = &buffer[cinfo->output_scanline * row_stride]; jpeg_read_scanlines( cinfo, &row_pointer, 1 ); } jpeg_finish_decompress( cinfo ); fclose( infile ); return( true ); } bool Image::WriteJpeg( const char *filename, int quality_override ) const { if ( config.colour_jpeg_files && colours == ZM_COLOUR_GRAY8 ) { Image temp_image( *this ); temp_image.Colourise( ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); return( temp_image.WriteJpeg( filename, quality_override ) ); } int quality = quality_override?quality_override:config.jpeg_file_quality; struct jpeg_compress_struct *cinfo = jpg_ccinfo[quality]; if ( !cinfo ) { cinfo = jpg_ccinfo[quality] = new jpeg_compress_struct; cinfo->err = jpeg_std_error( &jpg_err.pub ); jpg_err.pub.error_exit = zm_jpeg_error_exit; jpg_err.pub.emit_message = zm_jpeg_emit_message; jpeg_create_compress( cinfo ); } FILE *outfile; if ( (outfile = fopen( filename, "wb" )) == NULL ) { Error( "Can't open %s: %s", filename, strerror(errno) ); return( false ); } jpeg_stdio_dest( cinfo, outfile ); cinfo->image_width = width; /* image width and height, in pixels */ cinfo->image_height = height; switch(colours) { case ZM_COLOUR_GRAY8: { cinfo->input_components = 1; cinfo->in_color_space = JCS_GRAYSCALE; break; } case ZM_COLOUR_RGB32: { #ifdef JCS_EXTENSIONS cinfo->input_components = 4; if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { cinfo->in_color_space = JCS_EXT_BGRX; } else if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { cinfo->in_color_space = JCS_EXT_XRGB; } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { cinfo->in_color_space = JCS_EXT_XBGR; } else { /* Assume RGBA */ cinfo->in_color_space = JCS_EXT_RGBX; } #else Error("libjpeg-turbo is required for JPEG encoding directly from RGB32 source"); jpeg_abort_compress( cinfo ); fclose(outfile); return(false); #endif break; } case ZM_COLOUR_RGB24: default: { cinfo->input_components = 3; if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { #ifdef JCS_EXTENSIONS cinfo->in_color_space = JCS_EXT_BGR; #else Error("libjpeg-turbo is required for JPEG encoding directly from BGR24 source"); jpeg_abort_compress( cinfo ); fclose(outfile); return(false); #endif } else { /* Assume RGB */ /* #ifdef JCS_EXTENSIONS cinfo->out_color_space = JCS_EXT_RGB; #else cinfo->out_color_space = JCS_RGB; #endif */ cinfo->in_color_space = JCS_RGB; } break; } } jpeg_set_defaults( cinfo ); jpeg_set_quality( cinfo, quality, false ); cinfo->dct_method = JDCT_FASTEST; jpeg_start_compress( cinfo, TRUE ); if ( config.add_jpeg_comments && text[0] ) { jpeg_write_marker( cinfo, JPEG_COM, (const JOCTET *)text, strlen(text) ); } JSAMPROW row_pointer; /* pointer to a single row */ int row_stride = cinfo->image_width * colours; /* physical row width in buffer */ while ( cinfo->next_scanline < cinfo->image_height ) { row_pointer = &buffer[cinfo->next_scanline * row_stride]; jpeg_write_scanlines( cinfo, &row_pointer, 1 ); } jpeg_finish_compress( cinfo ); fclose( outfile ); return( true ); } bool Image::DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size, unsigned int p_colours, unsigned int p_subpixelorder) { unsigned int new_width, new_height, new_colours, new_subpixelorder; struct jpeg_decompress_struct *cinfo = jpg_dcinfo; if ( !cinfo ) { cinfo = jpg_dcinfo = new jpeg_decompress_struct; cinfo->err = jpeg_std_error( &jpg_err.pub ); jpg_err.pub.error_exit = zm_jpeg_error_exit; jpg_err.pub.emit_message = zm_jpeg_emit_message; jpeg_create_decompress( cinfo ); } if ( setjmp( jpg_err.setjmp_buffer ) ) { jpeg_abort_decompress( cinfo ); return( false ); } zm_jpeg_mem_src( cinfo, inbuffer, inbuffer_size ); jpeg_read_header( cinfo, TRUE ); if ( cinfo->num_components != 1 && cinfo->num_components != 3 ) { Error( "Unexpected colours when reading jpeg image: %d", colours ); jpeg_abort_decompress( cinfo ); return( false ); } /* Check if the image has at least one huffman table defined. If not, use the standard ones */ /* This is required for the MJPEG capture palette of USB devices */ if(cinfo->dc_huff_tbl_ptrs[0] == NULL) { zm_use_std_huff_tables(cinfo); } new_width = cinfo->image_width; new_height = cinfo->image_height; if ( width != new_width || height != new_height ) { Debug(9,"Image dimensions differ. Old: %ux%u New: %ux%u",width,height,new_width,new_height); } switch(p_colours) { case ZM_COLOUR_GRAY8: { cinfo->out_color_space = JCS_GRAYSCALE; new_colours = ZM_COLOUR_GRAY8; new_subpixelorder = ZM_SUBPIX_ORDER_NONE; break; } case ZM_COLOUR_RGB32: { #ifdef JCS_EXTENSIONS new_colours = ZM_COLOUR_RGB32; if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) { cinfo->out_color_space = JCS_EXT_BGRX; new_subpixelorder = ZM_SUBPIX_ORDER_BGRA; } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) { cinfo->out_color_space = JCS_EXT_XRGB; new_subpixelorder = ZM_SUBPIX_ORDER_ARGB; } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) { cinfo->out_color_space = JCS_EXT_XBGR; new_subpixelorder = ZM_SUBPIX_ORDER_ABGR; } else { /* Assume RGBA */ cinfo->out_color_space = JCS_EXT_RGBX; new_subpixelorder = ZM_SUBPIX_ORDER_RGBA; } break; #else Warning("libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead."); #endif } case ZM_COLOUR_RGB24: default: { new_colours = ZM_COLOUR_RGB24; if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) { #ifdef JCS_EXTENSIONS cinfo->out_color_space = JCS_EXT_BGR; new_subpixelorder = ZM_SUBPIX_ORDER_BGR; #else Warning("libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead."); cinfo->out_color_space = JCS_RGB; new_subpixelorder = ZM_SUBPIX_ORDER_RGB; #endif } else { /* Assume RGB */ /* #ifdef JCS_EXTENSIONS cinfo->out_color_space = JCS_EXT_RGB; #else cinfo->out_color_space = JCS_RGB; #endif */ cinfo->out_color_space = JCS_RGB; new_subpixelorder = ZM_SUBPIX_ORDER_RGB; } break; } } if(WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == NULL) { Error("Failed requesting writeable buffer for reading JPEG image."); jpeg_abort_decompress( cinfo ); return( false ); } jpeg_start_decompress( cinfo ); JSAMPROW row_pointer; /* pointer to a single row */ int row_stride = width * colours; /* physical row width in buffer */ while ( cinfo->output_scanline < cinfo->output_height ) { row_pointer = &buffer[cinfo->output_scanline * row_stride]; jpeg_read_scanlines( cinfo, &row_pointer, 1 ); } jpeg_finish_decompress( cinfo ); return( true ); } bool Image::EncodeJpeg( JOCTET *outbuffer, int *outbuffer_size, int quality_override ) const { if ( config.colour_jpeg_files && colours == ZM_COLOUR_GRAY8 ) { Image temp_image( *this ); temp_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); return( temp_image.EncodeJpeg( outbuffer, outbuffer_size, quality_override ) ); } int quality = quality_override?quality_override:config.jpeg_stream_quality; struct jpeg_compress_struct *cinfo = jpg_ccinfo[quality]; if ( !cinfo ) { cinfo = jpg_ccinfo[quality] = new jpeg_compress_struct; cinfo->err = jpeg_std_error( &jpg_err.pub ); jpg_err.pub.error_exit = zm_jpeg_error_exit; jpg_err.pub.emit_message = zm_jpeg_emit_message; jpeg_create_compress( cinfo ); } zm_jpeg_mem_dest( cinfo, outbuffer, outbuffer_size ); cinfo->image_width = width; /* image width and height, in pixels */ cinfo->image_height = height; switch(colours) { case ZM_COLOUR_GRAY8: { cinfo->input_components = 1; cinfo->in_color_space = JCS_GRAYSCALE; break; } case ZM_COLOUR_RGB32: { #ifdef JCS_EXTENSIONS cinfo->input_components = 4; if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { cinfo->in_color_space = JCS_EXT_BGRX; } else if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { cinfo->in_color_space = JCS_EXT_XRGB; } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { cinfo->in_color_space = JCS_EXT_XBGR; } else { /* Assume RGBA */ cinfo->in_color_space = JCS_EXT_RGBX; } #else Error("libjpeg-turbo is required for JPEG encoding directly from RGB32 source"); jpeg_abort_compress( cinfo ); return(false); #endif break; } case ZM_COLOUR_RGB24: default: { cinfo->input_components = 3; if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { #ifdef JCS_EXTENSIONS cinfo->in_color_space = JCS_EXT_BGR; #else Error("libjpeg-turbo is required for JPEG encoding directly from BGR24 source"); jpeg_abort_compress( cinfo ); return(false); #endif } else { /* Assume RGB */ /* #ifdef JCS_EXTENSIONS cinfo->out_color_space = JCS_EXT_RGB; #else cinfo->out_color_space = JCS_RGB; #endif */ cinfo->in_color_space = JCS_RGB; } break; } } jpeg_set_defaults( cinfo ); jpeg_set_quality( cinfo, quality, false ); cinfo->dct_method = JDCT_FASTEST; jpeg_start_compress( cinfo, TRUE ); JSAMPROW row_pointer; /* pointer to a single row */ int row_stride = cinfo->image_width * colours; /* physical row width in buffer */ while ( cinfo->next_scanline < cinfo->image_height ) { row_pointer = &buffer[cinfo->next_scanline * row_stride]; jpeg_write_scanlines( cinfo, &row_pointer, 1 ); } jpeg_finish_compress( cinfo ); return( true ); } #if HAVE_ZLIB_H bool Image::Unzip( const Bytef *inbuffer, unsigned long inbuffer_size ) { unsigned long zip_size = size; int result = uncompress( buffer, &zip_size, inbuffer, inbuffer_size ); if ( result != Z_OK ) { Error( "Unzip failed, result = %d", result ); return( false ); } if ( zip_size != (unsigned int)size ) { Error( "Unzip failed, size mismatch, expected %d bytes, got %ld", size, zip_size ); return( false ); } return( true ); } bool Image::Zip( Bytef *outbuffer, unsigned long *outbuffer_size, int compression_level ) const { int result = compress2( outbuffer, outbuffer_size, buffer, size, compression_level ); if ( result != Z_OK ) { Error( "Zip failed, result = %d", result ); return( false ); } return( true ); } #endif // HAVE_ZLIB_H bool Image::Crop( unsigned int lo_x, unsigned int lo_y, unsigned int hi_x, unsigned int hi_y ) { unsigned int new_width = (hi_x-lo_x)+1; unsigned int new_height = (hi_y-lo_y)+1; if ( lo_x > hi_x || lo_y > hi_y ) { Error( "Invalid or reversed crop region %d,%d -> %d,%d", lo_x, lo_y, hi_x, hi_y ); return( false ); } if ( lo_x < 0 || hi_x > (width-1) || ( lo_y < 0 || hi_y > (height-1) ) ) { Error( "Attempting to crop outside image, %d,%d -> %d,%d not in %d,%d", lo_x, lo_y, hi_x, hi_y, width-1, height-1 ); return( false ); } if ( new_width == width && new_height == height ) { return( true ); } unsigned int new_size = new_width*new_height*colours; uint8_t *new_buffer = AllocBuffer(new_size); unsigned int new_stride = new_width*colours; for ( unsigned int y = lo_y, ny = 0; y <= hi_y; y++, ny++ ) { unsigned char *pbuf = &buffer[((y*width)+lo_x)*colours]; unsigned char *pnbuf = &new_buffer[(ny*new_width)*colours]; memcpy( pnbuf, pbuf, new_stride ); } AssignDirect(new_width, new_height, colours, subpixelorder, new_buffer, new_size, ZM_BUFTYPE_ZM); return( true ); } bool Image::Crop( const Box &limits ) { return( Crop( limits.LoX(), limits.LoY(), limits.HiX(), limits.HiY() ) ); } /* Far from complete */ /* Need to implement all possible of overlays possible */ void Image::Overlay( const Image &image ) { if ( !(width == image.width && height == image.height) ) { Panic( "Attempt to overlay different sized images, expected %dx%d, got %dx%d", width, height, image.width, image.height ); } if( colours == image.colours && subpixelorder != image.subpixelorder ) { Warning("Attempt to overlay images of same format but with different subpixel order."); } /* Grayscale ontop of grayscale - complete */ if ( colours == ZM_COLOUR_GRAY8 && image.colours == ZM_COLOUR_GRAY8 ) { const uint8_t* const max_ptr = buffer+size; const uint8_t* psrc = image.buffer; uint8_t* pdest = buffer; while( pdest < max_ptr ) { if ( *psrc ) { *pdest = *psrc; } pdest++; psrc++; } /* RGB24 ontop of grayscale - convert to same format first - complete */ } else if ( colours == ZM_COLOUR_GRAY8 && image.colours == ZM_COLOUR_RGB24 ) { Colourise(image.colours, image.subpixelorder); const uint8_t* const max_ptr = buffer+size; const uint8_t* psrc = image.buffer; uint8_t* pdest = buffer; while( pdest < max_ptr ) { if ( RED_PTR_RGBA(psrc) || GREEN_PTR_RGBA(psrc) || BLUE_PTR_RGBA(psrc) ) { RED_PTR_RGBA(pdest) = RED_PTR_RGBA(psrc); GREEN_PTR_RGBA(pdest) = GREEN_PTR_RGBA(psrc); BLUE_PTR_RGBA(pdest) = BLUE_PTR_RGBA(psrc); } pdest += 3; psrc += 3; } /* RGB32 ontop of grayscale - convert to same format first - complete */ } else if( colours == ZM_COLOUR_GRAY8 && image.colours == ZM_COLOUR_RGB32 ) { Colourise(image.colours, image.subpixelorder); const Rgb* const max_ptr = (Rgb*)(buffer+size); const Rgb* prsrc = (Rgb*)image.buffer; Rgb* prdest = (Rgb*)buffer; if(subpixelorder == ZM_SUBPIX_ORDER_RGBA || subpixelorder == ZM_SUBPIX_ORDER_BGRA) { /* RGB\BGR\RGBA\BGRA subpixel order - Alpha byte is last */ while (prdest < max_ptr) { if ( RED_PTR_RGBA(prsrc) || GREEN_PTR_RGBA(prsrc) || BLUE_PTR_RGBA(prsrc) ) { *prdest = *prsrc; } prdest++; prsrc++; } } else { /* ABGR\ARGB subpixel order - Alpha byte is first */ while (prdest < max_ptr) { if ( RED_PTR_ABGR(prsrc) || GREEN_PTR_ABGR(prsrc) || BLUE_PTR_ABGR(prsrc) ) { *prdest = *prsrc; } prdest++; prsrc++; } } /* Grayscale ontop of RGB24 - complete */ } else if ( colours == ZM_COLOUR_RGB24 && image.colours == ZM_COLOUR_GRAY8 ) { const uint8_t* const max_ptr = buffer+size; const uint8_t* psrc = image.buffer; uint8_t* pdest = buffer; while( pdest < max_ptr ) { if ( *psrc ) { RED_PTR_RGBA(pdest) = GREEN_PTR_RGBA(pdest) = BLUE_PTR_RGBA(pdest) = *psrc; } pdest += 3; psrc++; } /* RGB24 ontop of RGB24 - not complete. need to take care of different subpixel orders */ } else if ( colours == ZM_COLOUR_RGB24 && image.colours == ZM_COLOUR_RGB24 ) { const uint8_t* const max_ptr = buffer+size; const uint8_t* psrc = image.buffer; uint8_t* pdest = buffer; while( pdest < max_ptr ) { if ( RED_PTR_RGBA(psrc) || GREEN_PTR_RGBA(psrc) || BLUE_PTR_RGBA(psrc) ) { RED_PTR_RGBA(pdest) = RED_PTR_RGBA(psrc); GREEN_PTR_RGBA(pdest) = GREEN_PTR_RGBA(psrc); BLUE_PTR_RGBA(pdest) = BLUE_PTR_RGBA(psrc); } pdest += 3; psrc += 3; } /* RGB32 ontop of RGB24 - TO BE DONE */ } else if ( colours == ZM_COLOUR_RGB24 && image.colours == ZM_COLOUR_RGB32 ) { Error("Overlay of RGB32 ontop of RGB24 is not supported."); /* Grayscale ontop of RGB32 - complete */ } else if ( colours == ZM_COLOUR_RGB32 && image.colours == ZM_COLOUR_GRAY8 ) { const Rgb* const max_ptr = (Rgb*)(buffer+size); Rgb* prdest = (Rgb*)buffer; const uint8_t* psrc = image.buffer; if(subpixelorder == ZM_SUBPIX_ORDER_RGBA || subpixelorder == ZM_SUBPIX_ORDER_BGRA) { /* RGBA\BGRA subpixel order - Alpha byte is last */ while (prdest < max_ptr) { if ( *psrc ) { RED_PTR_RGBA(prdest) = GREEN_PTR_RGBA(prdest) = BLUE_PTR_RGBA(prdest) = *psrc; } prdest++; psrc++; } } else { /* ABGR\ARGB subpixel order - Alpha byte is first */ while (prdest < max_ptr) { if ( *psrc ) { RED_PTR_ABGR(prdest) = GREEN_PTR_ABGR(prdest) = BLUE_PTR_ABGR(prdest) = *psrc; } prdest++; psrc++; } } /* RGB24 ontop of RGB32 - TO BE DONE */ } else if ( colours == ZM_COLOUR_RGB32 && image.colours == ZM_COLOUR_RGB24 ) { Error("Overlay of RGB24 ontop of RGB32 is not supported."); /* RGB32 ontop of RGB32 - not complete. need to take care of different subpixel orders */ } else if ( colours == ZM_COLOUR_RGB32 && image.colours == ZM_COLOUR_RGB32 ) { const Rgb* const max_ptr = (Rgb*)(buffer+size); Rgb* prdest = (Rgb*)buffer; const Rgb* prsrc = (Rgb*)image.buffer; if(image.subpixelorder == ZM_SUBPIX_ORDER_RGBA || image.subpixelorder == ZM_SUBPIX_ORDER_BGRA) { /* RGB\BGR\RGBA\BGRA subpixel order - Alpha byte is last */ while (prdest < max_ptr) { if ( RED_PTR_RGBA(prsrc) || GREEN_PTR_RGBA(prsrc) || BLUE_PTR_RGBA(prsrc) ) { *prdest = *prsrc; } prdest++; prsrc++; } } else { /* ABGR\ARGB subpixel order - Alpha byte is first */ while (prdest < max_ptr) { if ( RED_PTR_ABGR(prsrc) || GREEN_PTR_ABGR(prsrc) || BLUE_PTR_ABGR(prsrc) ) { *prdest = *prsrc; } prdest++; prsrc++; } } } } /* RGB32 compatible: complete */ void Image::Overlay( const Image &image, unsigned int x, unsigned int y ) { if ( !(width < image.width || height < image.height) ) { Panic( "Attempt to overlay image too big for destination, %dx%d > %dx%d", image.width, image.height, width, height ); } if ( !(width < (x+image.width) || height < (y+image.height)) ) { Panic( "Attempt to overlay image outside of destination bounds, %dx%d @ %dx%d > %dx%d", image.width, image.height, x, y, width, height ); } if ( !(colours == image.colours) ) { Panic( "Attempt to partial overlay differently coloured images, expected %d, got %d", colours, image.colours ); } unsigned int lo_x = x; unsigned int lo_y = y; unsigned int hi_x = (x+image.width)-1; unsigned int hi_y = (y+image.height-1); if ( colours == ZM_COLOUR_GRAY8 ) { const uint8_t *psrc = image.buffer; for ( unsigned int y = lo_y; y <= hi_y; y++ ) { uint8_t *pdest = &buffer[(y*width)+lo_x]; for ( unsigned int x = lo_x; x <= hi_x; x++ ) { *pdest++ = *psrc++; } } } else if ( colours == ZM_COLOUR_RGB24 ) { const uint8_t *psrc = image.buffer; for ( unsigned int y = lo_y; y <= hi_y; y++ ) { uint8_t *pdest = &buffer[colours*((y*width)+lo_x)]; for ( unsigned int x = lo_x; x <= hi_x; x++ ) { *pdest++ = *psrc++; *pdest++ = *psrc++; *pdest++ = *psrc++; } } } else if ( colours == ZM_COLOUR_RGB32 ) { const Rgb *psrc = (Rgb*)(image.buffer); for ( unsigned int y = lo_y; y <= hi_y; y++ ) { Rgb *pdest = (Rgb*)&buffer[((y*width)+lo_x)<<2]; for ( unsigned int x = lo_x; x <= hi_x; x++ ) { *pdest++ = *psrc++; } } } else { Error("Overlay called with unexpected colours: %d", colours); } } void Image::Blend( const Image &image, int transparency ) { #ifdef ZM_IMAGE_PROFILING struct timespec start,end,diff; unsigned long long executetime; unsigned long milpixels; #endif uint8_t* new_buffer; if ( !(width == image.width && height == image.height && colours == image.colours && subpixelorder == image.subpixelorder) ) { Panic( "Attempt to blend different sized images, expected %dx%dx%d %d, got %dx%dx%d %d", width, height, colours, subpixelorder, image.width, image.height, image.colours, image.subpixelorder ); } if(transparency <= 0) return; new_buffer = AllocBuffer(size); #ifdef ZM_IMAGE_PROFILING clock_gettime(CLOCK_THREAD_CPUTIME_ID,&start); #endif /* Do the blending */ (*fptr_blend)(buffer, image.buffer, new_buffer, size, transparency); #ifdef ZM_IMAGE_PROFILING clock_gettime(CLOCK_THREAD_CPUTIME_ID,&end); timespec_diff(&start,&end,&diff); executetime = (1000000000ull * diff.tv_sec) + diff.tv_nsec; milpixels = (unsigned long)((long double)size)/((((long double)executetime)/1000)); Debug(5, "Blend: %u colours blended in %llu nanoseconds, %lu million colours/s\n",size,executetime,milpixels); #endif AssignDirect( width, height, colours, subpixelorder, new_buffer, size, ZM_BUFTYPE_ZM); } Image *Image::Merge( unsigned int n_images, Image *images[] ) { if ( n_images <= 0 ) return( 0 ); if ( n_images == 1 ) return( new Image( *images[0] ) ); unsigned int width = images[0]->width; unsigned int height = images[0]->height; unsigned int colours = images[0]->colours; for ( unsigned int i = 1; i < n_images; i++ ) { if ( !(width == images[i]->width && height == images[i]->height && colours == images[i]->colours) ) { Panic( "Attempt to merge different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d", width, height, colours, images[i]->width, images[i]->height, images[i]->colours, i ); } } Image *result = new Image( width, height, images[0]->colours, images[0]->subpixelorder); unsigned int size = result->size; for ( unsigned int i = 0; i < size; i++ ) { unsigned int total = 0; uint8_t *pdest = result->buffer; for ( unsigned int j = 0; j < n_images; j++ ) { uint8_t *psrc = images[j]->buffer; total += *psrc; psrc++; } *pdest = total/n_images; pdest++; } return( result ); } Image *Image::Merge( unsigned int n_images, Image *images[], double weight ) { if ( n_images <= 0 ) return( 0 ); if ( n_images == 1 ) return( new Image( *images[0] ) ); unsigned int width = images[0]->width; unsigned int height = images[0]->height; unsigned int colours = images[0]->colours; for ( unsigned int i = 1; i < n_images; i++ ) { if ( !(width == images[i]->width && height == images[i]->height && colours == images[i]->colours) ) { Panic( "Attempt to merge different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d", width, height, colours, images[i]->width, images[i]->height, images[i]->colours, i ); } } Image *result = new Image( *images[0] ); unsigned int size = result->size; double factor = 1.0*weight; for ( unsigned int i = 1; i < n_images; i++ ) { uint8_t *pdest = result->buffer; uint8_t *psrc = images[i]->buffer; for ( unsigned int j = 0; j < size; j++ ) { *pdest = (uint8_t)(((*pdest)*(1.0-factor))+((*psrc)*factor)); pdest++; psrc++; } factor *= weight; } return( result ); } Image *Image::Highlight( unsigned int n_images, Image *images[], const Rgb threshold, const Rgb ref_colour ) { if ( n_images <= 0 ) return( 0 ); if ( n_images == 1 ) return( new Image( *images[0] ) ); unsigned int width = images[0]->width; unsigned int height = images[0]->height; unsigned int colours = images[0]->colours; for ( unsigned int i = 1; i < n_images; i++ ) { if ( !(width == images[i]->width && height == images[i]->height && colours == images[i]->colours) ) { Panic( "Attempt to highlight different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d", width, height, colours, images[i]->width, images[i]->height, images[i]->colours, i ); } } Image *result = new Image( width, height, images[0]->colours, images[0]->subpixelorder ); unsigned int size = result->size; for ( unsigned int c = 0; c < colours; c++ ) { for ( unsigned int i = 0; i < size; i++ ) { unsigned int count = 0; uint8_t *pdest = result->buffer+c; for ( unsigned int j = 0; j < n_images; j++ ) { uint8_t *psrc = images[j]->buffer+c; if ( (unsigned)abs((*psrc)-RGB_VAL(ref_colour,c)) >= RGB_VAL(threshold,c) ) { count++; } psrc += colours; } *pdest = (count*255)/n_images; pdest += 3; } } return( result ); } /* New function to allow buffer re-using instead of allocationg memory for the delta image everytime */ void Image::Delta( const Image &image, Image* targetimage) const { #ifdef ZM_IMAGE_PROFILING struct timespec start,end,diff; unsigned long long executetime; unsigned long milpixels; #endif if ( !(width == image.width && height == image.height && colours == image.colours && subpixelorder == image.subpixelorder) ) { Panic( "Attempt to get delta of different sized images, expected %dx%dx%d %d, got %dx%dx%d %d", width, height, colours, subpixelorder, image.width, image.height, image.colours, image.subpixelorder); } uint8_t *pdiff = targetimage->WriteBuffer(width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE); if(pdiff == NULL) { Panic("Failed requesting writeable buffer for storing the delta image"); } #ifdef ZM_IMAGE_PROFILING clock_gettime(CLOCK_THREAD_CPUTIME_ID,&start); #endif switch(colours) { case ZM_COLOUR_RGB24: { if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { /* BGR subpixel order */ (*fptr_delta8_bgr)(buffer, image.buffer, pdiff, pixels); } else { /* Assume RGB subpixel order */ (*fptr_delta8_rgb)(buffer, image.buffer, pdiff, pixels); } break; } case ZM_COLOUR_RGB32: { if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { /* ARGB subpixel order */ (*fptr_delta8_argb)(buffer, image.buffer, pdiff, pixels); } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { /* ABGR subpixel order */ (*fptr_delta8_abgr)(buffer, image.buffer, pdiff, pixels); } else if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { /* BGRA subpixel order */ (*fptr_delta8_bgra)(buffer, image.buffer, pdiff, pixels); } else { /* Assume RGBA subpixel order */ (*fptr_delta8_rgba)(buffer, image.buffer, pdiff, pixels); } break; } case ZM_COLOUR_GRAY8: (*fptr_delta8_gray8)(buffer, image.buffer, pdiff, pixels); break; default: Panic("Delta called with unexpected colours: %d",colours); break; } #ifdef ZM_IMAGE_PROFILING clock_gettime(CLOCK_THREAD_CPUTIME_ID,&end); timespec_diff(&start,&end,&diff); executetime = (1000000000ull * diff.tv_sec) + diff.tv_nsec; milpixels = (unsigned long)((long double)pixels)/((((long double)executetime)/1000)); Debug(5, "Delta: %u delta pixels generated in %llu nanoseconds, %lu million pixels/s\n",pixels,executetime,milpixels); #endif } const Coord Image::centreCoord( const char *text ) const { int index = 0; int line_no = 0; int text_len = strlen( text ); int line_len = 0; int max_line_len = 0; const char *line = text; while ( (index < text_len) && (line_len = strcspn( line, "\n" )) ) { if ( line_len > max_line_len ) max_line_len = line_len; index += line_len; while ( text[index] == '\n' ) { index++; } line = text+index; line_no++; } int x = (width - (max_line_len * CHAR_WIDTH) ) / 2; int y = (height - (line_no * LINE_HEIGHT) ) / 2; return( Coord( x, y ) ); } /* RGB32 compatible: complete */ void Image::Annotate( const char *p_text, const Coord &coord, const Rgb fg_colour, const Rgb bg_colour ) { strncpy( text, p_text, sizeof(text) ); unsigned int index = 0; unsigned int line_no = 0; unsigned int text_len = strlen( text ); unsigned int line_len = 0; const char *line = text; const uint8_t fg_r_col = RED_VAL_RGBA(fg_colour); const uint8_t fg_g_col = GREEN_VAL_RGBA(fg_colour); const uint8_t fg_b_col = BLUE_VAL_RGBA(fg_colour); const uint8_t fg_bw_col = fg_colour & 0xff; const Rgb fg_rgb_col = rgb_convert(fg_colour,subpixelorder); const bool fg_trans = (fg_colour == RGB_TRANSPARENT); const uint8_t bg_r_col = RED_VAL_RGBA(bg_colour); const uint8_t bg_g_col = GREEN_VAL_RGBA(bg_colour); const uint8_t bg_b_col = BLUE_VAL_RGBA(bg_colour); const uint8_t bg_bw_col = bg_colour & 0xff; const Rgb bg_rgb_col = rgb_convert(bg_colour,subpixelorder); const bool bg_trans = (bg_colour == RGB_TRANSPARENT); while ( (index < text_len) && (line_len = strcspn( line, "\n" )) ) { unsigned int line_width = line_len * CHAR_WIDTH; unsigned int lo_line_x = coord.X(); unsigned int lo_line_y = coord.Y() + (line_no * LINE_HEIGHT); unsigned int min_line_x = 0; unsigned int max_line_x = width - line_width; unsigned int min_line_y = 0; unsigned int max_line_y = height - LINE_HEIGHT; if ( lo_line_x > max_line_x ) lo_line_x = max_line_x; if ( lo_line_x < min_line_x ) lo_line_x = min_line_x; if ( lo_line_y > max_line_y ) lo_line_y = max_line_y; if ( lo_line_y < min_line_y ) lo_line_y = min_line_y; unsigned int hi_line_x = lo_line_x + line_width; unsigned int hi_line_y = lo_line_y + LINE_HEIGHT; // Clip anything that runs off the right of the screen if ( hi_line_x > width ) hi_line_x = width; if ( hi_line_y > height ) hi_line_y = height; if ( colours == ZM_COLOUR_GRAY8 ) { unsigned char *ptr = &buffer[(lo_line_y*width)+lo_line_x]; for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < CHAR_HEIGHT; y++, r++, ptr += width ) { unsigned char *temp_ptr = ptr; for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { int f = fontdata[(line[c] * CHAR_HEIGHT) + r]; for ( unsigned int i = 0; i < CHAR_WIDTH && x < hi_line_x; i++, x++, temp_ptr++ ) { if ( f & (0x80 >> i) ) { if ( !fg_trans ) *temp_ptr = fg_bw_col; } else if ( !bg_trans ) { *temp_ptr = bg_bw_col; } } } } } else if ( colours == ZM_COLOUR_RGB24 ) { unsigned int wc = width * colours; unsigned char *ptr = &buffer[((lo_line_y*width)+lo_line_x)*colours]; for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < CHAR_HEIGHT; y++, r++, ptr += wc ) { unsigned char *temp_ptr = ptr; for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { int f = fontdata[(line[c] * CHAR_HEIGHT) + r]; for ( unsigned int i = 0; i < CHAR_WIDTH && x < hi_line_x; i++, x++, temp_ptr += colours ) { if ( f & (0x80 >> i) ) { if ( !fg_trans ) { RED_PTR_RGBA(temp_ptr) = fg_r_col; GREEN_PTR_RGBA(temp_ptr) = fg_g_col; BLUE_PTR_RGBA(temp_ptr) = fg_b_col; } } else if ( !bg_trans ) { RED_PTR_RGBA(temp_ptr) = bg_r_col; GREEN_PTR_RGBA(temp_ptr) = bg_g_col; BLUE_PTR_RGBA(temp_ptr) = bg_b_col; } } } } } else if ( colours == ZM_COLOUR_RGB32 ) { unsigned int wc = width * colours; uint8_t *ptr = &buffer[((lo_line_y*width)+lo_line_x)<<2]; for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < CHAR_HEIGHT; y++, r++, ptr += wc ) { Rgb* temp_ptr = (Rgb*)ptr; for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) { int f = fontdata[(line[c] * CHAR_HEIGHT) + r]; for ( unsigned int i = 0; i < CHAR_WIDTH && x < hi_line_x; i++, x++, temp_ptr++ ) { if ( f & (0x80 >> i) ) { if ( !fg_trans ) { *temp_ptr = fg_rgb_col; } } else if ( !bg_trans ) { *temp_ptr = bg_rgb_col; } } } } } else { Panic("Annontate called with unexpected colours: %d",colours); return; } index += line_len; while ( text[index] == '\n' ) { index++; } line = text+index; line_no++; } } void Image::Timestamp( const char *label, const time_t when, const Coord &coord ) { char time_text[64]; strftime( time_text, sizeof(time_text), "%y/%m/%d %H:%M:%S", localtime( &when ) ); char text[64]; if ( label ) { snprintf( text, sizeof(text), "%s - %s", label, time_text ); Annotate( text, coord ); } else { Annotate( time_text, coord ); } } /* RGB32 compatible: complete */ void Image::Colourise(const unsigned int p_reqcolours, const unsigned int p_reqsubpixelorder) { Debug(9, "Colourise: Req colours: %u Req subpixel order: %u Current colours: %u Current subpixel order: %u",p_reqcolours,p_reqsubpixelorder,colours,subpixelorder); if ( colours != ZM_COLOUR_GRAY8) { Warning("Target image is already colourised, colours: %u",colours); return; } if ( p_reqcolours == ZM_COLOUR_RGB32 ) { /* RGB32 */ Rgb* new_buffer = (Rgb*)AllocBuffer(pixels*sizeof(Rgb)); const uint8_t *psrc = buffer; Rgb* pdest = new_buffer; Rgb subpixel; Rgb newpixel; if ( p_reqsubpixelorder == ZM_SUBPIX_ORDER_ABGR || p_reqsubpixelorder == ZM_SUBPIX_ORDER_ARGB) { /* ARGB\ABGR subpixel order. alpha byte is first (mem+0), so we need to shift the pixel left in the end */ for(unsigned int i=0;iLo().X():0; unsigned int lo_y = limits?limits->Lo().Y():0; unsigned int hi_x = limits?limits->Hi().X():width-1; unsigned int hi_y = limits?limits->Hi().Y():height-1; if ( colours == ZM_COLOUR_GRAY8 ) { for ( unsigned int y = lo_y; y <= hi_y; y++ ) { unsigned char *p = &buffer[(y*width)+lo_x]; for ( unsigned int x = lo_x; x <= hi_x; x++, p++) { *p = colour; } } } else if ( colours == ZM_COLOUR_RGB24 ) { for ( unsigned int y = lo_y; y <= hi_y; y++ ) { unsigned char *p = &buffer[colours*((y*width)+lo_x)]; for ( unsigned int x = lo_x; x <= hi_x; x++, p += 3) { RED_PTR_RGBA(p) = RED_VAL_RGBA(colour); GREEN_PTR_RGBA(p) = GREEN_VAL_RGBA(colour); BLUE_PTR_RGBA(p) = BLUE_VAL_RGBA(colour); } } } else if ( colours == ZM_COLOUR_RGB32 ) /* RGB32 */ { for ( unsigned int y = lo_y; y <= (unsigned int)hi_y; y++ ) { Rgb *p = (Rgb*)&buffer[((y*width)+lo_x)<<2]; for ( unsigned int x = lo_x; x <= (unsigned int)hi_x; x++, p++) { /* Fast, copies the entire pixel in a single pass */ *p = colour; } } } } /* RGB32 compatible: complete */ void Image::Fill( Rgb colour, int density, const Box *limits ) { /* Allow the faster version to be used if density is not used (density=1) */ if(density <= 1) return Fill(colour,limits); if ( !(colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB24 || colours == ZM_COLOUR_RGB32 ) ) { Panic( "Attempt to fill image with unexpected colours %d", colours ); } /* Convert the colour's RGBA subpixel order into the image's subpixel order */ colour = rgb_convert(colour,subpixelorder); unsigned int lo_x = limits?limits->Lo().X():0; unsigned int lo_y = limits?limits->Lo().Y():0; unsigned int hi_x = limits?limits->Hi().X():width-1; unsigned int hi_y = limits?limits->Hi().Y():height-1; if ( colours == ZM_COLOUR_GRAY8 ) { for ( unsigned int y = lo_y; y <= hi_y; y++ ) { unsigned char *p = &buffer[(y*width)+lo_x]; for ( unsigned int x = lo_x; x <= hi_x; x++, p++) { if ( ( x == lo_x || x == hi_x || y == lo_y || y == hi_y ) || (!(x%density) && !(y%density) ) ) *p = colour; } } } else if ( colours == ZM_COLOUR_RGB24 ) { for ( unsigned int y = lo_y; y <= hi_y; y++ ) { unsigned char *p = &buffer[colours*((y*width)+lo_x)]; for ( unsigned int x = lo_x; x <= hi_x; x++, p += 3) { if ( ( x == lo_x || x == hi_x || y == lo_y || y == hi_y ) || (!(x%density) && !(y%density) ) ) { RED_PTR_RGBA(p) = RED_VAL_RGBA(colour); GREEN_PTR_RGBA(p) = GREEN_VAL_RGBA(colour); BLUE_PTR_RGBA(p) = BLUE_VAL_RGBA(colour); } } } } else if ( colours == ZM_COLOUR_RGB32 ) /* RGB32 */ { for ( unsigned int y = lo_y; y <= hi_y; y++ ) { Rgb* p = (Rgb*)&buffer[((y*width)+lo_x)<<2]; for ( unsigned int x = lo_x; x <= hi_x; x++, p++) { if ( ( x == lo_x || x == hi_x || y == lo_y || y == hi_y ) || (!(x%density) && !(y%density) ) ) /* Fast, copies the entire pixel in a single pass */ *p = colour; } } } } /* RGB32 compatible: complete */ void Image::Outline( Rgb colour, const Polygon &polygon ) { if ( !(colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB24 || colours == ZM_COLOUR_RGB32 ) ) { Panic( "Attempt to outline image with unexpected colours %d", colours ); } /* Convert the colour's RGBA subpixel order into the image's subpixel order */ colour = rgb_convert(colour,subpixelorder); int n_coords = polygon.getNumCoords(); for ( int j = 0, i = n_coords-1; j < n_coords; i = j++ ) { const Coord &p1 = polygon.getCoord( i ); const Coord &p2 = polygon.getCoord( j ); int x1 = p1.X(); int x2 = p2.X(); int y1 = p1.Y(); int y2 = p2.Y(); double dx = x2 - x1; double dy = y2 - y1; double grad; Debug( 9, "dx: %.2lf, dy: %.2lf", dx, dy ); if ( fabs(dx) <= fabs(dy) ) { Debug( 9, "dx <= dy" ); if ( y1 != y2 ) grad = dx/dy; else grad = width; double x; int y, yinc = (y1 dy" ); if ( x1 != x2 ) grad = dy/dx; else grad = height; Debug( 9, "grad: %.2lf", grad ); double y; int x, xinc = (x1= Logger::DEBUG9 ) { for ( int i = 0; i < n_global_edges; i++ ) { Debug( 9, "%d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f", i, global_edges[i].min_y, global_edges[i].max_y, global_edges[i].min_x, global_edges[i]._1_m ); } } #endif int n_active_edges = 0; Edge active_edges[n_global_edges]; int y = global_edges[0].min_y; do { for ( int i = 0; i < n_global_edges; i++ ) { if ( global_edges[i].min_y == y ) { Debug( 9, "Moving global edge" ); active_edges[n_active_edges++] = global_edges[i]; if ( i < (n_global_edges-1) ) { //memcpy( &global_edges[i], &global_edges[i+1], sizeof(*global_edges)*(n_global_edges-i) ); memmove( &global_edges[i], &global_edges[i+1], sizeof(*global_edges)*(n_global_edges-i) ); i--; } n_global_edges--; } else { break; } } qsort( active_edges, n_active_edges, sizeof(*active_edges), Edge::CompareX ); #ifndef ZM_DBG_OFF if ( logLevel() >= Logger::DEBUG9 ) { for ( int i = 0; i < n_active_edges; i++ ) { Debug( 9, "%d - %d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f", y, i, active_edges[i].min_y, active_edges[i].max_y, active_edges[i].min_x, active_edges[i]._1_m ); } } #endif if ( !(y%density) ) { //Debug( 9, "%d", y ); for ( int i = 0; i < n_active_edges; ) { int lo_x = int(round(active_edges[i++].min_x)); int hi_x = int(round(active_edges[i++].min_x)); if( colours == ZM_COLOUR_GRAY8 ) { unsigned char *p = &buffer[(y*width)+lo_x]; for ( int x = lo_x; x <= hi_x; x++, p++) { if ( !(x%density) ) { //Debug( 9, " %d", x ); *p = colour; } } } else if( colours == ZM_COLOUR_RGB24 ) { unsigned char *p = &buffer[colours*((y*width)+lo_x)]; for ( int x = lo_x; x <= hi_x; x++, p += 3) { if ( !(x%density) ) { RED_PTR_RGBA(p) = RED_VAL_RGBA(colour); GREEN_PTR_RGBA(p) = GREEN_VAL_RGBA(colour); BLUE_PTR_RGBA(p) = BLUE_VAL_RGBA(colour); } } } else if( colours == ZM_COLOUR_RGB32 ) { Rgb *p = (Rgb*)&buffer[((y*width)+lo_x)<<2]; for ( int x = lo_x; x <= hi_x; x++, p++) { if ( !(x%density) ) { /* Fast, copies the entire pixel in a single pass */ *p = colour; } } } } } y++; for ( int i = n_active_edges-1; i >= 0; i-- ) { if ( y >= active_edges[i].max_y ) // Or >= as per sheets { Debug( 9, "Deleting active_edge" ); if ( i < (n_active_edges-1) ) { //memcpy( &active_edges[i], &active_edges[i+1], sizeof(*active_edges)*(n_active_edges-i) ); memmove( &active_edges[i], &active_edges[i+1], sizeof(*active_edges)*(n_active_edges-i) ); } n_active_edges--; } else { active_edges[i].min_x += active_edges[i]._1_m; } } } while ( n_global_edges || n_active_edges ); } void Image::Fill( Rgb colour, const Polygon &polygon ) { Fill( colour, 1, polygon ); } /* RGB32 compatible: complete */ void Image::Rotate( int angle ) { angle %= 360; if ( !angle ) { return; } if ( angle%90 ) { return; } unsigned int new_height = height; unsigned int new_width = width; uint8_t* rotate_buffer = AllocBuffer(size); switch( angle ) { case 90 : { new_height = width; new_width = height; unsigned int line_bytes = new_width*colours; unsigned char *s_ptr = buffer; if ( colours == ZM_COLOUR_GRAY8 ) { unsigned char *d_ptr; for ( unsigned int i = new_width; i > 0; i-- ) { d_ptr = rotate_buffer+(i-1); for ( unsigned int j = new_height; j > 0; j-- ) { *d_ptr = *s_ptr++; d_ptr += line_bytes; } } } else if ( colours == ZM_COLOUR_RGB32 ) { Rgb* s_rptr = (Rgb*)s_ptr; Rgb* d_rptr; for ( unsigned int i = new_width; i > 0; i-- ) { d_rptr = (Rgb*)(rotate_buffer+((i-1)<<2)); for ( unsigned int j = new_height; j > 0; j-- ) { *d_rptr = *s_rptr++; d_rptr += new_width; } } } else /* Assume RGB24 */ { unsigned char *d_ptr; for ( unsigned int i = new_width; i > 0; i-- ) { d_ptr = rotate_buffer+((i-1)*3); for ( unsigned int j = new_height; j > 0; j-- ) { *d_ptr = *s_ptr++; *(d_ptr+1) = *s_ptr++; *(d_ptr+2) = *s_ptr++; d_ptr += line_bytes; } } } break; } case 180 : { unsigned char *s_ptr = buffer+size; unsigned char *d_ptr = rotate_buffer; if ( colours == ZM_COLOUR_GRAY8 ) { while( s_ptr > buffer ) { s_ptr--; *d_ptr++ = *s_ptr; } } else if ( colours == ZM_COLOUR_RGB32 ) { Rgb* s_rptr = (Rgb*)s_ptr; Rgb* d_rptr = (Rgb*)d_ptr; while( s_rptr > (Rgb*)buffer ) { s_rptr--; *d_rptr++ = *s_rptr; } } else /* Assume RGB24 */ { while( s_ptr > buffer ) { s_ptr -= 3; *d_ptr++ = *s_ptr; *d_ptr++ = *(s_ptr+1); *d_ptr++ = *(s_ptr+2); } } break; } case 270 : { new_height = width; new_width = height; unsigned int line_bytes = new_width*colours; unsigned char *s_ptr = buffer+size; if ( colours == ZM_COLOUR_GRAY8 ) { unsigned char *d_ptr; for ( unsigned int i = new_width; i > 0; i-- ) { d_ptr = rotate_buffer+(i-1); for ( unsigned int j = new_height; j > 0; j-- ) { s_ptr--; *d_ptr = *s_ptr; d_ptr += line_bytes; } } } else if ( colours == ZM_COLOUR_RGB32 ) { Rgb* s_rptr = (Rgb*)s_ptr; Rgb* d_rptr; for ( unsigned int i = new_width; i > 0; i-- ) { d_rptr = (Rgb*)(rotate_buffer+((i-1)<<2)); for ( unsigned int j = new_height; j > 0; j-- ) { s_rptr--; *d_rptr = *s_rptr; d_rptr += new_width; } } } else /* Assume RGB24 */ { unsigned char *d_ptr; for ( unsigned int i = new_width; i > 0; i-- ) { d_ptr = rotate_buffer+((i-1)*3); for ( unsigned int j = new_height; j > 0; j-- ) { *(d_ptr+2) = *(--s_ptr); *(d_ptr+1) = *(--s_ptr); *d_ptr = *(--s_ptr); d_ptr += line_bytes; } } } break; } } AssignDirect( new_width, new_height, colours, subpixelorder, rotate_buffer, size, ZM_BUFTYPE_ZM); } /* RGB32 compatible: complete */ void Image::Flip( bool leftright ) { uint8_t* flip_buffer = AllocBuffer(size); unsigned int line_bytes = width*colours; unsigned int line_bytes2 = 2*line_bytes; if ( leftright ) { // Horizontal flip, left to right unsigned char *s_ptr = buffer+line_bytes; unsigned char *d_ptr = flip_buffer; unsigned char *max_d_ptr = flip_buffer + size; if ( colours == ZM_COLOUR_GRAY8 ) { while( d_ptr < max_d_ptr ) { for ( unsigned int j = 0; j < width; j++ ) { s_ptr--; *d_ptr++ = *s_ptr; } s_ptr += line_bytes2; } } else if ( colours == ZM_COLOUR_RGB32 ) { Rgb* s_rptr = (Rgb*)s_ptr; Rgb* d_rptr = (Rgb*)flip_buffer; Rgb* max_d_rptr = (Rgb*)max_d_ptr; while( d_rptr < max_d_rptr ) { for ( unsigned int j = 0; j < width; j++ ) { s_rptr--; *d_rptr++ = *s_rptr; } s_rptr += width * 2; } } else /* Assume RGB24 */ { while( d_ptr < max_d_ptr ) { for ( unsigned int j = 0; j < width; j++ ) { s_ptr -= 3; *d_ptr++ = *s_ptr; *d_ptr++ = *(s_ptr+1); *d_ptr++ = *(s_ptr+2); } s_ptr += line_bytes2; } } } else { // Vertical flip, top to bottom unsigned char *s_ptr = buffer+(height*line_bytes); unsigned char *d_ptr = flip_buffer; while( s_ptr > buffer ) { s_ptr -= line_bytes; memcpy( d_ptr, s_ptr, line_bytes ); d_ptr += line_bytes; } } AssignDirect( width, height, colours, subpixelorder, flip_buffer, size, ZM_BUFTYPE_ZM); } void Image::Scale( unsigned int factor ) { if ( !factor ) { Error( "Bogus scale factor %d found", factor ); return; } if ( factor == ZM_SCALE_BASE ) { return; } unsigned int new_width = (width*factor)/ZM_SCALE_BASE; unsigned int new_height = (height*factor)/ZM_SCALE_BASE; size_t scale_buffer_size = new_width * new_height * colours; uint8_t* scale_buffer = AllocBuffer(scale_buffer_size); if ( factor > ZM_SCALE_BASE ) { unsigned char *pd = scale_buffer; unsigned int wc = width*colours; unsigned int nwc = new_width*colours; unsigned int h_count = ZM_SCALE_BASE/2; unsigned int last_h_index = 0; unsigned int last_w_index = 0; unsigned int h_index; for ( unsigned int y = 0; y < height; y++ ) { unsigned char *ps = &buffer[y*wc]; unsigned int w_count = ZM_SCALE_BASE/2; unsigned int w_index; last_w_index = 0; for ( unsigned int x = 0; x < width; x++ ) { w_count += factor; w_index = w_count/ZM_SCALE_BASE; for (unsigned int f = last_w_index; f < w_index; f++ ) { for ( unsigned int c = 0; c < colours; c++ ) { *pd++ = *(ps+c); } } ps += colours; last_w_index = w_index; } h_count += factor; h_index = h_count/ZM_SCALE_BASE; for ( unsigned int f = last_h_index+1; f < h_index; f++ ) { memcpy( pd, pd-nwc, nwc ); pd += nwc; } last_h_index = h_index; } new_width = last_w_index; new_height = last_h_index; } else { unsigned char *pd = scale_buffer; unsigned int wc = width*colours; unsigned int xstart = factor/2; unsigned int ystart = factor/2; unsigned int h_count = ystart; unsigned int last_h_index = 0; unsigned int last_w_index = 0; unsigned int h_index; for ( unsigned int y = 0; y < (unsigned int)height; y++ ) { h_count += factor; h_index = h_count/ZM_SCALE_BASE; if ( h_index > last_h_index ) { unsigned int w_count = xstart; unsigned int w_index; last_w_index = 0; unsigned char *ps = &buffer[y*wc]; for ( unsigned int x = 0; x < (unsigned int)width; x++ ) { w_count += factor; w_index = w_count/ZM_SCALE_BASE; if ( w_index > last_w_index ) { for ( unsigned int c = 0; c < colours; c++ ) { *pd++ = *ps++; } } else { ps += colours; } last_w_index = w_index; } } last_h_index = h_index; } new_width = last_w_index; new_height = last_h_index; } AssignDirect( new_width, new_height, colours, subpixelorder, scale_buffer, scale_buffer_size, ZM_BUFTYPE_ZM); } void Image::Deinterlace_Discard() { /* Simple deinterlacing. Copy the even lines into the odd lines */ if ( colours == ZM_COLOUR_GRAY8 ) { const uint8_t *psrc; uint8_t *pdest; for (unsigned int y = 0; y < (unsigned int)height; y += 2) { psrc = buffer + (y * width); pdest = buffer + ((y+1) * width); for (unsigned int x = 0; x < (unsigned int)width; x++) { *pdest++ = *psrc++; } } } else if ( colours == ZM_COLOUR_RGB24 ) { const uint8_t *psrc; uint8_t *pdest; for (unsigned int y = 0; y < (unsigned int)height; y += 2) { psrc = buffer + ((y * width) * 3); pdest = buffer + (((y+1) * width) * 3); for (unsigned int x = 0; x < (unsigned int)width; x++) { *pdest++ = *psrc++; *pdest++ = *psrc++; *pdest++ = *psrc++; } } } else if ( colours == ZM_COLOUR_RGB32 ) { const Rgb *psrc; Rgb *pdest; for (unsigned int y = 0; y < (unsigned int)height; y += 2) { psrc = (Rgb*)(buffer + ((y * width) << 2)); pdest = (Rgb*)(buffer + (((y+1) * width) << 2)); for (unsigned int x = 0; x < (unsigned int)width; x++) { *pdest++ = *psrc++; } } } else { Error("Deinterlace called with unexpected colours: %d", colours); } } void Image::Deinterlace_Linear() { /* Simple deinterlacing. The odd lines are average of the line above and line below */ const uint8_t *pbelow, *pabove; uint8_t *pcurrent; if ( colours == ZM_COLOUR_GRAY8 ) { for (unsigned int y = 1; y < (unsigned int)(height-1); y += 2) { pabove = buffer + ((y-1) * width); pbelow = buffer + ((y+1) * width); pcurrent = buffer + (y * width); for (unsigned int x = 0; x < (unsigned int)width; x++) { *pcurrent++ = (*pabove++ + *pbelow++) >> 1; } } /* Special case for the last line */ pcurrent = buffer + ((height-1) * width); pabove = buffer + ((height-2) * width); for (unsigned int x = 0; x < (unsigned int)width; x++) { *pcurrent++ = *pabove++; } } else if ( colours == ZM_COLOUR_RGB24 ) { for (unsigned int y = 1; y < (unsigned int)(height-1); y += 2) { pabove = buffer + (((y-1) * width) * 3); pbelow = buffer + (((y+1) * width) * 3); pcurrent = buffer + ((y * width) * 3); for (unsigned int x = 0; x < (unsigned int)width; x++) { *pcurrent++ = (*pabove++ + *pbelow++) >> 1; *pcurrent++ = (*pabove++ + *pbelow++) >> 1; *pcurrent++ = (*pabove++ + *pbelow++) >> 1; } } /* Special case for the last line */ pcurrent = buffer + (((height-1) * width) * 3); pabove = buffer + (((height-2) * width) * 3); for (unsigned int x = 0; x < (unsigned int)width; x++) { *pcurrent++ = *pabove++; *pcurrent++ = *pabove++; *pcurrent++ = *pabove++; } } else if ( colours == ZM_COLOUR_RGB32 ) { for (unsigned int y = 1; y < (unsigned int)(height-1); y += 2) { pabove = buffer + (((y-1) * width) << 2); pbelow = buffer + (((y+1) * width) << 2); pcurrent = buffer + ((y * width) << 2); for (unsigned int x = 0; x < (unsigned int)width; x++) { *pcurrent++ = (*pabove++ + *pbelow++) >> 1; *pcurrent++ = (*pabove++ + *pbelow++) >> 1; *pcurrent++ = (*pabove++ + *pbelow++) >> 1; *pcurrent++ = (*pabove++ + *pbelow++) >> 1; } } /* Special case for the last line */ pcurrent = buffer + (((height-1) * width) << 2); pabove = buffer + (((height-2) * width) << 2); for (unsigned int x = 0; x < (unsigned int)width; x++) { *pcurrent++ = *pabove++; *pcurrent++ = *pabove++; *pcurrent++ = *pabove++; *pcurrent++ = *pabove++; } } else { Error("Deinterlace called with unexpected colours: %d", colours); } } void Image::Deinterlace_Blend() { /* Simple deinterlacing. Blend the fields together. 50% blend */ uint8_t *pabove, *pcurrent; if ( colours == ZM_COLOUR_GRAY8 ) { for (unsigned int y = 1; y < (unsigned int)height; y += 2) { pabove = buffer + ((y-1) * width); pcurrent = buffer + (y * width); for (unsigned int x = 0; x < (unsigned int)width; x++) { *pabove = (*pabove + *pcurrent) >> 1; *pcurrent++ = *pabove++; } } } else if ( colours == ZM_COLOUR_RGB24 ) { for (unsigned int y = 1; y < (unsigned int)height; y += 2) { pabove = buffer + (((y-1) * width) * 3); pcurrent = buffer + ((y * width) * 3); for (unsigned int x = 0; x < (unsigned int)width; x++) { *pabove = (*pabove + *pcurrent) >> 1; *pcurrent++ = *pabove++; *pabove = (*pabove + *pcurrent) >> 1; *pcurrent++ = *pabove++; *pabove = (*pabove + *pcurrent) >> 1; *pcurrent++ = *pabove++; } } } else if ( colours == ZM_COLOUR_RGB32 ) { for (unsigned int y = 1; y < (unsigned int)height; y += 2) { pabove = buffer + (((y-1) * width) << 2); pcurrent = buffer + ((y * width) << 2); for (unsigned int x = 0; x < (unsigned int)width; x++) { *pabove = (*pabove + *pcurrent) >> 1; *pcurrent++ = *pabove++; *pabove = (*pabove + *pcurrent) >> 1; *pcurrent++ = *pabove++; *pabove = (*pabove + *pcurrent) >> 1; *pcurrent++ = *pabove++; *pabove = (*pabove + *pcurrent) >> 1; *pcurrent++ = *pabove++; } } } else { Error("Deinterlace called with unexpected colours: %d", colours); } } void Image::Deinterlace_Blend_CustomRatio(int divider) { /* Simple deinterlacing. Blend the fields together at a custom ratio. */ /* 1 = 50% blending */ /* 2 = 25% blending */ /* 3 = 12.% blending */ /* 4 = 6.25% blending */ uint8_t *pabove, *pcurrent; uint8_t subpix1, subpix2; if ( divider < 1 || divider > 4 ) { Error("Deinterlace called with invalid blend ratio"); } if ( colours == ZM_COLOUR_GRAY8 ) { for (unsigned int y = 1; y < (unsigned int)height; y += 2) { pabove = buffer + ((y-1) * width); pcurrent = buffer + (y * width); for (unsigned int x = 0; x < (unsigned int)width; x++) { subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; *pcurrent++ = subpix1; *pabove++ = subpix2; } } } else if ( colours == ZM_COLOUR_RGB24 ) { for (unsigned int y = 1; y < (unsigned int)height; y += 2) { pabove = buffer + (((y-1) * width) * 3); pcurrent = buffer + ((y * width) * 3); for (unsigned int x = 0; x < (unsigned int)width; x++) { subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; *pcurrent++ = subpix1; *pabove++ = subpix2; subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; *pcurrent++ = subpix1; *pabove++ = subpix2; subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; *pcurrent++ = subpix1; *pabove++ = subpix2; } } } else if ( colours == ZM_COLOUR_RGB32 ) { for (unsigned int y = 1; y < (unsigned int)height; y += 2) { pabove = buffer + (((y-1) * width) << 2); pcurrent = buffer + ((y * width) << 2); for (unsigned int x = 0; x < (unsigned int)width; x++) { subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; *pcurrent++ = subpix1; *pabove++ = subpix2; subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; *pcurrent++ = subpix1; *pabove++ = subpix2; subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; *pcurrent++ = subpix1; *pabove++ = subpix2; subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; *pcurrent++ = subpix1; *pabove++ = subpix2; } } } else { Error("Deinterlace called with unexpected colours: %d", colours); } } void Image::Deinterlace_4Field(const Image* next_image, unsigned int threshold) { if ( !(width == next_image->width && height == next_image->height && colours == next_image->colours && subpixelorder == next_image->subpixelorder) ) { Panic( "Attempt to deinterlace different sized images, expected %dx%dx%d %d, got %dx%dx%d %d", width, height, colours, subpixelorder, next_image->width, next_image->height, next_image->colours, next_image->subpixelorder); } switch(colours) { case ZM_COLOUR_RGB24: { if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { /* BGR subpixel order */ std_deinterlace_4field_bgr(buffer, next_image->buffer, threshold, width, height); } else { /* Assume RGB subpixel order */ std_deinterlace_4field_rgb(buffer, next_image->buffer, threshold, width, height); } break; } case ZM_COLOUR_RGB32: { if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { /* ARGB subpixel order */ (*fptr_deinterlace_4field_argb)(buffer, next_image->buffer, threshold, width, height); } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { /* ABGR subpixel order */ (*fptr_deinterlace_4field_abgr)(buffer, next_image->buffer, threshold, width, height); } else if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { /* BGRA subpixel order */ (*fptr_deinterlace_4field_bgra)(buffer, next_image->buffer, threshold, width, height); } else { /* Assume RGBA subpixel order */ (*fptr_deinterlace_4field_rgba)(buffer, next_image->buffer, threshold, width, height); } break; } case ZM_COLOUR_GRAY8: (*fptr_deinterlace_4field_gray8)(buffer, next_image->buffer, threshold, width, height); break; default: Panic("Deinterlace_4Field called with unexpected colours: %d",colours); break; } } /************************************************* BLEND FUNCTIONS *************************************************/ __attribute__((noinline,__target__("sse2"))) void sse2_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) static uint32_t divider = 0; static uint32_t clearmask = 0; static double current_blendpercent = 0.0; if(current_blendpercent != blendpercent) { /* Attempt to match the blending percent to one of the possible values */ if(blendpercent < 2.34375) { // 1.5625% blending divider = 6; clearmask = 0x03030303; } else if(blendpercent < 4.6875) { // 3.125% blending divider = 5; clearmask = 0x07070707; } else if(blendpercent < 9.375) { // 6.25% blending divider = 4; clearmask = 0x0F0F0F0F; } else if(blendpercent < 18.75) { // 12.5% blending divider = 3; clearmask = 0x1F1F1F1F; } else if(blendpercent < 37.5) { // 25% blending divider = 2; clearmask = 0x3F3F3F3F; } else { // 50% blending divider = 1; clearmask = 0x7F7F7F7F; } current_blendpercent = blendpercent; } __asm__ __volatile__( "movd %4, %%xmm3\n\t" "movd %5, %%xmm4\n\t" "pshufd $0x0, %%xmm3, %%xmm3\n\t" "sub $0x10, %0\n\t" "sub $0x10, %1\n\t" "sub $0x10, %2\n\t" "sse2_fastblend_iter:\n\t" "movdqa (%0,%3),%%xmm0\n\t" "movdqa %%xmm0,%%xmm2\n\t" "movdqa (%1,%3),%%xmm1\n\t" "psrlq %%xmm4,%%xmm0\n\t" "psrlq %%xmm4,%%xmm1\n\t" "pand %%xmm3,%%xmm1\n\t" "pand %%xmm3,%%xmm0\n\t" "psubb %%xmm0,%%xmm1\n\t" "paddb %%xmm2,%%xmm1\n\t" "movntdq %%xmm1,(%2,%3)\n\t" "sub $0x10, %3\n\t" "jnz sse2_fastblend_iter\n\t" : : "r" (col1), "r" (col2), "r" (result), "r" (count), "m" (clearmask), "m" (divider) : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "cc", "memory" ); #else Panic("SSE function called on a non x86\\x86-64 platform"); #endif } __attribute__((noinline)) void std_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) { static int divider = 0; static double current_blendpercent = 0.0; const uint8_t* const max_ptr = result + count; if(current_blendpercent != blendpercent) { /* Attempt to match the blending percent to one of the possible values */ if(blendpercent < 2.34375) { // 1.5625% blending divider = 6; } else if(blendpercent < 4.6875) { // 3.125% blending divider = 5; } else if(blendpercent < 9.375) { // 6.25% blending divider = 4; } else if(blendpercent < 18.75) { // 12.5% blending divider = 3; } else if(blendpercent < 37.5) { // 25% blending divider = 2; } else { // 50% blending divider = 1; } current_blendpercent = blendpercent; } while(result < max_ptr) { result[0] = ((col2[0] - col1[0])>>divider) + col1[0]; result[1] = ((col2[1] - col1[1])>>divider) + col1[1]; result[2] = ((col2[2] - col1[2])>>divider) + col1[2]; result[3] = ((col2[3] - col1[3])>>divider) + col1[3]; result[4] = ((col2[4] - col1[4])>>divider) + col1[4]; result[5] = ((col2[5] - col1[5])>>divider) + col1[5]; result[6] = ((col2[6] - col1[6])>>divider) + col1[6]; result[7] = ((col2[7] - col1[7])>>divider) + col1[7]; result[8] = ((col2[8] - col1[8])>>divider) + col1[8]; result[9] = ((col2[9] - col1[9])>>divider) + col1[9]; result[10] = ((col2[10] - col1[10])>>divider) + col1[10]; result[11] = ((col2[11] - col1[11])>>divider) + col1[11]; result[12] = ((col2[12] - col1[12])>>divider) + col1[12]; result[13] = ((col2[13] - col1[13])>>divider) + col1[13]; result[14] = ((col2[14] - col1[14])>>divider) + col1[14]; result[15] = ((col2[15] - col1[15])>>divider) + col1[15]; col1 += 16; col2 += 16; result += 16; } } __attribute__((noinline)) void std_blend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) { double divide = blendpercent / 100.0; double opacity = 1.0 - divide; const uint8_t* const max_ptr = result + count; while(result < max_ptr) { *result++ = (*col1++ * opacity) + (*col2++ * divide); } } /************************************************* DELTA FUNCTIONS *************************************************/ /* Grayscale */ __attribute__((noinline)) void std_delta8_gray8(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { /* Loop unrolling is used to work on 16 bytes (16 grayscale pixels) at a time */ const uint8_t* const max_ptr = result + count; while(result < max_ptr) { result[0] = abs(col1[0] - col2[0]); result[1] = abs(col1[1] - col2[1]); result[2] = abs(col1[2] - col2[2]); result[3] = abs(col1[3] - col2[3]); result[4] = abs(col1[4] - col2[4]); result[5] = abs(col1[5] - col2[5]); result[6] = abs(col1[6] - col2[6]); result[7] = abs(col1[7] - col2[7]); result[8] = abs(col1[8] - col2[8]); result[9] = abs(col1[9] - col2[9]); result[10] = abs(col1[10] - col2[10]); result[11] = abs(col1[11] - col2[11]); result[12] = abs(col1[12] - col2[12]); result[13] = abs(col1[13] - col2[13]); result[14] = abs(col1[14] - col2[14]); result[15] = abs(col1[15] - col2[15]); col1 += 16; col2 += 16; result += 16; } } /* RGB24: RGB */ __attribute__((noinline)) void std_delta8_rgb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { /* Loop unrolling is used to work on 12 bytes (4 rgb24 pixels) at a time */ int r,g,b; const uint8_t* const max_ptr = result + count; while(result < max_ptr) { r = abs(col1[0] - col2[0]); g = abs(col1[1] - col2[1]); b = abs(col1[2] - col2[2]); result[0] = (r + r + b + g + g + g + g + g)>>3; r = abs(col1[3] - col2[3]); g = abs(col1[4] - col2[4]); b = abs(col1[5] - col2[5]); result[1] = (r + r + b + g + g + g + g + g)>>3; r = abs(col1[6] - col2[6]); g = abs(col1[7] - col2[7]); b = abs(col1[8] - col2[8]); result[2] = (r + r + b + g + g + g + g + g)>>3; r = abs(col1[9] - col2[9]); g = abs(col1[10] - col2[10]); b = abs(col1[11] - col2[11]); result[3] = (r + r + b + g + g + g + g + g)>>3; col1 += 12; col2 += 12; result += 4; } } /* RGB24: BGR */ __attribute__((noinline)) void std_delta8_bgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { /* Loop unrolling is used to work on 12 bytes (4 rgb24 pixels) at a time */ int r,g,b; const uint8_t* const max_ptr = result + count; while(result < max_ptr) { b = abs(col1[0] - col2[0]); g = abs(col1[1] - col2[1]); r = abs(col1[2] - col2[2]); result[0] = (r + r + b + g + g + g + g + g)>>3; b = abs(col1[3] - col2[3]); g = abs(col1[4] - col2[4]); r = abs(col1[5] - col2[5]); result[1] = (r + r + b + g + g + g + g + g)>>3; b = abs(col1[6] - col2[6]); g = abs(col1[7] - col2[7]); r = abs(col1[8] - col2[8]); result[2] = (r + r + b + g + g + g + g + g)>>3; b = abs(col1[9] - col2[9]); g = abs(col1[10] - col2[10]); r = abs(col1[11] - col2[11]); result[3] = (r + r + b + g + g + g + g + g)>>3; col1 += 12; col2 += 12; result += 4; } } /* RGB32: RGBA */ __attribute__((noinline)) void std_delta8_rgba(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { /* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */ int r,g,b; const uint8_t* const max_ptr = result + count; while(result < max_ptr) { r = abs(col1[0] - col2[0]); g = abs(col1[1] - col2[1]); b = abs(col1[2] - col2[2]); result[0] = (r + r + b + g + g + g + g + g)>>3; r = abs(col1[4] - col2[4]); g = abs(col1[5] - col2[5]); b = abs(col1[6] - col2[6]); result[1] = (r + r + b + g + g + g + g + g)>>3; r = abs(col1[8] - col2[8]); g = abs(col1[9] - col2[9]); b = abs(col1[10] - col2[10]); result[2] = (r + r + b + g + g + g + g + g)>>3; r = abs(col1[12] - col2[12]); g = abs(col1[13] - col2[13]); b = abs(col1[14] - col2[14]); result[3] = (r + r + b + g + g + g + g + g)>>3; col1 += 16; col2 += 16; result += 4; } } /* RGB32: BGRA */ __attribute__((noinline)) void std_delta8_bgra(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { /* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */ int r,g,b; const uint8_t* const max_ptr = result + count; while(result < max_ptr) { b = abs(col1[0] - col2[0]); g = abs(col1[1] - col2[1]); r = abs(col1[2] - col2[2]); result[0] = (r + r + b + g + g + g + g + g)>>3; b = abs(col1[4] - col2[4]); g = abs(col1[5] - col2[5]); r = abs(col1[6] - col2[6]); result[1] = (r + r + b + g + g + g + g + g)>>3; b = abs(col1[8] - col2[8]); g = abs(col1[9] - col2[9]); r = abs(col1[10] - col2[10]); result[2] = (r + r + b + g + g + g + g + g)>>3; b = abs(col1[12] - col2[12]); g = abs(col1[13] - col2[13]); r = abs(col1[14] - col2[14]); result[3] = (r + r + b + g + g + g + g + g)>>3; col1 += 16; col2 += 16; result += 4; } } /* RGB32: ARGB */ __attribute__((noinline)) void std_delta8_argb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { /* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */ int r,g,b; const uint8_t* const max_ptr = result + count; while(result < max_ptr) { r = abs(col1[1] - col2[1]); g = abs(col1[2] - col2[2]); b = abs(col1[3] - col2[3]); result[0] = (r + r + b + g + g + g + g + g)>>3; r = abs(col1[5] - col2[5]); g = abs(col1[6] - col2[6]); b = abs(col1[7] - col2[7]); result[1] = (r + r + b + g + g + g + g + g)>>3; r = abs(col1[9] - col2[9]); g = abs(col1[10] - col2[10]); b = abs(col1[11] - col2[11]); result[2] = (r + r + b + g + g + g + g + g)>>3; r = abs(col1[13] - col2[13]); g = abs(col1[14] - col2[14]); b = abs(col1[15] - col2[15]); result[3] = (r + r + b + g + g + g + g + g)>>3; col1 += 16; col2 += 16; result += 4; } } /* RGB32: ABGR */ __attribute__((noinline)) void std_delta8_abgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { /* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */ int r,g,b; const uint8_t* const max_ptr = result + count; while(result < max_ptr) { b = abs(col1[1] - col2[1]); g = abs(col1[2] - col2[2]); r = abs(col1[3] - col2[3]); result[0] = (r + r + b + g + g + g + g + g)>>3; b = abs(col1[5] - col2[5]); g = abs(col1[6] - col2[6]); r = abs(col1[7] - col2[7]); result[1] = (r + r + b + g + g + g + g + g)>>3; b = abs(col1[9] - col2[9]); g = abs(col1[10] - col2[10]); r = abs(col1[11] - col2[11]); result[2] = (r + r + b + g + g + g + g + g)>>3; b = abs(col1[13] - col2[13]); g = abs(col1[14] - col2[14]); r = abs(col1[15] - col2[15]); result[3] = (r + r + b + g + g + g + g + g)>>3; col1 += 16; col2 += 16; result += 4; } } /* Grayscale SSE2 */ __attribute__((noinline,__target__("sse2"))) void sse2_delta8_gray8(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) __asm__ __volatile__ ( "sub $0x10, %0\n\t" "sub $0x10, %1\n\t" "sub $0x10, %2\n\t" "sse2_delta8_gray8_iter:\n\t" "movdqa (%0,%3), %%xmm1\n\t" "movdqa (%1,%3), %%xmm2\n\t" "movdqa %%xmm1, %%xmm3\n\t" "movdqa %%xmm2, %%xmm4\n\t" "pmaxub %%xmm1, %%xmm2\n\t" "pminub %%xmm3, %%xmm4\n\t" "psubb %%xmm4, %%xmm2\n\t" "movntdq %%xmm2, (%2,%3)\n\t" "sub $0x10, %3\n\t" "jnz sse2_delta8_gray8_iter\n\t" : : "r" (col1), "r" (col2), "r" (result), "r" (count) : "%xmm1", "%xmm2", "%xmm3", "%xmm4", "cc", "memory" ); #else Panic("SSE function called on a non x86\\x86-64 platform"); #endif } /* RGB32: RGBA SSE2 */ __attribute__((noinline,__target__("sse2"))) void sse2_delta8_rgba(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) __asm__ __volatile__ ( "mov $0x1F1F1F1F, %%eax\n\t" "movd %%eax, %%xmm4\n\t" "pshufd $0x0, %%xmm4, %%xmm4\n\t" "mov $0xff, %%eax\n\t" "movd %%eax, %%xmm0\n\t" "pshufd $0x0, %%xmm0, %%xmm0\n\t" "sub $0x10, %0\n\t" "sub $0x10, %1\n\t" "sub $0x4, %2\n\t" "sse2_delta8_rgba_iter:\n\t" "movdqa (%0,%3,4), %%xmm1\n\t" "movdqa (%1,%3,4), %%xmm2\n\t" "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "movdqa %%xmm1, %%xmm5\n\t" "movdqa %%xmm2, %%xmm6\n\t" "pmaxub %%xmm1, %%xmm2\n\t" "pminub %%xmm5, %%xmm6\n\t" "psubb %%xmm6, %%xmm2\n\t" "movdqa %%xmm2, %%xmm3\n\t" "psrld $0x8, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "pslld $0x2, %%xmm2\n\t" "paddd %%xmm1, %%xmm2\n\t" "movdqa %%xmm3, %%xmm1\n\t" "pand %%xmm0, %%xmm1\n\t" "paddd %%xmm1, %%xmm1\n\t" "paddd %%xmm2, %%xmm1\n\t" "movdqa %%xmm3, %%xmm2\n\t" "psrld $0x10, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "paddd %%xmm2, %%xmm1\n\t" "packssdw %%xmm1, %%xmm1\n\t" "packuswb %%xmm1, %%xmm1\n\t" "movd %%xmm1, %%eax\n\t" "movnti %%eax, (%2,%3)\n\t" "sub $0x4, %3\n\t" "jnz sse2_delta8_rgba_iter\n\t" : : "r" (col1), "r" (col2), "r" (result), "r" (count) : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "cc", "memory" ); #else Panic("SSE function called on a non x86\\x86-64 platform"); #endif } /* RGB32: BGRA SSE2 */ __attribute__((noinline,__target__("sse2"))) void sse2_delta8_bgra(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) __asm__ __volatile__ ( "mov $0x1F1F1F1F, %%eax\n\t" "movd %%eax, %%xmm4\n\t" "pshufd $0x0, %%xmm4, %%xmm4\n\t" "mov $0xff, %%eax\n\t" "movd %%eax, %%xmm0\n\t" "pshufd $0x0, %%xmm0, %%xmm0\n\t" "sub $0x10, %0\n\t" "sub $0x10, %1\n\t" "sub $0x4, %2\n\t" "sse2_delta8_bgra_iter:\n\t" "movdqa (%0,%3,4), %%xmm1\n\t" "movdqa (%1,%3,4), %%xmm2\n\t" "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "movdqa %%xmm1, %%xmm5\n\t" "movdqa %%xmm2, %%xmm6\n\t" "pmaxub %%xmm1, %%xmm2\n\t" "pminub %%xmm5, %%xmm6\n\t" "psubb %%xmm6, %%xmm2\n\t" "movdqa %%xmm2, %%xmm3\n\t" "psrld $0x8, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "pslld $0x2, %%xmm2\n\t" "paddd %%xmm1, %%xmm2\n\t" "movdqa %%xmm3, %%xmm1\n\t" "pand %%xmm0, %%xmm1\n\t" "paddd %%xmm2, %%xmm1\n\t" "movdqa %%xmm3, %%xmm2\n\t" "psrld $0x10, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "paddd %%xmm2, %%xmm2\n\t" "paddd %%xmm2, %%xmm1\n\t" "packssdw %%xmm1, %%xmm1\n\t" "packuswb %%xmm1, %%xmm1\n\t" "movd %%xmm1, %%eax\n\t" "movnti %%eax, (%2,%3)\n\t" "sub $0x4, %3\n\t" "jnz sse2_delta8_bgra_iter\n\t" : : "r" (col1), "r" (col2), "r" (result), "r" (count) : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "cc", "memory" ); #else Panic("SSE function called on a non x86\\x86-64 platform"); #endif } /* RGB32: ARGB SSE2 */ __attribute__((noinline,__target__("sse2"))) void sse2_delta8_argb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) __asm__ __volatile__ ( "mov $0x1F1F1F1F, %%eax\n\t" "movd %%eax, %%xmm4\n\t" "pshufd $0x0, %%xmm4, %%xmm4\n\t" "mov $0xff, %%eax\n\t" "movd %%eax, %%xmm0\n\t" "pshufd $0x0, %%xmm0, %%xmm0\n\t" "sub $0x10, %0\n\t" "sub $0x10, %1\n\t" "sub $0x4, %2\n\t" "sse2_delta8_argb_iter:\n\t" "movdqa (%0,%3,4), %%xmm1\n\t" "movdqa (%1,%3,4), %%xmm2\n\t" "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "movdqa %%xmm1, %%xmm5\n\t" "movdqa %%xmm2, %%xmm6\n\t" "pmaxub %%xmm1, %%xmm2\n\t" "pminub %%xmm5, %%xmm6\n\t" "psubb %%xmm6, %%xmm2\n\t" "movdqa %%xmm2, %%xmm3\n\t" "psrld $0x10, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "pslld $0x2, %%xmm2\n\t" "paddd %%xmm1, %%xmm2\n\t" "movdqa %%xmm3, %%xmm1\n\t" "psrld $0x8, %%xmm1\n\t" "pand %%xmm0, %%xmm1\n\t" "paddd %%xmm1, %%xmm1\n\t" "paddd %%xmm2, %%xmm1\n\t" "movdqa %%xmm3, %%xmm2\n\t" "psrld $0x18, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "paddd %%xmm2, %%xmm1\n\t" "packssdw %%xmm1, %%xmm1\n\t" "packuswb %%xmm1, %%xmm1\n\t" "movd %%xmm1, %%eax\n\t" "movnti %%eax, (%2,%3)\n\t" "sub $0x4, %3\n\t" "jnz sse2_delta8_argb_iter\n\t" : : "r" (col1), "r" (col2), "r" (result), "r" (count) : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "cc", "memory" ); #else Panic("SSE function called on a non x86\\x86-64 platform"); #endif } /* RGB32: ABGR SSE2 */ __attribute__((noinline,__target__("sse2"))) void sse2_delta8_abgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) __asm__ __volatile__ ( "mov $0x1F1F1F1F, %%eax\n\t" "movd %%eax, %%xmm4\n\t" "pshufd $0x0, %%xmm4, %%xmm4\n\t" "mov $0xff, %%eax\n\t" "movd %%eax, %%xmm0\n\t" "pshufd $0x0, %%xmm0, %%xmm0\n\t" "sub $0x10, %0\n\t" "sub $0x10, %1\n\t" "sub $0x4, %2\n\t" "sse2_delta8_abgr_iter:\n\t" "movdqa (%0,%3,4), %%xmm1\n\t" "movdqa (%1,%3,4), %%xmm2\n\t" "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "movdqa %%xmm1, %%xmm5\n\t" "movdqa %%xmm2, %%xmm6\n\t" "pmaxub %%xmm1, %%xmm2\n\t" "pminub %%xmm5, %%xmm6\n\t" "psubb %%xmm6, %%xmm2\n\t" "movdqa %%xmm2, %%xmm3\n\t" "psrld $0x10, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "pslld $0x2, %%xmm2\n\t" "paddd %%xmm1, %%xmm2\n\t" "movdqa %%xmm3, %%xmm1\n\t" "psrld $0x8, %%xmm1\n\t" "pand %%xmm0, %%xmm1\n\t" "paddd %%xmm2, %%xmm1\n\t" "movdqa %%xmm3, %%xmm2\n\t" "psrld $0x18, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "paddd %%xmm2, %%xmm2\n\t" "paddd %%xmm2, %%xmm1\n\t" "packssdw %%xmm1, %%xmm1\n\t" "packuswb %%xmm1, %%xmm1\n\t" "movd %%xmm1, %%eax\n\t" "movnti %%eax, (%2,%3)\n\t" "sub $0x4, %3\n\t" "jnz sse2_delta8_abgr_iter\n\t" : : "r" (col1), "r" (col2), "r" (result), "r" (count) : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "cc", "memory" ); #else Panic("SSE function called on a non x86\\x86-64 platform"); #endif } /* RGB32: RGBA SSSE3 */ __attribute__((noinline,__target__("ssse3"))) void ssse3_delta8_rgba(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) __asm__ __volatile__ ( "mov $0x1F1F1F1F, %%eax\n\t" "movd %%eax, %%xmm4\n\t" "pshufd $0x0, %%xmm4, %%xmm4\n\t" "mov $0xff, %%eax\n\t" "movd %%eax, %%xmm0\n\t" "pshufd $0x0, %%xmm0, %%xmm0\n\t" "movdqa %4, %%xmm5\n\t" "sub $0x10, %0\n\t" "sub $0x10, %1\n\t" "sub $0x4, %2\n\t" "ssse3_delta8_rgba_iter:\n\t" "movdqa (%0,%3,4), %%xmm1\n\t" "movdqa (%1,%3,4), %%xmm2\n\t" "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm3\n\t" "movdqa %%xmm3, %%xmm2\n\t" "psrld $0x8, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "pslld $0x2, %%xmm2\n\t" "paddd %%xmm1, %%xmm2\n\t" "movdqa %%xmm3, %%xmm1\n\t" "pand %%xmm0, %%xmm1\n\t" "paddd %%xmm1, %%xmm1\n\t" "paddd %%xmm2, %%xmm1\n\t" "movdqa %%xmm3, %%xmm2\n\t" "psrld $0x10, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "paddd %%xmm2, %%xmm1\n\t" "pshufb %%xmm5, %%xmm1\n\t" "movd %%xmm1, %%eax\n\t" "movnti %%eax, (%2,%3)\n\t" "sub $0x4, %3\n\t" "jnz ssse3_delta8_rgba_iter\n\t" : : "r" (col1), "r" (col2), "r" (result), "r" (count), "m" (*movemask) : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "cc", "memory" ); #else Panic("SSE function called on a non x86\\x86-64 platform"); #endif } /* RGB32: BGRA SSSE3 */ __attribute__((noinline,__target__("ssse3"))) void ssse3_delta8_bgra(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) __asm__ __volatile__ ( "mov $0x1F1F1F1F, %%eax\n\t" "movd %%eax, %%xmm4\n\t" "pshufd $0x0, %%xmm4, %%xmm4\n\t" "mov $0xff, %%eax\n\t" "movd %%eax, %%xmm0\n\t" "pshufd $0x0, %%xmm0, %%xmm0\n\t" "movdqa %4, %%xmm5\n\t" "sub $0x10, %0\n\t" "sub $0x10, %1\n\t" "sub $0x4, %2\n\t" "ssse3_delta8_bgra_iter:\n\t" "movdqa (%0,%3,4), %%xmm1\n\t" "movdqa (%1,%3,4), %%xmm2\n\t" "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm3\n\t" "movdqa %%xmm3, %%xmm2\n\t" "psrld $0x8, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "pslld $0x2, %%xmm2\n\t" "paddd %%xmm1, %%xmm2\n\t" "movdqa %%xmm3, %%xmm1\n\t" "pand %%xmm0, %%xmm1\n\t" "paddd %%xmm2, %%xmm1\n\t" "movdqa %%xmm3, %%xmm2\n\t" "psrld $0x10, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "paddd %%xmm2, %%xmm2\n\t" "paddd %%xmm2, %%xmm1\n\t" "pshufb %%xmm5, %%xmm1\n\t" "movd %%xmm1, %%eax\n\t" "movnti %%eax, (%2,%3)\n\t" "sub $0x4, %3\n\t" "jnz ssse3_delta8_bgra_iter\n\t" : : "r" (col1), "r" (col2), "r" (result), "r" (count), "m" (*movemask) : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "cc", "memory" ); #else Panic("SSE function called on a non x86\\x86-64 platform"); #endif } /* RGB32: ARGB SSSE3 */ __attribute__((noinline,__target__("ssse3"))) void ssse3_delta8_argb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) __asm__ __volatile__ ( "mov $0x1F1F1F1F, %%eax\n\t" "movd %%eax, %%xmm4\n\t" "pshufd $0x0, %%xmm4, %%xmm4\n\t" "mov $0xff, %%eax\n\t" "movd %%eax, %%xmm0\n\t" "pshufd $0x0, %%xmm0, %%xmm0\n\t" "movdqa %4, %%xmm5\n\t" "sub $0x10, %0\n\t" "sub $0x10, %1\n\t" "sub $0x4, %2\n\t" "ssse3_delta8_argb_iter:\n\t" "movdqa (%0,%3,4), %%xmm1\n\t" "movdqa (%1,%3,4), %%xmm2\n\t" "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm3\n\t" "movdqa %%xmm3, %%xmm2\n\t" "psrld $0x10, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "pslld $0x2, %%xmm2\n\t" "paddd %%xmm1, %%xmm2\n\t" "movdqa %%xmm3, %%xmm1\n\t" "psrld $0x8, %%xmm1\n\t" "pand %%xmm0, %%xmm1\n\t" "paddd %%xmm1, %%xmm1\n\t" "paddd %%xmm2, %%xmm1\n\t" "movdqa %%xmm3, %%xmm2\n\t" "psrld $0x18, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "paddd %%xmm2, %%xmm1\n\t" "pshufb %%xmm5, %%xmm1\n\t" "movd %%xmm1, %%eax\n\t" "movnti %%eax, (%2,%3)\n\t" "sub $0x4, %3\n\t" "jnz ssse3_delta8_argb_iter\n\t" : : "r" (col1), "r" (col2), "r" (result), "r" (count), "m" (*movemask) : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "cc", "memory" ); #else Panic("SSE function called on a non x86\\x86-64 platform"); #endif } /* RGB32: ABGR SSSE3 */ __attribute__((noinline,__target__("ssse3"))) void ssse3_delta8_abgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) __asm__ __volatile__ ( "mov $0x1F1F1F1F, %%eax\n\t" "movd %%eax, %%xmm4\n\t" "pshufd $0x0, %%xmm4, %%xmm4\n\t" "mov $0xff, %%eax\n\t" "movd %%eax, %%xmm0\n\t" "pshufd $0x0, %%xmm0, %%xmm0\n\t" "movdqa %4, %%xmm5\n\t" "sub $0x10, %0\n\t" "sub $0x10, %1\n\t" "sub $0x4, %2\n\t" "ssse3_delta8_abgr_iter:\n\t" "movdqa (%0,%3,4), %%xmm1\n\t" "movdqa (%1,%3,4), %%xmm2\n\t" "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm3\n\t" "movdqa %%xmm3, %%xmm2\n\t" "psrld $0x10, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "pslld $0x2, %%xmm2\n\t" "paddd %%xmm1, %%xmm2\n\t" "movdqa %%xmm3, %%xmm1\n\t" "psrld $0x8, %%xmm1\n\t" "pand %%xmm0, %%xmm1\n\t" "paddd %%xmm2, %%xmm1\n\t" "movdqa %%xmm3, %%xmm2\n\t" "psrld $0x18, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "paddd %%xmm2, %%xmm2\n\t" "paddd %%xmm2, %%xmm1\n\t" "pshufb %%xmm5, %%xmm1\n\t" "movd %%xmm1, %%eax\n\t" "movnti %%eax, (%2,%3)\n\t" "sub $0x4, %3\n\t" "jnz ssse3_delta8_abgr_iter\n\t" : : "r" (col1), "r" (col2), "r" (result), "r" (count), "m" (*movemask) : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "cc", "memory" ); #else Panic("SSE function called on a non x86\\x86-64 platform"); #endif } /************************************************* CONVERT FUNCTIONS *************************************************/ /* RGB24 to grayscale */ __attribute__((noinline)) void std_convert_rgb_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { unsigned int r,g,b; const uint8_t* const max_ptr = result + count; while(result < max_ptr) { r = col1[0]; g = col1[1]; b = col1[2]; result[0] = (r + r + b + g + g + g + g + g)>>3; r = col1[3]; g = col1[4]; b = col1[5]; result[1] = (r + r + b + g + g + g + g + g)>>3; r = col1[6]; g = col1[7]; b = col1[8]; result[2] = (r + r + b + g + g + g + g + g)>>3; r = col1[9]; g = col1[10]; b = col1[11]; result[3] = (r + r + b + g + g + g + g + g)>>3; col1 += 12; result += 4; } } /* BGR24 to grayscale */ __attribute__((noinline)) void std_convert_bgr_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { unsigned int r,g,b; const uint8_t* const max_ptr = result + count; while(result < max_ptr) { b = col1[0]; g = col1[1]; r = col1[2]; result[0] = (r + r + b + g + g + g + g + g)>>3; b = col1[3]; g = col1[4]; r = col1[5]; result[1] = (r + r + b + g + g + g + g + g)>>3; b = col1[6]; g = col1[7]; r = col1[8]; result[2] = (r + r + b + g + g + g + g + g)>>3; b = col1[9]; g = col1[10]; r = col1[11]; result[3] = (r + r + b + g + g + g + g + g)>>3; col1 += 12; result += 4; } } /* RGBA to grayscale */ __attribute__((noinline)) void std_convert_rgba_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { unsigned int r,g,b; const uint8_t* const max_ptr = result + count; while(result < max_ptr) { r = col1[0]; g = col1[1]; b = col1[2]; result[0] = (r + r + b + g + g + g + g + g)>>3; r = col1[4]; g = col1[5]; b = col1[6]; result[1] = (r + r + b + g + g + g + g + g)>>3; r = col1[8]; g = col1[9]; b = col1[10]; result[2] = (r + r + b + g + g + g + g + g)>>3; r = col1[12]; g = col1[13]; b = col1[14]; result[3] = (r + r + b + g + g + g + g + g)>>3; col1 += 16; result += 4; } } /* BGRA to grayscale */ __attribute__((noinline)) void std_convert_bgra_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { unsigned int r,g,b; const uint8_t* const max_ptr = result + count; while(result < max_ptr) { b = col1[0]; g = col1[1]; r = col1[2]; result[0] = (r + r + b + g + g + g + g + g)>>3; b = col1[4]; g = col1[5]; r = col1[6]; result[1] = (r + r + b + g + g + g + g + g)>>3; b = col1[8]; g = col1[9]; r = col1[10]; result[2] = (r + r + b + g + g + g + g + g)>>3; b = col1[12]; g = col1[13]; r = col1[14]; result[3] = (r + r + b + g + g + g + g + g)>>3; col1 += 16; result += 4; } } /* ARGB to grayscale */ __attribute__((noinline)) void std_convert_argb_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { unsigned int r,g,b; const uint8_t* const max_ptr = result + count; while(result < max_ptr) { r = col1[1]; g = col1[2]; b = col1[3]; result[0] = (r + r + b + g + g + g + g + g)>>3; r = col1[5]; g = col1[6]; b = col1[7]; result[1] = (r + r + b + g + g + g + g + g)>>3; r = col1[9]; g = col1[10]; b = col1[11]; result[2] = (r + r + b + g + g + g + g + g)>>3; r = col1[13]; g = col1[14]; b = col1[15]; result[3] = (r + r + b + g + g + g + g + g)>>3; col1 += 16; result += 4; } } /* ABGR to grayscale */ __attribute__((noinline)) void std_convert_abgr_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { unsigned int r,g,b; const uint8_t* const max_ptr = result + count; while(result < max_ptr) { b = col1[1]; g = col1[2]; r = col1[3]; result[0] = (r + r + b + g + g + g + g + g)>>3; b = col1[5]; g = col1[6]; r = col1[7]; result[1] = (r + r + b + g + g + g + g + g)>>3; b = col1[9]; g = col1[10]; r = col1[11]; result[2] = (r + r + b + g + g + g + g + g)>>3; b = col1[13]; g = col1[14]; r = col1[15]; result[3] = (r + r + b + g + g + g + g + g)>>3; col1 += 16; result += 4; } } /* Converts a YUYV image into grayscale by extracting the Y channel */ __attribute__((noinline)) void std_convert_yuyv_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { const uint16_t* yuvbuf = (const uint16_t*)col1; const uint8_t* const max_ptr = result + count; while(result < max_ptr) { result[0] = (uint8_t)yuvbuf[0]; result[1] = (uint8_t)yuvbuf[1]; result[2] = (uint8_t)yuvbuf[2]; result[3] = (uint8_t)yuvbuf[3]; result[4] = (uint8_t)yuvbuf[4]; result[5] = (uint8_t)yuvbuf[5]; result[6] = (uint8_t)yuvbuf[6]; result[7] = (uint8_t)yuvbuf[7]; result[8] = (uint8_t)yuvbuf[8]; result[9] = (uint8_t)yuvbuf[9]; result[10] = (uint8_t)yuvbuf[10]; result[11] = (uint8_t)yuvbuf[11]; result[12] = (uint8_t)yuvbuf[12]; result[13] = (uint8_t)yuvbuf[13]; result[14] = (uint8_t)yuvbuf[14]; result[15] = (uint8_t)yuvbuf[15]; yuvbuf += 16; result += 16; } } /* RGBA to grayscale SSSE3 */ __attribute__((noinline,__target__("ssse3"))) void ssse3_convert_rgba_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) __asm__ __volatile__ ( "mov $0x1F1F1F1F, %%eax\n\t" "movd %%eax, %%xmm4\n\t" "pshufd $0x0, %%xmm4, %%xmm4\n\t" "mov $0xff, %%eax\n\t" "movd %%eax, %%xmm0\n\t" "pshufd $0x0, %%xmm0, %%xmm0\n\t" "movdqa %3, %%xmm5\n\t" "sub $0x10, %0\n\t" "sub $0x4, %1\n\t" "ssse3_convert_rgba_gray8_iter:\n\t" "movdqa (%0,%2,4), %%xmm3\n\t" "psrlq $0x3, %%xmm3\n\t" "pand %%xmm4, %%xmm3\n\t" "movdqa %%xmm3, %%xmm2\n\t" "psrld $0x8, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "pslld $0x2, %%xmm2\n\t" "paddd %%xmm1, %%xmm2\n\t" "movdqa %%xmm3, %%xmm1\n\t" "pand %%xmm0, %%xmm1\n\t" "paddd %%xmm1, %%xmm1\n\t" "paddd %%xmm2, %%xmm1\n\t" "movdqa %%xmm3, %%xmm2\n\t" "psrld $0x10, %%xmm2\n\t" "pand %%xmm0, %%xmm2\n\t" "paddd %%xmm2, %%xmm1\n\t" "pshufb %%xmm5, %%xmm1\n\t" "movd %%xmm1, %%eax\n\t" "movnti %%eax, (%1,%2)\n\t" "sub $0x4, %2\n\t" "jnz ssse3_convert_rgba_gray8_iter\n\t" : : "r" (col1), "r" (result), "r" (count), "m" (*movemask) : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "cc", "memory" ); #else Panic("SSE function called on a non x86\\x86-64 platform"); #endif } /* Converts a YUYV image into grayscale by extracting the Y channel */ __attribute__((noinline,__target__("ssse3"))) void ssse3_convert_yuyv_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) unsigned long i = 0; __attribute__((aligned(16))) static const uint8_t movemask1[16] = {0,2,4,6,8,10,12,14,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; __attribute__((aligned(16))) static const uint8_t movemask2[16] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0,2,4,6,8,10,12,14}; /* XMM0 - General purpose */ /* XMM1 - General purpose */ /* XMM2 - unused */ /* XMM3 - shift mask 1 */ /* XMM4 - shift mask 2 */ /* XMM5 - unused*/ /* XMM6 - unused */ /* XMM7 - unused */ __asm__ __volatile__ ( "movdqa %4, %%xmm3\n\t" "movdqa %5, %%xmm4\n\t" "algo_ssse3_convert_yuyv_gray8:\n\t" "movdqa (%0), %%xmm0\n\t" "pshufb %%xmm3, %%xmm0\n\t" "movdqa 0x10(%0), %%xmm1\n\t" "pshufb %%xmm4, %%xmm1\n\t" "por %%xmm1, %%xmm0\n\t" "movntdq %%xmm0, (%1)\n\t" "add $0x10, %3\n\t" "add $0x10, %1\n\t" "add $0x20, %0\n\t" "cmp %2, %3\n\t" "jb algo_ssse3_convert_yuyv_gray8\n\t" : #if (defined(_DEBUG) && !defined(__x86_64__)) /* Use one less register to allow compilation to success on 32bit with omit frame pointer disabled */ : "r" (col1), "r" (result), "m" (count), "r" (i), "m" (*movemask1), "m" (*movemask2) #else : "r" (col1), "r" (result), "r" (count), "r" (i), "m" (*movemask1), "m" (*movemask2) #endif : "%xmm3", "%xmm4", "cc", "memory" ); #else Panic("SSE function called on a non x86\\x86-64 platform"); #endif } /* YUYV to RGB24 - relocated from zm_local_camera.cpp */ __attribute__((noinline)) void zm_convert_yuyv_rgb(const uint8_t* col1, uint8_t* result, unsigned long count) { unsigned int r,g,b; unsigned int y1,y2,u,v; for(unsigned int i=0; i < count; i += 2, col1 += 4, result += 6) { y1 = col1[0]; u = col1[1]; y2 = col1[2]; v = col1[3]; r = y1 + r_v_table[v]; g = y1 - (g_u_table[u]+g_v_table[v]); b = y1 + b_u_table[u]; result[0] = r<0?0:(r>255?255:r); result[1] = g<0?0:(g>255?255:g); result[2] = b<0?0:(b>255?255:b); r = y2 + r_v_table[v]; g = y2 - (g_u_table[u]+g_v_table[v]); b = y2 + b_u_table[u]; result[3] = r<0?0:(r>255?255:r); result[4] = g<0?0:(g>255?255:g); result[5] = b<0?0:(b>255?255:b); } } /* YUYV to RGBA - modified the one above */ __attribute__((noinline)) void zm_convert_yuyv_rgba(const uint8_t* col1, uint8_t* result, unsigned long count) { unsigned int r,g,b; unsigned int y1,y2,u,v; for(unsigned int i=0; i < count; i += 2, col1 += 4, result += 8) { y1 = col1[0]; u = col1[1]; y2 = col1[2]; v = col1[3]; r = y1 + r_v_table[v]; g = y1 - (g_u_table[u]+g_v_table[v]); b = y1 + b_u_table[u]; result[0] = r<0?0:(r>255?255:r); result[1] = g<0?0:(g>255?255:g); result[2] = b<0?0:(b>255?255:b); r = y2 + r_v_table[v]; g = y2 - (g_u_table[u]+g_v_table[v]); b = y2 + b_u_table[u]; result[4] = r<0?0:(r>255?255:r); result[5] = g<0?0:(g>255?255:g); result[6] = b<0?0:(b>255?255:b); } } /* RGB555 to RGB24 - relocated from zm_local_camera.cpp */ __attribute__((noinline)) void zm_convert_rgb555_rgb(const uint8_t* col1, uint8_t* result, unsigned long count) { unsigned int r,g,b; for(unsigned int i=0; i < count; i++, col1 += 2, result += 3) { b = ((*col1)<<3)&0xf8; g = (((*(col1+1))<<6)|((*col1)>>2))&0xf8; r = ((*(col1+1))<<1)&0xf8; result[0] = r; result[1] = g; result[2] = b; } } /* RGB555 to RGBA - modified the one above */ __attribute__((noinline)) void zm_convert_rgb555_rgba(const uint8_t* col1, uint8_t* result, unsigned long count) { unsigned int r,g,b; for(unsigned int i=0; i < count; i++, col1 += 2, result += 4) { b = ((*col1)<<3)&0xf8; g = (((*(col1+1))<<6)|((*col1)>>2))&0xf8; r = ((*(col1+1))<<1)&0xf8; result[0] = r; result[1] = g; result[2] = b; } } /* RGB565 to RGB24 - relocated from zm_local_camera.cpp */ __attribute__((noinline)) void zm_convert_rgb565_rgb(const uint8_t* col1, uint8_t* result, unsigned long count) { unsigned int r,g,b; for(unsigned int i=0; i < count; i++, col1 += 2, result += 3) { b = ((*col1)<<3)&0xf8; g = (((*(col1+1))<<5)|((*col1)>>3))&0xfc; r = (*(col1+1))&0xf8; result[0] = r; result[1] = g; result[2] = b; } } /* RGB565 to RGBA - modified the one above */ __attribute__((noinline)) void zm_convert_rgb565_rgba(const uint8_t* col1, uint8_t* result, unsigned long count) { unsigned int r,g,b; for(unsigned int i=0; i < count; i++, col1 += 2, result += 4) { b = ((*col1)<<3)&0xf8; g = (((*(col1+1))<<5)|((*col1)>>3))&0xfc; r = (*(col1+1))&0xf8; result[0] = r; result[1] = g; result[2] = b; } } /************************************************* DEINTERLACE FUNCTIONS *************************************************/ /* Grayscale */ __attribute__((noinline)) void std_deinterlace_4field_gray8(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; const uint8_t* const max_ptr = col1 + (width*(height-1)); const uint8_t *max_ptr2; pcurrent = col1 + width; pncurrent = col2 + width; pabove = col1; pnabove = col2; pbelow = col1 + (width*2); while(pcurrent < max_ptr) { max_ptr2 = pcurrent + width; while(pcurrent < max_ptr2) { if((unsigned int)((abs(*pnabove - *pabove) + abs(*pncurrent - *pcurrent)) >> 1) >= threshold) { *pcurrent = (*pabove + *pbelow) >> 1; } pabove++; pnabove++; pcurrent++; pncurrent++; pbelow++; } pcurrent += width; pncurrent += width; pabove += width; pnabove += width; pbelow += width; } /* Special case for the last line */ max_ptr2 = pcurrent + width; while(pcurrent < max_ptr2) { if((unsigned int)((abs(*pnabove - *pabove) + abs(*pncurrent - *pcurrent)) >> 1) >= threshold) { *pcurrent = *pabove; } pabove++; pnabove++; pcurrent++; pncurrent++; } } /* RGB */ __attribute__((noinline)) void std_deinterlace_4field_rgb(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; const unsigned int row_width = width*3; const uint8_t* const max_ptr = col1 + (row_width * (height-1)); const uint8_t *max_ptr2; unsigned int b, g, r; unsigned int delta1, delta2; pcurrent = col1 + (width*3); pncurrent = col2 + (width*3); pabove = col1; pnabove = col2; pbelow = col1 + ((width*2)*3); while(pcurrent < max_ptr) { max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { r = abs(pnabove[0] - pabove[0]); g = abs(pnabove[1] - pabove[1]); b = abs(pnabove[2] - pabove[2]); delta1 = (r + r + b + g + g + g + g + g)>>3; r = abs(pncurrent[0] - pcurrent[0]); g = abs(pncurrent[1] - pcurrent[1]); b = abs(pncurrent[2] - pcurrent[2]); delta2 = (r + r + b + g + g + g + g + g)>>3; if(((delta1 + delta2) >> 1) >= threshold) { pcurrent[0] = (pabove[0] + pbelow[0]) >> 1; pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; } pabove += 3; pnabove += 3; pcurrent += 3; pncurrent += 3; pbelow += 3; } pcurrent += row_width; pncurrent += row_width; pabove += row_width; pnabove += row_width; pbelow += row_width; } /* Special case for the last line */ max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { r = abs(pnabove[0] - pabove[0]); g = abs(pnabove[1] - pabove[1]); b = abs(pnabove[2] - pabove[2]); delta1 = (r + r + b + g + g + g + g + g)>>3; r = abs(pncurrent[0] - pcurrent[0]); g = abs(pncurrent[1] - pcurrent[1]); b = abs(pncurrent[2] - pcurrent[2]); delta2 = (r + r + b + g + g + g + g + g)>>3; if(((delta1 + delta2) >> 1) >= threshold) { pcurrent[0] = pabove[0]; pcurrent[1] = pabove[1]; pcurrent[2] = pabove[2]; } pabove += 3; pnabove += 3; pcurrent += 3; pncurrent += 3; } } /* BGR */ __attribute__((noinline)) void std_deinterlace_4field_bgr(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; const unsigned int row_width = width*3; const uint8_t* const max_ptr = col1 + (row_width * (height-1)); const uint8_t *max_ptr2; unsigned int b, g, r; unsigned int delta1, delta2; pcurrent = col1 + (width*3); pncurrent = col2 + (width*3); pabove = col1; pnabove = col2; pbelow = col1 + ((width*2)*3); while(pcurrent < max_ptr) { max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { b = abs(pnabove[0] - pabove[0]); g = abs(pnabove[1] - pabove[1]); r = abs(pnabove[2] - pabove[2]); delta1 = (r + r + b + g + g + g + g + g)>>3; b = abs(pncurrent[0] - pcurrent[0]); g = abs(pncurrent[1] - pcurrent[1]); r = abs(pncurrent[2] - pcurrent[2]); delta2 = (r + r + b + g + g + g + g + g)>>3; if(((delta1 + delta2) >> 1) >= threshold) { pcurrent[0] = (pabove[0] + pbelow[0]) >> 1; pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; } pabove += 3; pnabove += 3; pcurrent += 3; pncurrent += 3; pbelow += 3; } pcurrent += row_width; pncurrent += row_width; pabove += row_width; pnabove += row_width; pbelow += row_width; } /* Special case for the last line */ max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { b = abs(pnabove[0] - pabove[0]); g = abs(pnabove[1] - pabove[1]); r = abs(pnabove[2] - pabove[2]); delta1 = (r + r + b + g + g + g + g + g)>>3; b = abs(pncurrent[0] - pcurrent[0]); g = abs(pncurrent[1] - pcurrent[1]); r = abs(pncurrent[2] - pcurrent[2]); delta2 = (r + r + b + g + g + g + g + g)>>3; if(((delta1 + delta2) >> 1) >= threshold) { pcurrent[0] = pabove[0]; pcurrent[1] = pabove[1]; pcurrent[2] = pabove[2]; } pabove += 3; pnabove += 3; pcurrent += 3; pncurrent += 3; } } /* RGBA */ __attribute__((noinline)) void std_deinterlace_4field_rgba(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; const unsigned int row_width = width*4; const uint8_t* const max_ptr = col1 + (row_width * (height-1)); const uint8_t *max_ptr2; unsigned int b, g, r; unsigned int delta1, delta2; pcurrent = col1 + row_width; pncurrent = col2 + row_width; pabove = col1; pnabove = col2; pbelow = col1 + (row_width*2); while(pcurrent < max_ptr) { max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { r = abs(pnabove[0] - pabove[0]); g = abs(pnabove[1] - pabove[1]); b = abs(pnabove[2] - pabove[2]); delta1 = (r + r + b + g + g + g + g + g)>>3; r = abs(pncurrent[0] - pcurrent[0]); g = abs(pncurrent[1] - pcurrent[1]); b = abs(pncurrent[2] - pcurrent[2]); delta2 = (r + r + b + g + g + g + g + g)>>3; if(((delta1 + delta2) >> 1) >= threshold) { pcurrent[0] = (pabove[0] + pbelow[0]) >> 1; pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; } pabove += 4; pnabove += 4; pcurrent += 4; pncurrent += 4; pbelow += 4; } pcurrent += row_width; pncurrent += row_width; pabove += row_width; pnabove += row_width; pbelow += row_width; } /* Special case for the last line */ max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { r = abs(pnabove[0] - pabove[0]); g = abs(pnabove[1] - pabove[1]); b = abs(pnabove[2] - pabove[2]); delta1 = (r + r + b + g + g + g + g + g)>>3; r = abs(pncurrent[0] - pcurrent[0]); g = abs(pncurrent[1] - pcurrent[1]); b = abs(pncurrent[2] - pcurrent[2]); delta2 = (r + r + b + g + g + g + g + g)>>3; if(((delta1 + delta2) >> 1) >= threshold) { pcurrent[0] = pabove[0]; pcurrent[1] = pabove[1]; pcurrent[2] = pabove[2]; } pabove += 4; pnabove += 4; pcurrent += 4; pncurrent += 4; } } /* BGRA */ __attribute__((noinline)) void std_deinterlace_4field_bgra(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; const unsigned int row_width = width*4; const uint8_t* const max_ptr = col1 + (row_width * (height-1)); const uint8_t *max_ptr2; unsigned int b, g, r; unsigned int delta1, delta2; pcurrent = col1 + row_width; pncurrent = col2 + row_width; pabove = col1; pnabove = col2; pbelow = col1 + (row_width*2); while(pcurrent < max_ptr) { max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { b = abs(pnabove[0] - pabove[0]); g = abs(pnabove[1] - pabove[1]); r = abs(pnabove[2] - pabove[2]); delta1 = (r + r + b + g + g + g + g + g)>>3; b = abs(pncurrent[0] - pcurrent[0]); g = abs(pncurrent[1] - pcurrent[1]); r = abs(pncurrent[2] - pcurrent[2]); delta2 = (r + r + b + g + g + g + g + g)>>3; if(((delta1 + delta2) >> 1) >= threshold) { pcurrent[0] = (pabove[0] + pbelow[0]) >> 1; pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; } pabove += 4; pnabove += 4; pcurrent += 4; pncurrent += 4; pbelow += 4; } pcurrent += row_width; pncurrent += row_width; pabove += row_width; pnabove += row_width; pbelow += row_width; } /* Special case for the last line */ max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { b = abs(pnabove[0] - pabove[0]); g = abs(pnabove[1] - pabove[1]); r = abs(pnabove[2] - pabove[2]); delta1 = (r + r + b + g + g + g + g + g)>>3; b = abs(pncurrent[0] - pcurrent[0]); g = abs(pncurrent[1] - pcurrent[1]); r = abs(pncurrent[2] - pcurrent[2]); delta2 = (r + r + b + g + g + g + g + g)>>3; if(((delta1 + delta2) >> 1) >= threshold) { pcurrent[0] = pabove[0]; pcurrent[1] = pabove[1]; pcurrent[2] = pabove[2]; } pabove += 4; pnabove += 4; pcurrent += 4; pncurrent += 4; } } /* ARGB */ __attribute__((noinline)) void std_deinterlace_4field_argb(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; const unsigned int row_width = width*4; const uint8_t* const max_ptr = col1 + (row_width * (height-1)); const uint8_t *max_ptr2; unsigned int b, g, r; unsigned int delta1, delta2; pcurrent = col1 + row_width; pncurrent = col2 + row_width; pabove = col1; pnabove = col2; pbelow = col1 + (row_width*2); while(pcurrent < max_ptr) { max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { r = abs(pnabove[1] - pabove[1]); g = abs(pnabove[2] - pabove[2]); b = abs(pnabove[3] - pabove[3]); delta1 = (r + r + b + g + g + g + g + g)>>3; r = abs(pncurrent[1] - pcurrent[1]); g = abs(pncurrent[2] - pcurrent[2]); b = abs(pncurrent[3] - pcurrent[3]); delta2 = (r + r + b + g + g + g + g + g)>>3; if(((delta1 + delta2) >> 1) >= threshold) { pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; pcurrent[3] = (pabove[3] + pbelow[3]) >> 1; } pabove += 4; pnabove += 4; pcurrent += 4; pncurrent += 4; pbelow += 4; } pcurrent += row_width; pncurrent += row_width; pabove += row_width; pnabove += row_width; pbelow += row_width; } /* Special case for the last line */ max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { r = abs(pnabove[1] - pabove[1]); g = abs(pnabove[2] - pabove[2]); b = abs(pnabove[3] - pabove[3]); delta1 = (r + r + b + g + g + g + g + g)>>3; r = abs(pncurrent[1] - pcurrent[1]); g = abs(pncurrent[2] - pcurrent[2]); b = abs(pncurrent[3] - pcurrent[3]); delta2 = (r + r + b + g + g + g + g + g)>>3; if(((delta1 + delta2) >> 1) >= threshold) { pcurrent[1] = pabove[1]; pcurrent[2] = pabove[2]; pcurrent[3] = pabove[3]; } pabove += 4; pnabove += 4; pcurrent += 4; pncurrent += 4; } } /* ABGR */ __attribute__((noinline)) void std_deinterlace_4field_abgr(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; const unsigned int row_width = width*4; const uint8_t* const max_ptr = col1 + (row_width * (height-1)); const uint8_t *max_ptr2; unsigned int b, g, r; unsigned int delta1, delta2; pcurrent = col1 + row_width; pncurrent = col2 + row_width; pabove = col1; pnabove = col2; pbelow = col1 + (row_width*2); while(pcurrent < max_ptr) { max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { b = abs(pnabove[1] - pabove[1]); g = abs(pnabove[2] - pabove[2]); r = abs(pnabove[3] - pabove[3]); delta1 = (r + r + b + g + g + g + g + g)>>3; b = abs(pncurrent[1] - pcurrent[1]); g = abs(pncurrent[2] - pcurrent[2]); r = abs(pncurrent[3] - pcurrent[3]); delta2 = (r + r + b + g + g + g + g + g)>>3; if(((delta1 + delta2) >> 1) >= threshold) { pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; pcurrent[3] = (pabove[3] + pbelow[3]) >> 1; } pabove += 4; pnabove += 4; pcurrent += 4; pncurrent += 4; pbelow += 4; } pcurrent += row_width; pncurrent += row_width; pabove += row_width; pnabove += row_width; pbelow += row_width; } /* Special case for the last line */ max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { b = abs(pnabove[1] - pabove[1]); g = abs(pnabove[2] - pabove[2]); r = abs(pnabove[3] - pabove[3]); delta1 = (r + r + b + g + g + g + g + g)>>3; b = abs(pncurrent[1] - pcurrent[1]); g = abs(pncurrent[2] - pcurrent[2]); r = abs(pncurrent[3] - pcurrent[3]); delta2 = (r + r + b + g + g + g + g + g)>>3; if(((delta1 + delta2) >> 1) >= threshold) { pcurrent[1] = pabove[1]; pcurrent[2] = pabove[2]; pcurrent[3] = pabove[3]; } pabove += 4; pnabove += 4; pcurrent += 4; pncurrent += 4; } } /* Grayscale SSSE3 */ __attribute__((noinline,__target__("ssse3"))) void ssse3_deinterlace_4field_gray8(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { union { uint32_t int32; uint8_t int8a[4]; } threshold_mask; threshold_mask.int8a[0] = threshold; threshold_mask.int8a[1] = 0; threshold_mask.int8a[2] = threshold; threshold_mask.int8a[3] = 0; unsigned long row_width = width; uint8_t* max_ptr = col1 + (row_width * (height-2)); uint8_t* max_ptr2 = col1 + row_width; __asm__ __volatile__ ( /* Load the threshold */ "mov %5, %%eax\n\t" "movd %%eax, %%xmm4\n\t" "pshufd $0x0, %%xmm4, %%xmm4\n\t" /* Zero the temporary register */ "pxor %%xmm0, %%xmm0\n\t" "algo_ssse3_deinterlace_4field_gray8:\n\t" /* Load pabove into xmm1 and pnabove into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ "pmaxub %%xmm2, %%xmm1\n\t" "pminub %%xmm5, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ /* Next row */ "add %4, %0\n\t" "add %4, %1\n\t" /* Load pcurrent into xmm1 and pncurrent into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ "pmaxub %%xmm2, %%xmm1\n\t" "pminub %%xmm6, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together "movdqa %%xmm1, %%xmm2\n\t" /* Do the comparison on words instead of bytes because we don't have unsigned comparison */ "punpcklbw %%xmm0, %%xmm1\n\t" // Expand pixels 0-7 into words into xmm1 "punpckhbw %%xmm0, %%xmm2\n\t" // Expand pixels 8-15 into words into xmm2 "pcmpgtw %%xmm4, %%xmm1\n\t" // Compare average delta with threshold for pixels 0-7 "pcmpgtw %%xmm4, %%xmm2\n\t" // Compare average delta with threshold for pixels 8-15 "packsswb %%xmm2, %%xmm1\n\t" // Pack the comparison results into xmm1 "movdqa (%0,%4), %%xmm2\n\t" // Load pbelow "pavgb %%xmm5, %%xmm2\n\t" // Average pabove and pbelow "pand %%xmm1, %%xmm2\n\t" // Filter out pixels in avg that shouldn't be copied "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced "por %%xmm2, %%xmm1\n\t" // Put the new values in pcurrent "movntdq %%xmm1, (%0)\n\t" // Write pcurrent "sub %4, %0\n\t" // Restore pcurrent to pabove "sub %4, %1\n\t" // Restore pncurrent to pnabove /* Next pixels */ "add $0x10, %0\n\t" // Add 16 to pcurrent "add $0x10, %1\n\t" // Add 16 to pncurrent /* Check if we reached the row end */ "cmp %2, %0\n\t" "jb algo_ssse3_deinterlace_4field_gray8\n\t" // Go for another iteration /* Next row */ "add %4, %0\n\t" // Add width to pcurrent "add %4, %1\n\t" // Add width to pncurrent "mov %0, %2\n\t" "add %4, %2\n\t" // Add width to max_ptr2 /* Check if we reached the end */ "cmp %3, %0\n\t" "jb algo_ssse3_deinterlace_4field_gray8\n\t" // Go for another iteration /* Special case for the last line */ /* Load pabove into xmm1 and pnabove into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ "pmaxub %%xmm2, %%xmm1\n\t" "pminub %%xmm5, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ /* Next row */ "add %4, %0\n\t" "add %4, %1\n\t" /* Load pcurrent into xmm1 and pncurrent into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ "pmaxub %%xmm2, %%xmm1\n\t" "pminub %%xmm6, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together "movdqa %%xmm1, %%xmm2\n\t" /* Do the comparison on words instead of bytes because we don't have unsigned comparison */ "punpcklbw %%xmm0, %%xmm1\n\t" // Expand pixels 0-7 into words into xmm1 "punpckhbw %%xmm0, %%xmm2\n\t" // Expand pixels 8-15 into words into xmm2 "pcmpgtw %%xmm4, %%xmm1\n\t" // Compare average delta with threshold for pixels 0-7 "pcmpgtw %%xmm4, %%xmm2\n\t" // Compare average delta with threshold for pixels 8-15 "packsswb %%xmm2, %%xmm1\n\t" // Pack the comparison results into xmm1 "pand %%xmm1, %%xmm5\n\t" // Filter out pixels in pabove that shouldn't be copied "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced "por %%xmm5, %%xmm1\n\t" // Put the new values in pcurrent "movntdq %%xmm1, (%0)\n\t" // Write pcurrent : : "r" (col1), "r" (col2), "r" (max_ptr2), "r" (max_ptr), "r" (row_width), "m" (threshold_mask.int32) : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" ); } /* RGBA SSSE3 */ __attribute__((noinline,__target__("ssse3"))) void ssse3_deinterlace_4field_rgba(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { __attribute__((aligned(16))) static const uint8_t movemask2[16] = {1,1,1,1,1,0,0,2,9,9,9,9,9,8,8,10}; const uint32_t threshold_val = threshold; unsigned long row_width = width*4; uint8_t* max_ptr = col1 + (row_width * (height-2)); uint8_t* max_ptr2 = col1 + row_width; __asm__ __volatile__ ( "mov $0x1F1F1F1F, %%eax\n\t" "movd %%eax, %%xmm4\n\t" "pshufd $0x0, %%xmm4, %%xmm4\n\t" "movdqa %6, %%xmm3\n\t" "mov %5, %%eax\n\t" #if defined(__x86_64__) "movd %%eax, %%xmm8\n\t" "pshufd $0x0, %%xmm8, %%xmm8\n\t" #endif /* Zero the temporary register */ "pxor %%xmm0, %%xmm0\n\t" "algo_ssse3_deinterlace_4field_rgba:\n\t" /* Load pabove into xmm1 and pnabove into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ /* Next row */ "add %4, %0\n\t" "add %4, %1\n\t" /* Load pcurrent into xmm1 and pncurrent into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else "movd %%eax, %%xmm7\n\t" // Setup the threshold "pshufd $0x0, %%xmm7, %%xmm7\n\t" "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif "movdqa (%0,%4), %%xmm2\n\t" // Load pbelow "pavgb %%xmm5, %%xmm2\n\t" // Average pabove and pbelow "pand %%xmm1, %%xmm2\n\t" // Filter out pixels in avg that shouldn't be copied "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced "por %%xmm2, %%xmm1\n\t" // Put the new values in pcurrent "movntdq %%xmm1, (%0)\n\t" // Write pcurrent "sub %4, %0\n\t" // Restore pcurrent to pabove "sub %4, %1\n\t" // Restore pncurrent to pnabove /* Next pixels */ "add $0x10, %0\n\t" // Add 16 to pcurrent "add $0x10, %1\n\t" // Add 16 to pncurrent /* Check if we reached the row end */ "cmp %2, %0\n\t" "jb algo_ssse3_deinterlace_4field_rgba\n\t" // Go for another iteration /* Next row */ "add %4, %0\n\t" // Add width to pcurrent "add %4, %1\n\t" // Add width to pncurrent "mov %0, %2\n\t" "add %4, %2\n\t" // Add width to max_ptr2 /* Check if we reached the end */ "cmp %3, %0\n\t" "jb algo_ssse3_deinterlace_4field_rgba\n\t" // Go for another iteration /* Special case for the last line */ /* Load pabove into xmm1 and pnabove into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ /* Next row */ "add %4, %0\n\t" "add %4, %1\n\t" /* Load pcurrent into xmm1 and pncurrent into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else "movd %%eax, %%xmm7\n\t" // Setup the threshold "pshufd $0x0, %%xmm7, %%xmm7\n\t" "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif "pand %%xmm1, %%xmm5\n\t" // Filter out pixels in pabove that shouldn't be copied "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced "por %%xmm5, %%xmm1\n\t" // Put the new values in pcurrent "movntdq %%xmm1, (%0)\n\t" // Write pcurrent : : "r" (col1), "r" (col2), "r" (max_ptr2), "r" (max_ptr), "r" (row_width), "m" (threshold_val), "m" (*movemask2) #if defined(__x86_64__) : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "cc", "memory" #else : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" #endif ); } /* BGRA SSSE3 */ __attribute__((noinline,__target__("ssse3"))) void ssse3_deinterlace_4field_bgra(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { __attribute__((aligned(16))) static const uint8_t movemask2[16] = {1,1,1,1,1,2,2,0,9,9,9,9,9,10,10,8}; const uint32_t threshold_val = threshold; unsigned long row_width = width*4; uint8_t* max_ptr = col1 + (row_width * (height-2)); uint8_t* max_ptr2 = col1 + row_width; __asm__ __volatile__ ( "mov $0x1F1F1F1F, %%eax\n\t" "movd %%eax, %%xmm4\n\t" "pshufd $0x0, %%xmm4, %%xmm4\n\t" "movdqa %6, %%xmm3\n\t" "mov %5, %%eax\n\t" #if defined(__x86_64__) "movd %%eax, %%xmm8\n\t" "pshufd $0x0, %%xmm8, %%xmm8\n\t" #endif /* Zero the temporary register */ "pxor %%xmm0, %%xmm0\n\t" "algo_ssse3_deinterlace_4field_bgra:\n\t" /* Load pabove into xmm1 and pnabove into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ /* Next row */ "add %4, %0\n\t" "add %4, %1\n\t" /* Load pcurrent into xmm1 and pncurrent into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else "movd %%eax, %%xmm7\n\t" // Setup the threshold "pshufd $0x0, %%xmm7, %%xmm7\n\t" "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif "movdqa (%0,%4), %%xmm2\n\t" // Load pbelow "pavgb %%xmm5, %%xmm2\n\t" // Average pabove and pbelow "pand %%xmm1, %%xmm2\n\t" // Filter out pixels in avg that shouldn't be copied "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced "por %%xmm2, %%xmm1\n\t" // Put the new values in pcurrent "movntdq %%xmm1, (%0)\n\t" // Write pcurrent "sub %4, %0\n\t" // Restore pcurrent to pabove "sub %4, %1\n\t" // Restore pncurrent to pnabove /* Next pixels */ "add $0x10, %0\n\t" // Add 16 to pcurrent "add $0x10, %1\n\t" // Add 16 to pncurrent /* Check if we reached the row end */ "cmp %2, %0\n\t" "jb algo_ssse3_deinterlace_4field_bgra\n\t" // Go for another iteration /* Next row */ "add %4, %0\n\t" // Add width to pcurrent "add %4, %1\n\t" // Add width to pncurrent "mov %0, %2\n\t" "add %4, %2\n\t" // Add width to max_ptr2 /* Check if we reached the end */ "cmp %3, %0\n\t" "jb algo_ssse3_deinterlace_4field_bgra\n\t" // Go for another iteration /* Special case for the last line */ /* Load pabove into xmm1 and pnabove into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ /* Next row */ "add %4, %0\n\t" "add %4, %1\n\t" /* Load pcurrent into xmm1 and pncurrent into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else "movd %%eax, %%xmm7\n\t" // Setup the threshold "pshufd $0x0, %%xmm7, %%xmm7\n\t" "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif "pand %%xmm1, %%xmm5\n\t" // Filter out pixels in pabove that shouldn't be copied "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced "por %%xmm5, %%xmm1\n\t" // Put the new values in pcurrent "movntdq %%xmm1, (%0)\n\t" // Write pcurrent : : "r" (col1), "r" (col2), "r" (max_ptr2), "r" (max_ptr), "r" (row_width), "m" (threshold_val), "m" (*movemask2) #if defined(__x86_64__) : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "cc", "memory" #else : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" #endif ); } /* ARGB SSSE3 */ __attribute__((noinline,__target__("ssse3"))) void ssse3_deinterlace_4field_argb(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { __attribute__((aligned(16))) static const uint8_t movemask2[16] = {2,2,2,2,2,1,1,3,10,10,10,10,10,9,9,11}; const uint32_t threshold_val = threshold; unsigned long row_width = width*4; uint8_t* max_ptr = col1 + (row_width * (height-2)); uint8_t* max_ptr2 = col1 + row_width; __asm__ __volatile__ ( "mov $0x1F1F1F1F, %%eax\n\t" "movd %%eax, %%xmm4\n\t" "pshufd $0x0, %%xmm4, %%xmm4\n\t" "movdqa %6, %%xmm3\n\t" "mov %5, %%eax\n\t" #if defined(__x86_64__) "movd %%eax, %%xmm8\n\t" "pshufd $0x0, %%xmm8, %%xmm8\n\t" #endif /* Zero the temporary register */ "pxor %%xmm0, %%xmm0\n\t" "algo_ssse3_deinterlace_4field_argb:\n\t" /* Load pabove into xmm1 and pnabove into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ /* Next row */ "add %4, %0\n\t" "add %4, %1\n\t" /* Load pcurrent into xmm1 and pncurrent into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else "movd %%eax, %%xmm7\n\t" // Setup the threshold "pshufd $0x0, %%xmm7, %%xmm7\n\t" "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif "movdqa (%0,%4), %%xmm2\n\t" // Load pbelow "pavgb %%xmm5, %%xmm2\n\t" // Average pabove and pbelow "pand %%xmm1, %%xmm2\n\t" // Filter out pixels in avg that shouldn't be copied "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced "por %%xmm2, %%xmm1\n\t" // Put the new values in pcurrent "movntdq %%xmm1, (%0)\n\t" // Write pcurrent "sub %4, %0\n\t" // Restore pcurrent to pabove "sub %4, %1\n\t" // Restore pncurrent to pnabove /* Next pixels */ "add $0x10, %0\n\t" // Add 16 to pcurrent "add $0x10, %1\n\t" // Add 16 to pncurrent /* Check if we reached the row end */ "cmp %2, %0\n\t" "jb algo_ssse3_deinterlace_4field_argb\n\t" // Go for another iteration /* Next row */ "add %4, %0\n\t" // Add width to pcurrent "add %4, %1\n\t" // Add width to pncurrent "mov %0, %2\n\t" "add %4, %2\n\t" // Add width to max_ptr2 /* Check if we reached the end */ "cmp %3, %0\n\t" "jb algo_ssse3_deinterlace_4field_argb\n\t" // Go for another iteration /* Special case for the last line */ /* Load pabove into xmm1 and pnabove into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ /* Next row */ "add %4, %0\n\t" "add %4, %1\n\t" /* Load pcurrent into xmm1 and pncurrent into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else "movd %%eax, %%xmm7\n\t" // Setup the threshold "pshufd $0x0, %%xmm7, %%xmm7\n\t" "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif "pand %%xmm1, %%xmm5\n\t" // Filter out pixels in pabove that shouldn't be copied "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced "por %%xmm5, %%xmm1\n\t" // Put the new values in pcurrent "movntdq %%xmm1, (%0)\n\t" // Write pcurrent : : "r" (col1), "r" (col2), "r" (max_ptr2), "r" (max_ptr), "r" (row_width), "m" (threshold_val), "m" (*movemask2) #if defined(__x86_64__) : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "cc", "memory" #else : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" #endif ); } /* ABGR SSSE3 */ __attribute__((noinline,__target__("ssse3"))) void ssse3_deinterlace_4field_abgr(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { __attribute__((aligned(16))) static const uint8_t movemask2[16] = {2,2,2,2,2,3,3,1,10,10,10,10,10,11,11,9}; const uint32_t threshold_val = threshold; unsigned long row_width = width*4; uint8_t* max_ptr = col1 + (row_width * (height-2)); uint8_t* max_ptr2 = col1 + row_width; __asm__ __volatile__ ( "mov $0x1F1F1F1F, %%eax\n\t" "movd %%eax, %%xmm4\n\t" "pshufd $0x0, %%xmm4, %%xmm4\n\t" "movdqa %6, %%xmm3\n\t" "mov %5, %%eax\n\t" #if defined(__x86_64__) "movd %%eax, %%xmm8\n\t" "pshufd $0x0, %%xmm8, %%xmm8\n\t" #endif /* Zero the temporary register */ "pxor %%xmm0, %%xmm0\n\t" "algo_ssse3_deinterlace_4field_abgr:\n\t" /* Load pabove into xmm1 and pnabove into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ /* Next row */ "add %4, %0\n\t" "add %4, %1\n\t" /* Load pcurrent into xmm1 and pncurrent into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else "movd %%eax, %%xmm7\n\t" // Setup the threshold "pshufd $0x0, %%xmm7, %%xmm7\n\t" "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif "movdqa (%0,%4), %%xmm2\n\t" // Load pbelow "pavgb %%xmm5, %%xmm2\n\t" // Average pabove and pbelow "pand %%xmm1, %%xmm2\n\t" // Filter out pixels in avg that shouldn't be copied "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced "por %%xmm2, %%xmm1\n\t" // Put the new values in pcurrent "movntdq %%xmm1, (%0)\n\t" // Write pcurrent "sub %4, %0\n\t" // Restore pcurrent to pabove "sub %4, %1\n\t" // Restore pncurrent to pnabove /* Next pixels */ "add $0x10, %0\n\t" // Add 16 to pcurrent "add $0x10, %1\n\t" // Add 16 to pncurrent /* Check if we reached the row end */ "cmp %2, %0\n\t" "jb algo_ssse3_deinterlace_4field_abgr\n\t" // Go for another iteration /* Next row */ "add %4, %0\n\t" // Add width to pcurrent "add %4, %1\n\t" // Add width to pncurrent "mov %0, %2\n\t" "add %4, %2\n\t" // Add width to max_ptr2 /* Check if we reached the end */ "cmp %3, %0\n\t" "jb algo_ssse3_deinterlace_4field_abgr\n\t" // Go for another iteration /* Special case for the last line */ /* Load pabove into xmm1 and pnabove into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ /* Next row */ "add %4, %0\n\t" "add %4, %1\n\t" /* Load pcurrent into xmm1 and pncurrent into xmm2 */ "movdqa (%0), %%xmm1\n\t" "movdqa (%1), %%xmm2\n\t" "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ "psrlq $0x3, %%xmm1\n\t" "psrlq $0x3, %%xmm2\n\t" "pand %%xmm4, %%xmm1\n\t" "pand %%xmm4, %%xmm2\n\t" "psubb %%xmm2, %%xmm1\n\t" "pabsb %%xmm1, %%xmm2\n\t" "movdqa %%xmm2, %%xmm1\n\t" "punpckldq %%xmm1, %%xmm1\n\t" "pshufb %%xmm3, %%xmm1\n\t" "psadbw %%xmm0, %%xmm1\n\t" "punpckhdq %%xmm2, %%xmm2\n\t" "pshufb %%xmm3, %%xmm2\n\t" "psadbw %%xmm0, %%xmm2\n\t" "packuswb %%xmm2, %%xmm1\n\t" "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else "movd %%eax, %%xmm7\n\t" // Setup the threshold "pshufd $0x0, %%xmm7, %%xmm7\n\t" "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif "pand %%xmm1, %%xmm5\n\t" // Filter out pixels in pabove that shouldn't be copied "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced "por %%xmm5, %%xmm1\n\t" // Put the new values in pcurrent "movntdq %%xmm1, (%0)\n\t" // Write pcurrent : : "r" (col1), "r" (col2), "r" (max_ptr2), "r" (max_ptr), "r" (row_width), "m" (threshold_val), "m" (*movemask2) #if defined(__x86_64__) : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "cc", "memory" #else : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" #endif ); } ZoneMinder-1.26.5/src/zm_image.h000066400000000000000000000323241225361755400164250ustar00rootroot00000000000000// // ZoneMinder Image Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_IMAGE_H #define ZM_IMAGE_H #include "zm.h" extern "C" { #include "zm_jpeg.h" } #include "zm_rgb.h" #include "zm_coord.h" #include "zm_box.h" #include "zm_poly.h" #include "zm_mem_utils.h" #include "zm_utils.h" #include #if HAVE_ZLIB_H #include #endif // HAVE_ZLIB_H #define ZM_BUFTYPE_DONTFREE 0 #define ZM_BUFTYPE_MALLOC 1 #define ZM_BUFTYPE_NEW 2 #define ZM_BUFTYPE_AVMALLOC 3 #define ZM_BUFTYPE_ZM 4 typedef void (*blend_fptr_t)(const uint8_t*, const uint8_t*, uint8_t*, unsigned long, double); typedef void (*delta_fptr_t)(const uint8_t*, const uint8_t*, uint8_t*, unsigned long); typedef void (*convert_fptr_t)(const uint8_t*, uint8_t*, unsigned long); typedef void (*deinterlace_4field_fptr_t)(uint8_t*, uint8_t*, unsigned int, unsigned int, unsigned int); typedef void* (*imgbufcpy_fptr_t)(void*, const void*, size_t); extern imgbufcpy_fptr_t fptr_imgbufcpy; /* Should be called from Image class functions */ inline static uint8_t* AllocBuffer(size_t p_bufsize) { uint8_t* buffer = (uint8_t*)zm_mallocaligned(16,p_bufsize); if(buffer == NULL) Fatal("Memory allocation failed: %s",strerror(errno)); return buffer; } inline static void DumpBuffer(uint8_t* buffer, int buffertype) { if (buffer && buffertype != ZM_BUFTYPE_DONTFREE) { if(buffertype == ZM_BUFTYPE_ZM) zm_freealigned(buffer); else if(buffertype == ZM_BUFTYPE_MALLOC) free(buffer); else if(buffertype == ZM_BUFTYPE_NEW) delete buffer; /*else if(buffertype == ZM_BUFTYPE_AVMALLOC) av_free(buffer); */ } } // // This is image class, and represents a frame captured from a // camera in raw form. // class Image { protected: struct Edge { int min_y; int max_y; double min_x; double _1_m; static int CompareYX( const void *p1, const void *p2 ) { const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2; if ( e1->min_y == e2->min_y ) return( int(e1->min_x - e2->min_x) ); else return( int(e1->min_y - e2->min_y) ); } static int CompareX( const void *p1, const void *p2 ) { const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2; return( int(e1->min_x - e2->min_x) ); } }; inline void DumpImgBuffer() { DumpBuffer(buffer,buffertype); buffer = NULL; allocation = 0; } inline void AllocImgBuffer(size_t p_bufsize) { if(buffer) DumpImgBuffer(); buffer = AllocBuffer(p_bufsize); buffertype = ZM_BUFTYPE_ZM; allocation = p_bufsize; } public: enum { CHAR_HEIGHT=11, CHAR_WIDTH=6 }; enum { LINE_HEIGHT=CHAR_HEIGHT+0 }; protected: static bool initialised; static unsigned char *abs_table; static unsigned char *y_r_table; static unsigned char *y_g_table; static unsigned char *y_b_table; static jpeg_compress_struct *jpg_ccinfo[101]; static jpeg_decompress_struct *jpg_dcinfo; static struct zm_error_mgr jpg_err; protected: unsigned int width; unsigned int height; unsigned int pixels; unsigned int colours; unsigned int size; unsigned int subpixelorder; unsigned long allocation; uint8_t *buffer; int buffertype; /* 0=not ours, no need to call free(), 1=malloc() buffer, 2=new buffer */ int holdbuffer; /* Hold the buffer instead of replacing it with new one */ char text[1024]; protected: static void Initialise(); public: Image(); Image( const char *filename ); Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer=0); Image( const Image &p_image ); ~Image(); inline unsigned int Width() const { return( width ); } inline unsigned int Height() const { return( height ); } inline unsigned int Pixels() const { return( pixels ); } inline unsigned int Colours() const { return( colours ); } inline unsigned int SubpixelOrder() const { return( subpixelorder ); } inline unsigned int Size() const { return( size ); } /* Internal buffer should not be modified from functions outside of this class */ inline const uint8_t* Buffer() const { return( buffer ); } inline const uint8_t* Buffer( unsigned int x, unsigned int y= 0 ) const { return( &buffer[colours*((y*width)+x)] ); } /* Request writeable buffer */ uint8_t* WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder); inline int IsBufferHeld() const { return holdbuffer; } inline void HoldBuffer(int tohold) { holdbuffer = tohold; } inline void Empty() { if(!holdbuffer) DumpImgBuffer(); width = height = colours = size = pixels = subpixelorder = 0; } void Assign( unsigned int p_width, unsigned int p_height, unsigned int p_colours, unsigned int p_subpixelorder, const uint8_t* new_buffer, const size_t buffer_size); void Assign( const Image &image ); void AssignDirect( const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, uint8_t *new_buffer, const size_t buffer_size, const int p_buffertype); inline void CopyBuffer( const Image &image ) { Assign(image); } inline Image &operator=( const Image &image ) { Assign(image); return *this; } inline Image &operator=( const unsigned char *new_buffer ) { (*fptr_imgbufcpy)(buffer, new_buffer, size); return( *this ); } bool ReadRaw( const char *filename ); bool WriteRaw( const char *filename ) const; bool ReadJpeg( const char *filename, unsigned int p_colours, unsigned int p_subpixelorder); bool WriteJpeg( const char *filename, int quality_override=0 ) const; bool DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size, unsigned int p_colours, unsigned int p_subpixelorder); bool EncodeJpeg( JOCTET *outbuffer, int *outbuffer_size, int quality_override=0 ) const; #if HAVE_ZLIB_H bool Unzip( const Bytef *inbuffer, unsigned long inbuffer_size ); bool Zip( Bytef *outbuffer, unsigned long *outbuffer_size, int compression_level=Z_BEST_SPEED ) const; #endif // HAVE_ZLIB_H bool Crop( unsigned int lo_x, unsigned int lo_y, unsigned int hi_x, unsigned int hi_y ); bool Crop( const Box &limits ); void Overlay( const Image &image ); void Overlay( const Image &image, unsigned int x, unsigned int y ); void Blend( const Image &image, int transparency=12 ); static Image *Merge( unsigned int n_images, Image *images[] ); static Image *Merge( unsigned int n_images, Image *images[], double weight ); static Image *Highlight( unsigned int n_images, Image *images[], const Rgb threshold=RGB_BLACK, const Rgb ref_colour=RGB_RED ); //Image *Delta( const Image &image ) const; void Delta( const Image &image, Image* targetimage) const; const Coord centreCoord( const char *text ) const; void Annotate( const char *p_text, const Coord &coord, const Rgb fg_colour=RGB_WHITE, const Rgb bg_colour=RGB_BLACK ); Image *HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits=0 ); //Image *HighlightEdges( Rgb colour, const Polygon &polygon ); void Timestamp( const char *label, const time_t when, const Coord &coord ); void Colourise(const unsigned int p_reqcolours, const unsigned int p_reqsubpixelorder); void DeColourise(); void Clear() { memset( buffer, 0, size ); } void Fill( Rgb colour, const Box *limits=0 ); void Fill( Rgb colour, int density, const Box *limits=0 ); void Outline( Rgb colour, const Polygon &polygon ); void Fill( Rgb colour, const Polygon &polygon ); void Fill( Rgb colour, int density, const Polygon &polygon ); void Rotate( int angle ); void Flip( bool leftright ); void Scale( unsigned int factor ); void Deinterlace_Discard(); void Deinterlace_Linear(); void Deinterlace_Blend(); void Deinterlace_Blend_CustomRatio(int divider); void Deinterlace_4Field(const Image* next_image, unsigned int threshold); }; #endif // ZM_IMAGE_H /* Blend functions */ void sse2_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent); void std_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent); void std_blend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent); /* Delta functions */ void std_delta8_gray8(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void std_delta8_rgb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void std_delta8_bgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void std_delta8_rgba(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void std_delta8_bgra(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void std_delta8_argb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void std_delta8_abgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void sse2_delta8_gray8(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void sse2_delta8_rgba(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void sse2_delta8_bgra(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void sse2_delta8_argb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void sse2_delta8_abgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void ssse3_delta8_rgba(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void ssse3_delta8_bgra(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void ssse3_delta8_argb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void ssse3_delta8_abgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); /* Convert functions */ void std_convert_rgb_gray8(const uint8_t* col1, uint8_t* result, unsigned long count); void std_convert_bgr_gray8(const uint8_t* col1, uint8_t* result, unsigned long count); void std_convert_rgba_gray8(const uint8_t* col1, uint8_t* result, unsigned long count); void std_convert_bgra_gray8(const uint8_t* col1, uint8_t* result, unsigned long count); void std_convert_argb_gray8(const uint8_t* col1, uint8_t* result, unsigned long count); void std_convert_abgr_gray8(const uint8_t* col1, uint8_t* result, unsigned long count); void std_convert_yuyv_gray8(const uint8_t* col1, uint8_t* result, unsigned long count); void ssse3_convert_rgba_gray8(const uint8_t* col1, uint8_t* result, unsigned long count); void ssse3_convert_yuyv_gray8(const uint8_t* col1, uint8_t* result, unsigned long count); void zm_convert_yuyv_rgb(const uint8_t* col1, uint8_t* result, unsigned long count); void zm_convert_yuyv_rgba(const uint8_t* col1, uint8_t* result, unsigned long count); void zm_convert_rgb555_rgb(const uint8_t* col1, uint8_t* result, unsigned long count); void zm_convert_rgb555_rgba(const uint8_t* col1, uint8_t* result, unsigned long count); void zm_convert_rgb565_rgb(const uint8_t* col1, uint8_t* result, unsigned long count); void zm_convert_rgb565_rgba(const uint8_t* col1, uint8_t* result, unsigned long count); /* Deinterlace_4Field functions */ void std_deinterlace_4field_gray8(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height); void std_deinterlace_4field_rgb(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height); void std_deinterlace_4field_bgr(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height); void std_deinterlace_4field_rgba(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height); void std_deinterlace_4field_bgra(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height); void std_deinterlace_4field_argb(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height); void std_deinterlace_4field_abgr(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height); void ssse3_deinterlace_4field_gray8(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height); void ssse3_deinterlace_4field_rgba(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height); void ssse3_deinterlace_4field_bgra(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height); void ssse3_deinterlace_4field_argb(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height); void ssse3_deinterlace_4field_abgr(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height); ZoneMinder-1.26.5/src/zm_image_analyser.cpp000066400000000000000000000033041225361755400206520ustar00rootroot00000000000000#include "zm_image_analyser.h" /*!\fn ImageAnalyser::ImageAnalyser(const ImageAnalyser& source) * \param source is the object to copy */ ImageAnalyser::ImageAnalyser(const ImageAnalyser& source) { m_Detectors = source.m_Detectors; } /*!\fn ImageAnalyser::operator=(const ImageAnalyser& source) * \param source is the object to copy */ ImageAnalyser& ImageAnalyser::operator=(const ImageAnalyser& source) { m_Detectors = source.m_Detectors; return *this; } ImageAnalyser::~ImageAnalyser() { for(DetectorsList::reverse_iterator It = m_Detectors.rbegin(); It != m_Detectors.rend(); ++It) delete *It; } /*!\fn ImageAnalyser::DoDetection(const Image &comp_image, Zone** zones, int n_numZones, Event::StringSetMap noteSetMap, std::string& det_cause) * \param comp_image is the image to analyse * \param zones is the zones array to analyse * \param n_numZones is the number of zones * \param noteSetMap is the map of events descriptions * \param det_cause is a string describing detection cause */ int ImageAnalyser::DoDetection(const Image &comp_image, Zone** zones, int n_numZones, Event::StringSetMap noteSetMap, std::string& det_cause) { Event::StringSet zoneSet; int score = 0; for(DetectorsList::iterator It = m_Detectors.begin(); It != m_Detectors.end(); ++It) { int detect_score = (*It)->Detect(comp_image, zones, n_numZones, zoneSet); if (detect_score) { score += detect_score; noteSetMap[(*It)->getDetectionCause()] = zoneSet; if (det_cause.length()) det_cause += ", "; det_cause += (*It)->getDetectionCause(); } } return score; } ZoneMinder-1.26.5/src/zm_image_analyser.h000066400000000000000000000012051225361755400203150ustar00rootroot00000000000000#ifndef ZM_IMAGE_ANALYSER_H #define ZM_IMAGE_ANALYSER_H #include #include #include #include #include #include "zm.h" #include "zm_image.h" #include "zm_zone.h" #include "zm_event.h" using namespace std; //! Class for handling image detection. class ImageAnalyser { public: //!Default constructor. ImageAnalyser() {}; //! Destructor. ~ImageAnalyser(); //! Copy constructor. ImageAnalyser(const ImageAnalyser& source); //! Overloaded operator=. ImageAnalyser& operator=(const ImageAnalyser& source); private: }; #endif //ZM_IMAGE_ANALYSER_H ZoneMinder-1.26.5/src/zm_jpeg.cpp000066400000000000000000000410771225361755400166300ustar00rootroot00000000000000/* * ZoneMinder JPEG memory encoding/decoding, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "zm_jpeg.h" #include "zm_logger.h" #include /* Overridden error handlers, mostly for decompression */ extern "C" { #define MAX_JPEG_ERRS 25 static int jpeg_err_count = 0; void zm_jpeg_error_exit( j_common_ptr cinfo ) { static char buffer[JMSG_LENGTH_MAX]; zm_error_ptr zmerr = (zm_error_ptr)cinfo->err; (zmerr->pub.format_message)( cinfo, buffer ); Error( "%s", buffer ); if ( ++jpeg_err_count == MAX_JPEG_ERRS ) { Fatal( "Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count ); } longjmp( zmerr->setjmp_buffer, 1 ); } void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level ) { static char buffer[JMSG_LENGTH_MAX]; zm_error_ptr zmerr = (zm_error_ptr)cinfo->err; if ( msg_level < 0 ) { /* It's a warning message. Since corrupt files may generate many warnings, * the policy implemented here is to show only the first warning, * unless trace_level >= 3. */ if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 ) { (zmerr->pub.format_message)( cinfo, buffer ); if (!strstr(buffer, "Corrupt JPEG data:")) Warning( "%s", buffer ); } /* Always count warnings in num_warnings. */ zmerr->pub.num_warnings++; } else { /* It's a trace message. Show it if trace_level >= msg_level. */ if ( zmerr->pub.trace_level >= msg_level ) { (zmerr->pub.format_message)( cinfo, buffer ); Debug( msg_level, "%s", buffer ); } } } /* Expanded data destination object for memory */ typedef struct { struct jpeg_destination_mgr pub; /* public fields */ JOCTET *outbuffer; /* target buffer */ int *outbuffer_size; JOCTET *buffer; /* start of buffer */ } mem_destination_mgr; typedef mem_destination_mgr * mem_dest_ptr; #define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ /* * Initialize destination --- called by jpeg_start_compress * before any data is actually written. */ static void init_destination (j_compress_ptr cinfo) { mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; /* Allocate the output buffer --- it will be released when done with image */ dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * SIZEOF(JOCTET)); dest->pub.next_output_byte = dest->buffer; dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; *(dest->outbuffer_size) = 0; } /* * Empty the output buffer --- called whenever buffer fills up. * * In typical applications, this should write the entire output buffer * (ignoring the current state of next_output_byte & free_in_buffer), * reset the pointer & count to the start of the buffer, and return TRUE * indicating that the buffer has been dumped. * * In applications that need to be able to suspend compression due to output * overrun, a FALSE return indicates that the buffer cannot be emptied now. * In this situation, the compressor will return to its caller (possibly with * an indication that it has not accepted all the supplied scanlines). The * application should resume compression after it has made more room in the * output buffer. Note that there are substantial restrictions on the use of * suspension --- see the documentation. * * When suspending, the compressor will back up to a convenient restart point * (typically the start of the current MCU). next_output_byte & free_in_buffer * indicate where the restart point will be if the current call returns FALSE. * Data beyond this point will be regenerated after resumption, so do not * write it out when emptying the buffer externally. */ static boolean empty_output_buffer (j_compress_ptr cinfo) { mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, OUTPUT_BUF_SIZE ); *(dest->outbuffer_size) += OUTPUT_BUF_SIZE; dest->pub.next_output_byte = dest->buffer; dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; return( TRUE ); } /* * Terminate destination --- called by jpeg_finish_compress * after all data has been written. Usually needs to flush buffer. * * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding * application must deal with any cleanup that should happen even * for error exit. */ static void term_destination (j_compress_ptr cinfo) { mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; if ( datacount > 0 ) { memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, datacount ); *(dest->outbuffer_size) += datacount; } } /* * Prepare for output to a stdio stream. * The caller must have already opened the stream, and is responsible * for closing it after finishing compression. */ void zm_jpeg_mem_dest (j_compress_ptr cinfo, JOCTET *outbuffer, int *outbuffer_size ) { mem_dest_ptr dest; /* The destination object is made permanent so that multiple JPEG images * can be written to the same file without re-executing jpeg_stdio_dest. * This makes it dangerous to use this manager and a different destination * manager serially with the same JPEG object, because their private object * sizes may be different. Caveat programmer. */ if ( cinfo->dest == NULL ) { /* first time for this JPEG object? */ cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_destination_mgr)); } dest = (mem_dest_ptr) cinfo->dest; dest->pub.init_destination = init_destination; dest->pub.empty_output_buffer = empty_output_buffer; dest->pub.term_destination = term_destination; dest->outbuffer = outbuffer; dest->outbuffer_size = outbuffer_size; } /* Expanded data source object for memory input */ typedef struct { struct jpeg_source_mgr pub; /* public fields */ JOCTET * inbuffer; /* source stream */ int inbuffer_size; int inbuffer_size_hwm; /* High water mark */ JOCTET * buffer; /* start of buffer */ boolean start_of_data; /* have we gotten any data yet? */ } mem_source_mgr; typedef mem_source_mgr * mem_src_ptr; #define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ /* * Initialize source --- called by jpeg_read_header * before any data is actually read. */ static void init_source (j_decompress_ptr cinfo) { mem_src_ptr src = (mem_src_ptr) cinfo->src; /* We reset the empty-input-file flag for each image, * but we don't clear the input buffer. * This is correct behavior for reading a series of images from one source. */ src->start_of_data = TRUE; src->pub.bytes_in_buffer = 0; } /* * Fill the input buffer --- called whenever buffer is emptied. * * In typical applications, this should read fresh data into the buffer * (ignoring the current state of next_input_byte & bytes_in_buffer), * reset the pointer & count to the start of the buffer, and return TRUE * indicating that the buffer has been reloaded. It is not necessary to * fill the buffer entirely, only to obtain at least one more byte. * * There is no such thing as an EOF return. If the end of the file has been * reached, the routine has a choice of ERREXIT() or inserting fake data into * the buffer. In most cases, generating a warning message and inserting a * fake EOI marker is the best course of action --- this will allow the * decompressor to output however much of the image is there. However, * the resulting error message is misleading if the real problem is an empty * input file, so we handle that case specially. * * In applications that need to be able to suspend compression due to input * not being available yet, a FALSE return indicates that no more data can be * obtained right now, but more may be forthcoming later. In this situation, * the decompressor will return to its caller (with an indication of the * number of scanlines it has read, if any). The application should resume * decompression after it has loaded more data into the input buffer. Note * that there are substantial restrictions on the use of suspension --- see * the documentation. * * When suspending, the decompressor will back up to a convenient restart point * (typically the start of the current MCU). next_input_byte & bytes_in_buffer * indicate where the restart point will be if the current call returns FALSE. * Data beyond this point must be rescanned after resumption, so move it to * the front of the buffer rather than discarding it. */ static boolean fill_input_buffer (j_decompress_ptr cinfo) { mem_src_ptr src = (mem_src_ptr) cinfo->src; size_t nbytes; memcpy( src->buffer, src->inbuffer, (size_t) src->inbuffer_size ); nbytes = src->inbuffer_size; if ( nbytes <= 0 ) { if ( src->start_of_data ) /* Treat empty input file as fatal error */ ERREXIT(cinfo, JERR_INPUT_EMPTY); WARNMS(cinfo, JWRN_JPEG_EOF); /* Insert a fake EOI marker */ src->buffer[0] = (JOCTET) 0xFF; src->buffer[1] = (JOCTET) JPEG_EOI; nbytes = 2; } src->pub.next_input_byte = src->buffer; src->pub.bytes_in_buffer = nbytes; src->start_of_data = FALSE; return( TRUE ); } /* * Skip data --- used to skip over a potentially large amount of * uninteresting data (such as an APPn marker). * * Writers of suspendable-input applications must note that skip_input_data * is not granted the right to give a suspension return. If the skip extends * beyond the data currently in the buffer, the buffer can be marked empty so * that the next read will cause a fill_input_buffer call that can suspend. * Arranging for additional bytes to be discarded before reloading the input * buffer is the application writer's problem. */ static void skip_input_data (j_decompress_ptr cinfo, long num_bytes) { mem_src_ptr src = (mem_src_ptr) cinfo->src; /* Just a dumb implementation for now. Could use fseek() except * it doesn't work on pipes. Not clear that being smart is worth * any trouble anyway --- large skips are infrequent. */ if ( num_bytes > 0 ) { while ( num_bytes > (long) src->pub.bytes_in_buffer ) { num_bytes -= (long) src->pub.bytes_in_buffer; (void) fill_input_buffer(cinfo); /* note we assume that fill_input_buffer will never return FALSE, * so suspension need not be handled. */ } src->pub.next_input_byte += (size_t) num_bytes; src->pub.bytes_in_buffer -= (size_t) num_bytes; } } /* * Terminate source --- called by jpeg_finish_decompress * after all data has been read. Often a no-op. * * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding * application must deal with any cleanup that should happen even * for error exit. */ static void term_source (j_decompress_ptr cinfo) { /* no work necessary here */ } /* * Prepare for input from a memory stream. * The caller must have already opened the stream, and is responsible * for closing it after finishing decompression. */ void zm_jpeg_mem_src( j_decompress_ptr cinfo, const JOCTET *inbuffer, int inbuffer_size ) { mem_src_ptr src; /* The source object and input buffer are made permanent so that a series * of JPEG images can be read from the same file by calling zm_jpeg_mem_src * only before the first one. (If we discarded the buffer at the end of * one image, we'd likely lose the start of the next one.) * This makes it unsafe to use this manager and a different source * manager serially with the same JPEG object. Caveat programmer. */ if ( cinfo->src == NULL ) { /* first time for this JPEG object? */ cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, SIZEOF(mem_source_mgr)); src = (mem_src_ptr) cinfo->src; src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET)); src->inbuffer_size_hwm = inbuffer_size; } else { src = (mem_src_ptr) cinfo->src; if ( src->inbuffer_size_hwm < inbuffer_size ) { src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET)); src->inbuffer_size_hwm = inbuffer_size; } } src = (mem_src_ptr) cinfo->src; src->pub.init_source = init_source; src->pub.fill_input_buffer = fill_input_buffer; src->pub.skip_input_data = skip_input_data; src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ src->pub.term_source = term_source; src->inbuffer = (JOCTET *)inbuffer; src->inbuffer_size = inbuffer_size; src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ src->pub.next_input_byte = NULL; /* until buffer loaded */ } void zm_use_std_huff_tables( j_decompress_ptr cinfo ) { /* JPEG standard Huffman tables (cf. JPEG standard section K.3) */ /* IMPORTANT: these are only valid for 8-bit data precision! */ static const JHUFF_TBL dclumin = { { /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, 0 }; static const JHUFF_TBL dcchrome = { { /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, 0 }; static const JHUFF_TBL aclumin = { { /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d }, { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa }, 0 }; static const JHUFF_TBL acchrome = { { /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 }, { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa }, 0 }; cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL*)&dclumin; cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL*)&dcchrome; cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL*)&aclumin; cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL*)&acchrome; } } ZoneMinder-1.26.5/src/zm_jpeg.h000066400000000000000000000030511225361755400162630ustar00rootroot00000000000000/* * ZoneMinder Jpeg Interface, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "jinclude.h" #include "jpeglib.h" #include "jerror.h" // Stop complaints about deuplicate definitions #undef HAVE_STDLIB_H #undef HAVE_STDDEF_H extern "C" { /* Stuff for overriden error handlers */ struct zm_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; typedef struct zm_error_mgr *zm_error_ptr; void zm_jpeg_error_exit( j_common_ptr cinfo ); void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level ); // Prototypes for memory compress/decompression object */ void zm_jpeg_mem_src(j_decompress_ptr cinfo, const JOCTET *inbuffer, int inbuffer_size ); void zm_jpeg_mem_dest(j_compress_ptr cinfo, JOCTET *outbuffer, int *outbuffer_size ); void zm_use_std_huff_tables( j_decompress_ptr cinfo ); } ZoneMinder-1.26.5/src/zm_local_camera.cpp000066400000000000000000002506161225361755400203060ustar00rootroot00000000000000// // ZoneMinder Local Camera Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" #if ZM_HAS_V4L #include "zm_local_camera.h" #include #include #include #include #include #include #include #include static unsigned int BigEndian; static int vidioctl( int fd, int request, void *arg ) { int result = -1; do { result = ioctl( fd, request, arg ); } while ( result == -1 && errno == EINTR ); return( result ); } #if HAVE_LIBSWSCALE static PixelFormat getFfPixFormatFromV4lPalette( int v4l_version, int palette ) { PixelFormat pixFormat = PIX_FMT_NONE; #if ZM_HAS_V4L2 if ( v4l_version == 2 ) { switch( palette ) { #ifdef V4L2_PIX_FMT_RGB444 case V4L2_PIX_FMT_RGB444 : pixFormat = PIX_FMT_RGB444; break; #endif // V4L2_PIX_FMT_RGB444 case V4L2_PIX_FMT_RGB555 : pixFormat = PIX_FMT_RGB555; break; case V4L2_PIX_FMT_RGB565 : pixFormat = PIX_FMT_RGB565; break; case V4L2_PIX_FMT_BGR24 : pixFormat = PIX_FMT_BGR24; break; case V4L2_PIX_FMT_RGB24 : pixFormat = PIX_FMT_RGB24; break; case V4L2_PIX_FMT_BGR32 : pixFormat = PIX_FMT_BGRA; break; case V4L2_PIX_FMT_RGB32 : pixFormat = PIX_FMT_ARGB; break; case V4L2_PIX_FMT_GREY : pixFormat = PIX_FMT_GRAY8; break; case V4L2_PIX_FMT_YUYV : pixFormat = PIX_FMT_YUYV422; break; case V4L2_PIX_FMT_YUV422P : pixFormat = PIX_FMT_YUV422P; break; case V4L2_PIX_FMT_YUV411P : pixFormat = PIX_FMT_YUV411P; break; #ifdef V4L2_PIX_FMT_YUV444 case V4L2_PIX_FMT_YUV444 : pixFormat = PIX_FMT_YUV444P; break; #endif // V4L2_PIX_FMT_YUV444 case V4L2_PIX_FMT_YUV410 : pixFormat = PIX_FMT_YUV410P; break; case V4L2_PIX_FMT_YUV420 : pixFormat = PIX_FMT_YUV420P; break; case V4L2_PIX_FMT_JPEG : case V4L2_PIX_FMT_MJPEG : pixFormat = PIX_FMT_YUVJ444P; break; case V4L2_PIX_FMT_UYVY : pixFormat = PIX_FMT_UYVY422; break; // These don't seem to have ffmpeg equivalents // See if you can match any of the ones in the default clause below!? case V4L2_PIX_FMT_RGB332 : case V4L2_PIX_FMT_RGB555X : case V4L2_PIX_FMT_RGB565X : //case V4L2_PIX_FMT_Y16 : //case V4L2_PIX_FMT_PAL8 : case V4L2_PIX_FMT_YVU410 : case V4L2_PIX_FMT_YVU420 : case V4L2_PIX_FMT_Y41P : //case V4L2_PIX_FMT_YUV555 : //case V4L2_PIX_FMT_YUV565 : //case V4L2_PIX_FMT_YUV32 : case V4L2_PIX_FMT_NV12 : case V4L2_PIX_FMT_NV21 : case V4L2_PIX_FMT_YYUV : case V4L2_PIX_FMT_HI240 : case V4L2_PIX_FMT_HM12 : //case V4L2_PIX_FMT_SBGGR8 : //case V4L2_PIX_FMT_SGBRG8 : //case V4L2_PIX_FMT_SBGGR16 : case V4L2_PIX_FMT_DV : case V4L2_PIX_FMT_MPEG : case V4L2_PIX_FMT_WNVA : case V4L2_PIX_FMT_SN9C10X : case V4L2_PIX_FMT_PWC1 : case V4L2_PIX_FMT_PWC2 : case V4L2_PIX_FMT_ET61X251 : //case V4L2_PIX_FMT_SPCA501 : //case V4L2_PIX_FMT_SPCA505 : //case V4L2_PIX_FMT_SPCA508 : //case V4L2_PIX_FMT_SPCA561 : //case V4L2_PIX_FMT_PAC207 : //case V4L2_PIX_FMT_PJPG : //case V4L2_PIX_FMT_YVYU : default : { Fatal( "Can't find swscale format for palette %d", palette ); break; // These are all spare and may match some of the above pixFormat = PIX_FMT_YUVJ420P; pixFormat = PIX_FMT_YUVJ422P; pixFormat = PIX_FMT_XVMC_MPEG2_MC; pixFormat = PIX_FMT_XVMC_MPEG2_IDCT; pixFormat = PIX_FMT_UYVY422; pixFormat = PIX_FMT_UYYVYY411; pixFormat = PIX_FMT_BGR565; pixFormat = PIX_FMT_BGR555; pixFormat = PIX_FMT_BGR8; pixFormat = PIX_FMT_BGR4; pixFormat = PIX_FMT_BGR4_BYTE; pixFormat = PIX_FMT_RGB8; pixFormat = PIX_FMT_RGB4; pixFormat = PIX_FMT_RGB4_BYTE; pixFormat = PIX_FMT_NV12; pixFormat = PIX_FMT_NV21; pixFormat = PIX_FMT_RGB32_1; pixFormat = PIX_FMT_BGR32_1; pixFormat = PIX_FMT_GRAY16BE; pixFormat = PIX_FMT_GRAY16LE; pixFormat = PIX_FMT_YUV440P; pixFormat = PIX_FMT_YUVJ440P; pixFormat = PIX_FMT_YUVA420P; //pixFormat = PIX_FMT_VDPAU_H264; //pixFormat = PIX_FMT_VDPAU_MPEG1; //pixFormat = PIX_FMT_VDPAU_MPEG2; } } } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 if ( v4l_version == 1 ) { switch( palette ) { case VIDEO_PALETTE_RGB32 : if(BigEndian) pixFormat = PIX_FMT_ARGB; else pixFormat = PIX_FMT_BGRA; break; case VIDEO_PALETTE_RGB24 : if(BigEndian) pixFormat = PIX_FMT_RGB24; else pixFormat = PIX_FMT_BGR24; break; case VIDEO_PALETTE_GREY : pixFormat = PIX_FMT_GRAY8; break; case VIDEO_PALETTE_RGB555 : pixFormat = PIX_FMT_RGB555; break; case VIDEO_PALETTE_RGB565 : pixFormat = PIX_FMT_RGB565; break; case VIDEO_PALETTE_YUYV : case VIDEO_PALETTE_YUV422 : pixFormat = PIX_FMT_YUYV422; break; case VIDEO_PALETTE_YUV422P : pixFormat = PIX_FMT_YUV422P; break; case VIDEO_PALETTE_YUV420P : pixFormat = PIX_FMT_YUV420P; break; default : { Fatal( "Can't find swscale format for palette %d", palette ); break; // These are all spare and may match some of the above pixFormat = PIX_FMT_YUVJ420P; pixFormat = PIX_FMT_YUVJ422P; pixFormat = PIX_FMT_YUVJ444P; pixFormat = PIX_FMT_XVMC_MPEG2_MC; pixFormat = PIX_FMT_XVMC_MPEG2_IDCT; pixFormat = PIX_FMT_UYVY422; pixFormat = PIX_FMT_UYYVYY411; pixFormat = PIX_FMT_BGR565; pixFormat = PIX_FMT_BGR555; pixFormat = PIX_FMT_BGR8; pixFormat = PIX_FMT_BGR4; pixFormat = PIX_FMT_BGR4_BYTE; pixFormat = PIX_FMT_RGB8; pixFormat = PIX_FMT_RGB4; pixFormat = PIX_FMT_RGB4_BYTE; pixFormat = PIX_FMT_NV12; pixFormat = PIX_FMT_NV21; pixFormat = PIX_FMT_RGB32_1; pixFormat = PIX_FMT_BGR32_1; pixFormat = PIX_FMT_GRAY16BE; pixFormat = PIX_FMT_GRAY16LE; pixFormat = PIX_FMT_YUV440P; pixFormat = PIX_FMT_YUVJ440P; pixFormat = PIX_FMT_YUVA420P; //pixFormat = PIX_FMT_VDPAU_H264; //pixFormat = PIX_FMT_VDPAU_MPEG1; //pixFormat = PIX_FMT_VDPAU_MPEG2; } } } #endif // ZM_HAS_V4L1 return( pixFormat ); } #endif // HAVE_LIBSWSCALE #if ZM_HAS_V4L2 static char palette_desc[32]; /* Automatic format selection prefered formats */ static const uint32_t prefered_rgb32_formats[] = {V4L2_PIX_FMT_BGR32, V4L2_PIX_FMT_RGB32, V4L2_PIX_FMT_BGR24, V4L2_PIX_FMT_RGB24, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420}; static const uint32_t prefered_rgb24_formats[] = {V4L2_PIX_FMT_BGR24, V4L2_PIX_FMT_RGB24, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420}; static const uint32_t prefered_gray8_formats[] = {V4L2_PIX_FMT_GREY, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420}; #endif int LocalCamera::camera_count = 0; int LocalCamera::channel_count = 0; int LocalCamera::channels[VIDEO_MAX_FRAME]; int LocalCamera::standards[VIDEO_MAX_FRAME]; int LocalCamera::vid_fd = -1; int LocalCamera::v4l_version = 0; #if ZM_HAS_V4L2 LocalCamera::V4L2Data LocalCamera::v4l2_data; #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 LocalCamera::V4L1Data LocalCamera::v4l1_data; #endif // ZM_HAS_V4L1 #if HAVE_LIBSWSCALE AVFrame **LocalCamera::capturePictures = 0; #endif // HAVE_LIBSWSCALE LocalCamera *LocalCamera::last_camera = NULL; LocalCamera::LocalCamera( int p_id, const std::string &p_device, int p_channel, int p_standard, const std::string &p_method, int p_width, int p_height, int p_colours, int p_palette, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, unsigned int p_extras) : Camera( p_id, LOCAL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), device( p_device ), channel( p_channel ), standard( p_standard ), palette( p_palette ), channel_index( 0 ), extras ( p_extras ) { // If we are the first, or only, input on this device then // do the initial opening etc device_prime = (camera_count++ == 0); v4l_version = (p_method=="v4l2"?2:1); if ( capture ) { if ( device_prime ) { Debug( 2, "V4L support enabled, using V4L%d api", v4l_version ); } if ( !last_camera || channel != last_camera->channel ) { // We are the first, or only, input that uses this channel channel_prime = true; channel_index = channel_count++; channels[channel_index] = channel; standards[channel_index] = standard; } else { // We are the second, or subsequent, input using this channel channel_prime = false; } } /* The V4L1 API doesn't care about endianness, we need to check the endianness of the machine */ uint32_t checkval = 0xAABBCCDD; if(*(unsigned char*)&checkval == 0xDD) { BigEndian = 0; Debug(2,"little-endian processor detected"); } else if(*(unsigned char*)&checkval == 0xAA) { BigEndian = 1; Debug(2,"Big-endian processor detected"); } else { Error("Unable to detect the processor's endianness. Assuming little-endian."); BigEndian = 0; } #if ZM_HAS_V4L2 if( v4l_version == 2 && palette == 0 ) { /* Use automatic format selection */ Debug(2,"Using automatic format selection"); palette = AutoSelectFormat(colours); if(palette == 0) { Error("Automatic format selection failed. Falling back to YUYV"); palette = V4L2_PIX_FMT_YUYV; } else { if(capture) { Info("Selected capture palette: %s (%c%c%c%c)", palette_desc, palette&0xff, (palette>>8)&0xff, (palette>>16)&0xff, (palette>>24)&0xff); } } } #endif if( capture ) { if ( last_camera ) { if ( (p_method == "v4l2" && v4l_version != 2) || (p_method == "v4l1" && v4l_version != 1) ) Fatal( "Different Video For Linux version used for monitors sharing same device" ); if ( standard != last_camera->standard ) Warning( "Different video standards defined for monitors sharing same device, results may be unpredictable or completely wrong" ); if ( palette != last_camera->palette ) Warning( "Different video palettes defined for monitors sharing same device, results may be unpredictable or completely wrong" ); if ( width != last_camera->width || height != last_camera->height ) Warning( "Different capture sizes defined for monitors sharing same device, results may be unpredictable or completely wrong" ); } } #if HAVE_LIBSWSCALE if( capture ) { /* Get ffmpeg pixel format based on capture palette and endianness */ capturePixFormat = getFfPixFormatFromV4lPalette( v4l_version, palette ); imagePixFormat = PIX_FMT_NONE; } #endif // HAVE_LIBSWSCALE /* V4L2 format matching */ #if ZM_HAS_V4L2 if ( v4l_version == 2 ) { /* Try to find a match for the selected palette and target colourspace */ /* RGB32 palette and 32bit target colourspace */ if(palette == V4L2_PIX_FMT_RGB32 && colours == ZM_COLOUR_RGB32) { conversion_type = 0; subpixelorder = ZM_SUBPIX_ORDER_ARGB; /* BGR32 palette and 32bit target colourspace */ } else if(palette == V4L2_PIX_FMT_BGR32 && colours == ZM_COLOUR_RGB32) { conversion_type = 0; subpixelorder = ZM_SUBPIX_ORDER_BGRA; /* RGB24 palette and 24bit target colourspace */ } else if(palette == V4L2_PIX_FMT_RGB24 && colours == ZM_COLOUR_RGB24) { conversion_type = 0; subpixelorder = ZM_SUBPIX_ORDER_RGB; /* BGR24 palette and 24bit target colourspace */ } else if(palette == V4L2_PIX_FMT_BGR24 && colours == ZM_COLOUR_RGB24) { conversion_type = 0; subpixelorder = ZM_SUBPIX_ORDER_BGR; /* Grayscale palette and grayscale target colourspace */ } else if(palette == V4L2_PIX_FMT_GREY && colours == ZM_COLOUR_GRAY8) { conversion_type = 0; subpixelorder = ZM_SUBPIX_ORDER_NONE; /* Unable to find a solution for the selected palette and target colourspace. Conversion required. Notify the user of performance penalty */ } else { if( capture ) Info("No direct match for the selected palette and target colorspace. Format conversion is required, performance penalty expected"); #if HAVE_LIBSWSCALE /* Try using swscale for the conversion */ conversion_type = 1; Debug(2,"Using swscale for image conversion"); if(colours == ZM_COLOUR_RGB32) { subpixelorder = ZM_SUBPIX_ORDER_RGBA; imagePixFormat = PIX_FMT_RGBA; } else if(colours == ZM_COLOUR_RGB24) { subpixelorder = ZM_SUBPIX_ORDER_RGB; imagePixFormat = PIX_FMT_RGB24; } else if(colours == ZM_COLOUR_GRAY8) { subpixelorder = ZM_SUBPIX_ORDER_NONE; imagePixFormat = PIX_FMT_GRAY8; } else { Panic("Unexpected colours: %d",colours); } if( capture ) { if(!sws_isSupportedInput(capturePixFormat)) { Error("swscale does not support the used capture format: %c%c%c%c",(capturePixFormat)&0xff,((capturePixFormat>>8)&0xff),((capturePixFormat>>16)&0xff),((capturePixFormat>>24)&0xff)); conversion_type = 2; /* Try ZM format conversions */ } if(!sws_isSupportedOutput(imagePixFormat)) { Error("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); conversion_type = 2; /* Try ZM format conversions */ } } #else /* Don't have swscale, see what we can do */ conversion_type = 2; #endif /* Our YUYV->Grayscale conversion is a lot faster than swscale's */ if(colours == ZM_COLOUR_GRAY8 && palette == V4L2_PIX_FMT_YUYV) { conversion_type = 2; } /* JPEG */ if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) { Debug(2,"Using JPEG image decoding"); conversion_type = 3; } if(conversion_type == 2) { Debug(2,"Using ZM for image conversion"); if(palette == V4L2_PIX_FMT_RGB32 && colours == ZM_COLOUR_GRAY8) { conversion_fptr = &std_convert_argb_gray8; subpixelorder = ZM_SUBPIX_ORDER_NONE; } else if(palette == V4L2_PIX_FMT_BGR32 && colours == ZM_COLOUR_GRAY8) { conversion_fptr = &std_convert_bgra_gray8; subpixelorder = ZM_SUBPIX_ORDER_NONE; } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_GRAY8) { /* Fast YUYV->Grayscale conversion by extracting the Y channel */ if(config.cpu_extensions && sseversion >= 35) { conversion_fptr = &ssse3_convert_yuyv_gray8; Debug(2,"Using SSSE3 YUYV->grayscale fast conversion"); } else { conversion_fptr = &std_convert_yuyv_gray8; Debug(2,"Using standard YUYV->grayscale fast conversion"); } subpixelorder = ZM_SUBPIX_ORDER_NONE; } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_RGB24) { conversion_fptr = &zm_convert_yuyv_rgb; subpixelorder = ZM_SUBPIX_ORDER_RGB; } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_RGB32) { conversion_fptr = &zm_convert_yuyv_rgba; subpixelorder = ZM_SUBPIX_ORDER_RGBA; } else if(palette == V4L2_PIX_FMT_RGB555 && colours == ZM_COLOUR_RGB24) { conversion_fptr = &zm_convert_rgb555_rgb; subpixelorder = ZM_SUBPIX_ORDER_RGB; } else if(palette == V4L2_PIX_FMT_RGB555 && colours == ZM_COLOUR_RGB32) { conversion_fptr = &zm_convert_rgb555_rgba; subpixelorder = ZM_SUBPIX_ORDER_RGBA; } else if(palette == V4L2_PIX_FMT_RGB565 && colours == ZM_COLOUR_RGB24) { conversion_fptr = &zm_convert_rgb565_rgb; subpixelorder = ZM_SUBPIX_ORDER_RGB; } else if(palette == V4L2_PIX_FMT_RGB565 && colours == ZM_COLOUR_RGB32) { conversion_fptr = &zm_convert_rgb565_rgba; subpixelorder = ZM_SUBPIX_ORDER_RGBA; } else { Fatal("Unable to find a suitable format conversion for the selected palette and target colorspace."); } } } } #endif // ZM_HAS_V4L2 /* V4L1 format matching */ #if ZM_HAS_V4L1 if ( v4l_version == 1) { /* Try to find a match for the selected palette and target colourspace */ /* RGB32 palette and 32bit target colourspace */ if(palette == VIDEO_PALETTE_RGB32 && colours == ZM_COLOUR_RGB32) { conversion_type = 0; if(BigEndian) { subpixelorder = ZM_SUBPIX_ORDER_ARGB; } else { subpixelorder = ZM_SUBPIX_ORDER_BGRA; } /* RGB24 palette and 24bit target colourspace */ } else if(palette == VIDEO_PALETTE_RGB24 && colours == ZM_COLOUR_RGB24) { conversion_type = 0; if(BigEndian) { subpixelorder = ZM_SUBPIX_ORDER_RGB; } else { subpixelorder = ZM_SUBPIX_ORDER_BGR; } /* Grayscale palette and grayscale target colourspace */ } else if(palette == VIDEO_PALETTE_GREY && colours == ZM_COLOUR_GRAY8) { conversion_type = 0; subpixelorder = ZM_SUBPIX_ORDER_NONE; /* Unable to find a solution for the selected palette and target colourspace. Conversion required. Notify the user of performance penalty */ } else { if( capture ) Info("No direct match for the selected palette and target colorspace. Format conversion is required, performance penalty expected"); #if HAVE_LIBSWSCALE /* Try using swscale for the conversion */ conversion_type = 1; Debug(2,"Using swscale for image conversion"); if(colours == ZM_COLOUR_RGB32) { subpixelorder = ZM_SUBPIX_ORDER_RGBA; imagePixFormat = PIX_FMT_RGBA; } else if(colours == ZM_COLOUR_RGB24) { subpixelorder = ZM_SUBPIX_ORDER_RGB; imagePixFormat = PIX_FMT_RGB24; } else if(colours == ZM_COLOUR_GRAY8) { subpixelorder = ZM_SUBPIX_ORDER_NONE; imagePixFormat = PIX_FMT_GRAY8; } else { Panic("Unexpected colours: %d",colours); } if( capture ) { if(!sws_isSupportedInput(capturePixFormat)) { Error("swscale does not support the used capture format"); conversion_type = 2; /* Try ZM format conversions */ } if(!sws_isSupportedOutput(imagePixFormat)) { Error("swscale does not support the target format"); conversion_type = 2; /* Try ZM format conversions */ } } #else /* Don't have swscale, see what we can do */ conversion_type = 2; #endif /* Our YUYV->Grayscale conversion is a lot faster than swscale's */ if(colours == ZM_COLOUR_GRAY8 && (palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422)) { conversion_type = 2; } if(conversion_type == 2) { Debug(2,"Using ZM for image conversion"); if(palette == VIDEO_PALETTE_RGB32 && colours == ZM_COLOUR_GRAY8) { if(BigEndian) { conversion_fptr = &std_convert_argb_gray8; subpixelorder = ZM_SUBPIX_ORDER_NONE; } else { conversion_fptr = &std_convert_bgra_gray8; subpixelorder = ZM_SUBPIX_ORDER_NONE; } } else if((palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_GRAY8) { /* Fast YUYV->Grayscale conversion by extracting the Y channel */ if(config.cpu_extensions && sseversion >= 35) { conversion_fptr = &ssse3_convert_yuyv_gray8; Debug(2,"Using SSSE3 YUYV->grayscale fast conversion"); } else { conversion_fptr = &std_convert_yuyv_gray8; Debug(2,"Using standard YUYV->grayscale fast conversion"); } subpixelorder = ZM_SUBPIX_ORDER_NONE; } else if((palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_RGB24) { conversion_fptr = &zm_convert_yuyv_rgb; subpixelorder = ZM_SUBPIX_ORDER_RGB; } else if((palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_RGB32) { conversion_fptr = &zm_convert_yuyv_rgba; subpixelorder = ZM_SUBPIX_ORDER_RGBA; } else if(palette == VIDEO_PALETTE_RGB555 && colours == ZM_COLOUR_RGB24) { conversion_fptr = &zm_convert_rgb555_rgb; subpixelorder = ZM_SUBPIX_ORDER_RGB; } else if(palette == VIDEO_PALETTE_RGB555 && colours == ZM_COLOUR_RGB32) { conversion_fptr = &zm_convert_rgb555_rgba; subpixelorder = ZM_SUBPIX_ORDER_RGBA; } else if(palette == VIDEO_PALETTE_RGB565 && colours == ZM_COLOUR_RGB24) { conversion_fptr = &zm_convert_rgb565_rgb; subpixelorder = ZM_SUBPIX_ORDER_RGB; } else if(palette == VIDEO_PALETTE_RGB565 && colours == ZM_COLOUR_RGB32) { conversion_fptr = &zm_convert_rgb565_rgba; subpixelorder = ZM_SUBPIX_ORDER_RGBA; } else { Fatal("Unable to find a suitable format conversion for the selected palette and target colorspace."); } } } } #endif // ZM_HAS_V4L1 last_camera = this; Debug(3,"Selected subpixelorder: %d",subpixelorder); #if HAVE_LIBSWSCALE /* Initialize swscale stuff */ if(capture && conversion_type == 1) { tmpPicture = avcodec_alloc_frame(); if ( !tmpPicture ) Fatal( "Could not allocate temporary picture" ); int pSize = avpicture_get_size( imagePixFormat, width, height ); if( (unsigned int)pSize != imagesize) { Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); } if(config.cpu_extensions && sseversion >= 20) { imgConversionContext = sws_getContext(width, height, capturePixFormat, width, height, imagePixFormat, SWS_BICUBIC | SWS_CPU_CAPS_SSE2, NULL, NULL, NULL ); } else { imgConversionContext = sws_getContext(width, height, capturePixFormat, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); } if ( !imgConversionContext ) Fatal( "Unable to initialise image scaling context" ); } #endif } LocalCamera::~LocalCamera() { if ( device_prime && capture ) Terminate(); #if HAVE_LIBSWSCALE /* Clean up swscale stuff */ if(capture && conversion_type == 1) { sws_freeContext(imgConversionContext); imgConversionContext = NULL; av_free(tmpPicture); tmpPicture = NULL; } #endif } void LocalCamera::Initialise() { #if HAVE_LIBSWSCALE if ( logDebugging() ) av_log_set_level( AV_LOG_DEBUG ); else av_log_set_level( AV_LOG_QUIET ); #endif // HAVE_LIBSWSCALE struct stat st; if ( stat( device.c_str(), &st ) < 0 ) Fatal( "Failed to stat video device %s: %s", device.c_str(), strerror(errno) ); if ( !S_ISCHR(st.st_mode) ) Fatal( "File %s is not device file: %s", device.c_str(), strerror(errno) ); Debug( 3, "Opening video device %s", device.c_str() ); //if ( (vid_fd = open( device.c_str(), O_RDWR|O_NONBLOCK, 0 )) < 0 ) if ( (vid_fd = open( device.c_str(), O_RDWR, 0 )) < 0 ) Fatal( "Failed to open video device %s: %s", device.c_str(), strerror(errno) ); #if ZM_HAS_V4L2 Debug( 2, "V4L2 support enabled, using V4L%d api", v4l_version ); if ( v4l_version == 2 ) { struct v4l2_capability vid_cap; Debug( 3, "Checking video device capabilities" ); if ( vidioctl( vid_fd, VIDIOC_QUERYCAP, &vid_cap ) < 0 ) Fatal( "Failed to query video device: %s", strerror(errno) ); if ( !(vid_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) ) Fatal( "Video device is not video capture device" ); if ( !(vid_cap.capabilities & V4L2_CAP_STREAMING) ) Fatal( "Video device does not support streaming i/o" ); Debug( 3, "Setting up video format" ); memset( &v4l2_data.fmt, 0, sizeof(v4l2_data.fmt) ); v4l2_data.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if ( vidioctl( vid_fd, VIDIOC_G_FMT, &v4l2_data.fmt ) < 0 ) Fatal( "Failed to get video format: %s", strerror(errno) ); Debug( 4, " v4l2_data.fmt.type = %08x", v4l2_data.fmt.type ); Debug( 4, " v4l2_data.fmt.fmt.pix.width = %08x", v4l2_data.fmt.fmt.pix.width ); Debug( 4, " v4l2_data.fmt.fmt.pix.height = %08x", v4l2_data.fmt.fmt.pix.height ); Debug( 4, " v4l2_data.fmt.fmt.pix.pixelformat = %08x", v4l2_data.fmt.fmt.pix.pixelformat ); Debug( 4, " v4l2_data.fmt.fmt.pix.field = %08x", v4l2_data.fmt.fmt.pix.field ); Debug( 4, " v4l2_data.fmt.fmt.pix.bytesperline = %08x", v4l2_data.fmt.fmt.pix.bytesperline ); Debug( 4, " v4l2_data.fmt.fmt.pix.sizeimage = %08x", v4l2_data.fmt.fmt.pix.sizeimage ); Debug( 4, " v4l2_data.fmt.fmt.pix.colorspace = %08x", v4l2_data.fmt.fmt.pix.colorspace ); Debug( 4, " v4l2_data.fmt.fmt.pix.priv = %08x", v4l2_data.fmt.fmt.pix.priv ); v4l2_data.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; v4l2_data.fmt.fmt.pix.width = width; v4l2_data.fmt.fmt.pix.height = height; v4l2_data.fmt.fmt.pix.pixelformat = palette; if ( (extras & 0xff) != 0 ) { v4l2_data.fmt.fmt.pix.field = (v4l2_field)(extras & 0xff); if ( vidioctl( vid_fd, VIDIOC_S_FMT, &v4l2_data.fmt ) < 0 ) { Warning( "Failed to set V4L2 field to %d, falling back to auto", (extras & 0xff) ); v4l2_data.fmt.fmt.pix.field = V4L2_FIELD_ANY; if ( vidioctl( vid_fd, VIDIOC_S_FMT, &v4l2_data.fmt ) < 0 ) { Fatal( "Failed to set video format: %s", strerror(errno) ); } } } else { if ( vidioctl( vid_fd, VIDIOC_S_FMT, &v4l2_data.fmt ) < 0 ) { Fatal( "Failed to set video format: %s", strerror(errno) ); } } /* Note VIDIOC_S_FMT may change width and height. */ Debug( 4, " v4l2_data.fmt.type = %08x", v4l2_data.fmt.type ); Debug( 4, " v4l2_data.fmt.fmt.pix.width = %08x", v4l2_data.fmt.fmt.pix.width ); Debug( 4, " v4l2_data.fmt.fmt.pix.height = %08x", v4l2_data.fmt.fmt.pix.height ); Debug( 4, " v4l2_data.fmt.fmt.pix.pixelformat = %08x", v4l2_data.fmt.fmt.pix.pixelformat ); Debug( 4, " v4l2_data.fmt.fmt.pix.field = %08x", v4l2_data.fmt.fmt.pix.field ); Debug( 4, " v4l2_data.fmt.fmt.pix.bytesperline = %08x", v4l2_data.fmt.fmt.pix.bytesperline ); Debug( 4, " v4l2_data.fmt.fmt.pix.sizeimage = %08x", v4l2_data.fmt.fmt.pix.sizeimage ); Debug( 4, " v4l2_data.fmt.fmt.pix.colorspace = %08x", v4l2_data.fmt.fmt.pix.colorspace ); Debug( 4, " v4l2_data.fmt.fmt.pix.priv = %08x", v4l2_data.fmt.fmt.pix.priv ); /* Buggy driver paranoia. */ unsigned int min; min = v4l2_data.fmt.fmt.pix.width * 2; if (v4l2_data.fmt.fmt.pix.bytesperline < min) v4l2_data.fmt.fmt.pix.bytesperline = min; min = v4l2_data.fmt.fmt.pix.bytesperline * v4l2_data.fmt.fmt.pix.height; if (v4l2_data.fmt.fmt.pix.sizeimage < min) v4l2_data.fmt.fmt.pix.sizeimage = min; v4l2_jpegcompression jpeg_comp; if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) { if( vidioctl( vid_fd, VIDIOC_G_JPEGCOMP, &jpeg_comp ) < 0 ) { if(errno == EINVAL) { Debug(2, "JPEG compression options are not available"); } else { Warning("Failed to get JPEG compression options: %s", strerror(errno) ); } } else { /* Set flags and quality. MJPEG should not have the huffman tables defined */ if(palette == V4L2_PIX_FMT_MJPEG) { jpeg_comp.jpeg_markers |= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI; } else { jpeg_comp.jpeg_markers |= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI | V4L2_JPEG_MARKER_DHT; } jpeg_comp.quality = 85; /* Update the JPEG options */ if( vidioctl( vid_fd, VIDIOC_S_JPEGCOMP, &jpeg_comp ) < 0 ) { Warning("Failed to set JPEG compression options: %s", strerror(errno) ); } else { if(vidioctl( vid_fd, VIDIOC_G_JPEGCOMP, &jpeg_comp ) < 0) { Debug(3,"Failed to get updated JPEG compression options: %s", strerror(errno) ); } else { Debug(4, "JPEG quality: %d",jpeg_comp.quality); Debug(4, "JPEG markers: %#x",jpeg_comp.jpeg_markers); } } } } Debug( 3, "Setting up request buffers" ); memset( &v4l2_data.reqbufs, 0, sizeof(v4l2_data.reqbufs) ); if ( channel_count > 1 ) if ( config.v4l_multi_buffer ) v4l2_data.reqbufs.count = 2*channel_count; else v4l2_data.reqbufs.count = 1; else v4l2_data.reqbufs.count = 8; v4l2_data.reqbufs.type = v4l2_data.fmt.type; v4l2_data.reqbufs.memory = V4L2_MEMORY_MMAP; if ( vidioctl( vid_fd, VIDIOC_REQBUFS, &v4l2_data.reqbufs ) < 0 ) { if ( errno == EINVAL ) { Fatal( "Unable to initialise memory mapping, unsupported in device" ); } else { Fatal( "Unable to initialise memory mapping: %s", strerror(errno) ); } } if ( v4l2_data.reqbufs.count < (config.v4l_multi_buffer?2:1) ) Fatal( "Insufficient buffer memory %d on video device", v4l2_data.reqbufs.count ); Debug( 3, "Setting up %d data buffers", v4l2_data.reqbufs.count ); v4l2_data.buffers = new V4L2MappedBuffer[v4l2_data.reqbufs.count]; #if HAVE_LIBSWSCALE capturePictures = new AVFrame *[v4l2_data.reqbufs.count]; #endif // HAVE_LIBSWSCALE for ( unsigned int i = 0; i < v4l2_data.reqbufs.count; i++ ) { struct v4l2_buffer vid_buf; memset( &vid_buf, 0, sizeof(vid_buf) ); //vid_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vid_buf.type = v4l2_data.fmt.type; //vid_buf.memory = V4L2_MEMORY_MMAP; vid_buf.memory = v4l2_data.reqbufs.memory; vid_buf.index = i; if ( vidioctl( vid_fd, VIDIOC_QUERYBUF, &vid_buf ) < 0 ) Fatal( "Unable to query video buffer: %s", strerror(errno) ); v4l2_data.buffers[i].length = vid_buf.length; v4l2_data.buffers[i].start = mmap( NULL, vid_buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, vid_fd, vid_buf.m.offset ); if ( v4l2_data.buffers[i].start == MAP_FAILED ) Fatal( "Can't map video buffer %d (%d bytes) to memory: %s(%d)", i, vid_buf.length, strerror(errno), errno ); #if HAVE_LIBSWSCALE capturePictures[i] = avcodec_alloc_frame(); if ( !capturePictures[i] ) Fatal( "Could not allocate picture" ); avpicture_fill( (AVPicture *)capturePictures[i], (uint8_t*)v4l2_data.buffers[i].start, capturePixFormat, v4l2_data.fmt.fmt.pix.width, v4l2_data.fmt.fmt.pix.height ); #endif // HAVE_LIBSWSCALE } Debug( 3, "Configuring video source" ); if ( vidioctl( vid_fd, VIDIOC_S_INPUT, &channel ) < 0 ) { Fatal( "Failed to set camera source %d: %s", channel, strerror(errno) ); } struct v4l2_input input; v4l2_std_id stdId; memset( &input, 0, sizeof(input) ); if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) { Fatal( "Failed to enumerate input %d: %s", channel, strerror(errno) ); } if ( (input.std != V4L2_STD_UNKNOWN) && ((input.std & standard) == V4L2_STD_UNKNOWN) ) { Fatal( "Device does not support video standard %d", standard ); } stdId = standard; if ( (input.std != V4L2_STD_UNKNOWN) && vidioctl( vid_fd, VIDIOC_S_STD, &stdId ) < 0 ) { Fatal( "Failed to set video standard %d: %s", standard, strerror(errno) ); } Contrast(contrast); Brightness(brightness); Hue(hue); Colour(colour); } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 if ( v4l_version == 1 ) { Debug( 3, "Configuring picture attributes" ); struct video_picture vid_pic; memset( &vid_pic, 0, sizeof(vid_pic) ); if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) Fatal( "Failed to get picture attributes: %s", strerror(errno) ); Debug( 4, "Old P:%d", vid_pic.palette ); Debug( 4, "Old D:%d", vid_pic.depth ); Debug( 4, "Old B:%d", vid_pic.brightness ); Debug( 4, "Old h:%d", vid_pic.hue ); Debug( 4, "Old Cl:%d", vid_pic.colour ); Debug( 4, "Old Cn:%d", vid_pic.contrast ); switch (vid_pic.palette = palette) { case VIDEO_PALETTE_RGB32 : { vid_pic.depth = 32; break; } case VIDEO_PALETTE_RGB24 : { vid_pic.depth = 24; break; } case VIDEO_PALETTE_GREY : { vid_pic.depth = 8; break; } case VIDEO_PALETTE_RGB565 : case VIDEO_PALETTE_YUYV : case VIDEO_PALETTE_YUV422 : case VIDEO_PALETTE_YUV420P : case VIDEO_PALETTE_YUV422P : default: { vid_pic.depth = 16; break; } } if ( brightness >= 0 ) vid_pic.brightness = brightness; if ( hue >= 0 ) vid_pic.hue = hue; if ( colour >= 0 ) vid_pic.colour = colour; if ( contrast >= 0 ) vid_pic.contrast = contrast; if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) { Error( "Failed to set picture attributes: %s", strerror(errno) ); if ( config.strict_video_config ) exit(-1); } Debug( 3, "Configuring window attributes" ); struct video_window vid_win; memset( &vid_win, 0, sizeof(vid_win) ); if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win) < 0 ) { Error( "Failed to get window attributes: %s", strerror(errno) ); exit(-1); } Debug( 4, "Old X:%d", vid_win.x ); Debug( 4, "Old Y:%d", vid_win.y ); Debug( 4, "Old W:%d", vid_win.width ); Debug( 4, "Old H:%d", vid_win.height ); vid_win.x = 0; vid_win.y = 0; vid_win.width = width; vid_win.height = height; vid_win.flags &= ~VIDEO_WINDOW_INTERLACE; if ( ioctl( vid_fd, VIDIOCSWIN, &vid_win ) < 0 ) { Error( "Failed to set window attributes: %s", strerror(errno) ); if ( config.strict_video_config ) exit(-1); } Info( "vid_win.width = %08x", vid_win.width ); Info( "vid_win.height = %08x", vid_win.height ); Info( "vid_win.flags = %08x", vid_win.flags ); Debug( 3, "Setting up request buffers" ); if ( ioctl( vid_fd, VIDIOCGMBUF, &v4l1_data.frames ) < 0 ) Fatal( "Failed to setup memory: %s", strerror(errno) ); if ( channel_count > 1 && !config.v4l_multi_buffer ) v4l1_data.frames.frames = 1; v4l1_data.buffers = new video_mmap[v4l1_data.frames.frames]; Debug( 4, "vmb.frames = %d", v4l1_data.frames.frames ); Debug( 4, "vmb.size = %d", v4l1_data.frames.size ); Debug( 3, "Setting up %d frame buffers", v4l1_data.frames.frames ); v4l1_data.bufptr = (unsigned char *)mmap( 0, v4l1_data.frames.size, PROT_READ|PROT_WRITE, MAP_SHARED, vid_fd, 0 ); if ( v4l1_data.bufptr == MAP_FAILED ) Fatal( "Could not mmap video: %s", strerror(errno) ); #if HAVE_LIBSWSCALE capturePictures = new AVFrame *[v4l1_data.frames.frames]; for ( int i = 0; i < v4l1_data.frames.frames; i++ ) { v4l1_data.buffers[i].frame = i; v4l1_data.buffers[i].width = width; v4l1_data.buffers[i].height = height; v4l1_data.buffers[i].format = palette; capturePictures[i] = avcodec_alloc_frame(); if ( !capturePictures[i] ) Fatal( "Could not allocate picture" ); avpicture_fill( (AVPicture *)capturePictures[i], (unsigned char *)v4l1_data.bufptr+v4l1_data.frames.offsets[i], capturePixFormat, width, height ); } #endif // HAVE_LIBSWSCALE Debug( 3, "Configuring video source" ); struct video_channel vid_src; memset( &vid_src, 0, sizeof(vid_src) ); vid_src.channel = channel; if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) Fatal( "Failed to get camera source: %s", strerror(errno) ); Debug( 4, "Old C:%d", vid_src.channel ); Debug( 4, "Old F:%d", vid_src.norm ); Debug( 4, "Old Fl:%x", vid_src.flags ); Debug( 4, "Old T:%d", vid_src.type ); vid_src.norm = standard; vid_src.flags = 0; vid_src.type = VIDEO_TYPE_CAMERA; if ( ioctl( vid_fd, VIDIOCSCHAN, &vid_src ) < 0 ) { Error( "Failed to set camera source %d: %s", channel, strerror(errno) ); if ( config.strict_video_config ) exit(-1); } if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win) < 0 ) Fatal( "Failed to get window data: %s", strerror(errno) ); Info( "vid_win.width = %08x", vid_win.width ); Info( "vid_win.height = %08x", vid_win.height ); Info( "vid_win.flags = %08x", vid_win.flags ); Debug( 4, "New X:%d", vid_win.x ); Debug( 4, "New Y:%d", vid_win.y ); Debug( 4, "New W:%d", vid_win.width ); Debug( 4, "New H:%d", vid_win.height ); if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) Fatal( "Failed to get window data: %s", strerror(errno) ); Debug( 4, "New P:%d", vid_pic.palette ); Debug( 4, "New D:%d", vid_pic.depth ); Debug( 4, "New B:%d", vid_pic.brightness ); Debug( 4, "New h:%d", vid_pic.hue ); Debug( 4, "New Cl:%d", vid_pic.colour ); Debug( 4, "New Cn:%d", vid_pic.contrast ); } #endif // ZM_HAS_V4L1 } void LocalCamera::Terminate() { #if ZM_HAS_V4L2 if ( v4l_version == 2 ) { Debug( 3, "Terminating video stream" ); //enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // enum v4l2_buf_type type = v4l2_data.fmt.type; enum v4l2_buf_type type = (v4l2_buf_type)v4l2_data.fmt.type; if ( vidioctl( vid_fd, VIDIOC_STREAMOFF, &type ) < 0 ) Error( "Failed to stop capture stream: %s", strerror(errno) ); Debug( 3, "Unmapping video buffers" ); for ( unsigned int i = 0; i < v4l2_data.reqbufs.count; i++ ) { #if HAVE_LIBSWSCALE /* Free capture pictures */ av_free(capturePictures[i]); capturePictures[i] = NULL; #endif if ( munmap( v4l2_data.buffers[i].start, v4l2_data.buffers[i].length ) < 0 ) Error( "Failed to munmap buffer %d: %s", i, strerror(errno) ); } } else #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 if ( v4l_version == 1 ) { #if HAVE_LIBSWSCALE for(int i=0; i < v4l1_data.frames.frames; i++) { /* Free capture pictures */ av_free(capturePictures[i]); capturePictures[i] = NULL; } #endif Debug( 3, "Unmapping video buffers" ); if ( munmap((char*)v4l1_data.bufptr, v4l1_data.frames.size) < 0 ) Error( "Failed to munmap buffers: %s", strerror(errno) ); delete[] v4l1_data.buffers; } #endif // ZM_HAS_V4L1 close( vid_fd ); } uint32_t LocalCamera::AutoSelectFormat(int p_colours) { /* Automatic format selection */ uint32_t selected_palette = 0; #if ZM_HAS_V4L2 char fmt_desc[64][32]; uint32_t fmt_fcc[64]; v4l2_fmtdesc fmtinfo; unsigned int nIndex = 0; //int nRet = 0; // compiler say it isn't used int enum_fd; /* Open the device */ if ((enum_fd = open( device.c_str(), O_RDWR, 0 )) < 0) { Error( "Automatic format selection failed to open video device %s: %s", device.c_str(), strerror(errno) ); return selected_palette; } /* Enumerate available formats */ memset(&fmtinfo, 0, sizeof(fmtinfo)); fmtinfo.index = nIndex; fmtinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; while(vidioctl( enum_fd, VIDIOC_ENUM_FMT, &fmtinfo ) >= 0) { /* Got a format. Copy it to the array */ strcpy(fmt_desc[nIndex], (const char*)(fmtinfo.description)); fmt_fcc[nIndex] = fmtinfo.pixelformat; Debug(6, "Got format: %s (%c%c%c%c) at index %d",fmt_desc[nIndex],fmt_fcc[nIndex]&0xff, (fmt_fcc[nIndex]>>8)&0xff, (fmt_fcc[nIndex]>>16)&0xff, (fmt_fcc[nIndex]>>24)&0xff ,nIndex); /* Proceed to the next index */ memset(&fmtinfo, 0, sizeof(fmtinfo)); fmtinfo.index = ++nIndex; fmtinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; } /* Select format */ int nIndexUsed = -1; int n_preferedformats = 0; const uint32_t* preferedformats; if(p_colours == ZM_COLOUR_RGB32) { /* 32bit */ preferedformats = prefered_rgb32_formats; n_preferedformats = sizeof(prefered_rgb32_formats) / sizeof(uint32_t); } else if(p_colours == ZM_COLOUR_GRAY8) { /* Grayscale */ preferedformats = prefered_gray8_formats; n_preferedformats = sizeof(prefered_gray8_formats) / sizeof(uint32_t); } else { /* Assume 24bit */ preferedformats = prefered_rgb24_formats; n_preferedformats = sizeof(prefered_rgb24_formats) / sizeof(uint32_t); } for( unsigned int i=0; i < (unsigned int)n_preferedformats && nIndexUsed < 0; i++ ) { for( unsigned int j=0; j < nIndex; j++ ) { if( preferedformats[i] == fmt_fcc[j] ) { /* Found a format! */ nIndexUsed = j; break; } } } /* Have we found a match? */ if(nIndexUsed >= 0) { /* Found a match */ selected_palette = fmt_fcc[nIndexUsed]; strcpy(palette_desc,fmt_desc[nIndexUsed]); } /* Close the device */ close(enum_fd); #endif /* ZM_HAS_V4L2 */ return selected_palette; } #define capString(test,prefix,yesString,noString,capability) \ (test) ? (prefix yesString " " capability "\n") : (prefix noString " " capability "\n") bool LocalCamera::GetCurrentSettings( const char *device, char *output, int version, bool verbose ) { output[0] = 0; char queryDevice[PATH_MAX] = ""; int devIndex = 0; do { if ( device ) strcpy( queryDevice, device ); else sprintf( queryDevice, "/dev/video%d", devIndex ); if ( (vid_fd = open(queryDevice, O_RDWR)) <= 0 ) { if ( device ) { Error( "Failed to open video device %s: %s", queryDevice, strerror(errno) ); if ( verbose ) sprintf( output+strlen(output), "Error, failed to open video device %s: %s\n", queryDevice, strerror(errno) ); else sprintf( output+strlen(output), "error%d\n", errno ); return( false ); } else { return( true ); } } if ( verbose ) sprintf( output+strlen(output), "Video Device: %s\n", queryDevice ); else sprintf( output+strlen(output), "d:%s|", queryDevice ); #if ZM_HAS_V4L2 if ( version == 2 ) { struct v4l2_capability vid_cap; if ( vidioctl( vid_fd, VIDIOC_QUERYCAP, &vid_cap ) < 0 ) { Error( "Failed to query video device: %s", strerror(errno) ); if ( verbose ) sprintf( output, "Error, failed to query video capabilities %s: %s\n", queryDevice, strerror(errno) ); else sprintf( output, "error%d\n", errno ); return( false ); } if ( verbose ) { sprintf( output+strlen(output), "General Capabilities\n" ); sprintf( output+strlen(output), " Driver: %s\n", vid_cap.driver ); sprintf( output+strlen(output), " Card: %s\n", vid_cap.card ); sprintf( output+strlen(output), " Bus: %s\n", vid_cap.bus_info ); sprintf( output+strlen(output), " Version: %u.%u.%u\n", (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff ); sprintf( output+strlen(output), " Type: 0x%x\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", vid_cap.capabilities, capString( vid_cap.capabilities&V4L2_CAP_VIDEO_CAPTURE, " ", "Supports", "Does not support", "video capture (X)" ), capString( vid_cap.capabilities&V4L2_CAP_VIDEO_OUTPUT, " ", "Supports", "Does not support", "video output" ), capString( vid_cap.capabilities&V4L2_CAP_VIDEO_OVERLAY, " ", "Supports", "Does not support", "frame buffer overlay" ), capString( vid_cap.capabilities&V4L2_CAP_VBI_CAPTURE, " ", "Supports", "Does not support", "VBI capture" ), capString( vid_cap.capabilities&V4L2_CAP_VBI_OUTPUT, " ", "Supports", "Does not support", "VBI output" ), capString( vid_cap.capabilities&V4L2_CAP_SLICED_VBI_CAPTURE, " ", "Supports", "Does not support", "sliced VBI capture" ), capString( vid_cap.capabilities&V4L2_CAP_SLICED_VBI_OUTPUT, " ", "Supports", "Does not support", "sliced VBI output" ), #ifdef V4L2_CAP_VIDEO_OUTPUT_OVERLAY capString( vid_cap.capabilities&V4L2_CAP_VIDEO_OUTPUT_OVERLAY, " ", "Supports", "Does not support", "video output overlay" ), #else // V4L2_CAP_VIDEO_OUTPUT_OVERLAY "", #endif // V4L2_CAP_VIDEO_OUTPUT_OVERLAY capString( vid_cap.capabilities&V4L2_CAP_TUNER, " ", "Has", "Does not have", "tuner" ), capString( vid_cap.capabilities&V4L2_CAP_AUDIO, " ", "Has", "Does not have", "audio in and/or out" ), capString( vid_cap.capabilities&V4L2_CAP_RADIO, " ", "Has", "Does not have", "radio" ), capString( vid_cap.capabilities&V4L2_CAP_READWRITE, " ", "Supports", "Does not support", "read/write i/o (X)" ), capString( vid_cap.capabilities&V4L2_CAP_ASYNCIO, " ", "Supports", "Does not support", "async i/o" ), capString( vid_cap.capabilities&V4L2_CAP_STREAMING, " ", "Supports", "Does not support", "streaming i/o (X)" ) ); } else { sprintf( output+strlen(output), "D:%s|", vid_cap.driver ); sprintf( output+strlen(output), "C:%s|", vid_cap.card ); sprintf( output+strlen(output), "B:%s|", vid_cap.bus_info ); sprintf( output+strlen(output), "V:%u.%u.%u|", (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff ); sprintf( output+strlen(output), "T:0x%x|", vid_cap.capabilities ); } if ( verbose ) sprintf( output+strlen(output), " Standards:\n" ); else sprintf( output+strlen(output), "S:" ); struct v4l2_standard standard; int standardIndex = 0; do { memset( &standard, 0, sizeof(standard) ); standard.index = standardIndex; if ( vidioctl( vid_fd, VIDIOC_ENUMSTD, &standard ) < 0 ) { if ( errno == EINVAL ) { standardIndex = -1; break; } else { Error( "Failed to enumerate standard %d: %s", standard.index, strerror(errno) ); if ( verbose ) sprintf( output, "Error, failed to enumerate standard %d: %s\n", standard.index, strerror(errno) ); else sprintf( output, "error%d\n", errno ); return( false ); } } if ( verbose ) sprintf( output+strlen(output), " %s\n", standard.name ); else sprintf( output+strlen(output), "%s/", standard.name ); } while ( standardIndex++ >= 0 ); if ( !verbose && output[strlen(output)-1] == '/') output[strlen(output)-1] = '|'; if ( verbose ) sprintf( output+strlen(output), " Formats:\n" ); else sprintf( output+strlen(output), "F:" ); struct v4l2_fmtdesc format; int formatIndex = 0; do { memset( &format, 0, sizeof(format) ); format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; format.index = formatIndex; if ( vidioctl( vid_fd, VIDIOC_ENUM_FMT, &format ) < 0 ) { if ( errno == EINVAL ) { formatIndex = -1; break; } else { Error( "Failed to enumerate format %d: %s", format.index, strerror(errno) ); if ( verbose ) sprintf( output, "Error, failed to enumerate format %d: %s\n", format.index, strerror(errno) ); else sprintf( output, "error%d\n", errno ); return( false ); } } if ( verbose ) sprintf( output+strlen(output), " %s (%c%c%c%c)\n", format.description, format.pixelformat&0xff, (format.pixelformat>>8)&0xff, (format.pixelformat>>16)&0xff, (format.pixelformat>>24)&0xff ); else sprintf( output+strlen(output), "%c%c%c%c/", format.pixelformat&0xff, (format.pixelformat>>8)&0xff, (format.pixelformat>>16)&0xff, (format.pixelformat>>24)&0xff ); } while ( formatIndex++ >= 0 ); if ( !verbose ) output[strlen(output)-1] = '|'; if(verbose) sprintf( output+strlen(output), "Crop Capabilities\n" ); struct v4l2_cropcap cropcap; memset( &cropcap, 0, sizeof(cropcap) ); cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if ( vidioctl( vid_fd, VIDIOC_CROPCAP, &cropcap ) < 0 ) { if(errno != EINVAL) { /* Failed querying crop capability, write error to the log and continue as if crop is not supported */ Error( "Failed to query crop capabilities: %s", strerror(errno) ); } if(verbose) { sprintf( output+strlen(output), " Cropping is not supported\n"); } else { /* Send fake crop bounds to not confuse things parsing this, such as monitor probe */ sprintf( output+strlen(output), "B:%dx%d|",0,0); } } else { struct v4l2_crop crop; memset( &crop, 0, sizeof(crop) ); crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if ( vidioctl( vid_fd, VIDIOC_G_CROP, &crop ) < 0 ) { if ( errno != EINVAL ) { /* Failed querying crop sizes, write error to the log and continue as if crop is not supported */ Error( "Failed to query crop: %s", strerror(errno) ); } if ( verbose ) { sprintf( output+strlen(output), " Cropping is not supported\n"); } else { /* Send fake crop bounds to not confuse things parsing this, such as monitor probe */ sprintf( output+strlen(output), "B:%dx%d|",0,0); } } else { /* Cropping supported */ if ( verbose ) { sprintf( output+strlen(output), " Bounds: %d x %d\n", cropcap.bounds.width, cropcap.bounds.height ); sprintf( output+strlen(output), " Default: %d x %d\n", cropcap.defrect.width, cropcap.defrect.height ); sprintf( output+strlen(output), " Current: %d x %d\n", crop.c.width, crop.c.height ); } else { sprintf( output+strlen(output), "B:%dx%d|", cropcap.bounds.width, cropcap.bounds.height ); } } } /* Crop code */ struct v4l2_input input; int inputIndex = 0; do { memset( &input, 0, sizeof(input) ); input.index = inputIndex; if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) { if ( errno == EINVAL ) { break; } else { Error( "Failed to enumerate input %d: %s", input.index, strerror(errno) ); if ( verbose ) sprintf( output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno) ); else sprintf( output, "error%d\n", errno ); return( false ); } } } while ( inputIndex++ >= 0 ); if ( verbose ) sprintf( output+strlen(output), "Inputs: %d\n", inputIndex ); else sprintf( output+strlen(output), "I:%d|", inputIndex ); inputIndex = 0; do { memset( &input, 0, sizeof(input) ); input.index = inputIndex; if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) { if ( errno == EINVAL ) { inputIndex = -1; break; } else { Error( "Failed to enumerate input %d: %s", input.index, strerror(errno) ); if ( verbose ) sprintf( output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno) ); else sprintf( output, "error%d\n", errno ); return( false ); } } if ( vidioctl( vid_fd, VIDIOC_S_INPUT, &input.index ) < 0 ) { Error( "Failed to set video input %d: %s", input.index, strerror(errno) ); if ( verbose ) sprintf( output, "Error, failed to switch to input %d: %s\n", input.index, strerror(errno) ); else sprintf( output, "error%d\n", errno ); return( false ); } if ( verbose ) { sprintf( output+strlen(output), " Input %d\n", input.index ); sprintf( output+strlen(output), " Name: %s\n", input.name ); sprintf( output+strlen(output), " Type: %s\n", input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") ); sprintf( output+strlen(output), " Audioset: %08x\n", input.audioset ); sprintf( output+strlen(output), " Standards: 0x%llx\n", input.std ); } else { sprintf( output+strlen(output), "i%d:%s|", input.index, input.name ); sprintf( output+strlen(output), "i%dT:%s|", input.index, input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") ); sprintf( output+strlen(output), "i%dS:%llx|", input.index, input.std ); } if ( verbose ) { sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_POWER, "Power ", "off", "on", " (X)" ) ); sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_SIGNAL, "Signal ", "not detected", "detected", " (X)" ) ); sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_COLOR, "Colour Signal ", "not detected", "detected", "" ) ); sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_H_LOCK, "Horizontal Lock ", "not detected", "detected", "" ) ); } else { sprintf( output+strlen(output), "i%dSP:%d|", input.index, input.status&V4L2_IN_ST_NO_POWER?0:1 ); sprintf( output+strlen(output), "i%dSS:%d|", input.index, input.status&V4L2_IN_ST_NO_SIGNAL?0:1 ); sprintf( output+strlen(output), "i%dSC:%d|", input.index, input.status&V4L2_IN_ST_NO_COLOR?0:1 ); sprintf( output+strlen(output), "i%dHP:%d|", input.index, input.status&V4L2_IN_ST_NO_H_LOCK?0:1 ); } } while ( inputIndex++ >= 0 ); if ( !verbose ) output[strlen(output)-1] = '\n'; } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 if ( version == 1 ) { struct video_capability vid_cap; memset( &vid_cap, 0, sizeof(video_capability) ); if ( ioctl( vid_fd, VIDIOCGCAP, &vid_cap ) < 0 ) { Error( "Failed to get video capabilities: %s", strerror(errno) ); if ( verbose ) sprintf( output, "Error, failed to get video capabilities %s: %s\n", queryDevice, strerror(errno) ); else sprintf( output, "error%d\n", errno ); return( false ); } if ( verbose ) { sprintf( output+strlen(output), "Video Capabilities\n" ); sprintf( output+strlen(output), " Name: %s\n", vid_cap.name ); sprintf( output+strlen(output), " Type: %d\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", vid_cap.type, vid_cap.type&VID_TYPE_CAPTURE?" Can capture\n":"", vid_cap.type&VID_TYPE_TUNER?" Can tune\n":"", vid_cap.type&VID_TYPE_TELETEXT?" Does teletext\n":"", vid_cap.type&VID_TYPE_OVERLAY?" Overlay onto frame buffer\n":"", vid_cap.type&VID_TYPE_CHROMAKEY?" Overlay by chromakey\n":"", vid_cap.type&VID_TYPE_CLIPPING?" Can clip\n":"", vid_cap.type&VID_TYPE_FRAMERAM?" Uses the frame buffer memory\n":"", vid_cap.type&VID_TYPE_SCALES?" Scalable\n":"", vid_cap.type&VID_TYPE_MONOCHROME?" Monochrome only\n":"", vid_cap.type&VID_TYPE_SUBCAPTURE?" Can capture subareas of the image\n":"", vid_cap.type&VID_TYPE_MPEG_DECODER?" Can decode MPEG streams\n":"", vid_cap.type&VID_TYPE_MPEG_ENCODER?" Can encode MPEG streams\n":"", vid_cap.type&VID_TYPE_MJPEG_DECODER?" Can decode MJPEG streams\n":"", vid_cap.type&VID_TYPE_MJPEG_ENCODER?" Can encode MJPEG streams\n":"" ); sprintf( output+strlen(output), " Video Channels: %d\n", vid_cap.channels ); sprintf( output+strlen(output), " Audio Channels: %d\n", vid_cap.audios ); sprintf( output+strlen(output), " Maximum Width: %d\n", vid_cap.maxwidth ); sprintf( output+strlen(output), " Maximum Height: %d\n", vid_cap.maxheight ); sprintf( output+strlen(output), " Minimum Width: %d\n", vid_cap.minwidth ); sprintf( output+strlen(output), " Minimum Height: %d\n", vid_cap.minheight ); } else { sprintf( output+strlen(output), "N:%s|", vid_cap.name ); sprintf( output+strlen(output), "T:%d|", vid_cap.type ); sprintf( output+strlen(output), "nC:%d|", vid_cap.channels ); sprintf( output+strlen(output), "nA:%d|", vid_cap.audios ); sprintf( output+strlen(output), "mxW:%d|", vid_cap.maxwidth ); sprintf( output+strlen(output), "mxH:%d|", vid_cap.maxheight ); sprintf( output+strlen(output), "mnW:%d|", vid_cap.minwidth ); sprintf( output+strlen(output), "mnH:%d|", vid_cap.minheight ); } struct video_window vid_win; memset( &vid_win, 0, sizeof(video_window) ); if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win ) < 0 ) { Error( "Failed to get window attributes: %s", strerror(errno) ); if ( verbose ) sprintf( output, "Error, failed to get window attributes: %s\n", strerror(errno) ); else sprintf( output, "error%d\n", errno ); return( false ); } if ( verbose ) { sprintf( output+strlen(output), "Window Attributes\n" ); sprintf( output+strlen(output), " X Offset: %d\n", vid_win.x ); sprintf( output+strlen(output), " Y Offset: %d\n", vid_win.y ); sprintf( output+strlen(output), " Width: %d\n", vid_win.width ); sprintf( output+strlen(output), " Height: %d\n", vid_win.height ); } else { sprintf( output+strlen(output), "X:%d|", vid_win.x ); sprintf( output+strlen(output), "Y:%d|", vid_win.y ); sprintf( output+strlen(output), "W:%d|", vid_win.width ); sprintf( output+strlen(output), "H:%d|", vid_win.height ); } struct video_picture vid_pic; memset( &vid_cap, 0, sizeof(video_picture) ); if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic ) < 0 ) { Error( "Failed to get picture attributes: %s", strerror(errno) ); if ( verbose ) sprintf( output, "Error, failed to get picture attributes: %s\n", strerror(errno) ); else sprintf( output, "error%d\n", errno ); return( false ); } if ( verbose ) { sprintf( output+strlen(output), "Picture Attributes\n" ); sprintf( output+strlen(output), " Palette: %d - %s\n", vid_pic.palette, vid_pic.palette==VIDEO_PALETTE_GREY?"Linear greyscale":( vid_pic.palette==VIDEO_PALETTE_HI240?"High 240 cube (BT848)":( vid_pic.palette==VIDEO_PALETTE_RGB565?"565 16 bit RGB":( vid_pic.palette==VIDEO_PALETTE_RGB24?"24bit RGB":( vid_pic.palette==VIDEO_PALETTE_RGB32?"32bit RGB":( vid_pic.palette==VIDEO_PALETTE_RGB555?"555 15bit RGB":( vid_pic.palette==VIDEO_PALETTE_YUV422?"YUV422 capture":( vid_pic.palette==VIDEO_PALETTE_YUYV?"YUYV":( vid_pic.palette==VIDEO_PALETTE_UYVY?"UVYV":( vid_pic.palette==VIDEO_PALETTE_YUV420?"YUV420":( vid_pic.palette==VIDEO_PALETTE_YUV411?"YUV411 capture":( vid_pic.palette==VIDEO_PALETTE_RAW?"RAW capture (BT848)":( vid_pic.palette==VIDEO_PALETTE_YUYV?"YUYV":( vid_pic.palette==VIDEO_PALETTE_YUV422?"YUV422":( vid_pic.palette==VIDEO_PALETTE_YUV422P?"YUV 4:2:2 Planar":( vid_pic.palette==VIDEO_PALETTE_YUV411P?"YUV 4:1:1 Planar":( vid_pic.palette==VIDEO_PALETTE_YUV420P?"YUV 4:2:0 Planar":( vid_pic.palette==VIDEO_PALETTE_YUV410P?"YUV 4:1:0 Planar":"Unknown" )))))))))))))))))); sprintf( output+strlen(output), " Colour Depth: %d\n", vid_pic.depth ); sprintf( output+strlen(output), " Brightness: %d\n", vid_pic.brightness ); sprintf( output+strlen(output), " Hue: %d\n", vid_pic.hue ); sprintf( output+strlen(output), " Colour :%d\n", vid_pic.colour ); sprintf( output+strlen(output), " Contrast: %d\n", vid_pic.contrast ); sprintf( output+strlen(output), " Whiteness: %d\n", vid_pic.whiteness ); } else { sprintf( output+strlen(output), "P:%d|", vid_pic.palette ); sprintf( output+strlen(output), "D:%d|", vid_pic.depth ); sprintf( output+strlen(output), "B:%d|", vid_pic.brightness ); sprintf( output+strlen(output), "h:%d|", vid_pic.hue ); sprintf( output+strlen(output), "Cl:%d|", vid_pic.colour ); sprintf( output+strlen(output), "Cn:%d|", vid_pic.contrast ); sprintf( output+strlen(output), "w:%d|", vid_pic.whiteness ); } for ( int chan = 0; chan < vid_cap.channels; chan++ ) { struct video_channel vid_src; memset( &vid_src, 0, sizeof(video_channel) ); vid_src.channel = chan; if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src ) < 0 ) { Error( "Failed to get channel %d attributes: %s", chan, strerror(errno) ); if ( verbose ) sprintf( output, "Error, failed to get channel %d attributes: %s\n", chan, strerror(errno) ); else sprintf( output, "error%d\n", errno ); return( false ); } if ( verbose ) { sprintf( output+strlen(output), "Channel %d Attributes\n", chan ); sprintf( output+strlen(output), " Name: %s\n", vid_src.name ); sprintf( output+strlen(output), " Channel: %d\n", vid_src.channel ); sprintf( output+strlen(output), " Flags: %d\n%s%s", vid_src.flags, vid_src.flags&VIDEO_VC_TUNER?" Channel has a tuner\n":"", vid_src.flags&VIDEO_VC_AUDIO?" Channel has audio\n":"" ); sprintf( output+strlen(output), " Type: %d - %s\n", vid_src.type, vid_src.type==VIDEO_TYPE_TV?"TV":( vid_src.type==VIDEO_TYPE_CAMERA?"Camera":"Unknown" )); sprintf( output+strlen(output), " Format: %d - %s\n", vid_src.norm, vid_src.norm==VIDEO_MODE_PAL?"PAL":( vid_src.norm==VIDEO_MODE_NTSC?"NTSC":( vid_src.norm==VIDEO_MODE_SECAM?"SECAM":( vid_src.norm==VIDEO_MODE_AUTO?"AUTO":"Unknown" )))); } else { sprintf( output+strlen(output), "n%d:%s|", chan, vid_src.name ); sprintf( output+strlen(output), "C%d:%d|", chan, vid_src.channel ); sprintf( output+strlen(output), "Fl%d:%x|", chan, vid_src.flags ); sprintf( output+strlen(output), "T%d:%d|", chan, vid_src.type ); sprintf( output+strlen(output), "F%d:%d%s|", chan, vid_src.norm, chan==(vid_cap.channels-1)?"":"," ); } } if ( !verbose ) output[strlen(output)-1] = '\n'; } #endif // ZM_HAS_V4L1 close( vid_fd ); if ( device ) break; } while ( ++devIndex < 32 ); return( true ); } int LocalCamera::Brightness( int p_brightness ) { #if ZM_HAS_V4L2 if ( v4l_version == 2 ) { struct v4l2_control vid_control; memset( &vid_control, 0, sizeof(vid_control) ); vid_control.id = V4L2_CID_BRIGHTNESS; if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) { if ( errno != EINVAL ) Error( "Unable to query brightness: %s", strerror(errno) ) else Warning( "Brightness control is not supported" ) //Info( "Brightness 1 %d", vid_control.value ); } else if ( p_brightness >= 0 ) { vid_control.value = p_brightness; //Info( "Brightness 2 %d", vid_control.value ); /* The driver may clamp the value or return ERANGE, ignored here */ if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) ) { if ( errno != ERANGE ) Error( "Unable to set brightness: %s", strerror(errno) ) else Warning( "Given brightness value (%d) may be out-of-range", p_brightness ) } //Info( "Brightness 3 %d", vid_control.value ); } return( vid_control.value ); } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 if ( v4l_version == 1 ) { struct video_picture vid_pic; memset( &vid_pic, 0, sizeof(video_picture) ); if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) { Error( "Failed to get picture attributes: %s", strerror(errno) ); return( -1 ); } if ( p_brightness >= 0 ) { vid_pic.brightness = p_brightness; if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) { Error( "Failed to set picture attributes: %s", strerror(errno) ); return( -1 ); } } return( vid_pic.brightness ); } #endif // ZM_HAS_V4L1 return( -1 ); } int LocalCamera::Hue( int p_hue ) { #if ZM_HAS_V4L2 if ( v4l_version == 2 ) { struct v4l2_control vid_control; memset( &vid_control, 0, sizeof(vid_control) ); vid_control.id = V4L2_CID_HUE; if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) { if ( errno != EINVAL ) Error( "Unable to query hue: %s", strerror(errno) ) else Warning( "Hue control is not supported" ) } else if ( p_hue >= 0 ) { vid_control.value = p_hue; /* The driver may clamp the value or return ERANGE, ignored here */ if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) < 0 ) { if ( errno != ERANGE ) Error( "Unable to set hue: %s", strerror(errno) ) else Warning( "Given hue value (%d) may be out-of-range", p_hue ) } } return( vid_control.value ); } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 if ( v4l_version == 1 ) { struct video_picture vid_pic; memset( &vid_pic, 0, sizeof(video_picture) ); if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) { Error( "Failed to get picture attributes: %s", strerror(errno) ); return( -1 ); } if ( p_hue >= 0 ) { vid_pic.hue = p_hue; if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) { Error( "Failed to set picture attributes: %s", strerror(errno) ); return( -1 ); } } return( vid_pic.hue ); } #endif // ZM_HAS_V4L1 return( -1 ); } int LocalCamera::Colour( int p_colour ) { #if ZM_HAS_V4L2 if ( v4l_version == 2 ) { struct v4l2_control vid_control; memset( &vid_control, 0, sizeof(vid_control) ); vid_control.id = V4L2_CID_SATURATION; if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) { if ( errno != EINVAL ) Error( "Unable to query saturation: %s", strerror(errno) ) else Warning( "Saturation control is not supported" ) } else if ( p_colour >= 0 ) { vid_control.value = p_colour; /* The driver may clamp the value or return ERANGE, ignored here */ if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) < 0 ) { if ( errno != ERANGE ) Error( "Unable to set saturation: %s", strerror(errno) ) else Warning( "Given saturation value (%d) may be out-of-range", p_colour ) } } return( vid_control.value ); } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 if ( v4l_version == 1 ) { struct video_picture vid_pic; memset( &vid_pic, 0, sizeof(video_picture) ); if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) { Error( "Failed to get picture attributes: %s", strerror(errno) ); return( -1 ); } if ( p_colour >= 0 ) { vid_pic.colour = p_colour; if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) { Error( "Failed to set picture attributes: %s", strerror(errno) ); return( -1 ); } } return( vid_pic.colour ); } #endif // ZM_HAS_V4L1 return( -1 ); } int LocalCamera::Contrast( int p_contrast ) { #if ZM_HAS_V4L2 if ( v4l_version == 2 ) { struct v4l2_control vid_control; memset( &vid_control, 0, sizeof(vid_control) ); vid_control.id = V4L2_CID_CONTRAST; if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) { if ( errno != EINVAL ) Error( "Unable to query contrast: %s", strerror(errno) ) else Warning( "Contrast control is not supported" ) } else if ( p_contrast >= 0 ) { vid_control.value = p_contrast; /* The driver may clamp the value or return ERANGE, ignored here */ if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) ) { if ( errno != ERANGE ) Error( "Unable to set contrast: %s", strerror(errno) ) else Warning( "Given contrast value (%d) may be out-of-range", p_contrast ) } } return( vid_control.value ); } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 if ( v4l_version == 1 ) { struct video_picture vid_pic; memset( &vid_pic, 0, sizeof(video_picture) ); if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) { Error( "Failed to get picture attributes: %s", strerror(errno) ); return( -1 ); } if ( p_contrast >= 0 ) { vid_pic.contrast = p_contrast; if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) { Error( "Failed to set picture attributes: %s", strerror(errno) ); return( -1 ); } } return( vid_pic.contrast ); } #endif // ZM_HAS_V4L1 return( -1 ); } int LocalCamera::PrimeCapture() { Initialise(); Debug( 2, "Priming capture" ); #if ZM_HAS_V4L2 if ( v4l_version == 2 ) { Debug( 3, "Queueing buffers" ); for ( unsigned int frame = 0; frame < v4l2_data.reqbufs.count; frame++ ) { struct v4l2_buffer vid_buf; memset( &vid_buf, 0, sizeof(vid_buf) ); vid_buf.type = v4l2_data.fmt.type; vid_buf.memory = v4l2_data.reqbufs.memory; vid_buf.index = frame; if ( vidioctl( vid_fd, VIDIOC_QBUF, &vid_buf ) < 0 ) Fatal( "Failed to queue buffer %d: %s", frame, strerror(errno) ); } v4l2_data.bufptr = NULL; Debug( 3, "Starting video stream" ); //enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //enum v4l2_buf_type type = v4l2_data.fmt.type; enum v4l2_buf_type type = (v4l2_buf_type)v4l2_data.fmt.type; if ( vidioctl( vid_fd, VIDIOC_STREAMON, &type ) < 0 ) Fatal( "Failed to start capture stream: %s", strerror(errno) ); } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 if ( v4l_version == 1 ) { for ( int frame = 0; frame < v4l1_data.frames.frames; frame++ ) { Debug( 3, "Queueing frame %d", frame ); if ( ioctl( vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[frame] ) < 0 ) { Error( "Capture failure for frame %d: %s", frame, strerror(errno) ); return( -1 ); } } } #endif // ZM_HAS_V4L1 return( 0 ); } int LocalCamera::PreCapture() { Debug( 2, "Pre-capturing" ); return( 0 ); } int LocalCamera::Capture( Image &image ) { Debug( 3, "Capturing" ); static uint8_t* buffer = NULL; static uint8_t* directbuffer = NULL; static int capture_frame = -1; int buffer_bytesused = 0; int captures_per_frame = 1; if ( channel_count > 1 ) captures_per_frame = config.captures_per_frame; // Do the capture, unless we are the second or subsequent camera on a channel, in which case just reuse the buffer if ( channel_prime ) { #if ZM_HAS_V4L2 if ( v4l_version == 2 ) { static struct v4l2_buffer vid_buf; memset( &vid_buf, 0, sizeof(vid_buf) ); vid_buf.type = v4l2_data.fmt.type; //vid_buf.memory = V4L2_MEMORY_MMAP; vid_buf.memory = v4l2_data.reqbufs.memory; Debug( 3, "Capturing %d frames", captures_per_frame ); while ( captures_per_frame ) { if ( vidioctl( vid_fd, VIDIOC_DQBUF, &vid_buf ) < 0 ) { if ( errno == EIO ) Warning( "Capture failure, possible signal loss?: %s", strerror(errno) ) else Error( "Unable to capture frame %d: %s", vid_buf.index, strerror(errno) ) return( -1 ); } v4l2_data.bufptr = &vid_buf; capture_frame = v4l2_data.bufptr->index; if ( --captures_per_frame ) { if ( vidioctl( vid_fd, VIDIOC_QBUF, &vid_buf ) < 0 ) { Error( "Unable to requeue buffer %d: %s", vid_buf.index, strerror(errno) ); return( -1 ); } } } Debug( 3, "Captured frame %d/%d from channel %d", capture_frame, v4l2_data.bufptr->sequence, channel ); buffer = (unsigned char *)v4l2_data.buffers[v4l2_data.bufptr->index].start; buffer_bytesused = v4l2_data.bufptr->bytesused; if((v4l2_data.fmt.fmt.pix.width * v4l2_data.fmt.fmt.pix.height) != (width * height)) { Fatal("Captured image dimensions differ: V4L2: %dx%d monitor: %dx%d",v4l2_data.fmt.fmt.pix.width,v4l2_data.fmt.fmt.pix.height,width,height); } } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 if ( v4l_version == 1 ) { Debug( 3, "Capturing %d frames", captures_per_frame ); while ( captures_per_frame ) { Debug( 3, "Syncing frame %d", v4l1_data.active_frame ); if ( ioctl( vid_fd, VIDIOCSYNC, &v4l1_data.active_frame ) < 0 ) { Error( "Sync failure for frame %d buffer %d: %s", v4l1_data.active_frame, captures_per_frame, strerror(errno) ); return( -1 ); } captures_per_frame--; if ( captures_per_frame ) { Debug( 3, "Capturing frame %d", v4l1_data.active_frame ); if ( ioctl( vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[v4l1_data.active_frame] ) < 0 ) { Error( "Capture failure for buffer %d (%d): %s", v4l1_data.active_frame, captures_per_frame, strerror(errno) ); return( -1 ); } } } capture_frame = v4l1_data.active_frame; Debug( 3, "Captured %d for channel %d", capture_frame, channel ); buffer = v4l1_data.bufptr+v4l1_data.frames.offsets[capture_frame]; } #endif // ZM_HAS_V4L1 } /* prime capture */ if(conversion_type != 0) { Debug( 3, "Performing format conversion" ); /* Request a writeable buffer of the target image */ directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); if(directbuffer == NULL) { Error("Failed requesting writeable buffer for the captured image."); return (-1); } #if HAVE_LIBSWSCALE if(conversion_type == 1) { Debug( 9, "Calling sws_scale to perform the conversion" ); /* Use swscale to convert the image directly into the shared memory */ avpicture_fill( (AVPicture *)tmpPicture, directbuffer, imagePixFormat, width, height ); sws_scale( imgConversionContext, capturePictures[capture_frame]->data, capturePictures[capture_frame]->linesize, 0, height, tmpPicture->data, tmpPicture->linesize ); } #endif if(conversion_type == 2) { Debug( 9, "Calling the conversion function" ); /* Call the image conversion function and convert directly into the shared memory */ (*conversion_fptr)(buffer, directbuffer, pixels); } else if(conversion_type == 3) { Debug( 9, "Decoding the JPEG image" ); /* JPEG decoding */ image.DecodeJpeg(buffer, buffer_bytesused, colours, subpixelorder); } } else { Debug( 3, "No format conversion performed. Assigning the image" ); /* No conversion was performed, the image is in the V4L buffers and needs to be copied into the shared memory */ image.Assign( width, height, colours, subpixelorder, buffer, imagesize); } return( 0 ); } int LocalCamera::PostCapture() { Debug( 2, "Post-capturing" ); // Requeue the buffer unless we need to switch or are a duplicate camera on a channel if ( channel_count > 1 || channel_prime ) { #if ZM_HAS_V4L2 if ( v4l_version == 2 ) { if ( channel_count > 1 ) { int next_channel = (channel_index+1)%channel_count; Debug( 3, "Switching video source to %d", channels[next_channel] ); if ( vidioctl( vid_fd, VIDIOC_S_INPUT, &channels[next_channel] ) < 0 ) { Error( "Failed to set camera source %d: %s", channels[next_channel], strerror(errno) ); return( -1 ); } v4l2_std_id stdId = standards[next_channel]; if ( vidioctl( vid_fd, VIDIOC_S_STD, &stdId ) < 0 ) { Error( "Failed to set video format %d: %s", standards[next_channel], strerror(errno) ); return( -1 ); } } Debug( 3, "Requeueing buffer %d", v4l2_data.bufptr->index ); if ( vidioctl( vid_fd, VIDIOC_QBUF, v4l2_data.bufptr ) < 0 ) { Error( "Unable to requeue buffer %d: %s", v4l2_data.bufptr->index, strerror(errno) ) return( -1 ); } } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 if ( v4l_version == 1 ) { if ( channel_count > 1 ) { Debug( 3, "Switching video source" ); int next_channel = (channel_index+1)%channel_count; struct video_channel vid_src; memset( &vid_src, 0, sizeof(vid_src) ); vid_src.channel = channel; if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) { Error( "Failed to get camera source %d: %s", channel, strerror(errno) ); return(-1); } vid_src.channel = channels[next_channel]; vid_src.norm = standards[next_channel]; vid_src.flags = 0; vid_src.type = VIDEO_TYPE_CAMERA; if ( ioctl( vid_fd, VIDIOCSCHAN, &vid_src ) < 0 ) { Error( "Failed to set camera source %d: %s", channel, strerror(errno) ); return( -1 ); } } Debug( 3, "Requeueing frame %d", v4l1_data.active_frame ); if ( ioctl( vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[v4l1_data.active_frame] ) < 0 ) { Error( "Capture failure for frame %d: %s", v4l1_data.active_frame, strerror(errno) ); return( -1 ); } v4l1_data.active_frame = (v4l1_data.active_frame+1)%v4l1_data.frames.frames; } #endif // ZM_HAS_V4L1 } return( 0 ); } #endif // ZM_HAS_V4L ZoneMinder-1.26.5/src/zm_local_camera.h000066400000000000000000000075061225361755400177510ustar00rootroot00000000000000// // ZoneMinder Local Camera Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_LOCAL_CAMERA_H #define ZM_LOCAL_CAMERA_H #include "zm.h" #include "zm_camera.h" #include "zm_image.h" #if ZM_HAS_V4L #ifdef HAVE_LINUX_VIDEODEV_H #include #endif // HAVE_LINUX_VIDEODEV_H #ifdef HAVE_LINUX_VIDEODEV2_H #include #endif // HAVE_LINUX_VIDEODEV2_H #include "zm_ffmpeg.h" // // Class representing 'local' cameras, i.e. those which are // directly connect to the host machine and which are accessed // via a video interface. // class LocalCamera : public Camera { protected: #if ZM_HAS_V4L2 struct V4L2MappedBuffer { void *start; size_t length; }; struct V4L2Data { v4l2_cropcap cropcap; v4l2_crop crop; v4l2_format fmt; v4l2_requestbuffers reqbufs; V4L2MappedBuffer *buffers; v4l2_buffer *bufptr; }; #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 struct V4L1Data { int active_frame; video_mbuf frames; video_mmap *buffers; unsigned char *bufptr; }; #endif // ZM_HAS_V4L1 protected: std::string device; int channel; int standard; int palette; bool device_prime; bool channel_prime; int channel_index; unsigned int extras; unsigned int conversion_type; /* 0 = no conversion needed, 1 = use libswscale, 2 = zm internal conversion, 3 = jpeg decoding */ convert_fptr_t conversion_fptr; /* Pointer to conversion function used */ uint32_t AutoSelectFormat(int p_colours); protected: static int camera_count; static int channel_count; static int channels[VIDEO_MAX_FRAME]; static int standards[VIDEO_MAX_FRAME]; static int vid_fd; static int v4l_version; #if ZM_HAS_V4L2 static V4L2Data v4l2_data; #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 static V4L1Data v4l1_data; #endif // ZM_HAS_V4L1 #if HAVE_LIBSWSCALE static AVFrame **capturePictures; PixelFormat imagePixFormat; PixelFormat capturePixFormat; struct SwsContext *imgConversionContext; AVFrame *tmpPicture; #endif // HAVE_LIBSWSCALE static LocalCamera *last_camera; public: LocalCamera( int p_id, const std::string &device, int p_channel, int p_format, const std::string &p_method, int p_width, int p_height, int p_colours, int p_palette, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, unsigned int p_extras = 0); ~LocalCamera(); void Initialise(); void Terminate(); const std::string &Device() const { return( device ); } int Channel() const { return( channel ); } int Standard() const { return( standard ); } int Palette() const { return( palette ); } int Extras() const { return( extras ); } int Brightness( int p_brightness=-1 ); int Hue( int p_hue=-1 ); int Colour( int p_colour=-1 ); int Contrast( int p_contrast=-1 ); int PrimeCapture(); int PreCapture(); int Capture( Image &image ); int PostCapture(); static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose ); }; #endif // ZM_HAS_V4L #endif // ZM_LOCAL_CAMERA_H ZoneMinder-1.26.5/src/zm_logger.cpp000066400000000000000000000446231225361755400171620ustar00rootroot00000000000000/* * ZoneMinder Logger Implementation, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "zm_logger.h" #include "zm_config.h" #include "zm_utils.h" #include #include #include #include #include #include #include #include #include bool Logger::smInitialised = false; Logger *Logger::smInstance = 0; Logger::StringMap Logger::smCodes; Logger::IntMap Logger::smSyslogPriorities; #if 0 static void subtractTime( struct timeval * const tp1, struct timeval * const tp2 ) { tp1->tv_sec -= tp2->tv_sec; if ( tp1->tv_usec <= tp2->tv_usec ) { tp1->tv_sec--; tp1->tv_usec = 1000000 - (tp2->tv_usec - tp1->tv_usec); } else { tp1->tv_usec = tp1->tv_usec - tp2->tv_usec; } } #endif void Logger::usrHandler( int sig ) { Logger *logger = fetch(); if ( sig == SIGUSR1 ) logger->level( logger->level()+1 ); else if ( sig == SIGUSR2 ) logger->level( logger->level()-1 ); Info( "Logger - Level changed to %d", logger->level() ); } Logger::Logger() : mLevel( INFO ), mTermLevel( NOLOG ), mDatabaseLevel( NOLOG ), mFileLevel( NOLOG ), mSyslogLevel( NOLOG ), mEffectiveLevel( NOLOG ), //mLogPath( config.path_logs ), //mLogFile( mLogPath+"/"+mId+".log" ), mDbConnected( false ), mLogFileFP( NULL ), mHasTerm( false ), mFlush( false ) { if ( smInstance ) { Panic( "Attempt to create second instance of Logger class" ); } if ( !smInitialised ) { smCodes[INFO] = "INF"; smCodes[WARNING] = "WAR"; smCodes[ERROR] = "ERR"; smCodes[FATAL] = "FAT"; smCodes[PANIC] = "PNC"; smCodes[NOLOG] = "OFF"; smSyslogPriorities[INFO] = LOG_INFO; smSyslogPriorities[WARNING] = LOG_WARNING; smSyslogPriorities[ERROR] = LOG_ERR; smSyslogPriorities[FATAL] = LOG_ERR; smSyslogPriorities[PANIC] = LOG_ERR; char code[4] = ""; for ( int i = DEBUG1; i <= DEBUG9; i++ ) { snprintf( code, sizeof(code), "DB%d", i ); smCodes[i] = code; smSyslogPriorities[i] = LOG_DEBUG; } smInitialised = true; } if ( fileno(stderr) && isatty(fileno(stderr)) ) mHasTerm = true; } Logger::~Logger() { terminate(); } void Logger::initialise( const std::string &id, const Options &options ) { char *envPtr; if ( !id.empty() ) this->id( id ); std::string tempLogFile; if ( options.mLogPath.size() ) { mLogPath = options.mLogPath; tempLogFile = mLogPath+"/"+mId+".log"; } if ( options.mLogFile.size() ) tempLogFile = options.mLogFile; else tempLogFile = mLogPath+"/"+mId+".log"; if ( (envPtr = getTargettedEnv( "LOG_FILE" )) ) tempLogFile = envPtr; Level tempLevel = INFO; Level tempTermLevel = mTermLevel; Level tempDatabaseLevel = mDatabaseLevel; Level tempFileLevel = mFileLevel; Level tempSyslogLevel = mSyslogLevel; if ( options.mTermLevel != NOOPT ) tempTermLevel = options.mTermLevel; if ( options.mDatabaseLevel != NOOPT ) tempDatabaseLevel = options.mDatabaseLevel; else tempDatabaseLevel = config.log_level_database >= DEBUG1 ? DEBUG9 : config.log_level_database; if ( options.mFileLevel != NOOPT ) tempFileLevel = options.mFileLevel; else tempFileLevel = config.log_level_file >= DEBUG1 ? DEBUG9 : config.log_level_file; if ( options.mSyslogLevel != NOOPT ) tempSyslogLevel = options.mSyslogLevel; else tempSyslogLevel = config.log_level_syslog >= DEBUG1 ? DEBUG9 : config.log_level_syslog; // Legacy if ( (envPtr = getenv( "LOG_PRINT" )) ) tempTermLevel = atoi(envPtr) ? DEBUG9 : NOLOG; if ( (envPtr = getTargettedEnv( "LOG_LEVEL" )) ) tempLevel = atoi(envPtr); if ( (envPtr = getTargettedEnv( "LOG_LEVEL_TERM" )) ) tempTermLevel = atoi(envPtr); if ( (envPtr = getTargettedEnv( "LOG_LEVEL_DATABASE" )) ) tempDatabaseLevel = atoi(envPtr); if ( (envPtr = getTargettedEnv( "LOG_LEVEL_FILE" )) ) tempFileLevel = atoi(envPtr); if ( (envPtr = getTargettedEnv( "LOG_LEVEL_SYSLOG" )) ) tempSyslogLevel = atoi(envPtr); if ( config.log_debug ) { StringVector targets = split( config.log_debug_target, "|" ); for ( unsigned int i = 0; i < targets.size(); i++ ) { const std::string &target = targets[i]; if ( target == mId || target == "_"+mId || target == "_"+mIdRoot || target == "_"+mIdRoot || target == "" ) { if ( config.log_debug_level > NOLOG ) { tempLevel = config.log_debug_level; if ( config.log_debug_file[0] ) { tempLogFile = config.log_debug_file; tempFileLevel = tempLevel; } } } } } logFile( tempLogFile ); termLevel( tempTermLevel ); databaseLevel( tempDatabaseLevel ); fileLevel( tempFileLevel ); syslogLevel( tempSyslogLevel ); level( tempLevel ); mFlush = (envPtr = getenv( "LOG_FLUSH")) ? atoi( envPtr ) : false; //mRuntime = (envPtr = getenv( "LOG_RUNTIME")) ? atoi( envPtr ) : false; { struct sigaction action; memset( &action, 0, sizeof(action) ); action.sa_handler = usrHandler; action.sa_flags = SA_RESTART; if ( sigaction( SIGUSR1, &action, 0 ) < 0 ) { Fatal( "sigaction(), error = %s", strerror(errno) ); } if ( sigaction( SIGUSR2, &action, 0 ) < 0) { Fatal( "sigaction(), error = %s", strerror(errno) ); } } mInitialised = true; Debug( 1, "LogOpts: level=%s/%s, screen=%s, database=%s, logfile=%s->%s, syslog=%s", smCodes[mLevel].c_str(), smCodes[mEffectiveLevel].c_str(), smCodes[mTermLevel].c_str(), smCodes[mDatabaseLevel].c_str(), smCodes[mFileLevel].c_str(), mLogFile.c_str(), smCodes[mSyslogLevel].c_str() ); } void Logger::terminate() { Info( "Terminating Logger" ); if ( mFileLevel > NOLOG ) closeFile(); if ( mSyslogLevel > NOLOG ) closeSyslog(); } bool Logger::boolEnv( const std::string &name, bool defaultValue ) { const char *envPtr = getenv( name.c_str() ); return( envPtr ? atoi( envPtr ) : defaultValue ); } int Logger::intEnv( const std::string &name, bool defaultValue ) { const char *envPtr = getenv( name.c_str() ); return( envPtr ? atoi( envPtr ) : defaultValue ); } std::string Logger::strEnv( const std::string &name, const std::string defaultValue ) { const char *envPtr = getenv( name.c_str() ); return( envPtr ? envPtr : defaultValue ); } char *Logger::getTargettedEnv( const std::string &name ) { char *envPtr = NULL; std::string envName; envName = name+"_"+mId; envPtr = getenv( envName.c_str() ); if ( !envPtr && mId != mIdRoot ) { envName = name+"_"+mIdRoot; envPtr = getenv( envName.c_str() ); } if ( !envPtr ) envPtr = getenv( name.c_str() ); return( envPtr ); } const std::string &Logger::id( const std::string &id ) { std::string tempId = id; size_t pos; // Remove whitespace while ( (pos = tempId.find_first_of( " \t" )) != std::string::npos ) { tempId.replace( pos, 1, "" ); } // Replace non-alphanum with underscore while ( (pos = tempId.find_first_not_of( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" )) != std::string::npos ) { tempId.replace( pos, 1, "_" ); } if ( mId != tempId ) { mId = tempId; pos = mId.find( '_' ); if ( pos != std::string::npos ) { mIdRoot = mId.substr( 0, pos ); if ( ++pos < mId.size() ) mIdArgs = mId.substr( pos ); } } return( mId ); } Logger::Level Logger::level( Logger::Level level ) { if ( level > NOOPT ) { level = limit(level); if ( mLevel != level ) mLevel = level; mEffectiveLevel = NOLOG; if ( mTermLevel > mEffectiveLevel ) mEffectiveLevel = mTermLevel; if ( mDatabaseLevel > mEffectiveLevel ) mEffectiveLevel = mDatabaseLevel; if ( mFileLevel > mEffectiveLevel ) mEffectiveLevel = mFileLevel; if ( mSyslogLevel > mEffectiveLevel ) mEffectiveLevel = mSyslogLevel; if ( mEffectiveLevel > mLevel) mEffectiveLevel = mLevel; } return( mLevel ); } Logger::Level Logger::termLevel( Logger::Level termLevel ) { if ( termLevel > NOOPT ) { if ( !mHasTerm ) termLevel = NOLOG; termLevel = limit(termLevel); if ( mTermLevel != termLevel ) mTermLevel = termLevel; } return( mTermLevel ); } Logger::Level Logger::databaseLevel( Logger::Level databaseLevel ) { if ( databaseLevel > NOOPT ) { databaseLevel = limit(databaseLevel); if ( mDatabaseLevel != databaseLevel ) { if ( databaseLevel > NOLOG && mDatabaseLevel <= NOLOG ) { if ( !mDbConnected ) { if ( !mysql_init( &mDbConnection ) ) { Fatal( "Can't initialise database connection: %s", mysql_error( &mDbConnection ) ); exit( mysql_errno( &mDbConnection ) ); } my_bool reconnect = 1; if ( mysql_options( &mDbConnection, MYSQL_OPT_RECONNECT, &reconnect ) ) Fatal( "Can't set database auto reconnect option: %s", mysql_error( &mDbConnection ) ); std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" ); if ( colonIndex != std::string::npos ) { std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex ); std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 ); if ( !mysql_real_connect( &mDbConnection, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) ) { Fatal( "Can't connect to database: %s", mysql_error( &mDbConnection ) ); exit( mysql_errno( &mDbConnection ) ); } } else { if ( !mysql_real_connect( &mDbConnection, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) ) { Fatal( "Can't connect to database: %s", mysql_error( &mDbConnection ) ); exit( mysql_errno( &mDbConnection ) ); } } unsigned long mysqlVersion = mysql_get_server_version( &mDbConnection ); if ( mysqlVersion < 50019 ) if ( mysql_options( &mDbConnection, MYSQL_OPT_RECONNECT, &reconnect ) ) Fatal( "Can't set database auto reconnect option: %s", mysql_error( &mDbConnection ) ); if ( mysql_select_db( &mDbConnection, staticConfig.DB_NAME.c_str() ) ) { Fatal( "Can't select database: %s", mysql_error( &mDbConnection ) ); exit( mysql_errno( &mDbConnection ) ); } mDbConnected = true; } } mDatabaseLevel = databaseLevel; } } return( mDatabaseLevel ); } Logger::Level Logger::fileLevel( Logger::Level fileLevel ) { if ( fileLevel > NOOPT ) { fileLevel = limit(fileLevel); if ( mFileLevel != fileLevel ) { if ( mFileLevel > NOLOG ) closeFile(); mFileLevel = fileLevel; if ( mFileLevel > NOLOG ) openFile(); } } return( mFileLevel ); } Logger::Level Logger::syslogLevel( Logger::Level syslogLevel ) { if ( syslogLevel > NOOPT ) { syslogLevel = limit(syslogLevel); if ( mSyslogLevel != syslogLevel ) { if ( mSyslogLevel > NOLOG ) closeSyslog(); mSyslogLevel = syslogLevel; if ( mSyslogLevel > NOLOG ) openSyslog(); } } return( mSyslogLevel ); } void Logger::logFile( const std::string &logFile ) { bool addLogPid = false; std::string tempLogFile = logFile; if ( tempLogFile[tempLogFile.length()-1] == '+' ) { tempLogFile.resize(tempLogFile.length()-1); addLogPid = true; } if ( addLogPid ) mLogFile = stringtf( "%s.%05d", tempLogFile.c_str(), getpid() ); else mLogFile = tempLogFile; } void Logger::openFile() { if ( mLogFile.size() && (mLogFileFP = fopen( mLogFile.c_str() ,"w" )) == (FILE *)NULL ) { mFileLevel = NOLOG; Fatal( "fopen() for %s, error = %s", mLogFile.c_str(), strerror(errno) ); } } void Logger::closeFile() { if ( mLogFileFP ) { fflush( mLogFileFP ); if ( fclose( mLogFileFP ) < 0 ) { Fatal( "fclose(), error = %s",strerror(errno) ); } mLogFileFP = (FILE *)NULL; } } void Logger::openSyslog() { (void) openlog( mId.c_str(), LOG_PID|LOG_NDELAY, LOG_LOCAL1 ); } void Logger::closeSyslog() { (void) closelog(); } void Logger::logPrint( bool hex, const char * const file, const int line, const int level, const char *fstring, ... ) { if ( level <= mEffectiveLevel ) { char classString[4]; char timeString[64]; char logString[8192]; va_list argPtr; struct timeval timeVal; if ( level < PANIC || level > DEBUG9 ) Panic( "Invalid logger level %d", level ); strncpy( classString, smCodes[level].c_str(), sizeof(classString) ); gettimeofday( &timeVal, NULL ); #if 0 if ( logRuntime ) { static struct timeval logStart; subtractTime( &timeVal, &logStart ); snprintf( timeString, sizeof(timeString), "%ld.%03ld", timeVal.tv_sec, timeVal.tv_usec/1000 ); } else { #endif char *timePtr = timeString; timePtr += strftime( timePtr, sizeof(timeString), "%x %H:%M:%S", localtime(&timeVal.tv_sec) ); snprintf( timePtr, sizeof(timeString)-(timePtr-timeString), ".%06ld", timeVal.tv_usec ); #if 0 } #endif pid_t tid; #ifdef HAVE_SYSCALL if ( (tid = syscall(SYS_gettid)) < 0 ) // Thread/Process id #endif // HAVE_SYSCALL tid = getpid(); // Process id char *logPtr = logString; logPtr += snprintf( logPtr, sizeof(logString), "%s %s[%d].%s-%s/%d [", timeString, mId.c_str(), tid, classString, file, line ); char *syslogStart = logPtr; va_start( argPtr, fstring ); if ( hex ) { unsigned char *data = va_arg( argPtr, unsigned char * ); int len = va_arg( argPtr, int ); int i; logPtr += snprintf( logPtr, sizeof(logString)-(logPtr-logString), "%d:", len ); for ( i = 0; i < len; i++ ) { logPtr += snprintf( logPtr, sizeof(logString)-(logPtr-logString), " %02x", data[i] ); } } else { logPtr += vsnprintf( logPtr, sizeof(logString)-(logPtr-logString), fstring, argPtr ); } va_end(argPtr); char *syslogEnd = logPtr; strncpy( logPtr, "]\n", sizeof(logString)-(logPtr-logString) ); if ( level <= mTermLevel ) { printf( "%s", logString ); fflush( stdout ); } if ( level <= mFileLevel ) { fprintf( mLogFileFP, "%s", logString ); if ( mFlush ) fflush( mLogFileFP ); } *syslogEnd = '\0'; if ( level <= mDatabaseLevel ) { char sql[ZM_SQL_MED_BUFSIZ]; char escapedString[(strlen(syslogStart)*2)+1]; mysql_real_escape_string( &mDbConnection, escapedString, syslogStart, strlen(syslogStart) ); snprintf( sql, sizeof(sql), "insert into Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) values ( %ld.%06ld, '%s', %d, %d, '%s', '%s', '%s', %d )", timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), tid, level, classString, escapedString, file, line ); if ( mysql_query( &mDbConnection, sql ) ) { databaseLevel( NOLOG ); Fatal( "Can't insert log entry: %s", mysql_error( &mDbConnection ) ); exit( mysql_errno( &mDbConnection ) ); } } if ( level <= mSyslogLevel ) { int priority = smSyslogPriorities[level]; //priority |= LOG_DAEMON; syslog( priority, "%s [%s]", classString, syslogStart ); } if ( level <= FATAL ) { if ( level <= PANIC ) abort(); exit( -1 ); } } } void logInit( const char *name, const Logger::Options &options ) { if ( !Logger::smInstance ) Logger::smInstance = new Logger(); Logger::Options tempOptions = options; tempOptions.mLogPath = config.path_logs; Logger::smInstance->initialise( name, tempOptions ); } void logTerm() { Logger::fetch()->terminate(); } ZoneMinder-1.26.5/src/zm_logger.h000066400000000000000000000144161225361755400166240ustar00rootroot00000000000000/* * ZoneMinder Logger Interface, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef ZM_LOGGER_H #define ZM_LOGGER_H #include "zm_config.h" #include #include #include #include #ifdef HAVE_SYS_SYSCALL_H #include #endif // HAVE_SYS_SYSCALL_H #include class Logger { public: enum { NOOPT=-6, NOLOG, PANIC, FATAL, ERROR, WARNING, INFO, DEBUG1, DEBUG2, DEBUG3, DEBUG4, DEBUG5, DEBUG6, DEBUG7, DEBUG8, DEBUG9 }; typedef int Level; typedef std::map StringMap; typedef std::map IntMap; class Options { public: int mTermLevel; int mDatabaseLevel; int mFileLevel; int mSyslogLevel; std::string mLogPath; std::string mLogFile; public: Options( Level termLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) : mTermLevel( termLevel ), mDatabaseLevel( databaseLevel ), mFileLevel( fileLevel ), mSyslogLevel( syslogLevel ), mLogPath( logPath ), mLogFile( logFile ) { } }; private: static bool smInitialised; static Logger *smInstance; static StringMap smCodes; static IntMap smSyslogPriorities; private: bool mInitialised; std::string mId; std::string mIdRoot; std::string mIdArgs; Level mLevel; // Level that is currently in operation Level mTermLevel; // Maximum level output via terminal Level mDatabaseLevel; // Maximum level output via database Level mFileLevel; // Maximum level output via file Level mSyslogLevel; // Maximum level output via syslog Level mEffectiveLevel; // Level optimised to take account of maxima bool mDbConnected; MYSQL mDbConnection; std::string mLogPath; std::string mLogFile; FILE *mLogFileFP; bool mHasTerm; bool mFlush; private: static void usrHandler( int sig ); public: friend void logInit( const char *name, const Options &options ); static Logger *fetch() { if ( !smInstance ) { smInstance = new Logger(); Options options; smInstance->initialise( "undef", options ); } return( smInstance ); } private: Logger(); ~Logger(); public: void initialise( const std::string &id, const Options &options ); void terminate(); private: int limit( int level ) { if ( level > DEBUG9 ) return( DEBUG9 ); if ( level < NOLOG ) return( NOLOG ); return( level ); } bool boolEnv( const std::string &name, bool defaultValue=false ); int intEnv( const std::string &name, bool defaultValue=0 ); std::string strEnv( const std::string &name, const std::string defaultValue="" ); char *getTargettedEnv( const std::string &name ); void loadEnv(); public: const std::string &id() const { return( mId ); } const std::string &id( const std::string &id ); Level level() const { return( mLevel ); } Level level( Level=NOOPT ); bool debugOn() { return( mEffectiveLevel >= DEBUG1 ); } Level termLevel( Level=NOOPT ); Level databaseLevel( Level=NOOPT ); Level fileLevel( Level=NOOPT ); Level syslogLevel( Level=NOOPT ); private: void logFile( const std::string &logFile ); void openFile(); void closeFile(); void openSyslog(); void closeSyslog(); public: void logPrint( bool hex, const char * const file, const int line, const int level, const char *fstring, ... ); }; void logInit( const char *name, const Logger::Options &options=Logger::Options() ); void logTerm(); inline const std::string &logId() { return( Logger::fetch()->id() ); } inline Logger::Level logLevel() { return( Logger::fetch()->level() ); } inline void logCapLevel( Logger::Level level ) { Logger::fetch()->level( level ); } inline Logger::Level logDebugging() { return( Logger::fetch()->debugOn() ); } #define logPrintf(logLevel,params...) {\ if ( logLevel <= Logger::fetch()->level() )\ Logger::fetch()->logPrint( false, __FILE__, __LINE__, logLevel, ##params );\ } #define logHexdump(logLevel,data,len) {\ if ( logLevel <= Logger::fetch()->level() )\ Logger::fetch()->logPrint( true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len );\ } /* Debug compiled out */ #ifndef DBG_OFF #define Debug(level,params...) logPrintf(level,##params) #define Hexdump(level,data,len) logHexdump(level,data,len) #else #define Debug(level,params...) #define Hexdump(level,data,len) #endif /* Standard debug calls */ #define Info(params...) logPrintf(Logger::INFO,##params) #define Warning(params...) logPrintf(Logger::WARNING,##params) #define Error(params...) logPrintf(Logger::ERROR,##params) #define Fatal(params...) logPrintf(Logger::FATAL,##params) #define Panic(params...) logPrintf(Logger::PANIC,##params) #define Mark() Info("Mark/%s/%d",__FILE__,__LINE__) #define Log() Info("Log") #ifdef __GNUC__ #define Enter(level) logPrintf(level,("Entering %s",__PRETTY_FUNCTION__)) #define Exit(level) logPrintf(level,("Exiting %s",__PRETTY_FUNCTION__)) #else #define Enter(level) #define Exit(level) #endif #endif // ZM_LOGGER_H ZoneMinder-1.26.5/src/zm_mem_utils.h000066400000000000000000000074661225361755400173520ustar00rootroot00000000000000// // ZoneMinder Memory Utilities, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_MEM_UTILS_H #define ZM_MEM_UTILS_H #include #include "zm.h" inline void* zm_mallocaligned(unsigned int reqalignment, size_t reqsize) { uint8_t* retptr; #if HAVE_POSIX_MEMALIGN if(posix_memalign((void**)&retptr,reqalignment,reqsize) != 0) return NULL; return retptr; #else uint8_t* alloc; retptr = (uint8_t*)malloc(reqsize+reqalignment+sizeof(void*)); if(retptr == NULL) return NULL; alloc = retptr + sizeof(void*); if(((long)alloc % reqalignment) != 0) alloc = alloc + (reqalignment - ((long)alloc % reqalignment)); /* Store a pointer before to the start of the block, just before returned aligned memory */ *(void**)(alloc - sizeof(void*)) = retptr; return alloc; #endif } inline void zm_freealigned(void* ptr) { #if HAVE_POSIX_MEMALIGN free(ptr); #else /* Start of block is stored before the block if it was allocated by zm_mallocaligned */ free(*(void**)((uint8_t*)ptr - sizeof(void*))); #endif } inline char *mempbrk( register const char *s, const char *accept, size_t limit ) { if ( limit <= 0 || !s || !accept || !*accept ) return( 0 ); register unsigned int i,j; size_t acc_len = strlen( accept ); for ( i = 0; i < limit; s++, i++ ) { for ( j = 0; j < acc_len; j++ ) { if ( *s == accept[j] ) { return( (char *)s ); } } } return( 0 ); } inline char *memstr( register const char *s, const char *n, size_t limit ) { if ( limit <= 0 || !s || !n ) return( 0 ); if ( !*n ) return( (char *)s ); register unsigned int i,j,k; size_t n_len = strlen( n ); for ( i = 0; i < limit; i++, s++ ) { if ( *s != *n ) continue; j = 1; k = 1; while ( true ) { if ( k >= n_len ) return( (char *)s ); if ( s[j++] != n[k++] ) break; } } return( 0 ); } inline size_t memspn( register const char *s, const char *accept, size_t limit ) { if ( limit <= 0 || !s || !accept || !*accept ) return( 0 ); register unsigned int i,j; size_t acc_len = strlen( accept ); for ( i = 0; i < limit; s++, i++ ) { register bool found = false; for ( j = 0; j < acc_len; j++ ) { if ( *s == accept[j] ) { found = true; break; } } if ( !found ) { return( i ); } } return( limit ); } inline size_t memcspn( register const char *s, const char *reject, size_t limit ) { if ( limit <= 0 || !s || !reject ) return( 0 ); if ( !*reject ) return( limit ); register unsigned int i,j; size_t rej_len = strlen( reject ); for ( i = 0; i < limit; s++, i++ ) { for ( j = 0; j < rej_len; j++ ) { if ( *s == reject[j] ) { return( i ); } } } return( limit ); } #endif // ZM_MEM_UTILS_H ZoneMinder-1.26.5/src/zm_monitor.cpp000066400000000000000000004345341225361755400173760ustar00rootroot00000000000000// // ZoneMinder Monitor Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include #include #include #include #include "zm.h" #include "zm_db.h" #include "zm_time.h" #include "zm_mpeg.h" #include "zm_signal.h" #include "zm_monitor.h" #if ZM_HAS_V4L #include "zm_local_camera.h" #endif // ZM_HAS_V4L #include "zm_remote_camera.h" #include "zm_remote_camera_http.h" #if HAVE_LIBAVFORMAT #include "zm_remote_camera_rtsp.h" #endif // HAVE_LIBAVFORMAT #include "zm_file_camera.h" #if HAVE_LIBAVFORMAT #include "zm_ffmpeg_camera.h" #endif // HAVE_LIBAVFORMAT #if ZM_MEM_MAPPED #include #include #else // ZM_MEM_MAPPED #include #include #endif // ZM_MEM_MAPPED //============================================================================= std::string trimSpaces(std::string str) { // Trim Both leading and trailing spaces size_t startpos = str.find_first_not_of(" \t"); // Find the first character position after excluding leading blank spaces size_t endpos = str.find_last_not_of(" \t"); // Find the first character position from reverse af // if all spaces or empty return an empty string if(( std::string::npos == startpos ) || ( std::string::npos == endpos)) { return std::string(""); } else return str.substr( startpos, endpos-startpos+1 ); } std::vector split(const std::string &s, char delim) { std::vector elems; std::stringstream ss(s); std::string item; while(std::getline(ss, item, delim)) { elems.push_back(trimSpaces(item)); } return elems; } //============================================================================= Monitor::MonitorLink::MonitorLink( int p_id, const char *p_name ) : id( p_id ) { strncpy( name, p_name, sizeof(name) ); #if ZM_MEM_MAPPED map_fd = -1; snprintf( mem_file, sizeof(mem_file), "%s/zm.mmap.%d", config.path_map, id ); #else // ZM_MEM_MAPPED shm_id = 0; #endif // ZM_MEM_MAPPED mem_size = 0; mem_ptr = 0; last_event = 0; last_state = IDLE; last_connect_time = 0; connected = false; } Monitor::MonitorLink::~MonitorLink() { disconnect(); } bool Monitor::MonitorLink::connect() { if ( !last_connect_time || (time( 0 ) - last_connect_time) > 60 ) { last_connect_time = time( 0 ); mem_size = sizeof(SharedData) + sizeof(TriggerData); Debug( 1, "link.mem.size=%d", mem_size ); #if ZM_MEM_MAPPED map_fd = open( mem_file, O_RDWR, (mode_t)0600 ); if ( map_fd < 0 ) { Debug( 3, "Can't open linked memory map file %s: %s", mem_file, strerror(errno) ); disconnect(); return( false ); } struct stat map_stat; if ( fstat( map_fd, &map_stat ) < 0 ) { Error( "Can't stat linked memory map file %s: %s", mem_file, strerror(errno) ); disconnect(); return( false ); } if ( map_stat.st_size == 0 ) { Error( "Linked memory map file %s is empty: %s", mem_file, strerror(errno) ); disconnect(); return( false ); } else if ( map_stat.st_size < mem_size ) { Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size ); disconnect(); return( false ); } mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 ); if ( mem_ptr == MAP_FAILED ) { Error( "Can't map file %s (%d bytes) to memory: %s", mem_file, mem_size, strerror(errno) ); disconnect(); return( false ); } #else // ZM_MEM_MAPPED shm_id = shmget( (config.shm_key&0xffff0000)|id, mem_size, 0700 ); if ( shm_id < 0 ) { Debug( 3, "Can't shmget link memory: %s", strerror(errno) ); connected = false; return( false ); } mem_ptr = (unsigned char *)shmat( shm_id, 0, 0 ); if ( mem_ptr < 0 ) { Debug( 3, "Can't shmat link memory: %s", strerror(errno) ); connected = false; return( false ); } #endif // ZM_MEM_MAPPED shared_data = (SharedData *)mem_ptr; trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData)); if ( !shared_data->valid ) { Debug( 3, "Linked memory not initialised by capture daemon" ); disconnect(); return( false ); } last_state = shared_data->state; last_event = shared_data->last_event; connected = true; return( true ); } return( false ); } bool Monitor::MonitorLink::disconnect() { if ( connected ) { connected = false; #if ZM_MEM_MAPPED if ( mem_ptr > 0 ) { msync( mem_ptr, mem_size, MS_ASYNC ); munmap( mem_ptr, mem_size ); } if ( map_fd >= 0 ) close( map_fd ); map_fd = -1; #else // ZM_MEM_MAPPED struct shmid_ds shm_data; if ( shmctl( shm_id, IPC_STAT, &shm_data ) < 0 ) { Debug( 3, "Can't shmctl: %s", strerror(errno) ); return( false ); } shm_id = 0; if ( shm_data.shm_nattch <= 1 ) { if ( shmctl( shm_id, IPC_RMID, 0 ) < 0 ) { Debug( 3, "Can't shmctl: %s", strerror(errno) ); return( false ); } } if ( shmdt( mem_ptr ) < 0 ) { Debug( 3, "Can't shmdt: %s", strerror(errno) ); return( false ); } #endif // ZM_MEM_MAPPED mem_size = 0; mem_ptr = 0; } return( true ); } bool Monitor::MonitorLink::isAlarmed() { if ( !connected ) { return( false ); } return( shared_data->state == ALARM ); } bool Monitor::MonitorLink::inAlarm() { if ( !connected ) { return( false ); } return( shared_data->state == ALARM || shared_data->state == ALERT ); } bool Monitor::MonitorLink::hasAlarmed() { if ( shared_data->state == ALARM ) { return( true ); } else if( shared_data->last_event != (unsigned int)last_event ) { last_event = shared_data->last_event; return( true ); } return( false ); } Monitor::Monitor( int p_id, const char *p_name, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, Purpose p_purpose, int p_n_zones, Zone *p_zones[] ) : id( p_id ), function( (Function)p_function ), enabled( p_enabled ), width( (p_orientation==ROTATE_90||p_orientation==ROTATE_270)?p_camera->Height():p_camera->Width() ), height( (p_orientation==ROTATE_90||p_orientation==ROTATE_270)?p_camera->Width():p_camera->Height() ), orientation( (Orientation)p_orientation ), deinterlacing( p_deinterlacing ), label_coord( p_label_coord ), image_buffer_count( p_image_buffer_count ), warmup_count( p_warmup_count ), pre_event_count( p_pre_event_count ), post_event_count( p_post_event_count ), stream_replay_buffer( p_stream_replay_buffer ), section_length( p_section_length ), frame_skip( p_frame_skip ), capture_delay( p_capture_delay ), alarm_capture_delay( p_alarm_capture_delay ), alarm_frame_count( p_alarm_frame_count ), fps_report_interval( p_fps_report_interval ), ref_blend_perc( p_ref_blend_perc ), alarm_ref_blend_perc( p_alarm_ref_blend_perc ), track_motion( p_track_motion ), signal_check_colour( p_signal_check_colour ), delta_image( width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE ), ref_image( width, height, p_camera->Colours(), p_camera->SubpixelOrder() ), purpose( p_purpose ), camera( p_camera ), n_zones( p_n_zones ), zones( p_zones ) { strncpy( name, p_name, sizeof(name) ); strncpy( event_prefix, p_event_prefix, sizeof(event_prefix) ); strncpy( label_format, p_label_format, sizeof(label_format) ); // Change \n to actual line feeds char *token_ptr = label_format; const char *token_string = "\n"; while( ( token_ptr = strstr( token_ptr, token_string ) ) ) { if ( *(token_ptr+1) ) { *token_ptr = '\n'; token_ptr++; strcpy( token_ptr, token_ptr+1 ); } else { *token_ptr = '\0'; break; } } fps = 0.0; event_count = 0; image_count = 0; ready_count = warmup_count; first_alarm_count = 0; last_alarm_count = 0; state = IDLE; if ( alarm_frame_count < 1 ) alarm_frame_count = 1; else if ( alarm_frame_count > MAX_PRE_ALARM_FRAMES ) alarm_frame_count = MAX_PRE_ALARM_FRAMES; auto_resume_time = 0; if ( strcmp( config.event_close_mode, "time" ) == 0 ) event_close_mode = CLOSE_TIME; else if ( strcmp( config.event_close_mode, "alarm" ) == 0 ) event_close_mode = CLOSE_ALARM; else event_close_mode = CLOSE_IDLE; Debug( 1, "monitor purpose=%d", purpose ); mem_size = sizeof(SharedData) + sizeof(TriggerData) + (image_buffer_count*sizeof(struct timeval)) + (image_buffer_count*camera->ImageSize()) + 64; /* Padding used to permit aligning the images buffer to 16 byte boundary */ Debug( 1, "mem.size=%d", mem_size ); #if ZM_MEM_MAPPED snprintf( mem_file, sizeof(mem_file), "%s/zm.mmap.%d", config.path_map, id ); map_fd = open( mem_file, O_RDWR|O_CREAT, (mode_t)0600 ); if ( map_fd < 0 ) Fatal( "Can't open memory map file %s, probably not enough space free: %s", mem_file, strerror(errno) ); struct stat map_stat; if ( fstat( map_fd, &map_stat ) < 0 ) Fatal( "Can't stat memory map file %s: %s", mem_file, strerror(errno) ); if ( map_stat.st_size != mem_size && purpose == CAPTURE ) { // Allocate the size if ( ftruncate( map_fd, mem_size ) < 0 ) Fatal( "Can't extend memory map file %s to %d bytes: %s", mem_file, mem_size, strerror(errno) ); } else if ( map_stat.st_size != mem_size ) { Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size ); } mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, map_fd, 0 ); if ( mem_ptr == MAP_FAILED ) if ( errno == EAGAIN ) { Debug( 1, "Unable to map file %s (%d bytes) to locked memory, trying unlocked", mem_file, mem_size ); mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 ); } if ( mem_ptr == MAP_FAILED ) Fatal( "Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno ); #else // ZM_MEM_MAPPED shm_id = shmget( (config.shm_key&0xffff0000)|id, mem_size, IPC_CREAT|0700 ); if ( shm_id < 0 ) { Error( "Can't shmget, probably not enough shared memory space free: %s", strerror(errno)); exit( -1 ); } mem_ptr = (unsigned char *)shmat( shm_id, 0, 0 ); if ( mem_ptr < 0 ) { Error( "Can't shmat: %s", strerror(errno)); exit( -1 ); } #endif // ZM_MEM_MAPPED shared_data = (SharedData *)mem_ptr; trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData)); struct timeval *shared_timestamps = (struct timeval *)((char *)trigger_data + sizeof(TriggerData)); unsigned char *shared_images = (unsigned char *)((char *)shared_timestamps + (image_buffer_count*sizeof(struct timeval))); if(((unsigned long)shared_images % 16) != 0) { /* Align images buffer to nearest 16 byte boundary */ Debug(3,"Aligning shared memory images to the next 16 byte boundary"); shared_images = (uint8_t*)((unsigned long)shared_images + (16 - ((unsigned long)shared_images % 16))); } if ( purpose == CAPTURE ) { memset( mem_ptr, 0, mem_size ); shared_data->size = sizeof(SharedData); shared_data->active = enabled; shared_data->signal = false; shared_data->state = IDLE; shared_data->last_write_index = image_buffer_count; shared_data->last_read_index = image_buffer_count; shared_data->last_write_time = 0; shared_data->last_event = 0; shared_data->action = (Action)0; shared_data->brightness = -1; shared_data->hue = -1; shared_data->colour = -1; shared_data->contrast = -1; shared_data->alarm_x = -1; shared_data->alarm_y = -1; shared_data->format = camera->SubpixelOrder(); shared_data->imagesize = camera->ImageSize(); trigger_data->size = sizeof(TriggerData); trigger_data->trigger_state = TRIGGER_CANCEL; trigger_data->trigger_score = 0; trigger_data->trigger_cause[0] = 0; trigger_data->trigger_text[0] = 0; trigger_data->trigger_showtext[0] = 0; shared_data->valid = true; } else if ( purpose == ANALYSIS ) { shared_data->state = IDLE; shared_data->last_read_time = 0; shared_data->alarm_x = -1; shared_data->alarm_y = -1; } if ( !shared_data->valid ) { if ( purpose != QUERY ) { Error( "Shared data not initialised by capture daemon" ); exit( -1 ); } else { Warning( "Shared data not initialised by capture daemon, some query functions may not be available or produce invalid results" ); } } image_buffer = new Snapshot[image_buffer_count]; for ( int i = 0; i < image_buffer_count; i++ ) { image_buffer[i].timestamp = &(shared_timestamps[i]); image_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]) ); image_buffer[i].image->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */ } if ( (deinterlacing & 0xff) == 4) { /* Four field motion adaptive deinterlacing in use */ /* Allocate a buffer for the next image */ next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder()); next_buffer.timestamp = new struct timeval; } if ( !n_zones ) { n_zones = 1; zones = new Zone *[1]; Coord coords[4] = { Coord( 0, 0 ), Coord( width-1, 0 ), Coord( width-1, height-1 ), Coord( 0, height-1 ) }; zones[0] = new Zone( this, 0, "All", Zone::ACTIVE, Polygon( sizeof(coords)/sizeof(*coords), coords ), RGB_RED, Zone::BLOBS ); } start_time = last_fps_time = time( 0 ); event = 0; Debug( 1, "Monitor %s has function %d", name, function ); Debug( 1, "Monitor %s LBF = '%s', LBX = %d, LBY = %d", name, label_format, label_coord.X(), label_coord.Y() ); Debug( 1, "Monitor %s IBC = %d, WUC = %d, pEC = %d, PEC = %d, EAF = %d, FRI = %d, RBP = %d, ARBP = %d, FM = %d", name, image_buffer_count, warmup_count, pre_event_count, post_event_count, alarm_frame_count, fps_report_interval, ref_blend_perc, alarm_ref_blend_perc, track_motion ); if ( purpose == ANALYSIS ) { static char path[PATH_MAX]; strncpy( path, config.dir_events, sizeof(path) ); struct stat statbuf; errno = 0; stat( path, &statbuf ); if ( errno == ENOENT || errno == ENOTDIR ) { if ( mkdir( path, 0755 ) ) { Error( "Can't make %s: %s", path, strerror(errno)); } } snprintf( path, sizeof(path), "%s/%d", config.dir_events, id ); errno = 0; stat( path, &statbuf ); if ( errno == ENOENT || errno == ENOTDIR ) { if ( mkdir( path, 0755 ) ) { Error( "Can't make %s: %s", path, strerror(errno)); } char temp_path[PATH_MAX]; snprintf( temp_path, sizeof(temp_path), "%d", id ); if ( chdir( config.dir_events ) < 0 ) Fatal( "Can't change directory to '%s': %s", config.dir_events, strerror(errno) ); if ( symlink( temp_path, name ) < 0 ) Fatal( "Can't symlink '%s' to '%s': %s", temp_path, name, strerror(errno) ); if ( chdir( ".." ) < 0 ) Fatal( "Can't change to parent directory: %s", strerror(errno) ); } while( shared_data->last_write_index == (unsigned int)image_buffer_count && shared_data->last_write_time == 0) { Warning( "Waiting for capture daemon" ); sleep( 1 ); } ref_image.Assign( width, height, camera->Colours(), camera->SubpixelOrder(), image_buffer[shared_data->last_write_index].image->Buffer(), camera->ImageSize()); n_linked_monitors = 0; linked_monitors = 0; ReloadLinkedMonitors( p_linked_monitors ); } } Monitor::~Monitor() { if ( event ) Info( "%s: %03d - Closing event %d, shutting down", name, image_count, event->Id() ); closeEvent(); if ( (deinterlacing & 0xff) == 4) { delete next_buffer.image; delete next_buffer.timestamp; } for ( int i = 0; i < image_buffer_count; i++ ) { delete image_buffer[i].image; } delete[] image_buffer; for ( int i = 0; i < n_zones; i++ ) { delete zones[i]; } delete[] zones; delete camera; if ( purpose == ANALYSIS ) { shared_data->state = state = IDLE; shared_data->last_read_index = image_buffer_count; shared_data->last_read_time = 0; } else if ( purpose == CAPTURE ) { shared_data->valid = false; memset( mem_ptr, 0, mem_size ); } #if ZM_MEM_MAPPED if ( msync( mem_ptr, mem_size, MS_SYNC ) < 0 ) Error( "Can't msync: %s", strerror(errno) ); if ( munmap( mem_ptr, mem_size ) < 0 ) Fatal( "Can't munmap: %s", strerror(errno) ); close( map_fd ); #else // ZM_MEM_MAPPED struct shmid_ds shm_data; if ( shmctl( shm_id, IPC_STAT, &shm_data ) < 0 ) { Error( "Can't shmctl: %s", strerror(errno) ); exit( -1 ); } if ( shm_data.shm_nattch <= 1 ) { if ( shmctl( shm_id, IPC_RMID, 0 ) < 0 ) { Error( "Can't shmctl: %s", strerror(errno) ); exit( -1 ); } } #endif // ZM_MEM_MAPPED } void Monitor::AddZones( int p_n_zones, Zone *p_zones[] ) { for ( int i = 0; i < n_zones; i++ ) delete zones[i]; delete[] zones; n_zones = p_n_zones; zones = p_zones; } Monitor::State Monitor::GetState() const { return( (State)shared_data->state ); } int Monitor::GetImage( int index, int scale ) const { if ( index < 0 || index > image_buffer_count ) { index = shared_data->last_write_index; } if ( index != image_buffer_count ) { Snapshot *snap = &image_buffer[index]; Image snap_image( *(snap->image) ); if ( scale != ZM_SCALE_BASE ) { snap_image.Scale( scale ); } static char filename[PATH_MAX]; snprintf( filename, sizeof(filename), "Monitor%d.jpg", id ); if ( !config.timestamp_on_capture ) { TimestampImage( &snap_image, snap->timestamp ); } snap_image.WriteJpeg( filename ); } else { Error( "Unable to generate image, no images in buffer" ); } return( 0 ); } struct timeval Monitor::GetTimestamp( int index ) const { if ( index < 0 || index > image_buffer_count ) { index = shared_data->last_write_index; } if ( index != image_buffer_count ) { Snapshot *snap = &image_buffer[index]; return( *(snap->timestamp) ); } else { static struct timeval null_tv = { 0, 0 }; return( null_tv ); } } unsigned int Monitor::GetLastReadIndex() const { return( shared_data->last_read_index!=(unsigned int)image_buffer_count?shared_data->last_read_index:-1 ); } unsigned int Monitor::GetLastWriteIndex() const { return( shared_data->last_write_index!=(unsigned int)image_buffer_count?shared_data->last_write_index:-1 ); } unsigned int Monitor::GetLastEvent() const { return( shared_data->last_event ); } double Monitor::GetFPS() const { int index1 = shared_data->last_write_index; if ( index1 == image_buffer_count ) { return( 0.0 ); } Snapshot *snap1 = &image_buffer[index1]; if ( !snap1->timestamp || !snap1->timestamp->tv_sec ) { return( 0.0 ); } struct timeval time1 = *snap1->timestamp; int image_count = image_buffer_count; int index2 = (index1+1)%image_buffer_count; if ( index2 == image_buffer_count ) { return( 0.0 ); } Snapshot *snap2 = &image_buffer[index2]; while ( !snap2->timestamp || !snap2->timestamp->tv_sec ) { if ( index1 == index2 ) { return( 0.0 ); } index2 = (index2+1)%image_buffer_count; snap2 = &image_buffer[index2]; image_count--; } struct timeval time2 = *snap2->timestamp; double time_diff = tvDiffSec( time2, time1 ); double curr_fps = image_count/time_diff; if ( curr_fps < 0.0 ) { //Error( "Negative FPS %f, time_diff = %lf (%d:%ld.%ld - %d:%ld.%ld), ibc: %d", curr_fps, time_diff, index2, time2.tv_sec, time2.tv_usec, index1, time1.tv_sec, time1.tv_usec, image_buffer_count ); return( 0.0 ); } return( curr_fps ); } void Monitor::ForceAlarmOn( int force_score, const char *force_cause, const char *force_text ) { trigger_data->trigger_state = TRIGGER_ON; trigger_data->trigger_score = force_score; strncpy( trigger_data->trigger_cause, force_cause, sizeof(trigger_data->trigger_cause) ); strncpy( trigger_data->trigger_text, force_text, sizeof(trigger_data->trigger_text) ); } void Monitor::ForceAlarmOff() { trigger_data->trigger_state = TRIGGER_OFF; } void Monitor::CancelForced() { trigger_data->trigger_state = TRIGGER_CANCEL; } void Monitor::actionReload() { shared_data->action |= RELOAD; } void Monitor::actionEnable() { shared_data->action |= RELOAD; static char sql[ZM_SQL_SML_BUFSIZ]; snprintf( sql, sizeof(sql), "update Monitors set Enabled = 1 where Id = '%d'", id ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } } void Monitor::actionDisable() { shared_data->action |= RELOAD; static char sql[ZM_SQL_SML_BUFSIZ]; snprintf( sql, sizeof(sql), "update Monitors set Enabled = 0 where Id = '%d'", id ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } } void Monitor::actionSuspend() { shared_data->action |= SUSPEND; } void Monitor::actionResume() { shared_data->action |= RESUME; } int Monitor::actionBrightness( int p_brightness ) { if ( purpose != CAPTURE ) { if ( p_brightness >= 0 ) { shared_data->brightness = p_brightness; shared_data->action |= SET_SETTINGS; int wait_loops = 10; while ( shared_data->action & SET_SETTINGS ) { if ( wait_loops-- ) usleep( 100000 ); else { Warning( "Timed out waiting to set brightness" ); return( -1 ); } } } else { shared_data->action |= GET_SETTINGS; int wait_loops = 10; while ( shared_data->action & GET_SETTINGS ) { if ( wait_loops-- ) usleep( 100000 ); else { Warning( "Timed out waiting to get brightness" ); return( -1 ); } } } return( shared_data->brightness ); } return( camera->Brightness( p_brightness ) ); } int Monitor::actionContrast( int p_contrast ) { if ( purpose != CAPTURE ) { if ( p_contrast >= 0 ) { shared_data->contrast = p_contrast; shared_data->action |= SET_SETTINGS; int wait_loops = 10; while ( shared_data->action & SET_SETTINGS ) { if ( wait_loops-- ) usleep( 100000 ); else { Warning( "Timed out waiting to set contrast" ); return( -1 ); } } } else { shared_data->action |= GET_SETTINGS; int wait_loops = 10; while ( shared_data->action & GET_SETTINGS ) { if ( wait_loops-- ) usleep( 100000 ); else { Warning( "Timed out waiting to get contrast" ); return( -1 ); } } } return( shared_data->contrast ); } return( camera->Contrast( p_contrast ) ); } int Monitor::actionHue( int p_hue ) { if ( purpose != CAPTURE ) { if ( p_hue >= 0 ) { shared_data->hue = p_hue; shared_data->action |= SET_SETTINGS; int wait_loops = 10; while ( shared_data->action & SET_SETTINGS ) { if ( wait_loops-- ) usleep( 100000 ); else { Warning( "Timed out waiting to set hue" ); return( -1 ); } } } else { shared_data->action |= GET_SETTINGS; int wait_loops = 10; while ( shared_data->action & GET_SETTINGS ) { if ( wait_loops-- ) usleep( 100000 ); else { Warning( "Timed out waiting to get hue" ); return( -1 ); } } } return( shared_data->hue ); } return( camera->Hue( p_hue ) ); } int Monitor::actionColour( int p_colour ) { if ( purpose != CAPTURE ) { if ( p_colour >= 0 ) { shared_data->colour = p_colour; shared_data->action |= SET_SETTINGS; int wait_loops = 10; while ( shared_data->action & SET_SETTINGS ) { if ( wait_loops-- ) usleep( 100000 ); else { Warning( "Timed out waiting to set colour" ); return( -1 ); } } } else { shared_data->action |= GET_SETTINGS; int wait_loops = 10; while ( shared_data->action & GET_SETTINGS ) { if ( wait_loops-- ) usleep( 100000 ); else { Warning( "Timed out waiting to get colour" ); return( -1 ); } } } return( shared_data->colour ); } return( camera->Colour( p_colour ) ); } void Monitor::DumpZoneImage( const char *zone_string ) { int exclude_id = 0; int extra_colour = 0; Polygon extra_zone; if ( zone_string ) { if ( !Zone::ParseZoneString( zone_string, exclude_id, extra_colour, extra_zone ) ) { Error( "Failed to parse zone string, ignoring" ); } } int index = shared_data->last_write_index; Snapshot *snap = &image_buffer[index]; Image *snap_image = snap->image; Image zone_image( *snap_image ); if(zone_image.Colours() == ZM_COLOUR_GRAY8) { zone_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); } for( int i = 0; i < n_zones; i++ ) { if ( exclude_id && (!extra_colour || extra_zone.getNumCoords()) && zones[i]->Id() == exclude_id ) continue; Rgb colour; if ( exclude_id && !extra_zone.getNumCoords() && zones[i]->Id() == exclude_id ) { colour = extra_colour; } else { if ( zones[i]->IsActive() ) { colour = RGB_RED; } else if ( zones[i]->IsInclusive() ) { colour = RGB_ORANGE; } else if ( zones[i]->IsExclusive() ) { colour = RGB_PURPLE; } else if ( zones[i]->IsPreclusive() ) { colour = RGB_BLUE; } else { colour = RGB_WHITE; } } zone_image.Fill( colour, 2, zones[i]->GetPolygon() ); zone_image.Outline( colour, zones[i]->GetPolygon() ); } if ( extra_zone.getNumCoords() ) { zone_image.Fill( extra_colour, 2, extra_zone ); zone_image.Outline( extra_colour, extra_zone ); } static char filename[PATH_MAX]; snprintf( filename, sizeof(filename), "Zones%d.jpg", id ); zone_image.WriteJpeg( filename ); } void Monitor::DumpImage( Image *dump_image ) const { if ( image_count && !(image_count%10) ) { static char filename[PATH_MAX]; static char new_filename[PATH_MAX]; snprintf( filename, sizeof(filename), "Monitor%d.jpg", id ); snprintf( new_filename, sizeof(new_filename), "Monitor%d-new.jpg", id ); dump_image->WriteJpeg( new_filename ); rename( new_filename, filename ); } } bool Monitor::CheckSignal( const Image *image ) { static bool static_undef = true; /* RGB24 colors */ static uint8_t red_val; static uint8_t green_val; static uint8_t blue_val; static uint8_t grayscale_val; /* 8bit grayscale color */ static Rgb colour_val; /* RGB32 color */ static int usedsubpixorder; if ( config.signal_check_points > 0 ) { if ( static_undef ) { static_undef = false; usedsubpixorder = camera->SubpixelOrder(); colour_val = rgb_convert(signal_check_colour, ZM_SUBPIX_ORDER_BGR); /* HTML colour code is actually BGR in memory, we want RGB */ colour_val = rgb_convert(colour_val, usedsubpixorder); red_val = RED_VAL_BGRA(signal_check_colour); green_val = GREEN_VAL_BGRA(signal_check_colour); blue_val = BLUE_VAL_BGRA(signal_check_colour); grayscale_val = signal_check_colour & 0xff; /* Clear all bytes but lowest byte */ } const uint8_t *buffer = image->Buffer(); int pixels = image->Pixels(); int width = image->Width(); int colours = image->Colours(); int index = 0; for ( int i = 0; i < config.signal_check_points; i++ ) { while( true ) { index = (int)(((long long)rand()*(long long)(pixels-1))/RAND_MAX); if ( !config.timestamp_on_capture || !label_format[0] ) break; // Avoid sampling the rows with timestamp in if ( index < (label_coord.Y()*width) || index >= (label_coord.Y()+Image::LINE_HEIGHT)*width ) break; } if(colours == ZM_COLOUR_GRAY8) { if ( *(buffer+index) != grayscale_val ) return true; } else if(colours == ZM_COLOUR_RGB24) { const uint8_t *ptr = buffer+(index*colours); if ( usedsubpixorder == ZM_SUBPIX_ORDER_BGR) { if ( (RED_PTR_BGRA(ptr) != red_val) || (GREEN_PTR_BGRA(ptr) != green_val) || (BLUE_PTR_BGRA(ptr) != blue_val) ) return true; } else { /* Assume RGB */ if ( (RED_PTR_RGBA(ptr) != red_val) || (GREEN_PTR_RGBA(ptr) != green_val) || (BLUE_PTR_RGBA(ptr) != blue_val) ) return true; } } else if(colours == ZM_COLOUR_RGB32) { if ( usedsubpixorder == ZM_SUBPIX_ORDER_ARGB || usedsubpixorder == ZM_SUBPIX_ORDER_ABGR) { if ( ARGB_ABGR_ZEROALPHA(*(((const Rgb*)buffer)+index)) != ARGB_ABGR_ZEROALPHA(colour_val) ) return true; } else { /* Assume RGBA or BGRA */ if ( RGBA_BGRA_ZEROALPHA(*(((const Rgb*)buffer)+index)) != RGBA_BGRA_ZEROALPHA(colour_val) ) return true; } } } return( false ); } return( true ); } bool Monitor::Analyse() { if ( shared_data->last_read_index == shared_data->last_write_index ) { return( false ); } struct timeval now; gettimeofday( &now, NULL ); if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) { fps = double(fps_report_interval)/(now.tv_sec-last_fps_time); Info( "%s: %d - Processing at %.2f fps", name, image_count, fps ); last_fps_time = now.tv_sec; } int index; if ( config.opt_adaptive_skip ) { int read_margin = shared_data->last_read_index - shared_data->last_write_index; if ( read_margin < 0 ) read_margin += image_buffer_count; int step = 1; if ( read_margin > 0 ) { step = (9*image_buffer_count)/(5*read_margin); } int pending_frames = shared_data->last_write_index - shared_data->last_read_index; if ( pending_frames < 0 ) pending_frames += image_buffer_count; Debug( 4, "RI:%d, WI: %d, PF = %d, RM = %d, Step = %d", shared_data->last_read_index, shared_data->last_write_index, pending_frames, read_margin, step ); if ( step <= pending_frames ) { index = (shared_data->last_read_index+step)%image_buffer_count; } else { if ( pending_frames ) { Warning( "Approaching buffer overrun, consider slowing capture, simplifying analysis or increasing ring buffer size" ); } index = shared_data->last_write_index%image_buffer_count; } } else { index = shared_data->last_write_index%image_buffer_count; } Snapshot *snap = &image_buffer[index]; struct timeval *timestamp = snap->timestamp; Image *snap_image = snap->image; if ( shared_data->action ) { if ( shared_data->action & RELOAD ) { Info( "Received reload indication at count %d", image_count ); shared_data->action &= ~RELOAD; Reload(); } if ( shared_data->action & SUSPEND ) { if ( Active() ) { Info( "Received suspend indication at count %d", image_count ); shared_data->active = false; //closeEvent(); } if ( config.max_suspend_time ) { auto_resume_time = now.tv_sec + config.max_suspend_time; } shared_data->action &= ~SUSPEND; } if ( shared_data->action & RESUME ) { if ( Enabled() && !Active() ) { Info( "Received resume indication at count %d", image_count ); shared_data->active = true; ref_image = *snap_image; ready_count = image_count+(warmup_count/2); shared_data->alarm_x = shared_data->alarm_y = -1; } shared_data->action &= ~RESUME; } } if ( auto_resume_time && (now.tv_sec >= auto_resume_time) ) { Info( "Auto resuming at count %d", image_count ); shared_data->active = true; ref_image = *snap_image; ready_count = image_count+(warmup_count/2); auto_resume_time = 0; } static bool static_undef = true; static struct timeval **timestamps; static Image **images; static int last_section_mod = 0; static bool last_signal; if ( static_undef ) { static_undef = false; timestamps = new struct timeval *[pre_event_count]; images = new Image *[pre_event_count]; last_signal = shared_data->signal; } if ( Enabled() ) { bool signal = shared_data->signal; bool signal_change = (signal != last_signal); if ( trigger_data->trigger_state != TRIGGER_OFF ) { unsigned int score = 0; if ( Ready() ) { std::string cause; Event::StringSetMap noteSetMap; if ( trigger_data->trigger_state == TRIGGER_ON ) { score += trigger_data->trigger_score; if ( !event ) { if ( cause.length() ) cause += ", "; cause += trigger_data->trigger_cause; } Event::StringSet noteSet; noteSet.insert( trigger_data->trigger_text ); noteSetMap[trigger_data->trigger_cause] = noteSet; } if ( signal_change ) { const char *signalText; if ( !signal ) signalText = "Lost"; else { signalText = "Reacquired"; score += 100; } Warning( "%s: %s", SIGNAL_CAUSE, signalText ); if ( event && !signal ) { Info( "%s: %03d - Closing event %d, signal loss", name, image_count, event->Id() ); closeEvent(); last_section_mod = 0; } if ( !event ) { if ( cause.length() ) cause += ", "; cause += SIGNAL_CAUSE; } Event::StringSet noteSet; noteSet.insert( signalText ); noteSetMap[SIGNAL_CAUSE] = noteSet; shared_data->state = state = IDLE; shared_data->active = signal; ref_image = *snap_image; } else if ( signal && Active() && (function == MODECT || function == MOCORD) ) { Event::StringSet zoneSet; int motion_score = DetectMotion( *snap_image, zoneSet ); //int motion_score = DetectBlack( *snap_image, zoneSet ); if ( motion_score ) { if ( !event ) { score += motion_score; if ( cause.length() ) cause += ", "; cause += MOTION_CAUSE; } else { score += motion_score; } noteSetMap[MOTION_CAUSE] = zoneSet; } shared_data->active = signal; } if ( (!signal_change && signal) && n_linked_monitors > 0 ) { bool first_link = true; Event::StringSet noteSet; for ( int i = 0; i < n_linked_monitors; i++ ) { if ( linked_monitors[i]->isConnected() ) { if ( linked_monitors[i]->hasAlarmed() ) { if ( !event ) { if ( first_link ) { if ( cause.length() ) cause += ", "; cause += LINKED_CAUSE; first_link = false; } } noteSet.insert( linked_monitors[i]->Name() ); score += 50; } } else { linked_monitors[i]->connect(); } } if ( noteSet.size() > 0 ) noteSetMap[LINKED_CAUSE] = noteSet; } if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) { if ( event ) { int section_mod = timestamp->tv_sec%section_length; if ( section_mod < last_section_mod ) { if ( state == IDLE || state == TAPE || event_close_mode == CLOSE_TIME ) { if ( state == TAPE ) { shared_data->state = state = IDLE; Info( "%s: %03d - Closing event %d, section end", name, image_count, event->Id() ) } else Info( "%s: %03d - Closing event %d, section end forced ", name, image_count, event->Id() ); closeEvent(); last_section_mod = 0; } } else { last_section_mod = section_mod; } } if ( !event ) { // Create event event = new Event( this, *timestamp, "Continuous", noteSetMap ); shared_data->last_event = event->Id(); Info( "%s: %03d - Opening new event %d, section start", name, image_count, event->Id() ); /* To prevent cancelling out an existing alert\prealarm\alarm state */ if ( state == IDLE ) { shared_data->state = state = TAPE; } //if ( config.overlap_timed_events ) if ( false ) { int pre_index = ((index+image_buffer_count)-pre_event_count)%image_buffer_count; int pre_event_images = pre_event_count; while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) { pre_index = (pre_index+1)%image_buffer_count; pre_event_images--; } if ( pre_event_images ) { for ( int i = 0; i < pre_event_images; i++ ) { timestamps[i] = image_buffer[pre_index].timestamp; images[i] = image_buffer[pre_index].image; pre_index = (pre_index+1)%image_buffer_count; } event->AddFrames( pre_event_images, images, timestamps ); } } } } if ( score ) { if ( (state == IDLE || state == TAPE || state == PREALARM ) ) { if ( Event::PreAlarmCount() >= (alarm_frame_count-1) ) { Info( "%s: %03d - Gone into alarm state", name, image_count ); shared_data->state = state = ALARM; if ( signal_change || (function != MOCORD && state != ALERT) ) { int pre_index; if ( alarm_frame_count > 1 ) pre_index = ((index+image_buffer_count)-((alarm_frame_count-1)+pre_event_count))%image_buffer_count; else pre_index = ((index+image_buffer_count)-pre_event_count)%image_buffer_count; int pre_event_images = pre_event_count; while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) { pre_index = (pre_index+1)%image_buffer_count; pre_event_images--; } event = new Event( this, *(image_buffer[pre_index].timestamp), cause, noteSetMap ); shared_data->last_event = event->Id(); Info( "%s: %03d - Opening new event %d, alarm start", name, image_count, event->Id() ); if ( pre_event_images ) { for ( int i = 0; i < pre_event_images; i++ ) { timestamps[i] = image_buffer[pre_index].timestamp; images[i] = image_buffer[pre_index].image; pre_index = (pre_index+1)%image_buffer_count; } event->AddFrames( pre_event_images, images, timestamps ); } if ( alarm_frame_count ) { event->SavePreAlarmFrames(); } } } else if ( state != PREALARM ) { Info( "%s: %03d - Gone into prealarm state", name, image_count ); shared_data->state = state = PREALARM; } } else if ( state == ALERT ) { Info( "%s: %03d - Gone back into alarm state", name, image_count ); shared_data->state = state = ALARM; } last_alarm_count = image_count; } else { if ( state == ALARM ) { Info( "%s: %03d - Gone into alert state", name, image_count ); shared_data->state = state = ALERT; } else if ( state == ALERT ) { if ( image_count-last_alarm_count > post_event_count ) { Info( "%s: %03d - Left alarm state (%d) - %d(%d) images", name, image_count, event->Id(), event->Frames(), event->AlarmFrames() ); //if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE ) if ( function != MOCORD || event_close_mode == CLOSE_ALARM ) { shared_data->state = state = IDLE; Info( "%s: %03d - Closing event %d, alarm end%s", name, image_count, event->Id(), (function==MOCORD)?", section truncated":"" ); closeEvent(); } else { shared_data->state = state = TAPE; } } } if ( state == PREALARM ) { if ( function != MOCORD ) { shared_data->state = state = IDLE; } else { shared_data->state = state = TAPE; } } if ( Event::PreAlarmCount() ) Event::EmptyPreAlarmFrames(); } if ( state != IDLE ) { if ( state == PREALARM || state == ALARM ) { if ( config.create_analysis_images ) { bool got_anal_image = false; Image alarm_image( *snap_image ); for( int i = 0; i < n_zones; i++ ) { if ( zones[i]->Alarmed() ) { if ( zones[i]->AlarmImage() ) { alarm_image.Overlay( *(zones[i]->AlarmImage()) ); got_anal_image = true; } if ( config.record_event_stats && state == ALARM ) { zones[i]->RecordStats( event ); } } } if ( got_anal_image ) { if ( state == PREALARM ) Event::AddPreAlarmFrame( snap_image, *timestamp, score, &alarm_image ); else event->AddFrame( snap_image, *timestamp, score, &alarm_image ); } else { if ( state == PREALARM ) Event::AddPreAlarmFrame( snap_image, *timestamp, score ); else event->AddFrame( snap_image, *timestamp, score ); } } else { for( int i = 0; i < n_zones; i++ ) { if ( zones[i]->Alarmed() ) { if ( config.record_event_stats && state == ALARM ) { zones[i]->RecordStats( event ); } } } if ( state == PREALARM ) Event::AddPreAlarmFrame( snap_image, *timestamp, score ); else event->AddFrame( snap_image, *timestamp, score ); } if ( event && noteSetMap.size() > 0 ) event->updateNotes( noteSetMap ); } else if ( state == ALERT ) { event->AddFrame( snap_image, *timestamp ); if ( noteSetMap.size() > 0 ) event->updateNotes( noteSetMap ); } else if ( state == TAPE ) { if ( !(image_count%(frame_skip+1)) ) { if ( config.bulk_frame_interval > 1 ) { event->AddFrame( snap_image, *timestamp, (event->Frames()AddFrame( snap_image, *timestamp ); } } } } } } else { if ( event ) { Info( "%s: %03d - Closing event %d, trigger off", name, image_count, event->Id() ); closeEvent(); } shared_data->state = state = IDLE; last_section_mod = 0; } if ( (!signal_change && signal) && (function == MODECT || function == MOCORD) ) { if ( state == ALARM ) { ref_image.Blend( *snap_image, alarm_ref_blend_perc ); } else { ref_image.Blend( *snap_image, ref_blend_perc ); } } last_signal = signal; } shared_data->last_read_index = index%image_buffer_count; //shared_data->last_read_time = image_buffer[index].timestamp->tv_sec; shared_data->last_read_time = now.tv_sec; image_count++; return( true ); } void Monitor::Reload() { Debug( 1, "Reloading monitor %s", name ); if ( event ) Info( "%s: %03d - Closing event %d, reloading", name, image_count, event->Id() ); closeEvent(); static char sql[ZM_SQL_MED_BUFSIZ]; snprintf( sql, sizeof(sql), "select Function+0, Enabled, LinkedMonitors, EventPrefix, LabelFormat, LabelX, LabelY, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = '%d'", id ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } MYSQL_RES *result = mysql_store_result( &dbconn ); if ( !result ) { Error( "Can't use query result: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } int n_monitors = mysql_num_rows( result ); if ( n_monitors != 1 ) { Error( "Bogus number of monitors, %d, returned. Can't reload", n_monitors ); return; } if ( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) { int index = 0; function = (Function)atoi(dbrow[index++]); enabled = atoi(dbrow[index++]); const char *p_linked_monitors = dbrow[index++]; strncpy( event_prefix, dbrow[index++], sizeof(event_prefix) ); strncpy( label_format, dbrow[index++], sizeof(label_format) ); label_coord = Coord( atoi(dbrow[index]), atoi(dbrow[index+1]) ); index += 2; warmup_count = atoi(dbrow[index++]); pre_event_count = atoi(dbrow[index++]); post_event_count = atoi(dbrow[index++]); alarm_frame_count = atoi(dbrow[index++]); section_length = atoi(dbrow[index++]); frame_skip = atoi(dbrow[index++]); capture_delay = (dbrow[index]&&atof(dbrow[index])>0.0)?int(DT_PREC_3/atof(dbrow[index])):0; index++; alarm_capture_delay = (dbrow[index]&&atof(dbrow[index])>0.0)?int(DT_PREC_3/atof(dbrow[index])):0; index++; fps_report_interval = atoi(dbrow[index++]); ref_blend_perc = atoi(dbrow[index++]); alarm_ref_blend_perc = atoi(dbrow[index++]); track_motion = atoi(dbrow[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->alarm_x = shared_data->alarm_y = -1; if ( enabled ) shared_data->active = true; ready_count = image_count+warmup_count; ReloadLinkedMonitors( p_linked_monitors ); } if ( mysql_errno( &dbconn ) ) { Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } mysql_free_result( result ); ReloadZones(); } void Monitor::ReloadZones() { Debug( 1, "Reloading zones for monitor %s", name ); for( int i = 0; i < n_zones; i++ ) { delete zones[i]; } delete[] zones; zones = 0; n_zones = Zone::Load( this, zones ); //DumpZoneImage(); } void Monitor::ReloadLinkedMonitors( const char *p_linked_monitors ) { Debug( 1, "Reloading linked monitors for monitor %s, '%s'", name, p_linked_monitors ); if ( n_linked_monitors ) { for( int i = 0; i < n_linked_monitors; i++ ) { delete linked_monitors[i]; } delete[] linked_monitors; linked_monitors = 0; } n_linked_monitors = 0; if ( p_linked_monitors ) { int n_link_ids = 0; unsigned int link_ids[256]; char link_id_str[8]; char *dest_ptr = link_id_str; const char *src_ptr = p_linked_monitors; while( 1 ) { dest_ptr = link_id_str; while( *src_ptr >= '0' && *src_ptr <= '9' ) { if ( (dest_ptr-link_id_str) < (unsigned int)(sizeof(link_id_str)-1) ) { *dest_ptr++ = *src_ptr++; } else { break; } } // Add the link monitor if ( dest_ptr != link_id_str ) { *dest_ptr = '\0'; unsigned int link_id = atoi(link_id_str); if ( link_id > 0 && link_id != id) { Debug( 3, "Found linked monitor id %d", link_id ); int j; for ( j = 0; j < n_link_ids; j++ ) { if ( link_ids[j] == link_id ) break; } if ( j == n_link_ids ) // Not already found { link_ids[n_link_ids++] = link_id; } } } if ( !*src_ptr ) break; while( *src_ptr && (*src_ptr < '0' || *src_ptr > '9') ) src_ptr++; if ( !*src_ptr ) break; } if ( n_link_ids > 0 ) { Debug( 1, "Linking to %d monitors", n_link_ids ); linked_monitors = new MonitorLink *[n_link_ids]; int count = 0; for ( int i = 0; i < n_link_ids; i++ ) { Debug( 1, "Checking linked monitor %d", link_ids[i] ); static char sql[ZM_SQL_SML_BUFSIZ]; snprintf( sql, sizeof(sql), "select Id, Name from Monitors where Id = %d and Function != 'None' and Function != 'Monitor' and Enabled = 1", link_ids[i] ); 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_monitors = mysql_num_rows( result ); if ( n_monitors == 1 ) { MYSQL_ROW dbrow = mysql_fetch_row( result ); Debug( 1, "Linking to monitor %d", link_ids[i] ); linked_monitors[count++] = new MonitorLink( link_ids[i], dbrow[1] ); } else { Warning( "Can't link to monitor %d, invalid id, function or not enabled", link_ids[i] ); } mysql_free_result( result ); } n_linked_monitors = count; } } } #if ZM_HAS_V4L int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose ) { static char sql[ZM_SQL_MED_BUFSIZ]; if ( !device[0] ) { strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Function != 'None' and Type = 'Local' order by Device, Channel", sizeof(sql) ); } else { snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Function != 'None' and Type = 'Local' and Device = '%s' order by Channel", device ); } 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_monitors = mysql_num_rows( result ); Debug( 1, "Got %d monitors", n_monitors ); delete[] monitors; monitors = new Monitor *[n_monitors]; for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { int col = 0; int id = atoi(dbrow[col]); col++; const char *name = dbrow[col]; col++; int function = atoi(dbrow[col]); col++; int enabled = atoi(dbrow[col]); col++; const char *linked_monitors = dbrow[col]; col++; const char *device = dbrow[col]; col++; int channel = atoi(dbrow[col]); col++; int format = atoi(dbrow[col]); col++; const char *method = dbrow[col]; col++; int width = atoi(dbrow[col]); col++; int height = atoi(dbrow[col]); col++; int colours = atoi(dbrow[col]); col++; int palette = atoi(dbrow[col]); col++; Orientation orientation = (Orientation)atoi(dbrow[col]); col++; unsigned int deinterlacing = atoi(dbrow[col]); col++; int brightness = atoi(dbrow[col]); col++; int contrast = atoi(dbrow[col]); col++; int hue = atoi(dbrow[col]); col++; int colour = atoi(dbrow[col]); col++; const char *event_prefix = dbrow[col]; col++; const char *label_format = dbrow[col]; col++; int label_x = atoi(dbrow[col]); col++; int label_y = atoi(dbrow[col]); col++; int image_buffer_count = atoi(dbrow[col]); col++; int warmup_count = atoi(dbrow[col]); col++; int pre_event_count = atoi(dbrow[col]); col++; int post_event_count = atoi(dbrow[col]); col++; int stream_replay_buffer = atoi(dbrow[col]); col++; int alarm_frame_count = atoi(dbrow[col]); col++; int section_length = atoi(dbrow[col]); col++; int frame_skip = atoi(dbrow[col]); col++; int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; int fps_report_interval = atoi(dbrow[col]); col++; int ref_blend_perc = atoi(dbrow[col]); col++; int alarm_ref_blend_perc = atoi(dbrow[col]); col++; int track_motion = atoi(dbrow[col]); col++; int signal_check_colour; if ( dbrow[col][0] == '#' ) signal_check_colour = strtol(dbrow[col]+1,0,16); else signal_check_colour = strtol(dbrow[col],0,16); col++; int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); int extras = (deinterlacing>>24)&0xff; Camera *camera = new LocalCamera( id, device, channel, format, method, cam_width, cam_height, colours, palette, brightness, contrast, hue, colour, purpose==CAPTURE, extras ); monitors[i] = new Monitor( id, name, function, enabled, linked_monitors, camera, orientation, deinterlacing, event_prefix, label_format, Coord( label_x, label_y ), image_buffer_count, warmup_count, pre_event_count, post_event_count, stream_replay_buffer, alarm_frame_count, section_length, frame_skip, capture_delay, alarm_capture_delay, fps_report_interval, ref_blend_perc, alarm_ref_blend_perc, track_motion, signal_check_colour, purpose, 0, 0 ); Zone **zones = 0; int n_zones = Zone::Load( monitors[i], zones ); monitors[i]->AddZones( n_zones, zones ); Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); } if ( mysql_errno( &dbconn ) ) { Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } // Yadda yadda mysql_free_result( result ); return( n_monitors ); } #endif // ZM_HAS_V4L int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const char *port, const char *path, Monitor **&monitors, Purpose purpose ) { static char sql[ZM_SQL_MED_BUFSIZ]; if ( !protocol ) { strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Remote'", sizeof(sql) ); } else { snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Remote' and Protocol = '%s' and Host = '%s' and Port = '%s' and Path = '%s'", protocol, host, port, path ); } 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_monitors = mysql_num_rows( result ); Debug( 1, "Got %d monitors", n_monitors ); delete[] monitors; monitors = new Monitor *[n_monitors]; for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { int col = 0; int id = atoi(dbrow[col]); col++; std::string name = dbrow[col]; col++; int function = atoi(dbrow[col]); col++; int enabled = atoi(dbrow[col]); col++; const char *linked_monitors = dbrow[col]; col++; std::string protocol = dbrow[col]; col++; std::string method = dbrow[col]; col++; std::string host = dbrow[col]; col++; std::string port = dbrow[col]; col++; std::string path = dbrow[col]; col++; int width = atoi(dbrow[col]); col++; int height = atoi(dbrow[col]); col++; int colours = atoi(dbrow[col]); col++; /* int palette = atoi(dbrow[col]); */ col++; Orientation orientation = (Orientation)atoi(dbrow[col]); col++; unsigned int deinterlacing = atoi(dbrow[col]); col++; int brightness = atoi(dbrow[col]); col++; int contrast = atoi(dbrow[col]); col++; int hue = atoi(dbrow[col]); col++; int colour = atoi(dbrow[col]); col++; std::string event_prefix = dbrow[col]; col++; std::string label_format = dbrow[col]; col++; int label_x = atoi(dbrow[col]); col++; int label_y = atoi(dbrow[col]); col++; int image_buffer_count = atoi(dbrow[col]); col++; int warmup_count = atoi(dbrow[col]); col++; int pre_event_count = atoi(dbrow[col]); col++; int post_event_count = atoi(dbrow[col]); col++; int stream_replay_buffer = atoi(dbrow[col]); col++; int alarm_frame_count = atoi(dbrow[col]); col++; int section_length = atoi(dbrow[col]); col++; int frame_skip = atoi(dbrow[col]); col++; int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; int fps_report_interval = atoi(dbrow[col]); col++; int ref_blend_perc = atoi(dbrow[col]); col++; int alarm_ref_blend_perc = atoi(dbrow[col]); col++; int track_motion = atoi(dbrow[col]); col++; int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); Camera *camera = 0; if ( protocol == "http" ) { camera = new RemoteCameraHttp( id, method, host, // Host port, // Port path, // Path cam_width, cam_height, colours, brightness, contrast, hue, colour, purpose==CAPTURE ); } #if HAVE_LIBAVFORMAT else if ( protocol == "rtsp" ) { camera = new RemoteCameraRtsp( id, method, host, // Host port, // Port path, // Path cam_width, cam_height, colours, brightness, contrast, hue, colour, purpose==CAPTURE ); } #endif // HAVE_LIBAVFORMAT else { Fatal( "Unexpected remote camera protocol '%s'", protocol.c_str() ); } monitors[i] = new Monitor( id, name.c_str(), function, enabled, linked_monitors, camera, orientation, deinterlacing, event_prefix.c_str(), label_format.c_str(), Coord( label_x, label_y ), image_buffer_count, warmup_count, pre_event_count, post_event_count, stream_replay_buffer, alarm_frame_count, section_length, frame_skip, capture_delay, alarm_capture_delay, fps_report_interval, ref_blend_perc, alarm_ref_blend_perc, track_motion, RGB_WHITE, purpose, 0, 0 ); Zone **zones = 0; int n_zones = Zone::Load( monitors[i], zones ); monitors[i]->AddZones( n_zones, zones ); Debug( 1, "Loaded monitor %d(%s), %d zones", id, name.c_str(), n_zones ); } if ( mysql_errno( &dbconn ) ) { Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } // Yadda yadda mysql_free_result( result ); return( n_monitors ); } int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ) { static char sql[ZM_SQL_MED_BUFSIZ]; if ( !file[0] ) { strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'File'", sizeof(sql) ); } else { snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'File' and Path = '%s'", file ); } 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_monitors = mysql_num_rows( result ); Debug( 1, "Got %d monitors", n_monitors ); delete[] monitors; monitors = new Monitor *[n_monitors]; for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { int col = 0; int id = atoi(dbrow[col]); col++; const char *name = dbrow[col]; col++; int function = atoi(dbrow[col]); col++; int enabled = atoi(dbrow[col]); col++; const char *linked_monitors = dbrow[col]; col++; const char *path = dbrow[col]; col++; int width = atoi(dbrow[col]); col++; int height = atoi(dbrow[col]); col++; int colours = atoi(dbrow[col]); col++; /* int palette = atoi(dbrow[col]); */ col++; Orientation orientation = (Orientation)atoi(dbrow[col]); col++; unsigned int deinterlacing = atoi(dbrow[col]); col++; int brightness = atoi(dbrow[col]); col++; int contrast = atoi(dbrow[col]); col++; int hue = atoi(dbrow[col]); col++; int colour = atoi(dbrow[col]); col++; const char *event_prefix = dbrow[col]; col++; const char *label_format = dbrow[col]; col++; int label_x = atoi(dbrow[col]); col++; int label_y = atoi(dbrow[col]); col++; int image_buffer_count = atoi(dbrow[col]); col++; int warmup_count = atoi(dbrow[col]); col++; int pre_event_count = atoi(dbrow[col]); col++; int post_event_count = atoi(dbrow[col]); col++; int stream_replay_buffer = atoi(dbrow[col]); col++; int alarm_frame_count = atoi(dbrow[col]); col++; int section_length = atoi(dbrow[col]); col++; int frame_skip = atoi(dbrow[col]); col++; int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; int fps_report_interval = atoi(dbrow[col]); col++; int ref_blend_perc = atoi(dbrow[col]); col++; int alarm_ref_blend_perc = atoi(dbrow[col]); col++; int track_motion = atoi(dbrow[col]); col++; int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); Camera *camera = new FileCamera( id, path, // File cam_width, cam_height, colours, brightness, contrast, hue, colour, purpose==CAPTURE ); monitors[i] = new Monitor( id, name, function, enabled, linked_monitors, camera, orientation, deinterlacing, event_prefix, label_format, Coord( label_x, label_y ), image_buffer_count, warmup_count, pre_event_count, post_event_count, stream_replay_buffer, alarm_frame_count, section_length, frame_skip, capture_delay, alarm_capture_delay, fps_report_interval, ref_blend_perc, alarm_ref_blend_perc, track_motion, RGB_WHITE, purpose, 0, 0 ); Zone **zones = 0; int n_zones = Zone::Load( monitors[i], zones ); monitors[i]->AddZones( n_zones, zones ); Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); } if ( mysql_errno( &dbconn ) ) { Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } // Yadda yadda mysql_free_result( result ); return( n_monitors ); } #if HAVE_LIBAVFORMAT int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose ) { static char sql[ZM_SQL_MED_BUFSIZ]; if ( !file[0] ) { strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Ffmpeg'", sizeof(sql) ); } else { snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Ffmpeg' and Path = '%s'", file ); } 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_monitors = mysql_num_rows( result ); Debug( 1, "Got %d monitors", n_monitors ); delete[] monitors; monitors = new Monitor *[n_monitors]; for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { int col = 0; int id = atoi(dbrow[col]); col++; const char *name = dbrow[col]; col++; int function = atoi(dbrow[col]); col++; int enabled = atoi(dbrow[col]); col++; const char *linked_monitors = dbrow[col]; col++; const char *path = dbrow[col]; col++; int width = atoi(dbrow[col]); col++; int height = atoi(dbrow[col]); col++; int colours = atoi(dbrow[col]); col++; /* int palette = atoi(dbrow[col]); */ col++; Orientation orientation = (Orientation)atoi(dbrow[col]); col++; unsigned int deinterlacing = atoi(dbrow[col]); col++; int brightness = atoi(dbrow[col]); col++; int contrast = atoi(dbrow[col]); col++; int hue = atoi(dbrow[col]); col++; int colour = atoi(dbrow[col]); col++; const char *event_prefix = dbrow[col]; col++; const char *label_format = dbrow[col]; col++; int label_x = atoi(dbrow[col]); col++; int label_y = atoi(dbrow[col]); col++; int image_buffer_count = atoi(dbrow[col]); col++; int warmup_count = atoi(dbrow[col]); col++; int pre_event_count = atoi(dbrow[col]); col++; int post_event_count = atoi(dbrow[col]); col++; int stream_replay_buffer = atoi(dbrow[col]); col++; int alarm_frame_count = atoi(dbrow[col]); col++; int section_length = atoi(dbrow[col]); col++; int frame_skip = atoi(dbrow[col]); col++; int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; int fps_report_interval = atoi(dbrow[col]); col++; int ref_blend_perc = atoi(dbrow[col]); col++; int alarm_ref_blend_perc = atoi(dbrow[col]); col++; int track_motion = atoi(dbrow[col]); col++; int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); Camera *camera = new FfmpegCamera( id, path, // File cam_width, cam_height, colours, brightness, contrast, hue, colour, purpose==CAPTURE ); monitors[i] = new Monitor( id, name, function, enabled, linked_monitors, camera, orientation, deinterlacing, event_prefix, label_format, Coord( label_x, label_y ), image_buffer_count, warmup_count, pre_event_count, post_event_count, stream_replay_buffer, alarm_frame_count, section_length, frame_skip, capture_delay, alarm_capture_delay, fps_report_interval, ref_blend_perc, alarm_ref_blend_perc, track_motion, RGB_WHITE, purpose, 0, 0 ); Zone **zones = 0; int n_zones = Zone::Load( monitors[i], zones ); monitors[i]->AddZones( n_zones, zones ); Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); } if ( mysql_errno( &dbconn ) ) { Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } // Yadda yadda mysql_free_result( result ); return( n_monitors ); } #endif // HAVE_LIBAVFORMAT Monitor *Monitor::Load( int id, bool load_zones, Purpose purpose ) { static char sql[ZM_SQL_MED_BUFSIZ]; snprintf( sql, sizeof(sql), "select Id, Name, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = %d", id ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } MYSQL_RES *result = mysql_store_result( &dbconn ); if ( !result ) { Error( "Can't use query result: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } int n_monitors = mysql_num_rows( result ); Debug( 1, "Got %d monitors", n_monitors ); Monitor *monitor = 0; for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { int col = 0; int id = atoi(dbrow[col]); col++; std::string name = dbrow[col]; col++; std::string type = dbrow[col]; col++; int function = atoi(dbrow[col]); col++; int enabled = atoi(dbrow[col]); col++; std::string linked_monitors = dbrow[col]; col++; std::string device = dbrow[col]; col++; int channel = atoi(dbrow[col]); col++; int format = atoi(dbrow[col]); col++; std::string protocol = dbrow[col]; col++; std::string method = dbrow[col]; col++; std::string host = dbrow[col]; col++; std::string port = dbrow[col]; col++; std::string path = dbrow[col]; col++; int width = atoi(dbrow[col]); col++; int height = atoi(dbrow[col]); col++; int colours = atoi(dbrow[col]); col++; int palette = atoi(dbrow[col]); col++; Orientation orientation = (Orientation)atoi(dbrow[col]); col++; unsigned int deinterlacing = atoi(dbrow[col]); col++; int brightness = atoi(dbrow[col]); col++; int contrast = atoi(dbrow[col]); col++; int hue = atoi(dbrow[col]); col++; int colour = atoi(dbrow[col]); col++; std::string event_prefix = dbrow[col]; col++; std::string label_format = dbrow[col]; col++; int label_x = atoi(dbrow[col]); col++; int label_y = atoi(dbrow[col]); col++; int image_buffer_count = atoi(dbrow[col]); col++; int warmup_count = atoi(dbrow[col]); col++; int pre_event_count = atoi(dbrow[col]); col++; int post_event_count = atoi(dbrow[col]); col++; int stream_replay_buffer = atoi(dbrow[col]); col++; int alarm_frame_count = atoi(dbrow[col]); col++; int section_length = atoi(dbrow[col]); col++; int frame_skip = atoi(dbrow[col]); col++; int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; int fps_report_interval = atoi(dbrow[col]); col++; int ref_blend_perc = atoi(dbrow[col]); col++; int alarm_ref_blend_perc = atoi(dbrow[col]); col++; int track_motion = atoi(dbrow[col]); col++; int signal_check_colour; if ( dbrow[col][0] == '#' ) signal_check_colour = strtol(dbrow[col]+1,0,16); else signal_check_colour = strtol(dbrow[col],0,16); int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); int extras = (deinterlacing>>24)&0xff; Camera *camera = 0; if ( type == "Local" ) { #if ZM_HAS_V4L camera = new LocalCamera( id, device.c_str(), channel, format, method, cam_width, cam_height, colours, palette, brightness, contrast, hue, colour, purpose==CAPTURE, extras ); #else // ZM_HAS_V4L Fatal( "You must have video4linux libraries and headers installed to use local analog or USB cameras for monitor %d", id ); #endif // ZM_HAS_V4L } else if ( type == "Remote" ) { if ( protocol == "http" ) { camera = new RemoteCameraHttp( id, method.c_str(), host.c_str(), port.c_str(), path.c_str(), cam_width, cam_height, colours, brightness, contrast, hue, colour, purpose==CAPTURE ); } else if ( protocol == "rtsp" ) { #if HAVE_LIBAVFORMAT camera = new RemoteCameraRtsp( id, method.c_str(), host.c_str(), port.c_str(), path.c_str(), cam_width, cam_height, colours, brightness, contrast, hue, colour, purpose==CAPTURE ); #else // HAVE_LIBAVFORMAT Fatal( "You must have ffmpeg libraries installed to use remote camera protocol '%s' for monitor %d", protocol.c_str(), id ); #endif // HAVE_LIBAVFORMAT } else { Fatal( "Unexpected remote camera protocol '%s' for monitor %d", protocol.c_str(), id ); } } else if ( type == "File" ) { camera = new FileCamera( id, path.c_str(), cam_width, cam_height, colours, brightness, contrast, hue, colour, purpose==CAPTURE ); } else if ( type == "Ffmpeg" ) { #if HAVE_LIBAVFORMAT camera = new FfmpegCamera( id, path.c_str(), cam_width, cam_height, colours, brightness, contrast, hue, colour, purpose==CAPTURE ); #else // HAVE_LIBAVFORMAT Fatal( "You must have ffmpeg libraries installed to use ffmpeg cameras for monitor %d", id ); #endif // HAVE_LIBAVFORMAT } else { Fatal( "Bogus monitor type '%s' for monitor %d", type.c_str(), id ); } monitor = new Monitor( id, name.c_str(), function, enabled, linked_monitors.c_str(), camera, orientation, deinterlacing, event_prefix.c_str(), label_format.c_str(), Coord( label_x, label_y ), image_buffer_count, warmup_count, pre_event_count, post_event_count, stream_replay_buffer, alarm_frame_count, section_length, frame_skip, capture_delay, alarm_capture_delay, fps_report_interval, ref_blend_perc, alarm_ref_blend_perc, track_motion, signal_check_colour, purpose, 0, 0 ); int n_zones = 0; if ( load_zones ) { Zone **zones = 0; n_zones = Zone::Load( monitor, zones ); monitor->AddZones( n_zones, zones ); } Debug( 1, "Loaded monitor %d(%s), %d zones", id, name.c_str(), n_zones ); } if ( mysql_errno( &dbconn ) ) { Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } // Yadda yadda mysql_free_result( result ); return( monitor ); } int Monitor::Capture() { static int FirstCapture = 1; int captureResult; int index = image_count%image_buffer_count; Image* capture_image = image_buffer[index].image; if ( (deinterlacing & 0xff) == 4) { if ( FirstCapture != 1 ) { /* Copy the next image into the shared memory */ capture_image->CopyBuffer(*(next_buffer.image)); } /* Capture a new next image */ captureResult = camera->Capture(*(next_buffer.image)); if ( FirstCapture ) { FirstCapture = 0; return 0; } } else { /* Capture directly into image buffer, avoiding the need to memcpy() */ captureResult = camera->Capture(*capture_image); } if ( captureResult != 0 ) { // Unable to capture image for temporary reason // Fake a signal loss image capture_image->Fill( signal_check_colour ); captureResult = 0; } else { captureResult = 1; } if ( captureResult == 1 ) { /* Deinterlacing */ if ( (deinterlacing & 0xff) == 1 ) { capture_image->Deinterlace_Discard(); } else if ( (deinterlacing & 0xff) == 2 ) { capture_image->Deinterlace_Linear(); } else if ( (deinterlacing & 0xff) == 3 ) { capture_image->Deinterlace_Blend(); } else if ( (deinterlacing & 0xff) == 4 ) { capture_image->Deinterlace_4Field( next_buffer.image, (deinterlacing>>8)&0xff ); } else if ( (deinterlacing & 0xff) == 5 ) { capture_image->Deinterlace_Blend_CustomRatio( (deinterlacing>>8)&0xff ); } if ( orientation != ROTATE_0 ) { switch ( orientation ) { case ROTATE_0 : { // No action required break; } case ROTATE_90 : case ROTATE_180 : case ROTATE_270 : { capture_image->Rotate( (orientation-1)*90 ); break; } case FLIP_HORI : case FLIP_VERT : { capture_image->Flip( orientation==FLIP_HORI ); break; } } } } if ( true ) { if ( capture_image->Size() != camera->ImageSize() ) { Error( "Captured image does not match expected size, check width, height and colour depth" ); return( -1 ); } if ( ((unsigned int)index == shared_data->last_read_index) && (function > MONITOR) ) { Warning( "Buffer overrun at index %d, image %d, slow down capture, speed up analysis or increase ring buffer size", index, image_count ); time_t now = time(0); double approxFps = double(image_buffer_count)/double(now-image_buffer[index].timestamp->tv_sec); time_t last_read_delta = now - shared_data->last_read_time; if ( last_read_delta > (image_buffer_count/approxFps) ) { Warning( "Last image read from shared memory %ld seconds ago, zma may have gone away", last_read_delta ) shared_data->last_read_index = image_buffer_count; } } gettimeofday( image_buffer[index].timestamp, NULL ); if ( config.timestamp_on_capture ) { TimestampImage( capture_image, image_buffer[index].timestamp ); } shared_data->signal = CheckSignal(capture_image); shared_data->last_write_index = index; shared_data->last_write_time = image_buffer[index].timestamp->tv_sec; image_count++; if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) { time_t now = image_buffer[index].timestamp->tv_sec; fps = double(fps_report_interval)/(now-last_fps_time); //Info( "%d -> %d -> %d", fps_report_interval, now, last_fps_time ); //Info( "%d -> %d -> %lf -> %lf", now-last_fps_time, fps_report_interval/(now-last_fps_time), double(fps_report_interval)/(now-last_fps_time), fps ); Info( "%s: %d - Capturing at %.2lf fps", name, image_count, fps ); last_fps_time = now; } if ( shared_data->action & GET_SETTINGS ) { shared_data->brightness = camera->Brightness(); shared_data->hue = camera->Hue(); shared_data->colour = camera->Colour(); shared_data->contrast = camera->Contrast(); shared_data->action &= ~GET_SETTINGS; } if ( shared_data->action & SET_SETTINGS ) { camera->Brightness( shared_data->brightness ); camera->Hue( shared_data->hue ); camera->Colour( shared_data->colour ); camera->Contrast( shared_data->contrast ); shared_data->action &= ~SET_SETTINGS; } return( 0 ); } shared_data->signal = false; return( -1 ); } void Monitor::TimestampImage( Image *ts_image, const struct timeval *ts_time ) const { if ( label_format[0] ) { // Expand the strftime macros first char label_time_text[256]; strftime( label_time_text, sizeof(label_time_text), label_format, localtime( &ts_time->tv_sec ) ); char label_text[1024]; const char *s_ptr = label_time_text; char *d_ptr = label_text; while ( *s_ptr && ((d_ptr-label_text) < (unsigned int)sizeof(label_text)) ) { if ( *s_ptr == '%' ) { bool found_macro = false; switch ( *(s_ptr+1) ) { case 'N' : d_ptr += snprintf( d_ptr, sizeof(label_text)-(d_ptr-label_text), "%s", name ); found_macro = true; break; case 'Q' : d_ptr += snprintf( d_ptr, sizeof(label_text)-(d_ptr-label_text), "%s", trigger_data->trigger_showtext ); found_macro = true; break; case 'f' : d_ptr += snprintf( d_ptr, sizeof(label_text)-(d_ptr-label_text), "%02ld", ts_time->tv_usec/10000 ); found_macro = true; break; } if ( found_macro ) { s_ptr += 2; continue; } } *d_ptr++ = *s_ptr++; } *d_ptr = '\0'; ts_image->Annotate( label_text, label_coord ); } } bool Monitor::closeEvent() { if ( event ) { if ( function == RECORD || function == MOCORD ) { gettimeofday( &(event->EndTime()), NULL ); } delete event; event = 0; return( true ); } return( false ); } //----------------------------------------- /* * NOTE Nextime's comment: * * OurCheckAlarms seems to be called only by DetectBlack method, and DetectBlack * method is only called in a commented line instead of DetectMotion in zm_monitor.cpp. * * Probably this is just a dead code used for debugghing purpose, so, instead of fixing it * it seems to be safe to just comment it out. * * Anyway, the issues with this code is that it assumes the image to be an RGB24 image, * so, as i've discussed on IRC with mastertheknife, changes needed are: * * Check if the image is 24 or 32 bits ( pImage->Colours() says 3 for 24 and 4 for 32 bits, * comparing it with ZM_COLOUR_RGB24 or ZM_COLOUR_RGB32 is the way ), and then * manage che check using RGB_VAL_RED() and so on macros instead of just RED(). * * Be carefull that in 32 bit images we need to check also where the alpha channel is, so, * (RGBA and BGRA) or (ABGR and ARGB) aren't the same! * * To check black pixels in 32 bit images i can do a more efficient way using * RGBA_ZERO_ALPHA(pixel) == RGBA_ZERO_ALPHA(RGB_BLACK), but before of that i need to * check where the alpha channel is and maybe convert it. * Maybe this won't work as they assign "23" to black_thr, so, they are not checking * if the pixel is black, but just "quasi" black is enough. * * Anyway, for the moment, comment out whole part. */ /* bool Monitor::OurCheckAlarms( Zone *zone, const Image *pImage ) { Info("Entering OurCheckAlarms >>>>>>>>>>>>>>>>>>>>>>>>>>>>"); unsigned char black_thr = 23; int min_alarm_score = 10; int max_alarm_score = 99; //bool alarm = false; unsigned int score; Polygon zone_polygon = zone->GetPolygon(); Info("Got polygon of a zone. It has %d vertices.", zone_polygon.getNumCoords()); zone->ResetStats(); Info("ResetStats done."); if ( !zone->CheckOverloadCount() ) { Info("CheckOverloadCount() return false, we'll return false."); return( false ); } Image *pMaskImage = new Image(pImage->Width(), pImage->Height(), ZM_COLOUR_GRAY8, pImage->SubpixelOrder()); Info("Mask image created."); pMaskImage->Fill(BLACK); Info("Mask image filled with BLACK."); if (pImage->Colours() == ZM_COLOUR_GRAY8) { Info("Analysed image is not colored! Set score = 0."); score = 0; } else { Info("Start processing image."); //Process image unsigned char *buffer = (unsigned char*)pImage->Buffer(); unsigned char *mask_buffer = (unsigned char*)pMaskImage->Buffer(); int black_pixels_count = 0; Info("Loop for black pixels counting and mask filling."); while (buffer < (pImage->Buffer() + pImage->Size())) { if ( (RED(buffer) < black_thr) && (GREEN(buffer) < black_thr) && (BLUE(buffer) < black_thr) ) { *mask_buffer = WHITE; black_pixels_count++; } buffer += pImage->Colours(); mask_buffer++; } if ( !black_pixels_count ) { delete pMaskImage; return( false ); } score = (100*black_pixels_count)/zone_polygon.Area(); Info("Number of black pixels is %d, zone polygon area is %d, score is %d", black_pixels_count, zone_polygon.Area(), score); if ( min_alarm_score && ( score < min_alarm_score) ) { delete pMaskImage; return( false ); } if ( max_alarm_score && (score > max_alarm_score) ) { zone->SetOverloadCount(zone->GetOverloadFrames()); delete pMaskImage; return( false ); } } zone->SetScore(score); Info("Score have been set in zone."); //Get mask Rgb alarm_colour = RGB_RED; Image *tempImage = pMaskImage->HighlightEdges(alarm_colour, &zone_polygon.Extent() ); Info("After HighlightEdges"); zone->SetAlarmImage(tempImage); Info("After SetAlarmImage"); delete pMaskImage; Info("After Delete pMaskImage"); delete tempImage; Info("Leaving OurCheckAlarms >>>>>>>>>>>>>>>>>>>>>>>>>>>>"); return true; } unsigned int Monitor::DetectBlack(const Image &comp_image, Event::StringSet &zoneSet ) { Info("Entering DetectBlack >>>>>>>>>>>>>>>>>>>>>>>>>>"); bool alarm = false; unsigned int score = 0; if ( n_zones <= 0 ) return( alarm ); // Coord alarm_centre; // int top_score = -1; // Find all alarm pixels in active zones Info("Number of zones to process %d", n_zones); for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) { Zone *zone = zones[n_zone]; if ( !zone->IsActive() ) { continue; } Debug( 3, "Checking active zone %s", zone->Label() ); Info( "Checking active zone %s", zone->Label() ); if ( OurCheckAlarms( zone, &comp_image ) ) { Info("OurCheckAlarm is TRUE!!!!!!"); alarm = true; score += zone->Score(); zone->SetAlarm(); Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); Info( "Zone is alarmed, zone score = %d", zone->Score() ); zoneSet.insert( zone->Label() ); // if ( config.opt_control && track_motion ) // { // if ( (int)zone->Score() > top_score ) // { // top_score = zone->Score(); // alarm_centre = zone->GetAlarmCentre(); // } // } } Info( "Finish checking active zone %s", zone->Label() ); } // if ( top_score > 0 ) // { // shared_data->alarm_x = alarm_centre.X(); // shared_data->alarm_y = alarm_centre.Y(); // // Info( "Got alarm centre at %d,%d, at count %d", shared_data->alarm_x, shared_data->alarm_y, image_count ); // } // else // { // shared_data->alarm_x = shared_data->alarm_y = -1; // } // This is a small and innocent hack to prevent scores of 0 being returned in alarm state Info("Leaving DetectBlack <<<<<<<<<<<<<<<<<<<<<<<<<<<"); return( score?score:alarm ); } */ //----------------------------------------------------------------------------------------------- unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &zoneSet ) { bool alarm = false; unsigned int score = 0; if ( n_zones <= 0 ) return( alarm ); if ( config.record_diag_images ) { static char diag_path[PATH_MAX] = ""; if ( !diag_path[0] ) { snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-r.jpg", config.dir_events, id ); } ref_image.WriteJpeg( diag_path ); } ref_image.Delta( comp_image, &delta_image); if ( config.record_diag_images ) { static char diag_path[PATH_MAX] = ""; if ( !diag_path[0] ) { snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-d.jpg", config.dir_events, id ); } delta_image.WriteJpeg( diag_path ); } // Blank out all exclusion zones for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) { Zone *zone = zones[n_zone]; zone->ClearAlarm(); if ( !zone->IsInactive() ) { continue; } Debug( 3, "Blanking inactive zone %s", zone->Label() ); delta_image.Fill( RGB_BLACK, zone->GetPolygon() ); } // Check preclusive zones first for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) { Zone *zone = zones[n_zone]; if ( !zone->IsPreclusive() ) { continue; } Debug( 3, "Checking preclusive zone %s", zone->Label() ); if ( zone->CheckAlarms( &delta_image ) ) { alarm = true; score += zone->Score(); Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); zoneSet.insert( zone->Label() ); //zone->ResetStats(); } } Coord alarm_centre; int top_score = -1; if ( alarm ) { alarm = false; score = 0; } else { // Find all alarm pixels in active zones for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) { Zone *zone = zones[n_zone]; if ( !zone->IsActive() ) { continue; } Debug( 3, "Checking active zone %s", zone->Label() ); if ( zone->CheckAlarms( &delta_image ) ) { alarm = true; score += zone->Score(); zone->SetAlarm(); Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); zoneSet.insert( zone->Label() ); if ( config.opt_control && track_motion ) { if ( (int)zone->Score() > top_score ) { top_score = zone->Score(); alarm_centre = zone->GetAlarmCentre(); } } } } if ( alarm ) { for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) { Zone *zone = zones[n_zone]; if ( !zone->IsInclusive() ) { continue; } Debug( 3, "Checking inclusive zone %s", zone->Label() ); if ( zone->CheckAlarms( &delta_image ) ) { alarm = true; score += zone->Score(); zone->SetAlarm(); Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); zoneSet.insert( zone->Label() ); if ( config.opt_control && track_motion ) { if ( zone->Score() > (unsigned int)top_score ) { top_score = zone->Score(); alarm_centre = zone->GetAlarmCentre(); } } } } } else { // Find all alarm pixels in exclusive zones for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) { Zone *zone = zones[n_zone]; if ( !zone->IsExclusive() ) { continue; } Debug( 3, "Checking exclusive zone %s", zone->Label() ); if ( zone->CheckAlarms( &delta_image ) ) { alarm = true; score += zone->Score(); zone->SetAlarm(); Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); zoneSet.insert( zone->Label() ); } } } } if ( top_score > 0 ) { shared_data->alarm_x = alarm_centre.X(); shared_data->alarm_y = alarm_centre.Y(); Info( "Got alarm centre at %d,%d, at count %d", shared_data->alarm_x, shared_data->alarm_y, image_count ); } else { shared_data->alarm_x = shared_data->alarm_y = -1; } // This is a small and innocent hack to prevent scores of 0 being returned in alarm state return( score?score:alarm ); } bool Monitor::DumpSettings( char *output, bool verbose ) { output[0] = 0; sprintf( output+strlen(output), "Id : %d\n", id ); sprintf( output+strlen(output), "Name : %s\n", name ); sprintf( output+strlen(output), "Type : %s\n", camera->IsLocal()?"Local":(camera->IsRemote()?"Remote":"File") ); #if ZM_HAS_V4L if ( camera->IsLocal() ) { sprintf( output+strlen(output), "Device : %s\n", ((LocalCamera *)camera)->Device().c_str() ); sprintf( output+strlen(output), "Channel : %d\n", ((LocalCamera *)camera)->Channel() ); sprintf( output+strlen(output), "Standard : %d\n", ((LocalCamera *)camera)->Standard() ); } else #endif // ZM_HAS_V4L if ( camera->IsRemote() ) { sprintf( output+strlen(output), "Protocol : %s\n", ((RemoteCamera *)camera)->Protocol().c_str() ); sprintf( output+strlen(output), "Host : %s\n", ((RemoteCamera *)camera)->Host().c_str() ); sprintf( output+strlen(output), "Port : %s\n", ((RemoteCamera *)camera)->Port().c_str() ); sprintf( output+strlen(output), "Path : %s\n", ((RemoteCamera *)camera)->Path().c_str() ); } else if ( camera->IsFile() ) { sprintf( output+strlen(output), "Path : %s\n", ((FileCamera *)camera)->Path() ); } #if HAVE_LIBAVFORMAT else if ( camera->IsFfmpeg() ) { sprintf( output+strlen(output), "Path : %s\n", ((FfmpegCamera *)camera)->Path().c_str() ); } #endif // HAVE_LIBAVFORMAT sprintf( output+strlen(output), "Width : %d\n", camera->Width() ); sprintf( output+strlen(output), "Height : %d\n", camera->Height() ); #if ZM_HAS_V4L if ( camera->IsLocal() ) { sprintf( output+strlen(output), "Palette : %d\n", ((LocalCamera *)camera)->Palette() ); } #endif // ZM_HAS_V4L sprintf( output+strlen(output), "Colours : %d\n", camera->Colours() ); sprintf( output+strlen(output), "Subpixel Order : %d\n", camera->SubpixelOrder() ); sprintf( output+strlen(output), "Event Prefix : %s\n", event_prefix ); sprintf( output+strlen(output), "Label Format : %s\n", label_format ); sprintf( output+strlen(output), "Label Coord : %d,%d\n", label_coord.X(), label_coord.Y() ); sprintf( output+strlen(output), "Image Buffer Count : %d\n", image_buffer_count ); sprintf( output+strlen(output), "Warmup Count : %d\n", warmup_count ); sprintf( output+strlen(output), "Pre Event Count : %d\n", pre_event_count ); sprintf( output+strlen(output), "Post Event Count : %d\n", post_event_count ); sprintf( output+strlen(output), "Stream Replay Buffer : %d\n", stream_replay_buffer ); sprintf( output+strlen(output), "Alarm Frame Count : %d\n", alarm_frame_count ); sprintf( output+strlen(output), "Section Length : %d\n", section_length ); sprintf( output+strlen(output), "Maximum FPS : %.2f\n", capture_delay?DT_PREC_3/capture_delay:0.0 ); sprintf( output+strlen(output), "Alarm Maximum FPS : %.2f\n", alarm_capture_delay?DT_PREC_3/alarm_capture_delay:0.0 ); sprintf( output+strlen(output), "Reference Blend %%ge : %d\n", ref_blend_perc ); sprintf( output+strlen(output), "Alarm Reference Blend %%ge : %d\n", alarm_ref_blend_perc ); sprintf( output+strlen(output), "Track Motion : %d\n", track_motion ); sprintf( output+strlen(output), "Function: %d - %s\n", function, function==NONE?"None":( function==MONITOR?"Monitor Only":( function==MODECT?"Motion Detection":( function==RECORD?"Continuous Record":( function==MOCORD?"Continuous Record with Motion Detection":( function==NODECT?"Externally Triggered only, no Motion Detection":"Unknown" )))))); sprintf( output+strlen(output), "Zones : %d\n", n_zones ); for ( int i = 0; i < n_zones; i++ ) { zones[i]->DumpSettings( output+strlen(output), verbose ); } return( true ); } bool MonitorStream::checkSwapPath( const char *path, bool create_path ) { uid_t uid = getuid(); gid_t gid = getgid(); struct stat stat_buf; if ( stat( path, &stat_buf ) < 0 ) { if ( create_path && errno == ENOENT ) { Debug( 3, "Swap path '%s' missing, creating", path ); if ( mkdir( path, 0755 ) ) { Error( "Can't mkdir %s: %s", path, strerror(errno)); return( false ); } if ( stat( path, &stat_buf ) < 0 ) { Error( "Can't stat '%s': %s", path, strerror(errno) ); return( false ); } } else { Error( "Can't stat '%s': %s", path, strerror(errno) ); return( false ); } } if ( !S_ISDIR(stat_buf.st_mode) ) { Error( "Swap image path '%s' is not a directory", path ); return( false ); } mode_t mask = 0; if ( uid == stat_buf.st_uid ) { // If we are the owner mask = 00700; } else if ( gid == stat_buf.st_gid ) { // If we are in the owner group mask = 00070; } else { // We are neither the owner nor in the group mask = 00007; } if ( (stat_buf.st_mode & mask) != mask ) { Error( "Insufficient permissions on swap image path '%s'", path ); return( false ); } return( true ); } void MonitorStream::processCommand( const CmdMsg *msg ) { Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ); // Check for incoming command switch( (MsgCommand)msg->msg_data[0] ) { case CMD_PAUSE : { Debug( 1, "Got PAUSE command" ); // Set paused flag paused = true; // Set delayed flag delayed = true; last_frame_sent = TV_2_FLOAT( now ); break; } case CMD_PLAY : { Debug( 1, "Got PLAY command" ); if ( paused ) { // Clear paused flag paused = false; // Set delayed_play flag delayed = true; } replay_rate = ZM_RATE_BASE; break; } case CMD_VARPLAY : { Debug( 1, "Got VARPLAY command" ); if ( paused ) { // Clear paused flag paused = false; // Set delayed_play flag delayed = true; } replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; break; } case CMD_STOP : { Debug( 1, "Got STOP command" ); // Clear paused flag paused = false; // Clear delayed_play flag delayed = false; break; } case CMD_FASTFWD : { Debug( 1, "Got FAST FWD command" ); if ( paused ) { // Clear paused flag paused = false; // Set delayed_play flag delayed = true; } // Set play rate switch ( replay_rate ) { case 2 * ZM_RATE_BASE : replay_rate = 5 * ZM_RATE_BASE; break; case 5 * ZM_RATE_BASE : replay_rate = 10 * ZM_RATE_BASE; break; case 10 * ZM_RATE_BASE : replay_rate = 25 * ZM_RATE_BASE; break; case 25 * ZM_RATE_BASE : case 50 * ZM_RATE_BASE : replay_rate = 50 * ZM_RATE_BASE; break; default : replay_rate = 2 * ZM_RATE_BASE; break; } break; } case CMD_SLOWFWD : { Debug( 1, "Got SLOW FWD command" ); // Set paused flag paused = true; // Set delayed flag delayed = true; // Set play rate replay_rate = ZM_RATE_BASE; // Set step step = 1; break; } case CMD_SLOWREV : { Debug( 1, "Got SLOW REV command" ); // Set paused flag paused = true; // Set delayed flag delayed = true; // Set play rate replay_rate = ZM_RATE_BASE; // Set step step = -1; break; } case CMD_FASTREV : { Debug( 1, "Got FAST REV command" ); if ( paused ) { // Clear paused flag paused = false; // Set delayed_play flag delayed = true; } // Set play rate switch ( replay_rate ) { case -2 * ZM_RATE_BASE : replay_rate = -5 * ZM_RATE_BASE; break; case -5 * ZM_RATE_BASE : replay_rate = -10 * ZM_RATE_BASE; break; case -10 * ZM_RATE_BASE : replay_rate = -25 * ZM_RATE_BASE; break; case -25 * ZM_RATE_BASE : case -50 * ZM_RATE_BASE : replay_rate = -50 * ZM_RATE_BASE; break; default : replay_rate = -2 * ZM_RATE_BASE; break; } break; } case CMD_ZOOMIN : { x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; Debug( 1, "Got ZOOM IN command, to %d,%d", x, y ); switch ( zoom ) { case 100: zoom = 150; break; case 150: zoom = 200; break; case 200: zoom = 300; break; case 300: zoom = 400; break; case 400: default : zoom = 500; break; } break; } case CMD_ZOOMOUT : { Debug( 1, "Got ZOOM OUT command" ); switch ( zoom ) { case 500: zoom = 400; break; case 400: zoom = 300; break; case 300: zoom = 200; break; case 200: zoom = 150; break; case 150: default : zoom = 100; break; } break; } case CMD_PAN : { x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; Debug( 1, "Got PAN command, to %d,%d", x, y ); break; } case CMD_SCALE : { scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; Debug( 1, "Got SCALE command, to %d", scale ); break; } case CMD_QUERY : { Debug( 1, "Got QUERY command, sending STATUS" ); break; } default : { Error( "Got unexpected command %d", msg->msg_data[0] ); break; } } struct { int id; int state; double fps; int buffer_level; int rate; double delay; int zoom; bool delayed; bool paused; bool enabled; bool forced; } status_data; status_data.id = monitor->Id(); status_data.fps = monitor->GetFPS(); status_data.state = monitor->shared_data->state; if ( playback_buffer > 0 ) status_data.buffer_level = (MOD_ADD( (temp_write_index-temp_read_index), 0, temp_image_buffer_count )*100)/temp_image_buffer_count; else status_data.buffer_level = 0; status_data.delayed = delayed; status_data.paused = paused; status_data.rate = replay_rate; status_data.delay = TV_2_FLOAT( now ) - TV_2_FLOAT( last_frame_timestamp ); status_data.zoom = zoom; //status_data.enabled = monitor->shared_data->active; status_data.enabled = monitor->trigger_data->trigger_state!=Monitor::TRIGGER_OFF; status_data.forced = monitor->trigger_data->trigger_state==Monitor::TRIGGER_ON; Debug( 2, "L:%d, D:%d, P:%d, R:%d, d:%.3f, Z:%d, E:%d F:%d", status_data.buffer_level, status_data.delayed, status_data.paused, status_data.rate, status_data.delay, status_data.zoom, status_data.enabled, status_data.forced ); DataMsg status_msg; status_msg.msg_type = MSG_DATA_WATCH; memcpy( &status_msg.msg_data, &status_data, sizeof(status_msg.msg_data) ); int nbytes = 0; if ( (nbytes = sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) )) < 0 ) { //if ( errno != EAGAIN ) { Error( "Can't sendto on sd %d: %s", sd, strerror(errno) ); //exit( -1 ); } } updateFrameRate( monitor->GetFPS() ); } bool MonitorStream::sendFrame( const char *filepath, struct timeval *timestamp ) { bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); if ( type != STREAM_JPEG ) send_raw = false; if ( !config.timestamp_on_capture && timestamp ) send_raw = false; if ( !send_raw ) { Image temp_image( filepath ); return( sendFrame( &temp_image, timestamp ) ); } else { int img_buffer_size = 0; static unsigned char img_buffer[ZM_MAX_IMAGE_SIZE]; FILE *fdj = NULL; if ( (fdj = fopen( filepath, "r" )) ) { img_buffer_size = fread( img_buffer, 1, sizeof(img_buffer), fdj ); fclose( fdj ); } else { Error( "Can't open %s: %s", filepath, strerror(errno) ); return( false ); } // Calculate how long it takes to actually send the frame struct timeval frameStartTime; gettimeofday( &frameStartTime, NULL ); fprintf( stdout, "--ZoneMinderFrame\r\n" ); fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size ); fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { if ( !zm_terminate ) Error( "Unable to send stream frame: %s", strerror(errno) ); return( false ); } fprintf( stdout, "\r\n\r\n" ); fflush( stdout ); struct timeval frameEndTime; gettimeofday( &frameEndTime, NULL ); int frameSendTime = tvDiffMsec( frameStartTime, frameEndTime ); if ( frameSendTime > 1000/maxfps ) { maxfps /= 2; Error( "Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps ); } last_frame_sent = TV_2_FLOAT( now ); return( true ); } return( false ); } bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp ) { Image *send_image = prepareImage( image ); if ( !config.timestamp_on_capture && timestamp ) monitor->TimestampImage( send_image, timestamp ); #if HAVE_LIBAVCODEC if ( type == STREAM_MPEG ) { if ( !vid_stream ) { vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() ); fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); vid_stream->OpenStream(); } static struct timeval base_time; struct DeltaTimeval delta_time; if ( !frame_count ) base_time = *timestamp; DELTA_TIMEVAL( delta_time, *timestamp, base_time, DT_PREC_3 ); /* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_time.delta ); } else #endif // HAVE_LIBAVCODEC { static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; int img_buffer_size = 0; unsigned char *img_buffer = temp_img_buffer; // Calculate how long it takes to actually send the frame struct timeval frameStartTime; gettimeofday( &frameStartTime, NULL ); fprintf( stdout, "--ZoneMinderFrame\r\n" ); switch( type ) { case STREAM_JPEG : send_image->EncodeJpeg( img_buffer, &img_buffer_size ); fprintf( stdout, "Content-Type: image/jpeg\r\n" ); break; case STREAM_RAW : fprintf( stdout, "Content-Type: image/x-rgb\r\n" ); img_buffer = (uint8_t*)send_image->Buffer(); img_buffer_size = send_image->Size(); break; case STREAM_ZIP : fprintf( stdout, "Content-Type: image/x-rgbz\r\n" ); unsigned long zip_buffer_size; send_image->Zip( img_buffer, &zip_buffer_size ); img_buffer_size = zip_buffer_size; break; default : Fatal( "Unexpected frame type %d", type ); break; } fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { if ( !zm_terminate ) Error( "Unable to send stream frame: %s", strerror(errno) ); return( false ); } fprintf( stdout, "\r\n\r\n" ); fflush( stdout ); struct timeval frameEndTime; gettimeofday( &frameEndTime, NULL ); int frameSendTime = tvDiffMsec( frameStartTime, frameEndTime ); if ( frameSendTime > 1000/maxfps ) { maxfps /= 1.5; Error( "Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps ); } } last_frame_sent = TV_2_FLOAT( now ); return( true ); } void MonitorStream::runStream() { if ( type == STREAM_SINGLE ) { // Not yet migrated over to stream class monitor->SingleImage( scale ); return; } openComms(); checkInitialised(); updateFrameRate( monitor->GetFPS() ); if ( type == STREAM_JPEG ) fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" ); int last_read_index = monitor->image_buffer_count; time_t stream_start_time; time( &stream_start_time ); frame_count = 0; temp_image_buffer = 0; temp_image_buffer_count = playback_buffer; temp_read_index = temp_image_buffer_count; temp_write_index = temp_image_buffer_count; char swap_path[PATH_MAX] = ""; bool buffered_playback = false; if ( connkey && playback_buffer > 0 ) { Debug( 2, "Checking swap image location" ); Debug( 3, "Checking swap image path" ); strncpy( swap_path, config.path_swap, sizeof(swap_path) ); if ( checkSwapPath( swap_path, false ) ) { snprintf( &(swap_path[strlen(swap_path)]), sizeof(swap_path)-strlen(swap_path), "/zmswap-m%d", monitor->Id() ); if ( checkSwapPath( swap_path, true ) ) { snprintf( &(swap_path[strlen(swap_path)]), sizeof(swap_path)-strlen(swap_path), "/zmswap-q%06d", connkey ); if ( checkSwapPath( swap_path, true ) ) { buffered_playback = true; } } } if ( !buffered_playback ) { Error( "Unable to validate swap image path, disabling buffered playback" ); } else { Debug( 2, "Assigning temporary buffer" ); temp_image_buffer = new SwapImage[temp_image_buffer_count]; memset( temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count ); Debug( 2, "Assigned temporary buffer" ); } } float max_secs_since_last_sent_frame = 10.0; //should be > keep alive amount (5 secs) while ( !zm_terminate ) { bool got_command = false; if ( feof( stdout ) || ferror( stdout ) || !monitor->ShmValid() ) { break; } gettimeofday( &now, NULL ); if ( connkey ) { while(checkCommandQueue()) { got_command = true; } } //bool frame_sent = false; if ( buffered_playback && delayed ) { if ( temp_read_index == temp_write_index ) { // Go back to live viewing Debug( 1, "Exceeded temporary streaming buffer" ); // Clear paused flag paused = false; // Clear delayed_play flag delayed = false; replay_rate = ZM_RATE_BASE; } else { if ( !paused ) { int temp_index = MOD_ADD( temp_read_index, 0, temp_image_buffer_count ); //Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index ); SwapImage *swap_image = &temp_image_buffer[temp_index]; if ( !swap_image->valid ) { paused = true; delayed = true; temp_read_index = MOD_ADD( temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count ); } else { //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 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 ) ); // If the next frame is due if ( actual_delta_time > expected_delta_time ) { //Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time ); if ( temp_index%frame_mod == 0 ) { Debug( 2, "Sending delayed frame %d", temp_index ); // Send the next frame if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) ) zm_terminate = true; memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) ); //frame_sent = true; } temp_read_index = MOD_ADD( temp_read_index, (replay_rate>0?1:-1), temp_image_buffer_count ); } } } else if ( step != 0 ) { 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]; // Send the next frame if ( !sendFrame( temp_image_buffer[temp_read_index].file_name, &temp_image_buffer[temp_read_index].timestamp ) ) zm_terminate = true; memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) ); //frame_sent = true; step = 0; } else { int temp_index = MOD_ADD( temp_read_index, 0, temp_image_buffer_count ); double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; if ( got_command || actual_delta_time > 5 ) { // Send keepalive Debug( 2, "Sending keepalive frame %d", temp_index ); // Send the next frame if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) ) zm_terminate = true; //frame_sent = true; } } } if ( temp_read_index == temp_write_index ) { // Go back to live viewing Warning( "Rewound over write index, resuming live play" ); // Clear paused flag paused = false; // Clear delayed_play flag delayed = false; replay_rate = ZM_RATE_BASE; } } if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) { int index = monitor->shared_data->last_write_index%monitor->image_buffer_count; last_read_index = monitor->shared_data->last_write_index; //Debug( 1, "%d: %x - %x", index, image_buffer[index].image, image_buffer[index].image->buffer ); if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) { if ( !paused && !delayed ) { // Send the next frame Monitor::Snapshot *snap = &monitor->image_buffer[index]; if ( !sendFrame( snap->image, snap->timestamp ) ) zm_terminate = true; memcpy( &last_frame_timestamp, snap->timestamp, sizeof(last_frame_timestamp) ); //frame_sent = true; temp_read_index = temp_write_index; } } if ( buffered_playback ) { if ( monitor->shared_data->valid ) { if ( monitor->image_buffer[index].timestamp->tv_sec ) { int temp_index = temp_write_index%temp_image_buffer_count; Debug( 2, "Storing frame %d", temp_index ); if ( !temp_image_buffer[temp_index].valid ) { snprintf( temp_image_buffer[temp_index].file_name, sizeof(temp_image_buffer[0].file_name), "%s/zmswap-i%05d.jpg", swap_path, temp_index ); 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) ); 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 ); if ( temp_write_index == temp_read_index ) { // Go back to live viewing Warning( "Exceeded temporary buffer, resuming live play" ); // Clear paused flag paused = false; // Clear delayed_play flag delayed = false; replay_rate = ZM_RATE_BASE; } } else { Warning( "Unable to store frame as timestamp invalid" ); } } else { Warning( "Unable to store frame as shared memory invalid" ); } } frame_count++; } usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) ); if ( ttl ) { if ( (now.tv_sec - stream_start_time) > ttl ) { break; } } if ( (TV_2_FLOAT( now ) - last_frame_sent) > max_secs_since_last_sent_frame ) { Error( "Terminating, last frame sent time %f secs more than maximum of %f", TV_2_FLOAT( now ) - last_frame_sent, max_secs_since_last_sent_frame ); break; } } if ( buffered_playback ) { char swap_path[PATH_MAX] = ""; snprintf( swap_path, sizeof(swap_path), "%s/zmswap-m%d/zmswap-q%06d", config.path_swap, monitor->Id(), connkey ); Debug( 1, "Cleaning swap files from %s", swap_path ); struct stat stat_buf; if ( stat( swap_path, &stat_buf ) < 0 ) { if ( errno != ENOENT ) { Error( "Can't stat '%s': %s", swap_path, strerror(errno) ); } } else if ( !S_ISDIR(stat_buf.st_mode) ) { Error( "Swap image path '%s' is not a directory", swap_path ); } else { char glob_pattern[PATH_MAX] = ""; snprintf( glob_pattern, sizeof(glob_pattern), "%s/*.*", swap_path ); glob_t pglob; int glob_status = glob( glob_pattern, 0, 0, &pglob ); if ( glob_status != 0 ) { if ( glob_status < 0 ) { Error( "Can't glob '%s': %s", glob_pattern, strerror(errno) ); } else { Debug( 1, "Can't glob '%s': %d", glob_pattern, glob_status ); } } else { for ( unsigned int i = 0; i < pglob.gl_pathc; i++ ) { if ( unlink( pglob.gl_pathv[i] ) < 0 ) { Error( "Can't unlink '%s': %s", pglob.gl_pathv[i], strerror(errno) ); } } } globfree( &pglob ); if ( rmdir( swap_path ) < 0 ) { Error( "Can't rmdir '%s': %s", swap_path, strerror(errno) ); } } } closeComms(); } void Monitor::SingleImage( int scale) { int img_buffer_size = 0; static JOCTET img_buffer[ZM_MAX_IMAGE_SIZE]; Image scaled_image; int index = shared_data->last_write_index%image_buffer_count; Snapshot *snap = &image_buffer[index]; Image *snap_image = snap->image; if ( scale != ZM_SCALE_BASE ) { scaled_image.Assign( *snap_image ); scaled_image.Scale( scale ); snap_image = &scaled_image; } if ( !config.timestamp_on_capture ) { TimestampImage( snap_image, snap->timestamp ); } snap_image->EncodeJpeg( img_buffer, &img_buffer_size ); fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size ); fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); fwrite( img_buffer, img_buffer_size, 1, stdout ); } void Monitor::SingleImageRaw( int scale) { Image scaled_image; int index = shared_data->last_write_index%image_buffer_count; Snapshot *snap = &image_buffer[index]; Image *snap_image = snap->image; if ( scale != ZM_SCALE_BASE ) { scaled_image.Assign( *snap_image ); scaled_image.Scale( scale ); snap_image = &scaled_image; } if ( !config.timestamp_on_capture ) { TimestampImage( snap_image, snap->timestamp ); } fprintf( stdout, "Content-Length: %d\r\n", snap_image->Size() ); fprintf( stdout, "Content-Type: image/x-rgb\r\n\r\n" ); fwrite( snap_image->Buffer(), snap_image->Size(), 1, stdout ); } void Monitor::SingleImageZip( int scale) { unsigned long img_buffer_size = 0; static Bytef img_buffer[ZM_MAX_IMAGE_SIZE]; Image scaled_image; int index = shared_data->last_write_index%image_buffer_count; Snapshot *snap = &image_buffer[index]; Image *snap_image = snap->image; if ( scale != ZM_SCALE_BASE ) { scaled_image.Assign( *snap_image ); scaled_image.Scale( scale ); snap_image = &scaled_image; } if ( !config.timestamp_on_capture ) { TimestampImage( snap_image, snap->timestamp ); } snap_image->Zip( img_buffer, &img_buffer_size ); fprintf( stdout, "Content-Length: %ld\r\n", img_buffer_size ); fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" ); fwrite( img_buffer, img_buffer_size, 1, stdout ); } ZoneMinder-1.26.5/src/zm_monitor.h000066400000000000000000000333451225361755400170360ustar00rootroot00000000000000// // ZoneMinder Monitor Class Interfaces, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_MONITOR_H #define ZM_MONITOR_H #include #include #include "zm.h" #include "zm_coord.h" #include "zm_image.h" #include "zm_rgb.h" #include "zm_zone.h" #include "zm_event.h" #include "zm_camera.h" #include "zm_image_analyser.h" #include #include #define SIGNAL_CAUSE "Signal" #define MOTION_CAUSE "Motion" #define LINKED_CAUSE "Linked" // // This is the main class for monitors. Each monitor is associated // with a camera and is effectively a collector for events. // class Monitor { friend class MonitorStream; public: typedef enum { QUERY=0, CAPTURE, ANALYSIS } Purpose; typedef enum { NONE=1, MONITOR, MODECT, RECORD, MOCORD, NODECT } Function; typedef enum { ROTATE_0=1, ROTATE_90, ROTATE_180, ROTATE_270, FLIP_HORI, FLIP_VERT } Orientation; typedef enum { IDLE, PREALARM, ALARM, ALERT, TAPE } State; protected: typedef std::set ZoneSet; typedef enum { GET_SETTINGS=0x1, SET_SETTINGS=0x2, RELOAD=0x4, SUSPEND=0x10, RESUME=0x20 } Action; typedef enum { CLOSE_TIME, CLOSE_IDLE, CLOSE_ALARM } EventCloseMode; /* sizeof(SharedData) expected to be 336 bytes on 32bit and 64bit */ typedef struct { uint32_t size; /* +0 */ uint32_t last_write_index; /* +4 */ uint32_t last_read_index; /* +8 */ uint32_t state; /* +12 */ uint32_t last_event; /* +16 */ uint32_t action; /* +20 */ int32_t brightness; /* +24 */ int32_t hue; /* +28 */ int32_t colour; /* +32 */ int32_t contrast; /* +36 */ int32_t alarm_x; /* +40 */ int32_t alarm_y; /* +44 */ uint8_t valid; /* +48 */ uint8_t active; /* +49 */ uint8_t signal; /* +50 */ uint8_t format; /* +51 */ uint32_t imagesize; /* +52 */ uint32_t epadding1; /* +56 */ uint32_t epadding2; /* +60 */ /* ** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038. ** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16. */ union { /* +64 */ time_t last_write_time; uint64_t extrapad1; }; union { /* +72 */ time_t last_read_time; uint64_t extrapad2; }; uint8_t control_state[256]; /* +80 */ } SharedData; typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState; /* sizeof(TriggerData) expected to be 560 on 32bit & and 64bit */ typedef struct { uint32_t size; uint32_t trigger_state; uint32_t trigger_score; uint32_t padding; char trigger_cause[32]; char trigger_text[256]; char trigger_showtext[256]; } TriggerData; /* sizeof(Snapshot) expected to be 16 bytes on 32bit and 32 bytes on 64bit */ struct Snapshot { struct timeval *timestamp; Image *image; void* padding; }; class MonitorLink { protected: unsigned int id; char name[64]; bool connected; time_t last_connect_time; #if ZM_MEM_MAPPED int map_fd; char mem_file[PATH_MAX]; #else // ZM_MEM_MAPPED int shm_id; #endif // ZM_MEM_MAPPED unsigned long mem_size; unsigned char *mem_ptr; volatile SharedData *shared_data; volatile TriggerData *trigger_data; int last_state; int last_event; public: MonitorLink( int p_id, const char *p_name ); ~MonitorLink(); inline int Id() const { return( id ); } inline const char *Name() const { return( name ); } inline bool isConnected() const { return( connected ); } inline time_t getLastConnectTime() const { return( last_connect_time ); } bool connect(); bool disconnect(); bool isAlarmed(); bool inAlarm(); bool hasAlarmed(); }; protected: // These are read from the DB and thereafter remain unchanged unsigned int id; char name[64]; Function function; // What the monitor is doing bool enabled; // Whether the monitor is enabled or asleep unsigned int width; // Normally the same as the camera, but not if partly rotated unsigned int height; // Normally the same as the camera, but not if partly rotated Orientation orientation; // Whether the image has to be rotated at all unsigned int deinterlacing; int brightness; // The statically saved brightness of the camera int contrast; // The statically saved contrast of the camera int hue; // The statically saved hue of the camera int colour; // The statically saved colour of the camera char event_prefix[64]; // The prefix applied to event names as they are created char label_format[64]; // The format of the timestamp on the images Coord label_coord; // The coordinates of the timestamp on the images int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count int warmup_count; // How many images to process before looking for events int pre_event_count; // How many images to hold and prepend to an alarm event int post_event_count; // How many unalarmed images must occur before the alarm state is reset int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now int section_length; // How long events should last in continuous modes int frame_skip; // How many frames to skip in continuous modes int capture_delay; // How long we wait between capture frames int alarm_capture_delay; // How long we wait between capture frames when in alarm state int alarm_frame_count; // How many alarm frames are required before an event is triggered int fps_report_interval; // How many images should be captured/processed between reporting the current FPS int ref_blend_perc; // Percentage of new image going into reference image. int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm. bool track_motion; // Whether this monitor tries to track detected motion Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected double fps; Image delta_image; Image ref_image; Purpose purpose; // What this monitor has been created to do int event_count; int image_count; int ready_count; int first_alarm_count; int last_alarm_count; int buffer_count; int prealarm_count; State state; time_t start_time; time_t last_fps_time; time_t auto_resume_time; EventCloseMode event_close_mode; #if ZM_MEM_MAPPED int map_fd; char mem_file[PATH_MAX]; #else // ZM_MEM_MAPPED int shm_id; #endif // ZM_MEM_MAPPED unsigned long mem_size; unsigned char *mem_ptr; SharedData *shared_data; TriggerData *trigger_data; Snapshot *image_buffer; Snapshot next_buffer; /* Used by four field deinterlacing */ Camera *camera; Event *event; int n_zones; Zone **zones; int iDoNativeMotDet; int n_linked_monitors; MonitorLink **linked_monitors; public: // OurCheckAlarms seems to be unused. Check it on zm_monitor.cpp for more info. //bool OurCheckAlarms( Zone *zone, const Image *pImage ); Monitor( int p_id, const char *p_name, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0 ); ~Monitor(); void AddZones( int p_n_zones, Zone *p_zones[] ); inline int ShmValid() const { return( shared_data->valid ); } inline int Id() const { return( id ); } inline const char *Name() const { return( name ); } inline Function GetFunction() const { return( function ); } inline bool Enabled() { if ( function <= MONITOR ) return( false ); return( enabled ); } inline const char *EventPrefix() const { return( event_prefix ); } inline bool Ready() { if ( function <= MONITOR ) return( false ); return( image_count > ready_count ); } inline bool Active() { if ( function <= MONITOR ) return( false ); return( enabled && shared_data->active ); } unsigned int Width() const { return( width ); } unsigned int Height() const { return( height ); } unsigned int Colours() const { return( camera->Colours() ); } unsigned int SubpixelOrder() const { return( camera->SubpixelOrder() ); } State GetState() const; int GetImage( int index=-1, int scale=100 ) const; struct timeval GetTimestamp( int index=-1 ) const; int GetCaptureDelay() const { return( capture_delay ); } int GetAlarmCaptureDelay() const { return( alarm_capture_delay ); } unsigned int GetLastReadIndex() const; unsigned int GetLastWriteIndex() const; unsigned int GetLastEvent() const; double GetFPS() const; void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" ); void ForceAlarmOff(); void CancelForced(); TriggerState GetTriggerState() const { return( (TriggerState)(trigger_data?trigger_data->trigger_state:TRIGGER_CANCEL )); } void actionReload(); void actionEnable(); void actionDisable(); void actionSuspend(); void actionResume(); int actionBrightness( int p_brightness=-1 ); int actionHue( int p_hue=-1 ); int actionColour( int p_colour=-1 ); int actionContrast( int p_contrast=-1 ); inline int PrimeCapture() { return( camera->PrimeCapture() ); } inline int PreCapture() { return( camera->PreCapture() ); } int Capture(); int PostCapture() { return( camera->PostCapture() ); } unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet ); // DetectBlack seems to be unused. Check it on zm_monitor.cpp for more info. //unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet ); bool CheckSignal( const Image *image ); bool Analyse(); void DumpImage( Image *dump_image ) const; void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const; bool closeEvent(); void Reload(); void ReloadZones(); void ReloadLinkedMonitors( const char * ); bool DumpSettings( char *output, bool verbose ); void DumpZoneImage( const char *zone_string=0 ); #if ZM_HAS_V4L static int LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose ); #endif // ZM_HAS_V4L static int LoadRemoteMonitors( const char *protocol, const char *host, const char*port, const char*path, Monitor **&monitors, Purpose purpose ); static int LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ); #if HAVE_LIBAVFORMAT static int LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose ); #endif // HAVE_LIBAVFORMAT static Monitor *Load( int id, bool load_zones, Purpose purpose ); //void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y ); //void StreamImages( int scale=100, int maxfps=10, time_t ttl=0, int msq_id=0 ); //void StreamImagesRaw( int scale=100, int maxfps=10, time_t ttl=0 ); //void StreamImagesZip( int scale=100, int maxfps=10, time_t ttl=0 ); void SingleImage( int scale=100 ); void SingleImageRaw( int scale=100 ); void SingleImageZip( int scale=100 ); #if HAVE_LIBAVCODEC //void StreamMpeg( const char *format, int scale=100, int maxfps=10, int bitrate=100000 ); #endif // HAVE_LIBAVCODEC }; #define MOD_ADD( var, delta, limit ) (((var)+(limit)+(delta))%(limit)) class MonitorStream : public StreamBase { protected: typedef struct SwapImage { bool valid; struct timeval timestamp; char file_name[PATH_MAX]; } SwapImage; private: SwapImage *temp_image_buffer; int temp_image_buffer_count; int temp_read_index; int temp_write_index; protected: time_t ttl; protected: int playback_buffer; bool delayed; int frame_count; protected: bool checkSwapPath( const char *path, bool create_path ); bool sendFrame( const char *filepath, struct timeval *timestamp ); bool sendFrame( Image *image, struct timeval *timestamp ); void processCommand( const CmdMsg *msg ); public: MonitorStream() : playback_buffer( 0 ), delayed( false ), frame_count( 0 ) { } void setStreamBuffer( int p_playback_buffer ) { playback_buffer = p_playback_buffer; } void setStreamTTL( time_t p_ttl ) { ttl = p_ttl; } void setStreamStart( int monitor_id ) { loadMonitor( monitor_id ); } void runStream(); }; #endif // ZM_MONITOR_H ZoneMinder-1.26.5/src/zm_mpeg.cpp000066400000000000000000000277531225361755400166400ustar00rootroot00000000000000/* * ZoneMinder MPEG class implementation, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include "zm.h" #include "zm_rgb.h" #include "zm_mpeg.h" #if HAVE_LIBAVCODEC extern "C" { #include } #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(54, 1, 0) static int encode_frame(AVCodecContext *c, AVFrame *frame) { AVPacket pkt = { 0 }; int ret, got_output; av_init_packet(&pkt); av_init_packet(&pkt); ret = avcodec_encode_video2(c, &pkt, frame, &got_output); if (ret < 0) return ret; ret = pkt.size; av_free_packet(&pkt); return ret; } #endif bool VideoStream::initialised = false; VideoStream::MimeData VideoStream::mime_data[] = { { "asf", "video/x-ms-asf" }, { "swf", "application/x-shockwave-flash" }, { "flv", "video/x-flv" }, { "mp4", "video/mp4" }, { "move", "video/quicktime" } }; void VideoStream::Initialise() { av_register_all(); initialised = true; } void VideoStream::SetupFormat( const char *p_filename, const char *p_format ) { filename = p_filename; format = p_format; /* auto detect the output format from the name. default is mpeg. */ of = av_guess_format( format, NULL, NULL); if ( !of ) { Warning( "Could not deduce output format from file extension: using mpeg" ); of = av_guess_format("mpeg", NULL, NULL); } if ( !of ) { Fatal( "Could not find suitable output format" ); } /* allocate the output media context */ ofc = (AVFormatContext *)av_mallocz(sizeof(AVFormatContext)); if ( !ofc ) { Panic( "Memory error" ); } ofc->oformat = of; snprintf( ofc->filename, sizeof(ofc->filename), "%s", filename ); } void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate ) { /* ffmpeg format matching */ switch(colours) { case ZM_COLOUR_RGB24: { if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { /* BGR subpixel order */ pf = PIX_FMT_BGR24; } else { /* Assume RGB subpixel order */ pf = PIX_FMT_RGB24; } break; } case ZM_COLOUR_RGB32: { if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { /* ARGB subpixel order */ pf = PIX_FMT_ARGB; } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { /* ABGR subpixel order */ pf = PIX_FMT_ABGR; } else if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { /* BGRA subpixel order */ pf = PIX_FMT_BGRA; } else { /* Assume RGBA subpixel order */ pf = PIX_FMT_RGBA; } break; } case ZM_COLOUR_GRAY8: pf = PIX_FMT_GRAY8; break; default: Panic("Unexpected colours: %d",colours); break; } /* add the video streams using the default format codecs and initialize the codecs */ ost = NULL; if (of->video_codec != CODEC_ID_NONE) { #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0) ost = av_new_stream(ofc, 0); #else ost = avformat_new_stream(ofc, 0); #endif if (!ost) { Panic( "Could not alloc stream" ); } #if ZM_FFMPEG_SVN AVCodecContext *c = ost->codec; #else AVCodecContext *c = &ost->codec; #endif c->codec_id = of->video_codec; #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) c->codec_type = AVMEDIA_TYPE_VIDEO; #else c->codec_type = CODEC_TYPE_VIDEO; #endif /* put sample parameters */ c->bit_rate = bitrate; c->pix_fmt = PIX_FMT_YUV420P; /* resolution must be a multiple of two */ c->width = width; c->height = height; #if ZM_FFMPEG_SVN /* time base: this is the fundamental unit of time (in seconds) in terms of which frame timestamps are represented. for fixed-fps content, timebase should be 1/framerate and timestamp increments should be identically 1. */ //c->time_base.den = (int)(frame_rate*100); //c->time_base.num = 100; c->time_base.den = frame_rate; c->time_base.num = 1; #else /* frames per second */ c->frame_rate = frame_rate; c->frame_rate_base = 1; #endif //c->gop_size = frame_rate/2; /* emit one intra frame every half second or so */ c->gop_size = 12; if ( c->gop_size < 3 ) c->gop_size = 3; // some formats want stream headers to be seperate if(!strcmp(ofc->oformat->name, "mp4") || !strcmp(ofc->oformat->name, "mov") || !strcmp(ofc->oformat->name, "3gp")) c->flags |= CODEC_FLAG_GLOBAL_HEADER; } } void VideoStream::SetParameters() { /* set the output parameters (must be done even if no parameters). */ #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0) if ( av_set_parameters(ofc, NULL) < 0 ) #else if ( avformat_write_header(ofc, NULL) < 0 ) #endif { Panic( "Invalid output format parameters" ); } //dump_format(ofc, 0, filename, 1); } const char *VideoStream::MimeType() const { for ( unsigned int i = 0; i < sizeof(mime_data)/sizeof(*mime_data); i++ ) { if ( strcmp( format, mime_data[i].format ) == 0 ) { return( mime_data[i].mime_type ); } } const char *mime_type = of->mime_type; if ( !mime_type ) { mime_type = "video/mpeg"; Warning( "Unable to determine mime type for '%s' format, using '%s' as default", format, mime_type ); } return( mime_type ); } void VideoStream::OpenStream() { /* now that all the parameters are set, we can open the video codecs and allocate the necessary encode buffers */ if ( ost ) { #if ZM_FFMPEG_SVN AVCodecContext *c = ost->codec; #else AVCodecContext *c = &ost->codec; #endif /* find the video encoder */ AVCodec *codec = avcodec_find_encoder(c->codec_id); if ( !codec ) { Panic( "codec not found" ); } /* open the codec */ #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 7, 0) if ( avcodec_open(c, codec) < 0 ) #else if ( avcodec_open2(c, codec, 0) < 0 ) #endif { Panic( "Could not open codec" ); } /* allocate the encoded raw picture */ opicture = avcodec_alloc_frame(); if ( !opicture ) { Panic( "Could not allocate opicture" ); } int size = avpicture_get_size( c->pix_fmt, c->width, c->height); uint8_t *opicture_buf = (uint8_t *)av_malloc(size); if ( !opicture_buf ) { av_free(opicture); Panic( "Could not allocate opicture" ); } avpicture_fill( (AVPicture *)opicture, opicture_buf, c->pix_fmt, c->width, c->height ); /* if the output format is not identical to the input format, then a temporary picture is needed too. It is then converted to the required output format */ tmp_opicture = NULL; if ( c->pix_fmt != pf ) { tmp_opicture = avcodec_alloc_frame(); if ( !tmp_opicture ) { Panic( "Could not allocate temporary opicture" ); } int size = avpicture_get_size( pf, c->width, c->height); uint8_t *tmp_opicture_buf = (uint8_t *)av_malloc(size); if (!tmp_opicture_buf) { av_free( tmp_opicture ); Panic( "Could not allocate temporary opicture" ); } avpicture_fill( (AVPicture *)tmp_opicture, tmp_opicture_buf, pf, c->width, c->height ); } } /* open the output file, if needed */ if ( !(of->flags & AVFMT_NOFILE) ) { #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) if ( avio_open(&ofc->pb, filename, AVIO_FLAG_WRITE) < 0 ) #else if ( url_fopen(&ofc->pb, filename, AVIO_FLAG_WRITE) < 0 ) #endif { Fatal( "Could not open '%s'", filename ); } } video_outbuf = NULL; if ( !(ofc->oformat->flags & AVFMT_RAWPICTURE) ) { /* allocate output buffer */ /* XXX: API change will be done */ video_outbuf_size = 200000; video_outbuf = (uint8_t *)malloc(video_outbuf_size); } /* write the stream header, if any */ #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0) av_write_header(ofc); #else avformat_write_header(ofc, NULL); #endif } VideoStream::VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height ) { if ( !initialised ) { Initialise(); } SetupFormat( filename, format ); SetupCodec( colours, subpixelorder, width, height, bitrate, frame_rate ); SetParameters(); } VideoStream::~VideoStream() { /* close each codec */ if (ost) { #if ZM_FFMPEG_SVN avcodec_close(ost->codec); #else avcodec_close(&ost->codec); #endif av_free(opicture->data[0]); av_free(opicture); if (tmp_opicture) { av_free(tmp_opicture->data[0]); av_free(tmp_opicture); } av_free(video_outbuf); } /* write the trailer, if any */ av_write_trailer(ofc); /* free the streams */ for( unsigned int i = 0; i < ofc->nb_streams; i++) { av_freep(&ofc->streams[i]); } if (!(of->flags & AVFMT_NOFILE)) { /* close the output file */ #if ZM_FFMPEG_SVN #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) avio_close(ofc->pb); #else url_fclose(ofc->pb); #endif #else url_fclose(&ofc->pb); #endif } /* free the stream */ av_free(ofc); } double VideoStream::EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp, unsigned int timestamp ) { #ifdef HAVE_LIBSWSCALE static struct SwsContext *img_convert_ctx = 0; #endif // HAVE_LIBSWSCALE double pts = 0.0; if (ost) { #if ZM_FFMPEG_048 pts = (double)ost->pts.val * ofc->pts_num / ofc->pts_den; #else pts = (double)ost->pts.val * ost->time_base.num / ost->time_base.den; #endif } #if ZM_FFMPEG_SVN AVCodecContext *c = ost->codec; #else AVCodecContext *c = &ost->codec; #endif if ( c->pix_fmt != pf ) { memcpy( tmp_opicture->data[0], buffer, buffer_size ); #ifdef HAVE_LIBSWSCALE if ( !img_convert_ctx ) { img_convert_ctx = sws_getCachedContext( NULL, c->width, c->height, pf, c->width, c->height, c->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL ); if ( !img_convert_ctx ) Panic( "Unable to initialise image scaling context" ); } sws_scale( img_convert_ctx, tmp_opicture->data, tmp_opicture->linesize, 0, c->height, opicture->data, opicture->linesize ); #else // HAVE_LIBSWSCALE Fatal("swscale is required for MPEG mode"); #endif // HAVE_LIBSWSCALE } else { memcpy( opicture->data[0], buffer, buffer_size ); } AVFrame *opicture_ptr = opicture; int ret = 0; if ( ofc->oformat->flags & AVFMT_RAWPICTURE ) { #if ZM_FFMPEG_048 ret = av_write_frame( ofc, ost->index, (uint8_t *)opicture_ptr, sizeof(AVPicture) ); #else AVPacket pkt; av_init_packet( &pkt ); #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) pkt.flags |= AV_PKT_FLAG_KEY; #else pkt.flags |= PKT_FLAG_KEY; #endif pkt.stream_index = ost->index; pkt.data = (uint8_t *)opicture_ptr; pkt.size = sizeof(AVPicture); ret = av_write_frame(ofc, &pkt); #endif } else { if ( add_timestamp ) ost->pts.val = timestamp; #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(54, 1, 0) // NEXTIME int out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, opicture_ptr); #else int out_size = encode_frame(c, opicture_ptr); #endif if ( out_size > 0 ) { #if ZM_FFMPEG_048 ret = av_write_frame(ofc, ost->index, video_outbuf, out_size); #else AVPacket pkt; av_init_packet(&pkt); #if ZM_FFMPEG_049 pkt.pts = c->coded_frame->pts; #else pkt.pts= av_rescale_q( c->coded_frame->pts, c->time_base, ost->time_base ); #endif if(c->coded_frame->key_frame) #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) pkt.flags |= AV_PKT_FLAG_KEY; #else pkt.flags |= PKT_FLAG_KEY; #endif pkt.stream_index = ost->index; pkt.data = video_outbuf; pkt.size = out_size; ret = av_write_frame( ofc, &pkt ); #endif } } if ( ret != 0 ) { Fatal( "Error %d while writing video frame: %s", ret, strerror( errno ) ); } return( pts ); } #endif // HAVE_LIBAVCODEC ZoneMinder-1.26.5/src/zm_mpeg.h000066400000000000000000000036321225361755400162730ustar00rootroot00000000000000/* * ZoneMinder MPEG Interface, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef ZM_MPEG_H #define ZM_MPEG_H #include "zm_ffmpeg.h" #if HAVE_LIBAVCODEC class VideoStream { protected: struct MimeData { const char *format; const char *mime_type; }; protected: static bool initialised; static struct MimeData mime_data[]; protected: const char *filename; const char *format; enum PixelFormat pf; AVOutputFormat *of; AVFormatContext *ofc; AVStream *ost; AVFrame *opicture; AVFrame *tmp_opicture; uint8_t *video_outbuf; int video_outbuf_size; double pts; protected: static void Initialise(); void SetupFormat( const char *p_filename, const char *format ); void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate ); void SetParameters(); public: VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height ); ~VideoStream(); const char *MimeType() const; void OpenStream(); double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 ); }; #endif // HAVE_LIBAVCODEC #endif // ZM_MPEG_H ZoneMinder-1.26.5/src/zm_poly.cpp000066400000000000000000000066561225361755400166720ustar00rootroot00000000000000// // ZoneMinder Polygon Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" #include "zm_poly.h" #include void Polygon::calcArea() { double float_area = 0.0L; for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) { double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L; float_area += trap_area; //printf( "%.2f (%.2f)\n", float_area, trap_area ); } area = (int)round(fabs(float_area)); } void Polygon::calcCentre() { if ( !area && n_coords ) calcArea(); double float_x = 0.0L, float_y = 0.0L; for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) { float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2))); float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2))); } float_x /= (6*area); float_y /= (6*area); //printf( "%.2f,%.2f\n", float_x, float_y ); centre = Coord( (int)round(float_x), (int)round(float_y) ); } Polygon::Polygon( int p_n_coords, const Coord *p_coords ) : n_coords( p_n_coords ) { coords = new Coord[n_coords]; int min_x = -1; int max_x = -1; int min_y = -1; int max_y = -1; for( int i = 0; i < n_coords; i++ ) { coords[i] = p_coords[i]; if ( min_x == -1 || coords[i].X() < min_x ) min_x = coords[i].X(); if ( max_x == -1 || coords[i].X() > max_x ) max_x = coords[i].X(); if ( min_y == -1 || coords[i].Y() < min_y ) min_y = coords[i].Y(); if ( max_y == -1 || coords[i].Y() > max_y ) max_y = coords[i].Y(); } extent = Box( min_x, min_y, max_x, max_y ); calcArea(); calcCentre(); } Polygon::Polygon( const Polygon &p_polygon ) : n_coords( p_polygon.n_coords ), extent( p_polygon.extent ), area( p_polygon.area ), centre( p_polygon.centre ) { coords = new Coord[n_coords]; for( int i = 0; i < n_coords; i++ ) { coords[i] = p_polygon.coords[i]; } } Polygon &Polygon::operator=( const Polygon &p_polygon ) { if ( n_coords < p_polygon.n_coords ) { delete[] coords; coords = new Coord[p_polygon.n_coords]; } n_coords = p_polygon.n_coords; for( int i = 0; i < n_coords; i++ ) { coords[i] = p_polygon.coords[i]; } extent = p_polygon.extent; area = p_polygon.area; centre = p_polygon.centre; return( *this ); } bool Polygon::isInside( const Coord &coord ) const { bool inside = false; for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) { if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) ) || ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y()))) && (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X())) { inside = !inside; } } return( inside ); } ZoneMinder-1.26.5/src/zm_poly.h000066400000000000000000000054541225361755400163320ustar00rootroot00000000000000// // ZoneMinder Polygon Class Interfaces, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_POLY_H #define ZM_POLY_H #include "zm.h" #include "zm_coord.h" #include "zm_box.h" #include // // Class used for storing a box, which is defined as a region // defined by two coordinates // class Polygon { protected: struct Edge { int min_y; int max_y; double min_x; double _1_m; static int CompareYX( const void *p1, const void *p2 ) { const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2; if ( e1->min_y == e2->min_y ) return( int(e1->min_x - e2->min_x) ); else return( int(e1->min_y - e2->min_y) ); } static int CompareX( const void *p1, const void *p2 ) { const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2; return( int(e1->min_x - e2->min_x) ); } }; struct Slice { int min_x; int max_x; int n_edges; int *edges; Slice() { n_edges = 0; edges = 0; } ~Slice() { delete edges; } }; protected: int n_coords; Coord *coords; Box extent; int area; Coord centre; Edge *edges; Slice *slices; protected: void initialiseEdges(); void calcArea(); void calcCentre(); public: inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 ) { } Polygon( int p_n_coords, const Coord *p_coords ); Polygon( const Polygon &p_polygon ); ~Polygon() { delete[] coords; } Polygon &operator=( const Polygon &p_polygon ); inline int getNumCoords() const { return( n_coords ); } inline const Coord &getCoord( int index ) const { return( coords[index] ); } inline const Box &Extent() const { return( extent ); } inline int LoX() const { return( extent.LoX() ); } inline int HiX() const { return( extent.HiX() ); } inline int LoY() const { return( extent.LoY() ); } inline int HiY() const { return( extent.HiY() ); } inline int Width() const { return( extent.Width() ); } inline int Height() const { return( extent.Height() ); } inline int Area() const { return( area ); } inline const Coord &Centre() const { return( centre ); } bool isInside( const Coord &coord ) const; }; #endif // ZM_POLY_H ZoneMinder-1.26.5/src/zm_regexp.cpp000066400000000000000000000066041225361755400171720ustar00rootroot00000000000000/* * ZoneMinder regular expression class implementation, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "zm.h" #include "zm_regexp.h" #if HAVE_LIBPCRE RegExpr::RegExpr( const char *pattern, int flags, int p_max_matches ) : max_matches( p_max_matches ), match_buffers( 0 ), match_lengths( 0 ), match_valid( 0 ) { const char *errstr; int erroffset = 0; if ( !(regex = pcre_compile( pattern, flags, &errstr, &erroffset, 0 )) ) { Panic( "pcre_compile(%s): %s at %d", pattern, errstr, erroffset ); } regextra = pcre_study( regex, 0, &errstr ); if ( errstr ) { Panic( "pcre_study(%s): %s", pattern, errstr ); } if ( (ok = (bool)regex) ) { match_vectors = new int[3*max_matches]; memset( match_vectors, 0, sizeof(*match_vectors)*3*max_matches ); match_buffers = new char *[max_matches]; memset( match_buffers, 0, sizeof(*match_buffers)*max_matches ); match_lengths = new int[max_matches]; memset( match_lengths, 0, sizeof(*match_lengths)*max_matches ); match_valid = new bool[max_matches]; memset( match_valid, 0, sizeof(*match_valid)*max_matches ); } n_matches = 0; } RegExpr::~RegExpr() { for ( int i = 0; i < max_matches; i++ ) { if ( match_buffers[i] ) { delete[] match_buffers[i]; } } delete[] match_valid; delete[] match_lengths; delete[] match_buffers; delete[] match_vectors; } int RegExpr::Match( const char *subject_string, int subject_length, int flags ) { match_string = subject_string; n_matches = pcre_exec( regex, regextra, subject_string, subject_length, 0, flags, match_vectors, 2*max_matches ); if ( n_matches <= 0 ) { if ( n_matches < PCRE_ERROR_NOMATCH ) { Error( "Error %d executing regular expression", n_matches ); } return( n_matches = 0 ); } for( int i = 0; i < max_matches; i++ ) { match_valid[i] = false; } return( n_matches ); } const char *RegExpr::MatchString( int match_index ) const { if ( match_index > n_matches ) { return( 0 ); } if ( !match_valid[match_index] ) { int match_len = match_vectors[(2*match_index)+1]-match_vectors[2*match_index]; if ( match_lengths[match_index] < (match_len+1) ) { delete[] match_buffers[match_index]; match_buffers[match_index] = new char[match_len+1]; match_lengths[match_index] = match_len+1; } memcpy( match_buffers[match_index], match_string+match_vectors[2*match_index], match_len ); match_buffers[match_index][match_len] = '\0'; match_valid[match_index] = true; } return( match_buffers[match_index] ); } int RegExpr::MatchLength( int match_index ) const { if ( match_index > n_matches ) { return( 0 ); } return( match_vectors[(2*match_index)+1]-match_vectors[2*match_index] ); } #endif // HAVE_LIBPCRE ZoneMinder-1.26.5/src/zm_regexp.h000066400000000000000000000033241225361755400166330ustar00rootroot00000000000000/* * ZoneMinder Regular Expression Interface, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "zm.h" #ifndef ZM_REGEXP_H #define ZM_REGEXP_H #if HAVE_LIBPCRE #if HAVE_PCRE_H #include #elif HAVE_PCRE_PCRE_H #include #else #error Unable to locate pcre.h, please do 'locate pcre.h' and report location to zoneminder.com #endif class RegExpr { protected: pcre *regex; pcre_extra *regextra; int max_matches; int *match_vectors; mutable char **match_buffers; int *match_lengths; bool *match_valid; protected: const char *match_string; int n_matches; protected: bool ok; public: RegExpr( const char *pattern, int cflags=0, int p_max_matches=32 ); ~RegExpr(); bool Ok() const { return( ok ); } int MatchCount() const { return( n_matches ); } int Match( const char *subject_string, int subject_length, int flags=0 ); const char *MatchString( int match_index ) const; int MatchLength( int match_index ) const; }; #endif // HAVE_LIBPCRE #endif // ZM_REGEXP_H ZoneMinder-1.26.5/src/zm_remote_camera.cpp000066400000000000000000000046621225361755400205050ustar00rootroot00000000000000// // ZoneMinder Remote Camera Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm_remote_camera.h" #include "zm_utils.h" RemoteCamera::RemoteCamera( int p_id, const std::string &p_protocol, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : Camera( p_id, REMOTE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), protocol( p_protocol ), host( p_host ), port( p_port ), path( p_path ), hp( 0 ) { if ( path[0] != '/' ) path = '/'+path; } RemoteCamera::~RemoteCamera() { if(hp != NULL) { freeaddrinfo(hp); hp = NULL; } } void RemoteCamera::Initialise() { if( protocol.empty() ) Fatal( "No protocol specified for remote camera" ); if( host.empty() ) Fatal( "No host specified for remote camera" ); if( port.empty() ) Fatal( "No port specified for remote camera" ); //if( path.empty() ) //Fatal( "No path specified for remote camera" ); // Cache as much as we can to speed things up std::string::size_type authIndex = host.find( '@' ); if ( authIndex != std::string::npos ) { auth = host.substr( 0, authIndex ); host.erase( 0, authIndex+1 ); auth64 = base64Encode( auth ); } struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp); if ( ret != 0 ) { Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) ); } } ZoneMinder-1.26.5/src/zm_remote_camera.h000066400000000000000000000041161225361755400201440ustar00rootroot00000000000000// // ZoneMinder Remote Camera Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_REMOTE_CAMERA_H #define ZM_REMOTE_CAMERA_H #include "zm_camera.h" #include #include #include #include // // Class representing 'remote' cameras, i.e. those which are // accessed over a network connection. // class RemoteCamera : public Camera { protected: std::string protocol; std::string host; std::string port; std::string path; std::string auth; std::string auth64; protected: struct addrinfo *hp; public: RemoteCamera( int p_id, const std::string &p_proto, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); virtual ~RemoteCamera(); const std::string &Protocol() const { return( protocol ); } const std::string &Host() const { return( host ); } const std::string &Port() const { return( port ); } const std::string &Path() const { return( path ); } const std::string &Auth() const { return( auth ); } virtual void Initialise(); virtual void Terminate() = 0; virtual int Connect() = 0; virtual int Disconnect() = 0; virtual int PreCapture() = 0; virtual int Capture( Image &image ) = 0; virtual int PostCapture() = 0; }; #endif // ZM_REMOTE_CAMERA_H ZoneMinder-1.26.5/src/zm_remote_camera_http.cpp000066400000000000000000001353061225361755400215440ustar00rootroot00000000000000// // ZoneMinder Remote Camera Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm_remote_camera_http.h" #include "zm_mem_utils.h" #include #include #include #include RemoteCameraHttp::RemoteCameraHttp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : RemoteCamera( p_id, "http", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture ) { sd = -1; timeout.tv_sec = 0; timeout.tv_usec = 0; if ( p_method == "simple" ) method = SIMPLE; else if ( p_method == "regexp" ) method = REGEXP; else Fatal( "Unrecognised method '%s' when creating HTTP camera %d", p_method.c_str(), id ); if ( capture ) { Initialise(); } } RemoteCameraHttp::~RemoteCameraHttp() { if ( capture ) { Terminate(); } } void RemoteCameraHttp::Initialise() { RemoteCamera::Initialise(); if ( request.empty() ) { request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version ); request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION ); request += stringtf( "Host: %s\r\n", host .c_str()); if ( strcmp( config.http_version, "1.0" ) == 0 ) request += stringtf( "Connection: Keep-Alive\r\n" ); if ( !auth.empty() ) request += stringtf( "Authorization: Basic %s\r\n", auth64.c_str() ); request += "\r\n"; Debug( 2, "Request: %s", request.c_str() ); } if ( !timeout.tv_sec ) { timeout.tv_sec = config.http_timeout/1000; timeout.tv_usec = (config.http_timeout%1000)*1000; } int max_size = width*height*colours; buffer.size( max_size ); mode = SINGLE_IMAGE; format = UNDEF; state = HEADER; } int RemoteCameraHttp::Connect() { struct addrinfo *p; for(p = hp; p != NULL; p = p->ai_next) { sd = socket( p->ai_family, p->ai_socktype, p->ai_protocol ); if ( sd < 0 ) { Warning("Can't create socket: %s", strerror(errno) ); continue; } if ( connect( sd, p->ai_addr, p->ai_addrlen ) < 0 ) { close(sd); sd = -1; Warning("Can't connect to remote camera: %s", strerror(errno) ); continue; } /* If we got here, we must have connected successfully */ break; } if(p == NULL) { Error("Unable to connect to the remote camera, aborting"); return( -1 ); } Debug( 3, "Connected to host, socket = %d", sd ); return( sd ); } int RemoteCameraHttp::Disconnect() { close( sd ); sd = -1; Debug( 3, "Disconnected from host" ); return( 0 ); } int RemoteCameraHttp::SendRequest() { if ( write( sd, request.data(), request.length() ) < 0 ) { Error( "Can't write: %s", strerror(errno) ); Disconnect(); return( -1 ); } format = UNDEF; state = HEADER; Debug( 3, "Request sent" ); return( 0 ); } int RemoteCameraHttp::ReadData( Buffer &buffer, int bytes_expected ) { fd_set rfds; FD_ZERO(&rfds); FD_SET(sd, &rfds); struct timeval temp_timeout = timeout; int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout ); if( n_found == 0 ) { Warning( "Select timed out" ); Disconnect(); return( 0 ); } else if ( n_found < 0) { Error( "Select error: %s", strerror(errno) ); return( -1 ); } int total_bytes_to_read = 0; if ( bytes_expected ) { total_bytes_to_read = bytes_expected; } else { if ( ioctl( sd, FIONREAD, &total_bytes_to_read ) < 0 ) { Error( "Can't ioctl(): %s", strerror(errno) ); return( -1 ); } if ( total_bytes_to_read == 0 ) { Debug( 3, "Socket closed" ); Disconnect(); return( 0 ); } } Debug( 3, "Expecting %d bytes", total_bytes_to_read ); int total_bytes_read = 0; do { static unsigned char temp_buffer[ZM_NETWORK_BUFSIZ]; int bytes_to_read = (unsigned int)total_bytes_to_read>(unsigned int)sizeof(temp_buffer)?sizeof(temp_buffer):total_bytes_to_read; int bytes_read = read( sd, temp_buffer, bytes_to_read ); if ( bytes_read < 0) { Error( "Read error: %s", strerror(errno) ); return( -1 ); } else if ( bytes_read == 0) { Debug( 3, "Socket closed" ); Disconnect(); return( 0 ); } else if ( bytes_read < bytes_to_read ) { Error( "Incomplete read, expected %d, got %d", bytes_to_read, bytes_read ); return( -1 ); } Debug( 3, "Read %d bytes", bytes_read ); buffer.append( temp_buffer, bytes_read ); total_bytes_read += bytes_read; total_bytes_to_read -= bytes_read; } while ( total_bytes_to_read ); return( total_bytes_read ); } int RemoteCameraHttp::GetResponse() { #if HAVE_LIBPCRE if ( method == REGEXP ) { const char *header = 0; int header_len = 0; const char *http_version = 0; int status_code = 0; const char *status_mesg = 0; const char *connection_type = ""; int content_length = 0; const char *content_type = ""; const char *content_boundary = ""; const char *subheader = 0; int subheader_len = 0; //int subcontent_length = 0; //const char *subcontent_type = ""; while ( true ) { switch( state ) { case HEADER : { static RegExpr *header_expr = 0; static RegExpr *status_expr = 0; static RegExpr *connection_expr = 0; static RegExpr *content_length_expr = 0; static RegExpr *content_type_expr = 0; int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { Error( "Connection dropped by remote end" ); return( 0 ); } else if ( buffer_len < 0 ) { Error( "Unable to read header data" ); return( -1 ); } if ( !header_expr ) header_expr = new RegExpr( "^(.+?\r?\n\r?\n)", PCRE_DOTALL ); if ( header_expr->Match( (char*)buffer, buffer.size() ) == 2 ) { header = header_expr->MatchString( 1 ); header_len = header_expr->MatchLength( 1 ); Debug( 4, "Captured header (%d bytes):\n'%s'", header_len, header ); if ( !status_expr ) status_expr = new RegExpr( "^HTTP/(1\\.[01]) +([0-9]+) +(.+?)\r?\n", PCRE_CASELESS ); if ( status_expr->Match( header, header_len ) < 4 ) { Error( "Unable to extract HTTP status from header" ); return( -1 ); } http_version = status_expr->MatchString( 1 ); status_code = atoi( status_expr->MatchString( 2 ) ); status_mesg = status_expr->MatchString( 3 ); if ( status_code < 200 || status_code > 299 ) { Error( "Invalid response status %d: %s", status_code, status_mesg ); return( -1 ); } Debug( 3, "Got status '%d' (%s), http version %s", status_code, status_mesg, http_version ); if ( !connection_expr ) connection_expr = new RegExpr( "Connection: ?(.+?)\r?\n", PCRE_CASELESS ); if ( connection_expr->Match( header, header_len ) == 2 ) { connection_type = connection_expr->MatchString( 1 ); Debug( 3, "Got connection '%s'", connection_type ); } if ( !content_length_expr ) content_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS ); if ( content_length_expr->Match( header, header_len ) == 2 ) { content_length = atoi( content_length_expr->MatchString( 1 ) ); Debug( 3, "Got content length '%d'", content_length ); } if ( !content_type_expr ) content_type_expr = new RegExpr( "Content-type: ?(.+?)(?:; ?boundary=(.+?))?\r?\n", PCRE_CASELESS ); if ( content_type_expr->Match( header, header_len ) >= 2 ) { content_type = content_type_expr->MatchString( 1 ); Debug( 3, "Got content type '%s'\n", content_type ); if ( content_type_expr->MatchCount() > 2 ) { content_boundary = content_type_expr->MatchString( 2 ); Debug( 3, "Got content boundary '%s'", content_boundary ); } } if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) { // Single image mode = SINGLE_IMAGE; format = JPEG; state = CONTENT; } else if ( !strcasecmp( content_type, "image/x-rgb" ) ) { // Single image mode = SINGLE_IMAGE; format = X_RGB; state = CONTENT; } else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) { // Single image mode = SINGLE_IMAGE; format = X_RGBZ; state = CONTENT; } else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) ) { // Image stream, so start processing if ( !content_boundary[0] ) { Error( "No content boundary found in header '%s'", header ); return( -1 ); } mode = MULTI_IMAGE; state = SUBHEADER; } //else if ( !strcasecmp( content_type, "video/mpeg" ) || !strcasecmp( content_type, "video/mpg" ) ) //{ //// MPEG stream, coming soon! //} else { Error( "Unrecognised content type '%s'", content_type ); return( -1 ); } buffer.consume( header_len ); } else { Debug( 3, "Unable to extract header from stream, retrying" ); //return( -1 ); } break; } case SUBHEADER : { static RegExpr *subheader_expr = 0; static RegExpr *subcontent_length_expr = 0; static RegExpr *subcontent_type_expr = 0; if ( !subheader_expr ) { char subheader_pattern[256] = ""; snprintf( subheader_pattern, sizeof(subheader_pattern), "^((?:\r?\n){0,2}?(?:--)?%s\r?\n.+?\r?\n\r?\n)", content_boundary ); subheader_expr = new RegExpr( subheader_pattern, PCRE_DOTALL ); } if ( subheader_expr->Match( (char *)buffer, (int)buffer ) == 2 ) { subheader = subheader_expr->MatchString( 1 ); subheader_len = subheader_expr->MatchLength( 1 ); Debug( 4, "Captured subheader (%d bytes):'%s'", subheader_len, subheader ); if ( !subcontent_length_expr ) subcontent_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS ); if ( subcontent_length_expr->Match( subheader, subheader_len ) == 2 ) { content_length = atoi( subcontent_length_expr->MatchString( 1 ) ); Debug( 3, "Got subcontent length '%d'", content_length ); } if ( !subcontent_type_expr ) subcontent_type_expr = new RegExpr( "Content-type: ?(.+?)\r?\n", PCRE_CASELESS ); if ( subcontent_type_expr->Match( subheader, subheader_len ) == 2 ) { content_type = subcontent_type_expr->MatchString( 1 ); Debug( 3, "Got subcontent type '%s'", content_type ); } buffer.consume( subheader_len ); state = CONTENT; } else { Debug( 3, "Unable to extract subheader from stream, retrying" ); int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { Error( "Connection dropped by remote end" ); return( 0 ); } else if ( buffer_len < 0 ) { return( -1 ); } } break; } case CONTENT : { if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) { format = JPEG; } else if ( !strcasecmp( content_type, "image/x-rgb" ) ) { format = X_RGB; } else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) { format = X_RGBZ; } else { Error( "Found unsupported content type '%s'", content_type ); return( -1 ); } if ( content_length ) { while ( (long)buffer.size() < content_length ) { int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { Error( "Connection dropped by remote end" ); return( 0 ); } else if ( buffer_len < 0 ) { Error( "Unable to read content" ); return( -1 ); } } Debug( 3, "Got end of image by length, content-length = %d", content_length ); } else { while ( !content_length ) { int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { if ( mode == MULTI_IMAGE ) { Error( "Connection dropped by remote end" ); return( 0 ); } } else if ( buffer_len < 0 ) { Error( "Unable to read content" ); return( -1 ); } static RegExpr *content_expr = 0; if ( buffer_len ) { if ( mode == MULTI_IMAGE ) { if ( !content_expr ) { char content_pattern[256] = ""; snprintf( content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary ); content_expr = new RegExpr( content_pattern, PCRE_DOTALL ); } if ( content_expr->Match( buffer, buffer.size() ) == 2 ) { content_length = content_expr->MatchLength( 1 ); Debug( 3, "Got end of image by pattern, content-length = %d", content_length ); } } } else { content_length = buffer.size(); Debug( 3, "Got end of image by closure, content-length = %d", content_length ); if ( mode == SINGLE_IMAGE ) { if ( !content_expr ) { content_expr = new RegExpr( "^(.+?)(?:\r?\n){1,2}?$", PCRE_DOTALL ); } if ( content_expr->Match( buffer, buffer.size() ) == 2 ) { content_length = content_expr->MatchLength( 1 ); Debug( 3, "Trimmed end of image, new content-length = %d", content_length ); } } } } } if ( mode == SINGLE_IMAGE ) { state = HEADER; Disconnect(); } else { state = SUBHEADER; } Debug( 3, "Returning %d (%d) bytes of captured content", content_length, buffer.size() ); return( content_length ); } case HEADERCONT : case SUBHEADERCONT : { // Ignore break; } } } } else #endif // HAVE_LIBPCRE { if ( method == REGEXP ) { Warning( "Unable to use netcam regexps as not compiled with libpcre" ); } static const char *http_match = "HTTP/"; static const char *connection_match = "Connection:"; static const char *content_length_match = "Content-length:"; static const char *content_type_match = "Content-type:"; static const char *boundary_match = "boundary="; static int http_match_len = 0; static int connection_match_len = 0; static int content_length_match_len = 0; static int content_type_match_len = 0; static int boundary_match_len = 0; if ( !http_match_len ) http_match_len = strlen( http_match ); if ( !connection_match_len ) connection_match_len = strlen( connection_match ); if ( !content_length_match_len ) content_length_match_len = strlen( content_length_match ); if ( !content_type_match_len ) content_type_match_len = strlen( content_type_match ); if ( !boundary_match_len ) boundary_match_len = strlen( boundary_match ); static int n_headers; //static char *headers[32]; static int n_subheaders; //static char *subheaders[32]; static char *http_header; static char *connection_header; static char *content_length_header; static char *content_type_header; static char *boundary_header; static char subcontent_length_header[32]; static char subcontent_type_header[64]; static char http_version[16]; static char status_code[16]; static char status_mesg[256]; static char connection_type[32]; static int content_length; static char content_type[32]; static char content_boundary[64]; static int content_boundary_len; while ( true ) { switch( state ) { case HEADER : { n_headers = 0; http_header = 0; connection_header = 0; content_length_header = 0; content_type_header = 0; http_version[0] = '\0'; status_code [0]= '\0'; status_mesg [0]= '\0'; connection_type [0]= '\0'; content_length = 0; content_type[0] = '\0'; content_boundary[0] = '\0'; content_boundary_len = 0; } case HEADERCONT : { int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { Error( "Connection dropped by remote end" ); return( 0 ); } else if ( buffer_len < 0 ) { Error( "Unable to read header" ); return( -1 ); } char *crlf = 0; char *header_ptr = (char *)buffer; int header_len = buffer.size(); bool all_headers = false; while( true ) { int crlf_len = memspn( header_ptr, "\r\n", header_len ); if ( n_headers ) { if ( (crlf_len == 2 && !strncmp( header_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( header_ptr, "\r\n\r\n", crlf_len )) ) { *header_ptr = '\0'; header_ptr += crlf_len; header_len -= buffer.consume( header_ptr-(char *)buffer ); all_headers = true; break; } } if ( crlf_len ) { if ( header_len == crlf_len ) { break; } else { *header_ptr = '\0'; header_ptr += crlf_len; header_len -= buffer.consume( header_ptr-(char *)buffer ); } } Debug( 6, "%s", header_ptr ); if ( (crlf = mempbrk( header_ptr, "\r\n", header_len )) ) { //headers[n_headers++] = header_ptr; n_headers++; if ( !http_header && (strncasecmp( header_ptr, http_match, http_match_len ) == 0) ) { http_header = header_ptr+http_match_len; Debug( 6, "Got http header '%s'", header_ptr ); } else if ( !connection_header && (strncasecmp( header_ptr, connection_match, connection_match_len) == 0) ) { connection_header = header_ptr+connection_match_len; Debug( 6, "Got connection header '%s'", header_ptr ); } else if ( !content_length_header && (strncasecmp( header_ptr, content_length_match, content_length_match_len) == 0) ) { content_length_header = header_ptr+content_length_match_len; Debug( 6, "Got content length header '%s'", header_ptr ); } else if ( !content_type_header && (strncasecmp( header_ptr, content_type_match, content_type_match_len) == 0) ) { content_type_header = header_ptr+content_type_match_len; Debug( 6, "Got content type header '%s'", header_ptr ); } else { Debug( 6, "Got ignored header '%s'", header_ptr ); } header_ptr = crlf; header_len -= buffer.consume( header_ptr-(char *)buffer ); } else { // No end of line found break; } } if ( all_headers ) { char *start_ptr, *end_ptr; if ( !http_header ) { Error( "Unable to extract HTTP status from header" ); return( -1 ); } start_ptr = http_header; end_ptr = start_ptr+strspn( start_ptr, "10." ); memset( http_version, 0, sizeof(http_version) ); strncpy( http_version, start_ptr, end_ptr-start_ptr ); start_ptr = end_ptr; start_ptr += strspn( start_ptr, " " ); end_ptr = start_ptr+strspn( start_ptr, "0123456789" ); memset( status_code, 0, sizeof(status_code) ); strncpy( status_code, start_ptr, end_ptr-start_ptr ); int status = atoi( status_code ); start_ptr = end_ptr; start_ptr += strspn( start_ptr, " " ); strcpy( status_mesg, start_ptr ); if ( status < 200 || status > 299 ) { Error( "Invalid response status %s: %s", status_code, status_mesg ); return( -1 ); } Debug( 3, "Got status '%d' (%s), http version %s", status, status_mesg, http_version ); if ( connection_header ) { memset( connection_type, 0, sizeof(connection_type) ); start_ptr = connection_header + strspn( connection_header, " " ); strcpy( connection_type, start_ptr ); Debug( 3, "Got connection '%s'", connection_type ); } if ( content_length_header ) { start_ptr = content_length_header + strspn( content_length_header, " " ); content_length = atoi( start_ptr ); Debug( 3, "Got content length '%d'", content_length ); } if ( content_type_header ) { memset( content_type, 0, sizeof(content_type) ); start_ptr = content_type_header + strspn( content_type_header, " " ); if ( (end_ptr = strchr( start_ptr, ';' )) ) { strncpy( content_type, start_ptr, end_ptr-start_ptr ); Debug( 3, "Got content type '%s'", content_type ); start_ptr = end_ptr + strspn( end_ptr, "; " ); if ( strncasecmp( start_ptr, boundary_match, boundary_match_len ) == 0 ) { start_ptr += boundary_match_len; start_ptr += strspn( start_ptr, "-" ); content_boundary_len = sprintf( content_boundary, "--%s", start_ptr ); Debug( 3, "Got content boundary '%s'", content_boundary ); } else { Error( "No content boundary found in header '%s'", content_type_header ); } } else { strcpy( content_type, start_ptr ); Debug( 3, "Got content type '%s'", content_type ); } } if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) { // Single image mode = SINGLE_IMAGE; format = JPEG; state = CONTENT; } else if ( !strcasecmp( content_type, "image/x-rgb" ) ) { // Single image mode = SINGLE_IMAGE; format = X_RGB; state = CONTENT; } else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) { // Single image mode = SINGLE_IMAGE; format = X_RGBZ; state = CONTENT; } else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) ) { // Image stream, so start processing if ( !content_boundary[0] ) { Error( "No content boundary found in header '%s'", content_type_header ); return( -1 ); } mode = MULTI_IMAGE; state = SUBHEADER; } //else if ( !strcasecmp( content_type, "video/mpeg" ) || !strcasecmp( content_type, "video/mpg" ) ) //{ //// MPEG stream, coming soon! //} else { Error( "Unrecognised content type '%s'", content_type ); return( -1 ); } } else { Debug( 3, "Unable to extract entire header from stream, continuing" ); state = HEADERCONT; //return( -1 ); } break; } case SUBHEADER : { n_subheaders = 0; boundary_header = 0; subcontent_length_header[0] = '\0'; subcontent_type_header[0] = '\0'; content_length = 0; content_type[0] = '\0'; } case SUBHEADERCONT : { char *crlf = 0; char *subheader_ptr = (char *)buffer; int subheader_len = buffer.size(); bool all_headers = false; while( true ) { int crlf_len = memspn( subheader_ptr, "\r\n", subheader_len ); if ( n_subheaders ) { if ( (crlf_len == 2 && !strncmp( subheader_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( subheader_ptr, "\r\n\r\n", crlf_len )) ) { *subheader_ptr = '\0'; subheader_ptr += crlf_len; subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); all_headers = true; break; } } if ( crlf_len ) { if ( subheader_len == crlf_len ) { break; } else { *subheader_ptr = '\0'; subheader_ptr += crlf_len; subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); } } Debug( 6, "%d: %s", subheader_len, subheader_ptr ); if ( (crlf = mempbrk( subheader_ptr, "\r\n", subheader_len )) ) { //subheaders[n_subheaders++] = subheader_ptr; n_subheaders++; if ( !boundary_header && (strncasecmp( subheader_ptr, content_boundary, content_boundary_len ) == 0) ) { boundary_header = subheader_ptr; Debug( 4, "Got boundary subheader '%s'", subheader_ptr ); } else if ( !subcontent_length_header[0] && (strncasecmp( subheader_ptr, content_length_match, content_length_match_len) == 0) ) { strncpy( subcontent_length_header, subheader_ptr+content_length_match_len, sizeof(subcontent_length_header) ); *(subcontent_length_header+strcspn( subcontent_length_header, "\r\n" )) = '\0'; Debug( 4, "Got content length subheader '%s'", subcontent_length_header ); } else if ( !subcontent_type_header[0] && (strncasecmp( subheader_ptr, content_type_match, content_type_match_len) == 0) ) { strncpy( subcontent_type_header, subheader_ptr+content_type_match_len, sizeof(subcontent_type_header) ); *(subcontent_type_header+strcspn( subcontent_type_header, "\r\n" )) = '\0'; Debug( 4, "Got content type subheader '%s'", subcontent_type_header ); } else { Debug( 6, "Got ignored subheader '%s' found", subheader_ptr ); } subheader_ptr = crlf; subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); } else { // No line end found break; } } if ( all_headers && boundary_header ) { char *start_ptr/*, *end_ptr*/; Debug( 3, "Got boundary '%s'", boundary_header ); if ( subcontent_length_header[0] ) { start_ptr = subcontent_length_header + strspn( subcontent_length_header, " " ); content_length = atoi( start_ptr ); Debug( 3, "Got subcontent length '%d'", content_length ); } if ( subcontent_type_header[0] ) { memset( content_type, 0, sizeof(content_type) ); start_ptr = subcontent_type_header + strspn( subcontent_type_header, " " ); strcpy( content_type, start_ptr ); Debug( 3, "Got subcontent type '%s'", content_type ); } state = CONTENT; } else { Debug( 3, "Unable to extract subheader from stream, retrying" ); int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { Error( "Connection dropped by remote end" ); return( 0 ); } else if ( buffer_len < 0 ) { Error( "Unable to read subheader" ); return( -1 ); } state = SUBHEADERCONT; } break; } case CONTENT : { if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) { format = JPEG; } else if ( !strcasecmp( content_type, "image/x-rgb" ) ) { format = X_RGB; } else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) { format = X_RGBZ; } else { Error( "Found unsupported content type '%s'", content_type ); return( -1 ); } if ( format == JPEG && buffer.size() >= 2 ) { if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) { Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] ); return( -1 ); } } if ( content_length ) { while ( (long)buffer.size() < content_length ) { //int buffer_len = ReadData( buffer, content_length-buffer.size() ); int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { Error( "Connection dropped by remote end" ); return( 0 ); } else if ( buffer_len < 0 ) { Error( "Unable to read content" ); return( -1 ); } } Debug( 3, "Got end of image by length, content-length = %d", content_length ); } else { int content_pos = 0; while ( !content_length ) { int buffer_len = ReadData( buffer ); if ( buffer_len == 0 ) { if ( mode == MULTI_IMAGE ) { Error( "Connection dropped by remote end" ); return( 0 ); } } else if ( buffer_len < 0 ) { Error( "Unable to read content" ); return( -1 ); } int buffer_size = buffer.size(); if ( buffer_len ) { if ( mode == MULTI_IMAGE ) { while ( char *start_ptr = (char *)memstr( (char *)buffer+content_pos, "\r\n--", buffer_size-content_pos ) ) { content_length = start_ptr - (char *)buffer; Debug( 3, "Got end of image by pattern (crlf--), content-length = %d", content_length ); break; } } } else { content_length = buffer_size; Debug( 3, "Got end of image by closure, content-length = %d", content_length ); if ( mode == SINGLE_IMAGE ) { char *end_ptr = (char *)buffer+buffer_size; while( *end_ptr == '\r' || *end_ptr == '\n' ) { content_length--; end_ptr--; } if ( end_ptr != ((char *)buffer+buffer_size) ) { Debug( 3, "Trimmed end of image, new content-length = %d", content_length ); } } } } } if ( mode == SINGLE_IMAGE ) { state = HEADER; Disconnect(); } else { state = SUBHEADER; } if ( format == JPEG && buffer.size() >= 2 ) { if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) { Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] ); return( -1 ); } } Debug( 3, "Returning %d (%d) bytes of captured content", content_length, buffer.size() ); return( content_length ); } } } } return( 0 ); } int RemoteCameraHttp::PreCapture() { if ( sd < 0 ) { Connect(); if ( sd < 0 ) { Error( "Unable to connect to camera" ); return( -1 ); } mode = SINGLE_IMAGE; buffer.clear(); } if ( mode == SINGLE_IMAGE ) { if ( SendRequest() < 0 ) { Error( "Unable to send request" ); Disconnect(); return( -1 ); } } return( 0 ); } int RemoteCameraHttp::Capture( Image &image ) { int content_length = GetResponse(); if ( content_length == 0 ) { Warning( "Unable to capture image, retrying" ); return( 1 ); } if ( content_length < 0 ) { Error( "Unable to get response" ); Disconnect(); return( -1 ); } switch( format ) { case JPEG : { if ( !image.DecodeJpeg( buffer.extract( content_length ), content_length, colours, subpixelorder ) ) { Error( "Unable to decode jpeg" ); Disconnect(); return( -1 ); } break; } case X_RGB : { if ( content_length != (long)image.Size() ) { Error( "Image length mismatch, expected %d bytes, content length was %d", image.Size(), content_length ); Disconnect(); return( -1 ); } image.Assign( width, height, colours, subpixelorder, buffer, imagesize ); break; } case X_RGBZ : { if ( !image.Unzip( buffer.extract( content_length ), content_length ) ) { Error( "Unable to unzip RGB image" ); Disconnect(); return( -1 ); } image.Assign( width, height, colours, subpixelorder, buffer, imagesize ); break; } default : { Error( "Unexpected image format encountered" ); Disconnect(); return( -1 ); } } return( 0 ); } int RemoteCameraHttp::PostCapture() { return( 0 ); } ZoneMinder-1.26.5/src/zm_remote_camera_http.h000066400000000000000000000040131225361755400211770ustar00rootroot00000000000000// // ZoneMinder Remote HTTP Camera Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_REMOTE_CAMERA_HTTP_H #define ZM_REMOTE_CAMERA_HTTP_H #include "zm_remote_camera.h" #include "zm_buffer.h" #include "zm_regexp.h" #include "zm_utils.h" // // Class representing 'remote' cameras, i.e. those which are // accessed over a network connection. // class RemoteCameraHttp : public RemoteCamera { protected: std::string request; struct timeval timeout; //struct hostent *hp; //struct sockaddr_in sa; int sd; Buffer buffer; enum { SINGLE_IMAGE, MULTI_IMAGE } mode; enum { UNDEF, JPEG, X_RGB, X_RGBZ } format; enum { HEADER, HEADERCONT, SUBHEADER, SUBHEADERCONT, CONTENT } state; enum { SIMPLE, REGEXP } method; public: RemoteCameraHttp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); ~RemoteCameraHttp(); void Initialise(); void Terminate() { Disconnect(); } int Connect(); int Disconnect(); int SendRequest(); int ReadData( Buffer &buffer, int bytes_expected=0 ); int GetResponse(); int PreCapture(); int Capture( Image &image ); int PostCapture(); }; #endif // ZM_REMOTE_CAMERA_HTTP_H ZoneMinder-1.26.5/src/zm_remote_camera_rtsp.cpp000066400000000000000000000236141225361755400215530ustar00rootroot00000000000000// // ZoneMinder Remote Camera Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" #if HAVE_LIBAVFORMAT #include "zm_remote_camera_rtsp.h" #include "zm_ffmpeg.h" #include "zm_mem_utils.h" #include #include RemoteCameraRtsp::RemoteCameraRtsp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : RemoteCamera( p_id, "rtsp", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture ), rtspThread( 0 ) { if ( p_method == "rtpUni" ) method = RtspThread::RTP_UNICAST; else if ( p_method == "rtpMulti" ) method = RtspThread::RTP_MULTICAST; else if ( p_method == "rtpRtsp" ) method = RtspThread::RTP_RTSP; else if ( p_method == "rtpRtspHttp" ) method = RtspThread::RTP_RTSP_HTTP; else Fatal( "Unrecognised method '%s' when creating RTSP camera %d", p_method.c_str(), id ); if ( capture ) { Initialise(); } mFormatContext = NULL; mVideoStreamId = -1; mCodecContext = NULL; mCodec = NULL; mRawFrame = NULL; mFrame = NULL; frameCount = 0; #if HAVE_LIBSWSCALE mConvertContext = NULL; #endif /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ if(colours == ZM_COLOUR_RGB32) { subpixelorder = ZM_SUBPIX_ORDER_RGBA; imagePixFormat = PIX_FMT_RGBA; } else if(colours == ZM_COLOUR_RGB24) { subpixelorder = ZM_SUBPIX_ORDER_RGB; imagePixFormat = PIX_FMT_RGB24; } else if(colours == ZM_COLOUR_GRAY8) { subpixelorder = ZM_SUBPIX_ORDER_NONE; imagePixFormat = PIX_FMT_GRAY8; } else { Panic("Unexpected colours: %d",colours); } } RemoteCameraRtsp::~RemoteCameraRtsp() { av_freep( &mFrame ); av_freep( &mRawFrame ); #if HAVE_LIBSWSCALE if ( mConvertContext ) { sws_freeContext( mConvertContext ); mConvertContext = NULL; } #endif if ( mCodecContext ) { avcodec_close( mCodecContext ); mCodecContext = NULL; // Freed by av_close_input_file } if ( capture ) { Terminate(); } } void RemoteCameraRtsp::Initialise() { RemoteCamera::Initialise(); int max_size = width*height*colours; buffer.size( max_size ); if ( logDebugging() ) av_log_set_level( AV_LOG_DEBUG ); else av_log_set_level( AV_LOG_QUIET ); av_register_all(); Connect(); } void RemoteCameraRtsp::Terminate() { Disconnect(); } int RemoteCameraRtsp::Connect() { rtspThread = new RtspThread( id, method, protocol, host, port, path, auth ); rtspThread->start(); return( 0 ); } int RemoteCameraRtsp::Disconnect() { if ( rtspThread ) { rtspThread->stop(); rtspThread->join(); delete rtspThread; rtspThread = 0; } return( 0 ); } int RemoteCameraRtsp::PrimeCapture() { Debug( 2, "Waiting for sources" ); for ( int i = 0; i < 100 && !rtspThread->hasSources(); i++ ) { usleep( 100000 ); } if ( !rtspThread->hasSources() ) Fatal( "No RTSP sources" ); Debug( 2, "Got sources" ); mFormatContext = rtspThread->getFormatContext(); // Find first video stream present mVideoStreamId = -1; for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ ) #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) #else if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) #endif { mVideoStreamId = i; break; } if ( mVideoStreamId == -1 ) Fatal( "Unable to locate video stream" ); // Get a pointer to the codec context for the video stream mCodecContext = mFormatContext->streams[mVideoStreamId]->codec; // Find the decoder for the video stream mCodec = avcodec_find_decoder( mCodecContext->codec_id ); if ( mCodec == NULL ) Panic( "Unable to locate codec %d decoder", mCodecContext->codec_id ); // Open codec #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 7, 0) if ( avcodec_open( mCodecContext, mCodec ) < 0 ) #else if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 ) #endif Panic( "Can't open codec" ); // Allocate space for the native video frame mRawFrame = avcodec_alloc_frame(); // Allocate space for the converted video frame mFrame = avcodec_alloc_frame(); if(mRawFrame == NULL || mFrame == NULL) Fatal( "Unable to allocate frame(s)"); int pSize = avpicture_get_size( imagePixFormat, width, height ); if( (unsigned int)pSize != imagesize) { Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); } /* #if HAVE_LIBSWSCALE if(!sws_isSupportedInput(mCodecContext->pix_fmt)) { Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff)); } if(!sws_isSupportedOutput(imagePixFormat)) { Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); } #else // HAVE_LIBSWSCALE Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" ); #endif // HAVE_LIBSWSCALE */ return( 0 ); } int RemoteCameraRtsp::PreCapture() { if ( !rtspThread->isRunning() ) return( -1 ); if ( !rtspThread->hasSources() ) { Error( "Cannot precapture, no RTP sources" ); return( -1 ); } return( 0 ); } int RemoteCameraRtsp::Capture( Image &image ) { AVPacket packet; uint8_t* directbuffer; int frameComplete = false; /* Request a writeable buffer of the target image */ directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); if(directbuffer == NULL) { Error("Failed requesting writeable buffer for the captured image."); return (-1); } while ( true ) { buffer.clear(); if ( !rtspThread->isRunning() ) return (-1); if ( rtspThread->getFrame( buffer ) ) { Debug( 3, "Read frame %d bytes", buffer.size() ); Debug( 4, "Address %p", buffer.head() ); Hexdump( 4, buffer.head(), 16 ); if ( !buffer.size() ) return( -1 ); if(mCodecContext->codec_id == CODEC_ID_H264) { // SPS and PPS frames should be saved and appended to IDR frames int nalType = (buffer.head()[3] & 0x1f); // SPS if(nalType == 7) { lastSps = buffer; continue; } // PPS else if(nalType == 8) { lastPps = buffer; continue; } // IDR else if(nalType == 5) { buffer += lastSps; buffer += lastPps; } } av_init_packet( &packet ); while ( !frameComplete && buffer.size() > 0 ) { packet.data = buffer.head(); packet.size = buffer.size(); int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ); if ( len < 0 ) { Error( "Error while decoding frame %d", frameCount ); Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() ); buffer.clear(); continue; } Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() ); //if ( buffer.size() < 400 ) //Hexdump( 0, buffer.head(), buffer.size() ); buffer -= len; } if ( frameComplete ) { Debug( 3, "Got frame %d", frameCount ); avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height); #if HAVE_LIBSWSCALE if(mConvertContext == NULL) { if(config.cpu_extensions && sseversion >= 20) { mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC | SWS_CPU_CAPS_SSE2, NULL, NULL, NULL ); } else { mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); } if(mConvertContext == NULL) Fatal( "Unable to create conversion context"); } if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 ) Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount ); #else // HAVE_LIBSWSCALE Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" ); #endif // HAVE_LIBSWSCALE frameCount++; } /* frame complete */ av_free_packet( &packet ); } /* getFrame() */ if(frameComplete) return (0); } return (0) ; } int RemoteCameraRtsp::PostCapture() { return( 0 ); } #endif // HAVE_LIBAVFORMAT ZoneMinder-1.26.5/src/zm_remote_camera_rtsp.h000066400000000000000000000044031225361755400212130ustar00rootroot00000000000000// // ZoneMinder Remote RTSP Camera Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_REMOTE_CAMERA_RTSP_H #define ZM_REMOTE_CAMERA_RTSP_H #include "zm_remote_camera.h" #include "zm_buffer.h" #include "zm_utils.h" #include "zm_rtsp.h" #include "zm_ffmpeg.h" // // Class representing 'remote' cameras, i.e. those which are // accessed over a network connection. // class RemoteCameraRtsp : public RemoteCamera { protected: struct sockaddr_in rtsp_sa; struct sockaddr_in rtcp_sa; int rtsp_sd; int rtp_sd; int rtcp_sd; Buffer buffer; Buffer lastSps; Buffer lastPps; RtspThread::RtspMethod method; RtspThread *rtspThread; int frameCount; #if HAVE_LIBAVFORMAT AVFormatContext *mFormatContext; int mVideoStreamId; AVCodecContext *mCodecContext; AVCodec *mCodec; AVFrame *mRawFrame; AVFrame *mFrame; PixelFormat imagePixFormat; #endif // HAVE_LIBAVFORMAT #if HAVE_LIBSWSCALE struct SwsContext *mConvertContext; #endif public: RemoteCameraRtsp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); ~RemoteCameraRtsp(); void Initialise(); void Terminate(); int Connect(); int Disconnect(); int PrimeCapture(); int PreCapture(); int Capture( Image &image ); int PostCapture(); }; #endif // ZM_REMOTE_CAMERA_RTSP_H ZoneMinder-1.26.5/src/zm_rgb.h000066400000000000000000000124231225361755400161130ustar00rootroot00000000000000// // ZoneMinder RGB Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_RGB_H #define ZM_RGB_H typedef uint32_t Rgb; // RGB colour type #define WHITE 0xff #define WHITE_R 0xff #define WHITE_G 0xff #define WHITE_B 0xff #define BLACK 0x00 #define BLACK_R 0x00 #define BLACK_G 0x00 #define BLACK_B 0x00 #define RGB_WHITE (0x00ffffff) #define RGB_BLACK (0x00000000) #define RGB_RED (0x000000ff) #define RGB_GREEN (0x0000ff00) #define RGB_BLUE (0x00ff0000) #define RGB_ORANGE (0x0000a5ff) #define RGB_PURPLE (0x00800080) #define RGB_TRANSPARENT (0x01000000) #define RGB_VAL(v,c) (((v)>>(16-((c)*8)))&0xff) /* RGB or RGBA macros */ #define BLUE_VAL_RGBA(v) (((v)>>16)&0xff) #define GREEN_VAL_RGBA(v) (((v)>>8)&0xff) #define RED_VAL_RGBA(v) ((v)&0xff) #define ALPHA_VAL_RGBA(v) ((v)>>24)&0xff) #define RED_PTR_RGBA(ptr) (*((uint8_t*)ptr)) #define GREEN_PTR_RGBA(ptr) (*((uint8_t*)ptr+1)) #define BLUE_PTR_RGBA(ptr) (*((uint8_t*)ptr+2)) #define ALPHA_PTR_RGBA(ptr) (*((uint8_t*)ptr+3)) /* BGR or BGRA */ #define RED_VAL_BGRA(v) (((v)>>16)&0xff) #define GREEN_VAL_BGRA(v) (((v)>>8)&0xff) #define BLUE_VAL_BGRA(v) ((v)&0xff) #define ALPHA_VAL_BGRA(v) ((v)>>24)&0xff) #define RED_PTR_BGRA(ptr) (*((uint8_t*)ptr+2)) #define GREEN_PTR_BGRA(ptr) (*((uint8_t*)ptr+1)) #define BLUE_PTR_BGRA(ptr) (*((uint8_t*)ptr)) #define ALPHA_PTR_BGRA(ptr) (*((uint8_t*)ptr+3)) /* ARGB */ #define BLUE_VAL_ARGB(v) (((v)>>24)&0xff) #define GREEN_VAL_ARGB(v) (((v)>>16)&0xff) #define RED_VAL_ARGB(v) (((v)>>8)&0xff) #define ALPHA_VAL_ARGB(v) ((v)&0xff) #define RED_PTR_ARGB(ptr) (*((uint8_t*)ptr+1)) #define GREEN_PTR_ARGB(ptr) (*((uint8_t*)ptr+2)) #define BLUE_PTR_ARGB(ptr) (*((uint8_t*)ptr+3)) #define ALPHA_PTR_ARGB(ptr) (*((uint8_t*)ptr)) /* ABGR */ #define BLUE_VAL_ABGR(v) (((v)>>8)&0xff) #define GREEN_VAL_ABGR(v) (((v)>>16)&0xff) #define RED_VAL_ABGR(v) (((v)>>24)&0xff) #define ALPHA_VAL_ABGR(v) ((v)&0xff) #define RED_PTR_ABGR(ptr) (*((uint8_t*)ptr+3)) #define GREEN_PTR_ABGR(ptr) (*((uint8_t*)ptr+2)) #define BLUE_PTR_ABGR(ptr) (*((uint8_t*)ptr+1)) #define ALPHA_PTR_ABGR(ptr) (*((uint8_t*)ptr)) #define RGBA_BGRA_ZEROALPHA(v) ((v)&0x00ffffff) #define ARGB_ABGR_ZEROALPHA(v) ((v)&0xffffff00) /* ITU-R BT.709: Y = (0.2126 * R) + (0.7152 * G) + (0.0722 * B) */ /* ITU-R BT.601: Y = (0.299 * R) + (0.587 * G) + (0.114 * B) */ /* The formulas below produce an almost identical result to the weighted algorithms from the ITU-R BT.601 standard and the newer ITU-R BT.709 standard, but a lot faster */ // #define RGB_FASTLUM_SINGLE_ITU709(v) ((RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3) // #define RGB_FASTLUM_VALUES_ITU709(ra,ga,ba) (((ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga)+(ga))>>3) // #define RGB_FASTLUM_SINGLE_ITU601(v) ((RED(v)+RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3) // #define RGB_FASTLUM_VALUES_ITU601(ra,ga,ba) (((ra)+(ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga))>>3) /* ZM colours */ #define ZM_COLOUR_RGB32 4 #define ZM_COLOUR_RGB24 3 #define ZM_COLOUR_GRAY8 1 /* Subpixel ordering */ /* Based on byte order naming. For example, for ARGB (on both little endian or big endian) byte+0 should be alpha, byte+1 should be red, and so on. */ #define ZM_SUBPIX_ORDER_NONE 2 #define ZM_SUBPIX_ORDER_RGB 6 #define ZM_SUBPIX_ORDER_BGR 5 #define ZM_SUBPIX_ORDER_BGRA 7 #define ZM_SUBPIX_ORDER_RGBA 8 #define ZM_SUBPIX_ORDER_ABGR 9 #define ZM_SUBPIX_ORDER_ARGB 10 /* A macro to use default subpixel order for a specified colour. */ /* for grayscale it will use NONE, for 3 colours it will use R,G,B, for 4 colours it will use R,G,B,A */ #define ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(c) ((c)<<1) /* Convert RGB colour value into BGR\ARGB\ABGR */ inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) { Rgb result; switch(p_subpixorder) { case ZM_SUBPIX_ORDER_BGR: case ZM_SUBPIX_ORDER_BGRA: { BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col); GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col); RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col); } break; case ZM_SUBPIX_ORDER_ARGB: { BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col); GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col); RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col); } break; case ZM_SUBPIX_ORDER_ABGR: { BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col); GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col); RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col); } break; /* Grayscale */ case ZM_SUBPIX_ORDER_NONE: result = p_col & 0xff; break; default: return p_col; break; } return result; } #endif // ZM_RGB_H ZoneMinder-1.26.5/src/zm_rtp.cpp000066400000000000000000000015411225361755400165000ustar00rootroot00000000000000// // ZoneMinder RTP/RTCP Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm_rtp.h" // Blank ZoneMinder-1.26.5/src/zm_rtp.h000066400000000000000000000016351225361755400161510ustar00rootroot00000000000000// // ZoneMinder RTP/RTCP Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_RTP_H #define ZM_RTP_H #include "zm.h" #define RTP_VERSION 2 #endif // ZM_RTP_H ZoneMinder-1.26.5/src/zm_rtp_ctrl.cpp000066400000000000000000000303421225361755400175250ustar00rootroot00000000000000// // ZoneMinder RTCP Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" #if HAVE_LIBAVFORMAT #include "zm_rtp_ctrl.h" #include "zm_time.h" #include "zm_rtsp.h" #include RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ) : mRtspThread( rtspThread ), mRtpSource( rtpSource ), mStop( false ) { } int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) { const RtcpPacket *rtcpPacket; rtcpPacket = (RtcpPacket *)packet; int consumed = 0; //printf( "C: " ); //for ( int i = 0; i < packetLen; i++ ) //printf( "%02x ", (unsigned char)packet[i] ); //printf( "\n" ); int ver = rtcpPacket->header.version; int count = rtcpPacket->header.count; int pt = rtcpPacket->header.pt; int len = ntohs(rtcpPacket->header.lenN); Debug( 5, "RTCP Ver: %d", ver ); Debug( 5, "RTCP Count: %d", count ); Debug( 5, "RTCP Pt: %d", pt ); Debug( 5, "RTCP len: %d", len ); switch( pt ) { case RTCP_SR : { uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN); Debug( 5, "RTCP Got SR (%x)", ssrc ); if ( mRtpSource.getSsrc() ) { if ( ssrc != mRtpSource.getSsrc() ) { Warning( "Discarding packet for unrecognised ssrc %x", ssrc ); return( -1 ); } } else if ( ssrc ) { mRtpSource.setSsrc( ssrc ); } if ( len > 1 ) { //printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts ); uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN); uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN); //printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts ); //printf( "Pkts:$sendpkts, Octs:$sendocts\n" ); uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN); mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime ); } break; } case RTCP_SDES : { ssize_t contentLen = packetLen - sizeof(rtcpPacket->header); while ( contentLen ) { Debug( 5, "RTCP CL: %zd", contentLen ); uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN); Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count ); if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) { Warning( "Discarding packet for unrecognised ssrc %x", ssrc ); return( -1 ); } unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item; for ( int i = 0; i < count; i++ ) { RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr; Debug( 5, "RTCP Item length %d", item->len ); switch( item->type ) { case RTCP_SDES_CNAME : { std::string cname( item->data, item->len ); Debug( 5, "RTCP Got CNAME %s", cname.c_str() ); break; } case RTCP_SDES_END : case RTCP_SDES_NAME : case RTCP_SDES_EMAIL : case RTCP_SDES_PHONE : case RTCP_SDES_LOC : case RTCP_SDES_TOOL : case RTCP_SDES_NOTE : case RTCP_SDES_PRIV : default : { Error( "Received unexpected SDES item type %d, ignoring", item->type ); return( -1 ); } } int paddedLen = 4+2+item->len+1; // Add null byte paddedLen = (((paddedLen-1)/4)+1)*4; Debug( 5, "RTCP PL:%d", paddedLen ); sdesPtr += paddedLen; contentLen -= paddedLen; } } break; } case RTCP_BYE : { Debug( 5, "RTCP Got BYE" ); mStop = true; break; } case RTCP_APP : { // Ignoring as per RFC 3550 Debug( 5, "Received RTCP_APP packet, ignoring."); break; } case RTCP_RR : default : { Error( "Received unexpected packet type %d, ignoring", pt ); return( -1 ); } } consumed = sizeof(uint32_t)*(len+1); return( consumed ); } int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen ) { RtcpPacket *rtcpPacket = (RtcpPacket *)packet; int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]); int wordLen = ((byteLen-1)/sizeof(uint32_t))+1; rtcpPacket->header.version = RTP_VERSION; rtcpPacket->header.p = 0; rtcpPacket->header.pt = RTCP_RR; rtcpPacket->header.count = 1; rtcpPacket->header.lenN = htons(wordLen-1); mRtpSource.updateRtcpStats(); Debug( 5, "Ssrc = %d", mRtspThread.getSsrc() ); Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() ); Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() ); Debug( 5, "Jitter = %d", mRtpSource.getJitter() ); Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() ); rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()); rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc()); rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets(); rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction(); rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq()); rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter()); rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp()); rtcpPacket->body.rr.rr[0].dlsrN = 0; return( wordLen*sizeof(uint32_t) ); } int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen ) { RtcpPacket *rtcpPacket = (RtcpPacket *)packet; const std::string &cname = mRtpSource.getCname(); int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.sdes)+sizeof(rtcpPacket->body.sdes.item[0])+cname.size(); int wordLen = ((byteLen-1)/sizeof(uint32_t))+1; rtcpPacket->header.version = RTP_VERSION; rtcpPacket->header.p = 0; rtcpPacket->header.pt = RTCP_SDES; rtcpPacket->header.count = 1; rtcpPacket->header.lenN = htons(wordLen-1); rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()); rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME; rtcpPacket->body.sdes.item[0].len = cname.size(); memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() ); return( wordLen*sizeof(uint32_t) ); } int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen ) { RtcpPacket *rtcpPacket = (RtcpPacket *)packet; int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]); int wordLen = ((byteLen-1)/sizeof(uint32_t))+1; rtcpPacket->header.version = RTP_VERSION; rtcpPacket->header.p = 0; rtcpPacket->header.pt = RTCP_BYE; rtcpPacket->header.count = 1; rtcpPacket->header.lenN = htons(wordLen-1); rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc()); return( wordLen*sizeof(uint32_t) ); } int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes ) { unsigned char *bufferPtr = buffer; // u_int32 len; /* length of compound RTCP packet in words */ // rtcp_t *r; /* RTCP header */ // rtcp_t *end; /* end of compound RTCP packet */ // if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) { // /* something wrong with packet format */ // } // end = (rtcp_t *)((u_int32 *)r + len); // do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1); // while (r < end && r->common.version == 2); // if (r != end) { // /* something wrong with packet format */ // } while ( nBytes > 0 ) { int consumed = recvPacket( bufferPtr, nBytes ); if ( consumed <= 0 ) break; bufferPtr += consumed; nBytes -= consumed; } return( nBytes ); } int RtpCtrlThread::run() { Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() ); SockAddrInet localAddr, remoteAddr; bool sendReports; UdpInetSocket rtpCtrlServer; if ( mRtpSource.getLocalHost() != "" ) { localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort(), "udp" ); if ( !rtpCtrlServer.bind( localAddr ) ) Fatal( "Failed to bind RTCP server" ); sendReports = false; Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); } else { localAddr.resolve( mRtpSource.getLocalCtrlPort(), "udp" ); if ( !rtpCtrlServer.bind( localAddr ) ) Fatal( "Failed to bind RTCP server" ); Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); remoteAddr.resolve( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort(), "udp" ); if ( !rtpCtrlServer.connect( remoteAddr ) ) Fatal( "Failed to connect RTCP server" ); Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() ); sendReports = true; } Select select( 10 ); select.addReader( &rtpCtrlServer ); unsigned char buffer[ZM_NETWORK_BUFSIZ]; while ( !mStop && select.wait() >= 0 ) { if ( mStop ) break; Select::CommsList readable = select.getReadable(); if ( readable.size() == 0 ) { Error( "RTCP timed out" ); break; } for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ ) { if ( UdpInetSocket *socket = dynamic_cast(*iter) ) { ssize_t nBytes = socket->recv( buffer, sizeof(buffer) ); Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() ); if ( nBytes ) { recvPackets( buffer, nBytes ); if ( sendReports ) { unsigned char *bufferPtr = buffer; bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); Debug( 4, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() ); if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 ) Error( "Unable to send: %s", strerror( errno ) ); //Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() ); } } else { mStop = true; break; } } else { Panic( "Barfed" ); } } } rtpCtrlServer.close(); mRtspThread.stop(); return( 0 ); } #endif // HAVE_LIBAVFORMAT ZoneMinder-1.26.5/src/zm_rtp_ctrl.h000066400000000000000000000114151225361755400171720ustar00rootroot00000000000000// // ZoneMinder RTCP Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_RTP_CTRL_H #define ZM_RTP_CTRL_H #include "zm_rtp.h" #include "zm_comms.h" #include "zm_thread.h" // Defined in ffmpeg rtp.h //#define RTP_MAX_SDES 255 // maximum text length for SDES // Big-endian mask for version, padding bit and packet type pair #define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe) #define RTCP_VALID_VALUE ((RTP_VERSION << 14) | RTCP_SR) class RtspThread; class RtpSource; class RtpCtrlThread : public Thread { friend class RtspThread; private: typedef enum { RTCP_SR = 200, RTCP_RR = 201, RTCP_SDES = 202, RTCP_BYE = 203, RTCP_APP = 204 } RtcpType; typedef enum { RTCP_SDES_END = 0, RTCP_SDES_CNAME = 1, RTCP_SDES_NAME = 2, RTCP_SDES_EMAIL = 3, RTCP_SDES_PHONE = 4, RTCP_SDES_LOC = 5, RTCP_SDES_TOOL = 6, RTCP_SDES_NOTE = 7, RTCP_SDES_PRIV = 8 } RtcpSdesType; struct RtcpCommonHeader { uint8_t count:5; // varies by packet type uint8_t p:1; // padding flag uint8_t version:2; // protocol version uint8_t pt; // RTCP packet type uint16_t lenN; // pkt len in words, w/o this word, network order }; // Reception report block struct RtcpRr { uint32_t ssrcN; // data source being reported int32_t lost:24; // cumul. no. pkts lost (signed!) uint32_t fraction:8; // fraction lost since last SR/RR uint32_t lastSeqN; // extended last seq. no. received, network order uint32_t jitterN; // interarrival jitter, network order uint32_t lsrN; // last SR packet from this source, network order uint32_t dlsrN; // delay since last SR packet, network order }; // SDES item struct RtcpSdesItem { uint8_t type; // type of item (rtcp_sdes_type_t) uint8_t len; // length of item (in octets) char data[]; // text, not null-terminated }; // RTCP packet struct RtcpPacket { RtcpCommonHeader header; // common header union { // Sender Report (SR) struct Sr { uint32_t ssrcN; // sender generating this report, network order uint32_t ntpSecN; // NTP timestamp, network order uint32_t ntpFracN; uint32_t rtpTsN; // RTP timestamp, network order uint32_t pSentN; // packets sent, network order uint32_t oSentN; // octets sent, network order RtcpRr rr[]; // variable-length list } sr; // Reception Report (RR) struct Rr { uint32_t ssrcN; // receiver generating this report RtcpRr rr[]; // variable-length list } rr; // source description (SDES) struct Sdes { uint32_t srcN; // first SSRC/CSRC RtcpSdesItem item[]; // list of SDES items } sdes; // BYE struct Bye { uint32_t srcN[]; // list of sources // can't express trailing text for reason (what does this mean? it's not even english!) } bye; } body; }; private: RtspThread &mRtspThread; RtpSource &mRtpSource; int mPort; bool mStop; private: int recvPacket( const unsigned char *packet, ssize_t packetLen ); int generateRr( const unsigned char *packet, ssize_t packetLen ); int generateSdes( const unsigned char *packet, ssize_t packetLen ); int generateBye( const unsigned char *packet, ssize_t packetLen ); int recvPackets( unsigned char *buffer, ssize_t nBytes ); int run(); public: RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ); void stop() { mStop = true; } }; #endif // ZM_RTP_CTRL_H ZoneMinder-1.26.5/src/zm_rtp_data.cpp000066400000000000000000000074741225361755400175040ustar00rootroot00000000000000// // ZoneMinder RTP Data Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" #if HAVE_LIBAVFORMAT #include "zm_rtp_data.h" #include "zm_rtsp.h" #include RtpDataThread::RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource ) : mRtspThread( rtspThread ), mRtpSource( rtpSource ), mStop( false ) { } bool RtpDataThread::recvPacket( const unsigned char *packet, size_t packetLen ) { const RtpDataHeader *rtpHeader; rtpHeader = (RtpDataHeader *)packet; //printf( "D: " ); //for ( int i = 0; i < 32; i++ ) //printf( "%02x ", (unsigned char)packet[i] ); //printf( "\n" ); Debug( 5, "Ver: %d", rtpHeader->version ); Debug( 5, "P: %d", rtpHeader->p ); Debug( 5, "Pt: %d", rtpHeader->pt ); Debug( 5, "Mk: %d", rtpHeader->m ); Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) ); Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) ); Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) ); //unsigned short seq = ntohs(rtpHeader->seqN); unsigned long ssrc = ntohl(rtpHeader->ssrcN); if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) { Warning( "Discarding packet for unrecognised ssrc %lx", ssrc ); return( false ); } return( mRtpSource.handlePacket( packet, packetLen ) ); } int RtpDataThread::run() { Debug( 2, "Starting data thread %d on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalDataPort() ); SockAddrInet localAddr; UdpInetServer rtpDataSocket; if ( mRtpSource.getLocalHost() != "" ) localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" ); else localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" ); if ( !rtpDataSocket.bind( localAddr ) ) Fatal( "Failed to bind RTP server" ); Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() ); Select select( 3 ); select.addReader( &rtpDataSocket ); unsigned char buffer[ZM_NETWORK_BUFSIZ]; while ( !mStop && select.wait() >= 0 ) { if ( mStop ) break; Select::CommsList readable = select.getReadable(); if ( readable.size() == 0 ) { Error( "RTP timed out" ); mStop = true; break; } for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ ) { if ( UdpInetServer *socket = dynamic_cast(*iter) ) { int nBytes = socket->recv( buffer, sizeof(buffer) ); Debug( 4, "Got %d bytes on sd %d", nBytes, socket->getReadDesc() ); if ( nBytes ) { recvPacket( buffer, nBytes ); } else { mStop = true; break; } } else { Panic( "Barfed" ); } } } rtpDataSocket.close(); mRtspThread.stop(); return( 0 ); } #endif // HAVE_LIBAVFORMAT ZoneMinder-1.26.5/src/zm_rtp_data.h000066400000000000000000000035431225361755400171420ustar00rootroot00000000000000// // ZoneMinder RTP Data Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_RTP_DATA_H #define ZM_RTP_DATA_H #include "zm_thread.h" #include "zm_buffer.h" #include class RtspThread; class RtpSource; struct RtpDataHeader { uint8_t cc:4; // CSRC count uint8_t x:1; // header extension flag uint8_t p:1; // padding flag uint8_t version:2; // protocol version uint8_t pt:7; // payload type uint8_t m:1; // marker bit uint16_t seqN; // sequence number, network order uint32_t timestampN; // timestamp, network order uint32_t ssrcN; // synchronization source, network order uint32_t csrc[]; // optional CSRC list }; class RtpDataThread : public Thread { friend class RtspThread; private: RtspThread &mRtspThread; RtpSource &mRtpSource; bool mStop; private: bool recvPacket( const unsigned char *packet, size_t packetLen ); int run(); public: RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource ); void stop() { mStop = true; } }; #endif // ZM_RTP_DATA_H ZoneMinder-1.26.5/src/zm_rtp_source.cpp000066400000000000000000000277041225361755400200710ustar00rootroot00000000000000// // ZoneMinder RTP Source Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm_rtp_source.h" #include "zm_time.h" #include "zm_rtp_data.h" #include #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,25,0) #define _AVCODECID AVCodecID #else #define _AVCODECID CodecID #endif RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ) : mId( id ), mSsrc( ssrc ), mLocalHost( localHost ), mRemoteHost( remoteHost ), mRtpClock( rtpClock ), mCodecId( codecId ), mFrame( 65536 ), mFrameCount( 0 ), mFrameGood( true ), mFrameReady( false ), mFrameProcessed( false ) { char hostname[256] = ""; gethostname( hostname, sizeof(hostname) ); mCname = stringtf( "zm-%d@%s", mId, hostname ); Debug( 3, "RTP CName = %s", mCname.c_str() ); init( seq ); mMaxSeq = seq - 1; mProbation = MIN_SEQUENTIAL; mLocalPortChans[0] = localPortBase; mLocalPortChans[1] = localPortBase+1; mRemotePortChans[0] = remotePortBase; mRemotePortChans[1] = remotePortBase+1; mRtpFactor = mRtpClock; mBaseTimeReal = tvNow(); mBaseTimeNtp = tvZero(); mBaseTimeRtp = rtpTime; mLastSrTimeReal = tvZero(); mLastSrTimeNtp = tvZero(); mLastSrTimeRtp = 0; if(mCodecId != CODEC_ID_H264 && mCodecId != CODEC_ID_MPEG4) Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." ); } void RtpSource::init( uint16_t seq ) { Debug( 3, "Initialising sequence" ); mBaseSeq = seq; mMaxSeq = seq; mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false mCycles = 0; mReceivedPackets = 0; mReceivedPrior = 0; mExpectedPrior = 0; // other initialization mJitter = 0; mTransit = 0; } bool RtpSource::updateSeq( uint16_t seq ) { uint16_t uDelta = seq - mMaxSeq; // Source is not valid until MIN_SEQUENTIAL packets with // sequential sequence numbers have been received. Debug( 5, "Seq: %d", seq ); if ( mProbation) { // packet is in sequence if ( seq == mMaxSeq + 1) { Debug( 3, "Sequence in probation %d, in sequence", mProbation ); mProbation--; mMaxSeq = seq; if ( mProbation == 0 ) { init( seq ); mReceivedPackets++; return( true ); } } else { Warning( "Sequence in probation %d, out of sequence", mProbation ); mProbation = MIN_SEQUENTIAL - 1; mMaxSeq = seq; return( false ); } return( true ); } else if ( uDelta < MAX_DROPOUT ) { if ( uDelta == 1 ) { Debug( 3, "Packet in sequence, gap %d", uDelta ); } else { Warning( "Packet in sequence, gap %d", uDelta ); } // in order, with permissible gap if ( seq < mMaxSeq ) { // Sequence number wrapped - count another 64K cycle. mCycles += RTP_SEQ_MOD; } mMaxSeq = seq; } else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER ) { Warning( "Packet out of sequence, gap %d", uDelta ); // the sequence number made a very large jump if ( seq == mBadSeq ) { Debug( 3, "Restarting sequence" ); // Two sequential packets -- assume that the other side // restarted without telling us so just re-sync // (i.e., pretend this was the first packet). init( seq ); } else { mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1); return( false ); } } else { Warning( "Packet duplicate or reordered, gap %d", uDelta ); // duplicate or reordered packet return( false ); } mReceivedPackets++; return( uDelta==1?true:false ); } void RtpSource::updateJitter( const RtpDataHeader *header ) { if ( mRtpFactor > 0 ) { Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) ); uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor ); Debug( 5, "Local RTP time = %x", localTimeRtp ); Debug( 5, "Packet RTP time = %x", ntohl(header->timestampN) ); uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN); Debug( 5, "Packet transit RTP time = %x", packetTransit ); if ( mTransit > 0 ) { // Jitter int d = packetTransit - mTransit; Debug( 5, "Jitter D = %d", d ); if ( d < 0 ) d = -d; //mJitter += (1./16.) * ((double)d - mJitter); mJitter += d - ((mJitter + 8) >> 4); } mTransit = packetTransit; } else { mJitter = 0; } Debug( 5, "RTP Jitter: %d", mJitter ); } void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ) { struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) ); Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime ); if ( mBaseTimeNtp.tv_sec == 0 ) { mBaseTimeReal = tvNow(); mBaseTimeNtp = ntpTime; mBaseTimeRtp = rtpTime; } else if ( !mRtpClock ) { Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime ); Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime ); double diffNtpTime = tvDiffSec( mBaseTimeNtp, ntpTime ); uint32_t diffRtpTime = rtpTime - mBaseTimeRtp; //Debug( 5, "Real-diff: %.6f", diffRealTime ); Debug( 5, "NTP-diff: %.6f", diffNtpTime ); Debug( 5, "RTP-diff: %d", diffRtpTime ); mRtpFactor = (uint32_t)(diffRtpTime / diffNtpTime); Debug( 5, "RTPfactor: %d", mRtpFactor ); } mLastSrTimeNtpSecs = ntpTimeSecs; mLastSrTimeNtpFrac = ntpTimeFrac; mLastSrTimeNtp = ntpTime; mLastSrTimeRtp = rtpTime; } void RtpSource::updateRtcpStats() { uint32_t extendedMax = mCycles + mMaxSeq; mExpectedPackets = extendedMax - mBaseSeq + 1; Debug( 5, "Expected packets = %d", mExpectedPackets ); // The number of packets lost is defined to be the number of packets // expected less the number of packets actually received: mLostPackets = mExpectedPackets - mReceivedPackets; Debug( 5, "Lost packets = %d", mLostPackets ); uint32_t expectedInterval = mExpectedPackets - mExpectedPrior; Debug( 5, "Expected interval = %d", expectedInterval ); mExpectedPrior = mExpectedPackets; uint32_t receivedInterval = mReceivedPackets - mReceivedPrior; Debug( 5, "Received interval = %d", receivedInterval ); mReceivedPrior = mReceivedPackets; uint32_t lostInterval = expectedInterval - receivedInterval; Debug( 5, "Lost interval = %d", lostInterval ); if ( expectedInterval == 0 || lostInterval <= 0 ) mLostFraction = 0; else mLostFraction = (lostInterval << 8) / expectedInterval; Debug( 5, "Lost fraction = %d", mLostFraction ); } bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) { const RtpDataHeader *rtpHeader; rtpHeader = (RtpDataHeader *)packet; int rtpHeaderSize = 12 + rtpHeader->cc * 4; // No need to check for nal type as non fragmented packets already have 001 start sequence appended bool h264FragmentEnd = (mCodecId == CODEC_ID_H264) && (packet[rtpHeaderSize+1] & 0x40); bool thisM = rtpHeader->m || h264FragmentEnd; if ( updateSeq( ntohs(rtpHeader->seqN) ) ) { Hexdump( 4, packet+rtpHeaderSize, 16 ); if ( mFrameGood ) { int extraHeader = 0; if( mCodecId == CODEC_ID_H264 ) { int nalType = (packet[rtpHeaderSize] & 0x1f); switch (nalType) { case 24: { extraHeader = 2; break; } case 25: case 26: case 27: { extraHeader = 3; break; } // FU-A and FU-B case 28: case 29: { // Is this NAL the first NAL in fragmentation sequence if ( packet[rtpHeaderSize+1] & 0x80 ) { // Now we will form new header of frame mFrame.append( "\x0\x0\x1\x0", 4 ); // Reconstruct NAL header from FU headers *(mFrame+3) = (packet[rtpHeaderSize+1] & 0x1f) | (packet[rtpHeaderSize] & 0xe0); } extraHeader = 2; break; } } // Append NAL frame start code if ( !mFrame.size() ) mFrame.append( "\x0\x0\x1", 3 ); } mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader ); } Hexdump( 4, mFrame.head(), 16 ); if ( thisM ) { if ( mFrameGood ) { Debug( 2, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() ); mFrameProcessed.setValueImmediate( false ); mFrameReady.updateValueSignal( true ); if ( !mFrameProcessed.getValueImmediate() ) { for ( int count = 0; !mFrameProcessed.getUpdatedValue( 1 ); count++ ) if( count > 1 ) return( false ); } mFrameCount++; } else { Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() ); } mFrame.clear(); } } else { if ( mFrame.size() ) { Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() ); } else { Warning( "Discarding frame %d", mFrameCount ); } mFrameGood = false; mFrame.clear(); } if ( thisM ) { mFrameGood = true; prevM = true; } else prevM = false; updateJitter( rtpHeader ); return( true ); } bool RtpSource::getFrame( Buffer &buffer ) { Debug( 3, "Getting frame" ); if ( !mFrameReady.getValueImmediate() ) { // Allow for a couple of spurious returns for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ ) if ( count > 1 ) return( false ); } buffer = mFrame; mFrameReady.setValueImmediate( false ); mFrameProcessed.updateValueSignal( true ); Debug( 3, "Copied %d bytes", buffer.size() ); return( true ); } #undef _AVCODECIDZoneMinder-1.26.5/src/zm_rtp_source.h000066400000000000000000000115051225361755400175260ustar00rootroot00000000000000// // ZoneMinder RTP Source Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_RTP_SOURCE_H #define ZM_RTP_SOURCE_H #include "zm_buffer.h" #include "zm_ffmpeg.h" #include "zm_thread.h" #include #include #include #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,25,0) #define _AVCODECID AVCodecID #else #define _AVCODECID CodecID #endif struct RtpDataHeader; class RtpSource { public: typedef enum { EMPTY, FILLING, READY } FrameState; private: static const int RTP_SEQ_MOD = 1<<16; static const int MAX_DROPOUT = 3000; static const int MAX_MISORDER = 100; static const int MIN_SEQUENTIAL = 2; private: // Identity int mId; // General id (usually monitor id) std::string mCname; // Canonical name, for SDES // RTP/RTCP fields uint32_t mSsrc; uint16_t mMaxSeq; // highest seq. number seen uint32_t mCycles; // shifted count of seq. number cycles uint32_t mBaseSeq; // base seq number uint32_t mBadSeq; // last 'bad' seq number + 1 uint32_t mProbation; // sequ. packets till source is valid uint32_t mReceivedPackets; // packets received uint32_t mExpectedPrior; // packet expected at last interval uint32_t mReceivedPrior; // packet received at last interval uint32_t mTransit; // relative trans time for prev pkt uint32_t mJitter; // estimated jitter // Ports/Channels std::string mLocalHost; int mLocalPortChans[2]; std::string mRemoteHost; int mRemotePortChans[2]; // Time keys uint32_t mRtpClock; uint32_t mRtpFactor; struct timeval mBaseTimeReal; struct timeval mBaseTimeNtp; uint32_t mBaseTimeRtp; struct timeval mLastSrTimeReal; uint32_t mLastSrTimeNtpSecs; uint32_t mLastSrTimeNtpFrac; struct timeval mLastSrTimeNtp; uint32_t mLastSrTimeRtp; // Stats, intermittently updated uint32_t mExpectedPackets; uint32_t mLostPackets; uint8_t mLostFraction; _AVCODECID mCodecId; Buffer mFrame; int mFrameCount; bool mFrameGood; bool prevM; ThreadData mFrameReady; ThreadData mFrameProcessed; private: void init( uint16_t seq ); public: RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ); bool updateSeq( uint16_t seq ); void updateJitter( const RtpDataHeader *header ); void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ); void updateRtcpStats(); bool handlePacket( const unsigned char *packet, size_t packetLen ); uint32_t getSsrc() const { return( mSsrc ); } void setSsrc( uint32_t ssrc ) { mSsrc = ssrc; } bool getFrame( Buffer &buffer ); const std::string &getCname() const { return( mCname ); } const std::string &getLocalHost() const { return( mLocalHost ); } int getLocalDataPort() const { return( mLocalPortChans[0] ); } int getLocalCtrlPort() const { return( mLocalPortChans[1] ); } const std::string &getRemoteHost() const { return( mRemoteHost ); } int getRemoteDataPort() const { return( mRemotePortChans[0] ); } int getRemoteCtrlPort() const { return( mRemotePortChans[1] ); } uint32_t getMaxSeq() const { return( mCycles + mMaxSeq ); } uint32_t getExpectedPackets() const { return( mExpectedPackets ); } uint32_t getLostPackets() const { return( mLostPackets ); } uint8_t getLostFraction() const { return( mLostFraction ); } uint32_t getJitter() const { return( mJitter >> 4 ); } uint32_t getLastSrTimestamp() const { return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) ); } }; #undef _AVCODECID #endif // ZM_RTP_SOURCE_H ZoneMinder-1.26.5/src/zm_rtsp.cpp000066400000000000000000000616541225361755400166760ustar00rootroot00000000000000// // ZoneMinder RTSP Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" #if HAVE_LIBAVFORMAT #include "zm_rtsp.h" #include "zm_rtp_data.h" #include "zm_rtp_ctrl.h" #include "zm_db.h" #include "zm_sdp.h" #include #include #include #include int RtspThread::smMinDataPort = 0; int RtspThread::smMaxDataPort = 0; RtspThread::PortSet RtspThread::smAssignedPorts; bool RtspThread::sendCommand( std::string message ) { if ( !mAuth.empty() ) message += stringtf( "Authorization: Basic %s\r\n", mAuth64.c_str() ); message += stringtf( "User-Agent: ZoneMinder/%s\r\n", ZM_VERSION ); message += stringtf( "CSeq: %d\r\n\r\n", ++mSeq ); Debug( 2, "Sending RTSP message: %s", message.c_str() ); if ( mMethod == RTP_RTSP_HTTP ) { message = base64Encode( message ); Debug( 2, "Sending encoded RTSP message: %s", message.c_str() ); if ( mRtspSocket2.send( message.c_str(), message.size() ) != (int)message.length() ) { Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); return( false ); } } else { if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) { Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); return( false ); } } return( true ); } bool RtspThread::recvResponse( std::string &response ) { if ( mRtspSocket.recv( response ) < 0 ) Error( "Recv failed; %s", strerror(errno) ); Debug( 2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size() ); float respVer = 0; int respCode = -1; char respText[ZM_NETWORK_BUFSIZ]; if ( sscanf( response.c_str(), "RTSP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 ) { if ( isalnum(response[0]) ) { Error( "Response parse failure in '%s'", response.c_str() ); } else { Error( "Response parse failure, %zd bytes follow", response.size() ); if ( response.size() ) Hexdump( Logger::ERROR, response.data(), min(response.size(),16) ); } return( false ); } if ( respCode != 200 ) { Error( "Unexpected response code %d, text is '%s'", respCode, respText ); return( false ); } return( true ); } int RtspThread::requestPorts() { if ( !smMinDataPort ) { char sql[ZM_SQL_SML_BUFSIZ]; strncpy( sql, "select Id from Monitors where Function != 'None' and Type = 'Remote' and Protocol = 'rtsp' and Method = 'rtpUni' order by Id asc", sizeof(sql) ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } MYSQL_RES *result = mysql_store_result( &dbconn ); if ( !result ) { Error( "Can't use query result: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } int nMonitors = mysql_num_rows( result ); int position = 0; if ( nMonitors ) { for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { int id = atoi(dbrow[0]); if ( mId == id ) { position = i; break; } } } else { // Minor hack for testing when not strictly enabled nMonitors = 1; position = 0; } int portRange = int(((config.max_rtp_port-config.min_rtp_port)+1)/nMonitors); smMinDataPort = config.min_rtp_port + (position * portRange); smMaxDataPort = smMinDataPort + portRange - 1; Debug( 2, "Assigned RTP port range is %d-%d", smMinDataPort, smMaxDataPort ); } for ( int i = smMinDataPort; i <= smMaxDataPort; i++ ) { PortSet::const_iterator iter = smAssignedPorts.find( i ); if ( iter == smAssignedPorts.end() ) { smAssignedPorts.insert( i ); return( i ); } } Panic( "Can assign RTP port, no ports left in pool" ); return( -1 ); } void RtspThread::releasePorts( int port ) { if ( port > 0 ) smAssignedPorts.erase( port ); } RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth ) : mId( id ), mMethod( method ), mProtocol( protocol ), mHost( host ), mPort( port ), mPath( path ), mAuth( auth ), mFormatContext( 0 ), mSeq( 0 ), mSession( 0 ), mSsrc( 0 ), mDist( UNDEFINED ), mRtpTime( 0 ), mStop( false ) { mUrl = mProtocol+"://"+mHost+":"+mPort; if ( !mPath.empty() ) { if ( mPath[0] == '/' ) mUrl += mPath; else mUrl += '/'+mPath; } mSsrc = rand(); Debug( 2, "RTSP Local SSRC is %x", mSsrc ); if ( mMethod == RTP_RTSP_HTTP ) mHttpSession = stringtf( "%d", rand() ); if ( !mAuth.empty() ) mAuth64 = base64Encode( mAuth ); } RtspThread::~RtspThread() { } int RtspThread::run() { std::string message; std::string response; response.reserve( ZM_NETWORK_BUFSIZ ); if ( !mRtspSocket.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) ) Fatal( "Unable to connect RTSP socket" ); //Select select( 0.25 ); //select.addReader( &mRtspSocket ); //while ( select.wait() ) //{ //mRtspSocket.recv( response ); //Debug( 4, "Drained %d bytes from RTSP socket", response.size() ); //} if ( mMethod == RTP_RTSP_HTTP ) { if ( !mRtspSocket2.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) ) Fatal( "Unable to connect auxiliary RTSP/HTTP socket" ); //Select select( 0.25 ); //select.addReader( &mRtspSocket2 ); //while ( select.wait() ) //{ //mRtspSocket2.recv( response ); //Debug( 4, "Drained %d bytes from HTTP socket", response.size() ); //} message = "GET "+mPath+" HTTP/1.0\r\n"; message += "X-SessionCookie: "+mHttpSession+"\r\n"; if ( !mAuth.empty() ) message += stringtf( "Authorization: Basic %s\r\n", mAuth64.c_str() ); message += "\r\n"; Debug( 2, "Sending HTTP message: %s", message.c_str() ); if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) { Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); return( -1 ); } if ( mRtspSocket.recv( response ) < 0 ) { Error( "Recv failed; %s", strerror(errno) ); return( -1 ); } Debug( 2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size() ); float respVer = 0; int respCode = -1; char respText[256]; if ( sscanf( response.c_str(), "HTTP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 ) { if ( isalnum(response[0]) ) { Error( "Response parse failure in '%s'", response.c_str() ); } else { Error( "Response parse failure, %zd bytes follow", response.size() ); if ( response.size() ) Hexdump( Logger::ERROR, response.data(), min(response.size(),16) ); } return( -1 ); } if ( respCode != 200 ) { Error( "Unexpected response code %d, text is '%s'", respCode, respText ); return( -1 ); } message = "POST "+mPath+" HTTP/1.0\r\n"; message += "X-SessionCookie: "+mHttpSession+"\r\n"; if ( !mAuth.empty() ) message += stringtf( "Authorization: Basic %s\r\n", mAuth64.c_str() ); message += "Content-Length: 32767\r\n"; message += "Content-Type: application/x-rtsp-tunnelled\r\n"; message += "\r\n"; Debug( 2, "Sending HTTP message: %s", message.c_str() ); if ( mRtspSocket2.send( message.c_str(), message.size() ) != (int)message.length() ) { Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); return( -1 ); } } std::string localHost = ""; int localPorts[2] = { 0, 0 }; //message = "OPTIONS * RTSP/1.0\r\n"; //sendCommand( message ); //recvResponse( response ); message = "DESCRIBE "+mUrl+" RTSP/1.0\r\n"; sendCommand( message ); sleep( 1 ); recvResponse( response ); const std::string endOfHeaders = "\r\n\r\n"; size_t sdpStart = response.find( endOfHeaders ); if( sdpStart == std::string::npos ) return( -1 ); sdpStart += endOfHeaders.length(); std::string sdp = response.substr( sdpStart ); Debug( 1, "Processing SDP '%s'", sdp.c_str() ); SessionDescriptor *sessDesc = 0; try { sessDesc = new SessionDescriptor( mUrl, sdp ); mFormatContext = sessDesc->generateFormatContext(); } catch( const Exception &e ) { Error( e.getMessage().c_str() ); return( -1 ); } #if 0 // New method using ffmpeg native functions std::string authUrl = mUrl; if ( !mAuth.empty() ) authUrl.insert( authUrl.find( "://" )+3, mAuth+"@" ); if ( av_open_input_file( &mFormatContext, authUrl.c_str(), NULL, 0, NULL ) != 0 ) { Error( "Unable to open input '%s'", authUrl.c_str() ); return( -1 ); } #endif uint32_t rtpClock = 0; std::string trackUrl = mUrl; #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,25,0) enum AVCodecID codecId; #else enum CodecID codecId; #endif if ( mFormatContext->nb_streams >= 1 ) { for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ ) { SessionDescriptor::MediaDescriptor *mediaDesc = sessDesc->getStream( i ); #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) #else if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) #endif { trackUrl += "/"+mediaDesc->getControlUrl(); rtpClock = mediaDesc->getClock(); codecId = mFormatContext->streams[i]->codec->codec_id; // Hackery pokery //rtpClock = mFormatContext->streams[i]->codec->sample_rate; break; } } } switch( mMethod ) { case RTP_UNICAST : { localPorts[0] = requestPorts(); localPorts[1] = localPorts[0]+1; message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP;unicast;client_port="+stringtf( "%d", localPorts[0] )+"-"+stringtf( "%d", localPorts[1] )+"\r\n"; break; } case RTP_MULTICAST : { message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP;multicast\r\n"; break; } case RTP_RTSP : case RTP_RTSP_HTTP : { message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP/TCP;unicast\r\n"; break; } default: { Panic( "Got unexpected method %d", mMethod ); break; } } if ( !sendCommand( message ) ) return( -1 ); if ( !recvResponse( response ) ) return( -1 ); StringVector lines = split( response, "\r\n" ); char *session = 0; int timeout = 0; char transport[256] = ""; for ( size_t i = 0; i < lines.size(); i++ ) { sscanf( lines[i].c_str(), "Session: %a[0-9a-fA-F]; timeout=%d", &session, &timeout ); sscanf( lines[i].c_str(), "Transport: %s", transport ); } if ( !session ) Fatal( "Unable to get session identifier from response '%s'", response.c_str() ); Debug( 2, "Got RTSP session %s, timeout %d secs", session, timeout ); if ( !transport[0] ) Fatal( "Unable to get transport details from response '%s'", response.c_str() ); Debug( 2, "Got RTSP transport %s", transport ); std::string method = ""; int remotePorts[2] = { 0, 0 }; int remoteChannels[2] = { 0, 0 }; std::string distribution = ""; unsigned long ssrc = 0; StringVector parts = split( transport, ";" ); for ( size_t i = 0; i < parts.size(); i++ ) { if ( parts[i] == "unicast" || parts[i] == "multicast" ) distribution = parts[i]; else if ( startsWith( parts[i], "server_port=" ) ) { method = "RTP/UNICAST"; StringVector subparts = split( parts[i], "=" ); StringVector ports = split( subparts[1], "-" ); remotePorts[0] = strtol( ports[0].c_str(), NULL, 10 ); remotePorts[1] = strtol( ports[1].c_str(), NULL, 10 ); } else if ( startsWith( parts[i], "interleaved=" ) ) { method = "RTP/RTSP"; StringVector subparts = split( parts[i], "=" ); StringVector channels = split( subparts[1], "-" ); remoteChannels[0] = strtol( channels[0].c_str(), NULL, 10 ); remoteChannels[1] = strtol( channels[1].c_str(), NULL, 10 ); } else if ( startsWith( parts[i], "port=" ) ) { method = "RTP/MULTICAST"; StringVector subparts = split( parts[i], "=" ); StringVector ports = split( subparts[1], "-" ); localPorts[0] = strtol( ports[0].c_str(), NULL, 10 ); localPorts[1] = strtol( ports[1].c_str(), NULL, 10 ); } else if ( startsWith( parts[i], "destination=" ) ) { StringVector subparts = split( parts[i], "=" ); localHost = subparts[1]; } else if ( startsWith( parts[i], "ssrc=" ) ) { StringVector subparts = split( parts[i], "=" ); ssrc = strtoll( subparts[1].c_str(), NULL, 16 ); } } Debug( 2, "RTSP Method is %s", method.c_str() ); Debug( 2, "RTSP Distribution is %s", distribution.c_str() ); Debug( 2, "RTSP SSRC is %lx", ssrc ); Debug( 2, "RTSP Local Host is %s", localHost.c_str() ); Debug( 2, "RTSP Local Ports are %d/%d", localPorts[0], localPorts[1] ); Debug( 2, "RTSP Remote Ports are %d/%d", remotePorts[0], remotePorts[1] ); Debug( 2, "RTSP Remote Channels are %d/%d", remoteChannels[0], remoteChannels[1] ); message = "PLAY "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\nRange: npt=0.000-\r\n"; if ( !sendCommand( message ) ) return( -1 ); if ( !recvResponse( response ) ) return( -1 ); lines = split( response, "\r\n" ); char *rtpInfo = 0; for ( size_t i = 0; i < lines.size(); i++ ) { sscanf( lines[i].c_str(), "RTP-Info: %as", &rtpInfo ); } if ( !rtpInfo ) Fatal( "Unable to get RTP Info identifier from response '%s'", response.c_str() ); Debug( 2, "Got RTP Info %s", rtpInfo ); int seq = 0; unsigned long rtpTime = 0; parts = split( rtpInfo, ";" ); for ( size_t i = 0; i < parts.size(); i++ ) { if ( startsWith( parts[i], "seq=" ) ) { StringVector subparts = split( parts[i], "=" ); seq = strtol( subparts[1].c_str(), NULL, 10 ); } else if ( startsWith( parts[i], "rtptime=" ) ) { StringVector subparts = split( parts[i], "=" ); rtpTime = strtol( subparts[1].c_str(), NULL, 10 ); } } Debug( 2, "RTSP Seq is %d", seq ); Debug( 2, "RTSP Rtptime is %ld", rtpTime ); switch( mMethod ) { case RTP_UNICAST : { RtpSource *source = new RtpSource( mId, "", localPorts[0], mHost, remotePorts[0], ssrc, seq, rtpClock, rtpTime, codecId ); mSources[ssrc] = source; RtpDataThread rtpDataThread( *this, *source ); RtpCtrlThread rtpCtrlThread( *this, *source ); rtpDataThread.start(); rtpCtrlThread.start(); while( !mStop ) { usleep( 100000 ); } #if 0 message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; if ( !sendCommand( message ) ) return( -1 ); if ( !recvResponse( response ) ) return( -1 ); #endif message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; if ( !sendCommand( message ) ) return( -1 ); if ( !recvResponse( response ) ) return( -1 ); rtpDataThread.stop(); rtpCtrlThread.stop(); //rtpDataThread.kill( SIGTERM ); //rtpCtrlThread.kill( SIGTERM ); rtpDataThread.join(); rtpCtrlThread.join(); delete mSources[ssrc]; mSources.clear(); releasePorts( localPorts[0] ); break; } case RTP_RTSP : case RTP_RTSP_HTTP : { RtpSource *source = new RtpSource( mId, "", remoteChannels[0], mHost, remoteChannels[0], ssrc, seq, rtpClock, rtpTime, codecId ); mSources[ssrc] = source; // These never actually run RtpDataThread rtpDataThread( *this, *source ); RtpCtrlThread rtpCtrlThread( *this, *source ); Select select( double(config.http_timeout)/1000.0 ); select.addReader( &mRtspSocket ); Buffer buffer( ZM_NETWORK_BUFSIZ ); time_t lastKeepalive = time(NULL); std::string keepaliveMessage = "OPTIONS * RTSP/1.0\r\n"; std::string keepaliveResponse = "RTSP/1.0 200 OK\r\n"; while ( !mStop && select.wait() >= 0 ) { Select::CommsList readable = select.getReadable(); if ( readable.size() == 0 ) { Error( "RTSP timed out" ); break; } static char tempBuffer[ZM_NETWORK_BUFSIZ]; ssize_t nBytes = mRtspSocket.recv( tempBuffer, sizeof(tempBuffer) ); buffer.append( tempBuffer, nBytes ); Debug( 4, "Read %zd bytes on sd %d, %d total", nBytes, mRtspSocket.getReadDesc(), buffer.size() ); while( buffer.size() > 0 ) { if ( buffer[0] == '$' ) { if ( buffer.size() < 4 ) break; unsigned char channel = buffer[1]; unsigned short len = ntohs( *((unsigned short *)(buffer+2)) ); Debug( 4, "Got %d bytes left, expecting %d byte packet on channel %d", buffer.size(), len, channel ); if ( (unsigned short)buffer.size() < (len+4) ) { Debug( 4, "Missing %d bytes, rereading", (len+4)-buffer.size() ); break; } if ( channel == remoteChannels[0] ) { Debug( 4, "Got %d bytes on data channel %d, packet length is %d", buffer.size(), channel, len ); Hexdump( 4, (char *)buffer, 16 ); rtpDataThread.recvPacket( buffer+4, len ); Debug( 4, "Received" ); } else if ( channel == remoteChannels[1] ) { // len = ntohs( *((unsigned short *)(buffer+2)) ); // Debug( 4, "Got %d bytes on control channel %d", nBytes, channel ); Debug( 4, "Got %d bytes on control channel %d, packet length is %d", buffer.size(), channel, len ); Hexdump( 4, (char *)buffer, 16 ); rtpCtrlThread.recvPackets( buffer+4, len ); } else { Error( "Unexpected channel selector %d in RTSP interleaved data", buffer[1] ); buffer.clear(); break; } buffer.consume( len+4 ); nBytes -= len+4; } else { if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 ) { Debug( 4, "Got keepalive response '%s'", (char *)buffer ); //buffer.consume( keepaliveResponse.size() ); if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) { int discardBytes = charPtr-(char *)buffer; buffer -= discardBytes; } else { buffer.clear(); } } else { if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) { int discardBytes = charPtr-(char *)buffer; Warning( "Unexpected format RTSP interleaved data, resyncing by %d bytes", discardBytes ); Hexdump( -1, (char *)buffer, discardBytes ); buffer -= discardBytes; } else { Warning( "Unexpected format RTSP interleaved data, dumping %d bytes", buffer.size() ); Hexdump( -1, (char *)buffer, 32 ); buffer.clear(); } } } } if ( (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) ) { if ( !sendCommand( message ) ) return( -1 ); lastKeepalive = time(NULL); } buffer.tidy( 1 ); } #if 0 message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; if ( !sendCommand( message ) ) return( -1 ); if ( !recvResponse( response ) ) return( -1 ); #endif message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; if ( !sendCommand( message ) ) return( -1 ); if ( !recvResponse( response ) ) return( -1 ); delete mSources[ssrc]; mSources.clear(); break; } case RTP_MULTICAST : { RtpSource *source = new RtpSource( mId, localHost, localPorts[0], mHost, remotePorts[0], ssrc, seq, rtpClock, rtpTime, codecId ); mSources[ssrc] = source; RtpDataThread rtpDataThread( *this, *source ); RtpCtrlThread rtpCtrlThread( *this, *source ); rtpDataThread.start(); rtpCtrlThread.start(); while( !mStop ) { usleep( 100000 ); } #if 0 message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; if ( !sendCommand( message ) ) return( -1 ); if ( !recvResponse( response ) ) return( -1 ); #endif message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; if ( !sendCommand( message ) ) return( -1 ); if ( !recvResponse( response ) ) return( -1 ); rtpDataThread.stop(); rtpCtrlThread.stop(); rtpDataThread.join(); rtpCtrlThread.join(); delete mSources[ssrc]; mSources.clear(); releasePorts( localPorts[0] ); break; } default: { Panic( "Got unexpected method %d", mMethod ); break; } } return( 0 ); } #endif // HAVE_LIBAVFORMAT ZoneMinder-1.26.5/src/zm_rtsp.h000066400000000000000000000061471225361755400163370ustar00rootroot00000000000000// // ZoneMinder RTSP Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_RTSP_H #define ZM_RTSP_H #include "zm.h" #include "zm_ffmpeg.h" #include "zm_comms.h" #include "zm_thread.h" #include "zm_rtp_source.h" #include #include class RtspThread : public Thread { public: typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod; typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist; private: typedef std::set PortSet; typedef std::set SsrcSet; typedef std::map SourceMap; private: static int smMinDataPort; static int smMaxDataPort; static PortSet smLocalSsrcs; static PortSet smAssignedPorts; private: int mId; RtspMethod mMethod; std::string mProtocol; std::string mHost; std::string mPort; std::string mPath; std::string mUrl; std::string mAuth; std::string mAuth64; std::string mHttpSession; ///< Only for RTSP over HTTP sessions TcpInetClient mRtspSocket; TcpInetClient mRtspSocket2; SourceMap mSources; AVFormatContext *mFormatContext; uint16_t mSeq; uint32_t mSession; uint32_t mSsrc; int mRemotePorts[2]; int mRemoteChannels[2]; RtspDist mDist; unsigned long mRtpTime; bool mStop; private: bool sendCommand( std::string message ); bool recvResponse( std::string &response ); public: RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth ); ~RtspThread(); public: int requestPorts(); void releasePorts( int port ); bool isValidSsrc( uint32_t ssrc ); bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header ); uint32_t getSsrc() const { return( mSsrc ); } bool hasSources() const { return( !mSources.empty() ); } AVFormatContext *getFormatContext() { return( mFormatContext ); } bool getFrame( Buffer &frame ) { SourceMap::iterator iter = mSources.begin(); if ( iter == mSources.end() ) return( false ); return( iter->second->getFrame( frame ) ); } int run(); void stop() { mStop = true; } bool stopped() const { return( mStop ); } }; #endif // ZM_RTSP_H ZoneMinder-1.26.5/src/zm_sdp.cpp000066400000000000000000000557721225361755400165000ustar00rootroot00000000000000// // ZoneMinder SDP Class Implementation, $Date: 2009-04-14 21:20:02 +0100 (Tue, 14 Apr 2009) $, $Revision: 2850 $ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" #if HAVE_LIBAVFORMAT #include "zm_sdp.h" #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = { { 0, "PCMU", AVMEDIA_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8000, 1 }, { 3, "GSM", AVMEDIA_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 4, "G723", AVMEDIA_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 5, "DVI4", AVMEDIA_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 6, "DVI4", AVMEDIA_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 }, { 7, "LPC", AVMEDIA_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 8, "PCMA", AVMEDIA_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 }, { 9, "G722", AVMEDIA_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 10, "L16", AVMEDIA_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 }, { 11, "L16", AVMEDIA_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 }, { 12, "QCELP", AVMEDIA_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 }, { 13, "CN", AVMEDIA_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 14, "MPA", AVMEDIA_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 }, { 14, "MPA", AVMEDIA_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 }, { 15, "G728", AVMEDIA_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 16, "DVI4", AVMEDIA_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 }, { 17, "DVI4", AVMEDIA_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 }, { 18, "G729", AVMEDIA_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 25, "CelB", AVMEDIA_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 }, { 26, "JPEG", AVMEDIA_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 }, { 28, "nv", AVMEDIA_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 }, { 31, "H261", AVMEDIA_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 }, { 32, "MPV", AVMEDIA_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 }, { 32, "MPV", AVMEDIA_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 }, { 33, "MP2T", AVMEDIA_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 }, { 34, "H263", AVMEDIA_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 }, { -1, "", AVMEDIA_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 } }; SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = { { "MP4V-ES", AVMEDIA_TYPE_VIDEO, CODEC_ID_MPEG4 }, { "mpeg4-generic", AVMEDIA_TYPE_AUDIO, CODEC_ID_AAC }, { "H264", AVMEDIA_TYPE_VIDEO, CODEC_ID_H264 }, { "AMR", AVMEDIA_TYPE_AUDIO, CODEC_ID_AMR_NB } }; #else SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = { { 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8000, 1 }, { 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 }, { 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 }, { 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 }, { 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 }, { 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 }, { 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 }, { 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 }, { 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 }, { 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 }, { 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 }, { 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 }, { 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 }, { 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 }, { 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 }, { 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 }, { 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 }, { 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 }, { -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 } }; SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = { { "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 }, { "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC }, { "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 }, { "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB } }; #endif SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) : mTtl( 16 ), mNoAddresses( 0 ) { StringVector tokens = split( connInfo, " " ); if ( tokens.size() < 3 ) throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" ); mNetworkType = tokens[0]; if ( mNetworkType != "IN" ) throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" ); mAddressType = tokens[1]; if ( mAddressType != "IP4" ) throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" ); StringVector addressTokens = split( tokens[2], "/" ); if ( addressTokens.size() < 1 ) throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" ); mAddress = addressTokens[0]; if ( addressTokens.size() >= 2 ) mTtl = atoi(addressTokens[1].c_str()); if ( addressTokens.size() >= 3 ) mNoAddresses = atoi(addressTokens[2].c_str()); } SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) : mValue( 0 ) { StringVector tokens = split( bandInfo, ":" ); if ( tokens.size() < 2 ) throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" ); mType = tokens[0]; //if ( mNetworkType != "IN" ) //throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" ); mValue = atoi(tokens[1].c_str()); } SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) : mType( type ), mPort( port ), mNumPorts( numPorts ), mTransport( transport ), mPayloadType( payloadType ), mFrameRate( 0.0 ), mClock( 0 ), mWidth( 0 ), mHeight( 0 ), mSprops( "" ), mConnInfo( 0 ) { } SessionDescriptor::SessionDescriptor( const std::string &url, const std::string &sdp ) : mUrl( url ), mConnInfo( 0 ), mBandInfo( 0 ) { MediaDescriptor *currMedia = 0; StringVector lines = split( sdp, "\r\n" ); for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); iter++ ) { std::string line = *iter; if ( line.empty() ) break; Debug( 3, "Processing SDP line '%s'", line.c_str() ); const char sdpType = line[0]; if ( line[1] != '=' ) throw Exception( "Invalid SDP format at '"+line+"'" ); line.erase( 0, 2 ); switch( sdpType ) { case 'v' : mVersion = line; break; case 'o' : mOwner = line; break; case 's' : mName = line; break; case 'i' : mInfo = line; break; case 'c' : mConnInfo = new ConnInfo( line ); break; case 'b' : mBandInfo = new BandInfo( line ); break; case 't' : mTimeInfo = line; break; case 'a' : { mAttributes.push_back( line ); StringVector tokens = split( line, ":", 2 ); std::string attrName = tokens[0]; if ( currMedia ) { if ( attrName == "control" ) { if ( tokens.size() < 2 ) throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" ); currMedia->setControlUrl( tokens[1] ); } else if ( attrName == "range" ) { } else if ( attrName == "rtpmap" ) { // a=rtpmap:96 MP4V-ES/90000 if ( tokens.size() < 2 ) throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" ); StringVector attrTokens = split( tokens[1], " " ); int payloadType = atoi(attrTokens[0].c_str()); if ( payloadType != currMedia->getPayloadType() ) throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) ); std::string payloadDesc = attrTokens[1]; //currMedia->setPayloadType( payloadType ); if ( attrTokens.size() > 1 ) { StringVector payloadTokens = split( attrTokens[1], "/" ); std::string payloadDesc = payloadTokens[0]; int payloadClock = atoi(payloadTokens[1].c_str()); currMedia->setPayloadDesc( payloadDesc ); currMedia->setClock( payloadClock ); } } else if ( attrName == "framesize" ) { // a=framesize:96 320-240 if ( tokens.size() < 2 ) throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" ); StringVector attrTokens = split( tokens[1], " " ); int payloadType = atoi(attrTokens[0].c_str()); if ( payloadType != currMedia->getPayloadType() ) throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) ); //currMedia->setPayloadType( payloadType ); StringVector sizeTokens = split( attrTokens[1], "-" ); int width = atoi(sizeTokens[0].c_str()); int height = atoi(sizeTokens[1].c_str()); currMedia->setFrameSize( width, height ); } else if ( attrName == "framerate" ) { // a=framerate:5.0 if ( tokens.size() < 2 ) throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" ); double frameRate = atof(tokens[1].c_str()); currMedia->setFrameRate( frameRate ); } else if ( attrName == "fmtp" ) { // a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F if ( tokens.size() < 2 ) throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" ); StringVector attrTokens = split( tokens[1], " ", 2 ); int payloadType = atoi(attrTokens[0].c_str()); if ( payloadType != currMedia->getPayloadType() ) throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) ); //currMedia->setPayloadType( payloadType ); if ( attrTokens.size() > 1 ) { StringVector attr2Tokens = split( attrTokens[1], "; " ); for ( unsigned int i = 0; i < attr2Tokens.size(); i++ ) { StringVector attr3Tokens = split( attr2Tokens[i], "=" ); //Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() ); if ( attr3Tokens[0] == "profile-level-id" ) { } else if ( attr3Tokens[0] == "config" ) { } else if ( attr3Tokens[0] == "sprop-parameter-sets" ) { size_t t = attr2Tokens[i].find("="); char *c = (char *)attr2Tokens[i].c_str() + t + 1; Debug(4, "sprop-parameter-sets value %s", c); currMedia->setSprops(std::string(c)); } else if ( attr3Tokens[0] == "sprop-parameter-sets" ) { size_t t = attr2Tokens[i].find("="); char *c = (char *)attr2Tokens[i].c_str() + t + 1; Debug(4, "sprop-parameter-sets value %s", c); currMedia->setSprops(std::string(c)); } else { Debug( 3, "Ignoring SDP fmtp attribute '%s' for media '%s'", attr3Tokens[0].c_str(), currMedia->getType().c_str() ) } } } } else if ( attrName == "mpeg4-iod" ) { // a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA" } else if ( attrName == "mpeg4-esid" ) { // a=mpeg4-esid:201 } else { Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() ) } } else { Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() ); } break; } case 'm' : { StringVector tokens = split( line, " " ); if ( tokens.size() < 4 ) throw Exception( "Can't parse SDP media description '"+line+"'" ); std::string mediaType = tokens[0]; if ( mediaType != "audio" && mediaType != "video" ) throw Exception( "Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'" ); StringVector portTokens = split( tokens[1], "/" ); int mediaPort = atoi(portTokens[0].c_str()); int mediaNumPorts = 1; if ( portTokens.size() > 1 ) mediaNumPorts = atoi(portTokens[1].c_str()); std::string mediaTransport = tokens[2]; if ( mediaTransport != "RTP/AVP" ) throw Exception( "Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'" ); int payloadType = atoi(tokens[3].c_str()); currMedia = new MediaDescriptor( mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType ); mMediaList.push_back( currMedia ); break; } } } } AVFormatContext *SessionDescriptor::generateFormatContext() const { AVFormatContext *formatContext = avformat_alloc_context(); strncpy( formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) ); /* if ( mName.length() ) strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) ); if ( mInfo.length() ) strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) ); */ //formatContext->nb_streams = mMediaList.size(); for ( unsigned int i = 0; i < mMediaList.size(); i++ ) { const MediaDescriptor *mediaDesc = mMediaList[i]; #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 8, 0) AVStream *stream = av_new_stream( formatContext, i ); #else AVStream *stream = avformat_new_stream( formatContext, NULL ); stream->id = i; #endif Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() ); #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) if ( mediaDesc->getType() == "video" ) stream->codec->codec_type = AVMEDIA_TYPE_VIDEO; else if ( mediaDesc->getType() == "audio" ) stream->codec->codec_type = AVMEDIA_TYPE_AUDIO; #else if ( mediaDesc->getType() == "video" ) stream->codec->codec_type = CODEC_TYPE_VIDEO; else if ( mediaDesc->getType() == "audio" ) stream->codec->codec_type = CODEC_TYPE_AUDIO; #endif if ( mediaDesc->getPayloadType() < PAYLOAD_TYPE_DYNAMIC ) { // Look in static table for ( unsigned int i = 0; i < (sizeof(smStaticPayloads)/sizeof(*smStaticPayloads)); i++ ) { if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() ) { Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName ); strncpy( stream->codec->codec_name, smStaticPayloads[i].payloadName, sizeof(stream->codec->codec_name) );; stream->codec->codec_type = smStaticPayloads[i].codecType; stream->codec->codec_id = smStaticPayloads[i].codecId; stream->codec->sample_rate = smStaticPayloads[i].clockRate; break; } } } else { // Look in dynamic table for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ ) { if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() ) { Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName ); strncpy( stream->codec->codec_name, smDynamicPayloads[i].payloadName, sizeof(stream->codec->codec_name) );; stream->codec->codec_type = smDynamicPayloads[i].codecType; stream->codec->codec_id = smDynamicPayloads[i].codecId; stream->codec->sample_rate = mediaDesc->getClock(); break; } } } if ( !stream->codec->codec_name[0] ) { Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() ); //return( 0 ); } if ( mediaDesc->getWidth() ) stream->codec->width = mediaDesc->getWidth(); if ( mediaDesc->getHeight() ) stream->codec->height = mediaDesc->getHeight(); if ( stream->codec->codec_id == CODEC_ID_H264 && mediaDesc->getSprops().size()) { uint8_t start_sequence[]= { 0, 0, 1 }; stream->codec->extradata_size= 0; stream->codec->extradata= NULL; char pvalue[1024], *value = pvalue; strcpy(pvalue, mediaDesc->getSprops().c_str()); while (*value) { char base64packet[1024]; uint8_t decoded_packet[1024]; uint32_t packet_size; char *dst = base64packet; while (*value && *value != ',' && (dst - base64packet) < (long)(sizeof(base64packet)) - 1) { *dst++ = *value++; } *dst++ = '\0'; if (*value == ',') value++; packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet)); Hexdump(4, (char *)decoded_packet, packet_size); if (packet_size) { uint8_t *dest = (uint8_t *)av_malloc(packet_size + sizeof(start_sequence) + stream->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); if(dest) { if(stream->codec->extradata_size) { // av_realloc? memcpy(dest, stream->codec->extradata, stream->codec->extradata_size); av_free(stream->codec->extradata); } memcpy(dest+stream->codec->extradata_size, start_sequence, sizeof(start_sequence)); memcpy(dest+stream->codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size); memset(dest+stream->codec->extradata_size+sizeof(start_sequence)+ packet_size, 0, FF_INPUT_BUFFER_PADDING_SIZE); stream->codec->extradata= dest; stream->codec->extradata_size+= sizeof(start_sequence)+packet_size; // } else { // av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!"); // return AVERROR(ENOMEM); } } } } } return( formatContext ); } #endif // HAVE_LIBAVFORMAT ZoneMinder-1.26.5/src/zm_sdp.h000066400000000000000000000140131225361755400161240ustar00rootroot00000000000000// // ZoneMinder SDP Class Interface, $Date: 2009-02-16 18:21:50 +0000 (Mon, 16 Feb 2009) $, $Revision: 2765 $ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_SDP_H #define ZM_SDP_H #include "zm.h" #include "zm_utils.h" #include "zm_exception.h" #include "zm_ffmpeg.h" #include #include #include class SessionDescriptor { protected: enum { PAYLOAD_TYPE_DYNAMIC=96 }; struct StaticPayloadDesc { int payloadType; const char payloadName[6]; enum AVMediaType codecType; #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,25,0) enum AVCodecID codecId; #else enum CodecID codecId; #endif int clockRate; int autoChannels; }; struct DynamicPayloadDesc { const char payloadName[32]; enum AVMediaType codecType; #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,25,0) enum AVCodecID codecId; #else enum CodecID codecId; #endif //int clockRate; //int autoChannels; }; public: class ConnInfo { protected: std::string mNetworkType; std::string mAddressType; std::string mAddress; int mTtl; int mNoAddresses; public: ConnInfo( const std::string &connInfo ); }; class BandInfo { protected: std::string mType; int mValue; public: BandInfo( const std::string &bandInfo ); }; class MediaDescriptor { protected: std::string mType; int mPort; int mNumPorts; std::string mTransport; int mPayloadType; std::string mPayloadDesc; std::string mControlUrl; double mFrameRate; int mClock; int mWidth; int mHeight; std::string mSprops; ConnInfo *mConnInfo; public: MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ); const std::string &getType() const { return( mType ); } int getPort() const { return( mPort ); } int getNumPorts() const { return( mNumPorts ); } const std::string &getTransport() const { return( mTransport ); } const int getPayloadType() const { return( mPayloadType ); } const std::string &getPayloadDesc() const { return( mPayloadDesc ); } void setPayloadDesc( const std::string &payloadDesc ) { mPayloadDesc = payloadDesc; } const std::string &getControlUrl() const { return( mControlUrl ); } void setControlUrl( const std::string &controlUrl ) { mControlUrl = controlUrl; } const int getClock() const { return( mClock ); } void setClock( int clock ) { mClock = clock; } void setFrameSize( int width, int height ) { mWidth = width; mHeight = height; } int getWidth() const { return( mWidth ); } int getHeight() const { return( mHeight ); } void setSprops(const std::string props) { mSprops = props; } const std::string getSprops() const { return ( mSprops ); } const double getFrameRate() const { return( mFrameRate ); } void setFrameRate( double frameRate ) { mFrameRate = frameRate; } }; typedef std::vector MediaList; protected: static StaticPayloadDesc smStaticPayloads[]; static DynamicPayloadDesc smDynamicPayloads[]; protected: std::string mUrl; std::string mVersion; std::string mOwner; std::string mName; std::string mInfo; ConnInfo *mConnInfo; BandInfo *mBandInfo; std::string mTimeInfo; StringVector mAttributes; MediaList mMediaList; public: SessionDescriptor( const std::string &url, const std::string &sdp ); const std::string &getUrl() const { return( mUrl ); } int getNumStreams() const { return( mMediaList.size() ); } MediaDescriptor *getStream( int index ) { if ( index < 0 || (unsigned int)index >= mMediaList.size() ) return( 0 ); return( mMediaList[index] ); } AVFormatContext *generateFormatContext() const; }; #if 0 v=0 o=- 1239719297054659 1239719297054674 IN IP4 192.168.1.11 s=Media Presentation e=NONE c=IN IP4 0.0.0.0 b=AS:174 t=0 0 a=control:* a=range:npt=now- a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA" m=video 0 RTP/AVP 96 b=AS:110 a=framerate:5.0 a=control:trackID=1 a=rtpmap:96 MP4V-ES/90000 a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F a=mpeg4-esid:201 m=audio 0 RTP/AVP 0 b=AS:64 a=control:trackID=2 #endif #endif // ZM_SDP_H ZoneMinder-1.26.5/src/zm_signal.cpp000066400000000000000000000112601225361755400171470ustar00rootroot00000000000000// // ZoneMinder Signal Handling Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" #include "zm_signal.h" #include #include #include #define TRACE_SIZE 16 bool zm_reload = false; bool zm_terminate = false; RETSIGTYPE zm_hup_handler(int signal) { Info("Got signal %d (%s), reloading", signal, strsignal(signal)); zm_reload = true; } RETSIGTYPE zm_term_handler(int signal) { Info("Got signal %d (%s), exiting", signal, strsignal(signal)); zm_terminate = true; } #if ( HAVE_SIGINFO_T && HAVE_UCONTEXT_T ) RETSIGTYPE zm_die_handler(int signal, siginfo_t * info, void *context) #else RETSIGTYPE zm_die_handler(int signal) #endif { void *cr2 = 0; void *ip = 0; Error("Got signal %d (%s), crashing", signal, strsignal(signal)); // Get more information if available #if ( HAVE_SIGINFO_T && HAVE_UCONTEXT_T ) if (info && context) { Debug(1, "Signal information: number %d code %d errno %d pid %d uid %d status %d", signal, info->si_code, info->si_errno, info->si_pid, info->si_uid, info->si_status); ucontext_t *uc = (ucontext_t *) context; #if defined(__x86_64__) cr2 = info->si_addr; ip = (void *)(uc->uc_mcontext.gregs[REG_RIP]); #else cr2 = info->si_addr; ip = (void *)(uc->uc_mcontext.gregs[REG_EIP]); #endif // defined(__x86_64__) // Print the signal address and instruction pointer if available if (ip) { Error("Signal address is %p, from %p", cr2, ip); } else { Error("Signal address is %p, no instruction pointer", cr2); } } #endif // ( HAVE_SIGINFO_T && HAVE_UCONTEXT_T ) // Print backtrace if enabled and available #if ( !defined(ZM_NO_CRASHTRACE) && HAVE_DECL_BACKTRACE && HAVE_DECL_BACKTRACE_SYMBOLS ) void *trace[TRACE_SIZE]; int trace_size = 0; trace_size = backtrace(trace, TRACE_SIZE); char cmd[1024] = "addr2line -e "; char *cmd_ptr = cmd + strlen(cmd); cmd_ptr += snprintf(cmd_ptr, sizeof(cmd) - (cmd_ptr - cmd), "%s", self); char **messages = backtrace_symbols(trace, trace_size); // Print the full backtrace for (int i = 0; i < trace_size; i++) { Error("Backtrace %u: %s", i, messages[i]); cmd_ptr += snprintf(cmd_ptr, sizeof(cmd) - (cmd_ptr - cmd), " %p", trace[i]); } free(messages); Info("Backtrace complete, please execute the following command for more information"); Info(cmd); #endif // ( !defined(ZM_NO_CRASHTRACE) && HAVE_DECL_BACKTRACE && HAVE_DECL_BACKTRACE_SYMBOLS ) exit(signal); } void zmSetHupHandler(SigHandler * handler) { sigset_t block_set; sigemptyset(&block_set); struct sigaction action, old_action; action.sa_handler = (SigHandler *) handler; action.sa_mask = block_set; action.sa_flags = SA_RESTART; sigaction(SIGHUP, &action, &old_action); } void zmSetTermHandler(SigHandler * handler) { sigset_t block_set; sigemptyset(&block_set); struct sigaction action, old_action; action.sa_handler = (SigHandler *) handler; action.sa_mask = block_set; action.sa_flags = SA_RESTART; sigaction(SIGTERM, &action, &old_action); sigaction(SIGINT, &action, &old_action); sigaction(SIGQUIT, &action, &old_action); } void zmSetDieHandler(SigHandler * handler) { sigset_t block_set; sigemptyset(&block_set); struct sigaction action, old_action; action.sa_mask = block_set; #if ( HAVE_SIGINFO_T && HAVE_UCONTEXT_T ) action.sa_sigaction = (void (*)(int, siginfo_t *, void *))handler; action.sa_flags = SA_SIGINFO; #else action.sa_handler = (SigHandler *) handler; action.sa_flags = 0; #endif sigaction(SIGBUS, &action, &old_action); sigaction(SIGSEGV, &action, &old_action); sigaction(SIGABRT, &action, &old_action); sigaction(SIGILL, &action, &old_action); sigaction(SIGFPE, &action, &old_action); } void zmSetDefaultHupHandler() { zmSetHupHandler((SigHandler *) zm_hup_handler); } void zmSetDefaultTermHandler() { zmSetTermHandler((SigHandler *) zm_term_handler); } void zmSetDefaultDieHandler() { if (config.dump_cores) { // Do nothing } else { zmSetDieHandler((SigHandler *) zm_die_handler); } } ZoneMinder-1.26.5/src/zm_signal.h000066400000000000000000000031151225361755400166140ustar00rootroot00000000000000/* * ZoneMinder Signal Handling Interface, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef ZM_SIGNAL_H #define ZM_SIGNAL_H #include #if HAVE_EXECINFO_H #include #endif #if HAVE_UCONTEXT_H #include #endif #include "zm.h" typedef RETSIGTYPE (SigHandler)( int ); extern bool zm_reload; extern bool zm_terminate; RETSIGTYPE zmc_hup_handler( int signal ); RETSIGTYPE zmc_term_handler( int signal ); #if ( HAVE_SIGINFO_T && HAVE_UCONTEXT_T ) RETSIGTYPE zmc_die_handler( int signal, siginfo_t *info, void *context ); #else RETSIGTYPE zmc_die_handler( int signal ); #endif void zmSetHupHandler( SigHandler *handler ); void zmSetTermHandler( SigHandler *handler ); void zmSetDieHandler( SigHandler *handler ); void zmSetDefaultHupHandler(); void zmSetDefaultTermHandler(); void zmSetDefaultDieHandler(); #endif // ZM_SIGNAL_H ZoneMinder-1.26.5/src/zm_stream.cpp000066400000000000000000000261631225361755400171750ustar00rootroot00000000000000// // ZoneMinder Stream Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include #include "zm.h" #include "zm_mpeg.h" #include "zm_monitor.h" #include "zm_stream.h" StreamBase::~StreamBase() { #if HAVE_LIBAVCODEC if ( vid_stream ) { delete vid_stream; vid_stream = NULL; } #endif closeComms(); } bool StreamBase::loadMonitor( int monitor_id ) { if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) ) { Fatal( "Unable to load monitor id %d for streaming", monitor_id ); return( false ); } return( true ); } bool StreamBase::checkInitialised() { if ( !monitor ) { Fatal( "Cannot stream, not initialised" ); return( false ); } return( true ); } void StreamBase::updateFrameRate( double fps ) { base_fps = fps; effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE; frame_mod = 1; Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod ); // Min frame repeat? while( effective_fps > maxfps ) { effective_fps /= 2.0; frame_mod *= 2; } Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod ); } bool StreamBase::checkCommandQueue() { if ( sd >= 0 ) { CmdMsg msg; memset( &msg, 0, sizeof(msg) ); int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 ); if ( nbytes < 0 ) { if ( errno != EAGAIN ) { Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) ); } } //else if ( (nbytes != sizeof(msg)) ) //{ //Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes ); //} else { processCommand( &msg ); return( true ); } } return( false ); } Image *StreamBase::prepareImage( Image *image ) { static int last_scale = 0; static int last_zoom = 0; static int last_x = 0; static int last_y = 0; if ( !last_scale ) last_scale = scale; if ( !last_zoom ) last_zoom = zoom; // 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. bool optimisedScaling = false; bool image_copied = false; int mag = (scale * zoom) / ZM_SCALE_BASE; 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_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(); 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; 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; 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; 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; 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; 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; 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; 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; Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height ); if ( mag != ZM_SCALE_BASE ) { if ( act_mag != ZM_SCALE_BASE ) { Debug( 3, "Magnifying by %d", mag ); if ( !image_copied ) { 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() ); if ( disp_image_width < virt_image_width || disp_image_height < virt_image_height ) { static Box last_crop; if ( mag != last_mag || x != last_x || y != last_y ) { 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) ) last_crop = Box(); Debug( 3, "Recalculating crop" ); // Recalculate crop parameters, as %ges 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; 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; Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y ); // Convert the click locations to the current image pixels click_x = ( click_x * act_image_width ) / 100; click_y = ( click_y * act_image_height ) / 100; Debug( 3, "Got readjusted click at %d,%d", click_x, click_y ); int lo_x = click_x - (send_image_width/2); if ( lo_x < 0 ) lo_x = 0; int hi_x = lo_x + (send_image_width-1); if ( hi_x >= act_image_width ) { hi_x = act_image_width - 1; lo_x = hi_x - (send_image_width - 1); } int lo_y = click_y - (send_image_height/2); if ( lo_y < 0 ) lo_y = 0; int hi_y = lo_y + (send_image_height-1); if ( hi_y >= act_image_height ) { hi_y = act_image_height - 1; lo_y = hi_y - (send_image_height - 1); } last_crop = Box( lo_x, lo_y, hi_x, hi_y ); } Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() ); if ( !image_copied ) { static Image copy_image; copy_image.Assign( *image ); image = ©_image; image_copied = true; } image->Crop( last_crop ); } last_scale = scale; last_zoom = zoom; last_x = x; last_y = y; return( image ); } bool StreamBase::sendTextFrame( const char *frame_text ) { Debug( 2, "Sending text frame '%s'", frame_text ); Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() ); image.Annotate( frame_text, image.centreCoord( frame_text ) ); if ( scale != 100 ) { image.Scale( scale ); } #if HAVE_LIBAVCODEC if ( type == STREAM_MPEG ) { if ( !vid_stream ) { vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() ); fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); vid_stream->OpenStream(); } /* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() ); } else #endif // HAVE_LIBAVCODEC { static unsigned char buffer[ZM_MAX_IMAGE_SIZE]; int n_bytes = 0; image.EncodeJpeg( buffer, &n_bytes ); fprintf( stdout, "--ZoneMinderFrame\r\n" ); fprintf( stdout, "Content-Length: %d\r\n", n_bytes ); fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 ) { Error( "Unable to send stream text frame: %s", strerror(errno) ); return( false ); } fprintf( stdout, "\r\n\r\n" ); fflush( stdout ); } last_frame_sent = TV_2_FLOAT( now ); return( true ); } void StreamBase::openComms() { if ( connkey > 0 ) { sd = socket( AF_UNIX, SOCK_DGRAM, 0 ); if ( sd < 0 ) { Fatal( "Can't create socket: %s", strerror(errno) ); } snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey ); unlink( loc_sock_path ); strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) ); loc_addr.sun_family = AF_UNIX; if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)) < 0 ) { Fatal( "Can't bind: %s", strerror(errno) ); } snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey ); strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) ); rem_addr.sun_family = AF_UNIX; } } void StreamBase::closeComms() { if ( connkey > 0 ) { if ( sd >= 0 ) { close( sd ); sd = -1; } if ( loc_sock_path[0] ) { unlink( loc_sock_path ); } } } ZoneMinder-1.26.5/src/zm_stream.h000066400000000000000000000105001225361755400166260ustar00rootroot00000000000000// // ZoneMinder Stream Interfaces, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_STREAM_H #define ZM_STREAM_H #include #include #include "zm.h" #include "zm_mpeg.h" class Monitor; #define TV_2_FLOAT( tv ) ( double((tv).tv_sec) + (double((tv).tv_usec) / 1000000.0) ) class StreamBase { public: typedef enum { STREAM_JPEG, STREAM_RAW, STREAM_ZIP, STREAM_SINGLE, STREAM_MPEG } StreamType; protected: static const int MAX_STREAM_DELAY = 5; // Seconds static const StreamType DEFAULT_TYPE = STREAM_JPEG; enum { DEFAULT_RATE=ZM_RATE_BASE }; enum { DEFAULT_SCALE=ZM_SCALE_BASE }; enum { DEFAULT_ZOOM=ZM_SCALE_BASE }; enum { DEFAULT_MAXFPS=10 }; enum { DEFAULT_BITRATE=100000 }; protected: typedef struct { int msg_type; char msg_data[16]; } CmdMsg; typedef struct { int msg_type; char msg_data[256]; } DataMsg; typedef enum { MSG_CMD=1, MSG_DATA_WATCH, MSG_DATA_EVENT } MsgType; typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUERY=99 } MsgCommand; protected: Monitor *monitor; StreamType type; const char *format; int replay_rate; int scale; int zoom; double maxfps; int bitrate; unsigned short x, y; protected: int connkey; int sd; char loc_sock_path[PATH_MAX]; struct sockaddr_un loc_addr; char rem_sock_path[PATH_MAX]; struct sockaddr_un rem_addr; protected: bool paused; int step; struct timeval now; double base_fps; double effective_fps; int frame_mod; double last_frame_sent; struct timeval last_frame_timestamp; #if HAVE_LIBAVCODEC VideoStream *vid_stream; #endif // HAVE_LIBAVCODEC CmdMsg msg; protected: bool loadMonitor( int monitor_id ); bool checkInitialised(); void updateFrameRate( double fps ); Image *prepareImage( Image *image ); bool sendTextFrame( const char *text ); bool checkCommandQueue(); virtual void processCommand( const CmdMsg *msg )=0; public: StreamBase() { monitor = 0; type = DEFAULT_TYPE; format = ""; replay_rate = DEFAULT_RATE; scale = DEFAULT_SCALE; zoom = DEFAULT_ZOOM; maxfps = DEFAULT_MAXFPS; bitrate = DEFAULT_BITRATE; paused = false; step = 0; x = 0; y = 0; connkey = 0; sd = -1; memset( &loc_sock_path, 0, sizeof(loc_sock_path) ); memset( &loc_addr, 0, sizeof(loc_addr) ); memset( &rem_sock_path, 0, sizeof(rem_sock_path) ); memset( &rem_addr, 0, sizeof(rem_addr) ); base_fps = 0.0; effective_fps = 0.0; frame_mod = 1; #if HAVE_LIBAVCODEC vid_stream = 0; #endif // HAVE_LIBAVCODEC } virtual ~StreamBase(); void setStreamType( StreamType p_type ) { type = p_type; } void setStreamFormat( const char *p_format ) { format = p_format; } void setStreamScale( int p_scale ) { scale = p_scale; } void setStreamReplayRate( int p_rate ) { replay_rate = p_rate; } void setStreamMaxFPS( double p_maxfps ) { maxfps = p_maxfps; } void setStreamBitrate( int p_bitrate ) { bitrate = p_bitrate; } void setStreamQueue( int p_connkey ) { connkey = p_connkey; } virtual void openComms(); virtual void closeComms(); virtual void runStream()=0; }; #endif // ZM_STREAM_H ZoneMinder-1.26.5/src/zm_thread.cpp000066400000000000000000000232251225361755400171450ustar00rootroot00000000000000// // ZoneMinder Thread Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm_thread.h" #include "zm_logger.h" #include "zm_utils.h" #include #include #include #include struct timespec getTimeout( int secs ) { struct timespec timeout; struct timeval temp_timeout; gettimeofday( &temp_timeout, 0 ); timeout.tv_sec = temp_timeout.tv_sec + secs; timeout.tv_nsec = temp_timeout.tv_usec*1000; return( timeout ); } struct timespec getTimeout( double secs ) { struct timespec timeout; struct timeval temp_timeout; gettimeofday( &temp_timeout, 0 ); timeout.tv_sec = temp_timeout.tv_sec + int(secs); timeout.tv_nsec = temp_timeout.tv_usec += (long int)(1000000000.0*(secs-int(secs))); if ( timeout.tv_nsec > 1000000000 ) { timeout.tv_sec += 1; timeout.tv_nsec -= 1000000000; } return( timeout ); } Mutex::Mutex() { if ( pthread_mutex_init( &mMutex, NULL ) < 0 ) throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) ); } Mutex::~Mutex() { if ( locked() ) Warning( "Destroying mutex when locked" ); if ( pthread_mutex_destroy( &mMutex ) < 0 ) throw ThreadException( stringtf( "Unable to destroy pthread mutex: %s", strerror(errno) ) ); } void Mutex::lock() { if ( pthread_mutex_lock( &mMutex ) < 0 ) throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) ); } void Mutex::lock( int secs ) { struct timespec timeout = getTimeout( secs ); if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 ) throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) ); } void Mutex::lock( double secs ) { struct timespec timeout = getTimeout( secs ); if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 ) throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) ); } void Mutex::unlock() { if ( pthread_mutex_unlock( &mMutex ) < 0 ) throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) ); } bool Mutex::locked() { int state = pthread_mutex_trylock( &mMutex ); if ( state != 0 && state != EBUSY ) throw ThreadException( stringtf( "Unable to trylock pthread mutex: %s", strerror(errno) ) ); if ( state != EBUSY ) unlock(); return( state == EBUSY ); } Condition::Condition( Mutex &mutex ) : mMutex( mutex ) { if ( pthread_cond_init( &mCondition, NULL ) < 0 ) throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) ); } Condition::~Condition() { if ( pthread_cond_destroy( &mCondition ) < 0 ) throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) ); } void Condition::wait() { // Locking done outside of this function if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 ) throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) ); } bool Condition::wait( int secs ) { // Locking done outside of this function Debug( 8, "Waiting for %d seconds", secs ); struct timespec timeout = getTimeout( secs ); if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT ) throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) ); return( errno != ETIMEDOUT ); } bool Condition::wait( double secs ) { // Locking done outside of this function struct timespec timeout = getTimeout( secs ); if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT ) throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) ); return( errno != ETIMEDOUT ); } void Condition::signal() { if ( pthread_cond_signal( &mCondition ) < 0 ) throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) ); } void Condition::broadcast() { if ( pthread_cond_broadcast( &mCondition ) < 0 ) throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) ); } template const T ThreadData::getValue() const { mMutex.lock(); const T valueCopy = mValue; mMutex.unlock(); return( valueCopy ); } template T ThreadData::setValue( const T value ) { mMutex.lock(); const T valueCopy = mValue = value; mMutex.unlock(); return( valueCopy ); } template const T ThreadData::getUpdatedValue() const { Debug( 8, "Waiting for value update, %p", this ); mMutex.lock(); mChanged = false; //do { mCondition.wait(); //} while ( !mChanged ); const T valueCopy = mValue; mMutex.unlock(); Debug( 9, "Got value update, %p", this ); return( valueCopy ); } template const T ThreadData::getUpdatedValue( double secs ) const { Debug( 8, "Waiting for value update, %.2f secs, %p", secs, this ); mMutex.lock(); mChanged = false; //do { mCondition.wait( secs ); //} while ( !mChanged ); const T valueCopy = mValue; mMutex.unlock(); Debug( 9, "Got value update, %p", this ); return( valueCopy ); } template const T ThreadData::getUpdatedValue( int secs ) const { Debug( 8, "Waiting for value update, %d secs, %p", secs, this ); mMutex.lock(); mChanged = false; //do { mCondition.wait( secs ); //} while ( !mChanged ); const T valueCopy = mValue; mMutex.unlock(); Debug( 9, "Got value update, %p", this ); return( valueCopy ); } template void ThreadData::updateValueSignal( const T value ) { Debug( 8, "Updating value with signal, %p", this ); mMutex.lock(); mValue = value; mChanged = true; mCondition.signal(); mMutex.unlock(); Debug( 9, "Updated value, %p", this ); } template void ThreadData::updateValueBroadcast( const T value ) { Debug( 8, "Updating value with broadcast, %p", this ); mMutex.lock(); mValue = value; mChanged = true; mCondition.broadcast(); mMutex.unlock(); Debug( 9, "Updated value, %p", this ); } Thread::Thread() : mThreadCondition( mThreadMutex ), mPid( -1 ), mStarted( false ), mRunning( false ) { Debug( 1, "Creating thread" ); } Thread::~Thread() { Debug( 1, "Destroying thread %d", mPid ); if ( mStarted ) join(); } void *Thread::mThreadFunc( void *arg ) { Debug( 2, "Invoking thread" ); Thread *thisPtr = (Thread *)arg; void *status = 0; try { thisPtr->mThreadMutex.lock(); thisPtr->mPid = thisPtr->id(); thisPtr->mThreadCondition.signal(); thisPtr->mThreadMutex.unlock(); thisPtr->mRunning = true; int run=(thisPtr->run()); status = (void *)&run; thisPtr->mRunning = false; Debug( 2, "Exiting thread, status %p", status ); } catch ( const ThreadException &e ) { Error( "%s", e.getMessage().c_str() ); thisPtr->mRunning = false; status = (void *)-1; Debug( 2, "Exiting thread after exception, status %p", status ); } return( status ); } void Thread::start() { Debug( 1, "Starting thread" ); if ( isThread() ) throw ThreadException( "Can't self start thread" ); mThreadMutex.lock(); if ( !mStarted ) { pthread_attr_t threadAttrs; pthread_attr_init( &threadAttrs ); pthread_attr_setscope( &threadAttrs, PTHREAD_SCOPE_SYSTEM ); mStarted = true; if ( pthread_create( &mThread, &threadAttrs, mThreadFunc, this ) < 0 ) throw ThreadException( stringtf( "Can't create thread: %s", strerror(errno) ) ); pthread_attr_destroy( &threadAttrs ); } else { Error( "Attempt to start already running thread %d", mPid ); } mThreadCondition.wait(); mThreadMutex.unlock(); Debug( 1, "Started thread %d", mPid ); } void Thread::join() { Debug( 1, "Joining thread %d", mPid ); if ( isThread() ) throw ThreadException( "Can't self join thread" ); mThreadMutex.lock(); if ( mPid >= 0 ) { if ( mStarted ) { void *threadStatus = 0; if ( pthread_join( mThread, &threadStatus ) < 0 ) throw ThreadException( stringtf( "Can't join sender thread: %s", strerror(errno) ) ); mStarted = false; Debug( 1, "Thread %d exited, status %p", mPid, threadStatus ); } else { Warning( "Attempt to join already finished thread %d", mPid ); } } else { Warning( "Attempt to join non-started thread %d", mPid ); } mThreadMutex.unlock(); Debug( 1, "Joined thread %d", mPid ); } void Thread::kill( int signal ) { pthread_kill( mThread, signal ); } // Some explicit template instantiations #include "zm_threaddata.cpp" ZoneMinder-1.26.5/src/zm_thread.h000066400000000000000000000114311225361755400166060ustar00rootroot00000000000000// // ZoneMinder Thread Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_THREAD_H #define ZM_THREAD_H #include #include #include #ifdef HAVE_SYS_SYSCALL_H #include #endif // HAVE_SYS_SYSCALL_H #include "zm_exception.h" #include "zm_utils.h" class ThreadException : public Exception { public: ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)syscall(SYS_gettid) ) ) { } }; class Mutex { friend class Condition; private: pthread_mutex_t mMutex; public: Mutex(); ~Mutex(); private: pthread_mutex_t *getMutex() { return( &mMutex ); } public: void lock(); void lock( int secs ); void lock( double secs ); void unlock(); bool locked(); }; class ScopedMutex { private: Mutex &mMutex; public: ScopedMutex( Mutex &mutex ) : mMutex( mutex ) { mMutex.lock(); } ~ScopedMutex() { mMutex.unlock(); } private: ScopedMutex( const ScopedMutex & ); }; class Condition { private: Mutex &mMutex; pthread_cond_t mCondition; public: Condition( Mutex &mutex ); ~Condition(); void wait(); bool wait( int secs ); bool wait( double secs ); void signal(); void broadcast(); }; class Semaphore : public Condition { private: Mutex mMutex; public: Semaphore() : Condition( mMutex ) { } void wait() { mMutex.lock(); Condition::wait(); mMutex.unlock(); } bool wait( int secs ) { mMutex.lock(); bool result = Condition::wait( secs ); mMutex.unlock(); return( result ); } bool wait( double secs ) { mMutex.lock(); bool result = Condition::wait( secs ); mMutex.unlock(); return( result ); } void signal() { mMutex.lock(); Condition::signal(); mMutex.unlock(); } void broadcast() { mMutex.lock(); Condition::broadcast(); mMutex.unlock(); } }; template class ThreadData { private: T mValue; mutable bool mChanged; mutable Mutex mMutex; mutable Condition mCondition; public: __attribute__((used)) ThreadData() : mCondition( mMutex ) { } __attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex ) { } //~ThreadData() {} __attribute__((used)) operator T() const { return( getValue() ); } __attribute__((used)) const T operator=( const T value ) { return( setValue( value ) ); } __attribute__((used)) const T getValueImmediate() const { return( mValue ); } __attribute__((used)) T setValueImmediate( const T value ) { return( mValue = value ); } __attribute__((used)) const T getValue() const; __attribute__((used)) T setValue( const T value ); __attribute__((used)) const T getUpdatedValue() const; __attribute__((used)) const T getUpdatedValue( double secs ) const; __attribute__((used)) const T getUpdatedValue( int secs ) const; __attribute__((used)) void updateValueSignal( const T value ); __attribute__((used)) void updateValueBroadcast( const T value ); }; class Thread { public: typedef void *(*ThreadFunc)( void * ); protected: pthread_t mThread; Mutex mThreadMutex; Condition mThreadCondition; pid_t mPid; bool mStarted; bool mRunning; protected: Thread(); virtual ~Thread(); pid_t id() const { return( (pid_t)syscall(SYS_gettid) ); } void exit( int status = 0 ) { //INFO( "Exiting" ); pthread_exit( (void *)&status ); } static void *mThreadFunc( void *arg ); public: virtual int run() = 0; void start(); void join(); void kill( int signal ); bool isThread() { return( mPid > -1 && pthread_equal( pthread_self(), mThread ) ); } bool isStarted() const { return( mStarted ); } bool isRunning() const { return( mRunning ); } }; #endif // ZM_THREAD_H ZoneMinder-1.26.5/src/zm_threaddata.cpp000066400000000000000000000016241225361755400177760ustar00rootroot00000000000000// // ZoneMinder Explicit Thread Template Class Instantiations, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // template class ThreadData; template class ThreadData; ZoneMinder-1.26.5/src/zm_time.cpp000066400000000000000000000015601225361755400166320ustar00rootroot00000000000000// // ZoneMinder Time Functions & Definitions Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm_time.h" // Blank ZoneMinder-1.26.5/src/zm_time.h000066400000000000000000000127121225361755400163000ustar00rootroot00000000000000// // ZoneMinder Time Functions & Definitions, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_TIME_H #define ZM_TIME_H #include "zm.h" #include // Structure used for storing the results of the subtraction // of one struct timeval from another struct DeltaTimeval { bool positive; unsigned long delta; unsigned long sec; unsigned long fsec; unsigned long prec; }; #define DT_GRAN_1000000 1000000 #define DT_PREC_6 DT_GRAN_1000000 #define DT_GRAN_100000 100000 #define DT_PREC_5 DT_GRAN_100000 #define DT_GRAN_10000 10000 #define DT_PREC_4 DT_GRAN_10000 #define DT_GRAN_1000 1000 #define DT_PREC_3 DT_GRAN_1000 #define DT_GRAN_100 100 #define DT_PREC_2 DT_GRAN_100 #define DT_GRAN_10 10 #define DT_PREC_1 DT_GRAN_10 #define DT_MAXGRAN DT_GRAN_1000000 // This obviously wouldn't work for massive deltas but as it's mostly // for frames it will only usually be a fraction of a second or so #define DELTA_TIMEVAL( result, time1, time2, precision ) \ { \ int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \ result.positive = (delta>=0); \ result.delta = abs(delta); \ result.sec = result.delta/(precision); \ result.fsec = result.delta%(precision); \ result.prec = (precision); \ } #define TIMEVAL_INTERVAL( result, time1, time2, precision ) \ { \ int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \ result.positive = (delta>=0); \ result.delta = abs(delta); \ result.sec = result.delta/(precision); \ result.fsec = result.delta%(precision); \ result.prec = (precision); \ } #define USEC_PER_SEC 1000000 #define MSEC_PER_SEC 1000 extern struct timeval tv; typedef typeof(tv.tv_sec) ast_time_t; typedef typeof(tv.tv_usec) ast_suseconds_t; inline int tvDiffUsec( struct timeval first, struct timeval last ) { return( (last.tv_sec - first.tv_sec) * USEC_PER_SEC) + ((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC ); } inline int tvDiffUsec( struct timeval first ) { struct timeval now; gettimeofday( &now, NULL ); return( tvDiffUsec( first, now ) ); } inline int tvDiffMsec( struct timeval first, struct timeval last ) { return( (last.tv_sec - first.tv_sec) * MSEC_PER_SEC) + (((MSEC_PER_SEC + last.tv_usec - first.tv_usec) / MSEC_PER_SEC) - MSEC_PER_SEC ); } inline int tvDiffMsec( struct timeval first ) { struct timeval now; gettimeofday( &now, NULL ); return( tvDiffMsec( first, now ) ); } inline double tvDiffSec( struct timeval first, struct timeval last ) { return( double(last.tv_sec - first.tv_sec) + double(((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC) / (1.0*USEC_PER_SEC) ) ); } inline double tvDiffSec( struct timeval first ) { struct timeval now; gettimeofday( &now, NULL ); return( tvDiffSec( first, now ) ); } inline struct timeval tvZero() { struct timeval t = { 0, 0 }; return( t ); } inline int tvIsZero( const struct timeval t ) { return( t.tv_sec == 0 && t.tv_usec == 0 ); } inline int tvCmp( struct timeval t1, struct timeval t2 ) { if ( t1.tv_sec < t2.tv_sec ) return( -1 ); if ( t1.tv_sec > t2.tv_sec ) return( 1 ); if ( t1.tv_usec < t2.tv_usec ) return( -1 ); if ( t1.tv_usec > t2.tv_usec ) return( 1 ); return( 0 ); } inline int tvEq( struct timeval t1, struct timeval t2 ) { return( t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec ); } inline struct timeval tvNow( void ) { struct timeval t; gettimeofday( &t, NULL ); return( t ); } inline struct timeval tvCheck( struct timeval &t ) { if ( t.tv_usec >= USEC_PER_SEC ) { Warning( "Timestamp too large %ld.%ld\n", t.tv_sec, (long int) t.tv_usec ); t.tv_sec += t.tv_usec / USEC_PER_SEC; t.tv_usec %= USEC_PER_SEC; } else if ( t.tv_usec < 0 ) { Warning( "Got negative timestamp %ld.%ld\n", t.tv_sec, (long int)t.tv_usec ); t.tv_usec = 0; } return( t ); } // Add t2 to t1 inline struct timeval tvAdd( struct timeval t1, struct timeval t2 ) { tvCheck(t1); tvCheck(t2); t1.tv_sec += t2.tv_sec; t1.tv_usec += t2.tv_usec; if ( t1.tv_usec >= USEC_PER_SEC ) { t1.tv_sec++; t1.tv_usec -= USEC_PER_SEC; } return( t1 ); } // Subtract t2 from t1 inline struct timeval tvSub( struct timeval t1, struct timeval t2 ) { tvCheck(t1); tvCheck(t2); t1.tv_sec -= t2.tv_sec; t1.tv_usec -= t2.tv_usec; if ( t1.tv_usec < 0 ) { t1.tv_sec--; t1.tv_usec += USEC_PER_SEC; } return( t1 ) ; } inline struct timeval tvMake( time_t sec, suseconds_t usec ) { struct timeval t; t.tv_sec = sec; t.tv_usec = usec; return( t ); } #endif // ZM_TIME_H ZoneMinder-1.26.5/src/zm_timer.cpp000066400000000000000000000056201225361755400170150ustar00rootroot00000000000000// // ZoneMinder Timer Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm_timer.h" #include "zm_logger.h" int Timer::TimerThread::mNextTimerId = 0; Timer::TimerThread::TimerThread( Timer &timer, int duration, bool repeat ) : mTimerId( 0 ), mTimer( timer ), mDuration( duration ), mRepeat( repeat ), mReset( false ), mExpiryFlag( true ) { mAccessMutex.lock(); mTimerId = mNextTimerId++; Debug( 5, "Creating timer %d for %d seconds%s", mTimerId, mDuration, mRepeat?", repeating":"" ); mAccessMutex.unlock(); } Timer::TimerThread::~TimerThread() { cancel(); } void Timer::TimerThread::cancel() { mAccessMutex.lock(); if ( mRunning ) { Debug( 4, "Cancelling timer %d", mTimerId ); mRepeat = false; mReset = false; mExpiryFlag.updateValueSignal( false ); } mAccessMutex.unlock(); } void Timer::TimerThread::reset() { mAccessMutex.lock(); if ( mRunning ) { Debug( 4, "Resetting timer" ); mReset = true; mExpiryFlag.updateValueSignal( false ); } else { Error( "Attempting to reset expired timer %d", mTimerId ); } mAccessMutex.unlock(); } int Timer::TimerThread::run() { Debug( 4, "Starting timer %d for %d seconds", mTimerId, mDuration ); bool timerExpired = false; do { mAccessMutex.lock(); mReset = false; mExpiryFlag.setValue( true ); mAccessMutex.unlock(); timerExpired = mExpiryFlag.getUpdatedValue( mDuration ); mAccessMutex.lock(); if ( timerExpired ) { Debug( 4, "Timer %d expired", mTimerId ); mTimer.expire(); } else { Debug( 4, "Timer %d %s", mTimerId, mReset?"reset":"cancelled" ); } mAccessMutex.unlock(); } while ( mRepeat || (mReset && !timerExpired) ); return( timerExpired ); } Timer::Timer( int timeout, bool repeat ) : mTimerThread( *this, timeout, repeat ) { mTimerThread.start(); } Timer::~Timer() { //cancel(); } void Timer::Timer::cancel() { mTimerThread.cancel(); } void Timer::Timer::reset() { mTimerThread.reset(); } ZoneMinder-1.26.5/src/zm_timer.h000066400000000000000000000040131225361755400164550ustar00rootroot00000000000000// // ZoneMinder Timer Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_TIMER_H #define ZM_TIMER_H #ifdef HAVE_SYS_SYSCALL_H #include #endif // HAVE_SYS_SYSCALL_H #include "zm_thread.h" #include "zm_exception.h" class Timer { private: class TimerException : public Exception { public: TimerException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)syscall(SYS_gettid) ) ) { } }; class TimerThread : public Thread { private: typedef ThreadData ExpiryFlag; private: static int mNextTimerId; private: int mTimerId; Timer &mTimer; int mDuration; int mRepeat; int mReset; ExpiryFlag mExpiryFlag; Mutex mAccessMutex; private: void quit() { cancel(); } public: TimerThread( Timer &timer, int timeout, bool repeat ); ~TimerThread(); void cancel(); void reset(); int run(); }; protected: TimerThread mTimerThread; protected: Timer( int timeout, bool repeat=false ); public: virtual ~Timer(); protected: virtual void expire()=0; public: void cancel(); void reset(); }; #endif // ZM_TIMER_H ZoneMinder-1.26.5/src/zm_user.cpp000066400000000000000000000144571225361755400166630ustar00rootroot00000000000000/* * ZoneMinder regular expression class implementation, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "zm.h" #include "zm_db.h" #include "zm_user.h" #include #include #include #include User::User() { username[0] = password[0] = 0; enabled = false; stream = events = control = monitors = system = PERM_NONE; monitor_ids = 0; } User::User( MYSQL_ROW &dbrow ) { int index = 0; strncpy( username, dbrow[index++], sizeof(username) ); strncpy( password, dbrow[index++], sizeof(password) ); enabled = (bool)atoi( dbrow[index++] ); stream = (Permission)atoi( dbrow[index++] ); events = (Permission)atoi( dbrow[index++] ); control = (Permission)atoi( dbrow[index++] ); monitors = (Permission)atoi( dbrow[index++] ); system = (Permission)atoi( dbrow[index++] ); monitor_ids = 0; char *monitor_ids_str = dbrow[index++]; if ( monitor_ids_str && *monitor_ids_str ) { monitor_ids = new int[strlen(monitor_ids_str)]; int n_monitor_ids = 0; const char *ptr = monitor_ids_str; do { int id = 0; while( isdigit( *ptr ) ) { id *= 10; id += *ptr-'0'; ptr++; } if ( id ) { monitor_ids[n_monitor_ids++] = id; if ( !*ptr ) break; } while ( !isdigit( *ptr ) ) ptr++; } while( *ptr ); monitor_ids[n_monitor_ids] = 0; } } User::~User() { delete monitor_ids; } bool User::canAccess( int monitor_id ) { if ( !monitor_ids ) { return( true ); } for ( int i = 0; monitor_ids[i]; i++ ) { if ( monitor_ids[i] == monitor_id ) { return( true ); } } return( false ); } // Function to load a user from username and password User *zmLoadUser( const char *username, const char *password ) { char sql[ZM_SQL_SML_BUFSIZ] = ""; if ( password ) { snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", username, password ); } else { snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", username ); } 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 ) { Warning( "Unable to authenticate user %s", username ); return( 0 ); } MYSQL_ROW dbrow = mysql_fetch_row( result ); User *user = new User( dbrow ); Info( "Authenticated user '%s'", user->getUsername() ); mysql_free_result( result ); return( user ); } // Function to validate an authentication string User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) { #if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT #ifdef HAVE_GCRYPT_H // Special initialisation for libgcrypt if ( !gcry_check_version( GCRYPT_VERSION ) ) { Fatal( "Unable to initialise libgcrypt" ); } gcry_control( GCRYCTL_DISABLE_SECMEM, 0 ); gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 ); #endif // HAVE_GCRYPT_H const char *remote_addr = ""; if ( use_remote_addr ) { remote_addr = getenv( "REMOTE_ADDR" ); if ( !remote_addr ) { Warning( "Can't determine remote address, using null" ); remote_addr = ""; } } Debug( 1, "Attempting to authenticate user from auth string '%s'", auth ); char sql[ZM_SQL_SML_BUFSIZ] = ""; snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1" ); 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 ) { Warning( "Unable to authenticate user" ); return( 0 ); } while( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) { const char *user = dbrow[0]; const char *pass = dbrow[1]; char auth_key[512] = ""; char auth_md5[32+1] = ""; size_t md5len = 16; unsigned char md5sum[md5len]; time_t now = time( 0 ); int max_tries = 2; for ( int i = 0; i < max_tries; i++, now -= (60*60) ) { struct tm *now_tm = localtime( &now ); snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d", config.auth_hash_secret, user, pass, remote_addr, now_tm->tm_hour, now_tm->tm_mday, now_tm->tm_mon, now_tm->tm_year ); #if HAVE_DECL_MD5 MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum ); #elif HAVE_DECL_GNUTLS_FINGERPRINT gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) }; gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len ); #endif auth_md5[0] = '\0'; for ( unsigned int j = 0; j < md5len; j++ ) { sprintf( &auth_md5[2*j], "%02x", md5sum[j] ); } Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s'", auth_key, auth_md5 ); if ( !strcmp( auth, auth_md5 ) ) { // We have a match User *user = new User( dbrow ); Info( "Authenticated user '%s'", user->getUsername() ); return( user ); } } } #else // HAVE_DECL_MD5 Error( "You need to build with gnutls or openssl installed to use hash based authentication" ); #endif // HAVE_DECL_MD5 return( 0 ); } ZoneMinder-1.26.5/src/zm_user.h000066400000000000000000000040541225361755400163200ustar00rootroot00000000000000/* * ZoneMinder User Class Interface, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "zm.h" #include "zm_db.h" #ifndef ZM_USER_H #define ZM_USER_H #if HAVE_GNUTLS_OPENSSL_H #include #endif #if HAVE_GNUTLS_GNUTLS_H #include #endif #if HAVE_GCRYPT_H #include #elif HAVE_LIBCRYPTO #include #endif // HAVE_L || HAVE_LIBCRYPTO class User { public: typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission; protected: char username[32+1]; char password[64+1]; bool enabled; Permission stream; Permission events; Permission control; Permission monitors; Permission system; int *monitor_ids; public: User(); User( MYSQL_ROW &dbrow ); ~User(); const char *getUsername() const { return( username ); } const char *getPassword() const { return( password ); } bool isEnabled() const { return( enabled ); } Permission getStream() const { return( stream ); } Permission getEvents() const { return( events ); } Permission getControl() const { return( control ); } Permission getMonitors() const { return( monitors ); } Permission getSystem() const { return( system ); } bool canAccess( int monitor_id ); }; User *zmLoadUser( const char *username, const char *password=0 ); User *zmLoadAuthUser( const char *auth, bool use_remote_addr ); #endif // ZM_USER_H ZoneMinder-1.26.5/src/zm_utils.cpp000066400000000000000000000157701225361755400170440ustar00rootroot00000000000000// // ZoneMinder General Utility Functions, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // //#include "zm_logger.h" #include "zm.h" #include "zm_utils.h" #include #include #include unsigned int sseversion = 0; const std::string stringtf( const char *format, ... ) { va_list ap; char tempBuffer[8192]; std::string tempString; va_start(ap, format ); vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap ); va_end(ap); tempString = tempBuffer; return( tempString ); } const std::string stringtf( const std::string &format, ... ) { va_list ap; char tempBuffer[8192]; std::string tempString; va_start(ap, format ); vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap ); va_end(ap); tempString = tempBuffer; return( tempString ); } bool startsWith( const std::string &haystack, const std::string &needle ) { return( haystack.substr( 0, needle.length() ) == needle ); } StringVector split( const std::string &string, const std::string chars, int limit ) { StringVector stringVector; std::string tempString = string; std::string::size_type startIndex = 0; std::string::size_type endIndex = 0; //Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit ); do { // Find delimiters endIndex = string.find_first_of( chars, startIndex ); //Info( "Got endIndex at %d", endIndex ); if ( endIndex > 0 ) { //Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() ); stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) ); } if ( endIndex == std::string::npos ) break; // Find non-delimiters startIndex = tempString.find_first_not_of( chars, endIndex ); if ( limit && (stringVector.size() == (unsigned int)(limit-1)) ) { stringVector.push_back( string.substr( startIndex ) ); break; } //Info( "Got new startIndex at %d", startIndex ); } while ( startIndex != std::string::npos ); //Info( "Finished with %d strings", stringVector.size() ); return( stringVector ); } const std::string base64Encode( const std::string &inString ) { static char base64_table[64] = { '\0' }; if ( !base64_table[0] ) { int i = 0; for ( char c = 'A'; c <= 'Z'; c++ ) base64_table[i++] = c; for ( char c = 'a'; c <= 'z'; c++ ) base64_table[i++] = c; for ( char c = '0'; c <= '9'; c++ ) base64_table[i++] = c; base64_table[i++] = '+'; base64_table[i++] = '/'; } std::string outString; outString.reserve( 2 * inString.size() ); const char *inPtr = inString.c_str(); while( *inPtr ) { unsigned char selection = *inPtr >> 2; unsigned char remainder = (*inPtr++ & 0x03) << 4; outString += base64_table[selection]; if ( *inPtr ) { selection = remainder | (*inPtr >> 4); remainder = (*inPtr++ & 0x0f) << 2; outString += base64_table[selection]; if ( *inPtr ) { selection = remainder | (*inPtr >> 6); outString += base64_table[selection]; selection = (*inPtr++ & 0x3f); outString += base64_table[selection]; } else { outString += base64_table[remainder]; outString += '='; } } else { outString += base64_table[remainder]; outString += '='; outString += '='; } } return( outString ); } /* Sets sse_version */ void ssedetect() { #if (defined(__i386__) || defined(__x86_64__)) /* x86 or x86-64 processor */ uint32_t r_edx, r_ecx; __asm__ __volatile__( "mov $0x1,%%eax\n\t" "cpuid\n\t" : "=d" (r_edx), "=c" (r_ecx) : : "%eax", "%ebx" ); if (r_ecx & 0x00000200) { sseversion = 35; /* SSSE3 */ Debug(1,"Detected a x86\\x86-64 processor with SSSE3"); } else if (r_ecx & 0x00000001) { sseversion = 30; /* SSE3 */ Debug(1,"Detected a x86\\x86-64 processor with SSE3"); } else if (r_edx & 0x04000000) { sseversion = 20; /* SSE2 */ Debug(1,"Detected a x86\\x86-64 processor with SSE2"); } else if (r_edx & 0x02000000) { sseversion = 10; /* SSE */ Debug(1,"Detected a x86\\x86-64 processor with SSE"); } else { sseversion = 0; Debug(1,"Detected a x86\\x86-64 processor"); } #else /* Non x86 or x86-64 processor, SSE2 is not available */ Debug(1,"Detected a non x86\\x86-64 processor"); sseversion = 0; #endif } /* SSE2 aligned memory copy. Useful for big copying of aligned memory like image buffers in ZM */ /* For platforms without SSE2 we will use standard x86 asm memcpy or glibc's memcpy() */ __attribute__((noinline,__target__("sse2"))) void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) if(bytes > 128) { unsigned int remainder = bytes % 128; const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder); __asm__ __volatile__( "sse2_copy_iter:\n\t" "movdqa (%0),%%xmm0\n\t" "movdqa 0x10(%0),%%xmm1\n\t" "movdqa 0x20(%0),%%xmm2\n\t" "movdqa 0x30(%0),%%xmm3\n\t" "movdqa 0x40(%0),%%xmm4\n\t" "movdqa 0x50(%0),%%xmm5\n\t" "movdqa 0x60(%0),%%xmm6\n\t" "movdqa 0x70(%0),%%xmm7\n\t" "movntdq %%xmm0,(%1)\n\t" "movntdq %%xmm1,0x10(%1)\n\t" "movntdq %%xmm2,0x20(%1)\n\t" "movntdq %%xmm3,0x30(%1)\n\t" "movntdq %%xmm4,0x40(%1)\n\t" "movntdq %%xmm5,0x50(%1)\n\t" "movntdq %%xmm6,0x60(%1)\n\t" "movntdq %%xmm7,0x70(%1)\n\t" "add $0x80, %0\n\t" "add $0x80, %1\n\t" "cmp %2, %0\n\t" "jb sse2_copy_iter\n\t" "test %3, %3\n\t" "jz sse2_copy_finish\n\t" "cld\n\t" "rep movsb\n\t" "sse2_copy_finish:\n\t" : : "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder) : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" ); } else { /* Standard memcpy */ __asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory"); } #else /* Non x86\x86-64 platform, use memcpy */ memcpy(dest,src,bytes); #endif return dest; } void timespec_diff(struct timespec *start, struct timespec *end, struct timespec *diff) { if (((end->tv_nsec)-(start->tv_nsec))<0) { diff->tv_sec = end->tv_sec-start->tv_sec-1; diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec; } else { diff->tv_sec = end->tv_sec-start->tv_sec; diff->tv_nsec = end->tv_nsec-start->tv_nsec; } } ZoneMinder-1.26.5/src/zm_utils.h000066400000000000000000000032341225361755400165010ustar00rootroot00000000000000// // ZoneMinder General Utility Functions, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_UTILS_H #define ZM_UTILS_H #include #include #include #include typedef std::vector StringVector; const std::string stringtf( const char *format, ... ); const std::string stringtf( const std::string &format, ... ); bool startsWith( const std::string &haystack, const std::string &needle ); StringVector split( const std::string &string, const std::string chars, int limit=0 ); const std::string base64Encode( const std::string &inString ); inline int max( int a, int b ) { return( a>=b?a:b ); } inline int min( int a, int b ) { return( a<=b?a:b ); } void ssedetect(); void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes); void timespec_diff(struct timespec *start, struct timespec *end, struct timespec *diff); extern unsigned int sseversion; #endif // ZM_UTILS_H ZoneMinder-1.26.5/src/zm_zone.cpp000066400000000000000000001000231225361755400166410ustar00rootroot00000000000000// // ZoneMinder Zone Class Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include "zm.h" #include "zm_db.h" #include "zm_zone.h" #include "zm_image.h" #include "zm_monitor.h" void Zone::Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames ) { monitor = p_monitor; id = p_id; label = new char[strlen(p_label)+1]; strcpy( label, p_label ); type = p_type; polygon = p_polygon; alarm_rgb = p_alarm_rgb; check_method = p_check_method; min_pixel_threshold = p_min_pixel_threshold; max_pixel_threshold = p_max_pixel_threshold; min_alarm_pixels = p_min_alarm_pixels; max_alarm_pixels = p_max_alarm_pixels; filter_box = p_filter_box; min_filter_pixels = p_min_filter_pixels; max_filter_pixels = p_max_filter_pixels; min_blob_pixels = p_min_blob_pixels; max_blob_pixels = p_max_blob_pixels; min_blobs = p_min_blobs; max_blobs = p_max_blobs; overload_frames = p_overload_frames; Debug( 1, "Initialised zone %d/%s - %d - %dx%d - Rgb:%06x, CM:%d, MnAT:%d, MxAT:%d, MnAP:%d, MxAP:%d, FB:%dx%d, MnFP:%d, MxFP:%d, MnBS:%d, MxBS:%d, MnB:%d, MxB:%d, OF: %d", id, label, type, polygon.Width(), polygon.Height(), alarm_rgb, check_method, min_pixel_threshold, max_pixel_threshold, min_alarm_pixels, max_alarm_pixels, filter_box.X(), filter_box.Y(), min_filter_pixels, max_filter_pixels, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs, overload_frames ); alarmed = false; pixel_diff = 0; alarm_pixels = 0; alarm_filter_pixels = 0; alarm_blob_pixels = 0; alarm_blobs = 0; min_blob_size = 0; max_blob_size = 0; image = 0; score = 0; overload_count = 0; pg_image = new Image( monitor->Width(), monitor->Height(), 1, ZM_SUBPIX_ORDER_NONE); pg_image->Clear(); pg_image->Fill( 0xff, polygon ); pg_image->Outline( 0xff, polygon ); ranges = new Range[monitor->Height()]; for ( unsigned int y = 0; y < monitor->Height(); y++) { ranges[y].lo_x = -1; ranges[y].hi_x = 0; ranges[y].off_x = 0; const uint8_t *ppoly = pg_image->Buffer( 0, y ); for ( unsigned int x = 0; x < monitor->Width(); x++, ppoly++ ) { if ( *ppoly ) { if ( ranges[y].lo_x == -1 ) { ranges[y].lo_x = x; } if ( (unsigned int)ranges[y].hi_x < x ) { ranges[y].hi_x = x; } } } } if ( config.record_diag_images ) { static char diag_path[PATH_MAX] = ""; if ( !diag_path[0] ) { snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-poly.jpg", config.dir_events, monitor->Name(), id); } pg_image->WriteJpeg( diag_path ); } } Zone::~Zone() { delete[] label; delete image; delete pg_image; delete[] ranges; } void Zone::RecordStats( const Event *event ) { static char sql[ZM_SQL_MED_BUFSIZ]; snprintf( sql, sizeof(sql), "insert into Stats set MonitorId=%d, ZoneId=%d, EventId=%d, FrameId=%d, PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, MinX=%d, MinY=%d, MaxX=%d, MaxY=%d, Score=%d", monitor->Id(), id, event->Id(), event->Frames()+1, pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, min_blob_size, max_blob_size, alarm_box.LoX(), alarm_box.LoY(), alarm_box.HiX(), alarm_box.HiY(), score ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't insert event stats: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } } //============================================================================= bool Zone::CheckOverloadCount() { Info("Overloaded count: %d, Overloaded frames: %d", overload_count, overload_frames); if ( overload_count ) { Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); overload_count--; return( false ); } return true; } void Zone::SetScore(unsigned int nScore) { score = nScore; } void Zone::SetAlarmImage(const Image* srcImage) { delete image; image = new Image(*srcImage); } int Zone::GetOverloadCount() { return overload_count; } void Zone::SetOverloadCount(int nOverCount) { overload_count = nOverCount; } int Zone::GetOverloadFrames() { return overload_frames; } //=========================================================================== bool Zone::CheckAlarms( const Image *delta_image ) { ResetStats(); if ( overload_count ) { Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); overload_count--; return( false ); } delete image; // Get the difference image Image *diff_image = image = new Image( *delta_image ); int diff_width = diff_image->Width(); uint8_t* diff_buff = (uint8_t*)diff_image->Buffer(); uint8_t* pdiff; const uint8_t* ppoly; unsigned int pixel_diff_count = 0; int alarm_lo_x = 0; int alarm_hi_x = 0; int alarm_lo_y = 0; int alarm_hi_y = 0; int alarm_mid_x = -1; int alarm_mid_y = -1; unsigned int lo_y = polygon.LoY(); unsigned int lo_x = polygon.LoX(); unsigned int hi_x = polygon.HiX(); unsigned int hi_y = polygon.HiY(); Debug( 4, "Checking alarms for zone %d/%s in lines %d -> %d", id, label, lo_y, hi_y ); Debug( 5, "Checking for alarmed pixels" ); /* if(config.cpu_extensions && sseversion >= 20) { sse2_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); } else { std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); } */ std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); if ( config.record_diag_images ) { static char diag_path[PATH_MAX] = ""; if ( !diag_path[0] ) { snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-%d.jpg", config.dir_events, monitor->Name(), id, 1 ); } diff_image->WriteJpeg( diag_path ); } if ( pixel_diff_count && alarm_pixels ) pixel_diff = pixel_diff_count/alarm_pixels; Debug( 5, "Got %d alarmed pixels, need %d -> %d, avg pixel diff %d", alarm_pixels, min_alarm_pixels, max_alarm_pixels, pixel_diff ); if( alarm_pixels ) { if( min_alarm_pixels && (alarm_pixels < (unsigned int)min_alarm_pixels) ) { /* Not enough pixels alarmed */ return (false); } else if( max_alarm_pixels && (alarm_pixels > (unsigned int)max_alarm_pixels) ) { /* Too many pixels alarmed */ overload_count = overload_frames; return (false); } } else { /* No alarmed pixels */ return (false); } score = (100*alarm_pixels)/polygon.Area(); if(score < 1) score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ Debug( 5, "Current score is %d", score ); if ( check_method >= FILTERED_PIXELS ) { int bx = filter_box.X(); int by = filter_box.Y(); int bx1 = bx-1; int by1 = by-1; Debug( 5, "Checking for filtered pixels" ); if ( bx > 1 || by > 1 ) { // Now remove any pixels smaller than our filter size unsigned char *cpdiff; int ldx, hdx, ldy, hdy; bool block; for ( unsigned int y = lo_y; y <= hi_y; y++ ) { int lo_x = ranges[y].lo_x; int hi_x = ranges[y].hi_x; pdiff = (uint8_t*)diff_image->Buffer( lo_x, y ); for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) { if ( *pdiff == WHITE ) { // Check participation in an X block ldx = (x>=(lo_x+bx1))?-bx1:lo_x-x; hdx = (x<=(hi_x-bx1))?0:((hi_x-x)-bx1); ldy = (y>=(lo_y+by1))?-by1:lo_y-y; hdy = (y<=(hi_y-by1))?0:((hi_y-y)-by1); block = false; for ( int dy = ldy; !block && dy <= hdy; dy++ ) { for ( int dx = ldx; !block && dx <= hdx; dx++ ) { block = true; for ( int dy2 = 0; block && dy2 < by; dy2++ ) { for ( int dx2 = 0; block && dx2 < bx; dx2++ ) { cpdiff = diff_buff + (((y+dy+dy2)*diff_width) + (x+dx+dx2)); if ( !*cpdiff ) { block = false; } } } } } if ( !block ) { *pdiff = BLACK; continue; } alarm_filter_pixels++; } } } } else { alarm_filter_pixels = alarm_pixels; } if ( config.record_diag_images ) { static char diag_path[PATH_MAX] = ""; if ( !diag_path[0] ) { snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 2 ); } diff_image->WriteJpeg( diag_path ); } Debug( 5, "Got %d filtered pixels, need %d -> %d", alarm_filter_pixels, min_filter_pixels, max_filter_pixels ); if( alarm_filter_pixels ) { if( min_filter_pixels && (alarm_filter_pixels < min_filter_pixels) ) { /* Not enough pixels alarmed */ return (false); } else if( max_filter_pixels && (alarm_filter_pixels > max_filter_pixels) ) { /* Too many pixels alarmed */ overload_count = overload_frames; return (false); } } else { /* No filtered pixels */ return (false); } score = (100*alarm_filter_pixels)/(polygon.Area()); if(score < 1) score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ Debug( 5, "Current score is %d", score ); if ( check_method >= BLOBS ) { Debug( 5, "Checking for blob pixels" ); typedef struct { unsigned char tag; int count; int lo_x; int hi_x; int lo_y; int hi_y; } BlobStats; BlobStats blob_stats[256]; memset( blob_stats, 0, sizeof(BlobStats)*256 ); uint8_t *spdiff; uint8_t last_x, last_y; BlobStats *bsx, *bsy; BlobStats *bsm, *bss; for ( unsigned int y = lo_y; y <= hi_y; y++ ) { int lo_x = ranges[y].lo_x; int hi_x = ranges[y].hi_x; pdiff = (uint8_t*)diff_image->Buffer( lo_x, y ); for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) { if ( *pdiff == WHITE ) { Debug( 9, "Got white pixel at %d,%d (%p)", x, y, pdiff ); //last_x = (x>lo_x)?*(pdiff-1):0; //last_y = (y>lo_y&&x>=last_lo_x&&x<=last_hi_x)?*(pdiff-diff_width):0; last_x = 0; if(x > 0) { if((x-1) >= lo_x) { last_x = *(pdiff-1); } } last_y = 0; if(y > 0) { if((y-1) >= lo_y && ranges[(y-1)].lo_x <= x && ranges[(y-1)].hi_x >= x) { last_y = *(pdiff-diff_width); } } if ( last_x ) { Debug( 9, "Left neighbour is %d", last_x ); bsx = &blob_stats[last_x]; if ( last_y ) { Debug( 9, "Top neighbour is %d", last_y ); bsy = &blob_stats[last_y]; if ( last_x == last_y ) { Debug( 9, "Matching neighbours, setting to %d", last_x ); // Add to the blob from the x side (either side really) *pdiff = last_x; alarm_blob_pixels++; bsx->count++; if ( x > bsx->hi_x ) bsx->hi_x = x; if ( (int)y > bsx->hi_y ) bsx->hi_y = y; } else { // Aggregate blobs bsm = bsx->count>=bsy->count?bsx:bsy; bss = bsm==bsx?bsy:bsx; Debug( 9, "Different neighbours, setting pixels of %d to %d", bss->tag, bsm->tag ); Debug( 9, "Master blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bsm->tag, bsm->count, bsm->lo_x, bsm->hi_x, bsm->lo_y, bsm->hi_y ); Debug( 9, "Slave blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bss->tag, bss->count, bss->lo_x, bss->hi_x, bss->lo_y, bss->hi_y ); // Now change all those pixels to the other setting int changed = 0; for ( int sy = bss->lo_y; sy <= bss->hi_y; sy++) { int lo_sx = bss->lo_x>=ranges[sy].lo_x?bss->lo_x:ranges[sy].lo_x; int hi_sx = bss->hi_x<=ranges[sy].hi_x?bss->hi_x:ranges[sy].hi_x; Debug( 9, "Changing %d, %d->%d", sy, lo_sx, hi_sx ); Debug( 9, "Range %d, %d->%d", sy, ranges[sy].lo_x, ranges[sy].hi_x ); spdiff = diff_buff + ((diff_width * sy) + lo_sx); for ( int sx = lo_sx; sx <= hi_sx; sx++, spdiff++ ) { Debug( 9, "Pixel at %d,%d (%p) is %d", sx, sy, spdiff, *spdiff ); if ( *spdiff == bss->tag ) { Debug( 9, "Setting pixel" ); *spdiff = bsm->tag; changed++; } } } *pdiff = bsm->tag; alarm_blob_pixels++; if ( !changed ) { Info( "Master blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bsm->tag, bsm->count, bsm->lo_x, bsm->hi_x, bsm->lo_y, bsm->hi_y ); Info( "Slave blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bss->tag, bss->count, bss->lo_x, bss->hi_x, bss->lo_y, bss->hi_y ); Error( "No pixels changed, exiting" ); exit( -1 ); } // Merge the slave blob into the master bsm->count += bss->count+1; if ( x > bsm->hi_x ) bsm->hi_x = x; if ( (int)y > bsm->hi_y ) bsm->hi_y = y; if ( bss->lo_x < bsm->lo_x ) bsm->lo_x = bss->lo_x; if ( bss->lo_y < bsm->lo_y ) bsm->lo_y = bss->lo_y; if ( bss->hi_x > bsm->hi_x ) bsm->hi_x = bss->hi_x; if ( bss->hi_y > bsm->hi_y ) bsm->hi_y = bss->hi_y; alarm_blobs--; Debug( 6, "Merging blob %d with %d at %d,%d, %d current blobs", bss->tag, bsm->tag, x, y, alarm_blobs ); // Clear out the old blob bss->tag = 0; bss->count = 0; bss->lo_x = 0; bss->lo_y = 0; bss->hi_x = 0; bss->hi_y = 0; } } else { Debug( 9, "Setting to left neighbour %d", last_x ); // Add to the blob from the x side *pdiff = last_x; alarm_blob_pixels++; bsx->count++; if ( x > bsx->hi_x ) bsx->hi_x = x; if ( (int)y > bsx->hi_y ) bsx->hi_y = y; } } else { if ( last_y ) { Debug( 9, "Top neighbour is %d", last_y ); Debug( 9, "Setting to top neighbour %d", last_y ); // Add to the blob from the y side BlobStats *bsy = &blob_stats[last_y]; *pdiff = last_y; alarm_blob_pixels++; bsy->count++; if ( x > bsy->hi_x ) bsy->hi_x = x; if ( (int)y > bsy->hi_y ) bsy->hi_y = y; } else { // Create a new blob int i; for ( i = (WHITE-1); i > 0; i-- ) { BlobStats *bs = &blob_stats[i]; // See if we can recycle one first, only if it's at least two rows up if ( bs->count && bs->hi_y < (int)(y-1) ) { if ( (min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels) ) { if ( config.create_analysis_images || config.record_diag_images ) { for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) { spdiff = diff_buff + ((diff_width * sy) + bs->lo_x); for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ ) { if ( *spdiff == bs->tag ) { *spdiff = BLACK; } } } } alarm_blobs--; alarm_blob_pixels -= bs->count; Debug( 6, "Eliminated blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ); bs->tag = 0; bs->count = 0; bs->lo_x = 0; bs->lo_y = 0; bs->hi_x = 0; bs->hi_y = 0; } } if ( !bs->count ) { Debug( 9, "Creating new blob %d", i ); *pdiff = i; alarm_blob_pixels++; bs->tag = i; bs->count++; bs->lo_x = bs->hi_x = x; bs->lo_y = bs->hi_y = y; alarm_blobs++; Debug( 6, "Created blob %d at %d,%d, %d current blobs", bs->tag, x, y, alarm_blobs ); break; } } if ( i == 0 ) { Warning( "Max blob count reached. Unable to allocate new blobs so terminating. Zone settings may be too sensitive." ); x = hi_x+1; y = hi_y+1; } } } } } } if ( config.record_diag_images ) { static char diag_path[PATH_MAX] = ""; if ( !diag_path[0] ) { snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 3 ); } diff_image->WriteJpeg( diag_path ); } if ( !alarm_blobs ) { return( false ); } Debug( 5, "Got %d raw blob pixels, %d raw blobs, need %d -> %d, %d -> %d", alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs ); // Now eliminate blobs under the threshold for ( int i = 1; i < WHITE; i++ ) { BlobStats *bs = &blob_stats[i]; if ( bs->count ) { if ( (min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels) ) { if ( config.create_analysis_images || config.record_diag_images ) { for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) { spdiff = diff_buff + ((diff_width * sy) + bs->lo_x); for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ ) { if ( *spdiff == bs->tag ) { *spdiff = BLACK; } } } } alarm_blobs--; alarm_blob_pixels -= bs->count; Debug( 6, "Eliminated blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ); bs->tag = 0; bs->count = 0; bs->lo_x = 0; bs->lo_y = 0; bs->hi_x = 0; bs->hi_y = 0; } else { Debug( 6, "Preserved blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ); if ( !min_blob_size || bs->count < min_blob_size ) min_blob_size = bs->count; if ( !max_blob_size || bs->count > max_blob_size ) max_blob_size = bs->count; } } } if ( config.record_diag_images ) { static char diag_path[PATH_MAX] = ""; if ( !diag_path[0] ) { snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 4 ); } diff_image->WriteJpeg( diag_path ); } Debug( 5, "Got %d blob pixels, %d blobs, need %d -> %d, %d -> %d", alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs ); if( alarm_blobs ) { if( min_blobs && (alarm_blobs < min_blobs) ) { /* Not enough pixels alarmed */ return (false); } else if(max_blobs && (alarm_blobs > max_blobs) ) { /* Too many pixels alarmed */ overload_count = overload_frames; return (false); } } else { /* No blobs */ return (false); } score = (100*alarm_blob_pixels)/(polygon.Area()); if(score < 1) score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ Debug( 5, "Current score is %d", score ); alarm_lo_x = polygon.HiX()+1; alarm_hi_x = polygon.LoX()-1; alarm_lo_y = polygon.HiY()+1; alarm_hi_y = polygon.LoY()-1; for ( int i = 1; i < WHITE; i++ ) { BlobStats *bs = &blob_stats[i]; if ( bs->count ) { if ( bs->count == max_blob_size ) { if ( config.weighted_alarm_centres ) { unsigned long x_total = 0; unsigned long y_total = 0; for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) { spdiff = diff_buff + ((diff_width * sy) + bs->lo_x); for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ ) { if ( *spdiff == bs->tag ) { x_total += sx; y_total += sy; } } } alarm_mid_x = int(round(x_total/bs->count)); alarm_mid_y = int(round(y_total/bs->count)); } else { alarm_mid_x = int((bs->hi_x+bs->lo_x+1)/2); alarm_mid_y = int((bs->hi_y+bs->lo_y+1)/2); } } if ( alarm_lo_x > bs->lo_x ) alarm_lo_x = bs->lo_x; if ( alarm_lo_y > bs->lo_y ) alarm_lo_y = bs->lo_y; if ( alarm_hi_x < bs->hi_x ) alarm_hi_x = bs->hi_x; if ( alarm_hi_y < bs->hi_y ) alarm_hi_y = bs->hi_y; } } } else { alarm_mid_x = int((alarm_hi_x+alarm_lo_x+1)/2); alarm_mid_y = int((alarm_hi_y+alarm_lo_y+1)/2); } } if ( type == INCLUSIVE ) { // score >>= 1; score /= 2; } else if ( type == EXCLUSIVE ) { // score <<= 1; score *= 2; } Debug( 5, "Adjusted score is %d", score ); // Now outline the changed region if ( score ) { alarm_box = Box( Coord( alarm_lo_x, alarm_lo_y ), Coord( alarm_hi_x, alarm_hi_y ) ); //if ( monitor->followMotion() ) if ( true ) { alarm_centre = Coord( alarm_mid_x, alarm_mid_y ); } else { alarm_centre = alarm_box.Centre(); } if ( (type < PRECLUSIVE) && check_method >= BLOBS && config.create_analysis_images ) { // First mask out anything we don't want for ( unsigned int y = lo_y; y <= hi_y; y++ ) { pdiff = diff_buff + ((diff_width * y) + lo_x); int lo_x2 = ranges[y].lo_x; int hi_x2 = ranges[y].hi_x; int lo_gap = lo_x2-lo_x; if ( lo_gap > 0 ) { if ( lo_gap == 1 ) { *pdiff++ = BLACK; } else { memset( pdiff, BLACK, lo_gap ); pdiff += lo_gap; } } ppoly = pg_image->Buffer( lo_x2, y ); for ( int x = lo_x2; x <= hi_x2; x++, pdiff++, ppoly++ ) { if ( !*ppoly ) { *pdiff = BLACK; } } int hi_gap = hi_x-hi_x2; if ( hi_gap > 0 ) { if ( hi_gap == 1 ) { *pdiff = BLACK; } else { memset( pdiff, BLACK, hi_gap ); } } } if( monitor->Colours() == ZM_COLOUR_GRAY8 ) { image = diff_image->HighlightEdges( alarm_rgb, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB, &polygon.Extent() ); } else { image = diff_image->HighlightEdges( alarm_rgb, monitor->Colours(), monitor->SubpixelOrder(), &polygon.Extent() ); } // Only need to delete this when 'image' becomes detached and points somewhere else delete diff_image; } else { delete image; image = 0; } Debug( 1, "%s: Pixel Diff: %d, Alarm Pixels: %d, Filter Pixels: %d, Blob Pixels: %d, Blobs: %d, Score: %d", Label(), pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, score ); } return( true ); } bool Zone::ParsePolygonString( const char *poly_string, Polygon &polygon ) { Debug( 3, "Parsing polygon string '%s'", poly_string ); char *str_ptr = new char[strlen(poly_string)+1]; char *str = str_ptr; strcpy( str, poly_string ); char *ws; int n_coords = 0; int max_n_coords = strlen(str)/4; Coord *coords = new Coord[max_n_coords]; while( true ) { if ( *str == '\0' ) { break; } ws = strchr( str, ' ' ); if ( ws ) { *ws = '\0'; } char *cp = strchr( str, ',' ); if ( !cp ) { Error( "Bogus coordinate %s found in polygon string", str ); delete[] coords; delete[] str_ptr; return( false ); } else { *cp = '\0'; char *xp = str; char *yp = cp+1; int x = atoi(xp); int y = atoi(yp); Debug( 3, "Got coordinate %d,%d from polygon string", x, y ); #if 0 if ( x < 0 ) x = 0; else if ( x >= width ) x = width-1; if ( y < 0 ) y = 0; else if ( y >= height ) y = height-1; #endif coords[n_coords++] = Coord( x, y ); } if ( ws ) str = ws+1; else break; } polygon = Polygon( n_coords, coords ); Debug( 3, "Successfully parsed polygon string" ); //printf( "Area: %d\n", pg.Area() ); //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); delete[] coords; delete[] str_ptr; return( true ); } bool Zone::ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon ) { Debug( 3, "Parsing zone string '%s'", zone_string ); char *str_ptr = new char[strlen(zone_string)+1]; char *str = str_ptr; strcpy( str, zone_string ); char *ws = strchr( str, ' ' ); if ( !ws ) { Debug( 3, "No initial whitespace found in zone string '%s', finishing", str ); } zone_id = strtol( str, 0, 10 ); Debug( 3, "Got zone %d from zone string", zone_id ); if ( !ws ) { delete str_ptr; return( true ); } *ws = '\0'; str = ws+1; ws = strchr( str, ' ' ); if ( !ws ) { Debug( 3, "No secondary whitespace found in zone string '%s', finishing", zone_string ); } colour = strtol( str, 0, 16 ); Debug( 3, "Got colour %06x from zone string", colour ); if ( !ws ) { delete str_ptr; return( true ); } *ws = '\0'; str = ws+1; bool result = ParsePolygonString( str, polygon ); //printf( "Area: %d\n", pg.Area() ); //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); delete[] str_ptr; return( result ); } int Zone::Load( Monitor *monitor, Zone **&zones ) { static char sql[ZM_SQL_MED_BUFSIZ]; snprintf( sql, sizeof(sql), "select Id,Name,Type+0,Units,Coords,AlarmRGB,CheckMethod+0,MinPixelThreshold,MaxPixelThreshold,MinAlarmPixels,MaxAlarmPixels,FilterX,FilterY,MinFilterPixels,MaxFilterPixels,MinBlobPixels,MaxBlobPixels,MinBlobs,MaxBlobs,OverloadFrames from Zones where MonitorId = %d order by Type, Id", monitor->Id() ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } MYSQL_RES *result = mysql_store_result( &dbconn ); if ( !result ) { Error( "Can't use query result: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } int n_zones = mysql_num_rows( result ); Debug( 1, "Got %d zones for monitor %s", n_zones, monitor->Name() ); delete[] zones; zones = new Zone *[n_zones]; for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { int col = 0; int Id = atoi(dbrow[col++]); const char *Name = dbrow[col++]; int Type = atoi(dbrow[col++]); const char *Units = dbrow[col++]; const char *Coords = dbrow[col++]; int AlarmRGB = dbrow[col]?atoi(dbrow[col]):0; col++; int CheckMethod = atoi(dbrow[col++]); int MinPixelThreshold = dbrow[col]?atoi(dbrow[col]):0; col++; int MaxPixelThreshold = dbrow[col]?atoi(dbrow[col]):0; col++; int MinAlarmPixels = dbrow[col]?atoi(dbrow[col]):0; col++; int MaxAlarmPixels = dbrow[col]?atoi(dbrow[col]):0; col++; int FilterX = dbrow[col]?atoi(dbrow[col]):0; col++; int FilterY = dbrow[col]?atoi(dbrow[col]):0; col++; int MinFilterPixels = dbrow[col]?atoi(dbrow[col]):0; col++; int MaxFilterPixels = dbrow[col]?atoi(dbrow[col]):0; col++; int MinBlobPixels = dbrow[col]?atoi(dbrow[col]):0; col++; int MaxBlobPixels = dbrow[col]?atoi(dbrow[col]):0; col++; int MinBlobs = dbrow[col]?atoi(dbrow[col]):0; col++; int MaxBlobs = dbrow[col]?atoi(dbrow[col]):0; col++; int OverloadFrames = dbrow[col]?atoi(dbrow[col]):0; col++; /* HTML colour code is actually BGR in memory, we want RGB */ AlarmRGB = rgb_convert(AlarmRGB, ZM_SUBPIX_ORDER_BGR); Debug( 5, "Parsing polygon %s", Coords ); Polygon polygon; if ( !ParsePolygonString( Coords, polygon ) ) { Error( "Unable to parse polygon string '%s' for zone %d/%s for monitor %s, ignoring", Coords, Id, Name, monitor->Name() ); continue; } if ( polygon.LoX() < 0 || polygon.HiX() >= (int)monitor->Width() || polygon.LoY() < 0 || polygon.HiY() >= (int)monitor->Height() ) { Error( "Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d), ignoring", Id, Name, monitor->Name(), polygon.LoX(), polygon.LoY(), polygon.HiX(), polygon.HiY() ); continue; } if ( false && !strcmp( Units, "Percent" ) ) { MinAlarmPixels = (MinAlarmPixels*polygon.Area())/100; MaxAlarmPixels = (MaxAlarmPixels*polygon.Area())/100; MinFilterPixels = (MinFilterPixels*polygon.Area())/100; MaxFilterPixels = (MaxFilterPixels*polygon.Area())/100; MinBlobPixels = (MinBlobPixels*polygon.Area())/100; MaxBlobPixels = (MaxBlobPixels*polygon.Area())/100; } if ( atoi(dbrow[2]) == Zone::INACTIVE ) { zones[i] = new Zone( monitor, Id, Name, polygon ); } else { zones[i] = new Zone( monitor, Id, Name, (Zone::ZoneType)Type, polygon, AlarmRGB, (Zone::CheckMethod)CheckMethod, MinPixelThreshold, MaxPixelThreshold, MinAlarmPixels, MaxAlarmPixels, Coord( FilterX, FilterY ), MinFilterPixels, MaxFilterPixels, MinBlobPixels, MaxBlobPixels, MinBlobs, MaxBlobs, OverloadFrames ); } } if ( mysql_errno( &dbconn ) ) { Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } // Yadda yadda mysql_free_result( result ); return( n_zones ); } bool Zone::DumpSettings( char *output, bool /*verbose*/ ) { output[0] = 0; sprintf( output+strlen(output), " Id : %d\n", id ); sprintf( output+strlen(output), " Label : %s\n", label ); sprintf( output+strlen(output), " Type: %d - %s\n", type, type==ACTIVE?"Active":( type==INCLUSIVE?"Inclusive":( type==EXCLUSIVE?"Exclusive":( type==PRECLUSIVE?"Preclusive":( type==INACTIVE?"Inactive":"Unknown" ))))); sprintf( output+strlen(output), " Shape : %d points\n", polygon.getNumCoords() ); for ( int i = 0; i < polygon.getNumCoords(); i++ ) { sprintf( output+strlen(output), " %i: %d,%d\n", i, polygon.getCoord( i ).X(), polygon.getCoord( i ).Y() ); } sprintf( output+strlen(output), " Alarm RGB : %06x\n", alarm_rgb ); sprintf( output+strlen(output), " Check Method: %d - %s\n", check_method, check_method==ALARMED_PIXELS?"Alarmed Pixels":( check_method==FILTERED_PIXELS?"FilteredPixels":( check_method==BLOBS?"Blobs":"Unknown" ))); sprintf( output+strlen(output), " Min Pixel Threshold : %d\n", min_pixel_threshold ); sprintf( output+strlen(output), " Max Pixel Threshold : %d\n", max_pixel_threshold ); sprintf( output+strlen(output), " Min Alarm Pixels : %d\n", min_alarm_pixels ); sprintf( output+strlen(output), " Max Alarm Pixels : %d\n", max_alarm_pixels ); sprintf( output+strlen(output), " Filter Box : %d,%d\n", filter_box.X(), filter_box.Y() ); sprintf( output+strlen(output), " Min Filter Pixels : %d\n", min_filter_pixels ); sprintf( output+strlen(output), " Max Filter Pixels : %d\n", max_filter_pixels ); sprintf( output+strlen(output), " Min Blob Pixels : %d\n", min_blob_pixels ); sprintf( output+strlen(output), " Max Blob Pixels : %d\n", max_blob_pixels ); sprintf( output+strlen(output), " Min Blobs : %d\n", min_blobs ); sprintf( output+strlen(output), " Max Blobs : %d\n", max_blobs ); return( true ); } void Zone::std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum) { uint32_t pixelsalarmed = 0; uint32_t pixelsdifference = 0; uint8_t *pdiff; const uint8_t *ppoly; uint8_t calc_max_pixel_threshold = 255; unsigned int lo_y; unsigned int hi_y; unsigned int lo_x; unsigned int hi_x; if(max_pixel_threshold) calc_max_pixel_threshold = max_pixel_threshold; lo_y = polygon.LoY(); hi_y = polygon.HiY(); for ( unsigned int y = lo_y; y <= hi_y; y++ ) { lo_x = ranges[y].lo_x; hi_x = ranges[y].hi_x; Debug( 7, "Checking line %d from %d -> %d", y, lo_x, hi_x ); pdiff = (uint8_t*)pdiff_image->Buffer( lo_x, y ); ppoly = ppoly_image->Buffer( lo_x, y ); for ( unsigned int x = lo_x; x <= hi_x; x++, pdiff++, ppoly++ ) { if ( *ppoly && (*pdiff > min_pixel_threshold) && (*pdiff <= calc_max_pixel_threshold) ) { pixelsalarmed++; pixelsdifference += *pdiff; *pdiff = WHITE; } else { *pdiff = BLACK; } } } /* Store the results */ *pixel_count = pixelsalarmed; *pixel_sum = pixelsdifference; Debug( 7, "STORED"); } ZoneMinder-1.26.5/src/zm_zone.h000066400000000000000000000145261225361755400163220ustar00rootroot00000000000000// // ZoneMinder Zone Class Interfaces, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZM_ZONE_H #define ZM_ZONE_H #include "zm_rgb.h" #include "zm_coord.h" #include "zm_poly.h" #include "zm_image.h" #include "zm_event.h" class Monitor; // // This describes a 'zone', or an area of an image that has certain // detection characteristics. // class Zone { protected: struct Range { int lo_x; int hi_x; int off_x; }; public: typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE } ZoneType; typedef enum { ALARMED_PIXELS=1, FILTERED_PIXELS, BLOBS } CheckMethod; protected: // Inputs Monitor *monitor; int id; char *label; ZoneType type; Polygon polygon; Rgb alarm_rgb; CheckMethod check_method; int min_pixel_threshold; int max_pixel_threshold; int min_alarm_pixels; int max_alarm_pixels; Coord filter_box; int min_filter_pixels; int max_filter_pixels; int min_blob_pixels; int max_blob_pixels; int min_blobs; int max_blobs; int overload_frames; // Outputs/Statistics bool alarmed; int pixel_diff; unsigned int alarm_pixels; int alarm_filter_pixels; int alarm_blob_pixels; int alarm_blobs; int min_blob_size; int max_blob_size; Box alarm_box; Coord alarm_centre; unsigned int score; Image *pg_image; Range *ranges; Image *image; int overload_count; protected: void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames ); void std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum); public: Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0 ) { Setup( p_monitor, p_id, p_label, p_type, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames ); } Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0 ) { Setup( p_monitor, p_id, p_label, Zone::ACTIVE, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames ); } Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon ) { Setup( p_monitor, p_id, p_label, Zone::INACTIVE, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0 ); } public: ~Zone(); inline int Id() const { return( id ); } inline const char *Label() const { return( label ); } inline ZoneType Type() const { return( type ); } inline bool IsActive() const { return( type == ACTIVE ); } inline bool IsInclusive() const { return( type == INCLUSIVE ); } inline bool IsExclusive() const { return( type == EXCLUSIVE ); } inline bool IsPreclusive() const { return( type == PRECLUSIVE ); } inline bool IsInactive() const { return( type == INACTIVE ); } inline const Image *AlarmImage() const { return( image ); } inline const Polygon &GetPolygon() const { return( polygon ); } inline bool Alarmed() const { return( alarmed ); } inline void SetAlarm() { alarmed = true; } inline void ClearAlarm() { alarmed = false; } inline Coord GetAlarmCentre() const { return( alarm_centre ); } inline unsigned int Score() const { return( score ); } inline void ResetStats() { alarmed = false; pixel_diff = 0; alarm_pixels = 0; alarm_filter_pixels = 0; alarm_blob_pixels = 0; alarm_blobs = 0; min_blob_size = 0; max_blob_size = 0; score = 0; } void RecordStats( const Event *event ); bool CheckAlarms( const Image *delta_image ); bool DumpSettings( char *output, bool verbose ); static bool ParsePolygonString( const char *polygon_string, Polygon &polygon ); static bool ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon ); static int Load( Monitor *monitor, Zone **&zones ); //================================================= bool CheckOverloadCount(); int GetOverloadCount(); void SetOverloadCount(int nOverCount); int GetOverloadFrames(); void SetScore(unsigned int nScore); void SetAlarmImage(const Image* srcImage); inline const Image *getPgImage() const { return( pg_image ); } inline const Range *getRanges() const { return( ranges ); } }; #endif // ZM_ZONE_H ZoneMinder-1.26.5/src/zma.cpp000066400000000000000000000057321225361755400157620ustar00rootroot00000000000000// // ZoneMinder Analysis Daemon, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include #include #include "zm.h" #include "zm_db.h" #include "zm_signal.h" #include "zm_monitor.h" void Usage() { fprintf( stderr, "zma -m \n" ); fprintf( stderr, "Options:\n" ); fprintf( stderr, " -m, --monitor : Specify which monitor to use\n" ); fprintf( stderr, " -h, --help : This screen\n" ); exit( 0 ); } int main( int argc, char *argv[] ) { self = argv[0]; srand( getpid() * time( 0 ) ); int id = -1; static struct option long_options[] = { {"monitor", 1, 0, 'm'}, {"help", 0, 0, 'h'}, {0, 0, 0, 0} }; while (1) { int option_index = 0; int c = getopt_long (argc, argv, "m:h", long_options, &option_index); if (c == -1) { break; } switch (c) { case 'm': id = atoi(optarg); break; case 'h': case '?': Usage(); break; default: //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); break; } } if (optind < argc) { fprintf( stderr, "Extraneous options, " ); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); Usage(); } if ( id < 0 ) { fprintf( stderr, "Bogus monitor %d\n", id ); Usage(); exit( 0 ); } char log_id_string[16]; snprintf( log_id_string, sizeof(log_id_string), "zma_m%d", id ); zmLoadConfig(); logInit( log_id_string ); ssedetect(); Monitor *monitor = Monitor::Load( id, true, Monitor::ANALYSIS ); if ( monitor ) { Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() ); if ( config.opt_frame_server ) { Event::OpenFrameSocket( monitor->Id() ); } zmSetDefaultHupHandler(); zmSetDefaultTermHandler(); zmSetDefaultDieHandler(); sigset_t block_set; sigemptyset( &block_set ); while( !zm_terminate ) { // Process the next image sigprocmask( SIG_BLOCK, &block_set, 0 ); if ( !monitor->Analyse() ) { usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE ); } if ( zm_reload ) { monitor->Reload(); zm_reload = false; } sigprocmask( SIG_UNBLOCK, &block_set, 0 ); } delete monitor; } else { fprintf( stderr, "Can't find monitor with id of %d\n", id ); } return( 0 ); } ZoneMinder-1.26.5/src/zmc.cpp000066400000000000000000000170611225361755400157620ustar00rootroot00000000000000// // ZoneMinder Capture Daemon, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include #include #include #include "zm.h" #include "zm_db.h" #include "zm_time.h" #include "zm_signal.h" #include "zm_monitor.h" void Usage() { fprintf( stderr, "zmc -d or -r -H -P -p or -f or -m \n" ); fprintf( stderr, "Options:\n" ); fprintf( stderr, " -d, --device : For local cameras, device to access. E.g /dev/video0 etc\n" ); fprintf( stderr, " -r -H -P -p : For remote cameras\n" ); fprintf( stderr, " -f, --file : For local images, jpg file to access.\n" ); fprintf( stderr, " -m, --monitor : For sources associated with a single monitor\n" ); fprintf( stderr, " -h, --help : This screen\n" ); exit( 0 ); } int main( int argc, char *argv[] ) { self = argv[0]; srand( getpid() * time( 0 ) ); const char *device = ""; const char *protocol = ""; const char *host = ""; const char *port = ""; const char *path = ""; const char *file = ""; int monitor_id = -1; static struct option long_options[] = { {"device", 1, 0, 'd'}, {"protocol", 1, 0, 'r'}, {"host", 1, 0, 'H'}, {"port", 1, 0, 'P'}, {"path", 1, 0, 'p'}, {"file", 1, 0, 'f'}, {"monitor", 1, 0, 'm'}, {"help", 0, 0, 'h'}, {0, 0, 0, 0} }; while (1) { int option_index = 0; int c = getopt_long (argc, argv, "d:H:P:p:f:m:h", long_options, &option_index); if (c == -1) { break; } switch (c) { case 'd': device = optarg; break; case 'H': host = optarg; break; case 'P': port = optarg; break; case 'p': path = optarg; break; case 'f': file = optarg; break; case 'm': monitor_id = atoi(optarg); break; case 'h': case '?': Usage(); break; default: //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); break; } } if (optind < argc) { fprintf( stderr, "Extraneous options, " ); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); Usage(); } int modes = ( device[0]?1:0 + host[0]?1:0 + file[0]?1:0 + (monitor_id>0?1:0) ); if ( modes > 1 ) { fprintf( stderr, "Only one of device, host/port/path, file or monitor id allowed\n" ); Usage(); exit( 0 ); } if ( modes < 1 ) { fprintf( stderr, "One of device, host/port/path, file or monitor id must be specified\n" ); Usage(); exit( 0 ); } char log_id_string[32] = ""; if ( device[0] ) { const char *slash_ptr = strrchr( device, '/' ); snprintf( log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device ); } else if ( host[0] ) { snprintf( log_id_string, sizeof(log_id_string), "zmc_h%s", host ); } else if ( file[0] ) { const char *slash_ptr = strrchr( file, '/' ); snprintf( log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file ); } else { snprintf( log_id_string, sizeof(log_id_string), "zmc_m%d", monitor_id ); } zmLoadConfig(); logInit( log_id_string ); ssedetect(); Monitor **monitors = 0; int n_monitors = 0; #if ZM_HAS_V4L if ( device[0] ) { n_monitors = Monitor::LoadLocalMonitors( device, monitors, Monitor::CAPTURE ); } else #endif // ZM_HAS_V4L if ( host[0] ) { if ( !port ) port = "80"; n_monitors = Monitor::LoadRemoteMonitors( protocol, host, port, path, monitors, Monitor::CAPTURE ); } else if ( file[0] ) { n_monitors = Monitor::LoadFileMonitors( file, monitors, Monitor::CAPTURE ); } else { Monitor *monitor = Monitor::Load( monitor_id, true, Monitor::CAPTURE ); if ( monitor ) { monitors = new Monitor *[1]; monitors[0] = monitor; n_monitors = 1; } } if ( !n_monitors ) { Error( "No monitors found" ); exit ( -1 ); } Info( "Starting Capture" ); zmSetDefaultTermHandler(); zmSetDefaultDieHandler(); sigset_t block_set; sigemptyset( &block_set ); sigaddset( &block_set, SIGUSR1 ); sigaddset( &block_set, SIGUSR2 ); if ( monitors[0]->PrimeCapture() < 0 ) { Error( "Failed to prime capture of initial monitor" ); exit( -1 ); } long *capture_delays = new long[n_monitors]; long *alarm_capture_delays = new long[n_monitors]; long *next_delays = new long[n_monitors]; struct timeval * last_capture_times = new struct timeval[n_monitors]; for ( int i = 0; i < n_monitors; i++ ) { last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0; capture_delays[i] = monitors[i]->GetCaptureDelay(); alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay(); } int result = 0; struct timeval now; struct DeltaTimeval delta_time; while( !zm_terminate ) { sigprocmask( SIG_BLOCK, &block_set, 0 ); for ( int i = 0; i < n_monitors; i++ ) { long min_delay = MAXINT; gettimeofday( &now, NULL ); for ( int j = 0; j < n_monitors; j++ ) { if ( last_capture_times[j].tv_sec ) { DELTA_TIMEVAL( delta_time, now, last_capture_times[j], DT_PREC_3 ); if ( monitors[i]->GetState() == Monitor::ALARM ) next_delays[j] = alarm_capture_delays[j]-delta_time.delta; else next_delays[j] = capture_delays[j]-delta_time.delta; if ( next_delays[j] < 0 ) next_delays[j] = 0; } else { next_delays[j] = 0; } if ( next_delays[j] <= min_delay ) { min_delay = next_delays[j]; } } if ( next_delays[i] <= min_delay || next_delays[i] <= 0 ) { if ( monitors[i]->PreCapture() < 0 ) { Error( "Failed to pre-capture monitor %d (%d/%d)", monitors[i]->Id(), i, n_monitors ); zm_terminate = true; result = -1; break; } if ( monitors[i]->Capture() < 0 ) { Error( "Failed to capture image from monitor %d (%d/%d)", monitors[i]->Id(), i, n_monitors ); zm_terminate = true; result = -1; break; } if ( monitors[i]->PostCapture() < 0 ) { Error( "Failed to post-capture monitor %d (%d/%d)", monitors[i]->Id(), i, n_monitors ); zm_terminate = true; result = -1; break; } if ( next_delays[i] > 0 ) { gettimeofday( &now, NULL ); DELTA_TIMEVAL( delta_time, now, last_capture_times[i], DT_PREC_3 ); long sleep_time = next_delays[i]-delta_time.delta; if ( sleep_time > 0 ) { usleep( sleep_time*(DT_MAXGRAN/DT_PREC_3) ); } } gettimeofday( &(last_capture_times[i]), NULL ); } } sigprocmask( SIG_UNBLOCK, &block_set, 0 ); } for ( int i = 0; i < n_monitors; i++ ) { delete monitors[i]; } delete monitors; delete [] alarm_capture_delays; delete [] capture_delays; delete [] next_delays; delete [] last_capture_times; return( result ); } ZoneMinder-1.26.5/src/zmf.cpp000066400000000000000000000204241225361755400157620ustar00rootroot00000000000000// // ZoneMinder Image File Writer Implementation, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include "zm.h" #include "zm_db.h" #include "zm_signal.h" #include "zm_monitor.h" #include "zmf.h" int OpenSocket( int monitor_id ) { int sd = socket( AF_UNIX, SOCK_STREAM, 0); if ( sd < 0 ) { Error( "Can't create socket: %s", strerror(errno) ); return( -1 ); } char sock_path[PATH_MAX] = ""; snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id ); if ( unlink( sock_path ) < 0 ) { Warning( "Can't unlink '%s': %s", sock_path, strerror(errno) ); } struct sockaddr_un addr; strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) ); addr.sun_family = AF_UNIX; if ( bind( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 ) { Error( "Can't bind: %s", strerror(errno) ); exit( -1 ); } if ( listen( sd, SOMAXCONN ) < 0 ) { Error( "Can't listen: %s", strerror(errno) ); return( -1 ); } struct sockaddr_un rem_addr; socklen_t rem_addr_len = sizeof(rem_addr); int new_sd = -1; if ( (new_sd = accept( sd, (struct sockaddr *)&rem_addr, &rem_addr_len )) < 0 ) { Error( "Can't accept: %s", strerror(errno) ); exit( -1 ); } close( sd ); sd = new_sd; Info( "Frame server socket open, awaiting images" ); return( sd ); } int ReopenSocket( int &sd, int monitor_id ) { close( sd ); return( sd = OpenSocket( monitor_id ) ); } void Usage() { fprintf( stderr, "zmf -m \n" ); fprintf( stderr, "Options:\n" ); fprintf( stderr, " -m, --monitor : Specify which monitor to use\n" ); fprintf( stderr, " -h, --help : This screen\n" ); exit( 0 ); } int main( int argc, char *argv[] ) { self = argv[0]; srand( getpid() * time( 0 ) ); int id = -1; static struct option long_options[] = { {"monitor", 1, 0, 'm'}, {"help", 0, 0, 'h'}, {0, 0, 0, 0} }; while (1) { int option_index = 0; int c = getopt_long (argc, argv, "m:h", long_options, &option_index); if (c == -1) { break; } switch (c) { case 'm': id = atoi(optarg); break; case 'h': case '?': Usage(); break; default: //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); break; } } if (optind < argc) { fprintf( stderr, "Extraneous options, " ); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); Usage(); } if ( id < 0 ) { fprintf( stderr, "Bogus monitor %d\n", id ); Usage(); exit( 0 ); } char log_id_string[16]; snprintf( log_id_string, sizeof(log_id_string), "m%d", id ); zmLoadConfig(); logInit( "zmf" ); ssedetect(); Monitor *monitor = Monitor::Load( id, false, Monitor::QUERY ); if ( !monitor ) { fprintf( stderr, "Can't find monitor with id of %d\n", id ); exit( -1 ); } char capt_path[PATH_MAX]; char anal_path[PATH_MAX]; snprintf( capt_path, sizeof(capt_path), "%s/%d/%%s/%%0%dd-capture.jpg", config.dir_events, monitor->Id(), config.event_image_digits ); snprintf( anal_path, sizeof(anal_path), "%s/%d/%%s/%%0%dd-analyse.jpg", config.dir_events, monitor->Id(), config.event_image_digits ); zmSetDefaultTermHandler(); zmSetDefaultDieHandler(); sigset_t block_set; sigemptyset( &block_set ); int sd = OpenSocket( monitor->Id() ); FrameHeader frame_header = { 0, 0, false, 0 }; //unsigned char *image_data = 0; fd_set rfds; struct timeval timeout; timeout.tv_sec = 1; timeout.tv_usec = 0; while( 1 ) { struct timeval temp_timeout = timeout; FD_ZERO(&rfds); FD_SET(sd, &rfds); int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout ); if( n_found == 0 ) { Debug( 1, "Select timed out" ); continue; } else if ( n_found < 0) { Error( "Select error: %s", strerror(errno) ); ReopenSocket( sd, monitor->Id() ); continue; } sigprocmask( SIG_BLOCK, &block_set, 0 ); int n_bytes = read( sd, &frame_header, sizeof(frame_header) ); if ( n_bytes != sizeof(frame_header) ) { if ( n_bytes < 0 ) { Error( "Can't read frame header: %s", strerror(errno) ); } else if ( n_bytes > 0 ) { Error( "Incomplete read of frame header, %d bytes only", n_bytes ); } else { Warning( "Socket closed at remote end" ); } ReopenSocket( sd, monitor->Id() ); continue; } Debug( 1, "Read frame header, expecting %ld bytes of image", frame_header.image_length ); static unsigned char image_data[ZM_MAX_IMAGE_SIZE]; // Read for pipe and loop until bytes expected have been read or an error occures int bytes_read = 0; do { n_bytes = read( sd, image_data+bytes_read, frame_header.image_length-bytes_read ); if (n_bytes < 0) break; // break on error if (n_bytes < (int)frame_header.image_length) { // print some informational messages if (bytes_read == 0) { Debug(4,"Image read : Short read %d bytes of %d expected bytes",n_bytes,frame_header.image_length); } else if (bytes_read+n_bytes == (int)frame_header.image_length) { Debug(5,"Image read : Read rest of short read: %d bytes read total of %d bytes",n_bytes,frame_header.image_length); } else { Debug(6,"Image read : continuing, read %d bytes (%d so far)", n_bytes, bytes_read+n_bytes); } } bytes_read+= n_bytes; } while (n_bytes>0 && (bytes_read < (ssize_t)frame_header.image_length) ); // Print errors if there was a problem if ( n_bytes < 1 ) { Error( "Only read %d bytes of %d\n", bytes_read, frame_header.image_length); if ( n_bytes < 0 ) { Error( "Can't read frame image data: %s", strerror(errno) ); } else { Warning( "Socket closed at remote end" ); } ReopenSocket( sd, monitor->Id() ); continue; } static char subpath[PATH_MAX] = ""; if ( config.use_deep_storage ) { struct tm *time = localtime( &frame_header.event_time ); snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec ); } else { snprintf( subpath, sizeof(subpath), "%ld", frame_header.event_id ); } static char path[PATH_MAX] = ""; snprintf( path, sizeof(path), frame_header.alarm_frame?anal_path:capt_path, subpath, frame_header.frame_id ); Debug( 1, "Got image, writing to %s", path ); FILE *fd = 0; if ( (fd = fopen( path, "w" )) < 0 ) { Error( "Can't fopen '%s': %s", path, strerror(errno) ); exit( -1 ); } if ( 0 == fwrite( image_data, frame_header.image_length, 1, fd ) ) { Error( "Can't fwrite image data: %s", strerror(errno) ); exit( -1 ); } fclose( fd ); sigprocmask( SIG_UNBLOCK, &block_set, 0 ); } } ZoneMinder-1.26.5/src/zmf.h000066400000000000000000000020161225361755400154240ustar00rootroot00000000000000// // ZoneMinder Image File Write Class Interface, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #ifndef ZMFILE_H #define ZMFILE_H struct FrameHeader { unsigned long event_id; time_t event_time; unsigned long frame_id; bool alarm_frame; unsigned long image_length; }; #endif // ZMFILE_H ZoneMinder-1.26.5/src/zmfix.cpp000066400000000000000000000120151225361755400163200ustar00rootroot00000000000000// // ZoneMinder Video Device Fixer, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include #include #include #include #include #include #include #include #include #include #include "zm.h" #include "zm_db.h" // Determine if we are a member of the group int inGroup( gid_t gid ) { // Get how many groups we are in int n_gids = getgroups( 0, NULL ); if ( n_gids < 0 ) { Error( "getgroups:%s", strerror(errno) ); return( -1 ); } // Not in any groups if ( !n_gids ) { return( 0 ); } // Allocate space to hold groups gid_t *gids = new gid_t[n_gids * sizeof(gid_t)]; if ( !gids ) { Error( "Unable to allocate groups: %s", strerror(errno) ); return( -1 ); } // Get list of groups if ( getgroups( n_gids, gids ) != n_gids ) { Error( "getgroups:%s", strerror(errno) ); delete[] gids; return( -1 ); } // See if gid in list of groups we belong to int in_gid = 0; for ( int i = 0; i < n_gids; i++ ) { if ( gids[i] == gid ) { in_gid = 1; } } delete[] gids; return( in_gid ); } bool fixDevice( const char *device_path ) { struct stat stat_buf; if ( stat( device_path, &stat_buf ) < 0 ) { Error( "Can't stat %s: %s", device_path, strerror(errno)); return( false ); } uid_t uid = getuid(); gid_t gid = getgid(); int in_gid; if ( (in_gid = inGroup( stat_buf.st_gid )) < 0 ) { return( false ); } mode_t mask = 0; if ( uid == stat_buf.st_uid ) { // If we are the owner mask = 00600; } else if ( gid == stat_buf.st_gid || in_gid ) { // If we are in the owner group mask = 00060; } else { // We are neither the owner nor in the group mask = 00006; } mode_t mode = stat_buf.st_mode; if ( (mode & mask) == mask ) { Debug( 1, "Permissions on %s are ok at %o", device_path, mode ); return( true ); } mode |= mask; Info( "Resetting permissions on %s to %o", device_path, mode ); if ( chmod( device_path, mode ) < 0 ) { Error( "Can't chmod %s to %o: %s", device_path, mode, strerror(errno)); return( false ); } return( true ); } int main( int argc, char *argv[] ) { self = argv[0]; zmLoadConfig(); logInit( "zmfix" ); logCapLevel( Logger::ERROR ); // Only do registered devices static char sql[ZM_SQL_SML_BUFSIZ]; snprintf( sql, sizeof(sql), "select distinct Device from Monitors where not isnull(Device) and Type = 'Local'" ); 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 ) ); } for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { fixDevice( dbrow[0] ); } if ( mysql_errno( &dbconn ) ) { Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } // Yadda yadda mysql_free_result( result ); snprintf( sql, sizeof(sql), "select distinct ControlDevice from Monitors where not isEmpty(ControlDevice)" ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } result = mysql_store_result( &dbconn ); if ( !result ) { Error( "Can't use query result: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { fixDevice( dbrow[0] ); } if ( mysql_errno( &dbconn ) ) { Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } // Yadda yadda mysql_free_result( result ); if ( config.opt_x10 ) { if ( config.x10_device ) { fixDevice( config.x10_device ); } } return( 0 ); } ZoneMinder-1.26.5/src/zms.cpp000066400000000000000000000217341225361755400160040ustar00rootroot00000000000000// // ZoneMinder Streaming Server, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include #include #include "zm.h" #include "zm_db.h" #include "zm_user.h" #include "zm_signal.h" #include "zm_monitor.h" bool ValidateAccess( User *user, int mon_id ) { bool allowed = true; if ( mon_id > 0 ) { if ( user->getStream() < User::PERM_VIEW ) allowed = false; if ( !user->canAccess( mon_id ) ) allowed = false; } else { if ( user->getEvents() < User::PERM_VIEW ) allowed = false; } if ( !allowed ) { Error( "Error, insufficient privileges for requested action" ); exit( -1 ); } return( allowed ); } int main( int argc, const char *argv[] ) { self = argv[0]; srand( getpid() * time( 0 ) ); enum { ZMS_MONITOR, ZMS_EVENT } source = ZMS_MONITOR; enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, ZMS_ZIP, ZMS_SINGLE } mode = ZMS_JPEG; char format[32] = ""; int monitor_id = 0; time_t event_time = 0; int event_id = 0; int frame_id = 1; unsigned int scale = 100; unsigned int rate = 100; double maxfps = 10.0; unsigned int bitrate = 100000; unsigned int ttl = 0; EventStream::StreamMode replay = EventStream::MODE_SINGLE; char username[64] = ""; char password[64] = ""; char auth[64] = ""; unsigned int connkey = 0; unsigned int playback_buffer = 0; bool nph = false; const char *basename = strrchr( argv[0], '/' ); if (basename) //if we found a / lets skip past it basename++; else //argv[0] will not always contain the full path, but rather just the script name basename = argv[0]; const char *nph_prefix = "nph-"; if ( basename && !strncmp( basename, nph_prefix, strlen(nph_prefix) ) ) { nph = true; } zmLoadConfig(); logInit( "zms" ); ssedetect(); zmSetDefaultTermHandler(); zmSetDefaultDieHandler(); const char *query = getenv( "QUERY_STRING" ); if ( query ) { Debug( 1, "Query: %s", query ); char temp_query[1024]; strncpy( temp_query, query, sizeof(temp_query) ); char *q_ptr = temp_query; char *parms[16]; // Shouldn't be more than this int parm_no = 0; while( (parm_no < 16) && (parms[parm_no] = strtok( q_ptr, "&" )) ) { parm_no++; q_ptr = NULL; } for ( int p = 0; p < parm_no; p++ ) { char *name = strtok( parms[p], "=" ); char *value = strtok( NULL, "=" ); if ( !value ) value = (char *)""; if ( !strcmp( name, "source" ) ) { source = !strcmp( value, "event" )?ZMS_EVENT:ZMS_MONITOR; } else if ( !strcmp( name, "mode" ) ) { mode = !strcmp( value, "jpeg" )?ZMS_JPEG:ZMS_MPEG; mode = !strcmp( value, "raw" )?ZMS_RAW:mode; mode = !strcmp( value, "zip" )?ZMS_ZIP:mode; mode = !strcmp( value, "single" )?ZMS_SINGLE:mode; } else if ( !strcmp( name, "format" ) ) strncpy( format, value, sizeof(format) ); else if ( !strcmp( name, "monitor" ) ) monitor_id = atoi( value ); else if ( !strcmp( name, "time" ) ) event_time = atoi( value ); else if ( !strcmp( name, "event" ) ) event_id = strtoull( value, (char **)NULL, 10 ); else if ( !strcmp( name, "frame" ) ) frame_id = strtoull( value, (char **)NULL, 10 ); else if ( !strcmp( name, "scale" ) ) scale = atoi( value ); else if ( !strcmp( name, "rate" ) ) rate = atoi( value ); else if ( !strcmp( name, "maxfps" ) ) maxfps = atof( value ); else if ( !strcmp( name, "bitrate" ) ) bitrate = atoi( value ); else if ( !strcmp( name, "ttl" ) ) ttl = atoi(value); else if ( !strcmp( name, "replay" ) ) { replay = !strcmp( value, "gapless" )?EventStream::MODE_ALL_GAPLESS:EventStream::MODE_SINGLE; replay = !strcmp( value, "all" )?EventStream::MODE_ALL:replay; } else if ( !strcmp( name, "connkey" ) ) connkey = atoi(value); else if ( !strcmp( name, "buffer" ) ) playback_buffer = atoi(value); else if ( config.opt_use_auth ) { if ( strcmp( config.auth_relay, "none" ) == 0 ) { if ( !strcmp( name, "user" ) ) { strncpy( username, value, sizeof(username) ); } } else { //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) { if ( !strcmp( name, "auth" ) ) { strncpy( auth, value, sizeof(auth) ); } } //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) { if ( !strcmp( name, "user" ) ) { strncpy( username, value, sizeof(username) ); } if ( !strcmp( name, "pass" ) ) { strncpy( password, value, sizeof(password) ); } } } } } } if ( config.opt_use_auth ) { User *user = 0; if ( strcmp( config.auth_relay, "none" ) == 0 ) { if ( *username ) { user = zmLoadUser( username ); } } else { //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) { if ( *auth ) { user = zmLoadAuthUser( auth, config.auth_hash_ips ); } } //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) { if ( *username && *password ) { user = zmLoadUser( username, password ); } } } if ( !user ) { Error( "Unable to authenticate user" ); return( -1 ); } ValidateAccess( user, monitor_id ); } setbuf( stdout, 0 ); if ( nph ) { fprintf( stdout, "HTTP/1.0 200 OK\r\n" ); } fprintf( stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION ); time_t now = time( 0 ); char date_string[64]; strftime( date_string, sizeof(date_string)-1, "%a, %d %b %Y %H:%M:%S GMT", gmtime( &now ) ); fprintf( stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" ); fprintf( stdout, "Last-Modified: %s\r\n", date_string ); fprintf( stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n" ); fprintf( stdout, "Cache-Control: post-check=0, pre-check=0\r\n" ); fprintf( stdout, "Pragma: no-cache\r\n"); // Removed as causing more problems than it fixed. //if ( !nph ) //{ //fprintf( stdout, "Content-Length: 0\r\n"); //} if ( source == ZMS_MONITOR ) { MonitorStream stream; stream.setStreamScale( scale ); stream.setStreamReplayRate( rate ); stream.setStreamMaxFPS( maxfps ); stream.setStreamTTL( ttl ); stream.setStreamQueue( connkey ); stream.setStreamBuffer( playback_buffer ); stream.setStreamStart( monitor_id ); if ( mode == ZMS_JPEG ) { stream.setStreamType( MonitorStream::STREAM_JPEG ); } else if ( mode == ZMS_RAW ) { stream.setStreamType( MonitorStream::STREAM_RAW ); } else if ( mode == ZMS_ZIP ) { stream.setStreamType( MonitorStream::STREAM_ZIP ); } else if ( mode == ZMS_SINGLE ) { stream.setStreamType( MonitorStream::STREAM_SINGLE ); } else { #if HAVE_LIBAVCODEC stream.setStreamFormat( format ); stream.setStreamBitrate( bitrate ); stream.setStreamType( MonitorStream::STREAM_MPEG ); #else // HAVE_LIBAVCODEC 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" ); return( -1 ); #endif // HAVE_LIBAVCODEC } stream.runStream(); } else if ( source == ZMS_EVENT ) { EventStream stream; stream.setStreamScale( scale ); stream.setStreamReplayRate( rate ); stream.setStreamMaxFPS( maxfps ); stream.setStreamMode( replay ); stream.setStreamQueue( connkey ); if ( monitor_id && event_time ) { stream.setStreamStart( monitor_id, event_time ); } else { stream.setStreamStart( event_id, frame_id ); } if ( mode == ZMS_JPEG ) { stream.setStreamType( EventStream::STREAM_JPEG ); } else { #if HAVE_LIBAVCODEC stream.setStreamFormat( format ); stream.setStreamBitrate( bitrate ); stream.setStreamType( EventStream::STREAM_MPEG ); #else // HAVE_LIBAVCODEC Error( "MPEG streaming of '%s' attempted while disabled", query ); fprintf( stderr, "MPEG streaming is disabled.\nYou should ensure the ffmpeg libraries are installed and detected and rebuild to use this functionality.\n" ); return( -1 ); #endif // HAVE_LIBAVCODEC } stream.runStream(); } return( 0 ); } ZoneMinder-1.26.5/src/zmstreamer.cpp000066400000000000000000000157551225361755400173720ustar00rootroot00000000000000// // ZoneMinder Streamer, $Date: 2010-10-14 23:21:00 +0200 (Thu, 14 Oct 2010) $ // Copyright (C) 2001-2010 Philip Coombes, Chris Kistner // // This program is based on revision 3143 of // http://svn.zoneminder.com/svn/zm/trunk/src/zms.cpp // // 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. #include #include #include #include #include "zm.h" #include "zm_db.h" #include "zm_user.h" #include "zm_signal.h" #include "zm_monitor.h" #include "zm_stream.h" // Possible command-line options #define OPTIONS "e:o:u:f:s:b:m:d:i:?" // Default ZMS values #define ZMS_DEFAULT_DEBUG 0 #define ZMS_DEFAULT_ID 1 #define ZMS_DEFAULT_BITRATE 100000 #define ZMS_DEFAULT_SCALE 100 #define ZMS_DEFAULT_MODE "mpeg" #define ZMS_DEFAULT_FORMAT "asf" #define ZMS_DEFAULT_FPS 25.0 #define ZMS_DEFAULT_BUFFER 1000 int main(int argc, char** argv) { self = argv[0]; // Set initial values to the default values int debug = ZMS_DEFAULT_DEBUG; int id = ZMS_DEFAULT_ID; int bitrate = ZMS_DEFAULT_BITRATE; int scale = ZMS_DEFAULT_SCALE; char mode[32]; sprintf(mode, "%s", ZMS_DEFAULT_MODE); char format[32]; sprintf(format, "%s", ZMS_DEFAULT_FORMAT); double maxfps = ZMS_DEFAULT_FPS; int buffer = ZMS_DEFAULT_BUFFER; // Parse command-line options int arg; while ((arg = getopt(argc, argv, OPTIONS)) != -1) { switch (arg) { case 'e': sprintf(mode, "%s", optarg); break; case 'o': sprintf(format, "%s", optarg); break; case 'u': buffer = atoi(optarg); break; case 'f': maxfps = atof(optarg); break; case 's': scale = atoi(optarg); break; case 'b': bitrate = atoi(optarg); break; case 'm': id = atoi(optarg); break; case 'd': debug = atoi(optarg); break; case 'i': case '?': printf("-e : Specify output mode: mpeg/jpg/zip/single/raw. Default = %s\n", ZMS_DEFAULT_MODE); printf("-o : Specify output format. Default = %s\n", ZMS_DEFAULT_FORMAT); printf("-u : Specify buffer size in ms. Default = %d\n", ZMS_DEFAULT_BUFFER); printf("-f : Specify maximum framerate. Default = %lf\n", ZMS_DEFAULT_FPS); printf("-s : Specify scale. Default = %d\n", ZMS_DEFAULT_SCALE); printf("-b : Specify bitrate. Default = %d\n", ZMS_DEFAULT_BITRATE); printf("-m : Specify monitor id. Default = %d\n", ZMS_DEFAULT_ID); printf("-d : 0 = off, 1 = no streaming, 2 = with streaming. Default = 0\n"); printf("-i or -? : This information\n"); return EXIT_SUCCESS; } } // Set stream type StreamBase::StreamType streamtype; if (!strcasecmp("raw", mode)) streamtype = MonitorStream::STREAM_RAW; else if (!strcasecmp("mpeg", mode)) streamtype = MonitorStream::STREAM_MPEG; else if (!strcasecmp("jpg", mode)) streamtype = MonitorStream::STREAM_JPEG; else if (!strcasecmp("single", mode)) streamtype = MonitorStream::STREAM_SINGLE; else if (!strcasecmp("zip", mode)) streamtype = MonitorStream::STREAM_ZIP; else streamtype = MonitorStream::STREAM_MPEG; if (debug) { // Show stream parameters printf("Stream parameters:\n"); switch (streamtype) { case MonitorStream::STREAM_MPEG: printf("Output mode (-e) = %s\n", "mpeg"); printf("Output format (-o) = %s\n", format); break; default: printf("Output mode (-e) = %s\n", mode); } printf("Buffer size (-u) = %d ms\n", buffer); printf("Maximum FPS (-f) = %lf FPS\n", maxfps); printf("Scale (-s) = %d%%\n", scale); printf("Bitrate (-b) = %d bps\n", bitrate); printf("Monitor Id (-m) = %d\n", id); } if (debug) { // Set ZM debugger to print to stdout printf("Setting up ZoneMinder debugger to print to stdout..."); setenv("ZM_DBG_PRINT", "1", 1); printf("Done.\n"); } // Loading ZM configurations printf("Loading ZoneMinder configurations..."); zmLoadConfig(); printf("Done.\n"); logInit("zmstreamer"); ssedetect(); // Setting stream parameters MonitorStream stream; stream.setStreamScale(scale); // default = 100 (scale) stream.setStreamReplayRate(100); // default = 100 (rate) stream.setStreamMaxFPS(maxfps); // default = 10 (maxfps) if (debug) stream.setStreamTTL(1); else stream.setStreamTTL(0); // default = 0 (ttl) stream.setStreamQueue(0); // default = 0 (connkey) stream.setStreamBuffer(buffer); // default = 0 (buffer) stream.setStreamStart(id); // default = 0 (monitor_id) stream.setStreamType(streamtype); if (streamtype == MonitorStream::STREAM_MPEG) { #if HAVE_LIBAVCODEC if (debug) printf("HAVE_LIBAVCODEC is set\n"); stream.setStreamFormat(format); // default = "" (format) stream.setStreamBitrate(bitrate); // default = 100000 (bitrate) #else fprintf(stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n"); return EXIT_FAILURE; #endif } if (debug != 1) { if (debug) printf("Running stream..."); // Output headers fprintf(stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION); time_t now = time(0); char date_string[64]; strftime(date_string, sizeof (date_string) - 1, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); fprintf(stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"); fprintf(stdout, "Last-Modified: %s\r\n", date_string); fprintf(stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n"); fprintf(stdout, "Cache-Control: post-check=0, pre-check=0\r\n"); fprintf(stdout, "Pragma: no-cache\r\n"); // Run stream stream.runStream(); } if (debug) printf("Done.\n"); return (EXIT_SUCCESS); } ZoneMinder-1.26.5/src/zmu.cpp000066400000000000000000000506011225361755400160010ustar00rootroot00000000000000// // ZoneMinder Control Utility, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // #include #include "zm.h" #include "zm_db.h" #include "zm_user.h" #include "zm_signal.h" #include "zm_monitor.h" #include "zm_local_camera.h" void Usage( int status=-1 ) { fprintf( stderr, "zmu <-d device_path> [-v] [function] [-U -P]\n" ); fprintf( stderr, "zmu <-m monitor_id> [-v] [function] [-U -P]\n" ); fprintf( stderr, "General options:\n" ); fprintf( stderr, " -h, --help : This screen\n" ); fprintf( stderr, " -v, --verbose : Produce more verbose output\n" ); fprintf( stderr, " -l, --list : List the current status of active (or all with -v) monitors\n" ); fprintf( stderr, "Options for use with devices:\n" ); fprintf( stderr, " -d, --device [device_path] : Get the current video device settings for [device_path] or all devices\n" ); fprintf( stderr, " -V, --version : Set the Video 4 Linux API version to use for the query, use 1 or 2\n" ); fprintf( stderr, " -q, --query : Query the current settings for the device\n" ); fprintf( stderr, "Options for use with monitors:\n" ); fprintf( stderr, " -m, --monitor : Specify which monitor to address, default 1 if absent\n" ); fprintf( stderr, " -q, --query : Query the current settings for the monitor\n" ); fprintf( stderr, " -s, --state : Output the current monitor state, 0 = idle, 1 = prealarm, 2 = alarm,\n" ); fprintf( stderr, " 3 = alert, 4 = tape\n" ); fprintf( stderr, " -B, --brightness [value] : Output the current brightness, set to value if given \n" ); fprintf( stderr, " -C, --contrast [value] : Output the current contrast, set to value if given \n" ); fprintf( stderr, " -H, --hue [value] : Output the current hue, set to value if given \n" ); fprintf( stderr, " -O, --colour [value] : Output the current colour, set to value if given \n" ); fprintf( stderr, " -i, --image [image_index] : Write captured image to disk as .jpg, last image captured\n" ); fprintf( stderr, " or specified ring buffer index if given.\n" ); fprintf( stderr, " -S, --scale : With --image specify any scaling (in %%) to be applied to the image\n" ); fprintf( stderr, " -t, --timestamp [image_index] : Output captured image timestamp, last image captured or specified\n" ); fprintf( stderr, " ring buffer index if given\n" ); fprintf( stderr, " -R, --read_index : Output ring buffer read index\n" ); fprintf( stderr, " -W, --write_index : Output ring buffer write index\n" ); fprintf( stderr, " -e, --event : Output last event index\n" ); fprintf( stderr, " -f, --fps : Output last Frames Per Second captured reading\n" ); fprintf( stderr, " -z, --zones : Write last captured image overlaid with zones to -Zones.jpg\n" ); fprintf( stderr, " -a, --alarm : Force alarm in monitor, this will trigger recording until cancelled with -c\n" ); fprintf( stderr, " -n, --noalarm : Force no alarms in monitor, this will prevent alarms until cancelled with -c\n" ); fprintf( stderr, " -c, --cancel : Cancel a forced alarm/noalarm in monitor, required after being enabled with -a or -n\n" ); fprintf( stderr, " -L, --reload : Signal monitor to reload settings\n" ); fprintf( stderr, " -E, --enable : Enable detection, wake monitor up\n" ); fprintf( stderr, " -D, --disable : Disable detection, put monitor to sleep\n" ); fprintf( stderr, " -u, --suspend : Suspend detection, useful to prevent bogus alarms when panning etc\n" ); fprintf( stderr, " -r, --resume : Resume detection after a suspend\n" ); fprintf( stderr, " -U, --username : When running in authenticated mode the username and\n" ); fprintf( stderr, " -P, --password : password combination of the given user\n" ); fprintf( stderr, " -A, --auth : Pass authentication hash string instead of user details\n" ); exit( status ); } typedef enum { ZMU_BOGUS = 0x00000000, ZMU_STATE = 0x00000001, ZMU_IMAGE = 0x00000002, ZMU_TIME = 0x00000004, ZMU_READ_IDX = 0x00000008, ZMU_WRITE_IDX = 0x00000010, ZMU_EVENT = 0x00000020, ZMU_FPS = 0x00000040, ZMU_ZONES = 0x00000080, ZMU_ALARM = 0x00000100, ZMU_NOALARM = 0x00000200, ZMU_CANCEL = 0x00000400, ZMU_QUERY = 0x00000800, ZMU_BRIGHTNESS = 0x00001000, ZMU_CONTRAST = 0x00002000, ZMU_HUE = 0x00004000, ZMU_COLOUR = 0x00008000, ZMU_RELOAD = 0x00010000, ZMU_ENABLE = 0x00100000, ZMU_DISABLE = 0x00200000, ZMU_SUSPEND = 0x00400000, ZMU_RESUME = 0x00800000, ZMU_LIST = 0x10000000, } Function; bool ValidateAccess( User *user, int mon_id, int function ) { bool allowed = true; if ( function & (ZMU_STATE|ZMU_IMAGE|ZMU_TIME|ZMU_READ_IDX|ZMU_WRITE_IDX|ZMU_FPS) ) { if ( user->getStream() < User::PERM_VIEW ) allowed = false; } if ( function & ZMU_EVENT ) { if ( user->getEvents() < User::PERM_VIEW ) allowed = false; } if ( function & (ZMU_ZONES|ZMU_QUERY|ZMU_LIST) ) { if ( user->getMonitors() < User::PERM_VIEW ) allowed = false; } if ( function & (ZMU_ALARM|ZMU_NOALARM|ZMU_CANCEL|ZMU_RELOAD|ZMU_ENABLE|ZMU_DISABLE|ZMU_SUSPEND|ZMU_RESUME|ZMU_BRIGHTNESS|ZMU_CONTRAST|ZMU_HUE|ZMU_COLOUR) ) { if ( user->getMonitors() < User::PERM_EDIT ) allowed = false; } if ( mon_id > 0 ) { if ( !user->canAccess( mon_id ) ) { allowed = false; } } if ( !allowed ) { fprintf( stderr, "Error, insufficient privileges for requested action\n" ); exit( -1 ); } return( allowed ); } int main( int argc, char *argv[] ) { self = argv[0]; srand( getpid() * time( 0 ) ); static struct option long_options[] = { {"device", 2, 0, 'd'}, {"monitor", 1, 0, 'm'}, {"verbose", 0, 0, 'v'}, {"image", 2, 0, 'i'}, {"scale", 1, 0, 'S'}, {"timestamp", 2, 0, 't'}, {"state", 0, 0, 's'}, {"brightness", 2, 0, 'B'}, {"contrast", 2, 0, 'C'}, {"hue", 2, 0, 'H'}, {"contrast", 2, 0, 'O'}, {"read_index", 0, 0, 'R'}, {"write_index", 0, 0, 'W'}, {"event", 0, 0, 'e'}, {"fps", 0, 0, 'f'}, {"zones", 2, 0, 'z'}, {"alarm", 0, 0, 'a'}, {"noalarm", 0, 0, 'n'}, {"cancel", 0, 0, 'c'}, {"reload", 0, 0, 'L'}, {"enable", 0, 0, 'E'}, {"disable", 0, 0, 'D'}, {"suspend", 0, 0, 'u'}, {"resume", 0, 0, 'r'}, {"query", 0, 0, 'q'}, {"username", 1, 0, 'U'}, {"password", 1, 0, 'P'}, {"auth", 1, 0, 'A'}, {"version", 1, 0, 'V'}, {"help", 0, 0, 'h'}, {"list", 0, 0, 'l'}, {0, 0, 0, 0} }; const char *device = 0; int mon_id = 0; bool verbose = false; int function = ZMU_BOGUS; int image_idx = -1; int scale = -1; int brightness = -1; int contrast = -1; int hue = -1; int colour = -1; char *zoneString = 0; char *username = 0; char *password = 0; char *auth = 0; #if ZM_HAS_V4L #if ZM_HAS_V4L2 int v4lVersion = 2; #elif ZM_HAS_V4L1 int v4lVersion = 1; #endif // ZM_HAS_V4L2/1 #endif // ZM_HAS_V4L while (1) { int option_index = 0; int c = getopt_long (argc, argv, "d:m:vsEDLurwei::S:t::fz::ancqhlB::C::H::O::U:P:A:V:", long_options, &option_index); if (c == -1) { break; } switch (c) { case 'd': if ( optarg ) device = optarg; break; case 'm': mon_id = atoi(optarg); break; case 'v': verbose = true; break; case 's': function |= ZMU_STATE; break; case 'i': function |= ZMU_IMAGE; if ( optarg ) image_idx = atoi( optarg ); break; case 'S': scale = atoi(optarg); break; case 't': function |= ZMU_TIME; if ( optarg ) image_idx = atoi( optarg ); break; case 'R': function |= ZMU_READ_IDX; break; case 'W': function |= ZMU_WRITE_IDX; break; case 'e': function |= ZMU_EVENT; break; case 'f': function |= ZMU_FPS; break; case 'z': function |= ZMU_ZONES; if ( optarg ) zoneString = optarg; break; case 'a': function |= ZMU_ALARM; break; case 'n': function |= ZMU_NOALARM; break; case 'c': function |= ZMU_CANCEL; break; case 'L': function |= ZMU_RELOAD; break; case 'E': function |= ZMU_ENABLE; break; case 'D': function |= ZMU_DISABLE; break; case 'u': function |= ZMU_SUSPEND; break; case 'r': function |= ZMU_RESUME; break; case 'q': function |= ZMU_QUERY; break; case 'B': function |= ZMU_BRIGHTNESS; if ( optarg ) brightness = atoi( optarg ); break; case 'C': function |= ZMU_CONTRAST; if ( optarg ) contrast = atoi( optarg ); break; case 'H': function |= ZMU_HUE; if ( optarg ) hue = atoi( optarg ); break; case 'O': function |= ZMU_COLOUR; if ( optarg ) colour = atoi( optarg ); break; case 'U': username = optarg; break; case 'P': password = optarg; break; case 'A': auth = optarg; break; #if ZM_HAS_V4L case 'V': v4lVersion = (atoi(optarg)==1)?1:2; break; #endif // ZM_HAS_V4L case 'h': Usage( 0 ); break; case 'l': function |= ZMU_LIST; break; case '?': Usage(); break; default: //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); break; } } if (optind < argc) { fprintf( stderr, "Extraneous options, " ); while (optind < argc) fprintf( stderr, "%s ", argv[optind++]); fprintf( stderr, "\n"); Usage(); } if ( device && !(function&ZMU_QUERY) ) { fprintf( stderr, "Error, -d option cannot be used with this option\n" ); Usage(); } if ( scale != -1 && !(function&ZMU_IMAGE) ) { fprintf( stderr, "Error, -S option cannot be used with this option\n" ); Usage(); } //printf( "Monitor %d, Function %d\n", mon_id, function ); zmLoadConfig(); logInit( "zmu" ); zmSetDefaultTermHandler(); zmSetDefaultDieHandler(); User *user = 0; if ( config.opt_use_auth ) { if ( strcmp( config.auth_relay, "none" ) == 0 ) { if ( !username ) { fprintf( stderr, "Error, username must be supplied\n" ); exit( -1 ); } if ( username ) { user = zmLoadUser( username ); } } else { if ( !(username && password) && !auth ) { fprintf( stderr, "Error, username and password or auth string must be supplied\n" ); exit( -1 ); } //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) { if ( auth ) { user = zmLoadAuthUser( auth, false ); } } //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) { if ( username && password ) { user = zmLoadUser( username, password ); } } } if ( !user ) { fprintf( stderr, "Error, unable to authenticate user\n" ); exit( -1 ); } ValidateAccess( user, mon_id, function ); } if ( mon_id > 0 ) { Monitor *monitor = Monitor::Load( mon_id, function&(ZMU_QUERY|ZMU_ZONES), Monitor::QUERY ); if ( monitor ) { if ( verbose ) { printf( "Monitor %d(%s)\n", monitor->Id(), monitor->Name() ); } char separator = ' '; bool have_output = false; if ( function & ZMU_STATE ) { Monitor::State state = monitor->GetState(); if ( verbose ) printf( "Current state: %s\n", state==Monitor::ALARM?"Alarm":(state==Monitor::ALERT?"Alert":"Idle") ); else { if ( have_output ) printf( "%c", separator ); printf( "%d", state ); have_output = true; } } if ( function & ZMU_TIME ) { struct timeval timestamp = monitor->GetTimestamp( image_idx ); if ( verbose ) { char timestamp_str[64] = "None"; if ( timestamp.tv_sec ) strftime( timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", localtime( ×tamp.tv_sec ) ); if ( image_idx == -1 ) printf( "Time of last image capture: %s.%02ld\n", timestamp_str, timestamp.tv_usec/10000 ); else printf( "Time of image %d capture: %s.%02ld\n", image_idx, timestamp_str, timestamp.tv_usec/10000 ); } else { if ( have_output ) printf( "%c", separator ); printf( "%ld.%02ld", timestamp.tv_sec, timestamp.tv_usec/10000 ); have_output = true; } } if ( function & ZMU_READ_IDX ) { if ( verbose ) printf( "Last read index: %d\n", monitor->GetLastReadIndex() ); else { if ( have_output ) printf( "%c", separator ); printf( "%d", monitor->GetLastReadIndex() ); have_output = true; } } if ( function & ZMU_WRITE_IDX ) { if ( verbose ) printf( "Last write index: %d\n", monitor->GetLastWriteIndex() ); else { if ( have_output ) printf( "%c", separator ); printf( "%d", monitor->GetLastWriteIndex() ); have_output = true; } } if ( function & ZMU_EVENT ) { if ( verbose ) printf( "Last event id: %d\n", monitor->GetLastEvent() ); else { if ( have_output ) printf( "%c", separator ); printf( "%d", monitor->GetLastEvent() ); have_output = true; } } if ( function & ZMU_FPS ) { if ( verbose ) printf( "Current capture rate: %.2f frames per second\n", monitor->GetFPS() ); else { if ( have_output ) printf( "%c", separator ); printf( "%.2f", monitor->GetFPS() ); have_output = true; } } if ( function & ZMU_IMAGE ) { if ( verbose ) { if ( image_idx == -1 ) printf( "Dumping last image captured to Monitor%d.jpg", monitor->Id() ); else printf( "Dumping buffer image %d to Monitor%d.jpg", image_idx, monitor->Id() ); if ( scale != -1 ) printf( ", scaling by %d%%", scale ); printf( "\n" ); } monitor->GetImage( image_idx, scale>0?scale:100 ); } if ( function & ZMU_ZONES ) { if ( verbose ) printf( "Dumping zone image to Zones%d.jpg\n", monitor->Id() ); monitor->DumpZoneImage( zoneString ); } if ( function & ZMU_ALARM ) { if ( verbose ) printf( "Forcing alarm on\n" ); monitor->ForceAlarmOn( config.forced_alarm_score, "Forced Web" ); } if ( function & ZMU_NOALARM ) { if ( verbose ) printf( "Forcing alarm off\n" ); monitor->ForceAlarmOff(); } if ( function & ZMU_CANCEL ) { if ( verbose ) printf( "Cancelling forced alarm on/off\n" ); monitor->CancelForced(); } if ( function & ZMU_RELOAD ) { if ( verbose ) printf( "Reloading monitor settings\n" ); monitor->actionReload(); } if ( function & ZMU_ENABLE ) { if ( verbose ) printf( "Enabling event generation\n" ); monitor->actionEnable(); } if ( function & ZMU_DISABLE ) { if ( verbose ) printf( "Disabling event generation\n" ); monitor->actionDisable(); } if ( function & ZMU_SUSPEND ) { if ( verbose ) printf( "Suspending event generation\n" ); monitor->actionSuspend(); } if ( function & ZMU_RESUME ) { if ( verbose ) printf( "Resuming event generation\n" ); monitor->actionResume(); } if ( function & ZMU_QUERY ) { char monString[16382] = ""; monitor->DumpSettings( monString, verbose ); printf( "%s\n", monString ); } if ( function & ZMU_BRIGHTNESS ) { if ( verbose ) { if ( brightness >= 0 ) printf( "New brightness: %d\n", monitor->actionBrightness( brightness ) ); else printf( "Current brightness: %d\n", monitor->actionBrightness() ); } else { if ( have_output ) printf( "%c", separator ); if ( brightness >= 0 ) printf( "%d", monitor->actionBrightness( brightness ) ); else printf( "%d", monitor->actionBrightness() ); have_output = true; } } if ( function & ZMU_CONTRAST ) { if ( verbose ) { if ( contrast >= 0 ) printf( "New brightness: %d\n", monitor->actionContrast( contrast ) ); else printf( "Current contrast: %d\n", monitor->actionContrast() ); } else { if ( have_output ) printf( "%c", separator ); if ( contrast >= 0 ) printf( "%d", monitor->actionContrast( contrast ) ); else printf( "%d", monitor->actionContrast() ); have_output = true; } } if ( function & ZMU_HUE ) { if ( verbose ) { if ( hue >= 0 ) printf( "New hue: %d\n", monitor->actionHue( hue ) ); else printf( "Current hue: %d\n", monitor->actionHue() ); } else { if ( have_output ) printf( "%c", separator ); if ( hue >= 0 ) printf( "%d", monitor->actionHue( hue ) ); else printf( "%d", monitor->actionHue() ); have_output = true; } } if ( function & ZMU_COLOUR ) { if ( verbose ) { if ( colour >= 0 ) printf( "New colour: %d\n", monitor->actionColour( colour ) ); else printf( "Current colour: %d\n", monitor->actionColour() ); } else { if ( have_output ) printf( "%c", separator ); if ( colour >= 0 ) printf( "%d", monitor->actionColour( colour ) ); else printf( "%d", monitor->actionColour() ); have_output = true; } } if ( have_output ) { printf( "\n" ); } if ( !function ) { Usage(); } delete monitor; } else { fprintf( stderr, "Error, invalid monitor id %d\n", mon_id ); exit( -1 ); } } else { if ( function & ZMU_QUERY ) { #if ZM_HAS_V4L char vidString[0x10000] = ""; bool ok = LocalCamera::GetCurrentSettings( device, vidString, v4lVersion, verbose ); printf( "%s", vidString ); exit( ok?0:-1 ); #else // ZM_HAS_V4L fprintf( stderr, "Error, video4linux is required for device querying\n" ); exit( -1 ); #endif // ZM_HAS_V4L } if ( function & ZMU_LIST ) { char sql[ZM_SQL_SML_BUFSIZ]; strncpy( sql, "select Id, Function+0 from Monitors", sizeof(sql) ); if ( !verbose ) { strncat( sql, " where Function != 'None'", sizeof(sql)-strlen(sql) ); } strncat( sql, " order by Id asc", sizeof(sql)-strlen(sql) ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } MYSQL_RES *result = mysql_store_result( &dbconn ); if ( !result ) { Error( "Can't use query result: %s", mysql_error( &dbconn ) ); exit( mysql_errno( &dbconn ) ); } int n_monitors = mysql_num_rows( result ); Debug( 1, "Got %d monitors", n_monitors ); printf( "%4s%5s%6s%9s%14s%6s%6s%8s%8s\n", "Id", "Func", "State", "TrgState", "LastImgTim", "RdIdx", "WrIdx", "LastEvt", "FrmRate" ); for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { int mon_id = atoi(dbrow[0]); int function = atoi(dbrow[1]); if ( !user || user->canAccess( mon_id ) ) { if ( function > 1 ) { Monitor *monitor = Monitor::Load( mon_id, false, Monitor::QUERY ); if ( monitor ) { struct timeval tv = monitor->GetTimestamp(); printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n", monitor->Id(), function, monitor->GetState(), monitor->GetTriggerState(), tv.tv_sec, tv.tv_usec/10000, monitor->GetLastReadIndex(), monitor->GetLastWriteIndex(), monitor->GetLastEvent(), monitor->GetFPS() ); delete monitor; } } else { struct timeval tv = { 0, 0 }; printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n", mon_id, function, 0, 0, tv.tv_sec, tv.tv_usec/10000, 0, 0, 0, 0.0 ); } } } mysql_free_result( result ); } } delete user; return( 0 ); } ZoneMinder-1.26.5/umutils/000077500000000000000000000000001225361755400153735ustar00rootroot00000000000000ZoneMinder-1.26.5/umutils/nextimeconfigure.zm000077500000000000000000000017161225361755400213260ustar00rootroot00000000000000#!/bin/bash #http://tom.webarts.ca/Blog/new-blog-items/buildingzoneminderandrequiredffmpegandx264fromsource export LD_LIBRARY_PATH="/usr/local/lib:/opt/libjpeg-turbo/lib:$LD_LIBRARY_PATH" export LDFLAGS=" -L/home/nextime/zm/libjpeg-turbo-1.2.1/.libs " DEB_HOST_GNU_TYPE=$(dpkg-architecture -qDEB_HOST_GNU_TYPE) DEB_BUILD_GNU_TYPE=$(dpkg-architecture -qDEB_BUILD_GNU_TYPE) CXXFLAGS=" -DZM_FFMPEG_CVS -DHAVE_LIBCRYPTO -msse2 -DJPEG_INCLUDE_DIR=/home/nextime/zm/libjpeg-turbo-1.2.1/ " CXXFLAGS="$CXXFLAGS -I/home/nextime/zm/libjpeg-turbo-1.2.1/ " CXXFLAGS="$CXXFLAGS" ./configure --with-libarch=lib/$DEB_HOST_GNU_TYPE --disable-debug --host=$DEB_HOST_GNU_TYPE --build=$DEB_BUILD_GNU_TYPE --sysconfdir=/etc/zm --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-mysql=/usr --with-webdir=/usr/share/zoneminder --with-ffmpeg=/usr --with-cgidir=/usr/lib/cgi-bin --with-webuser=www-data --with-webgroup=www-data --enable-crashtrace=no --enable-mmap=yes ZoneMinder-1.26.5/version000066400000000000000000000000071225361755400152760ustar00rootroot000000000000001.26.5 ZoneMinder-1.26.5/web/000077500000000000000000000000001225361755400144465ustar00rootroot00000000000000ZoneMinder-1.26.5/web/CMakeLists.txt000066400000000000000000000017761225361755400172210ustar00rootroot00000000000000# CMakeLists.txt for the ZoneMinder web files # Process the tools/mootools subdirectory add_subdirectory(tools/mootools) # Create files from the .in files configure_file(includes/config.php.in "${CMAKE_CURRENT_BINARY_DIR}/includes/config.php" @ONLY) # Install the web files install(DIRECTORY ajax css graphics includes js lang skins tools views DESTINATION "${ZM_WEBDIR}" PATTERN "*.in" EXCLUDE PATTERN "*Make*" EXCLUDE PATTERN "*cmake*" EXCLUDE) install(FILES index.php DESTINATION "${ZM_WEBDIR}") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/includes/config.php" DESTINATION "${ZM_WEBDIR}/includes") # Install the mootools symlinks (if its not in the source directory) if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tools/mootools/mootools-core.js" DESTINATION "${ZM_WEBDIR}/tools/mootools") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tools/mootools/mootools-more.js" DESTINATION "${ZM_WEBDIR}/tools/mootools") endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) ZoneMinder-1.26.5/web/Makefile.am000066400000000000000000000024021225361755400165000ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu # This should be set to your web directory webdir = @WEB_PREFIX@ # And these to the user and group of your webserver webuser = @WEB_USER@ webgroup = @WEB_GROUP@ SUBDIRS = \ ajax \ css \ graphics \ includes \ js \ lang \ skins \ tools \ views dist_web_DATA = \ index.php # Yes, you are correct. This is a HACK! install-data-hook: ( cd $(DESTDIR)$(webdir); chown $(webuser):$(webgroup) $(dist_web_DATA) ) ( cd $(DESTDIR)$(webdir); chown -R $(webuser):$(webgroup) $(SUBDIRS) ) @-( cd $(DESTDIR)$(webdir); if ! test -e events; then mkdir events; fi; chown $(webuser):$(webgroup) events; chmod u+w events ) @-( cd $(DESTDIR)$(webdir); if ! test -e images; then mkdir images; fi; chown $(webuser):$(webgroup) images; chmod u+w images ) @-( cd $(DESTDIR)$(webdir); if ! test -e sounds; then mkdir sounds; fi; chown $(webuser):$(webgroup) sounds; chmod u+w sounds ) @-( cd $(DESTDIR)$(webdir); if ! test -e tools; then mkdir tools; fi; chown $(webuser):$(webgroup) tools; chmod u+w tools ) @-( cd $(DESTDIR)$(webdir); if ! test -e temp; then mkdir temp; fi; chown $(webuser):$(webgroup) temp; chmod u+w temp ) uninstall-hook: @-( cd $(DESTDIR)$(webdir); rm -rf $(SUBDIRS) ) @-( cd $(DESTDIR)$(webdir); rm -rf events images sounds tools temp ) ZoneMinder-1.26.5/web/README.md000066400000000000000000000002001225361755400157150ustar00rootroot00000000000000Modern ZoneMinder Skin ======= This web frontend to ZoneMinder is a complete rewrite of the classic frontend, based on CakePHP. ZoneMinder-1.26.5/web/ajax/000077500000000000000000000000001225361755400153715ustar00rootroot00000000000000ZoneMinder-1.26.5/web/ajax/Makefile.am000066400000000000000000000002401225361755400174210ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/ajax dist_web_DATA = \ alarm.php \ control.php \ event.php \ log.php \ status.php \ stream.php \ zone.php ZoneMinder-1.26.5/web/ajax/alarm.php000066400000000000000000000016131225361755400171770ustar00rootroot00000000000000 ZoneMinder-1.26.5/web/ajax/control.php000066400000000000000000000041121225361755400175600ustar00rootroot00000000000000'.join( ' // ', $ctrlOutput ) ); ajaxResponse( 'Used script' ); } } else { ajaxError( "No command received" ); } } ajaxError( 'Unrecognised action or insufficient permissions' ); function ajaxCleanup() { global $socket; if ( !empty( $socket ) ) @socket_close( $socket ); } ?> ZoneMinder-1.26.5/web/ajax/event.php000066400000000000000000000113021225361755400172200ustar00rootroot00000000000000$videoFile ) ); else ajaxError( "Video Generation Failed" ); } $ok = true; break; } case 'deleteVideo' : { unlink( $videoFiles[$_REQUEST['id']] ); unset( $videoFiles[$_REQUEST['id']] ); ajaxResponse(); break; } case "export" : { require_once( ZM_SKIN_PATH.'/includes/export_functions.php' ); if ( !empty($_REQUEST['exportDetail']) ) $exportDetail = $_SESSION['export']['detail'] = $_REQUEST['exportDetail']; else $exportDetail = false; if ( !empty($_REQUEST['exportFrames']) ) $exportFrames = $_SESSION['export']['frames'] = $_REQUEST['exportFrames']; else $exportFrames = false; if ( !empty($_REQUEST['exportImages']) ) $exportImages = $_SESSION['export']['images'] = $_REQUEST['exportImages']; else $exportImages = false; if ( !empty($_REQUEST['exportVideo']) ) $exportVideo = $_SESSION['export']['video'] = $_REQUEST['exportVideo']; else $exportVideo = false; if ( !empty($_REQUEST['exportMisc']) ) $exportMisc = $_SESSION['export']['misc'] = $_REQUEST['exportMisc']; else $exportMisc = false; if ( !empty($_REQUEST['exportFormat']) ) $exportFormat = $_SESSION['export']['format'] = $_REQUEST['exportFormat']; else $exportFormat = ''; $exportIds = !empty($_REQUEST['eids'])?$_REQUEST['eids']:$_REQUEST['id']; if ( $exportFile = exportEvents( $exportIds, $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc, $exportFormat ) ) ajaxResponse( array( 'exportFile'=>$exportFile ) ); else ajaxError( "Export Failed" ); break; } } } if ( canEdit( 'Events' ) ) { switch ( $_REQUEST['action'] ) { case "rename" : { if ( !empty($_REQUEST['eventName']) ) dbQuery( "update Events set Name = '".dbEscape($_REQUEST['eventName'])."' where Id = '".dbEscape($_REQUEST['id'])."'" ); else ajaxError( "No new event name supplied" ); ajaxResponse( array( 'refreshEvent'=>true, 'refreshParent'=>true ) ); break; } case "eventdetail" : { dbQuery( "update Events set Cause = '".dbEscape($_REQUEST['newEvent']['Cause'])."', Notes = '".dbEscape($_REQUEST['newEvent']['Notes'])."' where Id = '".dbEscape($_REQUEST['id'])."'" ); ajaxResponse( array( 'refreshEvent'=>true, 'refreshParent'=>true ) ); break; } case "archive" : case "unarchive" : { $archiveVal = ($_REQUEST['action'] == "archive")?1:0; dbQuery( "update Events set Archived = ".$archiveVal." where Id = '".dbEscape($_REQUEST['id'])."'" ); ajaxResponse( array( 'refreshEvent'=>true, 'refreshParent'=>false ) ); break; } case "delete" : { deleteEvent( dbEscape($_REQUEST['id']) ); ajaxResponse( array( 'refreshEvent'=>false, 'refreshParent'=>true ) ); break; } } } ajaxError( 'Unrecognised action or insufficient permissions' ); ?> ZoneMinder-1.26.5/web/ajax/log.php000066400000000000000000000332051225361755400166660ustar00rootroot00000000000000 "web_js" ) ); $string = $_POST['message']; $file = preg_replace( '/\w+:\/\/\w+\//', '', $_POST['file'] ); if ( !empty( $_POST['line'] ) ) $line = $_POST['line']; else $line = NULL; $levels = array_flip(Logger::$codes); if ( !isset($levels[$_POST['level']]) ) Panic( "Unexpected logger level '".$_POST['level']."'" ); $level = $levels[$_POST['level']]; Logger::fetch()->logPrint( $level, $string, $file, $line ); } ajaxResponse(); break; } case 'query' : { if ( !canView( 'System' ) ) ajaxError( 'Insufficient permissions to view log entries' ); $minTime = isset($_POST['minTime'])?$_POST['minTime']:NULL; $maxTime = isset($_POST['maxTime'])?$_POST['maxTime']:NULL; $limit = isset($_POST['limit'])?$_POST['limit']:1000; $filter = isset($_POST['filter'])?$_POST['filter']:array(); $sortField = isset($_POST['sortField'])?$_POST['sortField']:'TimeKey'; $sortOrder = isset($_POST['sortOrder'])?$_POST['sortOrder']:'desc'; $filterFields = array( 'Component', 'Pid', 'Level', 'File', 'Line' ); //$filterSql = $filter?' where $countSql = "select count(*) as Total from Logs"; $total = dbFetchOne( $countSql, 'Total' ); $sql = "select * from Logs"; $where = array(); if ( $minTime ) $where[] = "TimeKey > ".dbEscape($minTime); elseif ( $maxTime ) $where[] = "TimeKey < ".dbEscape($maxTime); foreach ( $filter as $field=>$value ) if ( $field == 'Level' ) $where[] = dbEscape($field)." <= ".dbEscape($value); else $where[] = dbEscape($field)." = '".dbEscape($value)."'"; if ( count($where) ) $sql.= " where ".join( " and ", $where ); $sql .= " order by ".dbEscape($sortField)." ".dbEscape($sortOrder)." limit ".dbEscape($limit); $logs = array(); foreach ( dbFetchAll( $sql ) as $log ) { $log['DateTime'] = preg_replace( '/^\d+/', strftime( "%Y-%m-%d %H:%M:%S", intval($log['TimeKey']) ), $log['TimeKey'] ); $logs[] = $log; } $options = array(); $where = array(); foreach( $filter as $field=>$value ) if ( $field == 'Level' ) $where[$field] = dbEscape($field)." <= ".dbEscape($value); else $where[$field] = dbEscape($field)." = '".dbEscape($value)."'"; foreach( $filterFields as $field ) { $sql = "select distinct $field from Logs where not isnull($field)"; $fieldWhere = array_diff_key( $where, array( $field=>true ) ); if ( count($fieldWhere) ) $sql.= " and ".join( " and ", $fieldWhere ); $sql.= " order by $field asc"; if ( $field == 'Level' ) { foreach( dbFetchAll( $sql, $field ) as $value ) if ( $value <= Logger::INFO ) $options[$field][$value] = Logger::$codes[$value]; else $options[$field][$value] = "DB".$value; } else { foreach( dbFetchAll( $sql, $field ) as $value ) if ( $value != '' ) $options[$field][] = $value; } } if ( count($filter) ) { $sql = "select count(*) as Available from Logs where ".join( " and ", $where ); $available = dbFetchOne( $sql, 'Available' ); } ajaxResponse( array( 'updated' => preg_match( '/%/', DATE_FMT_CONSOLE_LONG )?strftime( DATE_FMT_CONSOLE_LONG ):date( DATE_FMT_CONSOLE_LONG ), 'total' => $total, 'available' => isset($available)?$available:$total, 'logs' => $logs, 'state' => logState(), 'options' => $options ) ); break; } case 'export' : { if ( !canView( 'System' ) ) ajaxError( 'Insufficient permissions to export logs' ); $minTime = isset($_POST['minTime'])?$_POST['minTime']:NULL; $maxTime = isset($_POST['maxTime'])?$_POST['maxTime']:NULL; if ( !is_null($minTime) && !is_null($maxTime) && $minTime > $maxTime ) { $tempTime = $minTime; $minTime = $maxTime; $maxTime = $tempTime; } //$limit = isset($_POST['limit'])?$_POST['limit']:1000; $filter = isset($_POST['filter'])?$_POST['filter']:array(); $sortField = isset($_POST['sortField'])?$_POST['sortField']:'TimeKey'; $sortOrder = isset($_POST['sortOrder'])?$_POST['sortOrder']:'asc'; $sql = "select * from Logs"; $where = array(); if ( $minTime ) { preg_match( '/(.+)(\.\d+)/', $minTime, $matches ); $minTime = strtotime($matches[1]).$matches[2]; $where[] = "TimeKey >= ".$minTime; } if ( $maxTime ) { preg_match( '/(.+)(\.\d+)/', $maxTime, $matches ); $maxTime = strtotime($matches[1]).$matches[2]; $where[] = "TimeKey <= ".$maxTime; } foreach ( $filter as $field=>$value ) if ( $value != '' ) if ( $field == 'Level' ) $where[] = dbEscape($field)." <= ".dbEscape($value); else $where[] = dbEscape($field)." = '".dbEscape($value)."'"; if ( count($where) ) $sql.= " where ".join( " and ", $where ); $sql .= " order by ".dbEscape($sortField)." ".dbEscape($sortOrder); //$sql .= " limit ".dbEscape($limit); $format = isset($_POST['format'])?$_POST['format']:'text'; switch( $format ) { case 'text' : $exportExt = "txt"; break; case 'tsv' : $exportExt = "tsv"; break; case 'html' : $exportExt = "html"; break; case 'xml' : $exportExt = "xml"; break; default : Fatal( "Unrecognised log export format '$format'" ); } $exportKey = substr(md5(rand()),0,8); $exportFile = "zm-log.$exportExt"; $exportPath = "temp/zm-log-$exportKey.$exportExt"; if ( !($exportFP = fopen( $exportPath, "w" )) ) Fatal( "Unable to open log export file $exportFile" ); $logs = array(); foreach ( dbFetchAll( $sql ) as $log ) { $log['DateTime'] = preg_replace( '/^\d+/', strftime( "%Y-%m-%d %H:%M:%S", intval($log['TimeKey']) ), $log['TimeKey'] ); $logs[] = $log; } switch( $format ) { case 'text' : { foreach ( $logs as $log ) { if ( $log['Line'] ) fprintf( $exportFP, "%s %s[%d].%s-%s/%d [%s]\n", $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Line'], $log['Message'] ); else fprintf( $exportFP, "%s %s[%d].%s-%s [%s]\n", $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['File'], $log['Message'] ); } break; } case 'tsv' : { fprintf( $exportFP, $SLANG['DateTime']."\t".$SLANG['Component']."\t".$SLANG['Pid']."\t".$SLANG['Level']."\t".$SLANG['Message']."\t".$SLANG['File']."\t".$SLANG['Line']."\n" ); foreach ( $logs as $log ) { fprintf( $exportFP, "%s\t%s\t%d\t%s\t%s\t%s\t%s\n", $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] ); } break; } case 'html' : { fwrite( $exportFP, ' '.$SLANG['ZoneMinderLog'].'

'.$SLANG['ZoneMinderLog'].'

'.htmlspecialchars(preg_match( '/%/', DATE_FMT_CONSOLE_LONG )?strftime( DATE_FMT_CONSOLE_LONG ):date( DATE_FMT_CONSOLE_LONG )).'

'.count($logs).' '.$SLANG['Logs'].'

' ); foreach ( $logs as $log ) { $classLevel = $log['Level']; if ( $classLevel < Logger::FATAL ) $classLevel = Logger::FATAL; elseif ( $classLevel > Logger::DEBUG ) $classLevel = Logger::DEBUG; $logClass = 'log-'.strtolower(Logger::$codes[$classLevel]); fprintf( $exportFP, " \n", $logClass, $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] ); } fwrite( $exportFP, '
'.$SLANG['DateTime'].''.$SLANG['Component'].''.$SLANG['Pid'].''.$SLANG['Level'].''.$SLANG['Message'].''.$SLANG['File'].''.$SLANG['Line'].'
%s%s%d%s%s%s%s
' ); break; } case 'xml' : { fwrite( $exportFP, ' '.$_POST['selector'].'' ); foreach ( $filter as $field=>$value ) if ( $value != '' ) fwrite( $exportFP, ' <'.strtolower($field).'>'.htmlspecialchars($value).' ' ); fwrite( $exportFP, ' '.$SLANG['DateTime'].''.$SLANG['Component'].''.$SLANG['Pid'].''.$SLANG['Level'].''.$SLANG['Message'].''.$SLANG['File'].''.$SLANG['Line'].' ' ); foreach ( $logs as $log ) { fprintf( $exportFP, " %s %s %d %s %s %d \n", $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], utf8_decode( $log['Message'] ), $log['File'], $log['Line'] ); } fwrite( $exportFP, ' ' ); break; } $exportExt = "xml"; break; } fclose( $exportFP ); ajaxResponse( array( 'key' => $exportKey, 'format' => $format, ) ); break; } case 'download' : { if ( !canView( 'System' ) ) ajaxError( 'Insufficient permissions to download logs' ); if ( empty($_REQUEST['key']) ) Fatal( "No log export key given" ); $exportKey = $_REQUEST['key']; if ( empty($_REQUEST['format']) ) Fatal( "No log export format given" ); $format = $_REQUEST['format']; switch( $format ) { case 'text' : $exportExt = "txt"; break; case 'tsv' : $exportExt = "tsv"; break; case 'html' : $exportExt = "html"; break; case 'xml' : $exportExt = "xml"; break; default : Fatal( "Unrecognised log export format '$format'" ); } $exportFile = "zm-log.$exportExt"; $exportPath = "temp/zm-log-$exportKey.$exportExt"; header( "Pragma: public" ); header( "Expires: 0" ); header( "Cache-Control: must-revalidate, post-check=0, pre-check=0" ); header( "Cache-Control: private", false ); // required by certain browsers header( "Content-Description: File Transfer" ); header( 'Content-Disposition: attachment; filename="'.$exportFile.'"' ); header( "Content-Transfer-Encoding: binary" ); header( "Content-Type: application/force-download" ); header( "Content-Length: ".filesize($exportPath) ); readfile( $exportPath ); exit( 0 ); break; } } ajaxError( 'Unrecognised action or insufficient permissions' ); ?> ZoneMinder-1.26.5/web/ajax/status.php000066400000000000000000000361221225361755400174310ustar00rootroot00000000000000 array( "permission" => "System", "table" => "Monitors", "limit" => 1, "elements" => array( "MonitorCount" => array( "sql" => "count(*)" ), "ActiveMonitorCount" => array( "sql" => "count(if(Function != 'None',1,NULL))" ), "State" => array( "func" => "daemonCheck()?".$SLANG['Running'].":".$SLANG['Stopped'] ), "Load" => array( "func" => "getLoad()" ), "Disk" => array( "func" => "getDiskPercent()" ), ), ), "monitor" => array( "permission" => "Monitors", "table" => "Monitors", "limit" => 1, "selector" => "Monitors.Id", "elements" => array( "Id" => array( "sql" => "Monitors.Id" ), "Name" => array( "sql" => "Monitors.Name" ), "Type" => true, "Function" => true, "Enabled" => true, "LinkedMonitors" => true, "Triggers" => true, "Device" => true, "Channel" => true, "Format" => true, "Host" => true, "Port" => true, "Path" => true, "Width" => array( "sql" => "Monitors.Width" ), "Height" => array( "sql" => "Monitors.Height" ), "Palette" => true, "Orientation" => true, "Brightness" => true, "Contrast" => true, "Hue" => true, "Colour" => true, "EventPrefix" => true, "LabelFormat" => true, "LabelX" => true, "LabelY" => true, "ImageBufferCount" => true, "WarmupCount" => true, "PreEventCount" => true, "PostEventCount" => true, "AlarmFrameCount" => true, "SectionLength" => true, "FrameSkip" => true, "MaxFPS" => true, "AlarmMaxFPS" => true, "FPSReportInterval" => true, "RefBlendPerc" => true, "Controllable" => true, "ControlId" => true, "ControlDevice" => true, "ControlAddress" => true, "AutoStopTimeout" => true, "TrackMotion" => true, "TrackDelay" => true, "ReturnLocation" => true, "ReturnDelay" => true, "DefaultView" => true, "DefaultRate" => true, "DefaultScale" => true, "WebColour" => true, "Sequence" => true, "MinEventId" => array( "sql" => "min(Events.Id)", "table" => "Events", "join" => "Events.MonitorId = Monitors.Id", "group" => "Events.MonitorId" ), "MaxEventId" => array( "sql" => "max(Events.Id)", "table" => "Events", "join" => "Events.MonitorId = Monitors.Id", "group" => "Events.MonitorId" ), "TotalEvents" => array( "sql" => "count(Events.Id)", "table" => "Events", "join" => "Events.MonitorId = Monitors.Id", "group" => "Events.MonitorId" ), "Status" => array( "zmu" => "-m ".escapeshellarg($_REQUEST['id'][0])." -s" ), "FrameRate" => array( "zmu" => "-m ".escapeshellarg($_REQUEST['id'][0])." -f" ), ), ), "events" => array( "permission" => "Events", "table" => "Events", "selector" => "Events.MonitorId", "elements" => array( "Id" => true, "Name" => true, "Cause" => true, "Notes" => true, "StartTime" => true, "StartTimeShort" => array( "sql" => "date_format( StartTime, '".MYSQL_FMT_DATETIME_SHORT."' )" ), "EndTime" => true, "Width" => true, "Height" => true, "Length" => true, "Frames" => true, "AlarmFrames" => true, "TotScore" => true, "AvgScore" => true, "MaxScore" => true, ), ), "event" => array( "permission" => "Events", "table" => "Events", "limit" => 1, "selector" => "Events.Id", "elements" => array( "Id" => array( "sql" => "Events.Id" ), "MonitorId" => true, "Name" => true, "Cause" => true, "StartTime" => true, "StartTimeShort" => array( "sql" => "date_format( StartTime, '".MYSQL_FMT_DATETIME_SHORT."' )" ), "EndTime" => true, "Width" => true, "Height" => true, "Length" => true, "Frames" => true, "AlarmFrames" => true, "TotScore" => true, "AvgScore" => true, "MaxScore" => true, "Archived" => true, "Videoed" => true, "Uploaded" => true, "Emailed" => true, "Messaged" => true, "Executed" => true, "Notes" => true, "MinFrameId" => array( "sql" => "min(Frames.FrameId)", "table" => "Frames", "join" => "Events.Id = Frames.EventId", "group" => "Frames.EventId" ), "MaxFrameId" => array( "sql" => "max(Frames.FrameId)", "table" => "Frames", "join" => "Events.Id = Frames.EventId", "group" => "Frames.EventId" ), "MinFrameDelta" => array( "sql" => "min(Frames.Delta)", "table" => "Frames", "join" => "Events.Id = Frames.EventId", "group" => "Frames.EventId" ), "MaxFrameDelta" => array( "sql" => "max(Frames.Delta)", "table" => "Frames", "join" => "Events.Id = Frames.EventId", "group" => "Frames.EventId" ), //"Path" => array( "postFunc" => "getEventPath" ), ), ), "frame" => array( "permission" => "Events", "table" => "Frames", "limit" => 1, "selector" => array( array( "table" => "Events", "join" => "Events.Id = Frames.EventId", "selector"=>"Events.Id" ), "Frames.FrameId" ), "elements" => array( //"Id" => array( "sql" => "Frames.FrameId" ), "FrameId" => true, "EventId" => true, "Type" => true, "TimeStamp" => true, "TimeStampShort" => array( "sql" => "date_format( StartTime, '".MYSQL_FMT_DATETIME_SHORT."' )" ), "Delta" => true, "Score" => true, //"Image" => array( "postFunc" => "getFrameImage" ), ), ), "frameimage" => array( "permission" => "Events", "func" => "getFrameImage()" ), "nearframe" => array( "permission" => "Events", "func" => "getNearFrame()" ), "nearevents" => array( "permission" => "Events", "func" => "getNearEvents()" ) ); function collectData() { global $statusData; $entitySpec = &$statusData[strtolower(validJsStr($_REQUEST['entity']))]; #print_r( $entitySpec ); if ( !canView( $entitySpec['permission'] ) ) ajaxError( 'Unrecognised action or insufficient permissions' ); if ( !empty($entitySpec['func']) ) { $data = eval( "return( ".$entitySpec['func']." );" ); } else { $data = array(); $postFuncs = array(); $fieldSql = array(); $joinSql = array(); $groupSql = array(); $elements = &$entitySpec['elements']; $lc_elements = array_change_key_case( $elements ); $id = false; if ( isset($_REQUEST['id']) ) if ( !is_array($_REQUEST['id']) ) $id = array( validJsStr($_REQUEST['id']) ); else $id = array_values( $_REQUEST['id'] ); if ( !isset($_REQUEST['element']) ) $_REQUEST['element'] = array_keys( $elements ); else if ( !is_array($_REQUEST['element']) ) $_REQUEST['element'] = array( validJsStr($_REQUEST['element']) ); if ( isset($entitySpec['selector']) ) { if ( !is_array($entitySpec['selector']) ) $entitySpec['selector'] = array( $entitySpec['selector'] ); foreach( $entitySpec['selector'] as $selector ) if ( is_array( $selector ) && isset($selector['table']) && isset($selector['join']) ) $joinSql[] = "left join ".$selector['table']." on ".$selector['join']; } foreach ( $_REQUEST['element'] as $element ) { if ( !($elementData = $lc_elements[strtolower($element)]) ) ajaxError( "Bad ".validJsStr($_REQUEST['entity'])." element ".$element ); if ( isset($elementData['func']) ) $data[$element] = eval( "return( ".$elementData['func']." );" ); else if ( isset($elementData['postFunc']) ) $postFuncs[$element] = $elementData['postFunc']; else if ( isset($elementData['zmu']) ) $data[$element] = exec( escapeshellcmd( getZmuCommand( " ".$elementData['zmu'] ) ) ); else { if ( isset($elementData['sql']) ) $fieldSql[] = $elementData['sql']." as ".$element; else $fieldSql[] = $element; if ( isset($elementData['table']) && isset($elementData['join']) ) { $joinSql[] = "left join ".$elementData['table']." on ".$elementData['join']; } if ( isset($elementData['group']) ) { $groupSql[] = $elementData['group']; } } } if ( count($fieldSql) ) { $sql = "select ".join( ", ", $fieldSql )." from ".$entitySpec['table']; if ( $joinSql ) $sql .= " ".join( " ", array_unique( $joinSql ) ); if ( $id && !empty($entitySpec['selector']) ) { $index = 0; $where = array(); foreach( $entitySpec['selector'] as $selector ) { if ( is_array( $selector ) ) $where[] = $selector['selector']." = ".dbEscape($id[$index]); else $where[] = $selector." = ".dbEscape($id[$index]); $index++; } $sql .= " where ".join( " and ", $where ); } if ( $groupSql ) $sql .= " group by ".join( ",", array_unique( $groupSql ) ); if ( !empty($_REQUEST['sort']) ) $sql .= " order by ".dbEscape($_REQUEST['sort']); if ( !empty($entitySpec['limit']) ) $limit = $entitySpec['limit']; elseif ( !empty($_REQUEST['count']) ) $limit = dbEscape($_REQUEST['count']); if ( !empty( $limit ) ) $sql .= " limit ".$limit; if ( isset($limit) && $limit == 1 ) { if ( $sqlData = dbFetchOne( $sql ) ) { foreach ( $postFuncs as $element=>$func ) $sqlData[$element] = eval( 'return( '.$func.'( $sqlData ) );' ); $data = array_merge( $data, $sqlData ); } } else { $count = 0; foreach( dbFetchAll( $sql ) as $sqlData ) { foreach ( $postFuncs as $element=>$func ) $sqlData[$element] = eval( 'return( '.$func.'( $sqlData ) );' ); $data[] = $sqlData; if ( isset($limi) && ++$count >= $limit ) break; } } } } #print_r( $data ); return( $data ); } $data = collectData(); if ( !isset($_REQUEST['layout']) ) { $_REQUEST['layout'] = "json"; } switch( $_REQUEST['layout'] ) { case 'xml NOT CURRENTLY SUPPORTED' : { header("Content-type: application/xml" ); echo( ''."\n" ); echo "<".strtolower($_REQUEST['entity']).">\n"; foreach ( $data as $key=>$value ) { $key = strtolower( $key ); echo "<$key>".htmlentities($value)."\n"; } echo "\n"; break; } case 'json' : { $response = array( strtolower(validJsStr($_REQUEST['entity'])) => $data ); if ( isset($_REQUEST['loopback']) ) $response['loopback'] = validJsStr($_REQUEST['loopback']); ajaxResponse( $response ); break; } case 'text' : { header("Content-type: text/plain" ); echo join( " ", array_values( $data ) ); break; } } function getFrameImage() { $eventId = dbEscape($_REQUEST['id'][0]); $frameId = dbEscape($_REQUEST['id'][1]); $sql = "select * from Frames where EventId = '".$eventId."' and FrameId = '".$frameId."'"; if ( !($frame = dbFetchOne( $sql )) ) { $frame = array(); $frame['EventId'] = $eventId; $frame['FrameId'] = $frameId; $frame['Type'] = "Virtual"; } $event = dbFetchOne( "select * from Events where Id = '".$frame['EventId']."'" ); $frame['Image'] = getImageSrc( $event, $frame, SCALE_BASE ); return( $frame ); } function getNearFrame() { $eventId = dbEscape($_REQUEST['id'][0]); $frameId = dbEscape($_REQUEST['id'][1]); $sql = "select FrameId from Frames where EventId = '".$eventId."' and FrameId <= '".$frameId."' order by FrameId desc limit 1"; if ( !$nearFrameId = dbFetchOne( $sql, 'FrameId' ) ) { $sql = "select * from Frames where EventId = '".$eventId."' and FrameId > '".$frameId."' order by FrameId asc limit 1"; if ( !$nearFrameId = dbFetchOne( $sql, 'FrameId' ) ) { return( array() ); } } $_REQUEST['entity'] = "frame"; $_REQUEST['id'][1] = $nearFrameId; return( collectData() ); } function getNearEvents() { global $user, $sortColumn, $sortOrder; $eventId = dbEscape($_REQUEST['id']); $event = dbFetchOne( "select * from Events where Id = '".$eventId."'" ); parseFilter( $_REQUEST['filter'] ); parseSort(); if ( $user['MonitorIds'] ) $midSql = " and MonitorId in (".join( ",", preg_split( '/["\'\s]*,["\'\s]*/', $user['MonitorIds'] ) ).")"; else $midSql = ''; $sql = "select E.Id as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where ".dbEscape($sortColumn)." ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn ".($sortOrder=='asc'?'desc':'asc'); $result = dbQuery( $sql ); while ( $id = dbFetchNext( $result, 'Id' ) ) { if ( $id == $eventId ) { $prevId = dbFetchNext( $result, 'Id' ); break; } } $sql = "select E.Id as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn $sortOrder"; $result = dbQuery( $sql ); while ( $id = dbFetchNext( $result, 'Id' ) ) { if ( $id == $eventId ) { $nextId = dbFetchNext( $result, 'Id' ); break; } } $result = array( 'EventId'=>$eventId ); $result['PrevEventId'] = empty($prevId)?0:$prevId; $result['NextEventId'] = empty($nextId)?0:$nextId; return( $result ); } ?> ZoneMinder-1.26.5/web/ajax/stream.php000066400000000000000000000104661225361755400174040ustar00rootroot00000000000000 0 ) { if ( count($rSockets) != 1 ) ajaxError( "Bogus return from select, ".count($rSockets)." sockets available" ); } switch( $nbytes = @socket_recvfrom( $socket, $msg, MSG_DATA_SIZE, 0, $remSockFile ) ) { case -1 : { ajaxError( "socket_recvfrom( $remSockFile ) failed: ".socket_strerror(socket_last_error()) ); break; } case 0 : { ajaxError( "No data to read from socket" ); break; } default : { if ( $nbytes != MSG_DATA_SIZE ) ajaxError( "Got unexpected message size, got $nbytes, expected ".MSG_DATA_SIZE ); break; } } $data = unpack( "ltype", $msg ); switch ( $data['type'] ) { case MSG_DATA_WATCH : { $data = unpack( "ltype/imonitor/istate/dfps/ilevel/irate/ddelay/izoom/Cdelayed/Cpaused/Cenabled/Cforced", $msg ); $data['fps'] = sprintf( "%.2f", $data['fps'] ); $data['rate'] /= RATE_BASE; $data['delay'] = sprintf( "%.2f", $data['delay'] ); $data['zoom'] = sprintf( "%.1f", $data['zoom']/SCALE_BASE ); ajaxResponse( array( 'status'=>$data ) ); break; } case MSG_DATA_EVENT : { $data = unpack( "ltype/ievent/iprogress/irate/izoom/Cpaused", $msg ); //$data['progress'] = sprintf( "%.2f", $data['progress'] ); $data['rate'] /= RATE_BASE; $data['zoom'] = sprintf( "%.1f", $data['zoom']/SCALE_BASE ); ajaxResponse( array( 'status'=>$data ) ); break; } default : { ajaxError( "Unexpected received message type '$type'" ); } } ajaxError( 'Unrecognised action or insufficient permissions' ); function ajaxCleanup() { global $socket, $locSockFile; if ( !empty( $socket ) ) @socket_close( $socket ); if ( !empty( $locSockFile ) ) @unlink( $locSockFile ); } ?> ZoneMinder-1.26.5/web/ajax/zone.php000066400000000000000000000023161225361755400170570ustar00rootroot00000000000000 ZM_DIR_IMAGES.'/Zones'.$monitor['Id'].'.jpg?'.time(), 'selfIntersecting' => isSelfIntersecting( $points ), 'area' => getPolyArea( $points ) ) ); break; } } } ajaxError( 'Unrecognised action or insufficient permissions' ); ?> ZoneMinder-1.26.5/web/css/000077500000000000000000000000001225361755400152365ustar00rootroot00000000000000ZoneMinder-1.26.5/web/css/Makefile.am000066400000000000000000000001561225361755400172740ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/css dist_web_DATA = \ reset.css \ spinner.css \ overlay.css ZoneMinder-1.26.5/web/css/overlay.css000066400000000000000000000014521225361755400174330ustar00rootroot00000000000000.overlayMask { position: absolute; opacity: 0.6; filter: alpha(opacity=60); -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=60); z-index: 999; background: #aaaaaa; } .overlay { display: none; position: absolute; background-color: #f0f0f0; border: 2px solid #555555; -moz-border-radius: 4px; z-index: 1000; overflow: hidden; } .overlayHeader { float: left; background-color: #dddddd; width: 100%; border-bottom: 1px solid #666666; color: black; } .overlayTitle { float: left; padding: 10px 6px; font-weight: bold; width: auto; } .overlayToolbar { float: right; font-weight: bold; padding: 6px 4px; width: auto; } .overlayBody { float: left; width: 100%; } .overlayContent { padding: 4px 4px 6px; } ZoneMinder-1.26.5/web/css/reset.css000066400000000000000000000036461225361755400171030ustar00rootroot00000000000000/* * ZoneMinder Reset Stylesheet, $Date$, $Revision$ * Copyright (C) 2001-2008 Philip Coombes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * Based on Reset Reloaded by Eric Meyer at http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/ */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, input, textarea, select, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0; padding: 0; border: 0; outline: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; } /* remember to define focus styles! */ :focus { outline: 0; } body { line-height: 1; color: black; background: white; } ol, ul { list-style: none; } /* tables still need 'cellspacing="0"' in the markup */ table { border-collapse: separate; border-spacing: 0; } caption, th, td { text-align: left; font-weight: normal; } blockquote:before, blockquote:after, q:before, q:after { content: ""; } blockquote, q { quotes: "" ""; } ZoneMinder-1.26.5/web/css/spinner.css000066400000000000000000000005371225361755400174330ustar00rootroot00000000000000.spinner { position: absolute; opacity: 0.9; filter: alpha(opacity=90); -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=90); z-index: 1001; background: #fff; } .spinner-msg { text-align: center; font-weight: bold; } .spinner-img { background: url(/graphics/spinner.gif) no-repeat; width: 24px; height: 24px; margin: 0 auto; } ZoneMinder-1.26.5/web/graphics/000077500000000000000000000000001225361755400162465ustar00rootroot00000000000000ZoneMinder-1.26.5/web/graphics/Makefile.am000066400000000000000000000001741225361755400203040ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/graphics dist_web_DATA = \ favicon.ico \ spinner.gif \ transparent.gif ZoneMinder-1.26.5/web/graphics/favicon.ico000066400000000000000000000004761225361755400203760ustar00rootroot00000000000000(( À€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿDDDDDDDDDDDDDDDDwwwwwwwwDDDGDwwDDDDGDtGDDGwwDtGDDDwwDDDDtDGwDDDDwDDwDDDDwtDGDDDDwwDGDGtDDDDGDGtDDDDGDwwDwwwwwwwwDDDDDDDDDDDDDDDDÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZoneMinder-1.26.5/web/graphics/spinner.gif000066400000000000000000000047611225361755400204230ustar00rootroot00000000000000GIF89aôÿÿÿÎÎÎúúúààà°°°èè莎ŽÈÈÈœœœØØØ¨¨¨ÀÀÀòòòvvv†††¸¸¸hhh!þCreated with ajaxload.info!ù!ÿ NETSCAPE2.0,® Ž$AeZ ù<ä ’„ÃŒQ46„<‹A” ߈áHa¡¡:’êID0ÄF„Ãa\xGƒ3€×!Ä ßO:-‡‚Rj—TJ‚ƒ* ƒ t †ˆŠŒŽ„—~—" ds]š  š)t–¥-"–i;H>³n§Qg]_* ‹ ®R±3 ÁGI? ÎË´¨v$ý›j3!!ù,° Ž$À0eZy¤0¨£q £ãŒP¤Ð£W  )"; qXˆ^ÏD50‡† Ո̢%‹`‹£ÔrÏJ{ 1‡ºÍ$ʈ…‡!!ù,´ Ž$@eš6$‚Æ Ž`Œ 3*Å=‹  …ßÈ Pˆ\"FÁ©’í²`PÐ-­ƒÓÐd5VÁ"2Ÿ|?n"!( ¸Š‘€è•)e€„4xyc?   ‡‰3‹…™™ ”#wyJ l% o€^[b_0 V T[0mœ $ƒ4 >„'VZ ¨cη3ƒÆ$Xš¼%!!ù,­ Ž$`e𤢍:D3 ÂH0¶,'j0¾Qƒs‰ ‚L(2HM¨Òj#Ðȉ…BŒ \Oéi`u§†=YŒù€EVL=I  ƒ…>‡‰‹•• suI WJm| \"_…b0 B¦ cV"d]*K1" H|@B?ÀI4…È#  S$¿-|¶|!!ù,¯ Ž$Ð4eš¤a¨:D Äh¶Œ³·œI Á/€K¤$W-á 0(`3œÍÑFãÙÀ=±pf@ïýtéøQ£ìÉ  {f~*€‚„yS*mg) ”enu E^Z^ g@ kw(b& -w#"º xW"¼t #Ç#”%U$Ë`¶t±o!!ù,´ Ž$Ð4eš¤a¬:*ÄØ± à1œˆ–ó‰v/€Kd¨ÉzÊâé–<Îp6%tP5Ù¡êS|ÉîØH(²FÕ¯›c¸Œ€`05xz*|~€v‰G„0t#  F hŽ0  #C d 1  I¢#(i - “ À uEL q ³Ì" h%±$Â$<Š·q!!ù ,­ Ž$Ð4eš¤a¬:*ÄØ± à1œˆ–ó‰v/€Kd¨ÉzÊâé–Îp6%tPÝñt¸ª¯é„5Å©3ÔÈn»G$ò€´ @aˆÏëwy{hoFS>k#  F Y" Š% E  Cb AI4$ (z¤:2• mI L½l#Æ# ¦F­#É#š>²F!!ù ,² Ž$Ð4eš¤a¬:*ÄØ± à1œˆ–ó‰v/€Kd¨©ŠVñtKG‚ˆ22ôëŽ7‚D"¯ªé$)‘„±é•Qø¼Šqp8 y l |~€‚„6zw2j# F Œ" ’% VŸC œ ]¥6a$¡ Q ª:2 \  EF I—&Ãx ¯"Í“¬F4$Ð]#¸x!!ù ,³ Ž$Ð4eZiä ’J16„<‹ŽˆB” Š?$r½œTêHzDP'"lä(†1±5–y½Ãá8tg†œ—p,’qÛ”Múÿ*   q ‚„†ˆŠŒ€”"}•# b?y{ {)Šs -s­:–9>e ¤E,C\3 ‡^·3[ ¬ž¾¸°S·Ä|˜»²?!;ZoneMinder-1.26.5/web/graphics/transparent.gif000066400000000000000000000000751225361755400213000ustar00rootroot00000000000000GIF89aôð1ÿÿÿ!ù,ô„©Ëí£œ´Ú‹³Þ¼û†âˆ;ZoneMinder-1.26.5/web/includes/000077500000000000000000000000001225361755400162545ustar00rootroot00000000000000ZoneMinder-1.26.5/web/includes/Makefile.am000066400000000000000000000003441225361755400203110ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/includes web_DATA = \ config.php dist_web_DATA = \ actions.php \ database.php \ functions.php \ control_functions.php \ lang.php \ logger.php EXTRA_DIST = \ config.php.in ZoneMinder-1.26.5/web/includes/actions.php000066400000000000000000001153671225361755400204420ustar00rootroot00000000000000$type ) { if ( preg_match( '/^(Can|Has)/', $name ) ) { $types[$name] = 'toggle'; } } $changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns ); if ( count( $changes ) ) { if ( !empty($_REQUEST['cid']) ) { dbQuery( "update Controls set ".implode( ", ", $changes )." where Id = '".dbEscape($_REQUEST['cid'])."'" ); } else { dbQuery( "insert into Controls set ".implode( ", ", $changes ) ); //$_REQUEST['cid'] = dbInsertId(); } $refreshParent = true; } $view = 'none'; } elseif ( $action == "delete" ) { if ( isset($_REQUEST['markCids']) ) { foreach( $_REQUEST['markCids'] as $markCid ) { dbQuery( "delete from Controls where Id = '".dbEscape($markCid)."'" ); dbQuery( "update Monitors set Controllable = 0, ControlId = 0 where ControlId = '".dbEscape($markCid)."'" ); $refreshParent = true; } } } } // Monitor edit actions, require a monitor id and edit permissions for that monitor if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) { $mid = validInt($_REQUEST['mid']); if ( $action == "function" ) { $monitor = dbFetchOne( "select * from Monitors where Id = '".$mid."'" ); $newFunction = validStr($_REQUEST['newFunction']); $newEnabled = validStr($_REQUEST['newEnabled']); if ($newEnabled != "1") $newEnabled = "0"; $oldFunction = $monitor['Function']; $oldEnabled = $monitor['Enabled']; if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled ) { dbQuery( "update Monitors set Function = '".dbEscape($newFunction)."', Enabled = '".$newEnabled."' where Id = '".$mid."'" ); $monitor['Function'] = $newFunction; $monitor['Enabled'] = $newEnabled; //if ( $cookies ) session_write_close(); if ( daemonCheck() ) { $restart = ($oldFunction == 'None') || ($newFunction == 'None') || ($newEnabled != $oldEnabled); zmaControl( $monitor, "stop" ); zmcControl( $monitor, $restart?"restart":"" ); zmaControl( $monitor, "start" ); } $refreshParent = true; } } elseif ( $action == "zone" && isset( $_REQUEST['zid'] ) ) { $zid = validInt($_REQUEST['zid']); $monitor = dbFetchOne( "select * from Monitors where Id = '".dbEscape($mid)."'" ); if ( !empty($zid) ) { $zone = dbFetchOne( "select * from Zones where MonitorId = '".dbEscape($mid)."' and Id = '".dbEscape($zid)."'" ); } else { $zone = array(); } if ( $_REQUEST['newZone']['Units'] == 'Percent' ) { $_REQUEST['newZone']['MinAlarmPixels'] = intval(($_REQUEST['newZone']['MinAlarmPixels']*$_REQUEST['newZone']['Area'])/100); $_REQUEST['newZone']['MaxAlarmPixels'] = intval(($_REQUEST['newZone']['MaxAlarmPixels']*$_REQUEST['newZone']['Area'])/100); if ( isset($_REQUEST['newZone']['MinFilterPixels']) ) $_REQUEST['newZone']['MinFilterPixels'] = intval(($_REQUEST['newZone']['MinFilterPixels']*$_REQUEST['newZone']['Area'])/100); if ( isset($_REQUEST['newZone']['MaxFilterPixels']) ) $_REQUEST['newZone']['MaxFilterPixels'] = intval(($_REQUEST['newZone']['MaxFilterPixels']*$_REQUEST['newZone']['Area'])/100); if ( isset($_REQUEST['newZone']['MinBlobPixels']) ) $_REQUEST['newZone']['MinBlobPixels'] = intval(($_REQUEST['newZone']['MinBlobPixels']*$_REQUEST['newZone']['Area'])/100); if ( isset($_REQUEST['newZone']['MaxBlobPixels']) ) $_REQUEST['newZone']['MaxBlobPixels'] = intval(($_REQUEST['newZone']['MaxBlobPixels']*$_REQUEST['newZone']['Area'])/100); } unset( $_REQUEST['newZone']['Points'] ); $types = array(); $changes = getFormChanges( $zone, $_REQUEST['newZone'], $types ); if ( count( $changes ) ) { if ( $zid > 0 ) { $sql = "update Zones set ".implode( ", ", $changes )." where MonitorId = '".dbEscape($mid)."' and Id = '".dbEscape($zid)."'"; } else { $sql = "insert into Zones set MonitorId = '".dbEscape($mid)."', ".implode( ", ", $changes ); } dbQuery( $sql ); //if ( $cookies ) session_write_close(); if ( daemonCheck() ) { zmaControl( $mid, "restart" ); } $refreshParent = true; } $view = 'none'; } elseif ( $action == "plugin" && isset($_REQUEST['pl'])) { $plugin=dbEscape($_REQUEST['pl']); $zid=validInt($_REQUEST['zid']); $sql="SELECT * FROM PluginsConfig WHERE MonitorId='".dbEscape($mid)."' AND ZoneId='".$zid."' AND pluginName='".$plugin."'"; $pconfs=dbFetchAll( $sql ); $changes=0; foreach( $pconfs as $pconf ) { $value=$_REQUEST['pluginOpt'][$pconf['Name']]; if(array_key_exists($pconf['Name'], $_REQUEST['pluginOpt']) && ($pconf['Value']!=$value)) { dbQuery("UPDATE PluginsConfig SET Value='".dbEscape($value)."' WHERE id='".$pconf['Id']."'"); $changes++; } } if($changes>0) { if ( daemonCheck() ) { zmaControl( $mid, "restart" ); } $refreshParent = true; } $view = 'none'; } elseif ( $action == "sequence" && isset($_REQUEST['smid']) ) { $smid = validInt($_REQUEST['smid']); $monitor = dbFetchOne( "select * from Monitors where Id = '".dbEscape($mid)."'" ); $smonitor = dbFetchOne( "select * from Monitors where Id = '".dbEscape($smid)."'" ); dbQuery( "update Monitors set Sequence = '".$smonitor['Sequence']."' where Id = '".$monitor['Id']."'" ); dbQuery( "update Monitors set Sequence = '".$monitor['Sequence']."' where Id = '".$smonitor['Id']."'" ); $refreshParent = true; fixSequences(); } if ( $action == "delete" ) { if ( isset($_REQUEST['markZids']) ) { $deletedZid = 0; foreach( $_REQUEST['markZids'] as $markZid ) { dbQuery( "delete from Zones where MonitorId = '".dbEscape($mid)."' && Id = '".dbEscape($markZid)."'" ); $deletedZid = 1; } if ( $deletedZid ) { //if ( $cookies ) //session_write_close(); if ( daemonCheck() ) zmaControl( $mid, "restart" ); $refreshParent = true; } } } } // Monitor edit actions, monitor id derived, require edit permissions for that monitor if ( canEdit( 'Monitors' ) ) { if ( $action == "monitor" ) { if ( !empty($_REQUEST['mid']) ) { $mid = validInt($_REQUEST['mid']); $monitor = dbFetchOne( "select * from Monitors where Id = '".dbEscape($mid)."'" ); if ( ZM_OPT_X10 ) { $x10Monitor = dbFetchOne( "select * from TriggersX10 where MonitorId = '".dbEscape($mid)."'" ); if ( !$x10Monitor ) $x10Monitor = array(); } } else { $monitor = array(); if ( ZM_OPT_X10 ) { $x10Monitor = array(); } } // Define a field type for anything that's not simple text equivalent $types = array( 'Triggers' => 'set', 'Controllable' => 'toggle', 'TrackMotion' => 'toggle', 'Enabled' => 'toggle', 'DoNativeMotDet' => 'toggle' ); $columns = getTableColumns( 'Monitors' ); $changes = getFormChanges( $monitor, $_REQUEST['newMonitor'], $types, $columns ); if ( count( $changes ) ) { if ( !empty($_REQUEST['mid']) ) { $mid = validInt($_REQUEST['mid']); $sql = "update Monitors set ".implode( ", ", $changes )." where Id = '".dbEscape($mid)."'"; dbQuery( $sql ); if ( isset($changes['Name']) ) { exec( escapeshellcmd( "mv ".ZM_DIR_EVENTS."/".$monitor['Name']." ".ZM_DIR_EVENTS."/".$_REQUEST['newMonitor']['Name'] ) ); } if ( isset($changes['Width']) || isset($changes['Height']) ) { $newW = $_REQUEST['newMonitor']['Width']; $newH = $_REQUEST['newMonitor']['Height']; $newA = $newW * $newH; $oldW = $monitor['Width']; $oldH = $monitor['Height']; $oldA = $oldW * $oldH; $zones = dbFetchAll( "select * from Zones where MonitorId = '".dbEscape($mid)."'" ); foreach ( $zones as $zone ) { $newZone = $zone; $points = coordsToPoints( $zone['Coords'] ); for ( $i = 0; $i < count($points); $i++ ) { $points[$i]['x'] = intval(($points[$i]['x']*($newW-1))/($oldW-1)); $points[$i]['y'] = intval(($points[$i]['y']*($newH-1))/($oldH-1)); } $newZone['Coords'] = pointsToCoords( $points ); $newZone['Area'] = intval(round(($zone['Area']*$newA)/$oldA)); $newZone['MinAlarmPixels'] = intval(round(($newZone['MinAlarmPixels']*$newA)/$oldA)); $newZone['MaxAlarmPixels'] = intval(round(($newZone['MaxAlarmPixels']*$newA)/$oldA)); $newZone['MinFilterPixels'] = intval(round(($newZone['MinFilterPixels']*$newA)/$oldA)); $newZone['MaxFilterPixels'] = intval(round(($newZone['MaxFilterPixels']*$newA)/$oldA)); $newZone['MinBlobPixels'] = intval(round(($newZone['MinBlobPixels']*$newA)/$oldA)); $newZone['MaxBlobPixels'] = intval(round(($newZone['MaxBlobPixels']*$newA)/$oldA)); $changes = getFormChanges( $zone, $newZone, $types ); if ( count( $changes ) ) { dbQuery( "update Zones set ".implode( ", ", $changes )." where MonitorId = '".dbEscape($mid)."' and Id = '".$zone['Id']."'" ); } } } } elseif ( !$user['MonitorIds'] ) { $maxSeq = dbFetchOne( "select max(Sequence) as MaxSequence from Monitors", "MaxSequence" ); $changes[] = "Sequence = ".($maxSeq+1); dbQuery( "insert into Monitors set ".implode( ", ", $changes ) ); $mid = dbInsertId(); $zoneArea = $_REQUEST['newMonitor']['Width'] * $_REQUEST['newMonitor']['Height']; dbQuery( "insert into Zones set MonitorId = ".dbEscape($mid).", Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = '".sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 )."', Area = ".$zoneArea.", AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels = ".intval(($zoneArea*3)/100).", MaxAlarmPixels = ".intval(($zoneArea*75)/100).", FilterX = 3, FilterY = 3, MinFilterPixels = ".intval(($zoneArea*3)/100).", MaxFilterPixels = ".intval(($zoneArea*75)/100).", MinBlobPixels = ".intval(($zoneArea*2)/100).", MinBlobs = 1" ); //$view = 'none'; mkdir( ZM_DIR_EVENTS.'/'.$mid, 0755 ); symlink( $mid, ZM_DIR_EVENTS.'/'.$_REQUEST['newMonitor']['Name'] ); if ( isset($_COOKIE['zmGroup']) ) { $sql = "update Groups set MonitorIds = concat(MonitorIds,',".$mid."') where Id = '".dbEscape($_COOKIE['zmGroup'])."'"; dbQuery( $sql ); } } $restart = true; } if ( ZM_OPT_X10 ) { $x10Changes = getFormChanges( $x10Monitor, $_REQUEST['newX10Monitor'] ); if ( count( $x10Changes ) ) { if ( $x10Monitor && isset($_REQUEST['newX10Monitor']) ) { dbQuery( "update TriggersX10 set ".implode( ", ", $x10Changes )." where MonitorId = '".dbEscape($mid)."'" ); } elseif ( !$user['MonitorIds'] ) { if ( !$x10Monitor ) { dbQuery( "insert into TriggersX10 set MonitorId = '".dbEscape($mid)."', ".implode( ", ", $x10Changes ) ); } else { dbQuery( "delete from TriggersX10 where MonitorId = '".dbEscape($mid)."'" ); } } $restart = true; } } if ( $restart ) { $monitor = dbFetchOne( "select * from Monitors where Id = '".dbEscape($mid)."'" ); fixDevices(); //if ( $cookies ) //session_write_close(); if ( daemonCheck() ) { zmaControl( $monitor, "stop" ); zmcControl( $monitor, "restart" ); zmaControl( $monitor, "start" ); } //daemonControl( 'restart', 'zmwatch.pl' ); $refreshParent = true; } $view = 'none'; } if ( $action == "delete" ) { if ( isset($_REQUEST['markMids']) && !$user['MonitorIds'] ) { foreach( $_REQUEST['markMids'] as $markMid ) { if ( canEdit( 'Monitors', $markMid ) ) { $sql = "select * from Monitors where Id = '".dbEscape($markMid)."'"; if ( $monitor = dbFetchOne( $sql ) ) { if ( daemonCheck() ) { zmaControl( $monitor, "stop" ); zmcControl( $monitor, "stop" ); } // This is the important stuff dbQuery( "delete from Monitors where Id = '".dbEscape($markMid)."'" ); dbQuery( "delete from Zones where MonitorId = '".dbEscape($markMid)."'" ); if ( ZM_OPT_X10 ) dbQuery( "delete from TriggersX10 where MonitorId = '".dbEscape($markMid)."'" ); fixSequences(); // If fast deletes are on, then zmaudit will clean everything else up later // If fast deletes are off and there are lots of events then this step may // well time out before completing, in which case zmaudit will still tidy up if ( !ZM_OPT_FAST_DELETE ) { $sql = "select Id from Events where MonitorId = '".dbEscape($markMid)."'"; $markEids = dbFetchAll( $sql, 'Id' ); foreach( $markEids as $markEid ) deleteEvent( $markEid ); deletePath( ZM_DIR_EVENTS."/".$monitor['Name'] ); deletePath( ZM_DIR_EVENTS."/".$monitor['Id'] ); } } } } } } } // Device view actions if ( canEdit( 'Devices' ) ) { if ( $action == "device" ) { if ( !empty($_REQUEST['command']) ) { setDeviceStatusX10( $_REQUEST['key'], $_REQUEST['command'] ); } elseif ( isset( $_REQUEST['newDevice'] ) ) { if ( isset($_REQUEST['did']) ) { dbQuery( "update Devices set Name = '".dbEscape($_REQUEST['newDevice']['Name'])."', KeyString = '".dbEscape($_REQUEST['newDevice']['KeyString'])."' where Id = '".dbEscape($_REQUEST['did'])."'" ); } else { dbQuery( "insert into Devices set Name = '".dbEscape($_REQUEST['newDevice']['Name'])."', KeyString = '".dbEscape($_REQUEST['newDevice']['KeyString'])."'" ); } $refreshParent = true; $view = 'none'; } } elseif ( $action == "delete" ) { if ( isset($_REQUEST['markDids']) ) { foreach( $_REQUEST['markDids'] as $markDid ) { dbQuery( "delete from Devices where Id = '".dbEscape($markDid)."'" ); $refreshParent = true; } } } } // System view actions if ( canView( 'System' ) ) { if ( $action == "setgroup" ) { if ( !empty($_REQUEST['gid']) ) { setcookie( "zmGroup", validInt($_REQUEST['gid']), time()+3600*24*30*12*10 ); } else { setcookie( "zmGroup", "", time()-3600*24*2 ); } $refreshParent = true; } } // System edit actions if ( canEdit( 'System' ) ) { if ( $action == "version" && isset($_REQUEST['option']) ) { $option = $_REQUEST['option']; switch( $option ) { case 'go' : { // Ignore this, the caller will open the page itself break; } case 'ignore' : { dbQuery( "update Config set Value = '".ZM_DYN_LAST_VERSION."' where Name = 'ZM_DYN_CURR_VERSION'" ); break; } case 'hour' : case 'day' : case 'week' : { $nextReminder = time(); if ( $option == 'hour' ) { $nextReminder += 60*60; } elseif ( $option == 'day' ) { $nextReminder += 24*60*60; } elseif ( $option == 'week' ) { $nextReminder += 7*24*60*60; } dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_NEXT_REMINDER'" ); break; } case 'never' : { dbQuery( "update Config set Value = '0' where Name = 'ZM_CHECK_FOR_UPDATES'" ); break; } } } if ( $action == "donate" && isset($_REQUEST['option']) ) { $option = $_REQUEST['option']; switch( $option ) { case 'go' : { // Ignore this, the caller will open the page itself break; } case 'hour' : case 'day' : case 'week' : case 'month' : { $nextReminder = time(); if ( $option == 'hour' ) { $nextReminder += 60*60; } elseif ( $option == 'day' ) { $nextReminder += 24*60*60; } elseif ( $option == 'week' ) { $nextReminder += 7*24*60*60; } elseif ( $option == 'month' ) { $nextReminder += 30*24*60*60; } dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_DONATE_REMINDER_TIME'" ); break; } case 'never' : case 'already' : { dbQuery( "update Config set Value = '0' where Name = 'ZM_DYN_SHOW_DONATE_REMINDER'" ); break; } } } if ( $action == "options" && isset($_REQUEST['tab']) ) { $configCat = $configCats[$_REQUEST['tab']]; $changed = false; foreach ( $configCat as $name=>$value ) { unset( $newValue ); if ( $value['Type'] == "boolean" && empty($_REQUEST['newConfig'][$name]) ) $newValue = 0; elseif ( isset($_REQUEST['newConfig'][$name]) ) $newValue = preg_replace( "/\r\n/", "\n", stripslashes( $_REQUEST['newConfig'][$name] ) ); if ( isset($newValue) && ($newValue != $value['Value']) ) { dbQuery( "update Config set Value = '".$newValue."' where Name = '".$name."'" ); $changed = true; } } if ( $changed ) { switch( $_REQUEST['tab'] ) { case "system" : case "config" : case "paths" : $restartWarning = true; break; case "web" : case "tools" : break; case "logging" : case "network" : case "mail" : case "upload" : $restartWarning = true; break; case "highband" : case "medband" : case "lowband" : case "phoneband" : break; } } loadConfig( false ); } elseif ( $action == "user" ) { if ( !empty($_REQUEST['uid']) ) $dbUser = dbFetchOne( "select * from Users where Id = '".dbEscape($_REQUEST['uid'])."'" ); else $dbUser = array(); $types = array(); $changes = getFormChanges( $dbUser, $_REQUEST['newUser'], $types ); if ( $_REQUEST['newUser']['Password'] ) $changes['Password'] = "Password = password('".dbEscape($_REQUEST['newUser']['Password'])."')"; else unset( $changes['Password'] ); if ( count( $changes ) ) { if ( !empty($_REQUEST['uid']) ) { $sql = "update Users set ".implode( ", ", $changes )." where Id = '".dbEscape($_REQUEST['uid'])."'"; } else { $sql = "insert into Users set ".implode( ", ", $changes ); } dbQuery( $sql ); $refreshParent = true; if ( $dbUser['Username'] == $user['Username'] ) userLogin( $dbUser['Username'], $dbUser['Password'] ); } $view = 'none'; } elseif ( $action == "state" ) { if ( !empty($_REQUEST['runState']) ) { //if ( $cookies ) session_write_close(); packageControl( $_REQUEST['runState'] ); $refreshParent = true; } } elseif ( $action == "save" ) { if ( !empty($_REQUEST['runState']) || !empty($_REQUEST['newState']) ) { $sql = "select Id,Function,Enabled from Monitors order by Id"; $definitions = array(); foreach( dbFetchAll( $sql ) as $monitor ) { $definitions[] = $monitor['Id'].":".$monitor['Function'].":".$monitor['Enabled']; } $definition = join( ',', $definitions ); if ( $_REQUEST['newState'] ) $_REQUEST['runState'] = $_REQUEST['newState']; dbQuery( "replace into States set Name = '".dbEscape($_REQUEST['runState'])."', Definition = '".dbEscape($definition)."'" ); } } elseif ( $action == "group" ) { if ( !empty($_REQUEST['gid']) ) { $sql = "update Groups set Name = '".dbEscape($_REQUEST['newGroup']['Name'])."', MonitorIds = '".dbEscape(join(',',$_REQUEST['newGroup']['MonitorIds']))."' where Id = '".dbEscape($_REQUEST['gid'])."'"; } else { $sql = "insert into Groups set Name = '".dbEscape($_REQUEST['newGroup']['Name'])."', MonitorIds = '".dbEscape(join(',',$_REQUEST['newGroup']['MonitorIds']))."'"; } dbQuery( $sql ); $refreshParent = true; $view = 'none'; } elseif ( $action == "delete" ) { if ( isset($_REQUEST['runState']) ) dbQuery( "delete from States where Name = '".dbEscape($_REQUEST['runState'])."'" ); if ( isset($_REQUEST['markUids']) ) { foreach( $_REQUEST['markUids'] as $markUid ) dbQuery( "delete from Users where Id = '".dbEscape($markUid)."'" ); if ( $markUid == $user['Id'] ) userLogout(); } if ( !empty($_REQUEST['gid']) ) { dbQuery( "delete from Groups where Id = '".dbEscape($_REQUEST['gid'])."'" ); if ( isset($_COOKIE['zmGroup']) ) { if ( $_REQUEST['gid'] == $_COOKIE['zmGroup'] ) { unset( $_COOKIE['zmGroup'] ); setcookie( "zmGroup", "", time()-3600*24*2 ); $refreshParent = true; } } } } } else { if ( ZM_USER_SELF_EDIT && $action == "user" ) { $uid = $user['Id']; $dbUser = dbFetchOne( "select Id, Password, Language from Users where Id = '".dbEscape($uid)."'" ); $types = array(); $changes = getFormChanges( $dbUser, $_REQUEST['newUser'], $types ); if ( !empty($_REQUEST['newUser']['Password']) ) $changes['Password'] = "Password = password('".dbEscape($_REQUEST['newUser']['Password'])."')"; else unset( $changes['Password'] ); if ( count( $changes ) ) { $sql = "update Users set ".implode( ", ", $changes )." where Id = '".dbEscape($uid)."'"; dbQuery( $sql ); $refreshParent = true; } $view = 'none'; } } if ( $action == "reset" ) { $_SESSION['zmEventResetTime'] = strftime( STRF_FMT_DATETIME_DB ); setcookie( "zmEventResetTime", $_SESSION['zmEventResetTime'], time()+3600*24*30*12*10 ); //if ( $cookies ) session_write_close(); } } ?> ZoneMinder-1.26.5/web/includes/config.php.in000066400000000000000000000140031225361755400206350ustar00rootroot00000000000000 0 ) { if ( php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']) ) print( "Warning, overriding installed $localConfigFile file with local copy\n" ); else error_log( "Warning, overriding installed $localConfigFile file with local copy" ); $configFile = $localConfigFile; } $cfg = fopen( $configFile, "r") or die("Could not open config file."); while ( !feof($cfg) ) { $str = fgets( $cfg, 256 ); if ( preg_match( '/^\s*$/', $str )) continue; elseif ( preg_match( '/^\s*#/', $str )) continue; elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches )) define( $matches[1], $matches[2] ); } fclose( $cfg ); // // This section is options normally derived from other options or configuration // define( "ZMU_PATH", ZM_PATH_BIN."/zmu" ); // Local path to the ZoneMinder Utility // // If setup supports Video 4 Linux v2 and/or v1 // define( "ZM_HAS_V4L2", "@ZM_HAS_V4L2@" ); // V4L2 support enabled define( "ZM_HAS_V4L1", "@ZM_HAS_V4L1@" ); // V4L1 support enabled define( "ZM_HAS_V4L", "@ZM_HAS_V4L@" ); // V4L support enabled // // If PCRE dev libraries are installed // define( "ZM_PCRE", "@ZM_PCRE@" ); // PCRE support enabled // // Alarm states // define( "STATE_IDLE", 0 ); define( "STATE_PREALARM", 1 ); define( "STATE_ALARM", 2 ); define( "STATE_ALERT", 3 ); define( "STATE_TAPE", 4 ); // // DVR Control Commands // define( "MSG_CMD", 1 ); define( "MSG_DATA_WATCH", 2 ); define( "MSG_DATA_EVENT", 3 ); define( "CMD_NONE", 0 ); define( "CMD_PAUSE", 1 ); define( "CMD_PLAY", 2 ); define( "CMD_STOP", 3 ); define( "CMD_FASTFWD", 4 ); define( "CMD_SLOWFWD", 5 ); define( "CMD_SLOWREV", 6 ); define( "CMD_FASTREV", 7 ); define( "CMD_ZOOMIN", 8 ); define( "CMD_ZOOMOUT", 9 ); define( "CMD_PAN", 10 ); define( "CMD_SCALE", 11 ); define( "CMD_PREV", 12 ); define( "CMD_NEXT", 13 ); define( "CMD_SEEK", 14 ); define( "CMD_VARPLAY", 15 ); define( "CMD_QUERY", 99 ); // // These are miscellaneous options you won't normally need to change // define( "MAX_EVENTS", 10 ); // The maximum number of events to show in the monitor event listing define( "RATE_BASE", 100 ); // The additional scaling factor used to help get fractional rates in integer format define( "SCALE_BASE", 100 ); // The additional scaling factor used to help get fractional scales in integer format // // Date and time formats, eventually some of these may end up in the language files // define( "DATE_FMT_CONSOLE_LONG", "D jS M, g:ia" ); // This is the main console date/time, date() or strftime() format define( "DATE_FMT_CONSOLE_SHORT", "%H:%M" ); // This is the xHTML console date/time, date() or strftime() format define( "STRF_FMT_DATETIME_DB", "%Y-%m-%d %H:%M:%S" ); // Strftime format for database queries, don't change define( "STRF_FMT_DATETIME", "%c" ); // Strftime locale aware format for dates with times define( "STRF_FMT_DATE", "%x" ); // Strftime locale aware format for dates without times define( "STRF_FMT_TIME", "%X" ); // Strftime locale aware format for times without dates define( "STRF_FMT_DATETIME_SHORT", "%y/%m/%d %H:%M:%S" ); // Strftime shorter format for dates with time, not locale aware define( "STRF_FMT_DATETIME_SHORTER", "%m/%d %H:%M:%S" ); // Strftime shorter format for dates with time, not locale aware, used where space is tight define( "MYSQL_FMT_DATETIME_SHORT", "%y/%m/%d %H:%i:%S" ); // MySQL date_format shorter format for dates with time require_once( 'database.php' ); loadConfig(); $GLOBALS['defaultUser'] = array( "Username" => "admin", "Password" => "", "Language" => "", "Enabled" => 1, "Stream" => 'View', "Events" => 'Edit', "Control" => 'Edit', "Monitors" => 'Edit', "Devices" => 'Edit', "System" => 'Edit', "MaxBandwidth" => "", "MonitorIds" => false ); function loadConfig( $defineConsts=true ) { global $config; global $configCats; $config = array(); $configCat = array(); $sql = "select * from Config order by Id asc"; $result = mysql_query( $sql ); if ( !$result ) echo mysql_error(); $monitors = array(); while( $row = mysql_fetch_assoc( $result ) ) { if ( $defineConsts ) define( $row['Name'], $row['Value'] ); $config[$row['Name']] = $row; if ( !($configCat = &$configCats[$row['Category']]) ) { $configCats[$row['Category']] = array(); $configCat = &$configCats[$row['Category']]; } $configCat[$row['Name']] = $row; } //print_r( $config ); //print_r( $configCats ); } ?> ZoneMinder-1.26.5/web/includes/control_functions.php000066400000000000000000001231431225361755400225410ustar00rootroot00000000000000 array( 'Up' => 'Left', 'Down' => 'Right', 'Left' => 'Down', 'Right' => 'Up', 'UpLeft' => 'DownLeft', 'UpRight' => 'UpLeft', 'DownLeft' => 'DownRight', 'DownRight' => 'UpRight', ), '180' => array( 'Up' => 'Down', 'Down' => 'Up', 'Left' => 'Right', 'Right' => 'Left', 'UpLeft' => 'DownRight', 'UpRight' => 'DownLeft', 'DownLeft' => 'UpRight', 'DownRight' => 'UpLeft', ), '270' => array( 'Up' => 'Right', 'Down' => 'Left', 'Left' => 'Up', 'Right' => 'Down', 'UpLeft' => 'UpRight', 'UpRight' => 'DownRight', 'DownLeft' => 'UpLeft', 'DownRight' => 'DownLeft', ), 'hori' => array( 'Up' => 'Up', 'Down' => 'Down', 'Left' => 'Right', 'Right' => 'Left', 'UpLeft' => 'UpRight', 'UpRight' => 'UpLeft', 'DownLeft' => 'DownRight', 'DownRight' => 'DownLeft', ), 'vert' => array( 'Up' => 'Down', 'Down' => 'Up', 'Left' => 'Left', 'Right' => 'Right', 'UpLeft' => 'DownLeft', 'UpRight' => 'DownRight', 'DownLeft' => 'UpLeft', 'DownRight' => 'UpRight', ), ); $new_dirn = $conversions[$monitor['Orientation']][$dirn]; $_REQUEST['control'] = preg_replace( "/_$dirn\$/", "_$new_dirn", $_REQUEST['control'] ); $dirn = $new_dirn; } if ( $monitor['HasPanSpeed'] && $xFactor ) { if ( $monitor['HasTurboPan'] ) { if ( $xFactor >= $turbo ) { $panSpeed = $monitor['TurboPanSpeed']; } else { $xFactor = $xFactor/$turbo; $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); } } else { $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); } $ctrlCommand .= " --panspeed=".$panSpeed; } if ( $monitor['HasTiltSpeed'] && $yFactor ) { if ( $monitor['HasTurboTilt'] ) { if ( $yFactor >= $turbo ) { $tiltSpeed = $monitor['TurboTiltSpeed']; } else { $yFactor = $yFactor/$turbo; $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); } } else { $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); } $ctrlCommand .= " --tiltspeed=".$tiltSpeed; } switch( $mode ) { case 'Rel' : case 'Abs' : { if ( preg_match( '/(Left|Right)$/', $dirn ) ) { $panStep = intval(round($monitor['MinPanStep']+(($monitor['MaxPanStep']-$monitor['MinPanStep'])*$xFactor))); $ctrlCommand .= " --panstep=".$panStep; } if ( preg_match( '/^(Up|Down)/', $dirn ) ) { $tiltStep = intval(round($monitor['MinTiltStep']+(($monitor['MaxTiltStep']-$monitor['MinTiltStep'])*$yFactor))); $ctrlCommand .= " --tiltstep=".$tiltStep; } break; } case 'Con' : { if ( $monitor['AutoStopTimeout'] ) { $slowPanSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$slow))); $slowTiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$slow))); if ( (!isset($panSpeed) || ($panSpeed < $slowPanSpeed)) && (!isset($tiltSpeed) || ($tiltSpeed < $slowTiltSpeed)) ) { $ctrlCommand .= " --autostop"; } } break; } } } } } } elseif ( isset($_REQUEST['x']) && isset($_REQUEST['y']) ) { if ( $_REQUEST['control'] == "moveMap" ) { $x = deScale( $_REQUEST['x'], $_REQUEST['scale'] ); $y = deScale( $_REQUEST['y'], $_REQUEST['scale'] ); switch ( $monitor['Orientation'] ) { case '0' : case '180' : case 'hori' : case 'vert' : $width = $monitor['Width']; $height = $monitor['Height']; break; case '90' : case '270' : $width = $monitor['Height']; $height = $monitor['Width']; break; } switch ( $monitor['Orientation'] ) { case '90' : $tempY = $y; $y = $height - $x; $x = $tempY; break; case '180' : $x = $width - $x; $y = $height - $y; break; case '270' : $tempX = $x; $x = $width - $y; $y = $tempX; break; case 'hori' : $x = $width - $x; break; case 'vert' : $y = $height - $y; break; } //$ctrlCommand .= " --xcoord=$x --ycoord=$y --width=$width --height=$height"; $ctrlCommand .= " --xcoord=$x --ycoord=$y"; } elseif ( $_REQUEST['control'] == "movePseudoMap" ) { $x = deScale( $_REQUEST['x'], $_REQUEST['scale'] ); $y = deScale( $_REQUEST['y'], $_REQUEST['scale'] ); $halfWidth = $monitor['Width'] / 2; $halfHeight = $monitor['Height'] / 2; $xFactor = ($x - $halfWidth)/$halfWidth; $yFactor = ($y - $halfHeight)/$halfHeight; switch ( $monitor['Orientation'] ) { case '90' : $tempYFactor = $y; $yFactor = -$xFactor; $xFactor = $tempYFactor; break; case '180' : $xFactor = -$xFactor; $yFactor = -$yFactor; break; case '270' : $tempXFactor = $x; $xFactor = -$yFactor; $yFactor = $tempXFactor; break; case 'hori' : $xFactor = -$xFactor; break; case 'vert' : $yFactor = -$yFactor; break; } $turbo = 0.9; // Threshold for turbo speed $blind = 0.1; // Threshold for blind spot $panControl = ''; $tiltControl = ''; if ( $xFactor > $blind ) { $panControl = 'Right'; } elseif ( $xFactor < -$blind ) { $panControl = 'Left'; } if ( $yFactor > $blind ) { $tiltControl = 'Down'; } elseif ( $yFactor < -$blind ) { $tiltControl = 'Up'; } $dirn = $tiltControl.$panControl; if ( !$dirn ) { // No command, probably in blind spot in middle $_REQUEST['control'] = 'null'; return( false ); } else { $_REQUEST['control'] = 'moveRel'.$dirn; $xFactor = abs($xFactor); $yFactor = abs($yFactor); if ( $monitor['HasPanSpeed'] && $xFactor ) { if ( $monitor['HasTurboPan'] ) { if ( $xFactor >= $turbo ) { $panSpeed = $monitor['TurboPanSpeed']; } else { $xFactor = $xFactor/$turbo; $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); } } else { $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); } } if ( $monitor['HasTiltSpeed'] && $yFactor ) { if ( $monitor['HasTurboTilt'] ) { if ( $yFactor >= $turbo ) { $tiltSpeed = $monitor['TurboTiltSpeed']; } else { $yFactor = $yFactor/$turbo; $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); } } else { $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); } } if ( preg_match( '/(Left|Right)$/', $dirn ) ) { $panStep = intval(round($monitor['MinPanStep']+(($monitor['MaxPanStep']-$monitor['MinPanStep'])*$xFactor))); $ctrlCommand .= " --panstep=".$panStep." --panspeed=".$panSpeed; } if ( preg_match( '/^(Up|Down)/', $dirn ) ) { $tiltStep = intval(round($monitor['MinTiltStep']+(($monitor['MaxTiltStep']-$monitor['MinTiltStep'])*$yFactor))); $ctrlCommand .= " --tiltstep=".$tiltStep." --tiltspeed=".$tiltSpeed; } } } elseif ( $_REQUEST['control'] == "moveConMap" ) { $x = deScale( $_REQUEST['x'], $_REQUEST['scale'] ); $y = deScale( $_REQUEST['y'], $_REQUEST['scale'] ); $halfWidth = $monitor['Width'] / 2; $halfHeight = $monitor['Height'] / 2; $xFactor = ($x - $halfWidth)/$halfWidth; $yFactor = ($y - $halfHeight)/$halfHeight; switch ( $monitor['Orientation'] ) { case '90' : $tempYFactor = $y; $yFactor = -$xFactor; $xFactor = $tempYFactor; break; case '180' : $xFactor = -$xFactor; $yFactor = -$yFactor; break; case '270' : $tempXFactor = $x; $xFactor = -$yFactor; $yFactor = $tempXFactor; break; case 'hori' : $xFactor = -$xFactor; break; case 'vert' : $yFactor = -$yFactor; break; } $slow = 0.9; // Threshold for slow speed/timeouts $turbo = 0.9; // Threshold for turbo speed $blind = 0.1; // Threshold for blind spot $panControl = ''; $tiltControl = ''; if ( $xFactor > $blind ) { $panControl = 'Right'; } elseif ( $xFactor < -$blind ) { $panControl = 'Left'; } if ( $yFactor > $blind ) { $tiltControl = 'Down'; } elseif ( $yFactor < -$blind ) { $tiltControl = 'Up'; } $dirn = $tiltControl.$panControl; if ( !$dirn ) { // No command, probably in blind spot in middle $_REQUEST['control'] = 'moveStop'; } else { $_REQUEST['control'] = 'moveCon'.$dirn; $xFactor = abs($xFactor); $yFactor = abs($yFactor); if ( $monitor['HasPanSpeed'] && $xFactor ) { if ( $monitor['HasTurboPan'] ) { if ( $xFactor >= $turbo ) { $panSpeed = $monitor['TurboPanSpeed']; } else { $xFactor = $xFactor/$turbo; $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); } } else { $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); } } if ( $monitor['HasTiltSpeed'] && $yFactor ) { if ( $monitor['HasTurboTilt'] ) { if ( $yFactor >= $turbo ) { $tiltSpeed = $monitor['TurboTiltSpeed']; } else { $yFactor = $yFactor/$turbo; $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); } } else { $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); } } if ( preg_match( '/(Left|Right)$/', $dirn ) ) { $ctrlCommand .= " --panspeed=".$panSpeed; } if ( preg_match( '/^(Up|Down)/', $dirn ) ) { $ctrlCommand .= " --tiltspeed=".$tiltSpeed; } if ( $monitor['AutoStopTimeout'] ) { $slowPanSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$slow))); $slowTiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$slow))); if ( (!isset($panSpeed) || ($panSpeed < $slowPanSpeed)) && (!isset($tiltSpeed) || ($tiltSpeed < $slowTiltSpeed)) ) { $ctrlCommand .= " --autostop"; } } } } else { $slow = 0.9; // Threshold for slow speed/timeouts $turbo = 0.9; // Threshold for turbo speed $long_y = 48; $short_x = 32; $short_y = 32; if ( preg_match( '/^([a-z]+)([A-Z][a-z]+)([A-Z][a-z]+)$/', $_REQUEST['control'], $matches ) ) { $command = $matches[1]; $mode = $matches[2]; $dirn = $matches[3]; switch( $command ) { case 'focus' : { switch( $dirn ) { case 'Near' : { $factor = ($long_y-($y+1))/$long_y; break; } case 'Far' : { $factor = ($y+1)/$long_y; break; } } if ( $monitor['HasFocusSpeed'] ) { $speed = intval(round($monitor['MinFocusSpeed']+(($monitor['MaxFocusSpeed']-$monitor['MinFocusSpeed'])*$factor))); $ctrlCommand .= " --speed=".$speed; } switch( $mode ) { case 'Abs' : case 'Rel' : { $step = intval(round($monitor['MinFocusStep']+(($monitor['MaxFocusStep']-$monitor['MinFocusStep'])*$factor))); $ctrlCommand .= " --step=".$step; break; } case 'Con' : { if ( $monitor['AutoStopTimeout'] ) { $slowSpeed = intval(round($monitor['MinFocusSpeed']+(($monitor['MaxFocusSpeed']-$monitor['MinFocusSpeed'])*$slow))); if ( $speed < $slowSpeed ) { $ctrlCommand .= " --autostop"; } } break; } } break; } case 'zoom' : { switch( $dirn ) { case 'Tele' : { $factor = ($long_y-($y+1))/$long_y; break; } case 'Wide' : { $factor = ($y+1)/$long_y; break; } } if ( $monitor['HasZoomSpeed'] ) { $speed = intval(round($monitor['MinZoomSpeed']+(($monitor['MaxZoomSpeed']-$monitor['MinZoomSpeed'])*$factor))); $ctrlCommand .= " --speed=".$speed; } switch( $mode ) { case 'Abs' : case 'Rel' : { $step = intval(round($monitor['MinZoomStep']+(($monitor['MaxZoomStep']-$monitor['MinZoomStep'])*$factor))); $ctrlCommand .= " --step=".$step; break; } case 'Con' : { if ( $monitor['AutoStopTimeout'] ) { $slowSpeed = intval(round($monitor['MinZoomSpeed']+(($monitor['MaxZoomSpeed']-$monitor['MinZoomSpeed'])*$slow))); if ( $speed < $slowSpeed ) { $ctrlCommand .= " --autostop"; } } break; } } break; } case 'iris' : { switch( $dirn ) { case 'Open' : { $factor = ($long_y-($y+1))/$long_y; break; } case 'Close' : { $factor = ($y+1)/$long_y; break; } } if ( $monitor['HasIrisSpeed'] ) { $speed = intval(round($monitor['MinIrisSpeed']+(($monitor['MaxIrisSpeed']-$monitor['MinIrisSpeed'])*$factor))); $ctrlCommand .= " --speed=".$speed; } switch( $mode ) { case 'Abs' : case 'Rel' : { $step = intval(round($monitor['MinIrisStep']+(($monitor['MaxIrisStep']-$monitor['MinIrisStep'])*$factor))); $ctrlCommand .= " --step=".$step; break; } } break; } case 'white' : { switch( $dirn ) { case 'In' : { $factor = ($long_y-($y+1))/$long_y; break; } case 'Out' : { $factor = ($y+1)/$long_y; break; } } if ( $monitor['HasWhiteSpeed'] ) { $speed = intval(round($monitor['MinWhiteSpeed']+(($monitor['MaxWhiteSpeed']-$monitor['MinWhiteSpeed'])*$factor))); $ctrlCommand .= " --speed=".$speed; } switch( $mode ) { case 'Abs' : case 'Rel' : { $step = intval(round($monitor['MinWhiteStep']+(($monitor['MaxWhiteStep']-$monitor['MinWhiteStep'])*$factor))); $ctrlCommand .= " --step=".$step; break; } } break; } case 'gain' : { switch( $dirn ) { case 'Up' : { $factor = ($long_y-($y+1))/$long_y; break; } case 'Down' : { $factor = ($y+1)/$long_y; break; } } if ( $monitor['HasGainSpeed'] ) { $speed = intval(round($monitor['MinGainSpeed']+(($monitor['MaxGainSpeed']-$monitor['MinGainSpeed'])*$factor))); $ctrlCommand .= " --speed=".$speed; } switch( $mode ) { case 'Abs' : case 'Rel' : { $step = intval(round($monitor['MinGainStep']+(($monitor['MaxGainStep']-$monitor['MinGainStep'])*$factor))); $ctrlCommand .= " --step=".$step; break; } } break; } case 'move' : { $xFactor = 0; $yFactor = 0; if ( preg_match( '/^Up/', $dirn ) ) { $yFactor = ($short_y-($y+1))/$short_y; } elseif ( preg_match( '/^Down/', $dirn ) ) { $yFactor = ($y+1)/$short_y; } if ( preg_match( '/Left$/', $dirn ) ) { $xFactor = ($short_x-($x+1))/$short_x; } elseif ( preg_match( '/Right$/', $dirn ) ) { $xFactor = ($x+1)/$short_x; } if ( $monitor['Orientation'] != '0' ) { $conversions = array( '90' => array( 'Up' => 'Left', 'Down' => 'Right', 'Left' => 'Down', 'Right' => 'Up', 'UpLeft' => 'DownLeft', 'UpRight' => 'UpLeft', 'DownLeft' => 'DownRight', 'DownRight' => 'UpRight', ), '180' => array( 'Up' => 'Down', 'Down' => 'Up', 'Left' => 'Right', 'Right' => 'Left', 'UpLeft' => 'DownRight', 'UpRight' => 'DownLeft', 'DownLeft' => 'UpRight', 'DownRight' => 'UpLeft', ), '270' => array( 'Up' => 'Right', 'Down' => 'Left', 'Left' => 'Up', 'Right' => 'Down', 'UpLeft' => 'UpRight', 'UpRight' => 'DownRight', 'DownLeft' => 'UpLeft', 'DownRight' => 'DownLeft', ), 'hori' => array( 'Up' => 'Up', 'Down' => 'Down', 'Left' => 'Right', 'Right' => 'Left', 'UpLeft' => 'UpRight', 'UpRight' => 'UpLeft', 'DownLeft' => 'DownRight', 'DownRight' => 'DownLeft', ), 'vert' => array( 'Up' => 'Down', 'Down' => 'Up', 'Left' => 'Left', 'Right' => 'Right', 'UpLeft' => 'DownLeft', 'UpRight' => 'DownRight', 'DownLeft' => 'UpLeft', 'DownRight' => 'UpRight', ), ); $new_dirn = $conversions[$monitor['Orientation']][$dirn]; $_REQUEST['control'] = preg_replace( "/_$dirn\$/", "_$new_dirn", $_REQUEST['control'] ); $dirn = $new_dirn; } if ( $monitor['HasPanSpeed'] && $xFactor ) { if ( $monitor['HasTurboPan'] ) { if ( $xFactor >= $turbo ) { $panSpeed = $monitor['TurboPanSpeed']; } else { $xFactor = $xFactor/$turbo; $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); } } else { $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); } $ctrlCommand .= " --panspeed=".$panSpeed; } if ( $monitor['HasTiltSpeed'] && $yFactor ) { if ( $monitor['HasTurboTilt'] ) { if ( $yFactor >= $turbo ) { $tiltSpeed = $monitor['TurboTiltSpeed']; } else { $yFactor = $yFactor/$turbo; $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); } } else { $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); } $ctrlCommand .= " --tiltspeed=".$tiltSpeed; } switch( $mode ) { case 'Rel' : case 'Abs' : { if ( preg_match( '/(Left|Right)$/', $dirn ) ) { $panStep = intval(round($monitor['MinPanStep']+(($monitor['MaxPanStep']-$monitor['MinPanStep'])*$xFactor))); $ctrlCommand .= " --panstep=".$panStep; } if ( preg_match( '/^(Up|Down)/', $dirn ) ) { $tiltStep = intval(round($monitor['MinTiltStep']+(($monitor['MaxTiltStep']-$monitor['MinTiltStep'])*$yFactor))); $ctrlCommand .= " --tiltstep=".$tiltStep; } break; } case 'Con' : { if ( $monitor['AutoStopTimeout'] ) { $slowPanSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$slow))); $slowTiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$slow))); if ( (!isset($panSpeed) || ($panSpeed < $slowPanSpeed)) && (!isset($tiltSpeed) || ($tiltSpeed < $slowTiltSpeed)) ) { $ctrlCommand .= " --autostop"; } } break; } } } } } } } else { if ( preg_match( '/^presetGoto(\d+)$/', $_REQUEST['control'], $matches ) ) { $_REQUEST['control'] = 'presetGoto'; $ctrlCommand .= " --preset=".$matches[1]; } elseif ( $_REQUEST['control'] == "presetGoto" && !empty($_REQUEST['preset']) ) { $ctrlCommand .= " --preset=".$_REQUEST['preset']; } elseif ( $_REQUEST['control'] == "presetSet" ) { if ( canEdit( 'Control' ) ) { $preset = validInt($_REQUEST['preset']); $newLabel = validJsStr($_REQUEST['newLabel']); $row = dbFetchOne( "select * from ControlPresets where MonitorId = '".$monitor['Id']."' and Preset = '".dbEscape($preset)."'" ); if ( $newLabel != $row['Label'] ) { if ( $newLabel ) $sql = "replace into ControlPresets ( MonitorId, Preset, Label ) values ( '".$monitor['Id']."', '".dbEscape($preset)."', '".dbEscape($newLabel)."' )"; else $sql = "delete from ControlPresets where MonitorId = '".$monitor['Id']."' and Preset = '".dbEscape($preset)."'"; dbQuery( $sql ); } $ctrlCommand .= " --preset=".$preset; } $ctrlCommand .= " --preset=".$preset; } elseif ( $_REQUEST['control'] == "moveMap" ) { $ctrlCommand .= " --xcoord=$x --ycoord=$y"; } } $ctrlCommand .= " --command=".$_REQUEST['control']; return( $ctrlCommand ); } ZoneMinder-1.26.5/web/includes/database.php000066400000000000000000000236451225361755400205430ustar00rootroot00000000000000= DB_LOG_DEBUG); if ( $dbLogLevel > DB_LOG_OFF ) Debug( "SQL-LOG: $sql".($noExecute?" (not executed)":"") ); return( $noExecute ); } function dbError( $sql ) { Fatal( "SQL-ERR '".mysql_error()."', statement was '".$sql."'" ); } function dbEscape( $string ) { if ( version_compare( phpversion(), "4.3.0", "<") ) if ( get_magic_quotes_gpc() ) return( mysql_escape_string( stripslashes( $string ) ) ); else return( mysql_escape_string( $string ) ); else if ( get_magic_quotes_gpc() ) return( mysql_real_escape_string( stripslashes( $string ) ) ); else return( mysql_real_escape_string( $string ) ); } function dbQuery( $sql ) { if ( dbLog( $sql, true ) ) return; if (!($result = mysql_query( $sql ))) dbError( $sql ); return( $result ); } function dbFetchOne( $sql, $col=false ) { dbLog( $sql ); if (!($result = mysql_query( $sql ))) dbError( $sql ); if ( $dbRow = mysql_fetch_assoc( $result ) ) return( $col?$dbRow[$col]:$dbRow ); return( false ); } function dbFetchAll( $sql, $col=false ) { dbLog( $sql ); if (!($result = mysql_query( $sql ))) dbError( $sql ); $dbRows = array(); while( $dbRow = mysql_fetch_assoc( $result ) ) $dbRows[] = $col?$dbRow[$col]:$dbRow; return( $dbRows ); } function dbFetchAssoc( $sql, $indexCol, $dataCol=false ) { dbLog( $sql ); if (!($result = mysql_query( $sql ))) dbError( $sql ); $dbRows = array(); while( $dbRow = mysql_fetch_assoc( $result ) ) $dbRows[$dbRow[$indexCol]] = $dataCol?$dbRow[$dataCol]:$dbRow; return( $dbRows ); } function dbFetch( $sql, $col=false ) { return( dbFetchAll( $sql, $col ) ); } function dbFetchNext( $result, $col=false ) { if ( $dbRow = mysql_fetch_assoc( $result ) ) return( $col?$dbRow[$col]:$dbRow ); return( false ); } function dbNumRows( $sql ) { dbLog( $sql ); if (!($result = mysql_query( $sql ))) dbError( $sql ); return( mysql_num_rows( $result ) ); } function dbInsertId() { return( mysql_insert_id() ); } function getEnumValues( $table, $column ) { $row = dbFetchOne( "describe $table $column" ); preg_match_all( "/'([^']+)'/", $row['Type'], $matches ); return( $matches[1] ); } function getSetValues( $table, $column ) { return( getEnumValues( $table, $column ) ); } function getUniqueValues( $table, $column, $asString=1 ) { $values = array(); $sql = "select distinct $column from $table where (not isnull($column) and $column != '') order by $column"; foreach( dbFetchAll( $sql ) as $row ) { if ( $asString ) $values[$row[$column]] = $row[$column]; else $values[] = $row[$column]; } return( $values ); } function getTableColumns( $table, $asString=1 ) { $columns = array(); $table = dbEscape($table); $sql = "describe $table"; foreach( dbFetchAll( $sql ) as $row ) { if ( $asString ) $columns[$row['Field']] = $row['Type']; else $columns[] = $row['Type']; } return( $columns ); } function getTableAutoInc( $table ) { $sql = "show table status where Name = '".dbEscape($table)."'"; $row = dbFetchOne( $sql ); return( $row['Auto_increment'] ); } function getTableDescription( $table, $asString=1 ) { $columns = array(); $table = dbEscape($table); $sql = "describe $table"; foreach( dbFetchAll( $sql ) as $row ) { $desc = array( 'name' => $row['Field'], 'required' => ($row['Null']=='NO')?true:false, 'default' => $row['Default'], 'db' => $row, ); if ( preg_match( "/^varchar\((\d+)\)$/", $row['Type'], $matches ) ) { $desc['type'] = 'text'; $desc['typeAttrib'] = 'varchar'; $desc['maxLength'] = $matches[1]; } elseif ( preg_match( "/^(\w+)?text$/", $row['Type'], $matches ) ) { $desc['type'] = 'text'; if (!empty($matches[1]) ) $desc['typeAttrib'] = $matches[1]; switch ( $matches[1] ) { case 'tiny' : $desc['maxLength'] = 255; break; case 'medium' : $desc['maxLength'] = 32768; break; case '' : case 'big' : //$desc['minLength'] = -128; break; default : Error( "Unexpected text qualifier '".$matches[1]."' found for field '".$row['Field']."' in table '".$table."'" ); break; } } elseif ( preg_match( "/^(enum|set)\((.*)\)$/", $row['Type'], $matches ) ) { $desc['type'] = 'text'; $desc['typeAttrib'] = $matches[1]; preg_match_all( "/'([^']+)'/", $matches[2], $matches ); $desc['values'] = $matches[1]; } elseif ( preg_match( "/^(\w+)?int\(\d+\)(?:\s+(unsigned))?$/", $row['Type'], $matches ) ) { $desc['type'] = 'integer'; switch ( $matches[1] ) { case 'tiny' : $desc['minValue'] = -128; $desc['maxValue'] = 127; break; case 'small' : $desc['minValue'] = -32768; $desc['maxValue'] = 32767; break; case 'medium' : $desc['minValue'] = -8388608; $desc['maxValue'] = 8388607; break; case '' : $desc['minValue'] = -2147483648; $desc['maxValue'] = 2147483647; break; case 'big' : //$desc['minValue'] = -128; //$desc['maxValue'] = 127; break; default : Error( "Unexpected integer qualifier '".$matches[1]."' found for field '".$row['Field']."' in table '".$table."'" ); break; } if ( !empty($matches[1]) ) $desc['typeAttrib'] = $matches[1]; if ( $desc['unsigned'] = ( isset($matches[2]) && $matches[2] == 'unsigned' ) ) { $desc['maxValue'] += (-$desc['minValue']); $desc['minValue'] = 0; } } elseif ( preg_match( "/^(?:decimal|numeric)\((\d+)(?:,(\d+))?\)(?:\s+(unsigned))?$/", $row['Type'], $matches ) ) { $desc['type'] = 'fixed'; $desc['range'] = $matches[1]; if ( isset($matches[2]) ) $desc['precision'] = $matches[2]; else $desc['precision'] = 0; $desc['unsigned'] = ( isset($matches[3]) && $matches[3] == 'unsigned' ); } elseif ( preg_match( "/^(datetime|timestamp|date|time)$/", $row['Type'], $matches ) ) { $desc['type'] = 'datetime'; switch ( $desc['typeAttrib'] = $matches[1] ) { case 'datetime' : case 'timestamp' : $desc['hasDate'] = true; $desc['hasTime'] = true; break; case 'date' : $desc['hasDate'] = true; $desc['hasTime'] = false; break; case 'time' : $desc['hasDate'] = false; $desc['hasTime'] = true; break; } } else { Error( "Can't parse database type '".$row['Type']."' found for field '".$row['Field']."' in table '".$table."'" ); } if ( $asString ) $columns[$row['Field']] = $desc; else $columns[] = $desc; } return( $columns ); } function dbFetchMonitor( $mid ) { return( dbFetchOne( "select * from Monitors where Id = '".dbEscape($mid)."'" ) ); } function dbFetchGroup( $gid ) { return( dbFetchOne( "select * from Groups where Id = '".dbEscape($gid)."'" ) ); } ?> ZoneMinder-1.26.5/web/includes/functions.php000066400000000000000000002170321225361755400210020ustar00rootroot00000000000000/dev/null' ) ) ); } function outputVideoStream( $id, $src, $width, $height, $format, $title="" ) { if ( file_exists( $src ) ) $mimeType = getMimeType( $src ); else { switch( $format ) { case 'asf' : $mimeType = "video/x-ms-asf"; break; case 'avi' : case 'wmv' : $mimeType = "video/x-msvideo"; break; case 'mov' : $mimeType = "video/quicktime"; break; case 'mpg' : case 'mpeg' : $mimeType = "video/mpeg"; break; case 'swf' : $mimeType = "application/x-shockwave-flash"; break; case '3gp' : $mimeType = "video/3gpp"; break; default : $mimeType = "video/$format"; break; } } if ( !$mimeType || ($mimeType == 'application/octet-stream') ) $mimeType = 'video/'.$format; $objectTag = false; if ( ZM_WEB_USE_OBJECT_TAGS ) { switch( $mimeType ) { case "video/x-ms-asf" : case "video/x-msvideo" : case "video/mp4" : { if ( isWindows() ) { ?> src="" name="" width="" height="" autostart="1" autoplay="1" showcontrols="0" controller="0"> <?= ZM_WEB_TITLE_PREFIX ?> - <?= validHtmlStr($title) ?> ZoneMinder-1.26.5/web/skins/classic/includes/init.php000066400000000000000000000000001225361755400224660ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/classic/includes/timeline_functions.php000066400000000000000000000434271225361755400254450ustar00rootroot00000000000000"; if ( $scaleRange >= $minLines ) { $scale['range'] = $scaleRange; break; } } if ( !isset($scale['range']) ) { $scale['range'] = (int)($range/($scale['factor']*$align)); } $scale['divisor'] = 1; while ( ($scale['range']/$scale['divisor']) > $maxLines ) { $scale['divisor']++; } $scale['lines'] = (int)($scale['range']/$scale['divisor']); return( $scale ); } function getYScale( $range, $minLines, $maxLines ) { $scale['range'] = $range; $scale['divisor'] = 1; while ( $scale['range']/$scale['divisor'] > $maxLines ) { $scale['divisor']++; } $scale['lines'] = (int)(($scale['range']-1)/$scale['divisor'])+1; return( $scale ); } function getSlotFrame( $slot ) { $slotFrame = isset($slot['frame'])?$slot['frame']['FrameId']:1; if ( false && $slotFrame ) { $slotFrame -= $monitor['PreEventCount']; if ( $slotFrame < 1 ) $slotFrame = 1; } return( $slotFrame ); } function parseFilterToTree( $filter ) { if ( count($filter['terms']) > 0 ) { $postfixExpr = array(); $postfixStack = array(); $priorities = array( '<' => 1, '<=' => 1, '>' => 1, '>=' => 1, '=' => 2, '!=' => 2, '=~' => 2, '!~' => 2, '=[]' => 2, '![]' => 2, 'and' => 3, 'or' => 4, ); for ( $i = 0; $i <= count($filter['terms']); $i++ ) { if ( !empty($filter['terms'][$i]['cnj']) ) { while( true ) { if ( !count($postfixStack) ) { $postfixStack[] = array( 'type'=>"cnj", 'value'=>$filter['terms'][$i]['cnj'], 'sqlValue'=>$filter['terms'][$i]['cnj']); break; } elseif ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' ) { $postfixStack[] = array( 'type'=>"cnj", 'value'=>$filter['terms'][$i]['cnj'], 'sqlValue'=>$filter['terms'][$i]['cnj']); break; } elseif ( $priorities[$filter['terms'][$i]['cnj']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) { $postfixStack[] = array( 'type'=>"cnj", 'value'=>$filter['terms'][$i]['cnj'], 'sqlValue'=>$filter['terms'][$i]['cnj']); break; } else { $postfixExpr[] = array_pop( $postfixStack ); } } } if ( !empty($filter['terms'][$i]['obr']) ) { for ( $j = 0; $j < $filter['terms'][$i]['obr']; $j++ ) { $postfixStack[] = array( 'type'=>"obr", 'value'=>$filter['terms'][$i]['obr']); } } if ( !empty($filter['terms'][$i]['attr']) ) { $dtAttr = false; switch ( $filter['terms'][$i]['attr']) { case 'MonitorName': $sqlValue = 'M.'.preg_replace( '/^Monitor/', '', $filter['terms'][$i]['attr']); break; case 'Name': $sqlValue = "E.Name"; break; case 'Cause': $sqlValue = "E.Cause"; break; case 'DateTime': $sqlValue = "E.StartTime"; $dtAttr = true; break; case 'Date': $sqlValue = "to_days( E.StartTime )"; $dtAttr = true; break; case 'Time': $sqlValue = "extract( hour_second from E.StartTime )"; break; case 'Weekday': $sqlValue = "weekday( E.StartTime )"; break; case 'Id': case 'Name': case 'MonitorId': case 'Length': case 'Frames': case 'AlarmFrames': case 'TotScore': case 'AvgScore': case 'MaxScore': case 'Archived': $sqlValue = "E.".$filter['terms'][$i]['attr']; break; case 'DiskPercent': $sqlValue = getDiskPercent(); break; case 'DiskBlocks': $sqlValue = getDiskBlocks(); break; default : $sqlValue = $filter['terms'][$i]['attr']; break; } if ( $dtAttr ) { $postfixExpr[] = array( 'type'=>"attr", 'value'=>$filter['terms'][$i]['attr'], 'sqlValue'=>$sqlValue, 'dtAttr'=>true ); } else { $postfixExpr[] = array( 'type'=>"attr", 'value'=>$filter['terms'][$i]['attr'], 'sqlValue'=>$sqlValue ); } } if ( isset($filter['terms'][$i]['op']) ) { if ( empty($filter['terms'][$i]['op']) ) { $filter['terms'][$i]['op' ]= '='; } switch ( $filter['terms'][$i]['op' ]) { case '=' : case '!=' : case '>=' : case '>' : case '<' : case '<=' : $sqlValue = $filter['terms'][$i]['op']; break; case '=~' : $sqlValue = "regexp"; break; case '!~' : $sqlValue = "not regexp"; break; case '=[]' : $sqlValue = 'in ('; break; case '![]' : $sqlValue = 'not in ('; break; } while( true ) { if ( !count($postfixStack) ) { $postfixStack[] = array( 'type'=>"op", 'value'=>$filter['terms'][$i]['op'], 'sqlValue'=>$sqlValue ); break; } elseif ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' ) { $postfixStack[] = array( 'type'=>"op", 'value'=>$filter['terms'][$i]['op'], 'sqlValue'=>$sqlValue ); break; } elseif ( $priorities[$filter['terms'][$i]['op']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) { $postfixStack[] = array( 'type'=>"op", 'value'=>$filter['terms'][$i]['op'], 'sqlValue'=>$sqlValue ); break; } else { $postfixExpr[] = array_pop( $postfixStack ); } } } if ( isset($filter['terms'][$i]['val']) ) { $valueList = array(); foreach ( preg_split( '/["\'\s]*?,["\'\s]*?/', preg_replace( '/^["\']+?(.+)["\']+?$/', '$1', $filter['terms'][$i]['val' ]) ) as $value ) { switch ( $filter['terms'][$i]['attr']) { case 'MonitorName': case 'Name': case 'Cause': $value = "'$value'"; break; case 'DateTime': $value = "'".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."'"; break; case 'Date': $value = "to_days( '".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."' )"; break; case 'Time': $value = "extract( hour_second from '".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."' )"; break; case 'Weekday': $value = "weekday( '".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."' )"; break; } $valueList[] = $value; } $postfixExpr[] = array( 'type'=>"val", 'value'=>$filter['terms'][$i]['val'], 'sqlValue'=>join( ',', $valueList ) ); } if ( !empty($filter['terms'][$i]['cbr']) ) { for ( $j = 0; $j < $filter['terms'][$i]['cbr']; $j++ ) { while ( count($postfixStack) ) { $element = array_pop( $postfixStack ); if ( $element['type'] == "obr" ) { $postfixExpr[count($postfixExpr)-1]['bracket'] = true; break; } $postfixExpr[] = $element; } } } } while ( count($postfixStack) ) { $postfixExpr[] = array_pop( $postfixStack ); } $exprStack = array(); //foreach ( $postfixExpr as $element ) //{ //echo $element['value']." "; //} //echo "
"; foreach ( $postfixExpr as $element ) { if ( $element['type'] == 'attr' || $element['type'] == 'val' ) { $node = array( 'data'=>$element, 'count'=>0 ); $exprStack[] = $node; } elseif ( $element['type'] == 'op' || $element['type'] == 'cnj' ) { $right = array_pop( $exprStack ); $left = array_pop( $exprStack ); $node = array( 'data'=>$element, 'count'=>2+$left['count']+$right['count'], 'right'=>$right, 'left'=>$left ); $exprStack[] = $node; } else { Fatal( "Unexpected element type '".$element['type']."', value '".$element['value']."'" ); } } if ( count($exprStack) != 1 ) { Fatal( "Expression stack has ".count($exprStack)." elements" ); } $exprTree = array_pop( $exprStack ); return( $exprTree ); } return( false ); } function _parseTreeToInfix( $node ) { $expression = ''; if ( isset($node) ) { if ( isset($node['left']) ) { if ( !empty($node['data']['bracket']) ) $expression .= '( '; $expression .= _parseTreeToInfix( $node['left'] ); } $expression .= $node['data']['value']." "; if ( isset($node['right']) ) { $expression .= _parseTreeToInfix( $node['right'] ); if ( !empty($node['data']['bracket']) ) $expression .= ') '; } } return( $expression ); } function parseTreeToInfix( $tree ) { return( _parseTreeToInfix( $tree ) ); } function _parseTreeToSQL( $node, $cbr=false ) { $expression = ''; if ( $node ) { if ( isset($node['left']) ) { if ( !empty($node['data']['bracket']) ) $expression .= '( '; $expression .= _parseTreeToSQL( $node['left'] ); } $inExpr = $node['data']['type'] == 'op' && ($node['data']['value'] == '=[]' || $node['data']['value'] == '![]'); $expression .= $node['data']['sqlValue']; if ( !$inExpr ) $expression .= ' '; if ( $cbr ) $expression .= ') '; if ( isset($node['right']) ) { $expression .= _parseTreeToSQL( $node['right'], $inExpr ); if ( !empty($node['data']['bracket']) ) $expression .= ') '; } } return( $expression ); } function parseTreeToSQL( $tree ) { return( _parseTreeToSQL( $tree ) ); } function _parseTreeToFilter( $node, &$terms, &$level ) { $elements = array(); if ( $node ) { if ( isset($node['left']) ) { if ( !empty($node['data']['bracket']) ) $terms[$level]['obr'] = 1; _parseTreeToFilter( $node['left'], $terms, $level ); } if ( $node['data']['type'] == 'cnj' ) { $level++; } $terms[$level][$node['data']['type']] = $node['data']['value']; if ( isset($node['right']) ) { _parseTreeToFilter( $node['right'], $terms, $level ); if ( !empty($node['data']['bracket']) ) $terms[$level]['cbr'] = 1; } } } function parseTreeToFilter( $tree ) { $terms = array(); if ( isset($tree) ) { $level = 0; _parseTreeToFilter( $tree, $terms, $level ); } return( array( 'terms' => $terms ) ); } function parseTreeToQuery( $tree ) { $filter = parseTreeToFilter( $tree ); parseFilter( $filter, false, '&' ); return( $filter['query'] ); } function _drawTree( $node, $level ) { if ( isset($node['left']) ) { _drawTree( $node['left'], $level+1 ); } echo str_repeat( ".", $level*2 ).$node['data']['value']."
"; if ( isset($node['right']) ) { _drawTree( $node['right'], $level+1 ); } } function drawTree( $tree ) { _drawTree( $tree, 0 ); } function _extractDatetimeRange( &$node, &$minTime, &$maxTime, &$expandable, $subOr ) { $pruned = $leftPruned = $rightPruned = false; if ( $node ) { if ( isset($node['left']) && isset($node['right']) ) { if ( $node['data']['type'] == 'cnj' && $node['data']['value'] == 'or' ) { $subOr = true; } elseif ( !empty($node['left']['data']['dtAttr']) ) { if ( $subOr ) { $expandable = false; } elseif ( $node['data']['type'] == 'op' ) { if ( $node['data']['value'] == '>' || $node['data']['value'] == '>=' ) { if ( !$minTime || $minTime > $node['right']['data']['sqlValue'] ) { $minTime = $node['right']['data']['value']; return( true ); } } if ( $node['data']['value'] == '<' || $node['data']['value'] == '<=' ) { if ( !$maxTime || $maxTime < $node['right']['data']['sqlValue'] ) { $maxTime = $node['right']['data']['value']; return( true ); } } } else { Fatal( "Unexpected node type '".$node['data']['type']."'" ); } return( false ); } $leftPruned = _extractDatetimeRange( $node['left'], $minTime, $maxTime, $expandable, $subOr ); $rightPruned = _extractDatetimeRange( $node['right'], $minTime, $maxTime, $expandable, $subOr ); if ( $leftPruned && $rightPruned ) { $pruned = true; } elseif ( $leftPruned ) { $node = $node['right']; } elseif ( $rightPruned ) { $node = $node['left']; } } } return( $pruned ); } function extractDatetimeRange( &$tree, &$minTime, &$maxTime, &$expandable ) { $minTime = ""; $maxTime = ""; $expandable = true; _extractDateTimeRange( $tree, $minTime, $maxTime, $expandable, false ); } function appendDatetimeRange( &$tree, $minTime, $maxTime=false ) { $attrNode = array( 'data'=>array( 'type'=>'attr', 'value'=>'DateTime', 'sqlValue'=>'E.StartTime', 'dtAttr'=>true ), 'count'=>0 ); $valNode = array( 'data'=>array( 'type'=>'val', 'value'=>$minTime, 'sqlValue'=>$minTime ), 'count'=>0 ); $opNode = array( 'data'=>array( 'type'=>'op', 'value'=>'>=', 'sqlValue'=>'>=' ), 'count'=>2, 'left'=>$attrNode, 'right'=>$valNode ); if ( isset($tree) ) { $cnjNode = array( 'data'=>array( 'type'=>'cnj', 'value'=>'and', 'sqlValue'=>'and' ), 'count'=>2+$tree['count']+$opNode['count'], 'left'=>$tree, 'right'=>$opNode ); $tree = $cnjNode; } else { $tree = $opNode; } if ( $maxTime ) { $attrNode = array( 'data'=>array( 'type'=>'attr', 'value'=>'DateTime', 'sqlValue'=>'E.StartTime', 'dtAttr'=>true ), 'count'=>0 ); $valNode = array( 'data'=>array( 'type'=>'val', 'value'=>$maxTime, 'sqlValue'=>$maxTime ), 'count'=>0 ); $opNode = array( 'data'=>array( 'type'=>'op', 'value'=>'<=', 'sqlValue'=>'<=' ), 'count'=>2, 'left'=>$attrNode, 'right'=>$valNode ); $cnjNode = array( 'data'=>array( 'type'=>'cnj', 'value'=>'and', 'sqlValue'=>'and' ), 'count'=>2+$tree['count']+$opNode['count'], 'left'=>$tree, 'right'=>$opNode ); $tree = $cnjNode; } } ?> ZoneMinder-1.26.5/web/skins/classic/js/000077500000000000000000000000001225361755400176325ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/classic/js/Makefile.am000066400000000000000000000001521225361755400216640ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/skins/classic/js dist_web_DATA = \ skin.js \ skin.js.php ZoneMinder-1.26.5/web/skins/classic/js/skin.js000066400000000000000000000225271225361755400211440ustar00rootroot00000000000000// // ZoneMinder base static javascript file, $Date$, $Revision$ // Copyright (C) 2001-2008 Philip Coombes // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // // This file should only contain static JavaScript and no php. // Use skin.js.php for JavaScript that need pre-processing // // Javascript window sizes var popupSizes = { 'bandwidth': { 'width': 200, 'height': 120 }, 'console': { 'width': 750, 'height': 312 }, 'control': { 'width': 380, 'height': 480 }, 'controlcaps': { 'width': 780, 'height': 320 }, 'controlcap': { 'width': 400, 'height': 400 }, 'cycle': { 'addWidth': 16, 'minWidth': 384, 'addHeight': 48 }, 'device': { 'width': 260, 'height': 150 }, 'devices': { 'width': 400, 'height': 240 }, 'donate': { 'width': 500, 'height': 280 }, 'event': { 'addWidth': 108, 'minWidth': 496, 'addHeight': 200, minHeight: 540 }, 'eventdetail': { 'width': 400, 'height': 220 }, 'events': { 'width': 760, 'height': 480 }, 'export': { 'width': 400, 'height': 340 }, 'filter': { 'width': 720, 'height': 360 }, 'filtersave': { 'width': 560, 'height': 160 }, 'frame': { 'addWidth': 32, 'minWidth': 384, 'addHeight': 100 }, 'frames': { 'width': 500, 'height': 300 }, 'function': { 'width': 248, 'height': 92 }, 'group': { 'width': 360, 'height': 180 }, 'groups': { 'width': 400, 'height': 220 }, 'image': { 'addWidth': 48, 'addHeight': 80 }, 'log': { 'width': 980, 'height': 720 }, 'login': { 'width': 720, 'height': 480 }, 'logout': { 'width': 240, 'height': 100 }, 'monitor': { 'width': 380, 'height': 364 }, 'monitorpreset':{ 'width': 400, 'height': 200 }, 'monitorprobe': { 'width': 500, 'height': 240 }, 'monitorselect':{ 'width': 160, 'height': 200 }, 'montage': { 'width': -1, 'height': -1 }, 'optionhelp': { 'width': 400, 'height': 320 }, 'options': { 'width': 960, 'height': 620 }, 'preset': { 'width': 300, 'height': 120 }, 'settings': { 'width': 200, 'height': 225 }, 'state': { 'width': 240, 'height': 124 }, 'stats': { 'width': 740, 'height': 200 }, 'timeline': { 'width': 760, 'height': 540 }, 'user': { 'width': 320, 'height': 420 }, 'version': { 'width': 360, 'height': 140 }, 'video': { 'width': 420, 'height': 360 }, 'videoview': { 'addWidth': 48, 'addHeight': 80 }, 'watch': { 'addWidth': 96, 'minWidth': 420, 'addHeight': 384 }, 'zone': { 'addWidth': 450, 'addHeight': 200, 'minHeight': 450 }, 'zones': { 'addWidth': 72, 'addHeight': 232 } }; var popupOptions = "resizable,scrollbars,status=no"; // Deprecated function newWindow( url, name, width, height ) { var windowId = window.open( url, name, popupOptions+",width="+width+",height="+height ); } function getPopupSize( tag, width, height ) { var popupSize = Object.clone( popupSizes[tag] ); if ( !popupSize ) { Error( "Can't find window size for tag '"+tag+"'" ); return( { 'width': 0, 'height': 0 } ); } if ( popupSize.width && popupSize.height ) { if ( width || height ) Warning( "Ignoring passed dimensions "+width+"x"+height+" when getting popup size for tag '"+tag+"'" ); return( popupSize ); } if ( popupSize.addWidth ) { popupSize.width = popupSize.addWidth; if ( !width ) Error( "Got addWidth but no passed width when getting popup size for tag '"+tag+"'" ); else popupSize.width += parseInt(width); } else if ( width ) { popupSize.width = width; Error( "Got passed width but no addWidth when getting popup size for tag '"+tag+"'" ); } if ( popupSize.minWidth && popupSize.width < popupSize.minWidth ) { Warning( "Adjusting to minimum width when getting popup size for tag '"+tag+"'" ); popupSize.width = popupSize.minWidth; } if ( popupSize.addHeight ) { popupSize.height = popupSize.addHeight; if ( !height ) Error( "Got addHeight but no passed height when getting popup size for tag '"+tag+"'" ); else popupSize.height += parseInt(height); } else if ( height ) { popupSize.height = height; Error( "Got passed height but no addHeight when getting popup size for tag '"+tag+"'" ); } if ( popupSize.minHeight && popupSize.height < popupSize.minHeight ) { Warning( "Adjusting to minimum height when getting popup size for tag '"+tag+"'" ); popupSize.height = popupSize.minHeight; } Debug( popupSize ); return( popupSize ); } function zmWindow() { var zmWin = window.open( 'http://www.zoneminder.com', 'ZoneMinder' ); zmWin.focus(); } function createPopup( url, name, tag, width, height ) { var popupSize = getPopupSize( tag, width, height ); var popupDimensions = ""; if ( popupSize.width > 0 ) popupDimensions += ",width="+popupSize.width; if ( popupSize.height > 0 ) popupDimensions += ",height="+popupSize.height; var popup = window.open( url, name, popupOptions+popupDimensions ); } function createEventPopup( eventId, eventFilter, width, height ) { var url = '?view=event&eid='+eventId; if ( eventFilter ) url += eventFilter; var name = 'zmEvent'; var popupSize = getPopupSize( 'event', width, height ); var popup = window.open( url, name, popupOptions+",width="+popupSize.width+",height="+popupSize.height ); } function createFramesPopup( eventId, width, height ) { var url = '?view=frames&eid='+eventId; var name = 'zmFrames'; var popupSize = getPopupSize( 'frames', width, height ); var popup = window.open( url, name, popupOptions+",width="+popupSize.width+",height="+popupSize.height ); } function createFramePopup( eventId, frameId, width, height ) { var url = '?view=frame&eid='+eventId+'&fid='+frameId; var name = 'zmFrame'; var popupSize = getPopupSize( 'frame', width, height ); var popup = window.open( url, name, popupOptions+",width="+popupSize.width+",height="+popupSize.height ); } function windowToFront() { top.window.focus(); } function closeWindow() { top.window.close(); } function refreshWindow() { window.location.reload( true ); } function refreshParentWindow() { if ( window.opener ) window.opener.location.reload( true ); } //Shows a message if there is an error in the streamObj or the stream doesn't exist. Returns true if error, false otherwise. function checkStreamForErrors( funcName, streamObj ) { if ( !streamObj ) { Error( funcName+": stream object was null" ); return true; } if ( streamObj.result == "Error" ) { Error( funcName+" stream error: "+streamObj.message ); return true; } return false; } function secsToTime( seconds ) { var timeString = "--"; if ( seconds < 60 ) timeString = seconds.toString(); else if ( seconds < 60*60 ) { var timeMins = parseInt(seconds/60); var timeSecs = seconds%60; if ( timeSecs < 10 ) timeSecs = '0'+timeSecs.toString().substr( 0, 4 ); else timeSecs = timeSecs.toString().substr( 0, 5 ); timeString = timeMins+":"+timeSecs; } else { var timeHours = parseInt(seconds/3600); var timeMins = (seconds%3600)/60; var timeSecs = seconds%60; if ( timeMins < 10 ) timeMins = '0'+timeMins.toString().substr( 0, 4 ); else timeMins = timeMins.toString().substr( 0, 5 ); if ( timeSecs < 10 ) timeSecs = '0'+timeSecs.toString().substr( 0, 4 ); else timeSecs = timeSecs.toString().substr( 0, 5 ); timeString = timeHours+":"+timeMins+":"+timeSecs; } return( timeString ); } function submitTab( tab ) { var form = $('contentForm'); form.action.value = ""; form.tab.value = tab; form.submit(); } function configureDeleteButton( element ) { var form = element.form; var checked = element.checked; if ( !checked ) { for ( var i = 0; i < form.elements.length; i++ ) { if ( form.elements[i].name == element.name ) { if ( form.elements[i].checked ) { checked = true; break; } } } } form.deleteBtn.disabled = !checked; } function confirmDelete( message ) { return( confirm( message?message:'Are you sure you wish to delete?' ) ); } if ( refreshParent ) { refreshParentWindow(); } if ( focusWindow ) { windowToFront(); } ZoneMinder-1.26.5/web/skins/classic/js/skin.js.php000066400000000000000000000027311225361755400217250ustar00rootroot00000000000000 var AJAX_TIMEOUT = ; var currentView = ''; var thisUrl = ""; var skinPath = ""; var canEditSystem = ; var canViewSystem = ; var refreshParent = ; var focusWindow = ; var imagePrefix = ""; ZoneMinder-1.26.5/web/skins/classic/lang/000077500000000000000000000000001225361755400201375ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/classic/lang/Makefile.am000066400000000000000000000001431225361755400221710ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/skins/classic/lang dist_web_DATA = # No files here ZoneMinder-1.26.5/web/skins/classic/skin.php000066400000000000000000000041551225361755400207000ustar00rootroot00000000000000 ZoneMinder-1.26.5/web/skins/classic/svn-commit.tmp000066400000000000000000000001421225361755400220310ustar00rootroot00000000000000 --This line, and those below, will be ignored-- M views/plugin.php M views/css/plugin.css ZoneMinder-1.26.5/web/skins/classic/views/000077500000000000000000000000001225361755400203535ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/classic/views/Makefile.am000066400000000000000000000014301225361755400224050ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu SUBDIRS = \ css \ js webdir = @WEB_PREFIX@/skins/classic/views dist_web_DATA = \ bandwidth.php \ blank.php \ console.php \ controlcap.php \ controlcaps.php \ control.php \ controlpreset.php \ cycle.php \ device.php \ devices.php \ donate.php \ error.php \ eventdetail.php \ event.php \ events.php \ export.php \ filter.php \ filtersave.php \ frame.php \ frames.php \ function.php \ group.php \ groups.php \ log.php \ login.php \ logout.php \ Makefile.am \ monitor.php \ monitorpreset.php \ monitorprobe.php \ montage.php \ none.php \ optionhelp.php \ options.php \ postlogin.php \ settings.php \ state.php \ stats.php \ status.php \ timeline.php \ user.php \ version.php \ video.php \ watch.php \ zone.php \ zones.php ZoneMinder-1.26.5/web/skins/classic/views/bandwidth.php000066400000000000000000000035651225361755400230410ustar00rootroot00000000000000

ZoneMinder-1.26.5/web/skins/classic/views/blank.php000066400000000000000000000021101225361755400221450ustar00rootroot00000000000000 ZoneMinder-1.26.5/web/skins/classic/views/console.php000066400000000000000000000334601225361755400225340ustar00rootroot00000000000000 $SLANG['Events'], "filter" => array( "terms" => array( ) ), ), array( "title" => $SLANG['Hour'], "filter" => array( "terms" => array( array( "attr" => "DateTime", "op" => ">=", "val" => "-1 hour" ), ) ), ), array( "title" => $SLANG['Day'], "filter" => array( "terms" => array( array( "attr" => "DateTime", "op" => ">=", "val" => "-1 day" ), ) ), ), array( "title" => $SLANG['Week'], "filter" => array( "terms" => array( array( "attr" => "DateTime", "op" => ">=", "val" => "-7 day" ), ) ), ), array( "title" => $SLANG['Month'], "filter" => array( "terms" => array( array( "attr" => "DateTime", "op" => ">=", "val" => "-1 month" ), ) ), ), array( "title" => $SLANG['Archived'], "filter" => array( "terms" => array( array( "attr" => "Archived", "op" => "=", "val" => "1" ), ) ), ), ); $running = daemonCheck(); $status = $running?$SLANG['Running']:$SLANG['Stopped']; if ( $group = dbFetchOne( "select * from Groups where Id = '".(empty($_COOKIE['zmGroup'])?0:dbEscape($_COOKIE['zmGroup']))."'" ) ) $groupIds = array_flip(explode( ',', $group['MonitorIds'] )); noCacheHeaders(); $maxWidth = 0; $maxHeight = 0; $cycleCount = 0; $minSequence = 0; $maxSequence = 1; $seqIdList = array(); $monitors = dbFetchAll( "select * from Monitors order by Sequence asc" ); $displayMonitors = array(); for ( $i = 0; $i < count($monitors); $i++ ) { if ( !visibleMonitor( $monitors[$i]['Id'] ) ) { continue; } if ( $group && !empty($groupIds) && !array_key_exists( $monitors[$i]['Id'], $groupIds ) ) { continue; } $monitors[$i]['Show'] = true; if ( empty($minSequence) || ($monitors[$i]['Sequence'] < $minSequence) ) { $minSequence = $monitors[$i]['Sequence']; } if ( $monitors[$i]['Sequence'] > $maxSequence ) { $maxSequence = $monitors[$i]['Sequence']; } $monitors[$i]['zmc'] = zmcStatus( $monitors[$i] ); $monitors[$i]['zma'] = zmaStatus( $monitors[$i] ); $monitors[$i]['ZoneCount'] = dbFetchOne( "select count(Id) as ZoneCount from Zones where MonitorId = '".$monitors[$i]['Id']."'", "ZoneCount" ); $counts = array(); for ( $j = 0; $j < count($eventCounts); $j++ ) { $filter = addFilterTerm( $eventCounts[$j]['filter'], count($eventCounts[$j]['filter']['terms']), array( "cnj" => "and", "attr" => "MonitorId", "op" => "=", "val" => $monitors[$i]['Id'] ) ); parseFilter( $filter ); $counts[] = "count(if(1".$filter['sql'].",1,NULL)) as EventCount$j"; $monitors[$i]['eventCounts'][$j]['filter'] = $filter; } $sql = "select ".join($counts,", ")." from Events as E where MonitorId = '".$monitors[$i]['Id']."'"; $counts = dbFetchOne( $sql ); if ( $monitors[$i]['Function'] != 'None' ) { $cycleCount++; $scaleWidth = reScale( $monitors[$i]['Width'], $monitors[$i]['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); $scaleHeight = reScale( $monitors[$i]['Height'], $monitors[$i]['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); if ( $maxWidth < $scaleWidth ) $maxWidth = $scaleWidth; if ( $maxHeight < $scaleHeight ) $maxHeight = $scaleHeight; } $monitors[$i] = array_merge( $monitors[$i], $counts ); $seqIdList[] = $monitors[$i]['Id']; $displayMonitors[] = $monitors[$i]; } $lastId = 0; $seqIdUpList = array(); foreach ( $seqIdList as $seqId ) { if ( !empty($lastId) ) $seqIdUpList[$seqId] = $lastId; else $seqIdUpList[$seqId] = $seqId; $lastId = $seqId; } $lastId = 0; $seqIdDownList = array(); foreach ( array_reverse($seqIdList) as $seqId ) { if ( !empty($lastId) ) $seqIdDownList[$seqId] = $lastId; else $seqIdDownList[$seqId] = $seqId; $lastId = $seqId; } $cycleWidth = $maxWidth; $cycleHeight = $maxHeight; $eventsView = ZM_WEB_EVENTS_VIEW; $eventsWindow = 'zm'.ucfirst(ZM_WEB_EVENTS_VIEW); $eventCount = 0; for ( $i = 0; $i < count($eventCounts); $i++ ) { $eventCounts[$i]['total'] = 0; } $zoneCount = 0; foreach( $displayMonitors as $monitor ) { for ( $i = 0; $i < count($eventCounts); $i++ ) { $eventCounts[$i]['total'] += $monitor['EventCount'.$i]; } $zoneCount += $monitor['ZoneCount']; } $seqUpFile = getSkinFile( 'graphics/seq-u.gif' ); $seqDownFile = getSkinFile( 'graphics/seq-d.gif' ); $versionClass = (ZM_DYN_DB_VERSION&&(ZM_DYN_DB_VERSION!=ZM_VERSION))?'errorText':''; xhtmlHeaders( __FILE__, $SLANG['Console'] ); ?>
'.$monitor['Function'].'', canEdit( 'Monitors' ) ) ?> '.$monitor['Device'].' ('.$monitor['Channel'].')', canEdit( 'Monitors' ) ) ?> '.preg_replace( '/^.*@/', '', $monitor['Host'] ).'', canEdit( 'Monitors' ) ) ?> '.preg_replace( '/^.*\//', '', $monitor['Path'] ).'', canEdit( 'Monitors' ) ) ?> '.preg_replace( '/^.*\//', '', $monitor['Path'] ).'', canEdit( 'Monitors' ) ) ?>   ', $monitor['Sequence']>$minSequence ) ?>', $monitor['Sequence']<$maxSequence ) ?> disabled="disabled"/>
ZoneMinder-1.26.5/web/skins/classic/views/control.php000066400000000000000000000046651225361755400225570ustar00rootroot00000000000000
ZoneMinder-1.26.5/web/skins/classic/views/controlcap.php000066400000000000000000001075711225361755400232430ustar00rootroot00000000000000 $SLANG['New'], 'Type' => "Local", 'Protocol' => "", 'CanWake' => "", 'CanSleep' => "", 'CanReset' => "", 'CanMove' => "", 'CanMoveDiag' => "", 'CanMoveMap' => "", 'CanMoveAbs' => "", 'CanMoveRel' => "", 'CanMoveCon' => "", 'CanPan' => "", 'MinPanRange' => "", 'MaxPanRange' => "", 'MinPanStep' => "", 'MaxPanStep' => "", 'HasPanSpeed' => "", 'MinPanSpeed' => "", 'MaxPanSpeed' => "", 'HasTurboPan' => "", 'TurboPanSpeed' => "", 'CanTilt' => "", 'MinTiltRange' => "", 'MaxTiltRange' => "", 'MinTiltStep' => "", 'MaxTiltStep' => "", 'HasTiltSpeed' => "", 'MinTiltSpeed' => "", 'MaxTiltSpeed' => "", 'HasTurboTilt' => "", 'TurboTiltSpeed' => "", 'CanZoom' => "", 'CanZoomAbs' => "", 'CanZoomRel' => "", 'CanZoomCon' => "", 'MinZoomRange' => "", 'MaxZoomRange' => "", 'MinZoomStep' => "", 'MaxZoomStep' => "", 'HasZoomSpeed' => "", 'MinZoomSpeed' => "", 'MaxZoomSpeed' => "", 'CanFocus' => "", 'CanAutoFocus' => "", 'CanFocusAbs' => "", 'CanFocusRel' => "", 'CanFocusCon' => "", 'MinFocusRange' => "", 'MaxFocusRange' => "", 'MinFocusStep' => "", 'MaxFocusStep' => "", 'HasFocusSpeed' => "", 'MinFocusSpeed' => "", 'MaxFocusSpeed' => "", 'CanIris' => "", 'CanAutoIris' => "", 'CanIrisAbs' => "", 'CanIrisRel' => "", 'CanIrisCon' => "", 'MinIrisRange' => "", 'MaxIrisRange' => "", 'MinIrisStep' => "", 'MaxIrisStep' => "", 'HasIrisSpeed' => "", 'MinIrisSpeed' => "", 'MaxIrisSpeed' => "", 'CanGain' => "", 'CanAutoGain' => "", 'CanGainAbs' => "", 'CanGainRel' => "", 'CanGainCon' => "", 'MinGainRange' => "", 'MaxGainRange' => "", 'MinGainStep' => "", 'MaxGainStep' => "", 'HasGainSpeed' => "", 'MinGainSpeed' => "", 'MaxGainSpeed' => "", 'CanWhite' => "", 'CanAutoWhite' => "", 'CanWhiteAbs' => "", 'CanWhiteRel' => "", 'CanWhiteCon' => "", 'MinWhiteRange' => "", 'MaxWhiteRange' => "", 'MinWhiteStep' => "", 'MaxWhiteStep' => "", 'HasWhiteSpeed' => "", 'MinWhiteSpeed' => "", 'MaxWhiteSpeed' => "", 'HasPresets' => "", 'NumPresets' => "", 'HasHomePreset' => "", 'CanSetPresets' => "", ); } $newControl = $control; } $focusWindow = true; xhtmlHeaders(__FILE__, $SLANG['ControlCap']." - ".$newControl['Name'] ); ?>
    $value ) { if ( $tab == $name ) { ?>
$SLANG['Local'], 'Remote'=>$SLANG['Remote'], 'Ffmpeg'=>$SLANG['Ffmpeg'] ); ?>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked">
checked="checked"/>
checked="checked"/>
checked="checked">
checked="checked"/>
checked="checked"/>
checked="checked">
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked">
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
disabled="disabled"/>
ZoneMinder-1.26.5/web/skins/classic/views/controlcaps.php000066400000000000000000000101261225361755400234130ustar00rootroot00000000000000
disabled="disabled"/>
disabled="disabled"/>
ZoneMinder-1.26.5/web/skins/classic/views/controlpreset.php000066400000000000000000000047401225361755400237740ustar00rootroot00000000000000

ZoneMinder-1.26.5/web/skins/classic/views/css/000077500000000000000000000000001225361755400211435ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/classic/views/css/Makefile.am000066400000000000000000000007731225361755400232060ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/skins/classic/views/css dist_web_DATA = \ console.css \ controlcaps.css \ control.css \ devices.css \ event.css \ events.css \ export.css \ filter.css \ frame.css \ frames.css \ groups.css \ log.css \ monitor.css \ montage_2wide.css \ montage_3wide50enlarge.css \ montage_3wide.css \ montage_4wide.css \ montage.css \ montage_freeform.css \ options.css \ stats.css \ timeline.css \ timeline.css.php \ video.css \ watch.css \ zone.css ZoneMinder-1.26.5/web/skins/classic/views/css/console.css000066400000000000000000000023461225361755400233240ustar00rootroot00000000000000#systemTime { float: left; } #title { margin: 0 auto; text-align: center; width: 50%; } #systemStats { float: right; } #monitorSummary { float: left; text-align: left; width: 20%; } #devices { float: left; } #loginBandwidth { margin: 0 auto; text-align: center; width: 40%; } #cycleMontage { float: right; } #options { float: right; text-align: right; width: 20%; } #consoleTable { width: 100%; } #consoleTable tr.highlight { background-color: #eeeeee; } #consoleTable thead th { padding-bottom: 4px; vertical-align: middle; } #consoleTable tfoot td { padding-top: 4px; vertical-align: middle; } #consoleTable th,td { height: 16px; text-align: left; } #consoleTable .colOrder { text-align: center; } #consoleTable .colMark { width: 32px; text-align: center; } #consoleTable .colEvents { text-align: right; } #consoleTable .colZones { text-align: right; } #consoleTable .colLeftButtons { text-align: left; } #consoleTable .colLeftButtons input { margin-right: 24px; } #consoleTable .colRightButtons { text-align: right; padding-right: 8px; } #consoleTable .colRightButtons input { margin: 0 8px; } ZoneMinder-1.26.5/web/skins/classic/views/css/control.css000066400000000000000000000000441225361755400233330ustar00rootroot00000000000000@import url(../../css/control.css); ZoneMinder-1.26.5/web/skins/classic/views/css/controlcaps.css000066400000000000000000000003571225361755400242110ustar00rootroot00000000000000#content table.major .colCanMove, #content table.major .colCanZoom, #content table.major .colCanFocus, #content table.major .colCanIris, #content table.major .colCanWhiteBal, #content table.major .colHasPresets { text-align: center; } ZoneMinder-1.26.5/web/skins/classic/views/css/devices.css000066400000000000000000000000571225361755400233010ustar00rootroot00000000000000input.set { border: 1px #7F7FB2 dashed; } ZoneMinder-1.26.5/web/skins/classic/views/css/event.css000066400000000000000000000063311225361755400230010ustar00rootroot00000000000000#dataBar { width: 100%; margin: 2px auto; text-align: center; } #dataBar #dataTable { width: 100%; } #dataBar #dataTable td { text-align: center; padding: 2px; } #menuBar1 { width: 100%; height: 1.5em; padding: 3px 0; text-align: center; clear: both; } #menuBar1 #nameControl { float: left; } #menuBar1 #nameControl #eventName { margin-right: 4px; } #menuBar1 #replayControl { float: right; margin-left: 8px; } #menuBar1 #scaleControl { float: right; margin-left: 8px; } #menuBar2 { width: 100%; height: 1.2em; padding: 3px 0; margin-bottom: 4px; } #menuBar2 div { text-align: left; float: left; padding: 0 12px; } #menuBar2 #closeWindow { float: right; text-align: right; } #imageFeed { text-align: center; } #monitorStatus { width: 100%; margin-top: 3px; margin-bottom: 2px; text-align: center; } #monitorStatus #enableAlarms { position: absolute; left: 4px; } #monitorStatus #forceAlarm { position: absolute; right: 4px; } #monitorStatus #monitorState { } #dvrControls { margin-top: 3px; margin-bottom: 2px; text-align: center; } #dvrControls input { height: 20px; width: 28px; padding-bottom: 3px; margin: 0 3px; } #dvrControls input[disabled=disabled] { color: #aaaaaa; } #dvrControls input.active { border: 1px solid blue; } #dvrControls input.inactive { border: 1px solid green; } #dvrControls input.unavail { border: 1px solid red; } #replayStatus { margin: 3px 0 2px; text-align: center; clear: both; } #replayStatus > span { padding: 0 4px; } #progressBar { position: relative; border: 1px solid #666666; height: 15px; margin: 0 auto; } #progressBar .progressBox { position: absolute; top: 0px; left: 0px; height: 15px; background: #eeeeee; border-left: 1px solid #999999; } #progressBar .complete { background: #aaaaaa; } #eventStills { width: 100%; position: relative; } #eventThumbsPanel { position: relative; width: 100%; margin: 4px auto; z-index: 1; } #eventThumbs { margin: 0 auto; width: 100%; overflow: hidden; height: 300px; } #eventThumbs img { height: 25px; /* HACK - Although this is pixels it will be interpreted as a scale %ge, so 25px = 25% scaling for thumbnails */ margin: 2px; background-color: #dddddd; } #eventThumbs img.placeholder { /* width: 100px; */ } #eventThumbs img.selected { } #eventImagePanel { position: absolute; z-index: 10; } #eventImageFrame { border: 2px solid gray; background-color: white; padding: 4px; } #eventImage { } #eventImageBar { margin-top: 2px; } #eventImageStats { float: left; } #eventImageData { margin: 0 auto; padding-top: 2px; } #eventImageClose { float: right; } #eventImageNav { position: relative; } #eventImageNav input { width: 32px; font-size: 16px; } #thumbsSliderPanel { width: 400px; margin: 4px auto 0; background: #888888; padding: 1px; } #thumbsSlider { width: 400px; height: 10px; background: #dddddd; } #thumbsKnob { width: 8px; height: 10px; background-color: #444444; } ZoneMinder-1.26.5/web/skins/classic/views/css/events.css000066400000000000000000000014721225361755400231650ustar00rootroot00000000000000#controls { height: 16px; width: 100%; text-align: center; margin: 0 auto; position: relative; } #controls a { width: 32%; } #controls #refreshLink { position: absolute; left: 0%; text-align: left; } #controls #filterLink { position: absolute; left: 34%; text-align: center; } #controls #timelineLink { position: absolute; left: 68%; text-align: right; } #contentTable.major .colTime { white-space: nowrap; } #contentTable.major .colId, #contentTable.major .colDuration, #contentTable.major .colFrames, #contentTable.major .colAlarmFrames, #contentTable.major .colTotScore, #contentTable.major .colMaxScore, #contentTable.major .colAvgScore { text-align: center; } #contentTable.major td.colDuration { text-align: right; padding-right: 8px; } ZoneMinder-1.26.5/web/skins/classic/views/css/export.css000066400000000000000000000003071225361755400231760ustar00rootroot00000000000000#contentTable + input { margin-top: 6px; } #contentTable th, #contentTable td { white-space: nowrap; } #exportProgress { margin: 8px auto 4px; } #downloadLink { margin-top: 8px; } ZoneMinder-1.26.5/web/skins/classic/views/css/filter.css000066400000000000000000000004731225361755400231460ustar00rootroot00000000000000#filterSelector { } table.filterTable { width: 100%; } #fieldsTable td { height: 16px; } #fieldsTable input[type=button] { width: 1.6em; margin-left: 2px; text-align: center; } #sortTable input[type=text] { margin-right: 4px; } #actionsTable input[type=text] { margin-left: 4px; } ZoneMinder-1.26.5/web/skins/classic/views/css/frame.css000066400000000000000000000005351225361755400227520ustar00rootroot00000000000000#controls { width: 80%; text-align: center; margin: 0 auto; } #controls a { width: 40px; margin-left: -20px; } #firstLink { position: absolute; left: 13%; } #prevLink { position: absolute; left: 37%; } #nextLink { position: absolute; left: 63%; } #lastLink { position: absolute; left: 87%; } ZoneMinder-1.26.5/web/skins/classic/views/css/frames.css000066400000000000000000000004731225361755400231360ustar00rootroot00000000000000#contentTable.major .colId, #contentTable.major .colType, #contentTable.major .colTimeStamp, #contentTable.major .colTimeDelta, #contentTable.major .colScore { text-align: center; } tr.alarm { background-color: #fa8072; } tr.bulk { background-color: #cccccc; } tr.normal { background-color: #ffffff; } ZoneMinder-1.26.5/web/skins/classic/views/css/groups.css000066400000000000000000000001311225361755400231670ustar00rootroot00000000000000#contentTable .colSelect { text-align: center; } #contentTable .colSelect input { } ZoneMinder-1.26.5/web/skins/classic/views/css/log.css000066400000000000000000000013351225361755400224400ustar00rootroot00000000000000#logSummary { margin: 4px auto 0; border-collapse: collapse; } #logSummary tr { margin: 0; padding: 0; } #logSummary td { border: 1px solid #7f7fb2; padding: 0 6px; text-align: center; font-size: 10px; line-height: 15px; } tr.log-fat td { background-color:#ffcccc; font-weight: bold; } tr.log-err td { background-color:#ffcccc; } tr.log-war td { background-color: #ffe4b5; } tr.log-dbg td { color: #666666; font-style: italic; } #exportLog label { vertical-align: middle; } #exportLog input[type=radio] { margin-right: 4px; vertical-align: middle; } #exportError { display: none; color: #dc143c; margin-bottom: 8px; } #exportErrorText { } ZoneMinder-1.26.5/web/skins/classic/views/css/monitor.css000066400000000000000000000001211225361755400233360ustar00rootroot00000000000000.swatch { border: 1px solid black; margin-left: 3px; padding: 0px; } ZoneMinder-1.26.5/web/skins/classic/views/css/montage.css000066400000000000000000000010241225361755400233040ustar00rootroot00000000000000#header { width: 99%; } #layout { margin-right: 10px; } #content { width: 99%; } #monitors .alarm { color: #ff0000; } #monitors .alert { color: #ffa500; } #monitors .imageFeed { text-align: center; vertical-align: middle; } #monitors .imageFeed img.idle { border: 2px solid #ffffff; } #monitors .imageFeed img.alarm { border: 2px solid #ff0000; } #monitors .imageFeed img.alert { border: 2px solid #ffa500; } #monitors .monitorState { margin: 2px auto; text-align: center; } ZoneMinder-1.26.5/web/skins/classic/views/css/montage_2wide.css000066400000000000000000000013031225361755400243760ustar00rootroot00000000000000#monitors { width: 100%; margin: 0 auto; text-align: center; } #monitors .monitorFrame { padding: 1px; float: left; } #monitors .monitor { } #monitorFrame0, #monitorFrame2, #monitorFrame4, #monitorFrame6, #monitorFrame8, #monitorFrame10, #monitorFrame12, #monitorFrame14 { width: 49%; clear: both; } #monitor0, #monitor2, #monitor4, #monitor6, #monitor8, #monitor10, #monitor12, #monitor14 { float: right; } #monitorFrame1, #monitorFrame3, #monitorFrame5, #monitorFrame7, #monitorFrame9, #monitorFrame11, #monitorFrame13, #monitorFrame15 { width: 49%; } #monitor1, #monitor3, #monitor5, #monitor7, #monitor9, #monitor11, #monitor13, #monitor15 { float: left; } ZoneMinder-1.26.5/web/skins/classic/views/css/montage_3wide.css000066400000000000000000000014451225361755400244060ustar00rootroot00000000000000#monitors { width: 100%; margin: 0 auto; text-align: center; } #monitors .monitorFrame { padding: 1px; float: left; width: 33%; } #monitors .monitor { text-align: center; } #monitorFrame0, #monitorFrame3, #monitorFrame6, #monitorFrame9, #monitorFrame12, #monitorFrame15 { clear: both; } #monitor0, #monitor3, #monitor6, #monitor9, #monitor12, #monitor15 { float: right; } #monitorFrame1, #monitorFrame4, #monitorFrame7, #monitorFrame10, #monitorFrame13, #monitorFrame16 { text-align: center; } #monitor1, #monitor4, #monitor7, #monitor10, #monitor13, #monitor16 { } #monitorFrame2, #monitorFrame5, #monitorFrame8, #monitorFrame11, #monitorFrame14, #monitorFrame17 { } #monitor2, #monitor5, #monitor8, #monitor11, #monitor14, #monitor17 { float: left; } ZoneMinder-1.26.5/web/skins/classic/views/css/montage_3wide50enlarge.css000066400000000000000000000027341225361755400261130ustar00rootroot00000000000000#monitors { width: 100%; margin: 0 auto; text-align: center; } #monitors .monitorFrame { float: left; padding: 1px; } #monitors .monitor { text-align: center; } #monitorFrame0, #monitorFrame3, #monitorFrame6, #monitorFrame9, #monitorFrame12, #monitorFrame15 { width: 32%; text-align: right; clear: both; } #monitor0, #monitor3, #monitor6, #monitor9, #monitor12, #monitor15 { margin-left: auto; margin-right: 0; } #monitorFrame1, #monitorFrame4, #monitorFrame7, #monitorFrame10, #monitorFrame13, #monitorFrame16 { width: 32%; text-align: center; } #monitor1, #monitor4, #monitor7, #monitor10, #monitor13, #monitor16 { margin-left: auto; margin-right: auto; } #monitorFrame2, #monitorFrame5, #monitorFrame8, #monitorFrame11, #monitorFrame14, #monitorFrame17 { width: 32%; text-align: left; } #monitor2, #monitor5, #monitor8, #monitor11, #monitor14, #monitor17 { margin-left: 0; margin-right: auto; } #monitors .imageFeed img { width: 100%; height: 100%; } #monitors .monitorFrame > div.alarm, #monitors .monitorFrame > div.alert { position: absolute; width: 96%; left: 2%; top: 72px; } #monitors .monitorFrame > div.alert { z-index: 100; } #monitors .monitorFrame > div.alarm { z-index: 200; } #monitors .monitorFrame div.alarm, #monitors .monitorFrame div.alert { font-size: 140%; line-height: 160%; } #monitors .monitorFrame div.monitorState { text-align: center; } ZoneMinder-1.26.5/web/skins/classic/views/css/montage_4wide.css000066400000000000000000000017651225361755400244140ustar00rootroot00000000000000#monitors { width: 100%; margin: 0 auto; text-align: center; } #monitors .monitorFrame { padding: 1px; float: left; width: 24.5%; } #monitors .monitor { text-align: center; } #monitorFrame0, #monitorFrame4, #monitorFrame8, #monitorFrame12, #monitorFrame16, #monitorFrame20 { clear: both; } #monitor0, #monitor4, #monitor8, #monitor12, #monitor16, #monitor20 { float: right; } #monitorFrame1, #monitorFrame5, #monitorFrame9, #monitorFrame13, #monitorFrame17, #monitorFrame21 { text-align: center; } #monitor1, #monitor5, #monitor9, #monitor13, #monitor17, #monitor21 { } #monitorFrame2, #monitorFrame6, #monitorFrame10, #monitorFrame14, #monitorFrame18, #monitorFrame22 { text-align: center; } #monitor2, #monitor6, #monitor10, #monitor14, #monitor18, #monitor22 { } #monitorFrame3, #monitorFrame7, #monitorFrame11, #monitorFrame15, #monitorFrame19, #monitorFrame23 { } #monitor3, #monitor7, #monitor11, #monitor15, #monitor19, #monitor23 { float: left; } ZoneMinder-1.26.5/web/skins/classic/views/css/montage_freeform.css000066400000000000000000000001721225361755400251740ustar00rootroot00000000000000#monitors { margin: 0 auto; text-align: center; } #monitors .monitorFrame { float: left; padding: 1px; } ZoneMinder-1.26.5/web/skins/classic/views/css/options.css000066400000000000000000000005671225361755400233600ustar00rootroot00000000000000input.small { width: 6em; } input.medium { width: 9em; } input.large { width: 20em; } #contentTable.optionTable th, #contentTable.optionTable td { vertical-align: top; } #contentTable.userTable th, #contentTable.userTable td { text-align: center; } #contentTable.userTable .colMonitor, #contentTable.userTable .colUsername { text-align: left; } ZoneMinder-1.26.5/web/skins/classic/views/css/plugin.css000066400000000000000000000005151225361755400231540ustar00rootroot00000000000000#settingsPanel { float: left; margin: 0 2px; } #pluginSettings { border-collapse: collapse; } #pluginSettings th, #pluginSettings td { border: 1px solid #7f7fb2; padding: 3px; text-align: left; } #pluginSettings th[scope=row] { padding: 4px 3px 3px; vertical-align: top; white-space: nowrap; } ZoneMinder-1.26.5/web/skins/classic/views/css/stats.css000066400000000000000000000006261225361755400230170ustar00rootroot00000000000000#contentTable.major .colZone, #contentTable.major .colPixelDiff, #contentTable.major .colAlarmPx, #contentTable.major .colFilterPx, #contentTable.major .colBlobPx, #contentTable.major .colBlobs, #contentTable.major .colBlobSizes, #contentTable.major .colAlarmLimits, #contentTable.major .colScore { text-align: center; } #contentTable.major .rowNoStats { text-align: center; padding: 20px; } ZoneMinder-1.26.5/web/skins/classic/views/css/timeline.css000066400000000000000000000065411225361755400234710ustar00rootroot00000000000000#content { position: relative; text-align: center; border: 1px solid #666666; margin: 0 auto; } #title { position: relative; margin: 0 auto; color: #016A9D; height: 30px; font-size: 13px; font-weight: bold; line-height: 20px; } #listLink { position: absolute; top: 5px; left: 20px; height: 15px; } #closeLink { position: absolute; top: 5px; right: 20px; height: 15px; } #topPanel { position: relative; height: 220px; margin: 4px auto 6px; } #topPanel #imagePanel { width: 50%; float: left; text-align: right; } #topPanel #image { float: right; margin: 0 auto; width: 90%; text-align: right; margin-right: 2px; background-color: #eeeeee; border: 1px solid #c8c8c8; } #topPanel #image img { float: left; top: 0px; background-color: #f8f8f8; width: 100%; } #topPanel #dataPanel { width: 45%; float: left; text-align: left; margin-left: 2px; } #topPanel #textPanel { text-align: left; width: 100%; height: 140px; margin: 0 auto; color: #016A9D; font-size: 11px; font-weight: bold; line-height: 14px; background-color: #eeeeee; border: 1px solid #c8c8c8; padding: 2px; } #topPanel #navPanel { width: 100%; height: 70px; margin: 4px auto; } #topPanel #navPanel input { background-color: #eeeeee; border: 1px solid #c8c8c8; padding: 5px; } #chartPanel { position: relative; margin: 0 auto; } #chartPanel #chart { position: relative; border: 1px solid black; margin: 0 auto; z-index: 0; } #chartPanel #activity { position: absolute; text-align: center; left: 0px; border-bottom: 1px solid #cccccc; } #chartPanel #activity div.activity { position: absolute; bottom: 0px; z-index: 3; width: 1px; } #chartPanel .events { position: absolute; text-align: center; left: 0px; background-color: #fcfcfc; border-bottom: 1px solid black; } #chartPanel .event { position: absolute; bottom: 0px; z-index: 3; } #chartLabels { margin: 25px auto 0; text-align: center; } #range { width: 30%; margin: 0 auto; color: #016A9D; font-size: 11px; font-weight: bold; line-height: 20px; } #key { float: right; margin-top: -4px; text-align: right; } span.keyEntry { } img.keyBox { position: relative; border: 1px solid black; width: 16px; height: 16px; top: 4px; margin-left: 4px; } div.majGridX { position: absolute; z-index: 1; top: 0px; width: 1px; border-left: dotted 1px #cccccc; } div.majTickX { position: absolute; bottom: -7px; width: 1px; height: 7px; border-left: solid 1px black; } div.majLabelX { position: absolute; text-align: center; bottom: -20px; width: 50px; font-size: 9px; font-weight: normal; } div.majGridY { position: absolute; z-index: 1; left: 0px; height: 1px; border-top: dotted 1px #cccccc; } div.majTickY { position: absolute; left: -7px; height: 1px; width: 7px; border-top: solid 1px black; } div.majLabelY { position: absolute; text-align: right; left: -30px; width: 20px; font-size: 9px; font-weight: normal; } div.zoom { position: absolute; z-index: 2; bottom: 0px; } ZoneMinder-1.26.5/web/skins/classic/views/css/timeline.css.php000066400000000000000000000027571225361755400242640ustar00rootroot00000000000000.chartSize { width: px; height: px; } .graphSize { width: px; height: px; } .graphHeight { height: px; } .graphWidth { width: px; } .imageSize { width: px; height: px; } .imageHeight { height: px; } .activitySize { width: px; height: px; } .eventsSize { width: px; height: px; } .eventsHeight { height: px; } #chartPanel .eventsPos { top: px; } #chartPanel .activityPos { top: px; } #chartPanel .eventsPos { top: px; } .monitorColour { background-color: ; } ZoneMinder-1.26.5/web/skins/classic/views/css/video.css000066400000000000000000000003371225361755400227660ustar00rootroot00000000000000#contentTable + input { margin-top: 6px; } #videoProgress { margin: 8px auto 4px; } #videoFilesHeader { margin: 8px auto 4px; } #videoNoFiles { margin: 4px auto; } #videoFile { margin-bottom: 6px; } ZoneMinder-1.26.5/web/skins/classic/views/css/watch.css000066400000000000000000000033521225361755400227660ustar00rootroot00000000000000@import url(../../css/control.css); #menuBar { margin: 6px auto 4px; text-align: center; } #menuBar #monitorName { float: left; } #menuBar #closeControl { float: right; } #menuBar #menuControls { margin: 0 auto; width: 60%; } #menuBar #menuControls #controlControl { float: left; } #menuBar #menuControls #eventsControl { float: left; } #menuBar #menuControls #settingsControl { float: right; } #menuBar #menuControls #scaleControl { margin: 0 auto; } #imageFeed{ text-align: center; } #monitorStatus { margin: 4px auto; text-align: center; } #monitorStatus #enableDisableAlarms { float: left; } #monitorStatus #forceCancelAlarm { float: right; } #monitorStatus #monitorState { } #dvrControls { margin-top: 3px; margin-bottom: 2px; text-align: center; } #dvrControls input { height: 20px; width: 28px; padding-bottom: 3px; margin: 0 3px; } #dvrControls input[disabled] { color: #aaaaaa; } #dvrControls input.active { border: 1px solid blue; } #dvrControls input.inactive { border: 1px solid green; } #dvrControls input.unavail { border: 1px solid red; } #replayStatus { margin: 3px 0 2px; text-align: center; clear: both; } #replayStatus > span { padding: 0 4px; } #events { margin: 0 auto; } #eventList { width: 100%; } #eventList thead td { font-weight: bold; } #eventList th, #eventList td { text-align: center; } li { display: inline; list-style-type: none; } span.alarm { color: #DC143C; font-weight: bold; } span.alert { color: #FF8C00; font-weight: bold; } #eventList tr.recent { background-color: #B0E0E6; } #eventList tr.highlight { background-color: #DCDCDC; } ZoneMinder-1.26.5/web/skins/classic/views/css/zone.css000066400000000000000000000034111225361755400226270ustar00rootroot00000000000000#settingsPanel { float: left; margin: 0 2px; } #zoneSettings { border-collapse: collapse; } #zoneSettings th, #zoneSettings td { border: 1px solid #7f7fb2; padding: 3px; text-align: left; } #zoneSettings th[scope=row] { padding: 4px 3px 3px; vertical-align: top; white-space: nowrap; } #definitionPanel { float: left; margin: 0 2px; text-align: center; } #definitionPanel input[type=submit], #definitionPanel input[type=submit] { margin: 0 4px; } #imagePanel { position: relative; } /* NB: The size of the imageFrame determines the area within which the markers * may be moved. When adjusting the padding and margins of the imageFrame and * the DIVs within it, test that the markers behave sensibly when dragged to * the extreme edges of the imageFrame. */ #imageFrame { position: relative; padding: 0 3px 3px 0; /* Compensate for negative margins in the markers just below. */ } #imageFrame div { position: absolute; width: 7px; height: 7px; margin-left: -3px; margin-top: -3px; z-index: 5; } #imageFrame div { background-image: url(../../graphics/point-g.gif); } #imageFrame div.highlight { background-image: url(../../graphics/point-o.gif); } #imageFrame div.active { background-image: url(../../graphics/point-r.gif); } #zonePoints { margin: 8px 0; border-collapse: collapse; } #zonePoints td { vertical-align: top; } #zonePoints table { border-collapse: collapse; } #zonePoints table tr.highlight { background-color: #f0e68c; } #zonePoints table tr.active { background-color: #ffa07a; } #zonePoints table th, #zonePoints table td { border: 1px solid #7f7fb2; padding: 3px; text-align: center; } #zonePoints table a { margin: 0 2px; } ZoneMinder-1.26.5/web/skins/classic/views/cycle.php000066400000000000000000000107171225361755400221710ustar00rootroot00000000000000
ZoneMinder-1.26.5/web/skins/classic/views/device.php000066400000000000000000000047541225361755400223350ustar00rootroot00000000000000 "", "Name" => "New Device", "KeyString" => "" ); } xhtmlHeaders( __FILE__, $SLANG['Device']." - ".$newDevice['Name'] ); ?>
disabled="disabled"/>
ZoneMinder-1.26.5/web/skins/classic/views/devices.php000066400000000000000000000066211225361755400225130ustar00rootroot00000000000000
'.validHtmlStr($device['Name']).' ('.validHtmlStr($device['KeyString']).')', canEdit( 'Devices' ) ) ?> onclick="switchDeviceOn( this, '' )"/> onclick="switchDeviceOff( this, '' )"/> disabled="disabled"/>
/>
ZoneMinder-1.26.5/web/skins/classic/views/donate.php000066400000000000000000000040671225361755400223450ustar00rootroot00000000000000 $SLANG['DonateYes'], "hour" => $SLANG['DonateRemindHour'], "day" => $SLANG['DonateRemindDay'], "week" => $SLANG['DonateRemindWeek'], "month" => $SLANG['DonateRemindMonth'], "never" => $SLANG['DonateRemindNever'], "already" => $SLANG['DonateAlready'], ); $focusWindow = true; xhtmlHeaders(__FILE__, $SLANG['Donate'] ); ?>

ZoneMinder-1.26.5/web/skins/classic/views/error.php000066400000000000000000000023651225361755400222230ustar00rootroot00000000000000

ZoneMinder-1.26.5/web/skins/classic/views/event.php000066400000000000000000000246551225361755400222210ustar00rootroot00000000000000 $SLANG['ReplaySingle'], 'all' => $SLANG['ReplayAll'], 'gapless' => $SLANG['ReplayGapless'], ); if ( isset( $_REQUEST['streamMode'] ) ) $streamMode = validHtmlStr($_REQUEST['streamMode']); else $streamMode = canStream()?'stream':'stills'; if ( isset( $_REQUEST['replayMode'] ) ) $replayMode = validHtmlStr($_REQUEST['replayMode']); if ( isset( $_COOKIE['replayMode']) && preg_match('#^[a-z]+$#', $_COOKIE['replayMode']) ) $replayMode = validHtmlStr($_COOKIE['replayMode']); else { $keys = array_keys( $replayModes ); $replayMode = array_shift( $keys ); } parseSort(); parseFilter( $_REQUEST['filter'] ); $filterQuery = $_REQUEST['filter']['query']; $panelSections = 40; $panelSectionWidth = (int)ceil(reScale($event['Width'],$scale)/$panelSections); $panelWidth = ($panelSections*$panelSectionWidth-1); $connkey = generateConnKey(); $focusWindow = true; xhtmlHeaders(__FILE__, $SLANG['Event'] ); ?>
s ">/ ">//

Mode:   Rate: x Progress: s Zoom: x
ZoneMinder-1.26.5/web/skins/classic/views/eventdetail.php000066400000000000000000000073251225361755400233770ustar00rootroot00000000000000
disabled="disabled"/>
ZoneMinder-1.26.5/web/skins/classic/views/events.php000066400000000000000000000267111225361755400223770ustar00rootroot00000000000000 $limit ) { $nEvents = $limit; } $pages = (int)ceil($nEvents/ZM_WEB_EVENTS_PER_PAGE); if ( $pages > 1 ) { if ( !empty($page) ) { if ( $page < 0 ) $page = 1; if ( $page > $pages ) $page = $pages; } } if ( !empty($page) ) { $limitStart = (($page-1)*ZM_WEB_EVENTS_PER_PAGE); if ( empty( $limit ) ) { $limitAmount = ZM_WEB_EVENTS_PER_PAGE; } else { $limitLeft = $limit - $limitStart; $limitAmount = ($limitLeft>ZM_WEB_EVENTS_PER_PAGE)?ZM_WEB_EVENTS_PER_PAGE:$limitLeft; } $eventsSql .= " limit $limitStart, $limitAmount"; } elseif ( !empty( $limit ) ) { $eventsSql .= " limit 0, ".dbEscape($limit); } $maxWidth = 0; $maxHeight = 0; $archived = false; $unarchived = false; $events = array(); foreach ( dbFetchAll( $eventsSql ) as $event ) { $events[] = $event; $scale = max( reScale( SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE ), SCALE_BASE ); $eventWidth = reScale( $event['Width'], $scale ); $eventHeight = reScale( $event['Height'], $scale ); if ( $maxWidth < $eventWidth ) $maxWidth = $eventWidth; if ( $maxHeight < $eventHeight ) $maxHeight = $eventHeight; if ( $event['Archived'] ) $archived = true; else $unarchived = true; } $maxShortcuts = 5; $pagination = getPagination( $pages, $page, $maxShortcuts, $filterQuery.$sortQuery.'&limit='.$limit ); $focusWindow = true; xhtmlHeaders(__FILE__, $SLANG['Events'] ); ?>

disabled="disabled"/>
' ) ?>   disabled="disabled"/>

ZoneMinder-1.26.5/web/skins/classic/views/export.php000066400000000000000000000130111225361755400224010ustar00rootroot00000000000000
checked="checked" onclick="configureExportButton( this )"/>
checked="checked" onclick="configureExportButton( this )"/>
checked="checked" onclick="configureExportButton( this )"/>
checked="checked" onclick="configureExportButton( this )"/>
checked="checked" onclick="configureExportButton( this )"/>
checked="checked" onclick="configureExportButton( this )"/> checked="checked" onclick="configureExportButton( this )"/>

ZoneMinder-1.26.5/web/skins/classic/views/filter.php000066400000000000000000000330641225361755400223570ustar00rootroot00000000000000$SLANG['ChooseFilter'] ); foreach ( dbFetchAll( "select * from Filters order by Name" ) as $row ) { $filterNames[$row['Name']] = $row['Name']; if ( $row['Background'] ) $filterNames[$row['Name']] .= "*"; if ( !empty($_REQUEST['reload']) && isset($_REQUEST['filterName']) && $_REQUEST['filterName'] == $row['Name'] ) $dbFilter = $row; } $backgroundStr = ""; if ( isset($dbFilter) ) { if ( $dbFilter['Background'] ) $backgroundStr = '['.strtolower($SLANG['Background']).']'; $_REQUEST['filter'] = jsonDecode( $dbFilter['Query'] ); $_REQUEST['sort_field'] = isset($_REQUEST['filter']['sort_field'])?$_REQUEST['filter']['sort_field']:"DateTime"; $_REQUEST['sort_asc'] = isset($_REQUEST['filter']['sort_asc'])?$_REQUEST['filter']['sort_asc']:"1"; $_REQUEST['limit'] = isset($_REQUEST['filter']['limit'])?$_REQUEST['filter']['limit']:""; unset( $_REQUEST['filter']['sort_field'] ); unset( $_REQUEST['filter']['sort_asc'] ); unset( $_REQUEST['filter']['limit'] ); } $conjunctionTypes = array( 'and' => $SLANG['ConjAnd'], 'or' => $SLANG['ConjOr'] ); $obracketTypes = array(); $cbracketTypes = array(); if ( isset($_REQUEST['filter']['terms']) ) { for ( $i = 0; $i <= count($_REQUEST['filter']['terms'])-2; $i++ ) { $obracketTypes[$i] = str_repeat( "(", $i ); $cbracketTypes[$i] = str_repeat( ")", $i ); } } $attrTypes = array( 'MonitorId' => $SLANG['AttrMonitorId'], 'MonitorName' => $SLANG['AttrMonitorName'], 'Id' => $SLANG['AttrId'], 'Name' => $SLANG['AttrName'], 'Cause' => $SLANG['AttrCause'], 'Notes' => $SLANG['AttrNotes'], 'DateTime' => $SLANG['AttrDateTime'], 'Date' => $SLANG['AttrDate'], 'Time' => $SLANG['AttrTime'], 'Weekday' => $SLANG['AttrWeekday'], 'Length' => $SLANG['AttrDuration'], 'Frames' => $SLANG['AttrFrames'], 'AlarmFrames' => $SLANG['AttrAlarmFrames'], 'TotScore' => $SLANG['AttrTotalScore'], 'AvgScore' => $SLANG['AttrAvgScore'], 'MaxScore' => $SLANG['AttrMaxScore'], 'Archived' => $SLANG['AttrArchiveStatus'], 'DiskPercent' => $SLANG['AttrDiskPercent'], 'DiskBlocks' => $SLANG['AttrDiskBlocks'], 'SystemLoad' => $SLANG['AttrSystemLoad'], ); $opTypes = array( '=' => $SLANG['OpEq'], '!=' => $SLANG['OpNe'], '>=' => $SLANG['OpGtEq'], '>' => $SLANG['OpGt'], '<' => $SLANG['OpLt'], '<=' => $SLANG['OpLtEq'], '=~' => $SLANG['OpMatches'], '!~' => $SLANG['OpNotMatches'], '=[]' => $SLANG['OpIn'], '![]' => $SLANG['OpNotIn'], ); $archiveTypes = array( '0' => $SLANG['ArchUnarchived'], '1' => $SLANG['ArchArchived'] ); $weekdays = array(); for ( $i = 0; $i < 7; $i++ ) { $weekdays[$i] = strftime( "%A", mktime( 12, 0, 0, 1, $i+1, 2001 ) ); } $sort_fields = array( 'Id' => $SLANG['AttrId'], 'Name' => $SLANG['AttrName'], 'Cause' => $SLANG['AttrCause'], 'Notes' => $SLANG['AttrNotes'], 'MonitorName' => $SLANG['AttrMonitorName'], 'DateTime' => $SLANG['AttrDateTime'], 'Length' => $SLANG['AttrDuration'], 'Frames' => $SLANG['AttrFrames'], 'AlarmFrames' => $SLANG['AttrAlarmFrames'], 'TotScore' => $SLANG['AttrTotalScore'], 'AvgScore' => $SLANG['AttrAvgScore'], 'MaxScore' => $SLANG['AttrMaxScore'], ); $sort_dirns = array( '1' => $SLANG['SortAsc'], '0' => $SLANG['SortDesc'] ); if ( empty($_REQUEST['sort_field']) ) { $_REQUEST['sort_field'] = ZM_WEB_EVENT_SORT_FIELD; $_REQUEST['sort_asc'] = (ZM_WEB_EVENT_SORT_ORDER == "asc"); } $hasCal = file_exists( 'tools/jscalendar/calendar.js' ); $focusWindow = true; xhtmlHeaders(__FILE__, $SLANG['EventFilter'] ); ?>

1 ) { echo buildSelect( $selectName, $filterNames, "submitToFilter( this, 1 );" ); } else { ?>

  2 ) { echo buildSelect( "filter[terms][$i][obr]", $obracketTypes ); } else { ?>  2 ) { echo buildSelect( "filter[terms][$i][cbr]", $cbracketTypes ); } else { ?>  1 ) { ?>

"/>

checked="checked" onclick="updateButtons( this )"/>
checked="checked" onclick="updateButtons( this )"/>
checked="checked" onclick="updateButtons( this )"/>
checked="checked" onclick="updateButtons( this )"/>
checked="checked" onclick="updateButtons( this )"/>
checked="checked"/>" size="32" maxlength="255" onchange="updateButtons( this )"/>
checked="checked" onclick="updateButtons( this )"/>

ZoneMinder-1.26.5/web/skins/classic/views/filtersave.php000066400000000000000000000074311225361755400232350ustar00rootroot00000000000000

checked="checked"/>

disabled="disabled"/>
ZoneMinder-1.26.5/web/skins/classic/views/frame.php000066400000000000000000000122071225361755400221600ustar00rootroot00000000000000$fid, 'Type'=>'Normal', 'Score'=>0 ); } else { $frame = dbFetchOne( "select * from Frames where EventId = '".dbEscape($eid)."' and Score = '".$event['MaxScore']."'" ); } $maxFid = $event['Frames']; $firstFid = 1; $prevFid = $frame['FrameId']-1; $nextFid = $frame['FrameId']+1; $lastFid = $maxFid; $alarmFrame = $frame['Type']=='Alarm'; if ( isset( $_REQUEST['scale'] ) ) $scale = validInt($_REQUEST['scale']); else $scale = max( reScale( SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE ), SCALE_BASE ); $imageData = getImageSrc( $event, $frame, $scale, (isset($_REQUEST['show']) && $_REQUEST['show']=="capt") ); $imagePath = $imageData['thumbPath']; $eventPath = $imageData['eventPath']; $dImagePath = sprintf( "%s/%0".ZM_EVENT_IMAGE_DIGITS."d-diag-d.jpg", $eventPath, $frame['FrameId'] ); $rImagePath = sprintf( "%s/%0".ZM_EVENT_IMAGE_DIGITS."d-diag-r.jpg", $eventPath, $frame['FrameId'] ); $focusWindow = true; xhtmlHeaders(__FILE__, $SLANG['Frame']." - ".$event['Id']." - ".$frame['FrameId'] ); ?>

"><?= $frame['EventId']." class=""/>

1 ) { ?> 1 ) { ?>

" width="" height="" class=""/>

ZoneMinder-1.26.5/web/skins/classic/views/frames.php000066400000000000000000000066611225361755400223520ustar00rootroot00000000000000
ZoneMinder-1.26.5/web/skins/classic/views/function.php000066400000000000000000000043671225361755400227230ustar00rootroot00000000000000

checked="checked"/>

ZoneMinder-1.26.5/web/skins/classic/views/group.php000066400000000000000000000056411225361755400222260ustar00rootroot00000000000000 "", "Name" => "New Group", "MonitorIds" => "" ); } xhtmlHeaders( __FILE__, $SLANG['Group']." - ".$newGroup['Name'] ); ?>
disabled="disabled"/>
ZoneMinder-1.26.5/web/skins/classic/views/groups.php000066400000000000000000000066531225361755400224150ustar00rootroot00000000000000
onclick="configureButtons( this );"/>
onclick="configureButtons( this );"/>
/> /> />
ZoneMinder-1.26.5/web/skins/classic/views/js/000077500000000000000000000000001225361755400207675ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/classic/views/js/Makefile.am000066400000000000000000000013601225361755400230230ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/skins/classic/views/js dist_web_DATA = \ console.js \ console.js.php \ control.js \ controlpreset.js \ controlpreset.js.php \ cycle.js \ cycle.js.php \ devices.js \ donate.js \ donate.js.php \ event.js \ event.js.php \ events.js \ events.js.php \ export.js \ export.js.php \ filter.js \ filter.js.php \ group.js \ groups.js \ log.js \ login.js \ Makefile.am \ monitor.js \ monitor.js.php \ monitorpreset.js \ monitorprobe.js \ montage.js \ montage.js.php \ options.js.php \ postlogin.js \ state.js \ state.js.php \ timeline.js \ timeline.js.php \ user.js \ version.js \ version.js.php \ video.js \ video.js.php \ watch.js \ watch.js.php \ zone.js \ zone.js.php ZoneMinder-1.26.5/web/skins/classic/views/js/console.js000066400000000000000000000034401225361755400227700ustar00rootroot00000000000000function setButtonStates( element ) { var form = element.form; var checked = 0; for ( var i = 0; i < form.elements.length; i++ ) { if ( form.elements[i].type == "checkbox" ) { if ( form.elements[i].checked ) { if ( checked++ > 1 ) break; } } } $(element).getParent( 'tr' ).toggleClass( 'highlight' ); form.editBtn.disabled = (checked!=1); form.deleteBtn.disabled = (checked==0); } function editMonitor( element ) { var form = element.form; for ( var i = 0; i < form.elements.length; i++ ) { if ( form.elements[i].type == "checkbox" ) { if ( form.elements[i].checked ) { var monitorId = form.elements[i].value; createPopup( '?view=monitor&mid='+monitorId, 'zmMonitor'+monitorId, 'monitor' ); form.elements[i].checked = false; setButtonStates( form.elements[i] ); //$(form.elements[i]).getParent( 'tr' ).removeClass( 'highlight' ); break; } } } } function deleteMonitor( element ) { if ( confirm( 'Warning, deleting a monitor also deletes all events and database entries associated with it.\nAre you sure you wish to delete?' ) ) { var form = element.form; form.elements['action'].value = 'delete'; form.submit(); } } function reloadWindow() { window.location.replace( thisUrl ); } function initPage() { reloadWindow.periodical( consoleRefreshTimeout ); if ( showVersionPopup ) createPopup( '?view=version', 'zmVersion', 'version' ); if ( showDonatePopup ) createPopup( '?view=donate', 'zmDonate', 'donate' ); } window.addEvent( 'domready', initPage ); ZoneMinder-1.26.5/web/skins/classic/views/js/console.js.php000066400000000000000000000016671225361755400235670ustar00rootroot00000000000000var consoleRefreshTimeout = ; 0 ) { if ( ZM_DYN_DONATE_REMINDER_TIME < time() ) { $showDonatePopup = true; } } else { $nextReminder = time() + 30*24*60*60; dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_DONATE_REMINDER_TIME'" ); } } } ?> var showVersionPopup = ; var showDonatePopup = ; ZoneMinder-1.26.5/web/skins/classic/views/js/control.js000066400000000000000000000026611225361755400230120ustar00rootroot00000000000000var controlParms = "view=request&request=control"; var controlReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, onSuccess: getControlResponse } ); function getControlResponse( respObj, respText ) { if ( !respObj ) return; //console.log( respText ); if ( respObj.result != 'Ok' ) { alert( "Control response was status = "+respObj.status+"\nmessage = "+respObj.message ); } } function controlCmd( control, event, xtell, ytell ) { var locParms = "&id="+$('mid').get('value'); if ( event && (xtell || ytell) ) { var xEvent = new Event( event ); var target = xEvent.target; var coords = $(target).getCoordinates(); var l = coords.left; var t = coords.top; var x = xEvent.page.x - l; var y = xEvent.page.y - t; if ( xtell ) { var xge = parseInt( (x*100)/coords.width ); if ( xtell == -1 ) xge = 100 - xge; else if ( xtell == 2 ) xge = 2*(50 - xge); locParms += "&xge="+xge; } if ( ytell ) { var yge = parseInt( (y*100)/coords.height ); if ( ytell == -1 ) yge = 100 - yge; else if ( ytell == 2 ) yge = 2*(50 - yge); locParms += "&yge="+yge; } } controlReq.send( controlParms+"&control="+control+locParms ); } ZoneMinder-1.26.5/web/skins/classic/views/js/controlpreset.js000066400000000000000000000004571225361755400242360ustar00rootroot00000000000000function updateLabel() { var presetIndex = $('contentForm').preset.getValue(); if ( labels[presetIndex] ) { $('contentForm').newLabel.value = labels[presetIndex]; } else { $('contentForm').newLabel.value = ""; } } window.addEvent( 'domready', updateLabel ); ZoneMinder-1.26.5/web/skins/classic/views/js/controlpreset.js.php000066400000000000000000000002251225361755400250150ustar00rootroot00000000000000var labels = new Array(); $label ) { ?> labels[] = ""; ZoneMinder-1.26.5/web/skins/classic/views/js/cycle.js000066400000000000000000000004111225361755400224200ustar00rootroot00000000000000function nextCycleView() { window.location.replace( '?view=cycle&group='+currGroup+'&mid='+nextMid+'&mode='+mode, cycleRefreshTimeout ); } function initCycle() { nextCycleView.periodical( cycleRefreshTimeout ); } window.addEvent( 'domready', initCycle ); ZoneMinder-1.26.5/web/skins/classic/views/js/cycle.js.php000066400000000000000000000003421225361755400232110ustar00rootroot00000000000000var currGroup = ""; var nextMid = ""; var mode = ""; var cycleRefreshTimeout = ; ZoneMinder-1.26.5/web/skins/classic/views/js/devices.js000066400000000000000000000017721225361755400227560ustar00rootroot00000000000000function switchDeviceOn( element, key ) { var form = element.form; form.view.value = currentView; form.action.value = 'device'; form.command.value = 'on'; form.key.value = key; form.submit(); } function switchDeviceOff( element, key ) { var form = element.form; form.view.value = currentView; form.action.value = 'device'; form.command.value = 'off'; form.key.value = key; form.submit(); } function deleteDevice( element ) { var form = element.form; form.view.value = currentView; form.action.value = 'delete'; form.submit(); } function configureButtons( element, name ) { var form = element.form; var checked = false; for (var i = 0; i < form.elements.length; i++) { if ( form.elements[i].name.indexOf(name) == 0) { if ( form.elements[i].checked ) { checked = true; break; } } } form.deleteBtn.disabled = !checked; } window.focus(); ZoneMinder-1.26.5/web/skins/classic/views/js/donate.js000066400000000000000000000004201225361755400225730ustar00rootroot00000000000000function submitForm( element ) { var form = element.form; if ( form.option.selectedIndex == 0 ) form.view.value = currentView; else form.view.value = 'none'; form.submit(); } if ( action == "donate" && option == "go" ) { zmWindow(); } ZoneMinder-1.26.5/web/skins/classic/views/js/donate.js.php000066400000000000000000000002521225361755400233640ustar00rootroot00000000000000var action = ''; var option = ''; ZoneMinder-1.26.5/web/skins/classic/views/js/event.js000066400000000000000000000541521225361755400224550ustar00rootroot00000000000000function setButtonState( element, butClass ) { element.className = butClass; element.disabled = (butClass != 'inactive'); } function changeScale() { var scale = $('scale').get('value'); var baseWidth = eventData.Width; var baseHeight = eventData.Height; var newWidth = ( baseWidth * scale ) / SCALE_BASE; var newHeight = ( baseHeight * scale ) / SCALE_BASE; streamScale( scale ); /*Stream could be an applet so can't use moo tools*/ var streamImg = document.getElementById('evtStream'); streamImg.style.width = newWidth + "px"; streamImg.style.height = newHeight + "px"; } function changeReplayMode() { var replayMode = $('replayMode').get('value'); Cookie.write( 'replayMode', replayMode, { duration: 10*365 }) refreshWindow(); } var streamParms = "view=request&request=stream&connkey="+connKey; var streamCmdTimer = null; var streamStatus = null; var lastEventId = 0; function getCmdResponse( respObj, respText ) { if ( checkStreamForErrors( "getCmdResponse" ,respObj ) ) return; if ( streamCmdTimer ) streamCmdTimer = clearTimeout( streamCmdTimer ); streamStatus = respObj.status; var eventId = streamStatus.event; if ( eventId != lastEventId ) { eventQuery( eventId ); lastEventId = eventId; } if ( streamStatus.paused == true ) { $('modeValue').set( 'text', "Paused" ); $('rate').addClass( 'hidden' ); streamPause( false ); } else { $('modeValue').set( 'text', "Replay" ); $('rateValue').set( 'text', streamStatus.rate ); $('rate').removeClass( 'hidden' ); streamPlay( false ); } $('progressValue').set( 'text', secsToTime( parseInt(streamStatus.progress) ) ); $('zoomValue').set( 'text', streamStatus.zoom ); if ( streamStatus.zoom == "1.0" ) setButtonState( $('zoomOutBtn'), 'unavail' ); else setButtonState( $('zoomOutBtn'), 'inactive' ); updateProgressBar(); streamCmdTimer = streamQuery.delay( streamTimeout ); } var streamReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getCmdResponse } ); function streamPause( action ) { setButtonState( $('pauseBtn'), 'active' ); setButtonState( $('playBtn'), 'inactive' ); setButtonState( $('fastFwdBtn'), 'unavail' ); setButtonState( $('slowFwdBtn'), 'inactive' ); setButtonState( $('slowRevBtn'), 'inactive' ); setButtonState( $('fastRevBtn'), 'unavail' ); if ( action ) streamReq.send( streamParms+"&command="+CMD_PAUSE ); } function streamPlay( action ) { setButtonState( $('pauseBtn'), 'inactive' ); if (streamStatus) setButtonState( $('playBtn'), streamStatus.rate==1?'active':'inactive' ); setButtonState( $('fastFwdBtn'), 'inactive' ); setButtonState( $('slowFwdBtn'), 'unavail' ); setButtonState( $('slowRevBtn'), 'unavail' ); setButtonState( $('fastRevBtn'), 'inactive' ); if ( action ) { streamReq.send( streamParms+"&command="+CMD_PLAY ); } } function streamFastFwd( action ) { setButtonState( $('pauseBtn'), 'inactive' ); setButtonState( $('playBtn'), 'inactive' ); setButtonState( $('fastFwdBtn'), 'inactive' ); setButtonState( $('slowFwdBtn'), 'unavail' ); setButtonState( $('slowRevBtn'), 'unavail' ); setButtonState( $('fastRevBtn'), 'inactive' ); if ( action ) streamReq.send( streamParms+"&command="+CMD_FASTFWD ); } function streamSlowFwd( action ) { setButtonState( $('pauseBtn'), 'inactive' ); setButtonState( $('playBtn'), 'inactive' ); setButtonState( $('fastFwdBtn'), 'unavail' ); setButtonState( $('slowFwdBtn'), 'active' ); setButtonState( $('slowRevBtn'), 'inactive' ); setButtonState( $('fastRevBtn'), 'unavail' ); if ( action ) streamReq.send( streamParms+"&command="+CMD_SLOWFWD ); setButtonState( $('pauseBtn'), 'active' ); setButtonState( $('slowFwdBtn'), 'inactive' ); } function streamSlowRev( action ) { setButtonState( $('pauseBtn'), 'inactive' ); setButtonState( $('playBtn'), 'inactive' ); setButtonState( $('fastFwdBtn'), 'unavail' ); setButtonState( $('slowFwdBtn'), 'inactive' ); setButtonState( $('slowRevBtn'), 'active' ); setButtonState( $('fastRevBtn'), 'unavail' ); if ( action ) streamReq.send( streamParms+"&command="+CMD_SLOWREV ); setButtonState( $('pauseBtn'), 'active' ); setButtonState( $('slowRevBtn'), 'inactive' ); } function streamFastRev( action ) { setButtonState( $('pauseBtn'), 'inactive' ); setButtonState( $('playBtn'), 'inactive' ); setButtonState( $('fastFwdBtn'), 'inactive' ); setButtonState( $('slowFwdBtn'), 'unavail' ); setButtonState( $('slowRevBtn'), 'unavail' ); setButtonState( $('fastRevBtn'), 'inactive' ); if ( action ) streamReq.send( streamParms+"&command="+CMD_FASTREV ); } function streamPrev( action ) { streamPlay( false ); if ( action ) streamReq.send( streamParms+"&command="+CMD_PREV ); } function streamNext( action ) { streamPlay( false ); if ( action ) streamReq.send( streamParms+"&command="+CMD_NEXT ); } function streamZoomIn( x, y ) { streamReq.send( streamParms+"&command="+CMD_ZOOMIN+"&x="+x+"&y="+y ); } function streamZoomOut() { streamReq.send( streamParms+"&command="+CMD_ZOOMOUT ); } function streamScale( scale ) { streamReq.send( streamParms+"&command="+CMD_SCALE+"&scale="+scale ); } function streamPan( x, y ) { streamReq.send( streamParms+"&command="+CMD_PAN+"&x="+x+"&y="+y ); } function streamSeek( offset ) { streamReq.send( streamParms+"&command="+CMD_SEEK+"&offset="+offset ); } function streamQuery() { streamReq.send( streamParms+"&command="+CMD_QUERY ); } var slider = null; var scroll = null; function getEventResponse( respObj, respText ) { if ( checkStreamForErrors( "getEventResponse", respObj ) ) return; eventData = respObj.event; if ( !$('eventStills').hasClass( 'hidden' ) && currEventId != eventData.Id ) resetEventStills(); currEventId = eventData.Id; $('dataId').set( 'text', eventData.Id ); if ( eventData.Notes ) { $('dataCause').setProperty( 'title', eventData.Notes ); } else { $('dataCause').setProperty( 'title', causeString ); } $('dataCause').set( 'text', eventData.Cause ); $('dataTime').set( 'text', eventData.StartTime ); $('dataDuration').set( 'text', eventData.Length ); $('dataFrames').set( 'text', eventData.Frames+"/"+eventData.AlarmFrames ); $('dataScore').set( 'text', eventData.TotScore+"/"+eventData.AvgScore+"/"+eventData.MaxScore ); $('eventName').setProperty( 'value', eventData.Name ); if ( canEditEvents ) { if ( parseInt(eventData.Archived) ) { $('archiveEvent').addClass( 'hidden' ); $('unarchiveEvent').removeClass( 'hidden' ); } else { $('archiveEvent').removeClass( 'hidden' ); $('unarchiveEvent').addClass( 'hidden' ); } } //var eventImg = $('eventImage'); //eventImg.setStyles( { 'width': eventData.width, 'height': eventData.height } ); drawProgressBar(); nearEventsQuery( eventData.Id ); } var eventReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getEventResponse } ); function eventQuery( eventId ) { var eventParms = "view=request&request=status&entity=event&id="+eventId; eventReq.send( eventParms ); } var prevEventId = 0; var nextEventId = 0; function getNearEventsResponse( respObj, respText ) { if ( checkStreamForErrors( "getNearEventsResponse", respObj ) ) return; prevEventId = respObj.nearevents.PrevEventId; nextEventId = respObj.nearevents.NextEventId; $('prevEventBtn').disabled = !prevEventId; $('nextEventBtn').disabled = !nextEventId; } var nearEventsReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getNearEventsResponse } ); function nearEventsQuery( eventId ) { var parms = "view=request&request=status&entity=nearevents&id="+eventId; nearEventsReq.send( parms ); } var frameBatch = 40; function loadEventThumb( event, frame, loadImage ) { var thumbImg = $('eventThumb'+frame.FrameId); if ( !thumbImg ) { console.error( "No holder found for frame "+frame.FrameId ); return; } var img = new Asset.image( imagePrefix+frame.Image.imagePath, { 'onload': ( function( loadImage ) { thumbImg.setProperty( 'src', img.getProperty( 'src' ) ); thumbImg.removeClass( 'placeholder' ); thumbImg.setProperty( 'class', frame.Type=='Alarm'?'alarm':'normal' ); thumbImg.setProperty( 'title', frame.FrameId+' / '+((frame.Type=='Alarm')?frame.Score:0) ); thumbImg.removeEvents( 'click' ); thumbImg.addEvent( 'click', function() { locateImage( frame.FrameId, true ); } ); if ( loadImage ) loadEventImage( event, frame ); } ).pass( loadImage ) } ); } function updateStillsSizes( noDelay ) { var containerDim = $('eventThumbs').getSize(); var containerWidth = containerDim.x; var containerHeight = containerDim.y; var popupWidth = parseInt($('eventImage').getStyle( 'width' )); var popupHeight = parseInt($('eventImage').getStyle( 'height' )); var left = (containerWidth - popupWidth)/2; if ( left < 0 ) left = 0; var top = (containerHeight - popupHeight)/2; if ( top < 0 ) top = 0; if ( popupHeight == 0 && !noDelay ) // image not yet loaded lets give it another second { updateStillsSizes.pass( true ).delay( 50 ); return; } $('eventImagePanel').setStyles( { 'left': left, 'top': top } ); } function loadEventImage( event, frame ) { console.debug( "Loading "+event.Id+"/"+frame.FrameId ); var eventImg = $('eventImage'); var thumbImg = $('eventThumb'+frame.FrameId); if ( eventImg.getProperty( 'src' ) != thumbImg.getProperty( 'src' ) ) { var eventImagePanel = $('eventImagePanel'); if ( eventImagePanel.getStyle( 'display' ) != 'none' ) { var lastThumbImg = $('eventThumb'+eventImg.getProperty( 'alt' )); lastThumbImg.removeClass('selected'); lastThumbImg.setOpacity( 1.0 ); } eventImg.setProperties( { 'class': frame.Type=='Alarm'?'alarm':'normal', 'src': thumbImg.getProperty( 'src' ), 'title': thumbImg.getProperty( 'title' ), 'alt': thumbImg.getProperty( 'alt' ), 'width': event.Width, 'height': event.Height } ); $('eventImageBar').setStyle( 'width', event.Width ); if ( frame.Type=='Alarm' ) $('eventImageStats').removeClass( 'hidden' ); else $('eventImageStats').addClass( 'hidden' ); thumbImg.addClass( 'selected' ); thumbImg.setOpacity( 0.5 ); if ( eventImagePanel.getStyle( 'display' ) == 'none' ) { eventImagePanel.setOpacity( 0 ); updateStillsSizes(); eventImagePanel.setStyle( 'display', 'block' ); new Fx.Tween( eventImagePanel, { duration: 500, transition: Fx.Transitions.Sine } ).start( 'opacity', 0, 1 ); } $('eventImageNo').set( 'text', frame.FrameId ); $('prevImageBtn').disabled = (frame.FrameId==1); $('nextImageBtn').disabled = (frame.FrameId==event.Frames); } } function hideEventImageComplete() { var eventImg = $('eventImage'); var thumbImg = $('eventThumb'+$('eventImage').getProperty( 'alt' )); thumbImg.removeClass('selected'); thumbImg.setOpacity( 1.0 ); $('prevImageBtn').disabled = true; $('nextImageBtn').disabled = true; $('eventImagePanel').setStyle( 'display', 'none' ); $('eventImageStats').addClass( 'hidden' ); } function hideEventImage() { if ( $('eventImagePanel').getStyle( 'display' ) != 'none' ) new Fx.Tween( $('eventImagePanel'), { duration: 500, transition: Fx.Transitions.Sine, onComplete: hideEventImageComplete } ).start( 'opacity', 1, 0 ); } function resetEventStills() { hideEventImage(); $('eventThumbs').empty(); if ( true || !slider ) { slider = new Slider( $('thumbsSlider'), $('thumbsKnob'), { /*steps: eventData.Frames,*/ onChange: function( step ) { if ( !step ) step = 0; var fid = parseInt((step * eventData.Frames)/this.options.steps); if ( fid < 1 ) fid = 1; else if ( fid > eventData.Frames ) fid = eventData.Frames; checkFrames( eventData.Id, fid ); scroll.toElement( 'eventThumb'+fid ); } } ).set( 0 ); } if ( $('eventThumbs').getStyle( 'height' ).match( /^\d+/ ) < (parseInt(eventData.Height)+80) ) $('eventThumbs').setStyle( 'height', (parseInt(eventData.Height)+80)+'px' ); } function getFrameResponse( respObj, respText ) { if ( checkStreamForErrors( "getFrameResponse", respObj ) ) return; var frame = respObj.frameimage; if ( !eventData ) { console.error( "No event "+frame.EventId+" found" ); return; } if ( !eventData['frames'] ) eventData['frames'] = new Object(); eventData['frames'][frame.FrameId] = frame; loadEventThumb( eventData, frame, respObj.loopback=="true" ); } var frameReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getFrameResponse } ); function frameQuery( eventId, frameId, loadImage ) { var parms = "view=request&request=status&entity=frameimage&id[0]="+eventId+"&id[1]="+frameId+"&loopback="+loadImage; frameReq.send( parms ); } var currFrameId = null; function checkFrames( eventId, frameId, loadImage ) { if ( !eventData ) { console.error( "No event "+eventId+" found" ); return; } if ( !eventData['frames'] ) eventData['frames'] = new Object(); currFrameId = frameId; var loFid = frameId - frameBatch/2; if ( loFid < 1 ) loFid = 1; var hiFid = loFid + (frameBatch-1); if ( hiFid > eventData.Frames ) hiFid = eventData.Frames; for ( var fid = loFid; fid <= hiFid; fid++ ) { if ( !$('eventThumb'+fid) ) { var img = new Element( 'img', { 'id': 'eventThumb'+fid, 'src': 'graphics/transparent.gif', 'alt': fid, 'class': 'placeholder' } ); img.addEvent( 'click', function () { eventData['frames'][fid] = null; checkFrames( eventId, fid ) } ); frameQuery( eventId, fid, loadImage && (fid == frameId) ); var imgs = $('eventThumbs').getElements( 'img' ); var injected = false; if ( fid < imgs.length ) { img.inject( imgs[fid-1], 'before' ); injected = true; } else { injected = imgs.some( function( thumbImg, index ) { if ( parseInt(img.getProperty( 'alt' )) < parseInt(thumbImg.getProperty( 'alt' )) ) { img.inject( thumbImg, 'before' ); return( true ); } return( false ); } ); } if ( !injected ) { img.inject( $('eventThumbs') ); } var scale = parseInt(img.getStyle('height')); img.setStyles( { 'width': parseInt((eventData.Width*scale)/100), 'height': parseInt((eventData.Height*scale)/100) } ); } else if ( eventData['frames'][fid] ) { if ( loadImage && (fid == frameId) ) { loadEventImage( eventData, eventData['frames'][fid], loadImage ); } } } $('prevThumbsBtn').disabled = (frameId==1); $('nextThumbsBtn').disabled = (frameId==eventData.Frames); } function locateImage( frameId, loadImage ) { if ( slider ) slider.fireEvent( 'tick', slider.toPosition( parseInt((frameId-1)*slider.options.steps/eventData.Frames) )); checkFrames( eventData.Id, frameId, loadImage ); scroll.toElement( 'eventThumb'+frameId ); } function prevImage() { if ( currFrameId > 1 ) locateImage( parseInt(currFrameId)-1, true ); } function nextImage() { if ( currFrameId < eventData.Frames ) locateImage( parseInt(currFrameId)+1, true ); } function prevThumbs() { if ( currFrameId > 1 ) locateImage( parseInt(currFrameId)>10?(parseInt(currFrameId)-10):1, $('eventImagePanel').getStyle('display')!="none" ); } function nextThumbs() { if ( currFrameId < eventData.Frames ) locateImage( parseInt(currFrameId)<(eventData.Frames-10)?(parseInt(currFrameId)+10):eventData.Frames, $('eventImagePanel').getStyle('display')!="none" ); } function prevEvent() { if ( prevEventId ) { eventQuery( prevEventId ); streamPrev( true ); } } function nextEvent() { if ( nextEventId ) { eventQuery( nextEventId ); streamNext( true ); } } function getActResponse( respObj, respText ) { if ( checkStreamForErrors( "getActResponse", respObj ) ) return; if ( respObj.refreshParent ) refreshParentWindow(); if ( respObj.refreshEvent ) eventQuery( eventData.Id ); } var actReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getActResponse } ); function actQuery( action, parms ) { var actParms = "view=request&request=event&id="+eventData.Id+"&action="+action; if ( parms != null ) actParms += "&"+Object.toQueryString( parms ); actReq.send( actParms ); } function deleteEvent() { actQuery( 'delete' ); streamNext( true ); } function renameEvent() { var newName = $('eventName').get('value'); actQuery( 'rename', { eventName: newName } ); } function editEvent() { createPopup( '?view=eventdetail&eid='+eventData.Id, 'zmEventDetail', 'eventdetail' ); } function exportEvent() { createPopup( '?view=export&eid='+eventData.Id, 'zmExport', 'export' ); } function archiveEvent() { actQuery( 'archive' ); } function unarchiveEvent() { actQuery( 'unarchive' ); } function showEventFrames() { createPopup( '?view=frames&eid='+eventData.Id, 'zmFrames', 'frames' ); } function showStream() { $('eventStills').addClass( 'hidden' ); $('eventStream').removeClass( 'hidden' ); $('streamEvent').addClass( 'hidden' ); $('stillsEvent').removeClass( 'hidden' ); //$(window).removeEvent( 'resize', updateStillsSizes ); } function showStills() { $('eventStream').addClass( 'hidden' ); $('eventStills').removeClass( 'hidden' ); $('stillsEvent').addClass( 'hidden' ); $('streamEvent').removeClass( 'hidden' ); streamPause( true ); if ( !scroll ) { scroll = new Fx.Scroll( 'eventThumbs', { wait: false, duration: 500, offset: { 'x': 0, 'y': 0 }, transition: Fx.Transitions.Quad.easeInOut } ); } resetEventStills(); $(window).addEvent( 'resize', updateStillsSizes ); } function showFrameStats() { var fid = $('eventImageNo').get('text'); createPopup( '?view=stats&eid='+eventData.Id+'&fid='+fid, 'zmStats', 'stats', eventData.Width, eventData.Height ); } function videoEvent() { createPopup( '?view=video&eid='+eventData.Id, 'zmVideo', 'video', eventData.Width, eventData.Height ); } function drawProgressBar() { var barWidth = 0; $('progressBar').addClass( 'invisible' ); var cells = $('progressBar').getElements( 'div' ); var cellWidth = parseInt( eventData.Width/$$(cells).length ); $$(cells).forEach( function( cell, index ) { if ( index == 0 ) $(cell).setStyles( { 'left': barWidth, 'width': cellWidth, 'borderLeft': 0 } ); else $(cell).setStyles( { 'left': barWidth, 'width': cellWidth } ); var offset = parseInt((index*eventData.Length)/$$(cells).length); $(cell).setProperty( 'title', '+'+secsToTime(offset)+'s' ); $(cell).removeEvent( 'click' ); $(cell).addEvent( 'click', function(){ streamSeek( offset ); } ); barWidth += $(cell).getCoordinates().width; } ); $('progressBar').setStyle( 'width', barWidth ); $('progressBar').removeClass( 'invisible' ); } function updateProgressBar() { if ( eventData && streamStatus ) { var cells = $('progressBar').getElements( 'div' ); var completeIndex = parseInt((($$(cells).length+1)*streamStatus.progress)/eventData.Length); $$(cells).forEach( function( cell, index ) { if ( index < completeIndex ) { if ( !$(cell).hasClass( 'complete' ) ) { $(cell).addClass( 'complete' ); } } else { if ( $(cell).hasClass( 'complete' ) ) { $(cell).removeClass( 'complete' ); } } } ); } } function handleClick( event ) { var target = event.target; var x = event.page.x - $(target).getLeft(); var y = event.page.y - $(target).getTop(); if ( event.shift ) streamPan( x, y ); else streamZoomIn( x, y ); } function initPage() { streamCmdTimer = streamQuery.delay( 250 ); eventQuery.pass( eventData.Id ).delay( 500 ); if ( canStreamNative ) { var streamImg = $('imageFeed').getElement('img'); if ( !streamImg ) streamImg = $('imageFeed').getElement('object'); $(streamImg).addEvent( 'click', function( event ) { handleClick( event ); } ); } } // Kick everything off window.addEvent( 'domready', initPage ); ZoneMinder-1.26.5/web/skins/classic/views/js/event.js.php000066400000000000000000000024301225361755400232330ustar00rootroot00000000000000// // Import constants // var CMD_NONE = ; var CMD_PAUSE = ; var CMD_PLAY = ; var CMD_STOP = ; var CMD_FASTFWD = ; var CMD_SLOWFWD = ; var CMD_SLOWREV = ; var CMD_FASTREV = ; var CMD_ZOOMIN = ; var CMD_ZOOMOUT = ; var CMD_PAN = ; var CMD_SCALE = ; var CMD_PREV = ; var CMD_NEXT = ; var CMD_SEEK = ; var CMD_QUERY = ; var SCALE_BASE = ; // // PHP variables to JS // var connKey = ''; var eventData = { Id: , Width: , Height: , Length: }; var filterQuery = ''; var sortQuery = ''; var scale = ; var canEditEvents = ; var streamTimeout = ; var canStreamNative = ; // // Strings // var deleteString = ""; var causeString = ""; ZoneMinder-1.26.5/web/skins/classic/views/js/events.js000066400000000000000000000101201225361755400226230ustar00rootroot00000000000000function closeWindows() { window.close(); // This is a hack. The only way to close an existing window is to try and open it! var filterWindow = window.open( thisUrl+'?view=none', 'zmFilter', 'width=1,height=1' ); filterWindow.close(); } function toggleCheckbox( element, name ) { var form = element.form; var checked = element.checked; for (var i = 0; i < form.elements.length; i++) if (form.elements[i].name.indexOf(name) == 0) form.elements[i].checked = checked; form.viewBtn.disabled = !checked; form.editBtn.disabled = !checked; form.archiveBtn.disabled = unarchivedEvents?!checked:true; form.unarchiveBtn.disabled = archivedEvents?!checked:true; form.exportBtn.disabled = !checked; form.deleteBtn.disabled = !checked; } function configureButton( element, name ) { var form = element.form; var checked = element.checked; if ( !checked ) { for (var i = 0; i < form.elements.length; i++) { if ( form.elements[i].name.indexOf(name) == 0) { if ( form.elements[i].checked ) { checked = true; break; } } } } if ( !element.checked ) form.toggleCheck.checked = false; form.viewBtn.disabled = !checked; form.editBtn.disabled = !checked; form.archiveBtn.disabled = (!checked)||(!unarchivedEvents); form.unarchiveBtn.disabled = (!checked)||(!archivedEvents); form.exportBtn.disabled = !checked; form.deleteBtn.disabled = !checked; } function deleteEvents( element, name ) { var form = element.form; var count = 0; for (var i = 0; i < form.elements.length; i++) { if (form.elements[i].name.indexOf(name) == 0) { if ( form.elements[i].checked ) { count++; break; } } } if ( count > 0 ) { if ( confirm( confirmDeleteEventsString ) ) { form.action.value = 'delete'; form.submit(); } } } function editEvents( element, name ) { var form = element.form; var eids = new Array(); for (var i = 0; i < form.elements.length; i++) { if (form.elements[i].name.indexOf(name) == 0) { if ( form.elements[i].checked ) { eids[eids.length] = 'eids[]='+form.elements[i].value; } } } createPopup( '?view=eventdetail&'+eids.join( '&' ), 'zmEventDetail', 'eventdetail' ); } function exportEvents( element, name ) { var form = element.form; var eids = new Array(); for (var i = 0; i < form.elements.length; i++) { if (form.elements[i].name.indexOf(name) == 0) { if ( form.elements[i].checked ) { eids[eids.length] = 'eids[]='+form.elements[i].value; } } } createPopup( '?view=export&'+eids.join( '&' ), 'zmExport', 'export' ); } function viewEvents( element, name ) { var form = element.form; var events = new Array(); for (var i = 0; i < form.elements.length; i++) { if ( form.elements[i].name.indexOf(name) == 0) { if ( form.elements[i].checked ) { events[events.length] = form.elements[i].value; } } } if ( events.length > 0 ) { createPopup( '?view=event&eid='+events[0]+'&filter[terms][0][attr]=Id&&filter[terms][0][op]=%3D%5B%5D&&filter[terms][0][val]='+events.join('%2C')+sortQuery+'&page=1&play=1', 'zmEvent', 'event', maxWidth, maxHeight ); } } function archiveEvents( element, name ) { var form = element.form; form.action.value = 'archive'; form.submit(); } function unarchiveEvents( element, name ) { var form = element.form; form.action.value = 'unarchive'; form.submit(); } if ( openFilterWindow ) { //opener.location.reload(true); createPopup( '?view=filter&page='+thisPage+filterQuery, 'zmFilter', 'filter' ); location.replace( '?view='+currentView+'&page='+thisPage+filterQuery ); } ZoneMinder-1.26.5/web/skins/classic/views/js/events.js.php000066400000000000000000000010511225361755400234140ustar00rootroot00000000000000//var openFilterWindow = ; var openFilterWindow = false; var archivedEvents = ; var unarchivedEvents = ; var filterQuery = ''; var sortQuery = ''; var maxWidth = ; var maxHeight = ; var confirmDeleteEventsString = ""; ZoneMinder-1.26.5/web/skins/classic/views/js/export.js000066400000000000000000000033451225361755400226530ustar00rootroot00000000000000function configureExportButton( element ) { var form = element.form; var checkCount = 0; var radioCount = 0; for ( var i = 0; i < form.elements.length; i++ ) if ( form.elements[i].type == "checkbox" && form.elements[i].checked ) checkCount++; else if ( form.elements[i].type == "radio" && form.elements[i].checked ) radioCount++; form.elements['exportButton'].disabled = (checkCount == 0 || radioCount == 0); } function startDownload( exportFile ) { window.location.replace( exportFile ); } var exportTimer = null; function exportProgress() { var tickerText = $('exportProgressTicker').get('text'); if ( tickerText.length < 1 || tickerText.length > 4 ) $('exportProgressTicker').set( 'text', '.' ); else $('exportProgressTicker').appendText( '.' ); } function exportResponse( respObj, respText ) { window.location.replace( thisUrl+'?view='+currentView+'&'+eidParm+'&exportFile='+respObj.exportFile+'&generated='+((respObj.result=='Ok')?1:0) ); } function exportEvent( form ) { var parms = 'view=request&request=event&action=export'; parms += '&'+$(form).toQueryString(); var query = new Request.JSON( { url: thisUrl, method: 'post', data: parms, onSuccess: exportResponse } ); query.send(); $('exportProgress').removeClass( 'hidden' ); $('exportProgress').setProperty( 'class', 'warnText' ); $('exportProgressText').set( 'text', exportProgressString ); exportProgress(); exportTimer = exportProgress.periodical( 500 ); } function initPage() { configureExportButton( $('exportButton') ); if ( exportReady ) { startDownload.pass( exportFile ).delay( 1500 ); } } window.addEvent( 'domready', initPage ); ZoneMinder-1.26.5/web/skins/classic/views/js/export.js.php000066400000000000000000000010221225361755400234270ustar00rootroot00000000000000 var eidParm = ''; var eidParm = 'eid='; var exportReady = ; var exportFile = ''; var exportProgressString = ''; ZoneMinder-1.26.5/web/skins/classic/views/js/filter.js000066400000000000000000000056411225361755400226200ustar00rootroot00000000000000function updateButtons( element ) { var form = element.form; if ( element.type == 'checkbox' && element.checked ) form.elements['executeButton'].disabled = false; else { var canExecute = false; if ( form.elements['autoArchive'].checked ) canExecute = true; else if ( form.elements['autoVideo'].checked ) canExecute = true; else if ( form.elements['autoUpload'] && form.elements['autoUpload'].checked ) canExecute = true; else if ( form.elements['autoEmail'].checked ) canExecute = true; else if ( form.elements['autoMessage'].checked ) canExecute = true; else if ( form.elements['autoExecute'].checked && form.elements['autoExecuteCmd'].value != '' ) canExecute = true; else if ( form.elements['autoDelete'].checked ) canExecute = true; form.elements['executeButton'].disabled = !canExecute; } } function clearValue( element, line ) { var form = element.form; var val = form.elements['filter[terms]['+line+'][val]']; val.value = ''; } function submitToFilter( element, reload ) { var form = element.form; form.target = window.name; form.view.value = 'filter'; form.reload.value = reload; form.submit(); } function submitToEvents( element ) { var form = element.form; if ( validateForm( form ) ) { form.target = 'zmEvents'; form.view.value = 'events'; form.action.value = ''; form.execute.value = 0; form.submit(); } } function executeFilter( element ) { var form = element.form; if ( validateForm( form ) ) { form.target = 'zmEvents'; form.view.value = 'events'; form.action.value = 'filter'; form.execute.value = 1; form.submit(); } } function saveFilter( element ) { var form = element.form; var popupName = 'zmEventsFilterSave'; createPopup( thisUrl, popupName, 'filtersave' ); form.target = popupName; form.view.value = 'filtersave'; form.submit(); } function deleteFilter( element, name ) { if ( confirm( deleteSavedFilterString+" '"+name+"'" ) ) { var form = element.form; form.action.value = 'delete'; form.fid.value = name; submitToFilter( element, 1 ); } } function addTerm( element, line ) { var form = element.form; form.target = window.name; form.view.value = currentView; form.action.value = 'filter'; form.subaction.value = 'addterm'; form.line.value = line; form.submit(); } function delTerm( element, line ) { var form = element.form; form.target = window.name; form.view.value = currentView; form.action.value = 'filter'; form.subaction.value = 'delterm'; form.line.value = line; form.submit(); } function init() { updateButtons( $('executeButton') ); } window.addEvent( 'domready', init ); ZoneMinder-1.26.5/web/skins/classic/views/js/filter.js.php000066400000000000000000000025211225361755400234000ustar00rootroot00000000000000var deleteSavedFilterString = ""; function validateForm( form ) { 2 ) { ?> var bracket_count = 0; var obr = form.elements['filter[terms][][obr]']; var cbr = form.elements['filter[terms][][cbr]']; bracket_count += parseInt(obr.options[obr.selectedIndex].value); bracket_count -= parseInt(cbr.options[cbr.selectedIndex].value); if ( bracket_count ) { alert( "" ); return( false ); } var val = form.elements['filter[terms][][val]']; if ( val.value == '' ) { alert( "" ); return( false ); } return( true ); } ZoneMinder-1.26.5/web/skins/classic/views/optionhelp.php000066400000000000000000000030711225361755400232460ustar00rootroot00000000000000", $optionHelpText ); $focusWindow = true; xhtmlHeaders(__FILE__, $SLANG['OptionHelp'] ); ?>

ZoneMinder-1.26.5/web/skins/classic/views/options.php000066400000000000000000000275001225361755400225630ustar00rootroot00000000000000
    $value ) { if ( $tab == $name ) { ?>
window.opener.location.reload();window.location.reload();'; } ?>
ZM_SKIN
/>
disabled="disabled"/>
disabled="disabled"/>
$value ) { $shortName = preg_replace( '/^ZM_/', '', $name ); $optionPromptText = !empty($OLANG[$shortName])?$OLANG[$shortName]:$value['Prompt']; ?>
 () checked="checked"/> 3 ) { ?> checked="checked"/>  /> /> /> />
/>
ZoneMinder-1.26.5/web/skins/classic/views/plugin.php000066400000000000000000000115511225361755400223650ustar00rootroot00000000000000 0 ) { $newZone = dbFetchOne( "select * from Zones where MonitorId = '".dbEscape($mid)."' and Id = '".dbEscape($zid)."'" ); } else { $view = "error"; return; } $monitor = dbFetchMonitor ( $mid ); $plugin = dbEscape($_REQUEST['pl']); $plugin_path = dirname(ZM_PLUGINS_CONFIG_PATH)."/".$plugin; $focusWindow = true; xhtmlHeaders(__FILE__, $SLANG['Plugin'] ); $pluginOptions=array( 'Enabled'=>array( 'Type'=>'select', 'Name'=>'Enabled', 'Choices'=>'yes,no', 'Value'=>'no' ) ); $optionNames=array(); if(file_exists($plugin_path."/config.php")) { include_once($plugin_path."/config.php"); } $sql="SELECT * FROM PluginsConfig WHERE MonitorId=$mid AND ZoneId=$zid AND pluginName='$plugin'"; foreach( dbFetchAll( $sql ) as $popt ) { if(array_key_exists($popt['Name'], $pluginOptions) && $popt['Type']==$pluginOptions[$popt['Name']]['Type'] && $popt['Choices']==$pluginOptions[$popt['Name']]['Choices'] ) { $pluginOptions[$popt['Name']]=$popt; array_push($optionNames, $popt['Name']); } else { dbQuery("DELETE from PluginsConfig WHERE Id=".$popt['Id']); } } foreach($pluginOptions as $name => $values) { if(!in_array($name, $optionNames)) { $popt=$pluginOptions[$name]; $sql="INSERT INTO PluginsConfig VALUES ('','".dbEscape($popt['Name'])."','".dbEscape($popt['Value'])."', '".dbEscape($popt['Type'])."','".dbEscape($popt['Choices'])."','$mid','$zid','$plugin')"; dbQuery($sql); } } $PLANG=array(); if(file_exists($plugin_path."/lang/".$user['Language'].".php")) { include_once($plugin_path."/lang/".$user['Language'].".php"); } function pLang($name) { global $PLANG; if(array_key_exists($name, $PLANG)) return $PLANG[$name]; else return $name; } ?>
$popt) { ?>
disabled="disabled"/>
ZoneMinder-1.26.5/web/skins/classic/views/postlogin.php000066400000000000000000000021011225361755400230740ustar00rootroot00000000000000

ZoneMinder-1.26.5/web/skins/classic/views/settings.php000066400000000000000000000064271225361755400227350ustar00rootroot00000000000000
disabled="disabled"/>
disabled="disabled"/>
disabled="disabled"/>
disabled="disabled"/>
disabled="disabled"/>
ZoneMinder-1.26.5/web/skins/classic/views/state.php000066400000000000000000000062341225361755400222110ustar00rootroot00000000000000

ZoneMinder-1.26.5/web/skins/classic/views/stats.php000066400000000000000000000101541225361755400222230ustar00rootroot00000000000000
1 ) { ?>
ZoneMinder-1.26.5/web/skins/classic/views/status.php000066400000000000000000000034311225361755400224100ustar00rootroot00000000000000
ZoneMinder-1.26.5/web/skins/classic/views/timeline.php000066400000000000000000001005061225361755400226740ustar00rootroot00000000000000700, "height"=>460, "image" => array( "width"=>264, "height"=>220, "topOffset"=>20, ), "imageText" => array( "width"=>400, "height"=>30, "topOffset"=>20, ), "graph" => array( "width"=>600, "height"=>160, "topOffset"=>30, ), "title" => array( "topOffset"=>50 ), "key" => array( "topOffset"=>50 ), "axes" => array( "x" => array( "height" => 20, ), "y" => array( "width" => 30, ), ), "grid" => array( "x" => array( "major" => array( "max" => 12, "min" => 4, ), "minor" => array( "max" => 48, "min" => 12, ), ), "y" => array( "major" => array( "max" => 8, "min" => 1, ), "minor" => array( "max" => 0, "min" => 0, ), ), ), ); $monitors = array(); $monitorsSql = "select * from Monitors order by Sequence asc"; //srand( 97981 ); foreach( dbFetchAll( $monitorsSql ) as $row ) { //if ( empty($row['WebColour']) ) //{ //$row['WebColour'] = sprintf( "#%02x%02x%02x", rand( 0, 255 ), rand( 0, 255), rand( 0, 255 ) ); //} $monitors[$row['Id']] = $row; } $rangeSql = "select min(E.StartTime) as MinTime, max(E.EndTime) as MaxTime from Events as E inner join Monitors as M on (E.MonitorId = M.Id) where not isnull(E.StartTime) and not isnull(E.EndTime)"; $eventsSql = "select E.Id,E.Name,E.StartTime,E.EndTime,E.Length,E.Frames,E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId from Events as E inner join Monitors as M on (E.MonitorId = M.Id) where not isnull(StartTime)"; if ( !empty($user['MonitorIds']) ) { $monFilterSql = " and M.Id in (".join( ",", preg_split( '/["\'\s]*,["\'\s]*/', $user['MonitorIds'] ) ).")"; $rangeSql .= $monFilterSql; $eventsSql .= $monFilterSql; } if ( isset($_REQUEST['filter']) ) $tree = parseFilterToTree( $_REQUEST['filter'] ); else $tree = false; if ( isset($_REQUEST['range']) ) $range = validHtmlStr($_REQUEST['range']); if ( isset($_REQUEST['minTime']) ) $minTime = validHtmlStr($_REQUEST['minTime']); if ( isset($_REQUEST['midTime']) ) $midTime = validHtmlStr($_REQUEST['midTime']); if ( isset($_REQUEST['maxTime']) ) $maxTime = validHtmlStr($_REQUEST['maxTime']); if ( isset($range) ) { $halfRange = (int)($range/2); if ( isset($midTime) ) { $midTimeT = strtotime($midTime); $minTimeT = $midTimeT-$halfRange; $maxTimeT = $midTimeT+$halfRange; if ( !($range%1) ) { $maxTimeT--; } $minTime = strftime( STRF_FMT_DATETIME_DB, $minTimeT ); $maxTime = strftime( STRF_FMT_DATETIME_DB, $maxTimeT ); } elseif ( isset($minTime) ) { $minTimeT = strtotime($minTime); $maxTimeT = $minTimeT + $range; $midTimeT = $minTimeT + $halfRange; $midTime = strftime( STRF_FMT_DATETIME_DB, $midTimeT ); $maxTime = strftime( STRF_FMT_DATETIME_DB, $maxTimeT ); } elseif ( isset($maxTime) ) { $maxTimeT = strtotime($maxTime); $minTimeT = $maxTimeT - $range; $midTimeT = $minTimeT + $halfRange; $minTime = strftime( STRF_FMT_DATETIME_DB, $minTimeT ); $midTime = strftime( STRF_FMT_DATETIME_DB, $midTimeT ); } } elseif ( isset($minTime) && isset($maxTime) ) { $minTimeT = strtotime($minTime); $maxTimeT = strtotime($maxTime); $range = ($maxTimeT - $minTimeT) + 1; $halfRange = (int)($range/2); $midTimeT = $minTimeT + $halfRange; $midTime = strftime( STRF_FMT_DATETIME_DB, $midTimeT ); } if ( isset($minTime) && isset($maxTime) ) { $tempMinTime = $tempMaxTime = $tempExpandable = false; extractDatetimeRange( $tree, $tempMinTime, $tempMaxTime, $tempExpandable ); $filterSql = parseTreeToSQL( $tree ); if ( $filterSql ) { $filterSql = " and $filterSql"; $eventsSql .= $filterSql; } } else { $filterSql = parseTreeToSQL( $tree ); $tempMinTime = $tempMaxTime = $tempExpandable = false; extractDatetimeRange( $tree, $tempMinTime, $tempMaxTime, $tempExpandable ); if ( $filterSql ) { $filterSql = " and $filterSql"; $rangeSql .= $filterSql; $eventsSql .= $filterSql; } if ( !isset($minTime) || !isset($maxTime) ) { // Dynamically determine range $row = dbFetchOne( $rangeSql ); if ( !isset($minTime) ) $minTime = $row['MinTime']; if ( !isset($maxTime) ) $maxTime = $row['MaxTime']; } if ( empty($minTime) ) $minTime = $tempMinTime; if ( empty($maxTime) ) $maxTime = $tempMaxTime; if ( empty($maxTime) ) $maxTime = "now"; $minTimeT = strtotime($minTime); $maxTimeT = strtotime($maxTime); $range = ($maxTimeT - $minTimeT) + 1; $halfRange = (int)($range/2); $midTimeT = $minTimeT + $halfRange; $midTime = strftime( STRF_FMT_DATETIME_DB, $midTimeT ); } //echo "MnT: $tempMinTime, MxT: $tempMaxTime, ExP: $tempExpandable
"; if ( $tree ) { appendDatetimeRange( $tree, $minTime, $maxTime ); $filterQuery = parseTreeToQuery( $tree ); } else { $filterQuery = false; } $scales = array( array( "name"=>"year", "factor"=>60*60*24*365, "align"=>1, "zoomout"=>2, "label"=>STRF_TL_AXIS_LABEL_YEAR ), array( "name"=>"month", "factor"=>60*60*24*30, "align"=>1, "zoomout"=>12, "label"=>STRF_TL_AXIS_LABEL_MONTH ), array( "name"=>"week", "factor"=>60*60*24*7, "align"=>1, "zoomout"=>4.25, "label"=>STRF_TL_AXIS_LABEL_WEEK, "labelCheck"=>"%W" ), array( "name"=>"day", "factor"=>60*60*24, "align"=>1, "zoomout"=>7, "label"=>STRF_TL_AXIS_LABEL_DAY ), array( "name"=>"hour4", "factor"=>60*60, "align"=>4, "zoomout"=>6, "label"=>STRF_TL_AXIS_LABEL_4HOUR, "labelCheck"=>"%H" ), array( "name"=>"hour", "factor"=>60*60, "align"=>1, "zoomout"=>4, "label"=>STRF_TL_AXIS_LABEL_HOUR, "labelCheck"=>"%H" ), array( "name"=>"minute10", "factor"=>60, "align"=>10, "zoomout"=>6, "label"=>STRF_TL_AXIS_LABEL_10MINUTE, "labelCheck"=>"%M" ), array( "name"=>"minute", "factor"=>60, "align"=>1, "zoomout"=>10, "label"=>STRF_TL_AXIS_LABEL_MINUTE, "labelCheck"=>"%M" ), array( "name"=>"second10", "factor"=>1, "align"=>10, "zoomout"=>6, "label"=>STRF_TL_AXIS_LABEL_10SECOND ), array( "name"=>"second", "factor"=>1, "align"=>1, "zoomout"=>10, "label"=>STRF_TL_AXIS_LABEL_SECOND ), ); $majXScale = getDateScale( $scales, $range, $chart['grid']['x']['major']['min'], $chart['grid']['x']['major']['max'] ); // Adjust the range etc for scale $minTimeT -= $minTimeT%($majXScale['factor']*$majXScale['align']); $minTime = strftime( STRF_FMT_DATETIME_DB, $minTimeT ); $maxTimeT += (($majXScale['factor']*$majXScale['align'])-$maxTimeT%($majXScale['factor']*$majXScale['align']))-1; if ( $maxTimeT > time() ) $maxTimeT = time(); $maxTime = strftime( STRF_FMT_DATETIME_DB, $maxTimeT ); $range = ($maxTimeT - $minTimeT) + 1; $halfRange = (int)($range/2); $midTimeT = $minTimeT + $halfRange; $midTime = strftime( STRF_FMT_DATETIME_DB, $midTimeT ); //echo "R:$range
"; //echo "MnT:$minTime
"; //echo "MnTt:$minTimeT
"; //echo "MdT:$midTime
"; //echo "MdTt:$midTimeT
"; //echo "MxT:$maxTime
"; //echo "MxTt:$maxTimeT
"; if ( isset($minTime) && isset($maxTime) ) { $eventsSql .= " and E.EndTime >= '$minTime' and E.StartTime <= '$maxTime'"; } $eventsSql .= " order by Id asc"; //echo "ESQL: $eventsSql
"; $chart['data'] = array( "x" => array( "lo" => strtotime( $minTime ), "hi" => strtotime( $maxTime ), ), "y" => array( "lo" => 0, "hi" => 0, ) ); $chart['data']['x']['range'] = ($chart['data']['x']['hi'] - $chart['data']['x']['lo']) + 1; $chart['data']['x']['density'] = $chart['data']['x']['range']/$chart['graph']['width']; $monEventSlots = array(); $monFrameSlots = array(); $monitorIds = array(); foreach( dbFetchAll( $eventsSql ) as $event ) { if ( !isset($monitorIds[$event['MonitorId']]) ) $monitorIds[$event['MonitorId']] = true; if ( !isset($monEventSlots[$event['MonitorId']]) ) $monEventSlots[$event['MonitorId']] = array(); if ( !isset($monFrameSlots[$event['MonitorId']]) ) $monFrameSlots[$event['MonitorId']] = array(); $currEventSlots = &$monEventSlots[$event['MonitorId']]; $currFrameSlots = &$monFrameSlots[$event['MonitorId']]; $startTimeT = strtotime($event['StartTime']); $startIndex = $rawStartIndex = (int)(($startTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']); if ( $startIndex < 0 ) $startIndex = 0; if ( isset($event['EndTime']) ) $endTimeT = strtotime($event['EndTime']); else $endTimeT = time(); $endIndex = $rawEndIndex = (int)(($endTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']); if ( $endIndex >= $chart['graph']['width'] ) $endIndex = $chart['graph']['width'] - 1; for ( $i = $startIndex; $i <= $endIndex; $i++ ) { if ( !isset($currEventSlots[$i]) ) { if ( $rawStartIndex == $rawEndIndex ) { $offset = 1; } else { $offset = 1 + ($event['Frames']?((int)(($event['Frames']-1)*(($i-$rawStartIndex)/($rawEndIndex-$rawStartIndex)))):0); } $currEventSlots[$i] = array( "count"=>0, "width"=>1, "offset"=>$offset, "event"=>$event ); } else { $currEventSlots[$i]['count']++; } } if ( $event['MaxScore'] > 0 ) { if ( $startIndex == $endIndex ) { $framesSql = "select FrameId,Score from Frames where EventId = '".$event['Id']."' and Score > 0 order by Score desc limit 1"; $frame = dbFetchOne( $framesSql ); $i = $startIndex; if ( !isset($currFrameSlots[$i]) ) { $currFrameSlots[$i] = array( "count"=>1, "value"=>$event['MaxScore'], "event"=>$event, "frame"=>$frame ); } else { $currFrameSlots[$i]['count']++; if ( $event['MaxScore'] > $currFrameSlots[$i]['value'] ) { $currFrameSlots[$i]['value'] = $event['MaxScore']; $currFrameSlots[$i]['event'] = $event; $currFrameSlots[$i]['frame'] = $frame; } } if ( $event['MaxScore'] > $chart['data']['y']['hi'] ) { $chart['data']['y']['hi'] = $event['MaxScore']; } } else { $framesSql = "select FrameId,Delta,unix_timestamp(TimeStamp) as TimeT,Score from Frames where EventId = '".$event['Id']."' and Score > 0"; $result = dbQuery( $framesSql ); while( $frame = dbFetchNext( $result ) ) { if ( $frame['Score'] == 0 ) continue; $frameTimeT = $frame['TimeT']; $frameTimeT = $startTimeT + $frame['Delta']; $frameIndex = (int)(($frameTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']); if ( $frameIndex < 0 ) continue; if ( $frameIndex >= $chart['graph']['width'] ) continue; if ( !isset($currFrameSlots[$frameIndex]) ) { $currFrameSlots[$frameIndex] = array( "count"=>1, "value"=>$frame['Score'], "event"=>$event, "frame"=>$frame ); } else { $currFrameSlots[$frameIndex]['count']++; if ( $frame['Score'] > $currFrameSlots[$frameIndex]['value'] ) { $currFrameSlots[$frameIndex]['value'] = $frame['Score']; $currFrameSlots[$frameIndex]['event'] = $event; $currFrameSlots[$frameIndex]['frame'] = $frame; } } if ( $frame['Score'] > $chart['data']['y']['hi'] ) { $chart['data']['y']['hi'] = $frame['Score']; } } } } } ksort( $monitorIds, SORT_NUMERIC ); ksort( $monEventSlots, SORT_NUMERIC ); ksort( $monFrameSlots, SORT_NUMERIC ); // No longer needed? if ( false ) { // Add on missing frames foreach( array_keys($monFrameSlots) as $monitorId ) { unset( $currFrameSlots ); $currFrameSlots = &$monFrameSlots[$monitorId]; for ( $i = 0; $i < $chart['graph']['width']; $i++ ) { if ( isset($currFrameSlots[$i]) ) { if ( !isset($currFrameSlots[$i]['frame']) ) { $framesSql = "select FrameId,Score from Frames where EventId = '".$currFrameSlots[$i]['event']['Id']."' and Score > 0 order by FrameId limit 1"; $currFrameSlots[$i]['frame'] = dbFetchOne( $framesSql ); } } } } } $chart['data']['y']['range'] = ($chart['data']['y']['hi'] - $chart['data']['y']['lo']) + 1; $chart['data']['y']['density'] = $chart['data']['y']['range']/$chart['graph']['height']; $majYScale = getYScale( $chart['data']['y']['range'], $chart['grid']['y']['major']['min'], $chart['grid']['y']['major']['max'] ); $maxWidth = 0; $maxHeight = 0; foreach ( array_keys($monitorIds) as $monitorId ) { if ( $maxWidth < $monitors[$monitorId]['Width'] ) $maxWidth = $monitors[$monitorId]['Width']; if ( $maxHeight < $monitors[$monitorId]['Height'] ) $maxHeight = $monitors[$monitorId]['Height']; } //print_r( $monEventSlots ); // Optimise boxes foreach( array_keys($monEventSlots) as $monitorId ) { unset( $currEventSlots ); $currEventSlots = &$monEventSlots[$monitorId]; for ( $i = 0; $i < $chart['graph']['width']; $i++ ) { if ( isset($currEventSlots[$i]) ) { if ( isset($currSlot) ) { if ( $currSlot['event']['Id'] == $currEventSlots[$i]['event']['Id'] ) { if ( $currSlot['width'] < $maxEventWidth ) { // Merge slots for the same long event $currSlot['width']++; unset( $currEventSlots[$i] ); continue; } elseif ( $currSlot['offset'] < $currEventSlots[$i]['offset'] ) { // Split very long events $currEventSlots[$i]['frame'] = array( 'FrameId'=>$currEventSlots[$i]['offset'] ); } } elseif ( $currSlot['width'] < $minEventWidth ) { // Merge multiple small events $currSlot['width']++; unset( $currEventSlots[$i] ); continue; } } $currSlot = &$currEventSlots[$i]; } else { unset( $currSlot ); } } if ( isset( $currSlot ) ) unset( $currSlot ); } //print_r( $monEventSlots ); // Stack events $frameSlots = array(); $frameMonitorIds = array_keys($monFrameSlots); for ( $i = 0; $i < $chart['graph']['width']; $i++ ) { foreach ( $frameMonitorIds as $frameMonitorId ) { unset( $currFrameSlots ); $currFrameSlots = &$monFrameSlots[$frameMonitorId]; if ( isset($currFrameSlots[$i]) ) { if ( !isset($frameSlots[$i]) ) { $frameSlots[$i] = array(); $frameSlots[$i][] = &$currFrameSlots[$i]; } else { $slotCount = count($frameSlots[$i]); for ( $j = 0; $j < $slotCount; $j++ ) { if ( $currFrameSlots[$i]['value'] > $frameSlots[$i][$j]['value'] ) { for ( $k = $slotCount; $k > $j; $k-- ) { $frameSlots[$i][$k] = $frameSlots[$i][$k-1]; } $frameSlots[$i][$j] = &$currFrameSlots[$i]; break 2; } } $frameSlots[$i][] = &$currFrameSlots[$i]; } } } } //print_r( $monEventSlots ); //print_r( $monFrameSlots ); //print_r( $chart ); $graphHeight = $chart['graph']['height']; if ( $mode == "overlay" ) { $minEventBarHeight = 10; $maxEventBarHeight = 40; if ( count($monitorIds) ) { $chart['graph']['eventBarHeight'] = $minEventBarHeight; while ( ($chart['graph']['eventsHeight'] = (($chart['graph']['eventBarHeight'] * count($monitorIds)) + (count($monitorIds)-1))) < $maxEventBarHeight ) { $chart['graph']['eventBarHeight']++; } } else { $chart['graph']['eventBarHeight'] = $maxEventBarHeight; $chart['graph']['eventsHeight'] = $maxEventBarHeight; } $chart['graph']['activityHeight'] = ($graphHeight - $chart['graph']['eventsHeight']); $chart['data']['y']['density'] = $chart['data']['y']['range']/$chart['graph']['activityHeight']; $chart['eventBars'] = array(); $top = $chart['graph']['activityHeight']; foreach ( array_keys($monitorIds) as $monitorId ) { $chart['eventBars'][$monitorId] = array( 'top' => $top ); $top += $chart['graph']['eventBarHeight']+1; } } elseif ( $mode == "split" ) { $minActivityBarHeight = 30; $minEventBarHeight = 10; $maxEventBarHeight = 40; if ( count($monitorIds) ) { $chart['graph']['eventBarHeight'] = $minEventBarHeight; $chart['graph']['activityBarHeight'] = $minActivityBarHeight; while ( ((($chart['graph']['eventBarHeight']+$chart['graph']['activityBarHeight']) * count($monitorIds)) + ((2*count($monitorIds))-1)) < $graphHeight ) { $chart['graph']['activityBarHeight']++; if ( $chart['graph']['eventBarHeight'] < $maxEventBarHeight ) { $chart['graph']['eventBarHeight']++; } } } else { $chart['graph']['eventBarHeight'] = $maxEventBarHeight; $chart['graph']['activityBarHeight'] = $graphHeight - $chart['graph']['eventBarHeight']; } $chart['data']['y']['density'] = $chart['data']['y']['range']/$chart['graph']['activityBarHeight']; ?> $top ); $chart['eventBars'][$monitorId] = array( 'top' => $top+$chart['graph']['activityBarHeight']+1 ); $top += $chart['graph']['activityBarHeight']+1+$chart['graph']['eventBarHeight']+1; } } preg_match( '/^(\d+)-(\d+)-(\d+) (\d+):(\d+)/', $minTime, $startMatches ); preg_match( '/^(\d+)-(\d+)-(\d+) (\d+):(\d+)/', $maxTime, $endMatches ); if ( $startMatches[1] != $endMatches[1] ) { // Different years $title = strftime( STRF_TL_AXIS_RANGE_YEAR1, $chart['data']['x']['lo'] )." - ".strftime( STRF_TL_AXIS_RANGE_YEAR2, $chart['data']['x']['hi'] ); } elseif ( $startMatches[2] != $endMatches[2] ) { // Different months $title = strftime( STRF_TL_AXIS_RANGE_MONTH1, $chart['data']['x']['lo'] )." - ".strftime( STRF_TL_AXIS_RANGE_MONTH2, $chart['data']['x']['hi'] ); } elseif ( $startMatches[3] != $endMatches[3] ) { // Different dates $title = strftime( STRF_TL_AXIS_RANGE_DAY1, $chart['data']['x']['lo'] )." - ".strftime( STRF_TL_AXIS_RANGE_DAY2, $chart['data']['x']['hi'] ); } else { // Different times $title = strftime( STRF_TL_AXIS_RANGE_TIME1, $chart['data']['x']['lo'] )." - ".strftime( STRF_TL_AXIS_RANGE_TIME2, $chart['data']['x']['hi'] ); } function drawXGrid( $chart, $scale, $labelClass, $tickClass, $gridClass, $zoomClass=false ) { global $SLANG; ob_start(); $labelCount = 0; $lastTick = 0; unset( $lastLabel ); $labelCheck = isset($scale['labelCheck'])?$scale['labelCheck']:$scale['label']; ?>
1 ) { $label = (int)(strftime( $labelCheck, $timeOffset )/$scale['align']); } else { $label = strftime( $labelCheck, $timeOffset ); } if ( !isset($lastLabel) || ($lastLabel != $label) ) { $labelCount++; } if ( $labelCount >= $scale['divisor'] ) { $labelCount = 0; if ( isset($lastLabel) ) { if ( $labelClass ) { ?>
<?= $SLANG['ViewEvent'] ?>

Pass your mouse over the graph to view a snapshot image and event details.

Click on the coloured sections of the graph, or the image, to view the event.

Click on the background to zoom in to a smaller time period based around your click.

Use the controls below to zoom out or navigate back and forward through the time range.

$slots ) { foreach ( $slots as $slot ) { $slotHeight = (int)($slot['value']/$chart['data']['y']['density']); if ( $slotHeight <= 0 ) continue; if ( $mouseover ) { $behaviours = array( 'onclick="'.getSlotShowEventBehaviour( $slot ).'"', 'onmouseover="'.getSlotPreviewEventBehaviour( $slot ).'"' ); } else { $behaviours = array( 'onclick="'.getSlotPreviewEventBehaviour( $slot ).'"' ); } ?>
>
$slot ) { $slotHeight = (int)($slot['value']/$chart['data']['y']['density']); if ( $slotHeight <= 0 ) continue; if ( $mouseover ) { $behaviours = array( 'onclick="'.getSlotShowEventBehaviour( $slot ).'"', 'onmouseover="'.getSlotPreviewEventBehaviour( $slot ).'"' ); } else { $behaviours = array( 'onclick="'.getSlotPreviewEventBehaviour( $slot ).'"' ); } ?>
>
>
<?= $monitors[$monitorId]['Name'] ?>
ZoneMinder-1.26.5/web/skins/classic/views/user.php000066400000000000000000000124501225361755400220440ustar00rootroot00000000000000$SLANG['No'], 1=>$SLANG['Yes'] ); $nv = array( 'None'=>$SLANG['None'], 'View'=>$SLANG['View'] ); $nve = array( 'None'=>$SLANG['None'], 'View'=>$SLANG['View'], 'Edit'=>$SLANG['Edit'] ); $bandwidths = array_merge( array( ""=>"" ), $bwArray ); $langs = array_merge( array( ""=>"" ), getLanguages() ); $sql = "select Id,Name from Monitors order by Sequence asc"; $monitors = array(); foreach( dbFetchAll( $sql ) as $monitor ) { $monitors[] = $monitor; } $focusWindow = true; xhtmlHeaders(__FILE__, $SLANG['User']." - ".$newUser['Username'] ); ?>
ZoneMinder-1.26.5/web/skins/classic/views/version.php000066400000000000000000000057041225361755400225570ustar00rootroot00000000000000 $SLANG['GoToZoneMinder'] ); if ( verNum( ZM_DYN_CURR_VERSION ) != verNum( ZM_DYN_LAST_VERSION ) ) { $options = array_merge( $options, array( "ignore" => $SLANG['VersionIgnore'], "hour" => $SLANG['VersionRemindHour'], "day" => $SLANG['VersionRemindDay'], "week" => $SLANG['VersionRemindWeek'], "never" => $SLANG['VersionRemindNever'] ) ); } $focusWindow = true; xhtmlHeaders(__FILE__, $SLANG['Version'] ); ?>

ZoneMinder-1.26.5/web/skins/classic/views/video.php000066400000000000000000000213331225361755400221740ustar00rootroot00000000000000

checked="checked"/>
disabled="disabled"/>

0 ) { preg_match( '/^(.+)-((?:r[_\d]+)|(?:F[_\d]+))-((?:s[_\d]+)|(?:S[0-9a-z]+))\.([^.]+)$/', $file, $matches ); if ( preg_match( '/^r(.+)$/', $matches[2], $temp_matches ) ) { $rate = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) ); $rateText = isset($rates[$rate])?$rates[$rate]:($rate."x"); } elseif ( preg_match( '/^F(.+)$/', $matches[2], $temp_matches ) ) { $rateText = $temp_matches[1]."fps"; } if ( preg_match( '/^s(.+)$/', $matches[3], $temp_matches ) ) { $scale = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) ); $scaleText = isset($scales[$scale])?$scales[$scale]:($scale."x"); } elseif ( preg_match( '/^S(.+)$/', $matches[3], $temp_matches ) ) { $scaleText = $temp_matches[1]; } $width = $scale?reScale( $event['Width'], $scale ):$event['Width']; $height = $scale?reScale( $event['Height'], $scale ):$event['Height']; ?>
 /  / 
ZoneMinder-1.26.5/web/skins/classic/views/watch.php000066400000000000000000000220141225361755400221710ustar00rootroot00000000000000
 -  fps
ZoneMinder-1.26.5/web/skins/classic/views/zone.php000066400000000000000000000276351225361755400220540ustar00rootroot00000000000000 0 ) { $zone = dbFetchOne( "select * from Zones where MonitorId = '".dbEscape($monitor['Id'])."' and Id = '".dbEscape($zid)."'" ); } else { $zone = array( 'Name' => $SLANG['New'], 'Id' => 0, 'MonitorId' => $monitor['Id'], 'NumCoords' => 4, 'Coords' => sprintf( "%d,%d %d,%d, %d,%d %d,%d", $minX, $minY, $maxX, $minY, $maxX, $maxY, $minX, $maxY ), 'Area' => $monitor['Width'] * $monitor['Height'], 'AlarmRGB' => 0xff0000, 'CheckMethod' => 'Blobs', 'MinPixelThreshold' => '', 'MaxPixelThreshold' => '', 'MinAlarmPixels' => '', 'MaxAlarmPixels' => '', 'FilterX' => '', 'FilterY' => '', 'MinFilterPixels' => '', 'MaxFilterPixels' => '', 'MinBlobPixels' => '', 'MaxBlobPixels' => '', 'MinBlobs' => '', 'MaxBlobs' => '', 'OverloadFrames' => '', ); } $zone['Points'] = coordsToPoints( $zone['Coords'] ); $newZone = $zone; } //if ( !$points ) //{ //$points = $zone['Points']; //} ksort( $newZone['Points'], SORT_NUMERIC ); $newZone['Coords'] = pointsToCoords( $newZone['Points'] ); $newZone['Area'] = getPolyArea( $newZone['Points'] ); $selfIntersecting = isSelfIntersecting( $newZone['Points'] ); $wd = getcwd(); chdir( ZM_DIR_IMAGES ); $command = getZmuCommand( " -m ".$mid." -z" ); $command .= '"'.$zid.' '.$hicolor.' '.$newZone['Coords'].'"'; $status = exec( escapeshellcmd( $command ) ); chdir( $wd ); $zoneImage = ZM_DIR_IMAGES.'/Zones'.$monitor['Id'].'.jpg?'.time(); $focusWindow = true; xhtmlHeaders(__FILE__, $SLANG['Zone'] ); ?>
"applyPreset()", "onblur"=>"this.selectedIndex=0" ) ) ?>
 /  / 
Zone Image
disabled="disabled"/> disabled="disabled"/> + 3 ) { ?>  X
 
disabled="disabled"/>
ZoneMinder-1.26.5/web/skins/classic/views/zones.php000066400000000000000000000103161225361755400222230ustar00rootroot00000000000000
<?= htmlspecialchars($zone['Name']) ?> zones
 /  disabled="disabled"/>
disabled="disabled"/>
ZoneMinder-1.26.5/web/skins/mobile/000077500000000000000000000000001225361755400170445ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/mobile/Makefile.am000066400000000000000000000002361225361755400211010ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/skins/mobile SUBDIRS = \ ajax \ css \ graphics \ includes \ lang \ views dist_web_DATA = \ skin.php ZoneMinder-1.26.5/web/skins/mobile/ajax/000077500000000000000000000000001225361755400177675ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/mobile/ajax/Makefile.am000066400000000000000000000001421225361755400220200ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/skins/mobile/ajax dist_web_DATA = # No files here ZoneMinder-1.26.5/web/skins/mobile/css/000077500000000000000000000000001225361755400176345ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/mobile/css/Makefile.am000066400000000000000000000001341225361755400216660ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/skins/mobile/css dist_web_DATA = \ skin.css ZoneMinder-1.26.5/web/skins/mobile/css/skin.css000066400000000000000000000103461225361755400213160ustar00rootroot00000000000000/* * Primary look and feel styles */ body { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 100%; color: #333333; font-weight: normal; text-align: center; } h1 { font-family: inherit; font-size: 120%; color: #000066; font-weight: bold; } h2 { font-family: inherit; font-size: 110%; color: #000066; font-weight: bold; } h3 { font-family: inherit; font-size: 100%; color: #016A9D; font-weight: bold; } p { font-family: inherit; font-size: 100%; color: #333333; font-weight: normal; } th { font-weight: bold; color: #016A9D; } a:link { color: #7F7FB2; text-decoration: none; } a:visited { color: #7F7FB2; text-decoration: none; } a:hover { color: #666699; text-decoration: underline; } label { margin-right: 4px; } input,textarea,select { border: 1px #7F7FB2 solid; font-family: inherit; font-size: 100%; color: #333333; } input[type=text], input[type=password], textarea { padding: 1px; } input.noborder { border: 0; } input[disabled] { color: #888888; } img.normal { border: white solid 1px; } img.alarm { border: red solid 1px; } hr { height: 1px; width: 100%; border: 0; color: #7f7fb2; background-color: #7f7fb2; } /* * Major league table for multiple inputs or presentation */ #content table.major { margin: 4px auto; width: 100%; border-collapse: collapse; } #content table.major tr.highlight { background-color: #eeeeee; } #content table.major thead tr th { padding-top: 6px; padding-bottom: 6px; vertical-align: middle; } #content table.major tfoot td { padding-top: 6px; padding-bottom: 6px; vertical-align: middle; } #content table.major th, #content table.major td { border: 1px solid #7f7fb2; padding: 3px; text-align: left; } #content table.major th { vertical-align: bottom; } #content table.major td { vertical-align: middle; } #content table.major th[scope=row] { padding: 4px 3px 3px; vertical-align: top; text-align: right; } #content table.major .colMark, #content table.major .colSelect { text-align: center; } /* * Lesser table for very simple forms */ #content table.minor { width: 200px; margin: 0 auto; } #content table.minor td { padding: 4px; } #content table.minor .colLeft { width: 50%; text-align: right; } #content table.minor .colRight { width: 50%; text-align: left; } #content table.minor input[type=submit] { margin-top: 4px; padding: 0 2px; font-size: 120%; } /* * Behavior classes */ .error { /*font-family: Verdana, Arial, Helvetica, sans-serif;*/ font-size: 100%; color: #DC143C; font-weight: bold; } .warn { /*font-family: Verdana, Arial, Helvetica, sans-serif;*/ font-size: 100%; color: #FF8C00; font-weight: bold; } .info { /*font-family: Verdana, Arial, Helvetica, sans-serif;*/ font-size: 100%; color: #688E23; font-weight: bold; } .errorText { color: #DC143C; } .warnText { color: #FF8C00; } .infoText { color: #688E23; } .disabledText { font-style: italic; } /* * Generic useful classes, especially with mootools */ .hidden { display: none; } .invisible { visibility: hidden; } .nowrap { white-space: nowrap; } div.clear { clear: both; } /* * Primary layout styles */ #page { width: 100%; } #header { width: 98%; line-height: 24px; margin: 4px auto 0; clear: both; } #header h2 { left: 0; } #headerControl { } #headerButtons { float: right; } #headerButtons a { margin-left: 8px; } #content { width: 98%; margin: 4px auto; line-height: 130%; text-align: center; clear: both; } #contentTable { width: 100%; } #content p { margin-top: 4px; } #content p.textblock { text-align: justify; padding: 4px; } #content > input[type=submit], #content > input[type=button] { margin-top: 4px; } #content table input[type=submit], #content table input[type=button] { margin-top: 0; } #contentButtons { margin: 4px auto 0; } #contentButtons input, #contentButtons a { margin: 0 4px; } #footer { width: 98%; margin: 4px auto 0; clear: both; } ZoneMinder-1.26.5/web/skins/mobile/graphics/000077500000000000000000000000001225361755400206445ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/mobile/graphics/Makefile.am000066400000000000000000000001461225361755400227010ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/skins/mobile/graphics dist_web_DATA = # No files here ZoneMinder-1.26.5/web/skins/mobile/includes/000077500000000000000000000000001225361755400206525ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/mobile/includes/Makefile.am000066400000000000000000000002311225361755400227020ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/skins/mobile/includes dist_web_DATA = \ init.php \ config.php \ functions.php \ control_functions.php ZoneMinder-1.26.5/web/skins/mobile/includes/config.php000066400000000000000000000052521225361755400226340ustar00rootroot00000000000000 "50x", "2000" => "20x", "500" => "5x", "200" => "2x", "100" => $SLANG['Real'], "50" => "1/2x", ); $scales = array( "400" => "4x", "300" => "3x", "200" => "2x", "150" => "1.5x", "100" => $SLANG['Actual'], "75" => "3/4x", "50" => "1/2x", "33" => "1/3x", "25" => "1/4x", ); switch ( $_COOKIE['zmBandwidth'] ) { case "phone" : // Very incomplete at present { define( "ZM_WEB_CAN_STREAM", ZM_WEB_P_CAN_STREAM ); // Override the automatic detection of browser streaming capability define( "ZM_WEB_STREAM_METHOD", ZM_WEB_P_STREAM_METHOD ); // Which method should be used to send video streams to your brow define( "ZM_WEB_DEFAULT_SCALE", ZM_WEB_P_DEFAULT_SCALE ); // What the default scaling factor applied to 'live' or 'event' views is (%) define( "ZM_WEB_DEFAULT_RATE", ZM_WEB_P_DEFAULT_RATE ); // What the default replay rate factor applied to 'event' views is (%) define( "ZM_WEB_VIDEO_BITRATE", ZM_WEB_P_VIDEO_BITRATE ); // What the bitrate of any streamed video should be define( "ZM_WEB_VIDEO_MAXFPS", ZM_WEB_P_VIDEO_MAXFPS ); // What the maximum frame rate of any streamed video should be define( "ZM_WEB_SCALE_THUMBS", ZM_WEB_P_SCALE_THUMBS ); // Image scaling for thumbnails, bandwidth versus cpu in rescaling define( "ZM_WEB_AJAX_TIMEOUT", ZM_WEB_P_AJAX_TIMEOUT ); // Timeout to use for Ajax requests, no timeout used if unset break; } } ?> ZoneMinder-1.26.5/web/skins/mobile/includes/control_functions.php000066400000000000000000000136711225361755400251430ustar00rootroot00000000000000
" name="preset" value=""/>
ZoneMinder-1.26.5/web/skins/mobile/includes/functions.php000066400000000000000000000036251225361755400234010ustar00rootroot00000000000000'."\n" ); ?> <?= ZM_WEB_TITLE_PREFIX ?> - <?= $title ?> ZoneMinder-1.26.5/web/skins/mobile/includes/init.php000066400000000000000000000012251225361755400223260ustar00rootroot00000000000000 ZoneMinder-1.26.5/web/skins/mobile/lang/000077500000000000000000000000001225361755400177655ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/mobile/lang/Makefile.am000066400000000000000000000001421225361755400220160ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/skins/mobile/lang dist_web_DATA = # No files here ZoneMinder-1.26.5/web/skins/mobile/skin.php000066400000000000000000000061071225361755400205250ustar00rootroot00000000000000GetDeviceCapabilitiesFromAgent($_SERVER['HTTP_USER_AGENT']); //print_r( $wurfl->wurfl_agent ); if ( $wurfl->wurfl_agent ) { if ( $wurfl->getDeviceCapability( 'html_wi_oma_xhtmlmp_1_0' ) ) { $device['width'] = $wurfl->getDeviceCapability( 'resolution_width' ); $device['height'] = $wurfl->getDeviceCapability( 'resolution_height' ); } } } else { // This is an example of using fixed device strings to just match your phone etc $devices = array( array( 'name'=>"Motorola V600", 'ua_match'=>"MOT-V600", 'skin'=>"mobile", 'cookies'=>false, 'width'=>176, 'height'=>220 ), ); foreach ( $devices as $tempDevice ) { if ( preg_match( '/'.$tempDevice['ua_match'].'/', $_SERVER['HTTP_USER_AGENT'] ) ) { $skin = $tempDevice['skin']; $cookies = $tempDevice['cookies']; break; } } } foreach ( getSkinIncludes( 'includes/config.php' ) as $includeFile ) require_once $includeFile; foreach ( getSkinIncludes( 'includes/functions.php' ) as $includeFile ) require_once $includeFile; if ( empty($view) ) $view = isset($user)?'console':'login'; if ( !isset($user) && ZM_OPT_USE_AUTH ) { if ( ZM_AUTH_TYPE == "remote" && !empty( $_SERVER['REMOTE_USER'] ) ) { $view = "postlogin"; $action = "login"; $_REQUEST['username'] = $_SERVER['REMOTE_USER']; } else { $view = "login"; } } // If there are additional actions foreach ( getSkinIncludes( 'includes/actions.php' ) as $includeFile ) require_once $includeFile; ?> ZoneMinder-1.26.5/web/skins/mobile/views/000077500000000000000000000000001225361755400202015ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/mobile/views/Makefile.am000066400000000000000000000004541225361755400222400ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu SUBDIRS = \ css webdir = @WEB_PREFIX@/skins/mobile/views dist_web_DATA = \ console.php \ devices.php \ error.php \ eventdetails.php \ event.php \ events.php \ filter.php \ frame.php \ function.php \ login.php \ montage.php \ state.php \ video.php \ watch.php ZoneMinder-1.26.5/web/skins/mobile/views/console.php000066400000000000000000000133251225361755400223600ustar00rootroot00000000000000 array( "terms" => array( array( "attr" => "Archived", "op" => "=", "val" => "0" ), array( "cnj" => "and", "attr" => "DateTime", "op" => ">=", "val" => "-1 hour" ), ) ), ), // Today array( "filter" => array( "terms" => array( array( "attr" => "Archived", "op" => "=", "val" => "0" ), array( "cnj" => "and", "attr" => "DateTime", "op" => ">=", "val" => "today" ), ) ), ), ); $running = daemonCheck(); $status = $running?$SLANG['Running']:$SLANG['Stopped']; if ( $group = dbFetchOne( "select * from Groups where Name = 'Mobile'" ) ) $groupIds = array_flip(explode( ',', $group['MonitorIds'] )); $maxWidth = 0; $maxHeight = 0; $cycleCount = 0; $monitors = dbFetchAll( "select * from Monitors order by Sequence asc" ); for ( $i = 0; $i < count($monitors); $i++ ) { if ( !visibleMonitor( $monitors[$i]['Id'] ) ) { continue; } if ( $group && !empty($groupIds) && !array_key_exists( $monitors[$i]['Id'], $groupIds ) ) { continue; } $monitors[$i]['Show'] = true; $monitors[$i]['zmc'] = zmcStatus( $monitors[$i] ); $monitors[$i]['zma'] = zmaStatus( $monitors[$i] ); $counts = array(); for ( $j = 0; $j < count($eventCounts); $j++ ) { $filter = addFilterTerm( $eventCounts[$j]['filter'], count($eventCounts[$j]['filter']['terms']), array( "cnj" => "and", "attr" => "MonitorId", "op" => "=", "val" => $monitors[$i]['Id'] ) ); parseFilter( $filter, false, '&' ); $counts[] = "count(if(1".$filter['sql'].",1,NULL)) as EventCount$j"; $monitors[$i]['eventCounts'][$j]['filter'] = $filter; } $sql = "select ".join($counts,", ")." from Events as E where MonitorId = '".$monitors[$i]['Id']."'"; $counts = dbFetchOne( $sql ); if ( $monitors[$i]['Function'] != 'None' ) { $cycleCount++; if ( $maxWidth < $monitors[$i]['Width'] ) $maxWidth = $monitors[$i]['Width']; if ( $maxHeight < $monitors[$i]['Height'] ) $maxHeight = $monitors[$i]['Height']; } $monitors[$i] = array_merge( $monitors[$i], $counts ); } xhtmlHeaders( __FILE__, $SLANG['Console'] ); ?>
1 ) { ?>
".substr( $monitor['Function'], 0, 4 )."", canEdit( 'Monitors' ) ) ?>
   
ZoneMinder-1.26.5/web/skins/mobile/views/css/000077500000000000000000000000001225361755400207715ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/mobile/views/css/Makefile.am000066400000000000000000000001451225361755400230250ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/skins/mobile/views/css dist_web_DATA = \ console.css ZoneMinder-1.26.5/web/skins/mobile/views/css/console.css000066400000000000000000000002541225361755400231460ustar00rootroot00000000000000#systemTime { float: left; } #systemState { margin: 0 auto; text-align: center; } #systemStats { float: right; } td.colEvents { text-align: right; } ZoneMinder-1.26.5/web/skins/mobile/views/devices.php000066400000000000000000000042451225361755400223410ustar00rootroot00000000000000

ZoneMinder-1.26.5/web/skins/mobile/views/error.php000066400000000000000000000021531225361755400220440ustar00rootroot00000000000000

ZoneMinder-1.26.5/web/skins/mobile/views/event.php000066400000000000000000000112711225361755400220350ustar00rootroot00000000000000=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn ".($sortOrder=='asc'?'desc':'asc'); $result = dbQuery( $sql ); while ( $row = dbFetchNext( $result ) ) { if ( $row['Id'] == $_REQUEST['eid'] ) { $prevEvent = dbFetchNext( $result ); break; } } $sql = "select E.* from Events as E inner join Monitors as M on E.MonitorId = M.Id where $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn $sortOrder"; $result = dbQuery( $sql ); while ( $row = dbFetchNext( $result ) ) { if ( $row['Id'] == $_REQUEST['eid'] ) { $nextEvent = dbFetchNext( $result ); break; } } $framesPerPage = 15; $framesPerLine = 3; $maxShortcuts = 3; $paged = $event['Frames'] > $framesPerPage; if ( $paged && !empty($_REQUEST['page']) ) { $loFrameId = (($_REQUEST['page']-1)*$framesPerPage)+1; $hiFrameId = min( $_REQUEST['page']*$framesPerPage, $event['Frames'] ); } else { $loFrameId = 1; $hiFrameId = $event['Frames']; } $sql = "select * from Frames where EventID = '".dbEscape($_REQUEST['eid'])."'"; if ( $paged && !empty($_REQUEST['page']) ) $sql .= " and FrameId between $loFrameId and $hiFrameId"; $sql .= " order by FrameId"; $frames = dbFetchAll( $sql ); $scale = getDeviceScale( $event['Width'], $event['Height'], $framesPerLine+0.3 ); $pages = (int)ceil($event['Frames']/$framesPerPage); if ( !empty($_REQUEST['fid']) ) $_REQUEST['page'] = ($_REQUEST['fid']/$framesPerPage)+1; $pagination = getPagination( $pages, $_REQUEST['page'], $maxShortcuts, '&eid='.$_REQUEST['eid'].$filterQuery.$sortQuery, '&' ); xhtmlHeaders( __FILE__, $SLANG['Event'].' - '.$event['Name'] ); ?>

<?= $frame['Type'] ?>/<?= $frame['Type']=='Alarm'?$frame['Score']:0 ?>
ZoneMinder-1.26.5/web/skins/mobile/views/eventdetails.php000066400000000000000000000066561225361755400234160ustar00rootroot00000000000000
s
()
//
1 <?= $frame['FrameId'] ?>
ZoneMinder-1.26.5/web/skins/mobile/views/events.php000066400000000000000000000135431225361755400222240ustar00rootroot00000000000000$deviceLines)?$deviceLines:$limitLeft; } $eventsSql .= " limit $limitStart, $limitAmount"; } elseif ( !empty( $_REQUEST['limit'] ) ) { $eventsSql .= " limit 0, ".$_REQUEST['limit']; } $nEvents = dbFetchOne( $countSql, 'EventCount' ); if ( !empty($limit) && $nEvents > $_REQUEST['limit'] ) { $nEvents = $_REQUEST['limit']; } $pages = (int)ceil($nEvents/$deviceLines); $maxShortcuts = 3; $pagination = getPagination( $pages, $_REQUEST['page'], $maxShortcuts, $filterQuery.$sortQuery.'&limit='.$_REQUEST['limit'], '&' ); xhtmlHeaders( __FILE__, $SLANG['Events'] ); ?>

ZoneMinder-1.26.5/web/skins/mobile/views/filter.php000066400000000000000000000035521225361755400222040ustar00rootroot00000000000000
0 ) { ?>

ZoneMinder-1.26.5/web/skins/mobile/views/frame.php000066400000000000000000000057401225361755400220120ustar00rootroot00000000000000
">
1 ) { ?> << 1 ) { ?> < > >>
ZoneMinder-1.26.5/web/skins/mobile/views/function.php000066400000000000000000000043521225361755400225430ustar00rootroot00000000000000
checked="checked"/>
ZoneMinder-1.26.5/web/skins/mobile/views/login.php000066400000000000000000000036351225361755400220310ustar00rootroot00000000000000
" size="12"/>
ZoneMinder-1.26.5/web/skins/mobile/views/montage.php000066400000000000000000000044461225361755400223540ustar00rootroot00000000000000
<?= $monitor['Name'] ?>
ZoneMinder-1.26.5/web/skins/mobile/views/state.php000066400000000000000000000040041225361755400220300ustar00rootroot00000000000000
ZoneMinder-1.26.5/web/skins/mobile/views/video.php000066400000000000000000000166471225361755400220360ustar00rootroot00000000000000= 352 && $deviceHeight >= 288 ) $videoSize = "352x288"; elseif ( $deviceWidth >= 176 && $deviceHeight >= 144 ) $videoSize = "176x144"; else $videoSize = "128x96"; $eventWidth = $event['Width']; $eventHeight = $event['Height']; if ( !isset( $rate ) ) $_REQUEST['rate'] = reScale( RATE_BASE, $event['DefaultRate'], ZM_WEB_DEFAULT_RATE ); $eventPath = ZM_DIR_EVENTS.'/'.getEventPath( $event ); $videoFormats = array(); $ffmpegFormats = preg_split( '/\s+/', ZM_FFMPEG_FORMATS ); foreach ( $ffmpegFormats as $ffmpegFormat ) { preg_match( '/^([^*]+)(\**)$/', $ffmpegFormat, $matches ); $videoFormats[$matches[1]] = $matches[1]; if ( $matches[2] == '*' ) $defaultVideoFormat = $matches[1]; elseif ( $matches[2] == '**' ) $defaultPhoneFormat = $matches[1]; } if ( !isset($_REQUEST['videoFormat']) ) { if ( isset($defaultPhoneFormat) ) $_REQUEST['videoFormat'] = $defaultPhoneFormat; elseif ( isset($defaultVideoFormat) ) $_REQUEST['videoFormat'] = $defaultVideoFormat; else $videoFormat = $ffmpegFormats[0]; } if ( !empty($_REQUEST['generate']) ) { $videoFile = createVideo( $event, $_REQUEST['videoFormat'], $_REQUEST['rate'], $videoSize, !empty($_REQUEST['overwrite']) ); } $videoFiles = array(); if ( $dir = opendir( $eventPath ) ) { while ( ($file = readdir( $dir )) !== false ) { $file = $eventPath.'/'.$file; if ( is_file( $file ) ) { if ( preg_match( '/-S([\da-z]+)\.(?:'.join( '|', $videoFormats ).')$/', $file, $matches ) ) { if ( $matches[1] == $videoSize ) { $videoFiles[] = $file; } } } } closedir( $dir ); } if ( isset($_REQUEST['download']) ) { header( "Content-type: ".getMimeType($videoFiles[$_REQUEST['download']])); header( "Content-length: ".filesize($videoFiles[$_REQUEST['download']])); header( "Content-disposition: attachment; filename=".preg_replace( "/^.*\//", "", $videoFiles[$_REQUEST['download']] )."; size=".filesize($videoFiles[$_REQUEST['download']]) ); readfile( $videoFiles[$_REQUEST['download']] ); exit; } xhtmlHeaders( __FILE__, $SLANG['Video'].' - '.$event['Name'] ); ?>
checked="checked"/>

0 ) { $index = 0; foreach ( $videoFiles as $file ) { preg_match( '/^(.+)-((?:r[_\d]+)|(?:F[_\d]+))-((?:s[_\d]+)|(?:S[0-9a-z]+))\.([^.]+)$/', $file, $matches ); if ( preg_match( '/^r(.+)$/', $matches[2], $temp_matches ) ) { $rate = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) ); $rateText = isset($rates[$rate])?$rates[$rate]:($rate."x"); } elseif ( preg_match( '/^F(.+)$/', $matches[2], $temp_matches ) ) { $rateText = $temp_matches[1]."fps"; } if ( preg_match( '/^s(.+)$/', $matches[3], $temp_matches ) ) { $scale = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) ); $scaleText = isset($scales[$scale])?$scales[$scale]:($scale."x"); } elseif ( preg_match( '/^S(.+)$/', $matches[3], $temp_matches ) ) { $scaleText = $temp_matches[1]; } ?>
 / 

ZoneMinder-1.26.5/web/skins/mobile/views/watch.php000066400000000000000000000125401225361755400220220ustar00rootroot00000000000000

 -  fps

ZoneMinder-1.26.5/web/skins/xml/000077500000000000000000000000001225361755400163755ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/xml/Makefile.am000066400000000000000000000001651225361755400204330ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/skins/xml SUBDIRS = \ includes \ views dist_web_DATA = \ skin.php ZoneMinder-1.26.5/web/skins/xml/includes/000077500000000000000000000000001225361755400202035ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/xml/includes/Makefile.am000066400000000000000000000001751225361755400222420ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/skins/xml/includes dist_web_DATA = \ init.php \ config.php \ functions.php ZoneMinder-1.26.5/web/skins/xml/includes/config.php000066400000000000000000000244061225361755400221670ustar00rootroot00000000000000 "100x", "5000" => "50x", "2500" => "25x", "1000" => "10x", "400" => "4x", "200" => "2x", "100" => $SLANG['Real'], "50" => "1/2x", "25" => "1/4x", ); $scales = array( "400" => "4x", "300" => "3x", "200" => "2x", "150" => "1.5x", "100" => $SLANG['Actual'], "75" => "3/4x", "50" => "1/2x", "33" => "1/3x", "25" => "1/4x", ); $bwArray = array( "high" => $SLANG['High'], "medium" => $SLANG['Medium'], "low" => $SLANG['Low'] ); /* Check if ZM_WEB_L_CAN_STREAM and ZM_WEB_L_STREAM_METHOD are defined */ if (!defined("ZM_WEB_L_CAN_STREAM")) { define ("ZM_WEB_L_CAN_STREAM", 1); define ("ZM_WEB_M_CAN_STREAM", 1); define ("ZM_WEB_H_CAN_STREAM", 1); } if (!defined("ZM_WEB_L_STREAM_METHOD")) { define ("ZM_WEB_L_STREAM_METHOD", "jpeg"); define ("ZM_WEB_M_STREAM_METHOD", "jpeg"); define ("ZM_WEB_H_STREAM_METHOD", "jpeg"); } switch ( $_COOKIE['zmBandwidth'] ) { case "high" : { define( "ZM_WEB_REFRESH_MAIN", ZM_WEB_H_REFRESH_MAIN ); // How often (in seconds) the main console window refreshes define( "ZM_WEB_REFRESH_CYCLE", ZM_WEB_H_REFRESH_CYCLE ); // How often the cycle watch windows swaps to the next monitor define( "ZM_WEB_REFRESH_IMAGE", ZM_WEB_H_REFRESH_IMAGE ); // How often the watched image is refreshed (if not streaming) define( "ZM_WEB_REFRESH_STATUS", ZM_WEB_H_REFRESH_STATUS ); // How often the little status frame refreshes itself in the watch window define( "ZM_WEB_REFRESH_EVENTS", ZM_WEB_H_REFRESH_EVENTS ); // How often the event listing is refreshed in the watch window, only for recent events define( "ZM_WEB_CAN_STREAM", ZM_WEB_H_CAN_STREAM ); // Override the automatic detection of browser streaming capability define( "ZM_WEB_STREAM_METHOD", ZM_WEB_H_STREAM_METHOD ); // Which method should be used to send video streams to your browser define( "ZM_WEB_DEFAULT_SCALE", ZM_WEB_H_DEFAULT_SCALE ); // What the default scaling factor applied to 'live' or 'event' views is (%) define( "ZM_WEB_DEFAULT_RATE", ZM_WEB_H_DEFAULT_RATE ); // What the default replay rate factor applied to 'event' views is (%) define( "ZM_WEB_VIDEO_BITRATE", ZM_WEB_H_VIDEO_BITRATE ); // What the bitrate of any streamed video should be define( "ZM_WEB_VIDEO_MAXFPS", ZM_WEB_H_VIDEO_MAXFPS ); // What the maximum frame rate of any streamed video should be define( "ZM_WEB_SCALE_THUMBS", ZM_WEB_H_SCALE_THUMBS ); // Image scaling for thumbnails, bandwidth versus cpu in rescaling define( "ZM_WEB_EVENTS_VIEW", ZM_WEB_H_EVENTS_VIEW ); // What the default view of multiple events should be. define( "ZM_WEB_SHOW_PROGRESS", ZM_WEB_H_SHOW_PROGRESS ); // Whether to show the progress of replay in event view. define( "ZM_WEB_AJAX_TIMEOUT", ZM_WEB_H_AJAX_TIMEOUT ); // Timeout to use for Ajax requests, no timeout used if unset break; } case "medium" : { define( "ZM_WEB_REFRESH_MAIN", ZM_WEB_M_REFRESH_MAIN ); // How often (in seconds) the main console window refreshes define( "ZM_WEB_REFRESH_CYCLE", ZM_WEB_M_REFRESH_CYCLE ); // How often the cycle watch windows swaps to the next monitor define( "ZM_WEB_REFRESH_IMAGE", ZM_WEB_M_REFRESH_IMAGE ); // How often the watched image is refreshed (if not streaming) define( "ZM_WEB_REFRESH_STATUS", ZM_WEB_M_REFRESH_STATUS ); // How often the little status frame refreshes itself in the watch window define( "ZM_WEB_REFRESH_EVENTS", ZM_WEB_M_REFRESH_EVENTS ); // How often the event listing is refreshed in the watch window, only for recent events define( "ZM_WEB_CAN_STREAM", ZM_WEB_M_CAN_STREAM ); // Override the automatic detection of browser streaming capability define( "ZM_WEB_STREAM_METHOD", ZM_WEB_M_STREAM_METHOD ); // Which method should be used to send video streams to your browser define( "ZM_WEB_DEFAULT_SCALE", ZM_WEB_M_DEFAULT_SCALE ); // What the default scaling factor applied to 'live' or 'event' views is (%) define( "ZM_WEB_DEFAULT_RATE", ZM_WEB_M_DEFAULT_RATE ); // What the default replay rate factor applied to 'event' views is (%) define( "ZM_WEB_VIDEO_BITRATE", ZM_WEB_M_VIDEO_BITRATE ); // What the bitrate of any streamed video should be define( "ZM_WEB_VIDEO_MAXFPS", ZM_WEB_M_VIDEO_MAXFPS ); // What the maximum frame rate of any streamed video should be define( "ZM_WEB_SCALE_THUMBS", ZM_WEB_M_SCALE_THUMBS ); // Image scaling for thumbnails, bandwidth versus cpu in rescaling define( "ZM_WEB_EVENTS_VIEW", ZM_WEB_M_EVENTS_VIEW ); // What the default view of multiple events should be. define( "ZM_WEB_SHOW_PROGRESS", ZM_WEB_M_SHOW_PROGRESS ); // Whether to show the progress of replay in event view. define( "ZM_WEB_AJAX_TIMEOUT", ZM_WEB_M_AJAX_TIMEOUT ); // Timeout to use for Ajax requests, no timeout used if unset break; } case "low" : { define( "ZM_WEB_REFRESH_MAIN", ZM_WEB_L_REFRESH_MAIN ); // How often (in seconds) the main console window refreshes define( "ZM_WEB_REFRESH_CYCLE", ZM_WEB_L_REFRESH_CYCLE ); // How often the cycle watch windows swaps to the next monitor define( "ZM_WEB_REFRESH_IMAGE", ZM_WEB_L_REFRESH_IMAGE ); // How often the watched image is refreshed (if not streaming) define( "ZM_WEB_REFRESH_STATUS", ZM_WEB_L_REFRESH_STATUS ); // How often the little status frame refreshes itself in the watch window define( "ZM_WEB_REFRESH_EVENTS", ZM_WEB_L_REFRESH_EVENTS ); // How often the event listing is refreshed in the watch window, only for recent events define( "ZM_WEB_CAN_STREAM", ZM_WEB_L_CAN_STREAM ); // Override the automatic detection of browser streaming capability define( "ZM_WEB_STREAM_METHOD", ZM_WEB_L_STREAM_METHOD ); // Which method should be used to send video streams to your browser define( "ZM_WEB_DEFAULT_SCALE", ZM_WEB_L_DEFAULT_SCALE ); // What the default scaling factor applied to 'live' or 'event' views is (%) define( "ZM_WEB_DEFAULT_RATE", ZM_WEB_L_DEFAULT_RATE ); // What the default replay rate factor applied to 'event' views is (%) define( "ZM_WEB_VIDEO_BITRATE", ZM_WEB_L_VIDEO_BITRATE ); // What the bitrate of any streamed video should be define( "ZM_WEB_VIDEO_MAXFPS", ZM_WEB_L_VIDEO_MAXFPS ); // What the maximum frame rate of any streamed video should be define( "ZM_WEB_SCALE_THUMBS", ZM_WEB_L_SCALE_THUMBS ); // Image scaling for thumbnails, bandwidth versus cpu in rescaling define( "ZM_WEB_EVENTS_VIEW", ZM_WEB_L_EVENTS_VIEW ); // What the default view of multiple events should be. define( "ZM_WEB_SHOW_PROGRESS", ZM_WEB_L_SHOW_PROGRESS ); // Whether to show the progress of replay in event view. define( "ZM_WEB_AJAX_TIMEOUT", ZM_WEB_L_AJAX_TIMEOUT ); // Timeout to use for Ajax requests, no timeout used if unset break; } } ?> ZoneMinder-1.26.5/web/skins/xml/includes/functions.php000066400000000000000000000317251225361755400227340ustar00rootroot00000000000000 $maj) return 1; if ((getClientVerMaj() == $maj) && (getClientVerMin() >= $min)) return 1; return 0; } function logXmlErr($str) { logXml($str, 1); } function logXml($str, $err = 0) { if (!defined("ZM_EYEZM_DEBUG")) { /* Check session variable */ if (isset($_SESSION['xml_debug'])) define("ZM_EYEZM_DEBUG", $_SESSION['xml_debug']); else define ("ZM_EYEZM_DEBUG", "0"); } if (!defined("ZM_EYEZM_LOG_TO_FILE")) { /* Check session variable */ if (isset($_SESSION['xml_log_to_file'])) define("ZM_EYEZM_LOG_TO_FILE", $_SESSION['xml_log_to_file']); else define ("ZM_EYEZM_LOG_TO_FILE", "1"); } if (!defined("ZM_EYEZM_LOG_FILE")) { /* Check session variable */ if (isset($_SESSION['xml_log_file'])) define("ZM_EYEZM_LOG_FILE", $_SESSION['xml_log_file']); else define ("ZM_EYEZM_LOG_FILE", "/tmp/zm_xml.log"); } /* Only log if debug is enabled */ if (ZM_EYEZM_DEBUG == 0) return; /* Logging is enabled, set log string */ $logstr = "XML_LOG (".($err?"ERROR":"NOTICE")."): ".$str.(ZM_EYEZM_LOG_TO_FILE?"\n":""); if (ZM_EYEZM_LOG_TO_FILE) { error_log("[".date("r")."] ".$logstr, 3, ZM_EYEZM_LOG_FILE); } else { error_log($logstr); } } /* Returns defval if varname is not set, otherwise return varname */ function getset($varname, $defval) { if (isset($_GET[$varname])) return $_GET[$varname]; return $defval; } function xml_header() { header ("content-type: text/xml"); echo ""; } function xml_tag_val($tag, $val) { echo "<".$tag.">".$val.""; } function xml_tag_sec($tag, $open) { if ($open) $tok = "<"; else $tok = ""; } function xhtmlHeaders( $file, $title ) { ?> /dev/null")) > 0) { /* More than one match */ return TRUE; } else { /* Check -formats tag also if we fail -codecs */ if (preg_match("/\b".$codec."\b/", shell_exec(getFfmpegPath()." -formats 2> /dev/null")) > 0) return TRUE; return FALSE; } } function exeExists($exepath) { $path = trim($exepath); return (file_exists($path) && is_readable($path) && ($path != "")); } /* Returns whether ffmpeg exists or not */ function ffmpegExists() { return exeExists(getFfmpegPath()); } /* Returns with PHP-GD exists */ function gdExists() { if (extension_loaded('gd') && function_exists('gd_info')) { return TRUE; } return FALSE; } function getFfmpeg264FoutParms($br, $fout) { $ffparms = "-analyzeduration 0 -acodec copy"; $ffparms .= " -vcodec libx264 -b ".$br; $ffparms .= " -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8"; $ffparms .= " -subq 5 -trellis 1 -refs 1 -coder 0 -me_range 16 -keyint_min 25"; $ffparms .= " -sc_threshold 40 -i_qfactor 0.71 -bt 16k"; $ffparms .= " -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.6"; $ffparms .= " -qmin 10 -qmax 51 -qdiff 4 -level 30"; $ffparms .= " -g 30 -analyzeduration 0 -async 2 ".$fout." 2> /dev/null"; return $ffparms; } /** Return FFMPEG parameters for H264 streaming */ function getFfmpeg264Str($width, $height, $br, $fin, $fout) { $ffparms = getFfmpeg264FoutParms($br, $fout); $ffstr = getFfmpegPath()." -t ".ZM_EYEZM_H264_MAX_DURATION." -analyzeduration 0 -i "; $ffstr .= $fin." -f mpegts ".$ffparms; return $ffstr; } /** Returns true when monitor exists */ function isMonitor($monitor) { $query = "select Id from Monitors where Id = ".$monitor; $res = dbFetchOne(escapeSql($query)); if ($res) return TRUE; logXml("Monitor ID ".$monitor." does not exist"); return FALSE; } /** Returns the width and height of a monitor */ function getMonitorDims($monitor) { $query = "select Width,Height from Monitors where Id = ".$monitor; $res = dbFetchOne(escapeSql($query)); return $res; } /** Returns the temp directory for H264 encoding */ function getTempDir() { /* Assume that the directory structure is /skins/xml/views */ return dirname(__FILE__)."/../../../temp"; } /** Returns the name of the m3u8 playlist based on monitor */ function m3u8fname($monitor) { return "stream_".$monitor.".m3u8"; } /** Erases the M3u8 and TS file names for a given monitor */ function eraseH264Files($monitor) { /** NOTE: This command executes an 'rm' command, so $monitor parameter * should be properly validated before executing */ /* Remove wdir/.m3u8 and wdir/sample_*.ts */ shell_exec("rm -f ".getTempDir()."/".m3u8fname($monitor)." ".getTempDir()."/sample_".$monitor."*.ts"); } function kill264proc($monitor) { /** NOTE: This command executes an 'kill' command, so $monitor parameter * should be properly validated before executing */ $pid = trim(shell_exec("pgrep -f -x \"zmstreamer -m ".$monitor."\"")); if ($pid == "") { logXml("No PID found for ZMStreamer to kill"); } else { shell_exec("kill -9 ".$pid); logXml("Killed process ".$pid." for Monitor ".$monitor); } } /** Return the command-line shell function to setup H264 stream */ function stream264fn ($mid, $width, $height, $br) { $cdir = "./temp"; $zmstrm = "zmstreamer -m ".$mid." 2> /dev/null"; $ffstr = getFfmpeg264Str($width, $height, $br, "-", "-"); $seg = "segmenter - ".ZM_EYEZM_SEG_DURATION." ".$cdir."/sample_".$mid." ".$cdir."/".m3u8fname($mid)." ../ 2> /dev/null"; $url = $zmstrm . " | ".$ffstr." | " . $seg; return "nohup ".$url." & echo $!"; } /** Generate the web-page presented to the viewer when using H264 */ function h264vidHtml($width, $height, $monitor, $br, $thumbsrc) { function printTermLink() { $str = "H264 Streaming Launching...
Tap to re-load if stream fails"; $str2 = "document.getElementById(\"loaddiv\").innerHTML = \"".$str."\";"; echo $str2; } $ajaxUrl = "?view=actions&action=spawn264&&monitor=".$monitor."&br=".$br; /* Call these two directly to bypass server blocking issues */ $ajax2Url = "./skins/xml/views/actions.php?action=chk264&monitor=".$monitor; $ajax2Url .= "&timeout=".ZM_EYEZM_H264_TIMEOUT; $ajax3Url = "./skins/xml/views/actions.php?action=kill264&monitor=".$monitor; ?>
Initializing H264 Stream ()...
This may take a few seconds
ZoneMinder-1.26.5/web/skins/xml/includes/init.php000066400000000000000000000000111225361755400216470ustar00rootroot00000000000000 ZoneMinder-1.26.5/web/skins/xml/skin.php000066400000000000000000000032171225361755400200550ustar00rootroot00000000000000 ZoneMinder-1.26.5/web/skins/xml/views/000077500000000000000000000000001225361755400175325ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/xml/views/Makefile.am000066400000000000000000000002251225361755400215650ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu SUBDIRS = webdir = @WEB_PREFIX@/skins/xml/views dist_web_DATA = \ console.php \ actions.php \ none.php \ notfound.png ZoneMinder-1.26.5/web/skins/xml/views/actions.php000066400000000000000000000340351225361755400217100ustar00rootroot00000000000000 */ if (!canEdit('Events')) { logXmlErr("User ".$user['Username']. " doesn't have edit Events perms"); exit; } if (!isset($_REQUEST['eid'])) { logXmlErr("EID not set for action delete-event"); exit; } $eid = validInteger($_REQUEST['eid']); $url = "./index.php?view=request&request=event&id=".$eid."&action=delete"; header("Location: ".$url); exit; } else if (!strcmp($action, "spawn264")) { /* ACTION: Spawn 264 streaming process. * Parms: [br|width|height] */ if (!canView('Stream')) { logXmlErr("User ".$user['Username']. " doesn't have view Stream perms"); exit; } if (!isset($_GET['monitor'])) { logXmlErr("Not all parameters specified for spawn264"); exit; } $monitor = validInteger($_REQUEST['monitor']); if (!isMonitor($monitor)) exit; $dims = getMonitorDims($monitor); $width = validInteger(getset('width', $dims['Width'])); $height = validInteger(getset('height', $dims['Height'])); $br = validString(getset('br', ZM_EYEZM_H264_DEFAULT_BR)); /* Check that we can stream first */ if (!canStream264()) { /* canStream264 will print out error */ exit; } $streamUrl = stream264fn($monitor, $width, $height, $br); logXml("Using H264 Pipe Function: ".$streamUrl); $pid = shell_exec($streamUrl); logXml("Streaming Process for monitor ".$monitor." ended, cleaning up files"); eraseH264Files($monitor); exit; } else if (!strcmp($action, "kill264")) { /* ACTION: Kill existing H264 stream process and cleanup files. * Parms: . * NOTE: This will be called directly by path, so include files * may not be available */ session_start(); require_once(dirname(__FILE__)."/../includes/functions.php"); if (!isset($_GET['monitor'])) { logXmlErr("Not all parameters specified for kill264"); exit; } $monitor = validInteger($_GET['monitor']); kill264proc($monitor); logXml("Killed Segmenter process for monitor ".$monitor); exit; } else if (!strcmp($action, "chk264")) { /* ACTION: Simply stalls while checking for 264 file. * Parms: * NOTE: This will be called directly by path, so include files * may not be available */ session_start(); require_once(dirname(__FILE__)."/../includes/functions.php"); if (!isset($_GET['monitor']) || !isset($_GET['timeout'])) { logXmlErr("Monitor not specified for chk264"); exit; } $monitor = validInteger($_GET['monitor']); $path = getTempDir()."/".m3u8fname($monitor); /* Wait for the second sample to become available */ $tsfile = getTempDir()."/sample_".$monitor."-2.ts"; /* Setup timeout */ $startTime = time(); $timeout = validInteger($_GET['timeout']); while (!file_exists($path) || !file_exists($tsfile)) { if (time() > $startTime + $timeout) { logXmlErr("Timed out waiting for stream to start, exiting..."); kill264proc($monitor); exit; } usleep(10000); } logXml("File exists, stream created after ".(time()-$startTime)." sec"); exit; } else if (!strcmp($action, "feed")) { /* ACTION: View a feed. Parms: [height|width|fps|scale|vcodec|br] */ if (!canView('Stream')) { logXmlErr("User ".$user['Username']. " doesn't have view Stream perms"); exit; } /* Check that required variables are set */ if (!isset($_REQUEST['monitor'])) { logXmlErr("Not all parameters set for action view-feed"); exit; } $monitor = validInteger($_REQUEST['monitor']); if (!isMonitor($monitor)) exit; $dims = getMonitorDims($monitor); $width = validInteger(getset('width', $dims['Width'])); $height = validInteger(getset('height', $dims['Height'])); $fps = validInteger(getset('fps', ZM_WEB_VIDEO_MAXFPS)); $scale = validInteger(getset('scale', 100)); $vcodec = validString(getset('vcodec', ZM_EYEZM_FEED_VCODEC)); /* Select which codec we want */ if (!strcmp($vcodec, "h264")) { /* Validate that we can in fact stream H264 */ if (!canStream264()) { /* canStream264 will print out error if * there is one */ echo "Server cannot stream H264. Check eyeZm log for details"; exit; } if (!requireVer("1", "2")) { echo "H264 Streaming requires eyeZm v1.2 or above"; logXmlErr("H264 Streaming requires eyeZm v1.2 or above"); exit; } $br = validString(getset('br', ZM_EYEZM_H264_DEFAULT_BR)); /* H264 processing */ noCacheHeaders(); /* Kill any existing processes and files */ kill264proc($monitor); eraseH264Files($monitor); logXml("Streaming H264 on Monitor ".$monitor.", ".$width."x".$height." @".$br); /* Get thumbnail source */ $thumbsrc = getStreamSrc( array( "mode=single", "monitor=".$monitor, "scale=".$scale, "maxfps=".$fps, "buffer=1000" ) ); logXml("Using thumbnail image from ".$thumbsrc); /* Generate H264 Web-page */ echo "\n"; h264vidHtml($width, $height, $monitor, $br, $thumbsrc); } else if (!strcmp($vcodec, "mjpeg")) { /* MJPEG streaming */ /* If $fps=0, get a single-shot */ if (!$fps) { /* single-shot */ $streamSrc = getStreamSrc( array( "mode=single", "monitor=".$monitor, "scale=".$scale, "maxfps=0", "buffer=1000" ) ); } else { $streamSrc = getStreamSrc( array( "mode=jpeg", "monitor=".$monitor, "scale=".$scale, "maxfps=".$fps, "buffer=1000" ) ); } noCacheHeaders(); xhtmlHeaders( __FILE__, "Stream" ); logXml("Streaming MJPEG on Monitor ".$monitor.", ".$width."x".$height." @".$fps."fps"); echo "\n"; echo "\n"; echo "
\n"; logXml("Using stream source: ".$streamSrc); outputImageStream("liveStream", $streamSrc, $width, $height, "stream"); echo "
"; } else { logXmlErr("Unsupported codec ".$vcodec." selected for streaming"); echo("Unsupported codec ".$vcodec." selected for streaming"); } exit; } else if (!strcmp($action, "vevent")) { /* ACTION: View an event. Parms: [fps|vcodec|br] */ if (!canView('Events')) { logXmlErr("User ".$user['Username']. " doesn't have view Events perms"); exit; } if (!isset($_GET['eid'])) { logXmlErr("Not all parameters set for Action View-event"); exit; } /* Grab event from the database */ $eid = validInteger($_GET['eid']); $eventsSql = "select E.Id, E.MonitorId, E.Name, E.StartTime, E.Length, E.Frames from Events as E where (E.Id = ".$eid.")"; $event = dbFetchOne(escapeSql($eventsSql)); /* Check if exists */ if (!$event) { logxmlErr("Requested event ID ".$eid." does not exist"); exit; } /* Calculate FPS */ $fps = validInteger(getset('fps',ceil($event['Frames'] / $event['Length']))); $vcodec = validString(getset('vcodec', ZM_EYEZM_EVENT_VCODEC)); $baseURL = ZM_PATH_WEB."/".getEventPathSafe($event); /* Here we validate the codec. * Check that FFMPEG exists and supports codecs */ if (!strcmp($vcodec, "mpeg4")) { if (!ffmpegSupportsCodec("mpeg4")) { logXmlErr("FFMPEG not installed, accessible in path/ZM_PATH_FFMPEG, or doesn't support mpeg4"); exit; } /* Can generate, we are good to go */ $fname = "capture.mov"; $ffparms = "-vcodec mpeg4 -r ".ZM_EYEZM_EVENT_FPS." ".$baseURL."/".$fname." 2> /dev/null"; } else if (!strcmp($vcodec, "h264")) { if (!ffmpegSupportsCodec("libx264")) { logXmlErr("FFMPEG not installed, accessible in path/ZM_PATH_FFMPEG, or doesn't support H264"); exit; } if (!requireVer("1","2")) { logXmlErr("H264 Event viewing requires eyeZm v1.2 or greater"); exit; } /* Good to go */ $fname = "capture.mp4"; $ffparms = getFfmpeg264FoutParms( validString(getset('br',ZM_EYEZM_H264_DEFAULT_EVBR)), $baseURL."/".$fname); } else { logXmlErr("Unknown codec ".$vcodec." selected for event viewing"); exit; } logXml("Selected ".$vcodec." for viewing event ".$event['Id']); $fnameOut = $baseURL."/".$fname; $shellCmd = getFfmpegPath()." -y -r ".$fps." -i ".$baseURL."/%0".ZM_EVENT_IMAGE_DIGITS."d-capture.jpg"; $shellCmd .= " ".$ffparms; logXml("Encoding event with command: ".$shellCmd); $shellOutput = shell_exec($shellCmd); /* Check that file exists */ if (!file_exists(trim($fnameOut))) { logXmlErr("Generate Event ".$event['Id']." file ".$fnameOut." does not exist"); exit; } $url = "./".getEventPathSafe($event)."/".$fname; logXml("Loading Event URL ".$url); header("Location: ".$url); exit; } else if (!strcmp($action, "vframe")) { /* ACTION: View a frame given by an event and frame-id. Parms: [alarm | analyze | qty | scale] * If 'alarm' is set, the returned frame will be the -th alarm frame. If 'analyze' is set, * the returned frame will be the %03d-analyse frame instead of %03d-capture, if ZM_CREATE_ANALYSIS_IMAGES * is set. Otherwise it just returns the captured frame. * If qty is set, it will apply a quality factor from 0-100, and if width is set, it will scale the jpeg accordingly */ if (!isset($_GET['eid']) || !isset($_GET['frame'])) { logXmlErr("Not all parameters set for action view-frame"); exit; } $eid = validInteger($_GET['eid']); $frame = validInteger($_GET['frame']); $eventsSql = "select E.Id, E.MonitorId, E.Name, E.StartTime, E.Length, E.Frames from Events as E where (E.Id = ".$eid.")"; $event = dbFetchOne(escapeSql($eventsSql)); $qty = validInteger(getset('qty', 100)); if ($qty > 100) $qty = 100; $scale = validInteger(getset('scale', 100)); if (!$event) { logxmlErr("Requested event ID ".$eid." does not exist"); exit; } /* Figure out the frame number. If 'alarm' is not set, this is just equal to the parameter. * If 'alarm' is set, need to query DB and grab the -th item */ if (isset($_GET['alarm'])) { $frameSql = escapeSql("select * from Frames as F where (F.EventId = ".$eid.") "); $frameSql .= " and (F.Type = 'Alarm') order by F.FrameId"; $i=0; foreach (dbFetchAll($frameSql) as $dbframe) { if ($i == $frame) { $frame = $dbframe['FrameId']; break; } $i++; } } if (isset($_GET['analyze']) && ZM_CREATE_ANALYSIS_IMAGES) { $suffix = "analyse"; } else { $suffix = "capture"; } /* A frame index of 0 is invalid, so if we see this, just use frame 1 */ if (!$frame) $frame = 1; /* Suffix based on 'analyze' */ $fname = sprintf("%0".ZM_EVENT_IMAGE_DIGITS."d-%s.jpg", $frame, $suffix); $url = "./".getEventPathSafe($event)."/".$fname; if (!file_exists($url)) { logXmlErr("Invalid frame image requested: ".$url); $url = "./skins/xml/views/notfound.png"; } /* Check if the image needs any processing - check for GD if requested */ if (($scale != 100) || ($qty < 100)) { if (!gdExists()) { logXmlErr("Lib GD is not loaded, but required for image scaling functions"); $url = "./skins/xml/views/notfound.png"; } else if (!$img = imagecreatefromjpeg($url)) { logXmlErr("Could not load JPEG from ".$url); $url = "./skins/xml/views/notfound.png"; } else { /* GD exists and we read the file ok */ header('Content-type: image/jpeg'); /* Check if resizing is needed */ if ($scale != 100) { list($width_orig, $height_orig) = getimagesize($url); $width_new = $width_orig * ($scale/100); $height_new = $height_orig * ($scale/100); $img_new = imagecreatetruecolor($width_new, $height_new); imagecopyresampled($img_new, $img, 0, 0, 0, 0, $width_new, $height_new, $width_orig, $height_orig); imagejpeg($img_new, NULL, $qty); } else { imagejpeg($img, NULL, $qty); } exit; } } header("Location: ".$url); exit; } else if (!strcmp($action, "state")) { /* ACTION: Change the state of the system. Parms: */ if (!canEdit('System')) { logXmlErr("User ".$user['Username']. " doesn't have edit System perms"); exit; } if (!isset($_GET['state'])) { logXmlErr("Server state not specified for action"); exit; } $url = "./index.php?view=none&action=state&runState=".validString($_GET['state']); header("Location: ".$url); exit; } else if (!strcmp($action, "func")) { /* ACTION: Change state of the monitor. Parms: */ if (!canEdit('Monitors')) { logXmlErr("User ".$user['Username']. " doesn't have monitors Edit perms"); exit; } if (!isset($_GET['mid']) || !isset($_GET['func']) || !isset($_GET['en'])) { logXmlErr("Not all parameters specified for action Monitor state"); exit; } $mid = validInteger($_GET['mid']); if (!isMonitor($mid)) exit; $url = "./index.php?view=none&action=function&mid=".$mid."&newFunction=".validString($_GET['func'])."&newEnabled=".validString($_GET['en']); header("Location: ".$url); exit; } else if (!strcmp($action, "vlog")) { /* ACTION: View log file. Must have debug and log to file enabled, and sufficient perms * Parms: [lines] */ if (!canEdit('System')) { logXmlErr("Insufficient permissions to view log file"); echo "Insufficient permissions to view log file"; exit; } if (!ZM_EYEZM_DEBUG || !ZM_EYEZM_LOG_TO_FILE) { echo "eyeZm Debug (EYEZM_DEBUG) or log-to-file (EYEZM_LOG_TO_FILE) not enabled. Please enable first"; exit; } if (!file_exists(ZM_EYEZM_LOG_FILE)) { echo "Log file ".ZM_EYEZM_LOG_FILE." doesn't exist"; exit; } $lines = validInteger(getset('lines',ZM_EYEZM_LOG_LINES)); logXml("Returning last ".$lines." lines of eyeZm Log from ".ZM_EYEZM_LOG_FILE); echo shell_exec("tail -n ".$lines." ".ZM_EYEZM_LOG_FILE); echo "\n\n--- Showing last ".$lines." lines ---\n"; echo "--- End of Log ---\n\n"; } } ?> ZoneMinder-1.26.5/web/skins/xml/views/console.php000066400000000000000000000226111225361755400217070ustar00rootroot00000000000000 $SLANG['Events'], "filter" => array( "terms" => array( ) ), ), array( "title" => $SLANG['Hour'], "filter" => array( "terms" => array( array( "attr" => "Archived", "op" => "=", "val" => "0" ), array( "cnj" => "and", "attr" => "DateTime", "op" => ">=", "val" => "-1 hour" ), ) ), ), array( "title" => $SLANG['Day'], "filter" => array( "terms" => array( array( "attr" => "Archived", "op" => "=", "val" => "0" ), array( "cnj" => "and", "attr" => "DateTime", "op" => ">=", "val" => "-1 day" ), ) ), ), array( "title" => $SLANG['Week'], "filter" => array( "terms" => array( array( "attr" => "Archived", "op" => "=", "val" => "0" ), array( "cnj" => "and", "attr" => "DateTime", "op" => ">=", "val" => "-7 day" ), ) ), ), array( "title" => $SLANG['Month'], "filter" => array( "terms" => array( array( "attr" => "Archived", "op" => "=", "val" => "0" ), array( "cnj" => "and", "attr" => "DateTime", "op" => ">=", "val" => "-1 month" ), ) ), ), array( "title" => $SLANG['Archived'], "filter" => array( "terms" => array( array( "attr" => "Archived", "op" => "=", "val" => "1" ), ) ), ), ); $running = daemonCheck(); $status = $running?$SLANG['Running']:$SLANG['Stopped']; if ( $group = dbFetchOne( "select * from Groups where Id = '".(empty($_COOKIE['zmGroup'])?0:dbEscape($_COOKIE['zmGroup']))."'" ) ) $groupIds = array_flip(split( ',', $group['MonitorIds'] )); $maxWidth = 0; $maxHeight = 0; $cycleCount = 0; $minSequence = 0; $maxSequence = 1; $seqIdList = array(); $monitors = dbFetchAll( "select * from Monitors order by Sequence asc" ); $displayMonitors = array(); for ( $i = 0; $i < count($monitors); $i++ ) { if ( !visibleMonitor( $monitors[$i]['Id'] ) ) { continue; } if ( $group && !empty($groupIds) && !array_key_exists( $monitors[$i]['Id'], $groupIds ) ) { continue; } $monitors[$i]['Show'] = true; if ( empty($minSequence) || ($monitors[$i]['Sequence'] < $minSequence) ) { $minSequence = $monitors[$i]['Sequence']; } if ( $monitors[$i]['Sequence'] > $maxSequence ) { $maxSequence = $monitors[$i]['Sequence']; } if (isset($_GET['nostatus'])) { $monitors[$i]['zmc'] = 1; $monitors[$i]['zma'] = 1; } else { $monitors[$i]['zmc'] = zmcStatus( $monitors[$i] ); $monitors[$i]['zma'] = zmaStatus( $monitors[$i] ); } $monitors[$i]['ZoneCount'] = dbFetchOne( "select count(Id) as ZoneCount from Zones where MonitorId = '".$monitors[$i]['Id']."'", "ZoneCount" ); $counts = array(); for ( $j = 0; $j < count($eventCounts); $j++ ) { $filter = addFilterTerm( $eventCounts[$j]['filter'], count($eventCounts[$j]['filter']['terms']), array( "cnj" => "and", "attr" => "MonitorId", "op" => "=", "val" => $monitors[$i]['Id'] ) ); parseFilter( $filter ); $counts[] = "count(if(1".$filter['sql'].",1,NULL)) as EventCount$j"; $monitors[$i]['eventCounts'][$j]['filter'] = $filter; } $sql = "select ".join($counts,", ")." from Events as E where MonitorId = '".$monitors[$i]['Id']."'"; $counts = dbFetchOne( $sql ); if ( $monitors[$i]['Function'] != 'None' ) { $cycleCount++; $scaleWidth = reScale( $monitors[$i]['Width'], $monitors[$i]['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); $scaleHeight = reScale( $monitors[$i]['Height'], $monitors[$i]['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); if ( $maxWidth < $scaleWidth ) $maxWidth = $scaleWidth; if ( $maxHeight < $scaleHeight ) $maxHeight = $scaleHeight; } $monitors[$i] = array_merge( $monitors[$i], $counts ); $seqIdList[] = $monitors[$i]['Id']; $displayMonitors[] = $monitors[$i]; } $states = dbFetchAll("select * from States"); /* XML Dump Starts here */ xml_header(); /* Print out the general section */ xml_tag_sec("ZM_XML", 1); xml_tag_sec("GENERAL", 1); xml_tag_val("RUNNING", $running); xml_tag_val("PROTOVER", ZM_EYEZM_PROTOCOL_VERSION); xml_tag_val("FEATURESET", ZM_EYEZM_FEATURE_SET); xml_tag_val("VERSION", ZM_VERSION); xml_tag_val("CANSTR264", canStream264(1)); xml_tag_val("GD", gdExists()); xml_tag_val("FVCODEC", ZM_EYEZM_FEED_VCODEC); xml_tag_val("FVTMT", ZM_EYEZM_H264_TIMEOUT); xml_tag_val("USER", $user['Username']); xml_tag_val("UID", $user['Id']); /* Permissions block */ xml_tag_sec("PERMS", 1); xml_tag_val("STREAM", $user['Stream']); xml_tag_val("EVENTS", $user['Events']); xml_tag_val("CONTROL", $user['Control']); xml_tag_val("MONITORS", $user['Monitors']); xml_tag_val("DEVICES", $user['Devices']); xml_tag_val("SYSTEM", $user['System']); xml_tag_sec("PERMS", 0); /* End permissions block */ if (canEdit('System')) { if ($running) { xml_tag_val("STATE", "stop"); xml_tag_val("STATE", "restart"); } else { xml_tag_val("STATE", "start"); } foreach ($states as $state) { xml_tag_val("STATE", $state['Name']); } } /* End general section */ xml_tag_sec("GENERAL", 0); /* Print out the monitors section */ xml_tag_sec("MONITOR_LIST", 1); foreach( $displayMonitors as $monitor ) { if (!canView('Monitors')) continue; xml_tag_sec("MONITOR", 1); xml_tag_val("ID", $monitor['Id']); xml_tag_val("NAME", $monitor['Name']); xml_tag_val("FUNCTION", $monitor['Function']); xml_tag_val("NUMEVENTS", $monitor['EventCount0']); xml_tag_val("ENABLED", $monitor['Enabled']); xml_tag_val("ZMC", $monitor['zmc']); xml_tag_val("ZMA", $monitor['zma']); xml_tag_val("STATE", ($monitor['zmc']!=1)?"ERROR":( ($monitor['zma']==1)?"OK":"WARN")); xml_tag_val("WIDTH", $monitor['Width']); xml_tag_val("HEIGHT", $monitor['Height']); /* Form the data-base query for this monitor */ $pageOffset = 0; $offset = 0; if (isset($_GET['numEvents'])) { $numEvents = validInteger($_GET['numEvents']); $eventsSql = "select E.Id,E.MonitorId,M.Name As MonitorName,E.Cause,E.Name,E.StartTime,E.Length,E.Frames,E.AlarmFrames,E.TotScore,E.AvgScore,E.MaxScore,E.Archived from Monitors as M inner join Events as E on (M.Id = E.MonitorId) and ( E.MonitorId = ".$monitor['Id']." ) order by E.StartTime desc"; $eventsSql .= " limit ".$numEvents; /* If there is an pageOff tag for this monitor, then retrieve the offset. Otherwise, don't specify offset */ if (isset($_GET['pageOff'.$monitor['Id']])) { /* If pageOffset is greater than we actually have, * we need to adjust it */ $pageOffset = validInteger($_GET['pageOff'.$monitor['Id']]); if ($pageOffset >= ceil($monitor['EventCount0']/$numEvents)) { $pageOffset = 0; } $offset = $pageOffset * $numEvents; } $eventsSql .= " offset ".$offset; } else { unset($eventsSql); } xml_tag_val("PAGEOFF", $pageOffset); xml_tag_sec("EVENTS",1); if (canView('Events') && isset($eventsSql)) { foreach ( dbFetchAll( escapeSql($eventsSql) ) as $event ) { xml_tag_sec("EVENT",1); xml_tag_val("ID",$event['Id']); xml_tag_val("NAME",$event['Name']); xml_tag_val("TIME", strftime( STRF_FMT_DATETIME_SHORTER, strtotime($event['StartTime']))); xml_tag_val("DURATION", $event['Length']); xml_tag_val("FRAMES", $event['Frames']); xml_tag_val("FPS", ($event['Length'] > 0)?ceil($event['Frames']/$event['Length']):0); xml_tag_val("TOTSCORE", $event['TotScore']); xml_tag_val("AVGSCORE", $event['AvgScore']); xml_tag_val("MAXSCORE", $event['MaxScore']); /* Grab the max frame-id from Frames table. If AlarmFrames = 0, don't try * to grab any frames, and just signal the max frame index as index 0 */ $fridx = 1; $alarmFrames = 1; if ($event['AlarmFrames']) { $framesSql = "select FrameId from Frames where (Type = 'Alarm') and (EventId = ".$event['Id'].") order by Score desc limit 1"; $fr = dbFetchOne($framesSql); $fridx = $fr['FrameId']; $alarmFrames = $event['AlarmFrames']; } xml_tag_val("ALARMFRAMES", $alarmFrames); xml_tag_val("MAXFRAMEID", $fridx); xml_tag_sec("EVENT",0); } } xml_tag_sec("EVENTS",0); xml_tag_sec("MONITOR", 0); } xml_tag_sec("MONITOR_LIST", 0); xml_tag_sec("ZM_XML", 0); ?> ZoneMinder-1.26.5/web/skins/xml/views/none.php000066400000000000000000000000001225361755400211700ustar00rootroot00000000000000ZoneMinder-1.26.5/web/skins/xml/views/notfound.png000066400000000000000000000162441225361755400221030ustar00rootroot00000000000000‰PNG  IHDR@ð!½a( pHYs  šœ OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-Û cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFÏIDATxÚìÝMl”×¹ð§Ñ]![BÜllë"ÊŽĆ`¯ Žì6é4s“t“`y02‹8nPVµew… X ­"‹Ê´›@®QX$ml‰¦+C"E•b)Êf“!eÙÜÅp˜™×~=3öL0Éï·A‰`>Î{fÎÿ}ÎÇü,""âû„'4€€€€€€€€€€€€€              €€€€€€€€€€€€€              €€€€€€€€€€€€€              €€€€€€€€€€€€€€             €€ÀÑhOvÜî¸ÑùdçíÎÛÍ{Ü¥¯—ö.íXùîþöûÛËÏSüEñÝ⻋_.þrñ—‹_Þ¸1PûñÞ~ãbÿÅþò/.-.FœÿàÂô…éǯÝÛ·µÝk»ѳ³çóžÏëo‡<=;»?ïþ<¢}[û½ö{+ß­l_Ù±ôõ­½·öþtûw¶›Eûn '¿þÚë¯EôüWÏg=ŸE,ýïÒÓKOGœ¾tæ­3oi@È5ô롳Cg#Ÿ<<x¾y{ìåc王XüòF܈ˆ·ßxûÀÛ":žíØÑ±#b4F¯Œ^‰8ôܡۇnG,sw×Ý]ùµ÷¥ÞBo¡"N-ÆâcÜî)\üóű‹c¦.L\˜Øx ›94s¨Ü¾ ï.ìZØqâÜxŒoÁ@–‚jÞuoU;7ËÕ?\Ý{uoÄÄ;“1ékä‘éëéû[ßß"º‹ÝOt?±tz)–4 ˜)` òàιٖ¿YÞµ\ÇÀž‚À:øÍž›«ÿª >¶×S}Ÿô}RÄŸzsèÍr@j4P¥à—¤JÈV |ÓG§†§†#®¾Öu­+ÿº¶ ¶äy–ÿoùçË?÷ýñ¨¥à×è÷´’ …Þ—zïõV޹±ýÆöˆ‘SÇ®»¾©‡Þ_ħÿ|úÜésů‹ïß+OáÖšBË›ºKSÌ«ÎÿìüWç¿Êÿݶ¿mwÛîˆãÏŸ8>qúÒ™8S_ÀÙÒí“*Ì…W ŸJ¯swìŽXš\Ú·´ïÑÝàþßÇwüw=;{>éù¤|ýÒ”vSOÒ„Ês•l…»¢ÿ4õ§Ñ~––0¤~VëzeÛ«ÑÍõn¢ÊnªÈ^÷T¡Î®½Ë¾¯Tñ¯7h§ÒÔoR¿N×/õùOç^™{euå;=?€Ps ª^ƒ¶Æ¹))Pdà‡PMi@hÛßVh[»B¹n1­=KAªðja¬P½  ªò–ÖÐuDéyûžê;Ôw(âàɃ÷Þk^Å(íÖˆá^=€¥×1pdàöÀ툾K"/ˆf+l·ænýûÖ¿k·KÚ”Ó]ìžéž©ú+»c÷êÕ½Wz¯D >=øþàû#§Fî¬Ñ.©_U\÷ªÇKÁ²wu¿¨õº¿Áùa+Ü)8>yxìpý¬ðliJ¼ðja¸01~tüòøår¥<{½z_êíïí¯úül_l XÕ»‰*]ßô:ÛçÚßo¿¼F5]¿Zï«óÉÎBg!bäÔ±8¶Îë;þüèÄèDÄèäha´P;È÷¾Ô;Ó;³ækªx7tc ?9yk£ÒÞ÷TßDßDíÇÉ«àÍ6ÿÂü +X™_™¯ZV€Ê›8¦cºþÊV­—Lž.©lÐH•—4p¤)¥Ó@—Ö°ÿàB\hJ0Y{êêüÿœÿýùßGL=;53U1À¥ŠÈÈ—k¤ëTHÿÿ\7øUU ³–Ô.iàOí˜þÝÐgk·ËüÍùç_ŒXy£tLÊë§^ïõŠ ð™7μræ•Õ7éX•ˆØ{›Ð¿s¦ÆS¿OÁ£–¹¿Ï™;’?e]üªvѧ@?÷éÜѹ£å~›>_£“¥]ðçA¿«X"‘ÞǦÖxÖ±‰ªês”®{GtÜë¸W¾Þ÷ÿq?îGÄ¥Ó—/ F,~¹8°8P®èf?7)°µŸ+õ»ìBEð«j‡4E_®À¯ßnÙŠf´¨²  ?A¤êN>ï 6+ pyÁhîïW\=Ѿ­ýlûÙˆBTÀZSÍëT(×ø†~}øìá³GÞ¨ÇvüiòOq$ŽTþËÒq5ÅÅþb¹BU^ƒØœ˜7 §öê9ÝóZOE HiñÓÒTm¹]KkÛþ¶þ¶ŠÊPÞîÔ4u— ~)Í~tiüÒøê4ãsmç‡]v•+=iŠ3{ýÓTæà¾Á‰Á5VÅó´ö'§³ÍZ_.åoîÆÝªþYªÌf+~›¨¾8öEUÀšŽér{¶ok­½â:§ví{ªw¶w¶\ñÍûÔ»Æ3¯ê]ê‘ÙÈ©‘OG>­šª~+ÞªzžR¿Ý_„+ιŒQ®€Nޝ¾195rgäNU`¬j·ˆ˜ˆ5‚£Ý¿l%ŽaKËôEŸ°ZÎ6ÿ›ùßÔ4sÖ֨ܳy ÖTlª˜eßWZ“”gpßÀåË«§¬+*S-©¼f×.ÿàüôùéòëÎ{_y{öxœ4˜­P=¬„ÖȲ»ì±3«|fóN³§xmçl@«÷ϼ©÷ìõHjõ³$UJk~~2»Å³A»ÑvHý*ïó—íÿõ®QÌûœdƒY^»M¼31;1[ûówüÓVÙô*€lYykÐÒ}×5Õ’€ÒÀZGå"M]툵7¤×¶¿m²mrõÀ”¦r¿\\X\ˆhßÖþmû·}Oõ-ô-D‡‹¥ö¨Z³–Ú%êܵÜèûÊ®]Jàìdz³£ûK•ޏŽ??ztôhÄÊw+7WnÖp÷ ^¼¼fPùãé?FDÄ@ Ô~ÝáìÔôâÒ⯙©éVÉV¸SЮ¨Ìmðq×^S—*ÌÍþeì T½ŸŸ:>GUŸß¼5“›=Ï3ÍT±82Ð?PQ±N–×ÛnyQ»ÙJTÙ¢0÷Ø–L¡dŸ¯ÖÅ=Ø8¯"öð—GL¥_„8ûÎÙɳ“åÊXZ»”*$“c“'&O4`ɾ¯ŠµKUÒ®Ël…0­ÉË«pe+;ÙJÜFÌlÉÛuY±y'mxP±úaÏ%\ã€à¦Tˆò>?iÍë꺲¹F?§õ~ŽòÞW½›ÂjmFÊû|6Ün™Š¨Ý¿€°Á¼Ñ/úF+%Ù Pk«wj³^i[Þ_ª@¤¿wðäÁ;ï”×Ú5KÅÁµuïT L›C’ÔžÙM5y•¡le¦Ñ RnÏé/› ­jç¼ ½vÈ[S×PÀ¬Ì+*rÕíXç/¼4Úßò7›Ô9Õœ¹îÙöÈ ¼¶[­ç­À0[RÞÀÓê©«zƒÀ:‹Þ7T¡LkÜf?º—Öÿ«iŠ·%?ž»Ñ0Ñã=Þu¼+í]«Ž7É›Z¯¨ÜTMo6H´ª[]¬ª´TáÜ7Ø5ئÈÓH<ø…‘ÍÞ¨åMýç÷ZãFoÝãšòÞO£7”ÝÅîBwaÍ`o÷/[† [Rv€lÕâüîâ­µ–(OÞµ`µüÊf}S±i‘üfH£¿›·©&oãÞü±ÎçM©åõ³¼þž•Ö¢f+ãÙ©Ðzoõëx¸;¹jÓO­þ–ý^¨·½Ö ªUŸóu*uõÇtÌÌìAä±—·¶®US(ykujíòË[ÛVKÞqù—ÖžRKqfìlál¡…°ÆîßÚï¯4pçMáæUv²S†åó KÇåäõ“é£SÃSë×Ò¥)é¼ë˜ Héß§ÇÍ>OÞuÙðG‹+Üyýl蹡™¡™Õï3I¿¸’=¾äáù‹™%yA3˜Òó¥ÇŸ>:=4=´úßÕêoÙë\ÿTs}ÇÔ¬Óno½¹ºÝR¿HŸËìyž­þþ‚Í0Ì–’;5Wç/klàùª~ë¶Þ“úÓëéÞùÞŠ<ý”U ÙJFàoüåFÿþòâù4°}Xü°ða!âÖÜ­=·ö”+ij3{ìJ Ú¿jWìF¹ ½ÿ‹/•6³Ôª„¤Í$éܾÓÍÅÅ=Å=+ß­ìXÙѳ³§«§ku…*ír­ulLªd ÄÀÌ@ÅÖ×N_ëºÖ±üÍòžå=ÝÅîþîþˆ S^¸ðBóÚζs³+yý,ýymgé}.}½Ô¿Ô_®¬u<Û1ÓQÑéu8wâê‰Òçn{l_30½çYf¯[ç“]Õ×kGìX÷¡ª¿U¬‹:úS½7lÙÀ*øÙvK7¤×¶U·[v—u­ë*€P{`¬«¢±YÙŠB½‹ðÓ9xÙ; µ¦ÆÒ€šW)K¯kÕ\ç²5ªâÀæªçÛ覄|S «UYMo:È7[ Jí‘Ú7½Îô÷ÆŽOOU ½®ÔŽÙöO»ª_4©ÿåµs«6Ÿäõ³ì/ad+îé—4ÒõÈ»n)ؤ€œ½AÉöãt½Òf¦unôªlvsFöß×ÚdTo»e/û÷è¥ЈŸ•þøþ{M› ®¥s ÓÚÀT™hôGæ×Y¶kyWí5†[My µô¾­„äíMSͪ¬ÔjÿËùmµÞg£ý¶ÞëÖìëõ¨?ß?–÷…(ü„˜@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Òÿ¬/y ¢ ¡IEND®B`‚ZoneMinder-1.26.5/web/tools/000077500000000000000000000000001225361755400156065ustar00rootroot00000000000000ZoneMinder-1.26.5/web/tools/Makefile.am000066400000000000000000000000561225361755400176430ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu SUBDIRS = \ mootools ZoneMinder-1.26.5/web/tools/mootools/000077500000000000000000000000001225361755400174615ustar00rootroot00000000000000ZoneMinder-1.26.5/web/tools/mootools/CMakeLists.txt000066400000000000000000000043221225361755400222220ustar00rootroot00000000000000# CMakeLists.txt for the Mootools # The only purpose of this file is to create the symlinks required. # Find the latest mootools-core version and create a symlink mootools-core.js to it file(GLOB mtcorelist RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "mootools-core-*.js") if(NOT mtcorelist) message(WARNING " Unable to find mootools-core files") else(NOT mtcorelist) list(SORT mtcorelist) list(LENGTH mtcorelist mtcorelistcount) math(EXPR mtcoreindex "${mtcorelistcount} - 1") list(GET mtcorelist ${mtcoreindex} mtcorelatest) message(STATUS "Using mootools core file: ${mtcorelatest}") execute_process(COMMAND ln -f -s "${mtcorelatest}" "${CMAKE_CURRENT_BINARY_DIR}/mootools-core.js" RESULT_VARIABLE mtcoreresult) if(mtcoreresult) message(WARNING " Failed creating the required symlinks for mootools-core. Exit code: ${mtcoreresult}") endif(mtcoreresult) endif(NOT mtcorelist) # Find the latest mootools-more version and create a symlink mootools-more.js to it file(GLOB mtmorelist RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "mootools-more-*.js") if(NOT mtmorelist) message(WARNING " Unable to find mootools-more files") else(NOT mtmorelist) list(SORT mtmorelist) list(LENGTH mtmorelist mtmorelistcount) math(EXPR mtmoreindex "${mtmorelistcount} - 1") list(GET mtmorelist ${mtmoreindex} mtmorelatest) message(STATUS "Using mootools more file: ${mtmorelatest}") execute_process(COMMAND ln -f -s "${mtmorelatest}" "${CMAKE_CURRENT_BINARY_DIR}/mootools-more.js" RESULT_VARIABLE mtmoreresult) if(mtmoreresult) message(WARNING " Failed creating the required symlinks for mootools-more. Exit code: ${mtmoreresult}") endif(mtmoreresult) endif(NOT mtmorelist) # If this is an out-of-source build, copy the mootools files we picked to the binary directory # This is required to fix a cmake bug regarding installing symlinks pointing to nonexistent files if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/${mtcorelatest}" "${CMAKE_CURRENT_BINARY_DIR}/${mtcorelatest}") execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/${mtmorelatest}" "${CMAKE_CURRENT_BINARY_DIR}/${mtmorelatest}") endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) ZoneMinder-1.26.5/web/tools/mootools/Makefile.am000066400000000000000000000010461225361755400215160ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/tools/mootools dist_web_DATA = \ mootools-core-1.3.2-nc.js \ mootools-core-1.3.2-yc.js \ mootools-more-1.3.2.1-nc.js \ mootools-more-1.3.2.1-yc.js # Yes, you are correct. This is a HACK! install-data-hook: ( cd $(DESTDIR)$(webdir); rm -f mootools-core.js mootools-more.js ) ( cd $(DESTDIR)$(webdir); ln -s mootools-core-1.3.2-yc.js mootools-core.js ) ( cd $(DESTDIR)$(webdir); ln -s mootools-more-1.3.2.1-yc.js mootools-more.js ) uninstall-hook: @-( cd $(DESTDIR)$(webdir); rm -f mootools-* ) ZoneMinder-1.26.5/web/tools/mootools/mootools-core-1.3.2-nc.js000066400000000000000000004135551225361755400236720ustar00rootroot00000000000000/* --- MooTools: the javascript framework web build: - http://mootools.net/core/7c56cfef9dddcf170a5d68e3fb61cfd7 packager build: - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady Core/Swiff /* --- name: Core description: The heart of MooTools. license: MIT-style license. copyright: Copyright (c) 2006-2010 [Valerio Proietti](http://mad4milk.net/). authors: The MooTools production team (http://mootools.net/developers/) inspiration: - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php) - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php) provides: [Core, MooTools, Type, typeOf, instanceOf, Native] ... */ (function(){ this.MooTools = { version: '1.3.2', build: 'c9f1ff10e9e7facb65e9481049ed1b450959d587' }; // typeOf, instanceOf var typeOf = this.typeOf = function(item){ if (item == null) return 'null'; if (item.$family) return item.$family(); if (item.nodeName){ if (item.nodeType == 1) return 'element'; if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace'; } else if (typeof item.length == 'number'){ if (item.callee) return 'arguments'; if ('item' in item) return 'collection'; } return typeof item; }; var instanceOf = this.instanceOf = function(item, object){ if (item == null) return false; var constructor = item.$constructor || item.constructor; while (constructor){ if (constructor === object) return true; constructor = constructor.parent; } return item instanceof object; }; // Function overloading var Function = this.Function; var enumerables = true; for (var i in {toString: 1}) enumerables = null; if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor']; Function.prototype.overloadSetter = function(usePlural){ var self = this; return function(a, b){ if (a == null) return this; if (usePlural || typeof a != 'string'){ for (var k in a) self.call(this, k, a[k]); if (enumerables) for (var i = enumerables.length; i--;){ k = enumerables[i]; if (a.hasOwnProperty(k)) self.call(this, k, a[k]); } } else { self.call(this, a, b); } return this; }; }; Function.prototype.overloadGetter = function(usePlural){ var self = this; return function(a){ var args, result; if (usePlural || typeof a != 'string') args = a; else if (arguments.length > 1) args = arguments; if (args){ result = {}; for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]); } else { result = self.call(this, a); } return result; }; }; Function.prototype.extend = function(key, value){ this[key] = value; }.overloadSetter(); Function.prototype.implement = function(key, value){ this.prototype[key] = value; }.overloadSetter(); // From var slice = Array.prototype.slice; Function.from = function(item){ return (typeOf(item) == 'function') ? item : function(){ return item; }; }; Array.from = function(item){ if (item == null) return []; return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item]; }; Number.from = function(item){ var number = parseFloat(item); return isFinite(number) ? number : null; }; String.from = function(item){ return item + ''; }; // hide, protect Function.implement({ hide: function(){ this.$hidden = true; return this; }, protect: function(){ this.$protected = true; return this; } }); // Type var Type = this.Type = function(name, object){ if (name){ var lower = name.toLowerCase(); var typeCheck = function(item){ return (typeOf(item) == lower); }; Type['is' + name] = typeCheck; if (object != null){ object.prototype.$family = (function(){ return lower; }).hide(); } } if (object == null) return null; object.extend(this); object.$constructor = Type; object.prototype.$constructor = object; return object; }; var toString = Object.prototype.toString; Type.isEnumerable = function(item){ return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' ); }; var hooks = {}; var hooksOf = function(object){ var type = typeOf(object.prototype); return hooks[type] || (hooks[type] = []); }; var implement = function(name, method){ if (method && method.$hidden) return; var hooks = hooksOf(this); for (var i = 0; i < hooks.length; i++){ var hook = hooks[i]; if (typeOf(hook) == 'type') implement.call(hook, name, method); else hook.call(this, name, method); } var previous = this.prototype[name]; if (previous == null || !previous.$protected) this.prototype[name] = method; if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){ return method.apply(item, slice.call(arguments, 1)); }); }; var extend = function(name, method){ if (method && method.$hidden) return; var previous = this[name]; if (previous == null || !previous.$protected) this[name] = method; }; Type.implement({ implement: implement.overloadSetter(), extend: extend.overloadSetter(), alias: function(name, existing){ implement.call(this, name, this.prototype[existing]); }.overloadSetter(), mirror: function(hook){ hooksOf(this).push(hook); return this; } }); new Type('Type', Type); // Default Types var force = function(name, object, methods){ var isType = (object != Object), prototype = object.prototype; if (isType) object = new Type(name, object); for (var i = 0, l = methods.length; i < l; i++){ var key = methods[i], generic = object[key], proto = prototype[key]; if (generic) generic.protect(); if (isType && proto){ delete prototype[key]; prototype[key] = proto.protect(); } } if (isType) object.implement(prototype); return force; }; force('String', String, [ 'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search', 'slice', 'split', 'substr', 'substring', 'toLowerCase', 'toUpperCase' ])('Array', Array, [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight' ])('Number', Number, [ 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision' ])('Function', Function, [ 'apply', 'call', 'bind' ])('RegExp', RegExp, [ 'exec', 'test' ])('Object', Object, [ 'create', 'defineProperty', 'defineProperties', 'keys', 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames', 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen' ])('Date', Date, ['now']); Object.extend = extend.overloadSetter(); Date.extend('now', function(){ return +(new Date); }); new Type('Boolean', Boolean); // fixes NaN returning as Number Number.prototype.$family = function(){ return isFinite(this) ? 'number' : 'null'; }.hide(); // Number.random Number.extend('random', function(min, max){ return Math.floor(Math.random() * (max - min + 1) + min); }); // forEach, each var hasOwnProperty = Object.prototype.hasOwnProperty; Object.extend('forEach', function(object, fn, bind){ for (var key in object){ if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object); } }); Object.each = Object.forEach; Array.implement({ forEach: function(fn, bind){ for (var i = 0, l = this.length; i < l; i++){ if (i in this) fn.call(bind, this[i], i, this); } }, each: function(fn, bind){ Array.forEach(this, fn, bind); return this; } }); // Array & Object cloning, Object merging and appending var cloneOf = function(item){ switch (typeOf(item)){ case 'array': return item.clone(); case 'object': return Object.clone(item); default: return item; } }; Array.implement('clone', function(){ var i = this.length, clone = new Array(i); while (i--) clone[i] = cloneOf(this[i]); return clone; }); var mergeOne = function(source, key, current){ switch (typeOf(current)){ case 'object': if (typeOf(source[key]) == 'object') Object.merge(source[key], current); else source[key] = Object.clone(current); break; case 'array': source[key] = current.clone(); break; default: source[key] = current; } return source; }; Object.extend({ merge: function(source, k, v){ if (typeOf(k) == 'string') return mergeOne(source, k, v); for (var i = 1, l = arguments.length; i < l; i++){ var object = arguments[i]; for (var key in object) mergeOne(source, key, object[key]); } return source; }, clone: function(object){ var clone = {}; for (var key in object) clone[key] = cloneOf(object[key]); return clone; }, append: function(original){ for (var i = 1, l = arguments.length; i < l; i++){ var extended = arguments[i] || {}; for (var key in extended) original[key] = extended[key]; } return original; } }); // Object-less types ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){ new Type(name); }); // Unique ID var UID = Date.now(); String.extend('uniqueID', function(){ return (UID++).toString(36); }); })(); /* --- name: Array description: Contains Array Prototypes like each, contains, and erase. license: MIT-style license. requires: Type provides: Array ... */ Array.implement({ /**/ every: function(fn, bind){ for (var i = 0, l = this.length; i < l; i++){ if ((i in this) && !fn.call(bind, this[i], i, this)) return false; } return true; }, filter: function(fn, bind){ var results = []; for (var i = 0, l = this.length; i < l; i++){ if ((i in this) && fn.call(bind, this[i], i, this)) results.push(this[i]); } return results; }, indexOf: function(item, from){ var len = this.length; for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){ if (this[i] === item) return i; } return -1; }, map: function(fn, bind){ var results = []; for (var i = 0, l = this.length; i < l; i++){ if (i in this) results[i] = fn.call(bind, this[i], i, this); } return results; }, some: function(fn, bind){ for (var i = 0, l = this.length; i < l; i++){ if ((i in this) && fn.call(bind, this[i], i, this)) return true; } return false; }, /**/ clean: function(){ return this.filter(function(item){ return item != null; }); }, invoke: function(methodName){ var args = Array.slice(arguments, 1); return this.map(function(item){ return item[methodName].apply(item, args); }); }, associate: function(keys){ var obj = {}, length = Math.min(this.length, keys.length); for (var i = 0; i < length; i++) obj[keys[i]] = this[i]; return obj; }, link: function(object){ var result = {}; for (var i = 0, l = this.length; i < l; i++){ for (var key in object){ if (object[key](this[i])){ result[key] = this[i]; delete object[key]; break; } } } return result; }, contains: function(item, from){ return this.indexOf(item, from) != -1; }, append: function(array){ this.push.apply(this, array); return this; }, getLast: function(){ return (this.length) ? this[this.length - 1] : null; }, getRandom: function(){ return (this.length) ? this[Number.random(0, this.length - 1)] : null; }, include: function(item){ if (!this.contains(item)) this.push(item); return this; }, combine: function(array){ for (var i = 0, l = array.length; i < l; i++) this.include(array[i]); return this; }, erase: function(item){ for (var i = this.length; i--;){ if (this[i] === item) this.splice(i, 1); } return this; }, empty: function(){ this.length = 0; return this; }, flatten: function(){ var array = []; for (var i = 0, l = this.length; i < l; i++){ var type = typeOf(this[i]); if (type == 'null') continue; array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]); } return array; }, pick: function(){ for (var i = 0, l = this.length; i < l; i++){ if (this[i] != null) return this[i]; } return null; }, hexToRgb: function(array){ if (this.length != 3) return null; var rgb = this.map(function(value){ if (value.length == 1) value += value; return value.toInt(16); }); return (array) ? rgb : 'rgb(' + rgb + ')'; }, rgbToHex: function(array){ if (this.length < 3) return null; if (this.length == 4 && this[3] == 0 && !array) return 'transparent'; var hex = []; for (var i = 0; i < 3; i++){ var bit = (this[i] - 0).toString(16); hex.push((bit.length == 1) ? '0' + bit : bit); } return (array) ? hex : '#' + hex.join(''); } }); /* --- name: String description: Contains String Prototypes like camelCase, capitalize, test, and toInt. license: MIT-style license. requires: Type provides: String ... */ String.implement({ test: function(regex, params){ return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this); }, contains: function(string, separator){ return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1; }, trim: function(){ return this.replace(/^\s+|\s+$/g, ''); }, clean: function(){ return this.replace(/\s+/g, ' ').trim(); }, camelCase: function(){ return this.replace(/-\D/g, function(match){ return match.charAt(1).toUpperCase(); }); }, hyphenate: function(){ return this.replace(/[A-Z]/g, function(match){ return ('-' + match.charAt(0).toLowerCase()); }); }, capitalize: function(){ return this.replace(/\b[a-z]/g, function(match){ return match.toUpperCase(); }); }, escapeRegExp: function(){ return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1'); }, toInt: function(base){ return parseInt(this, base || 10); }, toFloat: function(){ return parseFloat(this); }, hexToRgb: function(array){ var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); return (hex) ? hex.slice(1).hexToRgb(array) : null; }, rgbToHex: function(array){ var rgb = this.match(/\d{1,3}/g); return (rgb) ? rgb.rgbToHex(array) : null; }, substitute: function(object, regexp){ return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){ if (match.charAt(0) == '\\') return match.slice(1); return (object[name] != null) ? object[name] : ''; }); } }); /* --- name: Number description: Contains Number Prototypes like limit, round, times, and ceil. license: MIT-style license. requires: Type provides: Number ... */ Number.implement({ limit: function(min, max){ return Math.min(max, Math.max(min, this)); }, round: function(precision){ precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0); return Math.round(this * precision) / precision; }, times: function(fn, bind){ for (var i = 0; i < this; i++) fn.call(bind, i, this); }, toFloat: function(){ return parseFloat(this); }, toInt: function(base){ return parseInt(this, base || 10); } }); Number.alias('each', 'times'); (function(math){ var methods = {}; math.each(function(name){ if (!Number[name]) methods[name] = function(){ return Math[name].apply(null, [this].concat(Array.from(arguments))); }; }); Number.implement(methods); })(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']); /* --- name: Function description: Contains Function Prototypes like create, bind, pass, and delay. license: MIT-style license. requires: Type provides: Function ... */ Function.extend({ attempt: function(){ for (var i = 0, l = arguments.length; i < l; i++){ try { return arguments[i](); } catch (e){} } return null; } }); Function.implement({ attempt: function(args, bind){ try { return this.apply(bind, Array.from(args)); } catch (e){} return null; }, /**/ bind: function(bind){ var self = this, args = (arguments.length > 1) ? Array.slice(arguments, 1) : null; return function(){ if (!args && !arguments.length) return self.call(bind); if (args && arguments.length) return self.apply(bind, args.concat(Array.from(arguments))); return self.apply(bind, args || arguments); }; }, /**/ pass: function(args, bind){ var self = this; if (args != null) args = Array.from(args); return function(){ return self.apply(bind, args || arguments); }; }, delay: function(delay, bind, args){ return setTimeout(this.pass((args == null ? [] : args), bind), delay); }, periodical: function(periodical, bind, args){ return setInterval(this.pass((args == null ? [] : args), bind), periodical); } }); /* --- name: Object description: Object generic methods license: MIT-style license. requires: Type provides: [Object, Hash] ... */ (function(){ var hasOwnProperty = Object.prototype.hasOwnProperty; Object.extend({ subset: function(object, keys){ var results = {}; for (var i = 0, l = keys.length; i < l; i++){ var k = keys[i]; if (k in object) results[k] = object[k]; } return results; }, map: function(object, fn, bind){ var results = {}; for (var key in object){ if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object); } return results; }, filter: function(object, fn, bind){ var results = {}; for (var key in object){ var value = object[key]; if (hasOwnProperty.call(object, key) && fn.call(bind, value, key, object)) results[key] = value; } return results; }, every: function(object, fn, bind){ for (var key in object){ if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false; } return true; }, some: function(object, fn, bind){ for (var key in object){ if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true; } return false; }, keys: function(object){ var keys = []; for (var key in object){ if (hasOwnProperty.call(object, key)) keys.push(key); } return keys; }, values: function(object){ var values = []; for (var key in object){ if (hasOwnProperty.call(object, key)) values.push(object[key]); } return values; }, getLength: function(object){ return Object.keys(object).length; }, keyOf: function(object, value){ for (var key in object){ if (hasOwnProperty.call(object, key) && object[key] === value) return key; } return null; }, contains: function(object, value){ return Object.keyOf(object, value) != null; }, toQueryString: function(object, base){ var queryString = []; Object.each(object, function(value, key){ if (base) key = base + '[' + key + ']'; var result; switch (typeOf(value)){ case 'object': result = Object.toQueryString(value, key); break; case 'array': var qs = {}; value.each(function(val, i){ qs[i] = val; }); result = Object.toQueryString(qs, key); break; default: result = key + '=' + encodeURIComponent(value); } if (value != null) queryString.push(result); }); return queryString.join('&'); } }); })(); /* --- name: Browser description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash. license: MIT-style license. requires: [Array, Function, Number, String] provides: [Browser, Window, Document] ... */ (function(){ var document = this.document; var window = document.window = this; var UID = 1; this.$uid = (window.ActiveXObject) ? function(item){ return (item.uid || (item.uid = [UID++]))[0]; } : function(item){ return item.uid || (item.uid = UID++); }; $uid(window); $uid(document); var ua = navigator.userAgent.toLowerCase(), platform = navigator.platform.toLowerCase(), UA = ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, 'unknown', 0], mode = UA[1] == 'ie' && document.documentMode; var Browser = this.Browser = { extend: Function.prototype.extend, name: (UA[1] == 'version') ? UA[3] : UA[1], version: mode || parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]), Platform: { name: ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0] }, Features: { xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector), json: !!(window.JSON) }, Plugins: {} }; Browser[Browser.name] = true; Browser[Browser.name + parseInt(Browser.version, 10)] = true; Browser.Platform[Browser.Platform.name] = true; // Request Browser.Request = (function(){ var XMLHTTP = function(){ return new XMLHttpRequest(); }; var MSXML2 = function(){ return new ActiveXObject('MSXML2.XMLHTTP'); }; var MSXML = function(){ return new ActiveXObject('Microsoft.XMLHTTP'); }; return Function.attempt(function(){ XMLHTTP(); return XMLHTTP; }, function(){ MSXML2(); return MSXML2; }, function(){ MSXML(); return MSXML; }); })(); Browser.Features.xhr = !!(Browser.Request); // Flash detection var version = (Function.attempt(function(){ return navigator.plugins['Shockwave Flash'].description; }, function(){ return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); }) || '0 r0').match(/\d+/g); Browser.Plugins.Flash = { version: Number(version[0] || '0.' + version[1]) || 0, build: Number(version[2]) || 0 }; // String scripts Browser.exec = function(text){ if (!text) return text; if (window.execScript){ window.execScript(text); } else { var script = document.createElement('script'); script.setAttribute('type', 'text/javascript'); script.text = text; document.head.appendChild(script); document.head.removeChild(script); } return text; }; String.implement('stripScripts', function(exec){ var scripts = ''; var text = this.replace(/]*>([\s\S]*?)<\/script>/gi, function(all, code){ scripts += code + '\n'; return ''; }); if (exec === true) Browser.exec(scripts); else if (typeOf(exec) == 'function') exec(scripts, text); return text; }); // Window, Document Browser.extend({ Document: this.Document, Window: this.Window, Element: this.Element, Event: this.Event }); this.Window = this.$constructor = new Type('Window', function(){}); this.$family = Function.from('window').hide(); Window.mirror(function(name, method){ window[name] = method; }); this.Document = document.$constructor = new Type('Document', function(){}); document.$family = Function.from('document').hide(); Document.mirror(function(name, method){ document[name] = method; }); document.html = document.documentElement; if (!document.head) document.head = document.getElementsByTagName('head')[0]; if (document.execCommand) try { document.execCommand("BackgroundImageCache", false, true); } catch (e){} /**/ if (this.attachEvent && !this.addEventListener){ var unloadEvent = function(){ this.detachEvent('onunload', unloadEvent); document.head = document.html = document.window = null; }; this.attachEvent('onunload', unloadEvent); } // IE fails on collections and ) var arrayFrom = Array.from; try { arrayFrom(document.html.childNodes); } catch(e){ Array.from = function(item){ if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array'){ var i = item.length, array = new Array(i); while (i--) array[i] = item[i]; return array; } return arrayFrom(item); }; var prototype = Array.prototype, slice = prototype.slice; ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name){ var method = prototype[name]; Array[name] = function(item){ return method.apply(Array.from(item), slice.call(arguments, 1)); }; }); } /**/ })(); /* --- name: Event description: Contains the Event Class, to make the event object cross-browser. license: MIT-style license. requires: [Window, Document, Array, Function, String, Object] provides: Event ... */ var Event = new Type('Event', function(event, win){ if (!win) win = window; var doc = win.document; event = event || win.event; if (event.$extended) return event; this.$extended = true; var type = event.type, target = event.target || event.srcElement, page = {}, client = {}, related = null, rightClick, wheel, code, key; while (target && target.nodeType == 3) target = target.parentNode; if (type.indexOf('key') != -1){ code = event.which || event.keyCode; key = Object.keyOf(Event.Keys, code); if (type == 'keydown'){ var fKey = code - 111; if (fKey > 0 && fKey < 13) key = 'f' + fKey; } if (!key) key = String.fromCharCode(code).toLowerCase(); } else if ((/click|mouse|menu/i).test(type)){ doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body; page = { x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft, y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop }; client = { x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX, y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY }; if ((/DOMMouseScroll|mousewheel/).test(type)){ wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3; } rightClick = (event.which == 3) || (event.button == 2); if ((/over|out/).test(type)){ related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element']; var testRelated = function(){ while (related && related.nodeType == 3) related = related.parentNode; return true; }; var hasRelated = (Browser.firefox2) ? testRelated.attempt() : testRelated(); related = (hasRelated) ? related : null; } } else if ((/gesture|touch/i).test(type)){ this.rotation = event.rotation; this.scale = event.scale; this.targetTouches = event.targetTouches; this.changedTouches = event.changedTouches; var touches = this.touches = event.touches; if (touches && touches[0]){ var touch = touches[0]; page = {x: touch.pageX, y: touch.pageY}; client = {x: touch.clientX, y: touch.clientY}; } } return Object.append(this, { event: event, type: type, page: page, client: client, rightClick: rightClick, wheel: wheel, relatedTarget: document.id(related), target: document.id(target), code: code, key: key, shift: event.shiftKey, control: event.ctrlKey, alt: event.altKey, meta: event.metaKey }); }); Event.Keys = { 'enter': 13, 'up': 38, 'down': 40, 'left': 37, 'right': 39, 'esc': 27, 'space': 32, 'backspace': 8, 'tab': 9, 'delete': 46 }; Event.implement({ stop: function(){ return this.stopPropagation().preventDefault(); }, stopPropagation: function(){ if (this.event.stopPropagation) this.event.stopPropagation(); else this.event.cancelBubble = true; return this; }, preventDefault: function(){ if (this.event.preventDefault) this.event.preventDefault(); else this.event.returnValue = false; return this; } }); /* --- name: Class description: Contains the Class Function for easily creating, extending, and implementing reusable Classes. license: MIT-style license. requires: [Array, String, Function, Number] provides: Class ... */ (function(){ var Class = this.Class = new Type('Class', function(params){ if (instanceOf(params, Function)) params = {initialize: params}; var newClass = function(){ reset(this); if (newClass.$prototyping) return this; this.$caller = null; var value = (this.initialize) ? this.initialize.apply(this, arguments) : this; this.$caller = this.caller = null; return value; }.extend(this).implement(params); newClass.$constructor = Class; newClass.prototype.$constructor = newClass; newClass.prototype.parent = parent; return newClass; }); var parent = function(){ if (!this.$caller) throw new Error('The method "parent" cannot be called.'); var name = this.$caller.$name, parent = this.$caller.$owner.parent, previous = (parent) ? parent.prototype[name] : null; if (!previous) throw new Error('The method "' + name + '" has no parent.'); return previous.apply(this, arguments); }; var reset = function(object){ for (var key in object){ var value = object[key]; switch (typeOf(value)){ case 'object': var F = function(){}; F.prototype = value; object[key] = reset(new F); break; case 'array': object[key] = value.clone(); break; } } return object; }; var wrap = function(self, key, method){ if (method.$origin) method = method.$origin; var wrapper = function(){ if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.'); var caller = this.caller, current = this.$caller; this.caller = current; this.$caller = wrapper; var result = method.apply(this, arguments); this.$caller = current; this.caller = caller; return result; }.extend({$owner: self, $origin: method, $name: key}); return wrapper; }; var implement = function(key, value, retain){ if (Class.Mutators.hasOwnProperty(key)){ value = Class.Mutators[key].call(this, value); if (value == null) return this; } if (typeOf(value) == 'function'){ if (value.$hidden) return this; this.prototype[key] = (retain) ? value : wrap(this, key, value); } else { Object.merge(this.prototype, key, value); } return this; }; var getInstance = function(klass){ klass.$prototyping = true; var proto = new klass; delete klass.$prototyping; return proto; }; Class.implement('implement', implement.overloadSetter()); Class.Mutators = { Extends: function(parent){ this.parent = parent; this.prototype = getInstance(parent); }, Implements: function(items){ Array.from(items).each(function(item){ var instance = new item; for (var key in instance) implement.call(this, key, instance[key], true); }, this); } }; })(); /* --- name: Class.Extras description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks. license: MIT-style license. requires: Class provides: [Class.Extras, Chain, Events, Options] ... */ (function(){ this.Chain = new Class({ $chain: [], chain: function(){ this.$chain.append(Array.flatten(arguments)); return this; }, callChain: function(){ return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false; }, clearChain: function(){ this.$chain.empty(); return this; } }); var removeOn = function(string){ return string.replace(/^on([A-Z])/, function(full, first){ return first.toLowerCase(); }); }; this.Events = new Class({ $events: {}, addEvent: function(type, fn, internal){ type = removeOn(type); this.$events[type] = (this.$events[type] || []).include(fn); if (internal) fn.internal = true; return this; }, addEvents: function(events){ for (var type in events) this.addEvent(type, events[type]); return this; }, fireEvent: function(type, args, delay){ type = removeOn(type); var events = this.$events[type]; if (!events) return this; args = Array.from(args); events.each(function(fn){ if (delay) fn.delay(delay, this, args); else fn.apply(this, args); }, this); return this; }, removeEvent: function(type, fn){ type = removeOn(type); var events = this.$events[type]; if (events && !fn.internal){ var index = events.indexOf(fn); if (index != -1) delete events[index]; } return this; }, removeEvents: function(events){ var type; if (typeOf(events) == 'object'){ for (type in events) this.removeEvent(type, events[type]); return this; } if (events) events = removeOn(events); for (type in this.$events){ if (events && events != type) continue; var fns = this.$events[type]; for (var i = fns.length; i--;) if (i in fns){ this.removeEvent(type, fns[i]); } } return this; } }); this.Options = new Class({ setOptions: function(){ var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments)); if (this.addEvent) for (var option in options){ if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue; this.addEvent(option, options[option]); delete options[option]; } return this; } }); })(); /* --- name: Slick.Parser description: Standalone CSS3 Selector parser provides: Slick.Parser ... */ ;(function(){ var parsed, separatorIndex, combinatorIndex, reversed, cache = {}, reverseCache = {}, reUnescape = /\\/g; var parse = function(expression, isReversed){ if (expression == null) return null; if (expression.Slick === true) return expression; expression = ('' + expression).replace(/^\s+|\s+$/g, ''); reversed = !!isReversed; var currentCache = (reversed) ? reverseCache : cache; if (currentCache[expression]) return currentCache[expression]; parsed = { Slick: true, expressions: [], raw: expression, reverse: function(){ return parse(this.raw, true); } }; separatorIndex = -1; while (expression != (expression = expression.replace(regexp, parser))); parsed.length = parsed.expressions.length; return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed; }; var reverseCombinator = function(combinator){ if (combinator === '!') return ' '; else if (combinator === ' ') return '!'; else if ((/^!/).test(combinator)) return combinator.replace(/^!/, ''); else return '!' + combinator; }; var reverse = function(expression){ var expressions = expression.expressions; for (var i = 0; i < expressions.length; i++){ var exp = expressions[i]; var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)}; for (var j = 0; j < exp.length; j++){ var cexp = exp[j]; if (!cexp.reverseCombinator) cexp.reverseCombinator = ' '; cexp.combinator = cexp.reverseCombinator; delete cexp.reverseCombinator; } exp.reverse().push(last); } return expression; }; var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan MIT License return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, function(match){ return '\\' + match; }); }; var regexp = new RegExp( /* #!/usr/bin/env ruby puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'') __END__ "(?x)^(?:\ \\s* ( , ) \\s* # Separator \n\ | \\s* ( + ) \\s* # Combinator \n\ | ( \\s+ ) # CombinatorChildren \n\ | ( + | \\* ) # Tag \n\ | \\# ( + ) # ID \n\ | \\. ( + ) # ClassName \n\ | # Attribute \n\ \\[ \ \\s* (+) (?: \ \\s* ([*^$!~|]?=) (?: \ \\s* (?:\ ([\"']?)(.*?)\\9 \ )\ ) \ )? \\s* \ \\](?!\\]) \n\ | :+ ( + )(?:\ \\( (?:\ (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\ ) \\)\ )?\ )" */ "^(?:\\s*(,)\\s*|\\s*(+)\\s*|(\\s+)|(+|\\*)|\\#(+)|\\.(+)|\\[\\s*(+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)" .replace(//, '[' + escapeRegExp(">+~`!@$%^&={}\\;/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])') .replace(//g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])') ); function parser( rawMatch, separator, combinator, combinatorChildren, tagName, id, className, attributeKey, attributeOperator, attributeQuote, attributeValue, pseudoMarker, pseudoClass, pseudoQuote, pseudoClassQuotedValue, pseudoClassValue ){ if (separator || separatorIndex === -1){ parsed.expressions[++separatorIndex] = []; combinatorIndex = -1; if (separator) return ''; } if (combinator || combinatorChildren || combinatorIndex === -1){ combinator = combinator || ' '; var currentSeparator = parsed.expressions[separatorIndex]; if (reversed && currentSeparator[combinatorIndex]) currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator); currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'}; } var currentParsed = parsed.expressions[separatorIndex][combinatorIndex]; if (tagName){ currentParsed.tag = tagName.replace(reUnescape, ''); } else if (id){ currentParsed.id = id.replace(reUnescape, ''); } else if (className){ className = className.replace(reUnescape, ''); if (!currentParsed.classList) currentParsed.classList = []; if (!currentParsed.classes) currentParsed.classes = []; currentParsed.classList.push(className); currentParsed.classes.push({ value: className, regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)') }); } else if (pseudoClass){ pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue; pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null; if (!currentParsed.pseudos) currentParsed.pseudos = []; currentParsed.pseudos.push({ key: pseudoClass.replace(reUnescape, ''), value: pseudoClassValue, type: pseudoMarker.length == 1 ? 'class' : 'element' }); } else if (attributeKey){ attributeKey = attributeKey.replace(reUnescape, ''); attributeValue = (attributeValue || '').replace(reUnescape, ''); var test, regexp; switch (attributeOperator){ case '^=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) ); break; case '$=' : regexp = new RegExp( escapeRegExp(attributeValue) +'$' ); break; case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break; case '|=' : regexp = new RegExp( '^'+ escapeRegExp(attributeValue) +'(-|$)' ); break; case '=' : test = function(value){ return attributeValue == value; }; break; case '*=' : test = function(value){ return value && value.indexOf(attributeValue) > -1; }; break; case '!=' : test = function(value){ return attributeValue != value; }; break; default : test = function(value){ return !!value; }; } if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){ return false; }; if (!test) test = function(value){ return value && regexp.test(value); }; if (!currentParsed.attributes) currentParsed.attributes = []; currentParsed.attributes.push({ key: attributeKey, operator: attributeOperator, value: attributeValue, test: test }); } return ''; }; // Slick NS var Slick = (this.Slick || {}); Slick.parse = function(expression){ return parse(expression); }; Slick.escapeRegExp = escapeRegExp; if (!this.Slick) this.Slick = Slick; }).apply(/**/(typeof exports != 'undefined') ? exports : /**/this); /* --- name: Slick.Finder description: The new, superfast css selector engine. provides: Slick.Finder requires: Slick.Parser ... */ ;(function(){ var local = {}, featuresCache = {}, toString = Object.prototype.toString; // Feature / Bug detection local.isNativeCode = function(fn){ return (/\{\s*\[native code\]\s*\}/).test('' + fn); }; local.isXML = function(document){ return (!!document.xmlVersion) || (!!document.xml) || (toString.call(document) == '[object XMLDocument]') || (document.nodeType == 9 && document.documentElement.nodeName != 'HTML'); }; local.setDocument = function(document){ // convert elements / window arguments to document. if document cannot be extrapolated, the function returns. var nodeType = document.nodeType; if (nodeType == 9); // document else if (nodeType) document = document.ownerDocument; // node else if (document.navigator) document = document.document; // window else return; // check if it's the old document if (this.document === document) return; this.document = document; // check if we have done feature detection on this document before var root = document.documentElement, rootUid = this.getUIDXML(root), features = featuresCache[rootUid], feature; if (features){ for (feature in features){ this[feature] = features[feature]; } return; } features = featuresCache[rootUid] = {}; features.root = root; features.isXMLDocument = this.isXML(document); features.brokenStarGEBTN = features.starSelectsClosedQSA = features.idGetsName = features.brokenMixedCaseQSA = features.brokenGEBCN = features.brokenCheckedQSA = features.brokenEmptyAttributeQSA = features.isHTMLDocument = features.nativeMatchesSelector = false; var starSelectsClosed, starSelectsComments, brokenSecondClassNameGEBCN, cachedGetElementsByClassName, brokenFormAttributeGetter; var selected, id = 'slick_uniqueid'; var testNode = document.createElement('div'); var testRoot = document.body || document.getElementsByTagName('body')[0] || root; testRoot.appendChild(testNode); // on non-HTML documents innerHTML and getElementsById doesnt work properly try { testNode.innerHTML = ''; features.isHTMLDocument = !!document.getElementById(id); } catch(e){}; if (features.isHTMLDocument){ testNode.style.display = 'none'; // IE returns comment nodes for getElementsByTagName('*') for some documents testNode.appendChild(document.createComment('')); starSelectsComments = (testNode.getElementsByTagName('*').length > 1); // IE returns closed nodes (EG:"") for getElementsByTagName('*') for some documents try { testNode.innerHTML = 'foo'; selected = testNode.getElementsByTagName('*'); starSelectsClosed = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/'); } catch(e){}; features.brokenStarGEBTN = starSelectsComments || starSelectsClosed; // IE returns elements with the name instead of just id for getElementsById for some documents try { testNode.innerHTML = ''; features.idGetsName = document.getElementById(id) === testNode.firstChild; } catch(e){}; if (testNode.getElementsByClassName){ // Safari 3.2 getElementsByClassName caches results try { testNode.innerHTML = ''; testNode.getElementsByClassName('b').length; testNode.firstChild.className = 'b'; cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2); } catch(e){}; // Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one try { testNode.innerHTML = ''; brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2); } catch(e){}; features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN; } if (testNode.querySelectorAll){ // IE 8 returns closed nodes (EG:"") for querySelectorAll('*') for some documents try { testNode.innerHTML = 'foo'; selected = testNode.querySelectorAll('*'); features.starSelectsClosedQSA = (selected && !!selected.length && selected[0].nodeName.charAt(0) == '/'); } catch(e){}; // Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode try { testNode.innerHTML = ''; features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length; } catch(e){}; // Webkit and Opera dont return selected options on querySelectorAll try { testNode.innerHTML = ''; features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0); } catch(e){}; // IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll try { testNode.innerHTML = ''; features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0); } catch(e){}; } // IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input try { testNode.innerHTML = '
'; brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's'); } catch(e){}; // native matchesSelector function features.nativeMatchesSelector = root.matchesSelector || /*root.msMatchesSelector ||*/ root.mozMatchesSelector || root.webkitMatchesSelector; if (features.nativeMatchesSelector) try { // if matchesSelector trows errors on incorrect sintaxes we can use it features.nativeMatchesSelector.call(root, ':slick'); features.nativeMatchesSelector = null; } catch(e){}; } try { root.slick_expando = 1; delete root.slick_expando; features.getUID = this.getUIDHTML; } catch(e) { features.getUID = this.getUIDXML; } testRoot.removeChild(testNode); testNode = selected = testRoot = null; // getAttribute features.getAttribute = (features.isHTMLDocument && brokenFormAttributeGetter) ? function(node, name){ var method = this.attributeGetters[name]; if (method) return method.call(node); var attributeNode = node.getAttributeNode(name); return (attributeNode) ? attributeNode.nodeValue : null; } : function(node, name){ var method = this.attributeGetters[name]; return (method) ? method.call(node) : node.getAttribute(name); }; // hasAttribute features.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ? function(node, attribute) { return node.hasAttribute(attribute); } : function(node, attribute) { node = node.getAttributeNode(attribute); return !!(node && (node.specified || node.nodeValue)); }; // contains // FIXME: Add specs: local.contains should be different for xml and html documents? features.contains = (root && this.isNativeCode(root.contains)) ? function(context, node){ return context.contains(node); } : (root && root.compareDocumentPosition) ? function(context, node){ return context === node || !!(context.compareDocumentPosition(node) & 16); } : function(context, node){ if (node) do { if (node === context) return true; } while ((node = node.parentNode)); return false; }; // document order sorting // credits to Sizzle (http://sizzlejs.com/) features.documentSorter = (root.compareDocumentPosition) ? function(a, b){ if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0; return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; } : ('sourceIndex' in root) ? function(a, b){ if (!a.sourceIndex || !b.sourceIndex) return 0; return a.sourceIndex - b.sourceIndex; } : (document.createRange) ? function(a, b){ if (!a.ownerDocument || !b.ownerDocument) return 0; var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); aRange.setStart(a, 0); aRange.setEnd(a, 0); bRange.setStart(b, 0); bRange.setEnd(b, 0); return aRange.compareBoundaryPoints(Range.START_TO_END, bRange); } : null ; root = null; for (feature in features){ this[feature] = features[feature]; } }; // Main Method var reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/, reEmptyAttribute = /\[.+[*$^]=(?:""|'')?\]/, qsaFailExpCache = {}; local.search = function(context, expression, append, first){ var found = this.found = (first) ? null : (append || []); if (!context) return found; else if (context.navigator) context = context.document; // Convert the node from a window to a document else if (!context.nodeType) return found; // setup var parsed, i, uniques = this.uniques = {}, hasOthers = !!(append && append.length), contextIsDocument = (context.nodeType == 9); if (this.document !== (contextIsDocument ? context : context.ownerDocument)) this.setDocument(context); // avoid duplicating items already in the append array if (hasOthers) for (i = found.length; i--;) uniques[this.getUID(found[i])] = true; // expression checks if (typeof expression == 'string'){ // expression is a string /**/ var simpleSelector = expression.match(reSimpleSelector); simpleSelectors: if (simpleSelector) { var symbol = simpleSelector[1], name = simpleSelector[2], node, nodes; if (!symbol){ if (name == '*' && this.brokenStarGEBTN) break simpleSelectors; nodes = context.getElementsByTagName(name); if (first) return nodes[0] || null; for (i = 0; node = nodes[i++];){ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node); } } else if (symbol == '#'){ if (!this.isHTMLDocument || !contextIsDocument) break simpleSelectors; node = context.getElementById(name); if (!node) return found; if (this.idGetsName && node.getAttributeNode('id').nodeValue != name) break simpleSelectors; if (first) return node || null; if (!(hasOthers && uniques[this.getUID(node)])) found.push(node); } else if (symbol == '.'){ if (!this.isHTMLDocument || ((!context.getElementsByClassName || this.brokenGEBCN) && context.querySelectorAll)) break simpleSelectors; if (context.getElementsByClassName && !this.brokenGEBCN){ nodes = context.getElementsByClassName(name); if (first) return nodes[0] || null; for (i = 0; node = nodes[i++];){ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node); } } else { var matchClass = new RegExp('(^|\\s)'+ Slick.escapeRegExp(name) +'(\\s|$)'); nodes = context.getElementsByTagName('*'); for (i = 0; node = nodes[i++];){ className = node.className; if (!(className && matchClass.test(className))) continue; if (first) return node; if (!(hasOthers && uniques[this.getUID(node)])) found.push(node); } } } if (hasOthers) this.sort(found); return (first) ? null : found; } /**/ /**/ querySelector: if (context.querySelectorAll) { if (!this.isHTMLDocument || qsaFailExpCache[expression] //TODO: only skip when expression is actually mixed case || this.brokenMixedCaseQSA || (this.brokenCheckedQSA && expression.indexOf(':checked') > -1) || (this.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression)) || (!contextIsDocument //Abort when !contextIsDocument and... // there are multiple expressions in the selector // since we currently only fix non-document rooted QSA for single expression selectors && expression.indexOf(',') > -1 ) || Slick.disableQSA ) break querySelector; var _expression = expression, _context = context; if (!contextIsDocument){ // non-document rooted QSA // credits to Andrew Dupont var currentId = _context.getAttribute('id'), slickid = 'slickid__'; _context.setAttribute('id', slickid); _expression = '#' + slickid + ' ' + _expression; context = _context.parentNode; } try { if (first) return context.querySelector(_expression) || null; else nodes = context.querySelectorAll(_expression); } catch(e) { qsaFailExpCache[expression] = 1; break querySelector; } finally { if (!contextIsDocument){ if (currentId) _context.setAttribute('id', currentId); else _context.removeAttribute('id'); context = _context; } } if (this.starSelectsClosedQSA) for (i = 0; node = nodes[i++];){ if (node.nodeName > '@' && !(hasOthers && uniques[this.getUID(node)])) found.push(node); } else for (i = 0; node = nodes[i++];){ if (!(hasOthers && uniques[this.getUID(node)])) found.push(node); } if (hasOthers) this.sort(found); return found; } /**/ parsed = this.Slick.parse(expression); if (!parsed.length) return found; } else if (expression == null){ // there is no expression return found; } else if (expression.Slick){ // expression is a parsed Slick object parsed = expression; } else if (this.contains(context.documentElement || context, expression)){ // expression is a node (found) ? found.push(expression) : found = expression; return found; } else { // other junk return found; } /**//**/ // cache elements for the nth selectors this.posNTH = {}; this.posNTHLast = {}; this.posNTHType = {}; this.posNTHTypeLast = {}; /**//**/ // if append is null and there is only a single selector with one expression use pushArray, else use pushUID this.push = (!hasOthers && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID; if (found == null) found = []; // default engine var j, m, n; var combinator, tag, id, classList, classes, attributes, pseudos; var currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions; search: for (i = 0; (currentExpression = expressions[i]); i++) for (j = 0; (currentBit = currentExpression[j]); j++){ combinator = 'combinator:' + currentBit.combinator; if (!this[combinator]) continue search; tag = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase(); id = currentBit.id; classList = currentBit.classList; classes = currentBit.classes; attributes = currentBit.attributes; pseudos = currentBit.pseudos; lastBit = (j === (currentExpression.length - 1)); this.bitUniques = {}; if (lastBit){ this.uniques = uniques; this.found = found; } else { this.uniques = {}; this.found = []; } if (j === 0){ this[combinator](context, tag, id, classes, attributes, pseudos, classList); if (first && lastBit && found.length) break search; } else { if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++){ this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList); if (found.length) break search; } else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList); } currentItems = this.found; } // should sort if there are nodes in append and if you pass multiple expressions. if (hasOthers || (parsed.expressions.length > 1)) this.sort(found); return (first) ? (found[0] || null) : found; }; // Utils local.uidx = 1; local.uidk = 'slick-uniqueid'; local.getUIDXML = function(node){ var uid = node.getAttribute(this.uidk); if (!uid){ uid = this.uidx++; node.setAttribute(this.uidk, uid); } return uid; }; local.getUIDHTML = function(node){ return node.uniqueNumber || (node.uniqueNumber = this.uidx++); }; // sort based on the setDocument documentSorter method. local.sort = function(results){ if (!this.documentSorter) return results; results.sort(this.documentSorter); return results; }; /**//**/ local.cacheNTH = {}; local.matchNTH = /^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/; local.parseNTHArgument = function(argument){ var parsed = argument.match(this.matchNTH); if (!parsed) return false; var special = parsed[2] || false; var a = parsed[1] || 1; if (a == '-') a = -1; var b = +parsed[3] || 0; parsed = (special == 'n') ? {a: a, b: b} : (special == 'odd') ? {a: 2, b: 1} : (special == 'even') ? {a: 2, b: 0} : {a: 0, b: a}; return (this.cacheNTH[argument] = parsed); }; local.createNTHPseudo = function(child, sibling, positions, ofType){ return function(node, argument){ var uid = this.getUID(node); if (!this[positions][uid]){ var parent = node.parentNode; if (!parent) return false; var el = parent[child], count = 1; if (ofType){ var nodeName = node.nodeName; do { if (el.nodeName != nodeName) continue; this[positions][this.getUID(el)] = count++; } while ((el = el[sibling])); } else { do { if (el.nodeType != 1) continue; this[positions][this.getUID(el)] = count++; } while ((el = el[sibling])); } } argument = argument || 'n'; var parsed = this.cacheNTH[argument] || this.parseNTHArgument(argument); if (!parsed) return false; var a = parsed.a, b = parsed.b, pos = this[positions][uid]; if (a == 0) return b == pos; if (a > 0){ if (pos < b) return false; } else { if (b < pos) return false; } return ((pos - b) % a) == 0; }; }; /**//**/ local.pushArray = function(node, tag, id, classes, attributes, pseudos){ if (this.matchSelector(node, tag, id, classes, attributes, pseudos)) this.found.push(node); }; local.pushUID = function(node, tag, id, classes, attributes, pseudos){ var uid = this.getUID(node); if (!this.uniques[uid] && this.matchSelector(node, tag, id, classes, attributes, pseudos)){ this.uniques[uid] = true; this.found.push(node); } }; local.matchNode = function(node, selector){ if (this.isHTMLDocument && this.nativeMatchesSelector){ try { return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]')); } catch(matchError) {} } var parsed = this.Slick.parse(selector); if (!parsed) return true; // simple (single) selectors var expressions = parsed.expressions, reversedExpressions, simpleExpCounter = 0, i; for (i = 0; (currentExpression = expressions[i]); i++){ if (currentExpression.length == 1){ var exp = currentExpression[0]; if (this.matchSelector(node, (this.isXMLDocument) ? exp.tag : exp.tag.toUpperCase(), exp.id, exp.classes, exp.attributes, exp.pseudos)) return true; simpleExpCounter++; } } if (simpleExpCounter == parsed.length) return false; var nodes = this.search(this.document, parsed), item; for (i = 0; item = nodes[i++];){ if (item === node) return true; } return false; }; local.matchPseudo = function(node, name, argument){ var pseudoName = 'pseudo:' + name; if (this[pseudoName]) return this[pseudoName](node, argument); var attribute = this.getAttribute(node, name); return (argument) ? argument == attribute : !!attribute; }; local.matchSelector = function(node, tag, id, classes, attributes, pseudos){ if (tag){ var nodeName = (this.isXMLDocument) ? node.nodeName : node.nodeName.toUpperCase(); if (tag == '*'){ if (nodeName < '@') return false; // Fix for comment nodes and closed nodes } else { if (nodeName != tag) return false; } } if (id && node.getAttribute('id') != id) return false; var i, part, cls; if (classes) for (i = classes.length; i--;){ cls = node.getAttribute('class') || node.className; if (!(cls && classes[i].regexp.test(cls))) return false; } if (attributes) for (i = attributes.length; i--;){ part = attributes[i]; if (part.operator ? !part.test(this.getAttribute(node, part.key)) : !this.hasAttribute(node, part.key)) return false; } if (pseudos) for (i = pseudos.length; i--;){ part = pseudos[i]; if (!this.matchPseudo(node, part.key, part.value)) return false; } return true; }; var combinators = { ' ': function(node, tag, id, classes, attributes, pseudos, classList){ // all child nodes, any level var i, item, children; if (this.isHTMLDocument){ getById: if (id){ item = this.document.getElementById(id); if ((!item && node.all) || (this.idGetsName && item && item.getAttributeNode('id').nodeValue != id)){ // all[id] returns all the elements with that name or id inside node // if theres just one it will return the element, else it will be a collection children = node.all[id]; if (!children) return; if (!children[0]) children = [children]; for (i = 0; item = children[i++];){ var idNode = item.getAttributeNode('id'); if (idNode && idNode.nodeValue == id){ this.push(item, tag, null, classes, attributes, pseudos); break; } } return; } if (!item){ // if the context is in the dom we return, else we will try GEBTN, breaking the getById label if (this.contains(this.root, node)) return; else break getById; } else if (this.document !== node && !this.contains(node, item)) return; this.push(item, tag, null, classes, attributes, pseudos); return; } getByClass: if (classes && node.getElementsByClassName && !this.brokenGEBCN){ children = node.getElementsByClassName(classList.join(' ')); if (!(children && children.length)) break getByClass; for (i = 0; item = children[i++];) this.push(item, tag, id, null, attributes, pseudos); return; } } getByTag: { children = node.getElementsByTagName(tag); if (!(children && children.length)) break getByTag; if (!this.brokenStarGEBTN) tag = null; for (i = 0; item = children[i++];) this.push(item, tag, id, classes, attributes, pseudos); } }, '>': function(node, tag, id, classes, attributes, pseudos){ // direct children if ((node = node.firstChild)) do { if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos); } while ((node = node.nextSibling)); }, '+': function(node, tag, id, classes, attributes, pseudos){ // next sibling while ((node = node.nextSibling)) if (node.nodeType == 1){ this.push(node, tag, id, classes, attributes, pseudos); break; } }, '^': function(node, tag, id, classes, attributes, pseudos){ // first child node = node.firstChild; if (node){ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos); else this['combinator:+'](node, tag, id, classes, attributes, pseudos); } }, '~': function(node, tag, id, classes, attributes, pseudos){ // next siblings while ((node = node.nextSibling)){ if (node.nodeType != 1) continue; var uid = this.getUID(node); if (this.bitUniques[uid]) break; this.bitUniques[uid] = true; this.push(node, tag, id, classes, attributes, pseudos); } }, '++': function(node, tag, id, classes, attributes, pseudos){ // next sibling and previous sibling this['combinator:+'](node, tag, id, classes, attributes, pseudos); this['combinator:!+'](node, tag, id, classes, attributes, pseudos); }, '~~': function(node, tag, id, classes, attributes, pseudos){ // next siblings and previous siblings this['combinator:~'](node, tag, id, classes, attributes, pseudos); this['combinator:!~'](node, tag, id, classes, attributes, pseudos); }, '!': function(node, tag, id, classes, attributes, pseudos){ // all parent nodes up to document while ((node = node.parentNode)) if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos); }, '!>': function(node, tag, id, classes, attributes, pseudos){ // direct parent (one level) node = node.parentNode; if (node !== this.document) this.push(node, tag, id, classes, attributes, pseudos); }, '!+': function(node, tag, id, classes, attributes, pseudos){ // previous sibling while ((node = node.previousSibling)) if (node.nodeType == 1){ this.push(node, tag, id, classes, attributes, pseudos); break; } }, '!^': function(node, tag, id, classes, attributes, pseudos){ // last child node = node.lastChild; if (node){ if (node.nodeType == 1) this.push(node, tag, id, classes, attributes, pseudos); else this['combinator:!+'](node, tag, id, classes, attributes, pseudos); } }, '!~': function(node, tag, id, classes, attributes, pseudos){ // previous siblings while ((node = node.previousSibling)){ if (node.nodeType != 1) continue; var uid = this.getUID(node); if (this.bitUniques[uid]) break; this.bitUniques[uid] = true; this.push(node, tag, id, classes, attributes, pseudos); } } }; for (var c in combinators) local['combinator:' + c] = combinators[c]; var pseudos = { /**/ 'empty': function(node){ var child = node.firstChild; return !(child && child.nodeType == 1) && !(node.innerText || node.textContent || '').length; }, 'not': function(node, expression){ return !this.matchNode(node, expression); }, 'contains': function(node, text){ return (node.innerText || node.textContent || '').indexOf(text) > -1; }, 'first-child': function(node){ while ((node = node.previousSibling)) if (node.nodeType == 1) return false; return true; }, 'last-child': function(node){ while ((node = node.nextSibling)) if (node.nodeType == 1) return false; return true; }, 'only-child': function(node){ var prev = node; while ((prev = prev.previousSibling)) if (prev.nodeType == 1) return false; var next = node; while ((next = next.nextSibling)) if (next.nodeType == 1) return false; return true; }, /**/ 'nth-child': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTH'), 'nth-last-child': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHLast'), 'nth-of-type': local.createNTHPseudo('firstChild', 'nextSibling', 'posNTHType', true), 'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true), 'index': function(node, index){ return this['pseudo:nth-child'](node, '' + index + 1); }, 'even': function(node){ return this['pseudo:nth-child'](node, '2n'); }, 'odd': function(node){ return this['pseudo:nth-child'](node, '2n+1'); }, /**/ /**/ 'first-of-type': function(node){ var nodeName = node.nodeName; while ((node = node.previousSibling)) if (node.nodeName == nodeName) return false; return true; }, 'last-of-type': function(node){ var nodeName = node.nodeName; while ((node = node.nextSibling)) if (node.nodeName == nodeName) return false; return true; }, 'only-of-type': function(node){ var prev = node, nodeName = node.nodeName; while ((prev = prev.previousSibling)) if (prev.nodeName == nodeName) return false; var next = node; while ((next = next.nextSibling)) if (next.nodeName == nodeName) return false; return true; }, /**/ // custom pseudos 'enabled': function(node){ return !node.disabled; }, 'disabled': function(node){ return node.disabled; }, 'checked': function(node){ return node.checked || node.selected; }, 'focus': function(node){ return this.isHTMLDocument && this.document.activeElement === node && (node.href || node.type || this.hasAttribute(node, 'tabindex')); }, 'root': function(node){ return (node === this.root); }, 'selected': function(node){ return node.selected; } /**/ }; for (var p in pseudos) local['pseudo:' + p] = pseudos[p]; // attributes methods local.attributeGetters = { 'class': function(){ return this.getAttribute('class') || this.className; }, 'for': function(){ return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for'); }, 'href': function(){ return ('href' in this) ? this.getAttribute('href', 2) : this.getAttribute('href'); }, 'style': function(){ return (this.style) ? this.style.cssText : this.getAttribute('style'); }, 'tabindex': function(){ var attributeNode = this.getAttributeNode('tabindex'); return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null; }, 'type': function(){ return this.getAttribute('type'); } }; // Slick var Slick = local.Slick = (this.Slick || {}); Slick.version = '1.1.5'; // Slick finder Slick.search = function(context, expression, append){ return local.search(context, expression, append); }; Slick.find = function(context, expression){ return local.search(context, expression, null, true); }; // Slick containment checker Slick.contains = function(container, node){ local.setDocument(container); return local.contains(container, node); }; // Slick attribute getter Slick.getAttribute = function(node, name){ return local.getAttribute(node, name); }; // Slick matcher Slick.match = function(node, selector){ if (!(node && selector)) return false; if (!selector || selector === node) return true; local.setDocument(node); return local.matchNode(node, selector); }; // Slick attribute accessor Slick.defineAttributeGetter = function(name, fn){ local.attributeGetters[name] = fn; return this; }; Slick.lookupAttributeGetter = function(name){ return local.attributeGetters[name]; }; // Slick pseudo accessor Slick.definePseudo = function(name, fn){ local['pseudo:' + name] = function(node, argument){ return fn.call(node, argument); }; return this; }; Slick.lookupPseudo = function(name){ var pseudo = local['pseudo:' + name]; if (pseudo) return function(argument){ return pseudo.call(this, argument); }; return null; }; // Slick overrides accessor Slick.override = function(regexp, fn){ local.override(regexp, fn); return this; }; Slick.isXML = local.isXML; Slick.uidOf = function(node){ return local.getUIDHTML(node); }; if (!this.Slick) this.Slick = Slick; }).apply(/**/(typeof exports != 'undefined') ? exports : /**/this); /* --- name: Element description: One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser, time-saver methods to let you easily work with HTML Elements. license: MIT-style license. requires: [Window, Document, Array, String, Function, Number, Slick.Parser, Slick.Finder] provides: [Element, Elements, $, $$, Iframe, Selectors] ... */ var Element = function(tag, props){ var konstructor = Element.Constructors[tag]; if (konstructor) return konstructor(props); if (typeof tag != 'string') return document.id(tag).set(props); if (!props) props = {}; if (!(/^[\w-]+$/).test(tag)){ var parsed = Slick.parse(tag).expressions[0][0]; tag = (parsed.tag == '*') ? 'div' : parsed.tag; if (parsed.id && props.id == null) props.id = parsed.id; var attributes = parsed.attributes; if (attributes) for (var i = 0, l = attributes.length; i < l; i++){ var attr = attributes[i]; if (props[attr.key] != null) continue; if (attr.value != null && attr.operator == '=') props[attr.key] = attr.value; else if (!attr.value && !attr.operator) props[attr.key] = true; } if (parsed.classList && props['class'] == null) props['class'] = parsed.classList.join(' '); } return document.newElement(tag, props); }; if (Browser.Element) Element.prototype = Browser.Element.prototype; new Type('Element', Element).mirror(function(name){ if (Array.prototype[name]) return; var obj = {}; obj[name] = function(){ var results = [], args = arguments, elements = true; for (var i = 0, l = this.length; i < l; i++){ var element = this[i], result = results[i] = element[name].apply(element, args); elements = (elements && typeOf(result) == 'element'); } return (elements) ? new Elements(results) : results; }; Elements.implement(obj); }); if (!Browser.Element){ Element.parent = Object; Element.Prototype = {'$family': Function.from('element').hide()}; Element.mirror(function(name, method){ Element.Prototype[name] = method; }); } Element.Constructors = {}; var IFrame = new Type('IFrame', function(){ var params = Array.link(arguments, { properties: Type.isObject, iframe: function(obj){ return (obj != null); } }); var props = params.properties || {}, iframe; if (params.iframe) iframe = document.id(params.iframe); var onload = props.onload || function(){}; delete props.onload; props.id = props.name = [props.id, props.name, iframe ? (iframe.id || iframe.name) : 'IFrame_' + String.uniqueID()].pick(); iframe = new Element(iframe || 'iframe', props); var onLoad = function(){ onload.call(iframe.contentWindow); }; if (window.frames[props.id]) onLoad(); else iframe.addListener('load', onLoad); return iframe; }); var Elements = this.Elements = function(nodes){ if (nodes && nodes.length){ var uniques = {}, node; for (var i = 0; node = nodes[i++];){ var uid = Slick.uidOf(node); if (!uniques[uid]){ uniques[uid] = true; this.push(node); } } } }; Elements.prototype = {length: 0}; Elements.parent = Array; new Type('Elements', Elements).implement({ filter: function(filter, bind){ if (!filter) return this; return new Elements(Array.filter(this, (typeOf(filter) == 'string') ? function(item){ return item.match(filter); } : filter, bind)); }.protect(), push: function(){ var length = this.length; for (var i = 0, l = arguments.length; i < l; i++){ var item = document.id(arguments[i]); if (item) this[length++] = item; } return (this.length = length); }.protect(), unshift: function(){ var items = []; for (var i = 0, l = arguments.length; i < l; i++){ var item = document.id(arguments[i]); if (item) items.push(item); } return Array.prototype.unshift.apply(this, items); }.protect(), concat: function(){ var newElements = new Elements(this); for (var i = 0, l = arguments.length; i < l; i++){ var item = arguments[i]; if (Type.isEnumerable(item)) newElements.append(item); else newElements.push(item); } return newElements; }.protect(), append: function(collection){ for (var i = 0, l = collection.length; i < l; i++) this.push(collection[i]); return this; }.protect(), empty: function(){ while (this.length) delete this[--this.length]; return this; }.protect() }); (function(){ // FF, IE var splice = Array.prototype.splice, object = {'0': 0, '1': 1, length: 2}; splice.call(object, 1, 1); if (object[1] == 1) Elements.implement('splice', function(){ var length = this.length; splice.apply(this, arguments); while (length >= this.length) delete this[length--]; return this; }.protect()); Elements.implement(Array.prototype); Array.mirror(Elements); /**/ var createElementAcceptsHTML; try { var x = document.createElement(''); createElementAcceptsHTML = (x.name == 'x'); } catch(e){} var escapeQuotes = function(html){ return ('' + html).replace(/&/g, '&').replace(/"/g, '"'); }; /**/ Document.implement({ newElement: function(tag, props){ if (props && props.checked != null) props.defaultChecked = props.checked; /**/// Fix for readonly name and type properties in IE < 8 if (createElementAcceptsHTML && props){ tag = '<' + tag; if (props.name) tag += ' name="' + escapeQuotes(props.name) + '"'; if (props.type) tag += ' type="' + escapeQuotes(props.type) + '"'; tag += '>'; delete props.name; delete props.type; } /**/ return this.id(this.createElement(tag)).set(props); } }); })(); Document.implement({ newTextNode: function(text){ return this.createTextNode(text); }, getDocument: function(){ return this; }, getWindow: function(){ return this.window; }, id: (function(){ var types = { string: function(id, nocash, doc){ id = Slick.find(doc, '#' + id.replace(/(\W)/g, '\\$1')); return (id) ? types.element(id, nocash) : null; }, element: function(el, nocash){ $uid(el); if (!nocash && !el.$family && !(/^(?:object|embed)$/i).test(el.tagName)){ Object.append(el, Element.Prototype); } return el; }, object: function(obj, nocash, doc){ if (obj.toElement) return types.element(obj.toElement(doc), nocash); return null; } }; types.textnode = types.whitespace = types.window = types.document = function(zero){ return zero; }; return function(el, nocash, doc){ if (el && el.$family && el.uid) return el; var type = typeOf(el); return (types[type]) ? types[type](el, nocash, doc || document) : null; }; })() }); if (window.$ == null) Window.implement('$', function(el, nc){ return document.id(el, nc, this.document); }); Window.implement({ getDocument: function(){ return this.document; }, getWindow: function(){ return this; } }); [Document, Element].invoke('implement', { getElements: function(expression){ return Slick.search(this, expression, new Elements); }, getElement: function(expression){ return document.id(Slick.find(this, expression)); } }); if (window.$$ == null) Window.implement('$$', function(selector){ if (arguments.length == 1){ if (typeof selector == 'string') return Slick.search(this.document, selector, new Elements); else if (Type.isEnumerable(selector)) return new Elements(selector); } return new Elements(arguments); }); (function(){ var collected = {}, storage = {}; var formProps = {input: 'checked', option: 'selected', textarea: 'value'}; var get = function(uid){ return (storage[uid] || (storage[uid] = {})); }; var clean = function(item){ var uid = item.uid; if (item.removeEvents) item.removeEvents(); if (item.clearAttributes) item.clearAttributes(); if (uid != null){ delete collected[uid]; delete storage[uid]; } return item; }; var camels = ['defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap' ]; var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readOnly', 'multiple', 'selected', 'noresize', 'defer', 'defaultChecked' ]; var attributes = { 'html': 'innerHTML', 'class': 'className', 'for': 'htmlFor', 'text': (function(){ var temp = document.createElement('div'); return (temp.textContent == null) ? 'innerText' : 'textContent'; })() }; var readOnly = ['type']; var expandos = ['value', 'defaultValue']; var uriAttrs = /^(?:href|src|usemap)$/i; bools = bools.associate(bools); camels = camels.associate(camels.map(String.toLowerCase)); readOnly = readOnly.associate(readOnly); Object.append(attributes, expandos.associate(expandos)); var inserters = { before: function(context, element){ var parent = element.parentNode; if (parent) parent.insertBefore(context, element); }, after: function(context, element){ var parent = element.parentNode; if (parent) parent.insertBefore(context, element.nextSibling); }, bottom: function(context, element){ element.appendChild(context); }, top: function(context, element){ element.insertBefore(context, element.firstChild); } }; inserters.inside = inserters.bottom; var injectCombinator = function(expression, combinator){ if (!expression) return combinator; expression = Object.clone(Slick.parse(expression)); var expressions = expression.expressions; for (var i = expressions.length; i--;) expressions[i][0].combinator = combinator; return expression; }; Element.implement({ set: function(prop, value){ var property = Element.Properties[prop]; (property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value); }.overloadSetter(), get: function(prop){ var property = Element.Properties[prop]; return (property && property.get) ? property.get.apply(this) : this.getProperty(prop); }.overloadGetter(), erase: function(prop){ var property = Element.Properties[prop]; (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop); return this; }, setProperty: function(attribute, value){ attribute = camels[attribute] || attribute; if (value == null) return this.removeProperty(attribute); var key = attributes[attribute]; (key) ? this[key] = value : (bools[attribute]) ? this[attribute] = !!value : this.setAttribute(attribute, '' + value); return this; }, setProperties: function(attributes){ for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]); return this; }, getProperty: function(attribute){ attribute = camels[attribute] || attribute; var key = attributes[attribute] || readOnly[attribute]; return (key) ? this[key] : (bools[attribute]) ? !!this[attribute] : (uriAttrs.test(attribute) ? this.getAttribute(attribute, 2) : (key = this.getAttributeNode(attribute)) ? key.nodeValue : null) || null; }, getProperties: function(){ var args = Array.from(arguments); return args.map(this.getProperty, this).associate(args); }, removeProperty: function(attribute){ attribute = camels[attribute] || attribute; var key = attributes[attribute]; (key) ? this[key] = '' : (bools[attribute]) ? this[attribute] = false : this.removeAttribute(attribute); return this; }, removeProperties: function(){ Array.each(arguments, this.removeProperty, this); return this; }, hasClass: function(className){ return this.className.clean().contains(className, ' '); }, addClass: function(className){ if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean(); return this; }, removeClass: function(className){ this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1'); return this; }, toggleClass: function(className, force){ if (force == null) force = !this.hasClass(className); return (force) ? this.addClass(className) : this.removeClass(className); }, adopt: function(){ var parent = this, fragment, elements = Array.flatten(arguments), length = elements.length; if (length > 1) parent = fragment = document.createDocumentFragment(); for (var i = 0; i < length; i++){ var element = document.id(elements[i], true); if (element) parent.appendChild(element); } if (fragment) this.appendChild(fragment); return this; }, appendText: function(text, where){ return this.grab(this.getDocument().newTextNode(text), where); }, grab: function(el, where){ inserters[where || 'bottom'](document.id(el, true), this); return this; }, inject: function(el, where){ inserters[where || 'bottom'](this, document.id(el, true)); return this; }, replaces: function(el){ el = document.id(el, true); el.parentNode.replaceChild(this, el); return this; }, wraps: function(el, where){ el = document.id(el, true); return this.replaces(el).grab(el, where); }, getPrevious: function(expression){ return document.id(Slick.find(this, injectCombinator(expression, '!~'))); }, getAllPrevious: function(expression){ return Slick.search(this, injectCombinator(expression, '!~'), new Elements); }, getNext: function(expression){ return document.id(Slick.find(this, injectCombinator(expression, '~'))); }, getAllNext: function(expression){ return Slick.search(this, injectCombinator(expression, '~'), new Elements); }, getFirst: function(expression){ return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]); }, getLast: function(expression){ return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast()); }, getParent: function(expression){ return document.id(Slick.find(this, injectCombinator(expression, '!'))); }, getParents: function(expression){ return Slick.search(this, injectCombinator(expression, '!'), new Elements); }, getSiblings: function(expression){ return Slick.search(this, injectCombinator(expression, '~~'), new Elements); }, getChildren: function(expression){ return Slick.search(this, injectCombinator(expression, '>'), new Elements); }, getWindow: function(){ return this.ownerDocument.window; }, getDocument: function(){ return this.ownerDocument; }, getElementById: function(id){ return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1'))); }, getSelected: function(){ this.selectedIndex; // Safari 3.2.1 return new Elements(Array.from(this.options).filter(function(option){ return option.selected; })); }, toQueryString: function(){ var queryString = []; this.getElements('input, select, textarea').each(function(el){ var type = el.type; if (!el.name || el.disabled || type == 'submit' || type == 'reset' || type == 'file' || type == 'image') return; var value = (el.get('tag') == 'select') ? el.getSelected().map(function(opt){ // IE return document.id(opt).get('value'); }) : ((type == 'radio' || type == 'checkbox') && !el.checked) ? null : el.get('value'); Array.from(value).each(function(val){ if (typeof val != 'undefined') queryString.push(encodeURIComponent(el.name) + '=' + encodeURIComponent(val)); }); }); return queryString.join('&'); }, destroy: function(){ var children = clean(this).getElementsByTagName('*'); Array.each(children, clean); Element.dispose(this); return null; }, empty: function(){ Array.from(this.childNodes).each(Element.dispose); return this; }, dispose: function(){ return (this.parentNode) ? this.parentNode.removeChild(this) : this; }, match: function(expression){ return !expression || Slick.match(this, expression); } }); var cleanClone = function(node, element, keepid){ if (!keepid) node.setAttributeNode(document.createAttribute('id')); if (node.clearAttributes){ node.clearAttributes(); node.mergeAttributes(element); node.removeAttribute('uid'); if (node.options){ var no = node.options, eo = element.options; for (var i = no.length; i--;) no[i].selected = eo[i].selected; } } var prop = formProps[element.tagName.toLowerCase()]; if (prop && element[prop]) node[prop] = element[prop]; }; Element.implement('clone', function(contents, keepid){ contents = contents !== false; var clone = this.cloneNode(contents), i; if (contents){ var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*'); for (i = ce.length; i--;) cleanClone(ce[i], te[i], keepid); } cleanClone(clone, this, keepid); if (Browser.ie){ var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object'); for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML; } return document.id(clone); }); var contains = {contains: function(element){ return Slick.contains(this, element); }}; if (!document.contains) Document.implement(contains); if (!document.createElement('div').contains) Element.implement(contains); [Element, Window, Document].invoke('implement', { addListener: function(type, fn){ if (type == 'unload'){ var old = fn, self = this; fn = function(){ self.removeListener('unload', fn); old(); }; } else { collected[$uid(this)] = this; } if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]); else this.attachEvent('on' + type, fn); return this; }, removeListener: function(type, fn){ if (this.removeEventListener) this.removeEventListener(type, fn, !!arguments[2]); else this.detachEvent('on' + type, fn); return this; }, retrieve: function(property, dflt){ var storage = get($uid(this)), prop = storage[property]; if (dflt != null && prop == null) prop = storage[property] = dflt; return prop != null ? prop : null; }, store: function(property, value){ var storage = get($uid(this)); storage[property] = value; return this; }, eliminate: function(property){ var storage = get($uid(this)); delete storage[property]; return this; } }); /**/ if (window.attachEvent && !window.addEventListener) window.addListener('unload', function(){ Object.each(collected, clean); if (window.CollectGarbage) CollectGarbage(); }); /**/ })(); Element.Properties = {}; Element.Properties.style = { set: function(style){ this.style.cssText = style; }, get: function(){ return this.style.cssText; }, erase: function(){ this.style.cssText = ''; } }; Element.Properties.tag = { get: function(){ return this.tagName.toLowerCase(); } }; /**/ (function(maxLength){ if (maxLength != null) Element.Properties.maxlength = Element.Properties.maxLength = { get: function(){ var maxlength = this.getAttribute('maxLength'); return maxlength == maxLength ? null : maxlength; } }; })(document.createElement('input').getAttribute('maxLength')); /**/ /**/ Element.Properties.html = (function(){ var tableTest = Function.attempt(function(){ var table = document.createElement('table'); table.innerHTML = ''; }); var wrapper = document.createElement('div'); var translations = { table: [1, '', '
'], select: [1, ''], tbody: [2, '', '
'], tr: [3, '', '
'] }; translations.thead = translations.tfoot = translations.tbody; var html = { set: function(){ var html = Array.flatten(arguments).join(''); var wrap = (!tableTest && translations[this.get('tag')]); if (wrap){ var first = wrapper; first.innerHTML = wrap[1] + html + wrap[2]; for (var i = wrap[0]; i--;) first = first.firstChild; this.empty().adopt(first.childNodes); } else { this.innerHTML = html; } } }; html.erase = html.set; return html; })(); /**/ /* --- name: Element.Style description: Contains methods for interacting with the styles of Elements in a fashionable way. license: MIT-style license. requires: Element provides: Element.Style ... */ (function(){ var html = document.html; Element.Properties.styles = {set: function(styles){ this.setStyles(styles); }}; var hasOpacity = (html.style.opacity != null); var reAlpha = /alpha\(opacity=([\d.]+)\)/i; var setOpacity = function(element, opacity){ if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1; if (hasOpacity){ element.style.opacity = opacity; } else { opacity = (opacity * 100).limit(0, 100).round(); opacity = (opacity == 100) ? '' : 'alpha(opacity=' + opacity + ')'; var filter = element.style.filter || element.getComputedStyle('filter') || ''; element.style.filter = reAlpha.test(filter) ? filter.replace(reAlpha, opacity) : filter + opacity; } }; Element.Properties.opacity = { set: function(opacity){ var visibility = this.style.visibility; if (opacity == 0 && visibility != 'hidden') this.style.visibility = 'hidden'; else if (opacity != 0 && visibility != 'visible') this.style.visibility = 'visible'; setOpacity(this, opacity); }, get: (hasOpacity) ? function(){ var opacity = this.style.opacity || this.getComputedStyle('opacity'); return (opacity == '') ? 1 : opacity; } : function(){ var opacity, filter = (this.style.filter || this.getComputedStyle('filter')); if (filter) opacity = filter.match(reAlpha); return (opacity == null || filter == null) ? 1 : (opacity[1] / 100); } }; var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat'; Element.implement({ getComputedStyle: function(property){ if (this.currentStyle) return this.currentStyle[property.camelCase()]; var defaultView = Element.getDocument(this).defaultView, computed = defaultView ? defaultView.getComputedStyle(this, null) : null; return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : null; }, setOpacity: function(value){ setOpacity(this, value); return this; }, getOpacity: function(){ return this.get('opacity'); }, setStyle: function(property, value){ switch (property){ case 'opacity': return this.set('opacity', parseFloat(value)); case 'float': property = floatName; } property = property.camelCase(); if (typeOf(value) != 'string'){ var map = (Element.Styles[property] || '@').split(' '); value = Array.from(value).map(function(val, i){ if (!map[i]) return ''; return (typeOf(val) == 'number') ? map[i].replace('@', Math.round(val)) : val; }).join(' '); } else if (value == String(Number(value))){ value = Math.round(value); } this.style[property] = value; return this; }, getStyle: function(property){ switch (property){ case 'opacity': return this.get('opacity'); case 'float': property = floatName; } property = property.camelCase(); var result = this.style[property]; if (!result || property == 'zIndex'){ result = []; for (var style in Element.ShortStyles){ if (property != style) continue; for (var s in Element.ShortStyles[style]) result.push(this.getStyle(s)); return result.join(' '); } result = this.getComputedStyle(property); } if (result){ result = String(result); var color = result.match(/rgba?\([\d\s,]+\)/); if (color) result = result.replace(color[0], color[0].rgbToHex()); } if (Browser.opera || (Browser.ie && isNaN(parseFloat(result)))){ if ((/^(height|width)$/).test(property)){ var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0; values.each(function(value){ size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt(); }, this); return this['offset' + property.capitalize()] - size + 'px'; } if (Browser.opera && String(result).indexOf('px') != -1) return result; if ((/^border(.+)Width|margin|padding/).test(property)) return '0px'; } return result; }, setStyles: function(styles){ for (var style in styles) this.setStyle(style, styles[style]); return this; }, getStyles: function(){ var result = {}; Array.flatten(arguments).each(function(key){ result[key] = this.getStyle(key); }, this); return result; } }); Element.Styles = { left: '@px', top: '@px', bottom: '@px', right: '@px', width: '@px', height: '@px', maxWidth: '@px', maxHeight: '@px', minWidth: '@px', minHeight: '@px', backgroundColor: 'rgb(@, @, @)', backgroundPosition: '@px @px', color: 'rgb(@, @, @)', fontSize: '@px', letterSpacing: '@px', lineHeight: '@px', clip: 'rect(@px @px @px @px)', margin: '@px @px @px @px', padding: '@px @px @px @px', border: '@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)', borderWidth: '@px @px @px @px', borderStyle: '@ @ @ @', borderColor: 'rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)', zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@' }; Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}}; ['Top', 'Right', 'Bottom', 'Left'].each(function(direction){ var Short = Element.ShortStyles; var All = Element.Styles; ['margin', 'padding'].each(function(style){ var sd = style + direction; Short[style][sd] = All[sd] = '@px'; }); var bd = 'border' + direction; Short.border[bd] = All[bd] = '@px @ rgb(@, @, @)'; var bdw = bd + 'Width', bds = bd + 'Style', bdc = bd + 'Color'; Short[bd] = {}; Short.borderWidth[bdw] = Short[bd][bdw] = All[bdw] = '@px'; Short.borderStyle[bds] = Short[bd][bds] = All[bds] = '@'; Short.borderColor[bdc] = Short[bd][bdc] = All[bdc] = 'rgb(@, @, @)'; }); })(); /* --- name: Element.Event description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events. license: MIT-style license. requires: [Element, Event] provides: Element.Event ... */ (function(){ Element.Properties.events = {set: function(events){ this.addEvents(events); }}; [Element, Window, Document].invoke('implement', { addEvent: function(type, fn){ var events = this.retrieve('events', {}); if (!events[type]) events[type] = {keys: [], values: []}; if (events[type].keys.contains(fn)) return this; events[type].keys.push(fn); var realType = type, custom = Element.Events[type], condition = fn, self = this; if (custom){ if (custom.onAdd) custom.onAdd.call(this, fn); if (custom.condition){ condition = function(event){ if (custom.condition.call(this, event)) return fn.call(this, event); return true; }; } realType = custom.base || realType; } var defn = function(){ return fn.call(self); }; var nativeEvent = Element.NativeEvents[realType]; if (nativeEvent){ if (nativeEvent == 2){ defn = function(event){ event = new Event(event, self.getWindow()); if (condition.call(self, event) === false) event.stop(); }; } this.addListener(realType, defn, arguments[2]); } events[type].values.push(defn); return this; }, removeEvent: function(type, fn){ var events = this.retrieve('events'); if (!events || !events[type]) return this; var list = events[type]; var index = list.keys.indexOf(fn); if (index == -1) return this; var value = list.values[index]; delete list.keys[index]; delete list.values[index]; var custom = Element.Events[type]; if (custom){ if (custom.onRemove) custom.onRemove.call(this, fn); type = custom.base || type; } return (Element.NativeEvents[type]) ? this.removeListener(type, value, arguments[2]) : this; }, addEvents: function(events){ for (var event in events) this.addEvent(event, events[event]); return this; }, removeEvents: function(events){ var type; if (typeOf(events) == 'object'){ for (type in events) this.removeEvent(type, events[type]); return this; } var attached = this.retrieve('events'); if (!attached) return this; if (!events){ for (type in attached) this.removeEvents(type); this.eliminate('events'); } else if (attached[events]){ attached[events].keys.each(function(fn){ this.removeEvent(events, fn); }, this); delete attached[events]; } return this; }, fireEvent: function(type, args, delay){ var events = this.retrieve('events'); if (!events || !events[type]) return this; args = Array.from(args); events[type].keys.each(function(fn){ if (delay) fn.delay(delay, this, args); else fn.apply(this, args); }, this); return this; }, cloneEvents: function(from, type){ from = document.id(from); var events = from.retrieve('events'); if (!events) return this; if (!type){ for (var eventType in events) this.cloneEvents(from, eventType); } else if (events[type]){ events[type].keys.each(function(fn){ this.addEvent(type, fn); }, this); } return this; } }); Element.NativeEvents = { click: 2, dblclick: 2, mouseup: 2, mousedown: 2, contextmenu: 2, //mouse buttons mousewheel: 2, DOMMouseScroll: 2, //mouse wheel mouseover: 2, mouseout: 2, mousemove: 2, selectstart: 2, selectend: 2, //mouse movement keydown: 2, keypress: 2, keyup: 2, //keyboard orientationchange: 2, // mobile touchstart: 2, touchmove: 2, touchend: 2, touchcancel: 2, // touch gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window error: 1, abort: 1, scroll: 1 //misc }; var check = function(event){ var related = event.relatedTarget; if (related == null) return true; if (!related) return false; return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related)); }; Element.Events = { mouseenter: { base: 'mouseover', condition: check }, mouseleave: { base: 'mouseout', condition: check }, mousewheel: { base: (Browser.firefox) ? 'DOMMouseScroll' : 'mousewheel' } }; })(); /* --- name: Element.Dimensions description: Contains methods to work with size, scroll, or positioning of Elements and the window object. license: MIT-style license. credits: - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html). - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html). requires: [Element, Element.Style] provides: [Element.Dimensions] ... */ (function(){ var element = document.createElement('div'), child = document.createElement('div'); element.style.height = '0'; element.appendChild(child); var brokenOffsetParent = (child.offsetParent === element); element = child = null; var isOffset = function(el){ return styleString(el, 'position') != 'static' || isBody(el); }; var isOffsetStatic = function(el){ return isOffset(el) || (/^(?:table|td|th)$/i).test(el.tagName); }; Element.implement({ scrollTo: function(x, y){ if (isBody(this)){ this.getWindow().scrollTo(x, y); } else { this.scrollLeft = x; this.scrollTop = y; } return this; }, getSize: function(){ if (isBody(this)) return this.getWindow().getSize(); return {x: this.offsetWidth, y: this.offsetHeight}; }, getScrollSize: function(){ if (isBody(this)) return this.getWindow().getScrollSize(); return {x: this.scrollWidth, y: this.scrollHeight}; }, getScroll: function(){ if (isBody(this)) return this.getWindow().getScroll(); return {x: this.scrollLeft, y: this.scrollTop}; }, getScrolls: function(){ var element = this.parentNode, position = {x: 0, y: 0}; while (element && !isBody(element)){ position.x += element.scrollLeft; position.y += element.scrollTop; element = element.parentNode; } return position; }, getOffsetParent: brokenOffsetParent ? function(){ var element = this; if (isBody(element) || styleString(element, 'position') == 'fixed') return null; var isOffsetCheck = (styleString(element, 'position') == 'static') ? isOffsetStatic : isOffset; while ((element = element.parentNode)){ if (isOffsetCheck(element)) return element; } return null; } : function(){ var element = this; if (isBody(element) || styleString(element, 'position') == 'fixed') return null; try { return element.offsetParent; } catch(e) {} return null; }, getOffsets: function(){ if (this.getBoundingClientRect && !Browser.Platform.ios){ var bound = this.getBoundingClientRect(), html = document.id(this.getDocument().documentElement), htmlScroll = html.getScroll(), elemScrolls = this.getScrolls(), isFixed = (styleString(this, 'position') == 'fixed'); return { x: bound.left.toInt() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft, y: bound.top.toInt() + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop }; } var element = this, position = {x: 0, y: 0}; if (isBody(this)) return position; while (element && !isBody(element)){ position.x += element.offsetLeft; position.y += element.offsetTop; if (Browser.firefox){ if (!borderBox(element)){ position.x += leftBorder(element); position.y += topBorder(element); } var parent = element.parentNode; if (parent && styleString(parent, 'overflow') != 'visible'){ position.x += leftBorder(parent); position.y += topBorder(parent); } } else if (element != this && Browser.safari){ position.x += leftBorder(element); position.y += topBorder(element); } element = element.offsetParent; } if (Browser.firefox && !borderBox(this)){ position.x -= leftBorder(this); position.y -= topBorder(this); } return position; }, getPosition: function(relative){ if (isBody(this)) return {x: 0, y: 0}; var offset = this.getOffsets(), scroll = this.getScrolls(); var position = { x: offset.x - scroll.x, y: offset.y - scroll.y }; if (relative && (relative = document.id(relative))){ var relativePosition = relative.getPosition(); return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)}; } return position; }, getCoordinates: function(element){ if (isBody(this)) return this.getWindow().getCoordinates(); var position = this.getPosition(element), size = this.getSize(); var obj = { left: position.x, top: position.y, width: size.x, height: size.y }; obj.right = obj.left + obj.width; obj.bottom = obj.top + obj.height; return obj; }, computePosition: function(obj){ return { left: obj.x - styleNumber(this, 'margin-left'), top: obj.y - styleNumber(this, 'margin-top') }; }, setPosition: function(obj){ return this.setStyles(this.computePosition(obj)); } }); [Document, Window].invoke('implement', { getSize: function(){ var doc = getCompatElement(this); return {x: doc.clientWidth, y: doc.clientHeight}; }, getScroll: function(){ var win = this.getWindow(), doc = getCompatElement(this); return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop}; }, getScrollSize: function(){ var doc = getCompatElement(this), min = this.getSize(), body = this.getDocument().body; return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.scrollHeight, min.y)}; }, getPosition: function(){ return {x: 0, y: 0}; }, getCoordinates: function(){ var size = this.getSize(); return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x}; } }); // private methods var styleString = Element.getComputedStyle; function styleNumber(element, style){ return styleString(element, style).toInt() || 0; } function borderBox(element){ return styleString(element, '-moz-box-sizing') == 'border-box'; } function topBorder(element){ return styleNumber(element, 'border-top-width'); } function leftBorder(element){ return styleNumber(element, 'border-left-width'); } function isBody(element){ return (/^(?:body|html)$/i).test(element.tagName); } function getCompatElement(element){ var doc = element.getDocument(); return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body; } })(); //aliases Element.alias({position: 'setPosition'}); //compatability [Window, Document, Element].invoke('implement', { getHeight: function(){ return this.getSize().y; }, getWidth: function(){ return this.getSize().x; }, getScrollTop: function(){ return this.getScroll().y; }, getScrollLeft: function(){ return this.getScroll().x; }, getScrollHeight: function(){ return this.getScrollSize().y; }, getScrollWidth: function(){ return this.getScrollSize().x; }, getTop: function(){ return this.getPosition().y; }, getLeft: function(){ return this.getPosition().x; } }); /* --- name: Fx description: Contains the basic animation logic to be extended by all other Fx Classes. license: MIT-style license. requires: [Chain, Events, Options] provides: Fx ... */ (function(){ var Fx = this.Fx = new Class({ Implements: [Chain, Events, Options], options: { /* onStart: nil, onCancel: nil, onComplete: nil, */ fps: 60, unit: false, duration: 500, frames: null, frameSkip: true, link: 'ignore' }, initialize: function(options){ this.subject = this.subject || this; this.setOptions(options); }, getTransition: function(){ return function(p){ return -(Math.cos(Math.PI * p) - 1) / 2; }; }, step: function(now){ if (this.options.frameSkip){ var diff = (this.time != null) ? (now - this.time) : 0, frames = diff / this.frameInterval; this.time = now; this.frame += frames; } else { this.frame++; } if (this.frame < this.frames){ var delta = this.transition(this.frame / this.frames); this.set(this.compute(this.from, this.to, delta)); } else { this.frame = this.frames; this.set(this.compute(this.from, this.to, 1)); this.stop(); } }, set: function(now){ return now; }, compute: function(from, to, delta){ return Fx.compute(from, to, delta); }, check: function(){ if (!this.isRunning()) return true; switch (this.options.link){ case 'cancel': this.cancel(); return true; case 'chain': this.chain(this.caller.pass(arguments, this)); return false; } return false; }, start: function(from, to){ if (!this.check(from, to)) return this; this.from = from; this.to = to; this.frame = (this.options.frameSkip) ? 0 : -1; this.time = null; this.transition = this.getTransition(); var frames = this.options.frames, fps = this.options.fps, duration = this.options.duration; this.duration = Fx.Durations[duration] || duration.toInt(); this.frameInterval = 1000 / fps; this.frames = frames || Math.round(this.duration / this.frameInterval); this.fireEvent('start', this.subject); pushInstance.call(this, fps); return this; }, stop: function(){ if (this.isRunning()){ this.time = null; pullInstance.call(this, this.options.fps); if (this.frames == this.frame){ this.fireEvent('complete', this.subject); if (!this.callChain()) this.fireEvent('chainComplete', this.subject); } else { this.fireEvent('stop', this.subject); } } return this; }, cancel: function(){ if (this.isRunning()){ this.time = null; pullInstance.call(this, this.options.fps); this.frame = this.frames; this.fireEvent('cancel', this.subject).clearChain(); } return this; }, pause: function(){ if (this.isRunning()){ this.time = null; pullInstance.call(this, this.options.fps); } return this; }, resume: function(){ if ((this.frame < this.frames) && !this.isRunning()) pushInstance.call(this, this.options.fps); return this; }, isRunning: function(){ var list = instances[this.options.fps]; return list && list.contains(this); } }); Fx.compute = function(from, to, delta){ return (to - from) * delta + from; }; Fx.Durations = {'short': 250, 'normal': 500, 'long': 1000}; // global timers var instances = {}, timers = {}; var loop = function(){ var now = Date.now(); for (var i = this.length; i--;){ var instance = this[i]; if (instance) instance.step(now); } }; var pushInstance = function(fps){ var list = instances[fps] || (instances[fps] = []); list.push(this); if (!timers[fps]) timers[fps] = loop.periodical(Math.round(1000 / fps), list); }; var pullInstance = function(fps){ var list = instances[fps]; if (list){ list.erase(this); if (!list.length && timers[fps]){ delete instances[fps]; timers[fps] = clearInterval(timers[fps]); } } }; })(); /* --- name: Fx.CSS description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements. license: MIT-style license. requires: [Fx, Element.Style] provides: Fx.CSS ... */ Fx.CSS = new Class({ Extends: Fx, //prepares the base from/to object prepare: function(element, property, values){ values = Array.from(values); if (values[1] == null){ values[1] = values[0]; values[0] = element.getStyle(property); } var parsed = values.map(this.parse); return {from: parsed[0], to: parsed[1]}; }, //parses a value into an array parse: function(value){ value = Function.from(value)(); value = (typeof value == 'string') ? value.split(' ') : Array.from(value); return value.map(function(val){ val = String(val); var found = false; Object.each(Fx.CSS.Parsers, function(parser, key){ if (found) return; var parsed = parser.parse(val); if (parsed || parsed === 0) found = {value: parsed, parser: parser}; }); found = found || {value: val, parser: Fx.CSS.Parsers.String}; return found; }); }, //computes by a from and to prepared objects, using their parsers. compute: function(from, to, delta){ var computed = []; (Math.min(from.length, to.length)).times(function(i){ computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser}); }); computed.$family = Function.from('fx:css:value'); return computed; }, //serves the value as settable serve: function(value, unit){ if (typeOf(value) != 'fx:css:value') value = this.parse(value); var returned = []; value.each(function(bit){ returned = returned.concat(bit.parser.serve(bit.value, unit)); }); return returned; }, //renders the change to an element render: function(element, property, value, unit){ element.setStyle(property, this.serve(value, unit)); }, //searches inside the page css to find the values for a selector search: function(selector){ if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector]; var to = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$'); Array.each(document.styleSheets, function(sheet, j){ var href = sheet.href; if (href && href.contains('://') && !href.contains(document.domain)) return; var rules = sheet.rules || sheet.cssRules; Array.each(rules, function(rule, i){ if (!rule.style) return; var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){ return m.toLowerCase(); }) : null; if (!selectorText || !selectorTest.test(selectorText)) return; Object.each(Element.Styles, function(value, style){ if (!rule.style[style] || Element.ShortStyles[style]) return; value = String(rule.style[style]); to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value; }); }); }); return Fx.CSS.Cache[selector] = to; } }); Fx.CSS.Cache = {}; Fx.CSS.Parsers = { Color: { parse: function(value){ if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true); return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false; }, compute: function(from, to, delta){ return from.map(function(value, i){ return Math.round(Fx.compute(from[i], to[i], delta)); }); }, serve: function(value){ return value.map(Number); } }, Number: { parse: parseFloat, compute: Fx.compute, serve: function(value, unit){ return (unit) ? value + unit : value; } }, String: { parse: Function.from(false), compute: function(zero, one){ return one; }, serve: function(zero){ return zero; } } }; /* --- name: Fx.Tween description: Formerly Fx.Style, effect to transition any CSS property for an element. license: MIT-style license. requires: Fx.CSS provides: [Fx.Tween, Element.fade, Element.highlight] ... */ Fx.Tween = new Class({ Extends: Fx.CSS, initialize: function(element, options){ this.element = this.subject = document.id(element); this.parent(options); }, set: function(property, now){ if (arguments.length == 1){ now = property; property = this.property || this.options.property; } this.render(this.element, property, now, this.options.unit); return this; }, start: function(property, from, to){ if (!this.check(property, from, to)) return this; var args = Array.flatten(arguments); this.property = this.options.property || args.shift(); var parsed = this.prepare(this.element, this.property, args); return this.parent(parsed.from, parsed.to); } }); Element.Properties.tween = { set: function(options){ this.get('tween').cancel().setOptions(options); return this; }, get: function(){ var tween = this.retrieve('tween'); if (!tween){ tween = new Fx.Tween(this, {link: 'cancel'}); this.store('tween', tween); } return tween; } }; Element.implement({ tween: function(property, from, to){ this.get('tween').start(arguments); return this; }, fade: function(how){ var fade = this.get('tween'), o = 'opacity', toggle; how = [how, 'toggle'].pick(); switch (how){ case 'in': fade.start(o, 1); break; case 'out': fade.start(o, 0); break; case 'show': fade.set(o, 1); break; case 'hide': fade.set(o, 0); break; case 'toggle': var flag = this.retrieve('fade:flag', this.get('opacity') == 1); fade.start(o, (flag) ? 0 : 1); this.store('fade:flag', !flag); toggle = true; break; default: fade.start(o, arguments); } if (!toggle) this.eliminate('fade:flag'); return this; }, highlight: function(start, end){ if (!end){ end = this.retrieve('highlight:original', this.getStyle('background-color')); end = (end == 'transparent') ? '#fff' : end; } var tween = this.get('tween'); tween.start('background-color', start || '#ffff88', end).chain(function(){ this.setStyle('background-color', this.retrieve('highlight:original')); tween.callChain(); }.bind(this)); return this; } }); /* --- name: Fx.Morph description: Formerly Fx.Styles, effect to transition any number of CSS properties for an element using an object of rules, or CSS based selector rules. license: MIT-style license. requires: Fx.CSS provides: Fx.Morph ... */ Fx.Morph = new Class({ Extends: Fx.CSS, initialize: function(element, options){ this.element = this.subject = document.id(element); this.parent(options); }, set: function(now){ if (typeof now == 'string') now = this.search(now); for (var p in now) this.render(this.element, p, now[p], this.options.unit); return this; }, compute: function(from, to, delta){ var now = {}; for (var p in from) now[p] = this.parent(from[p], to[p], delta); return now; }, start: function(properties){ if (!this.check(properties)) return this; if (typeof properties == 'string') properties = this.search(properties); var from = {}, to = {}; for (var p in properties){ var parsed = this.prepare(this.element, p, properties[p]); from[p] = parsed.from; to[p] = parsed.to; } return this.parent(from, to); } }); Element.Properties.morph = { set: function(options){ this.get('morph').cancel().setOptions(options); return this; }, get: function(){ var morph = this.retrieve('morph'); if (!morph){ morph = new Fx.Morph(this, {link: 'cancel'}); this.store('morph', morph); } return morph; } }; Element.implement({ morph: function(props){ this.get('morph').start(props); return this; } }); /* --- name: Fx.Transitions description: Contains a set of advanced transitions to be used with any of the Fx Classes. license: MIT-style license. credits: - Easing Equations by Robert Penner, , modified and optimized to be used with MooTools. requires: Fx provides: Fx.Transitions ... */ Fx.implement({ getTransition: function(){ var trans = this.options.transition || Fx.Transitions.Sine.easeInOut; if (typeof trans == 'string'){ var data = trans.split(':'); trans = Fx.Transitions; trans = trans[data[0]] || trans[data[0].capitalize()]; if (data[1]) trans = trans['ease' + data[1].capitalize() + (data[2] ? data[2].capitalize() : '')]; } return trans; } }); Fx.Transition = function(transition, params){ params = Array.from(params); var easeIn = function(pos){ return transition(pos, params); }; return Object.append(easeIn, { easeIn: easeIn, easeOut: function(pos){ return 1 - transition(1 - pos, params); }, easeInOut: function(pos){ return (pos <= 0.5 ? transition(2 * pos, params) : (2 - transition(2 * (1 - pos), params))) / 2; } }); }; Fx.Transitions = { linear: function(zero){ return zero; } }; Fx.Transitions.extend = function(transitions){ for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]); }; Fx.Transitions.extend({ Pow: function(p, x){ return Math.pow(p, x && x[0] || 6); }, Expo: function(p){ return Math.pow(2, 8 * (p - 1)); }, Circ: function(p){ return 1 - Math.sin(Math.acos(p)); }, Sine: function(p){ return 1 - Math.cos(p * Math.PI / 2); }, Back: function(p, x){ x = x && x[0] || 1.618; return Math.pow(p, 2) * ((x + 1) * p - x); }, Bounce: function(p){ var value; for (var a = 0, b = 1; 1; a += b, b /= 2){ if (p >= (7 - 4 * a) / 11){ value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2); break; } } return value; }, Elastic: function(p, x){ return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x && x[0] || 1) / 3); } }); ['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){ Fx.Transitions[transition] = new Fx.Transition(function(p){ return Math.pow(p, i + 2); }); }); /* --- name: Request description: Powerful all purpose Request Class. Uses XMLHTTPRequest. license: MIT-style license. requires: [Object, Element, Chain, Events, Options, Browser] provides: Request ... */ (function(){ var empty = function(){}, progressSupport = ('onprogress' in new Browser.Request); var Request = this.Request = new Class({ Implements: [Chain, Events, Options], options: {/* onRequest: function(){}, onLoadstart: function(event, xhr){}, onProgress: function(event, xhr){}, onComplete: function(){}, onCancel: function(){}, onSuccess: function(responseText, responseXML){}, onFailure: function(xhr){}, onException: function(headerName, value){}, onTimeout: function(){}, user: '', password: '',*/ url: '', data: '', headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' }, async: true, format: false, method: 'post', link: 'ignore', isSuccess: null, emulation: true, urlEncoded: true, encoding: 'utf-8', evalScripts: false, evalResponse: false, timeout: 0, noCache: false }, initialize: function(options){ this.xhr = new Browser.Request(); this.setOptions(options); this.headers = this.options.headers; }, onStateChange: function(){ var xhr = this.xhr; if (xhr.readyState != 4 || !this.running) return; this.running = false; this.status = 0; Function.attempt(function(){ var status = xhr.status; this.status = (status == 1223) ? 204 : status; }.bind(this)); xhr.onreadystatechange = empty; if (progressSupport) xhr.onprogress = xhr.onloadstart = empty; clearTimeout(this.timer); this.response = {text: this.xhr.responseText || '', xml: this.xhr.responseXML}; if (this.options.isSuccess.call(this, this.status)) this.success(this.response.text, this.response.xml); else this.failure(); }, isSuccess: function(){ var status = this.status; return (status >= 200 && status < 300); }, isRunning: function(){ return !!this.running; }, processScripts: function(text){ if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return Browser.exec(text); return text.stripScripts(this.options.evalScripts); }, success: function(text, xml){ this.onSuccess(this.processScripts(text), xml); }, onSuccess: function(){ this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain(); }, failure: function(){ this.onFailure(); }, onFailure: function(){ this.fireEvent('complete').fireEvent('failure', this.xhr); }, loadstart: function(event){ this.fireEvent('loadstart', [event, this.xhr]); }, progress: function(event){ this.fireEvent('progress', [event, this.xhr]); }, timeout: function(){ this.fireEvent('timeout', this.xhr); }, setHeader: function(name, value){ this.headers[name] = value; return this; }, getHeader: function(name){ return Function.attempt(function(){ return this.xhr.getResponseHeader(name); }.bind(this)); }, check: function(){ if (!this.running) return true; switch (this.options.link){ case 'cancel': this.cancel(); return true; case 'chain': this.chain(this.caller.pass(arguments, this)); return false; } return false; }, send: function(options){ if (!this.check(options)) return this; this.options.isSuccess = this.options.isSuccess || this.isSuccess; this.running = true; var type = typeOf(options); if (type == 'string' || type == 'element') options = {data: options}; var old = this.options; options = Object.append({data: old.data, url: old.url, method: old.method}, options); var data = options.data, url = String(options.url), method = options.method.toLowerCase(); switch (typeOf(data)){ case 'element': data = document.id(data).toQueryString(); break; case 'object': case 'hash': data = Object.toQueryString(data); } if (this.options.format){ var format = 'format=' + this.options.format; data = (data) ? format + '&' + data : format; } if (this.options.emulation && !['get', 'post'].contains(method)){ var _method = '_method=' + method; data = (data) ? _method + '&' + data : _method; method = 'post'; } if (this.options.urlEncoded && ['post', 'put'].contains(method)){ var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : ''; this.headers['Content-type'] = 'application/x-www-form-urlencoded' + encoding; } if (!url) url = document.location.pathname; var trimPosition = url.lastIndexOf('/'); if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition); if (this.options.noCache) url += (url.contains('?') ? '&' : '?') + String.uniqueID(); if (data && method == 'get'){ url += (url.contains('?') ? '&' : '?') + data; data = null; } var xhr = this.xhr; if (progressSupport){ xhr.onloadstart = this.loadstart.bind(this); xhr.onprogress = this.progress.bind(this); } xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password); if (this.options.user && 'withCredentials' in xhr) xhr.withCredentials = true; xhr.onreadystatechange = this.onStateChange.bind(this); Object.each(this.headers, function(value, key){ try { xhr.setRequestHeader(key, value); } catch (e){ this.fireEvent('exception', [key, value]); } }, this); this.fireEvent('request'); xhr.send(data); if (!this.options.async) this.onStateChange(); if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this); return this; }, cancel: function(){ if (!this.running) return this; this.running = false; var xhr = this.xhr; xhr.abort(); clearTimeout(this.timer); xhr.onreadystatechange = empty; if (progressSupport) xhr.onprogress = xhr.onloadstart = empty; this.xhr = new Browser.Request(); this.fireEvent('cancel'); return this; } }); var methods = {}; ['get', 'post', 'put', 'delete', 'GET', 'POST', 'PUT', 'DELETE'].each(function(method){ methods[method] = function(data){ var object = { method: method }; if (data != null) object.data = data; return this.send(object); }; }); Request.implement(methods); Element.Properties.send = { set: function(options){ var send = this.get('send').cancel(); send.setOptions(options); return this; }, get: function(){ var send = this.retrieve('send'); if (!send){ send = new Request({ data: this, link: 'cancel', method: this.get('method') || 'post', url: this.get('action') }); this.store('send', send); } return send; } }; Element.implement({ send: function(url){ var sender = this.get('send'); sender.send({data: this, url: url || sender.options.url}); return this; } }); })(); /* --- name: Request.HTML description: Extends the basic Request Class with additional methods for interacting with HTML responses. license: MIT-style license. requires: [Element, Request] provides: Request.HTML ... */ Request.HTML = new Class({ Extends: Request, options: { update: false, append: false, evalScripts: true, filter: false, headers: { Accept: 'text/html, application/xml, text/xml, */*' } }, success: function(text){ var options = this.options, response = this.response; response.html = text.stripScripts(function(script){ response.javascript = script; }); var match = response.html.match(/]*>([\s\S]*?)<\/body>/i); if (match) response.html = match[1]; var temp = new Element('div').set('html', response.html); response.tree = temp.childNodes; response.elements = temp.getElements('*'); if (options.filter) response.tree = response.elements.filter(options.filter); if (options.update) document.id(options.update).empty().set('html', response.html); else if (options.append) document.id(options.append).adopt(temp.getChildren()); if (options.evalScripts) Browser.exec(response.javascript); this.onSuccess(response.tree, response.elements, response.html, response.javascript); } }); Element.Properties.load = { set: function(options){ var load = this.get('load').cancel(); load.setOptions(options); return this; }, get: function(){ var load = this.retrieve('load'); if (!load){ load = new Request.HTML({data: this, link: 'cancel', update: this, method: 'get'}); this.store('load', load); } return load; } }; Element.implement({ load: function(){ this.get('load').send(Array.link(arguments, {data: Type.isObject, url: Type.isString})); return this; } }); /* --- name: JSON description: JSON encoder and decoder. license: MIT-style license. See Also: requires: [Array, String, Number, Function] provides: JSON ... */ if (typeof JSON == 'undefined') this.JSON = {}; (function(){ var special = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'}; var escape = function(chr){ return special[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4); }; JSON.validate = function(string){ string = string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). replace(/(?:^|:|,)(?:\s*\[)+/g, ''); return (/^[\],:{}\s]*$/).test(string); }; JSON.encode = JSON.stringify ? function(obj){ return JSON.stringify(obj); } : function(obj){ if (obj && obj.toJSON) obj = obj.toJSON(); switch (typeOf(obj)){ case 'string': return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"'; case 'array': return '[' + obj.map(JSON.encode).clean() + ']'; case 'object': case 'hash': var string = []; Object.each(obj, function(value, key){ var json = JSON.encode(value); if (json) string.push(JSON.encode(key) + ':' + json); }); return '{' + string + '}'; case 'number': case 'boolean': return '' + obj; case 'null': return 'null'; } return null; }; JSON.decode = function(string, secure){ if (!string || typeOf(string) != 'string') return null; if (secure || JSON.secure){ if (JSON.parse) return JSON.parse(string); if (!JSON.validate(string)) throw new Error('JSON could not decode the input; security is enabled and the value is not secure.'); } return eval('(' + string + ')'); }; })(); /* --- name: Request.JSON description: Extends the basic Request Class with additional methods for sending and receiving JSON data. license: MIT-style license. requires: [Request, JSON] provides: Request.JSON ... */ Request.JSON = new Class({ Extends: Request, options: { /*onError: function(text, error){},*/ secure: true }, initialize: function(options){ this.parent(options); Object.append(this.headers, { 'Accept': 'application/json', 'X-Request': 'JSON' }); }, success: function(text){ var json; try { json = this.response.json = JSON.decode(text, this.options.secure); } catch (error){ this.fireEvent('error', [text, error]); return; } if (json == null) this.onFailure(); else this.onSuccess(json, text); } }); /* --- name: Cookie description: Class for creating, reading, and deleting browser Cookies. license: MIT-style license. credits: - Based on the functions by Peter-Paul Koch (http://quirksmode.org). requires: [Options, Browser] provides: Cookie ... */ var Cookie = new Class({ Implements: Options, options: { path: '/', domain: false, duration: false, secure: false, document: document, encode: true }, initialize: function(key, options){ this.key = key; this.setOptions(options); }, write: function(value){ if (this.options.encode) value = encodeURIComponent(value); if (this.options.domain) value += '; domain=' + this.options.domain; if (this.options.path) value += '; path=' + this.options.path; if (this.options.duration){ var date = new Date(); date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000); value += '; expires=' + date.toGMTString(); } if (this.options.secure) value += '; secure'; this.options.document.cookie = this.key + '=' + value; return this; }, read: function(){ var value = this.options.document.cookie.match('(?:^|;)\\s*' + this.key.escapeRegExp() + '=([^;]*)'); return (value) ? decodeURIComponent(value[1]) : null; }, dispose: function(){ new Cookie(this.key, Object.merge({}, this.options, {duration: -1})).write(''); return this; } }); Cookie.write = function(key, value, options){ return new Cookie(key, options).write(value); }; Cookie.read = function(key){ return new Cookie(key).read(); }; Cookie.dispose = function(key, options){ return new Cookie(key, options).dispose(); }; /* --- name: DOMReady description: Contains the custom event domready. license: MIT-style license. requires: [Browser, Element, Element.Event] provides: [DOMReady, DomReady] ... */ (function(window, document){ var ready, loaded, checks = [], shouldPoll, timer, testElement = document.createElement('div'); var domready = function(){ clearTimeout(timer); if (ready) return; Browser.loaded = ready = true; document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check); document.fireEvent('domready'); window.fireEvent('domready'); }; var check = function(){ for (var i = checks.length; i--;) if (checks[i]()){ domready(); return true; } return false; }; var poll = function(){ clearTimeout(timer); if (!check()) timer = setTimeout(poll, 10); }; document.addListener('DOMContentLoaded', domready); /**/ // doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/ // testElement.doScroll() throws when the DOM is not ready, only in the top window var doScrollWorks = function(){ try { testElement.doScroll(); return true; } catch (e){} return false; } // If doScroll works already, it can't be used to determine domready // e.g. in an iframe if (testElement.doScroll && !doScrollWorks()){ checks.push(doScrollWorks); shouldPoll = true; } /**/ if (document.readyState) checks.push(function(){ var state = document.readyState; return (state == 'loaded' || state == 'complete'); }); if ('onreadystatechange' in document) document.addListener('readystatechange', check); else shouldPoll = true; if (shouldPoll) poll(); Element.Events.domready = { onAdd: function(fn){ if (ready) fn.call(this); } }; // Make sure that domready fires before load Element.Events.load = { base: 'load', onAdd: function(fn){ if (loaded && this == window) fn.call(this); }, condition: function(){ if (this == window){ domready(); delete Element.Events.load; } return true; } }; // This is based on the custom load event window.addEvent('load', function(){ loaded = true; }); })(window, document); /* --- name: Swiff description: Wrapper for embedding SWF movies. Supports External Interface Communication. license: MIT-style license. credits: - Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject. requires: [Options, Object, Element] provides: Swiff ... */ (function(){ var Swiff = this.Swiff = new Class({ Implements: Options, options: { id: null, height: 1, width: 1, container: null, properties: {}, params: { quality: 'high', allowScriptAccess: 'always', wMode: 'window', swLiveConnect: true }, callBacks: {}, vars: {} }, toElement: function(){ return this.object; }, initialize: function(path, options){ this.instance = 'Swiff_' + String.uniqueID(); this.setOptions(options); options = this.options; var id = this.id = options.id || this.instance; var container = document.id(options.container); Swiff.CallBacks[this.instance] = {}; var params = options.params, vars = options.vars, callBacks = options.callBacks; var properties = Object.append({height: options.height, width: options.width}, options.properties); var self = this; for (var callBack in callBacks){ Swiff.CallBacks[this.instance][callBack] = (function(option){ return function(){ return option.apply(self.object, arguments); }; })(callBacks[callBack]); vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack; } params.flashVars = Object.toQueryString(vars); if (Browser.ie){ properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'; params.movie = path; } else { properties.type = 'application/x-shockwave-flash'; } properties.data = path; var build = ''; } build += ''; this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild; }, replaces: function(element){ element = document.id(element, true); element.parentNode.replaceChild(this.toElement(), element); return this; }, inject: function(element){ document.id(element, true).appendChild(this.toElement()); return this; }, remote: function(){ return Swiff.remote.apply(Swiff, [this.toElement()].append(arguments)); } }); Swiff.CallBacks = {}; Swiff.remote = function(obj, fn){ var rs = obj.CallFunction('' + __flash__argumentsToXML(arguments, 2) + ''); return eval(rs); }; })(); ZoneMinder-1.26.5/web/tools/mootools/mootools-core-1.3.2-yc.js000066400000000000000000002412731225361755400237010ustar00rootroot00000000000000/* --- MooTools: the javascript framework web build: - http://mootools.net/core/7c56cfef9dddcf170a5d68e3fb61cfd7 packager build: - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady Core/Swiff copyrights: - [MooTools](http://mootools.net) licenses: - [MIT License](http://mootools.net/license.txt) ... */ (function(){this.MooTools={version:"1.3.2",build:"c9f1ff10e9e7facb65e9481049ed1b450959d587"};var o=this.typeOf=function(i){if(i==null){return"null";}if(i.$family){return i.$family(); }if(i.nodeName){if(i.nodeType==1){return"element";}if(i.nodeType==3){return(/\S/).test(i.nodeValue)?"textnode":"whitespace";}}else{if(typeof i.length=="number"){if(i.callee){return"arguments"; }if("item" in i){return"collection";}}}return typeof i;};var j=this.instanceOf=function(t,i){if(t==null){return false;}var s=t.$constructor||t.constructor; while(s){if(s===i){return true;}s=s.parent;}return t instanceof i;};var f=this.Function;var p=true;for(var k in {toString:1}){p=null;}if(p){p=["hasOwnProperty","valueOf","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","constructor"]; }f.prototype.overloadSetter=function(s){var i=this;return function(u,t){if(u==null){return this;}if(s||typeof u!="string"){for(var v in u){i.call(this,v,u[v]); }if(p){for(var w=p.length;w--;){v=p[w];if(u.hasOwnProperty(v)){i.call(this,v,u[v]);}}}}else{i.call(this,u,t);}return this;};};f.prototype.overloadGetter=function(s){var i=this; return function(u){var v,t;if(s||typeof u!="string"){v=u;}else{if(arguments.length>1){v=arguments;}}if(v){t={};for(var w=0;w-1:this.indexOf(a)>-1;},trim:function(){return this.replace(/^\s+|\s+$/g,"");},clean:function(){return this.replace(/\s+/g," ").trim(); },camelCase:function(){return this.replace(/-\D/g,function(a){return a.charAt(1).toUpperCase();});},hyphenate:function(){return this.replace(/[A-Z]/g,function(a){return("-"+a.charAt(0).toLowerCase()); });},capitalize:function(){return this.replace(/\b[a-z]/g,function(a){return a.toUpperCase();});},escapeRegExp:function(){return this.replace(/([-.*+?^${}()|[\]\/\\])/g,"\\$1"); },toInt:function(a){return parseInt(this,a||10);},toFloat:function(){return parseFloat(this);},hexToRgb:function(b){var a=this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); return(a)?a.slice(1).hexToRgb(b):null;},rgbToHex:function(b){var a=this.match(/\d{1,3}/g);return(a)?a.rgbToHex(b):null;},substitute:function(a,b){return this.replace(b||(/\\?\{([^{}]+)\}/g),function(d,c){if(d.charAt(0)=="\\"){return d.slice(1); }return(a[c]!=null)?a[c]:"";});}});Number.implement({limit:function(b,a){return Math.min(a,Math.max(b,this));},round:function(a){a=Math.pow(10,a||0).toFixed(a<0?-a:0); return Math.round(this*a)/a;},times:function(b,c){for(var a=0;a1)?Array.slice(arguments,1):null;return function(){if(!b&&!arguments.length){return a.call(c);}if(b&&arguments.length){return a.apply(c,b.concat(Array.from(arguments))); }return a.apply(c,b||arguments);};},pass:function(b,c){var a=this;if(b!=null){b=Array.from(b);}return function(){return a.apply(c,b||arguments);};},delay:function(b,c,a){return setTimeout(this.pass((a==null?[]:a),c),b); },periodical:function(c,b,a){return setInterval(this.pass((a==null?[]:a),b),c);}});(function(){var a=Object.prototype.hasOwnProperty;Object.extend({subset:function(d,g){var f={}; for(var e=0,b=g.length;e]*>([\s\S]*?)<\/script>/gi,function(r,s){e+=s+"\n"; return"";});if(p===true){o.exec(e);}else{if(typeOf(p)=="function"){p(e,q);}}return q;});o.extend({Document:this.Document,Window:this.Window,Element:this.Element,Event:this.Event}); this.Window=this.$constructor=new Type("Window",function(){});this.$family=Function.from("window").hide();Window.mirror(function(e,p){i[e]=p;});this.Document=k.$constructor=new Type("Document",function(){}); k.$family=Function.from("document").hide();Document.mirror(function(e,p){k[e]=p;});k.html=k.documentElement;if(!k.head){k.head=k.getElementsByTagName("head")[0]; }if(k.execCommand){try{k.execCommand("BackgroundImageCache",false,true);}catch(g){}}if(this.attachEvent&&!this.addEventListener){var d=function(){this.detachEvent("onunload",d); k.head=k.html=k.window=null;};this.attachEvent("onunload",d);}var m=Array.from;try{m(k.html.childNodes);}catch(g){Array.from=function(p){if(typeof p!="string"&&Type.isEnumerable(p)&&typeOf(p)!="array"){var e=p.length,q=new Array(e); while(e--){q[e]=p[e];}return q;}return m(p);};var l=Array.prototype,n=l.slice;["pop","push","reverse","shift","sort","splice","unshift","concat","join","slice"].each(function(e){var p=l[e]; Array[e]=function(q){return p.apply(Array.from(q),n.call(arguments,1));};});}})();var Event=new Type("Event",function(a,i){if(!i){i=window;}var o=i.document; a=a||i.event;if(a.$extended){return a;}this.$extended=true;var n=a.type,k=a.target||a.srcElement,m={},c={},q=null,h,l,b,p;while(k&&k.nodeType==3){k=k.parentNode; }if(n.indexOf("key")!=-1){b=a.which||a.keyCode;p=Object.keyOf(Event.Keys,b);if(n=="keydown"){var d=b-111;if(d>0&&d<13){p="f"+d;}}if(!p){p=String.fromCharCode(b).toLowerCase(); }}else{if((/click|mouse|menu/i).test(n)){o=(!o.compatMode||o.compatMode=="CSS1Compat")?o.html:o.body;m={x:(a.pageX!=null)?a.pageX:a.clientX+o.scrollLeft,y:(a.pageY!=null)?a.pageY:a.clientY+o.scrollTop}; c={x:(a.pageX!=null)?a.pageX-i.pageXOffset:a.clientX,y:(a.pageY!=null)?a.pageY-i.pageYOffset:a.clientY};if((/DOMMouseScroll|mousewheel/).test(n)){l=(a.wheelDelta)?a.wheelDelta/120:-(a.detail||0)/3; }h=(a.which==3)||(a.button==2);if((/over|out/).test(n)){q=a.relatedTarget||a[(n=="mouseover"?"from":"to")+"Element"];var j=function(){while(q&&q.nodeType==3){q=q.parentNode; }return true;};var g=(Browser.firefox2)?j.attempt():j();q=(g)?q:null;}}else{if((/gesture|touch/i).test(n)){this.rotation=a.rotation;this.scale=a.scale; this.targetTouches=a.targetTouches;this.changedTouches=a.changedTouches;var f=this.touches=a.touches;if(f&&f[0]){var e=f[0];m={x:e.pageX,y:e.pageY};c={x:e.clientX,y:e.clientY}; }}}}return Object.append(this,{event:a,type:n,page:m,client:c,rightClick:h,wheel:l,relatedTarget:document.id(q),target:document.id(k),code:b,key:p,shift:a.shiftKey,control:a.ctrlKey,alt:a.altKey,meta:a.metaKey}); });Event.Keys={enter:13,up:38,down:40,left:37,right:39,esc:27,space:32,backspace:8,tab:9,"delete":46};Event.implement({stop:function(){return this.stopPropagation().preventDefault(); },stopPropagation:function(){if(this.event.stopPropagation){this.event.stopPropagation();}else{this.event.cancelBubble=true;}return this;},preventDefault:function(){if(this.event.preventDefault){this.event.preventDefault(); }else{this.event.returnValue=false;}return this;}});(function(){var a=this.Class=new Type("Class",function(h){if(instanceOf(h,Function)){h={initialize:h}; }var g=function(){e(this);if(g.$prototyping){return this;}this.$caller=null;var i=(this.initialize)?this.initialize.apply(this,arguments):this;this.$caller=this.caller=null; return i;}.extend(this).implement(h);g.$constructor=a;g.prototype.$constructor=g;g.prototype.parent=c;return g;});var c=function(){if(!this.$caller){throw new Error('The method "parent" cannot be called.'); }var g=this.$caller.$name,h=this.$caller.$owner.parent,i=(h)?h.prototype[g]:null;if(!i){throw new Error('The method "'+g+'" has no parent.');}return i.apply(this,arguments); };var e=function(g){for(var h in g){var j=g[h];switch(typeOf(j)){case"object":var i=function(){};i.prototype=j;g[h]=e(new i);break;case"array":g[h]=j.clone(); break;}}return g;};var b=function(g,h,j){if(j.$origin){j=j.$origin;}var i=function(){if(j.$protected&&this.$caller==null){throw new Error('The method "'+h+'" cannot be called.'); }var l=this.caller,m=this.$caller;this.caller=m;this.$caller=i;var k=j.apply(this,arguments);this.$caller=m;this.caller=l;return k;}.extend({$owner:g,$origin:j,$name:h}); return i;};var f=function(h,i,g){if(a.Mutators.hasOwnProperty(h)){i=a.Mutators[h].call(this,i);if(i==null){return this;}}if(typeOf(i)=="function"){if(i.$hidden){return this; }this.prototype[h]=(g)?i:b(this,h,i);}else{Object.merge(this.prototype,h,i);}return this;};var d=function(g){g.$prototyping=true;var h=new g;delete g.$prototyping; return h;};a.implement("implement",f.overloadSetter());a.Mutators={Extends:function(g){this.parent=g;this.prototype=d(g);},Implements:function(g){Array.from(g).each(function(j){var h=new j; for(var i in h){f.call(this,i,h[i],true);}},this);}};})();(function(){this.Chain=new Class({$chain:[],chain:function(){this.$chain.append(Array.flatten(arguments)); return this;},callChain:function(){return(this.$chain.length)?this.$chain.shift().apply(this,arguments):false;},clearChain:function(){this.$chain.empty(); return this;}});var a=function(b){return b.replace(/^on([A-Z])/,function(c,d){return d.toLowerCase();});};this.Events=new Class({$events:{},addEvent:function(d,c,b){d=a(d); this.$events[d]=(this.$events[d]||[]).include(c);if(b){c.internal=true;}return this;},addEvents:function(b){for(var c in b){this.addEvent(c,b[c]);}return this; },fireEvent:function(e,c,b){e=a(e);var d=this.$events[e];if(!d){return this;}c=Array.from(c);d.each(function(f){if(b){f.delay(b,this,c);}else{f.apply(this,c); }},this);return this;},removeEvent:function(e,d){e=a(e);var c=this.$events[e];if(c&&!d.internal){var b=c.indexOf(d);if(b!=-1){delete c[b];}}return this; },removeEvents:function(d){var e;if(typeOf(d)=="object"){for(e in d){this.removeEvent(e,d[e]);}return this;}if(d){d=a(d);}for(e in this.$events){if(d&&d!=e){continue; }var c=this.$events[e];for(var b=c.length;b--;){if(b in c){this.removeEvent(e,c[b]);}}}return this;}});this.Options=new Class({setOptions:function(){var b=this.options=Object.merge.apply(null,[{},this.options].append(arguments)); if(this.addEvent){for(var c in b){if(typeOf(b[c])!="function"||!(/^on[A-Z]/).test(c)){continue;}this.addEvent(c,b[c]);delete b[c];}}return this;}});})(); (function(){var k,n,l,g,a={},c={},m=/\\/g;var e=function(q,p){if(q==null){return null;}if(q.Slick===true){return q;}q=(""+q).replace(/^\s+|\s+$/g,"");g=!!p; var o=(g)?c:a;if(o[q]){return o[q];}k={Slick:true,expressions:[],raw:q,reverse:function(){return e(this.raw,true);}};n=-1;while(q!=(q=q.replace(j,b))){}k.length=k.expressions.length; return o[k.raw]=(g)?h(k):k;};var i=function(o){if(o==="!"){return" ";}else{if(o===" "){return"!";}else{if((/^!/).test(o)){return o.replace(/^!/,"");}else{return"!"+o; }}}};var h=function(u){var r=u.expressions;for(var p=0;p+)\\s*|(\\s+)|(+|\\*)|\\#(+)|\\.(+)|\\[\\s*(+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)".replace(//,"["+f(">+~`!@$%^&={}\\;/g,"(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])").replace(//g,"(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])")); function b(x,s,D,z,r,C,q,B,A,y,u,F,G,v,p,w){if(s||n===-1){k.expressions[++n]=[];l=-1;if(s){return"";}}if(D||z||l===-1){D=D||" ";var t=k.expressions[n]; if(g&&t[l]){t[l].reverseCombinator=i(D);}t[++l]={combinator:D,tag:"*"};}var o=k.expressions[n][l];if(r){o.tag=r.replace(m,"");}else{if(C){o.id=C.replace(m,""); }else{if(q){q=q.replace(m,"");if(!o.classList){o.classList=[];}if(!o.classes){o.classes=[];}o.classList.push(q);o.classes.push({value:q,regexp:new RegExp("(^|\\s)"+f(q)+"(\\s|$)")}); }else{if(G){w=w||p;w=w?w.replace(m,""):null;if(!o.pseudos){o.pseudos=[];}o.pseudos.push({key:G.replace(m,""),value:w,type:F.length==1?"class":"element"}); }else{if(B){B=B.replace(m,"");u=(u||"").replace(m,"");var E,H;switch(A){case"^=":H=new RegExp("^"+f(u));break;case"$=":H=new RegExp(f(u)+"$");break;case"~=":H=new RegExp("(^|\\s)"+f(u)+"(\\s|$)"); break;case"|=":H=new RegExp("^"+f(u)+"(-|$)");break;case"=":E=function(I){return u==I;};break;case"*=":E=function(I){return I&&I.indexOf(u)>-1;};break; case"!=":E=function(I){return u!=I;};break;default:E=function(I){return !!I;};}if(u==""&&(/^[*$^]=$/).test(A)){E=function(){return false;};}if(!E){E=function(I){return I&&H.test(I); };}if(!o.attributes){o.attributes=[];}o.attributes.push({key:B,operator:A,value:u,test:E});}}}}}return"";}var d=(this.Slick||{});d.parse=function(o){return e(o); };d.escapeRegExp=f;if(!this.Slick){this.Slick=d;}}).apply((typeof exports!="undefined")?exports:this);(function(){var j={},l={},b=Object.prototype.toString; j.isNativeCode=function(c){return(/\{\s*\[native code\]\s*\}/).test(""+c);};j.isXML=function(c){return(!!c.xmlVersion)||(!!c.xml)||(b.call(c)=="[object XMLDocument]")||(c.nodeType==9&&c.documentElement.nodeName!="HTML"); };j.setDocument=function(w){var t=w.nodeType;if(t==9){}else{if(t){w=w.ownerDocument;}else{if(w.navigator){w=w.document;}else{return;}}}if(this.document===w){return; }this.document=w;var y=w.documentElement,u=this.getUIDXML(y),o=l[u],A;if(o){for(A in o){this[A]=o[A];}return;}o=l[u]={};o.root=y;o.isXMLDocument=this.isXML(w); o.brokenStarGEBTN=o.starSelectsClosedQSA=o.idGetsName=o.brokenMixedCaseQSA=o.brokenGEBCN=o.brokenCheckedQSA=o.brokenEmptyAttributeQSA=o.isHTMLDocument=o.nativeMatchesSelector=false; var m,n,x,q,r;var s,c="slick_uniqueid";var z=w.createElement("div");var p=w.body||w.getElementsByTagName("body")[0]||y;p.appendChild(z);try{z.innerHTML=''; o.isHTMLDocument=!!w.getElementById(c);}catch(v){}if(o.isHTMLDocument){z.style.display="none";z.appendChild(w.createComment(""));n=(z.getElementsByTagName("*").length>1); try{z.innerHTML="foo";s=z.getElementsByTagName("*");m=(s&&!!s.length&&s[0].nodeName.charAt(0)=="/");}catch(v){}o.brokenStarGEBTN=n||m;try{z.innerHTML=''; o.idGetsName=w.getElementById(c)===z.firstChild;}catch(v){}if(z.getElementsByClassName){try{z.innerHTML='';z.getElementsByClassName("b").length; z.firstChild.className="b";q=(z.getElementsByClassName("b").length!=2);}catch(v){}try{z.innerHTML='';x=(z.getElementsByClassName("a").length!=2); }catch(v){}o.brokenGEBCN=q||x;}if(z.querySelectorAll){try{z.innerHTML="foo";s=z.querySelectorAll("*");o.starSelectsClosedQSA=(s&&!!s.length&&s[0].nodeName.charAt(0)=="/"); }catch(v){}try{z.innerHTML='';o.brokenMixedCaseQSA=!z.querySelectorAll(".MiX").length;}catch(v){}try{z.innerHTML=''; o.brokenCheckedQSA=(z.querySelectorAll(":checked").length==0);}catch(v){}try{z.innerHTML='';o.brokenEmptyAttributeQSA=(z.querySelectorAll('[class*=""]').length!=0); }catch(v){}}try{z.innerHTML='
';r=(z.firstChild.getAttribute("action")!="s");}catch(v){}o.nativeMatchesSelector=y.matchesSelector||y.mozMatchesSelector||y.webkitMatchesSelector; if(o.nativeMatchesSelector){try{o.nativeMatchesSelector.call(y,":slick");o.nativeMatchesSelector=null;}catch(v){}}}try{y.slick_expando=1;delete y.slick_expando; o.getUID=this.getUIDHTML;}catch(v){o.getUID=this.getUIDXML;}p.removeChild(z);z=s=p=null;o.getAttribute=(o.isHTMLDocument&&r)?function(D,B){var E=this.attributeGetters[B]; if(E){return E.call(D);}var C=D.getAttributeNode(B);return(C)?C.nodeValue:null;}:function(C,B){var D=this.attributeGetters[B];return(D)?D.call(C):C.getAttribute(B); };o.hasAttribute=(y&&this.isNativeCode(y.hasAttribute))?function(C,B){return C.hasAttribute(B);}:function(C,B){C=C.getAttributeNode(B);return !!(C&&(C.specified||C.nodeValue)); };o.contains=(y&&this.isNativeCode(y.contains))?function(B,C){return B.contains(C);}:(y&&y.compareDocumentPosition)?function(B,C){return B===C||!!(B.compareDocumentPosition(C)&16); }:function(B,C){if(C){do{if(C===B){return true;}}while((C=C.parentNode));}return false;};o.documentSorter=(y.compareDocumentPosition)?function(C,B){if(!C.compareDocumentPosition||!B.compareDocumentPosition){return 0; }return C.compareDocumentPosition(B)&4?-1:C===B?0:1;}:("sourceIndex" in y)?function(C,B){if(!C.sourceIndex||!B.sourceIndex){return 0;}return C.sourceIndex-B.sourceIndex; }:(w.createRange)?function(E,C){if(!E.ownerDocument||!C.ownerDocument){return 0;}var D=E.ownerDocument.createRange(),B=C.ownerDocument.createRange();D.setStart(E,0); D.setEnd(E,0);B.setStart(C,0);B.setEnd(C,0);return D.compareBoundaryPoints(Range.START_TO_END,B);}:null;y=null;for(A in o){this[A]=o[A];}};var e=/^([#.]?)((?:[\w-]+|\*))$/,g=/\[.+[*$^]=(?:""|'')?\]/,f={}; j.search=function(U,z,H,s){var p=this.found=(s)?null:(H||[]);if(!U){return p;}else{if(U.navigator){U=U.document;}else{if(!U.nodeType){return p;}}}var F,O,V=this.uniques={},I=!!(H&&H.length),y=(U.nodeType==9); if(this.document!==(y?U:U.ownerDocument)){this.setDocument(U);}if(I){for(O=p.length;O--;){V[this.getUID(p[O])]=true;}}if(typeof z=="string"){var r=z.match(e); simpleSelectors:if(r){var u=r[1],v=r[2],A,E;if(!u){if(v=="*"&&this.brokenStarGEBTN){break simpleSelectors;}E=U.getElementsByTagName(v);if(s){return E[0]||null; }for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}else{if(u=="#"){if(!this.isHTMLDocument||!y){break simpleSelectors;}A=U.getElementById(v); if(!A){return p;}if(this.idGetsName&&A.getAttributeNode("id").nodeValue!=v){break simpleSelectors;}if(s){return A||null;}if(!(I&&V[this.getUID(A)])){p.push(A); }}else{if(u=="."){if(!this.isHTMLDocument||((!U.getElementsByClassName||this.brokenGEBCN)&&U.querySelectorAll)){break simpleSelectors;}if(U.getElementsByClassName&&!this.brokenGEBCN){E=U.getElementsByClassName(v); if(s){return E[0]||null;}for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}else{var T=new RegExp("(^|\\s)"+d.escapeRegExp(v)+"(\\s|$)");E=U.getElementsByTagName("*"); for(O=0;A=E[O++];){className=A.className;if(!(className&&T.test(className))){continue;}if(s){return A;}if(!(I&&V[this.getUID(A)])){p.push(A);}}}}}}if(I){this.sort(p); }return(s)?null:p;}querySelector:if(U.querySelectorAll){if(!this.isHTMLDocument||f[z]||this.brokenMixedCaseQSA||(this.brokenCheckedQSA&&z.indexOf(":checked")>-1)||(this.brokenEmptyAttributeQSA&&g.test(z))||(!y&&z.indexOf(",")>-1)||d.disableQSA){break querySelector; }var S=z,x=U;if(!y){var C=x.getAttribute("id"),t="slickid__";x.setAttribute("id",t);S="#"+t+" "+S;U=x.parentNode;}try{if(s){return U.querySelector(S)||null; }else{E=U.querySelectorAll(S);}}catch(Q){f[z]=1;break querySelector;}finally{if(!y){if(C){x.setAttribute("id",C);}else{x.removeAttribute("id");}U=x;}}if(this.starSelectsClosedQSA){for(O=0; A=E[O++];){if(A.nodeName>"@"&&!(I&&V[this.getUID(A)])){p.push(A);}}}else{for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}if(I){this.sort(p); }return p;}F=this.Slick.parse(z);if(!F.length){return p;}}else{if(z==null){return p;}else{if(z.Slick){F=z;}else{if(this.contains(U.documentElement||U,z)){(p)?p.push(z):p=z; return p;}else{return p;}}}}this.posNTH={};this.posNTHLast={};this.posNTHType={};this.posNTHTypeLast={};this.push=(!I&&(s||(F.length==1&&F.expressions[0].length==1)))?this.pushArray:this.pushUID; if(p==null){p=[];}var M,L,K;var B,J,D,c,q,G,W;var N,P,o,w,R=F.expressions;search:for(O=0;(P=R[O]);O++){for(M=0;(o=P[M]);M++){B="combinator:"+o.combinator; if(!this[B]){continue search;}J=(this.isXMLDocument)?o.tag:o.tag.toUpperCase();D=o.id;c=o.classList;q=o.classes;G=o.attributes;W=o.pseudos;w=(M===(P.length-1)); this.bitUniques={};if(w){this.uniques=V;this.found=p;}else{this.uniques={};this.found=[];}if(M===0){this[B](U,J,D,q,G,W,c);if(s&&w&&p.length){break search; }}else{if(s&&w){for(L=0,K=N.length;L1)){this.sort(p);}return(s)?(p[0]||null):p;};j.uidx=1;j.uidk="slick-uniqueid";j.getUIDXML=function(m){var c=m.getAttribute(this.uidk); if(!c){c=this.uidx++;m.setAttribute(this.uidk,c);}return c;};j.getUIDHTML=function(c){return c.uniqueNumber||(c.uniqueNumber=this.uidx++);};j.sort=function(c){if(!this.documentSorter){return c; }c.sort(this.documentSorter);return c;};j.cacheNTH={};j.matchNTH=/^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;j.parseNTHArgument=function(p){var n=p.match(this.matchNTH); if(!n){return false;}var o=n[2]||false;var m=n[1]||1;if(m=="-"){m=-1;}var c=+n[3]||0;n=(o=="n")?{a:m,b:c}:(o=="odd")?{a:2,b:1}:(o=="even")?{a:2,b:0}:{a:0,b:m}; return(this.cacheNTH[p]=n);};j.createNTHPseudo=function(o,m,c,n){return function(r,p){var t=this.getUID(r);if(!this[c][t]){var z=r.parentNode;if(!z){return false; }var q=z[o],s=1;if(n){var y=r.nodeName;do{if(q.nodeName!=y){continue;}this[c][this.getUID(q)]=s++;}while((q=q[m]));}else{do{if(q.nodeType!=1){continue; }this[c][this.getUID(q)]=s++;}while((q=q[m]));}}p=p||"n";var u=this.cacheNTH[p]||this.parseNTHArgument(p);if(!u){return false;}var x=u.a,w=u.b,v=this[c][t]; if(x==0){return w==v;}if(x>0){if(v":function(o,c,q,n,m,p){if((o=o.firstChild)){do{if(o.nodeType==1){this.push(o,c,q,n,m,p); }}while((o=o.nextSibling));}},"+":function(o,c,q,n,m,p){while((o=o.nextSibling)){if(o.nodeType==1){this.push(o,c,q,n,m,p);break;}}},"^":function(o,c,q,n,m,p){o=o.firstChild; if(o){if(o.nodeType==1){this.push(o,c,q,n,m,p);}else{this["combinator:+"](o,c,q,n,m,p);}}},"~":function(p,c,r,o,m,q){while((p=p.nextSibling)){if(p.nodeType!=1){continue; }var n=this.getUID(p);if(this.bitUniques[n]){break;}this.bitUniques[n]=true;this.push(p,c,r,o,m,q);}},"++":function(o,c,q,n,m,p){this["combinator:+"](o,c,q,n,m,p); this["combinator:!+"](o,c,q,n,m,p);},"~~":function(o,c,q,n,m,p){this["combinator:~"](o,c,q,n,m,p);this["combinator:!~"](o,c,q,n,m,p);},"!":function(o,c,q,n,m,p){while((o=o.parentNode)){if(o!==this.document){this.push(o,c,q,n,m,p); }}},"!>":function(o,c,q,n,m,p){o=o.parentNode;if(o!==this.document){this.push(o,c,q,n,m,p);}},"!+":function(o,c,q,n,m,p){while((o=o.previousSibling)){if(o.nodeType==1){this.push(o,c,q,n,m,p); break;}}},"!^":function(o,c,q,n,m,p){o=o.lastChild;if(o){if(o.nodeType==1){this.push(o,c,q,n,m,p);}else{this["combinator:!+"](o,c,q,n,m,p);}}},"!~":function(p,c,r,o,m,q){while((p=p.previousSibling)){if(p.nodeType!=1){continue; }var n=this.getUID(p);if(this.bitUniques[n]){break;}this.bitUniques[n]=true;this.push(p,c,r,o,m,q);}}};for(var h in i){j["combinator:"+h]=i[h];}var k={empty:function(c){var m=c.firstChild; return !(m&&m.nodeType==1)&&!(c.innerText||c.textContent||"").length;},not:function(c,m){return !this.matchNode(c,m);},contains:function(c,m){return(c.innerText||c.textContent||"").indexOf(m)>-1; },"first-child":function(c){while((c=c.previousSibling)){if(c.nodeType==1){return false;}}return true;},"last-child":function(c){while((c=c.nextSibling)){if(c.nodeType==1){return false; }}return true;},"only-child":function(n){var m=n;while((m=m.previousSibling)){if(m.nodeType==1){return false;}}var c=n;while((c=c.nextSibling)){if(c.nodeType==1){return false; }}return true;},"nth-child":j.createNTHPseudo("firstChild","nextSibling","posNTH"),"nth-last-child":j.createNTHPseudo("lastChild","previousSibling","posNTHLast"),"nth-of-type":j.createNTHPseudo("firstChild","nextSibling","posNTHType",true),"nth-last-of-type":j.createNTHPseudo("lastChild","previousSibling","posNTHTypeLast",true),index:function(m,c){return this["pseudo:nth-child"](m,""+c+1); },even:function(c){return this["pseudo:nth-child"](c,"2n");},odd:function(c){return this["pseudo:nth-child"](c,"2n+1");},"first-of-type":function(c){var m=c.nodeName; while((c=c.previousSibling)){if(c.nodeName==m){return false;}}return true;},"last-of-type":function(c){var m=c.nodeName;while((c=c.nextSibling)){if(c.nodeName==m){return false; }}return true;},"only-of-type":function(n){var m=n,o=n.nodeName;while((m=m.previousSibling)){if(m.nodeName==o){return false;}}var c=n;while((c=c.nextSibling)){if(c.nodeName==o){return false; }}return true;},enabled:function(c){return !c.disabled;},disabled:function(c){return c.disabled;},checked:function(c){return c.checked||c.selected;},focus:function(c){return this.isHTMLDocument&&this.document.activeElement===c&&(c.href||c.type||this.hasAttribute(c,"tabindex")); },root:function(c){return(c===this.root);},selected:function(c){return c.selected;}};for(var a in k){j["pseudo:"+a]=k[a];}j.attributeGetters={"class":function(){return this.getAttribute("class")||this.className; },"for":function(){return("htmlFor" in this)?this.htmlFor:this.getAttribute("for");},href:function(){return("href" in this)?this.getAttribute("href",2):this.getAttribute("href"); },style:function(){return(this.style)?this.style.cssText:this.getAttribute("style");},tabindex:function(){var c=this.getAttributeNode("tabindex");return(c&&c.specified)?c.nodeValue:null; },type:function(){return this.getAttribute("type");}};var d=j.Slick=(this.Slick||{});d.version="1.1.5";d.search=function(m,n,c){return j.search(m,n,c); };d.find=function(c,m){return j.search(c,m,null,true);};d.contains=function(c,m){j.setDocument(c);return j.contains(c,m);};d.getAttribute=function(m,c){return j.getAttribute(m,c); };d.match=function(m,c){if(!(m&&c)){return false;}if(!c||c===m){return true;}j.setDocument(m);return j.matchNode(m,c);};d.defineAttributeGetter=function(c,m){j.attributeGetters[c]=m; return this;};d.lookupAttributeGetter=function(c){return j.attributeGetters[c];};d.definePseudo=function(c,m){j["pseudo:"+c]=function(o,n){return m.call(o,n); };return this;};d.lookupPseudo=function(c){var m=j["pseudo:"+c];if(m){return function(n){return m.call(this,n);};}return null;};d.override=function(m,c){j.override(m,c); return this;};d.isXML=j.isXML;d.uidOf=function(c){return j.getUIDHTML(c);};if(!this.Slick){this.Slick=d;}}).apply((typeof exports!="undefined")?exports:this); var Element=function(b,g){var h=Element.Constructors[b];if(h){return h(g);}if(typeof b!="string"){return document.id(b).set(g);}if(!g){g={};}if(!(/^[\w-]+$/).test(b)){var e=Slick.parse(b).expressions[0][0]; b=(e.tag=="*")?"div":e.tag;if(e.id&&g.id==null){g.id=e.id;}var d=e.attributes;if(d){for(var f=0,c=d.length;f=this.length){delete this[e--];}return this; }.protect());}Elements.implement(Array.prototype);Array.mirror(Elements);var f;try{var a=document.createElement("");f=(a.name=="x");}catch(c){}var d=function(e){return(""+e).replace(/&/g,"&").replace(/"/g,"""); };Document.implement({newElement:function(e,h){if(h&&h.checked!=null){h.defaultChecked=h.checked;}if(f&&h){e="<"+e;if(h.name){e+=' name="'+d(h.name)+'"'; }if(h.type){e+=' type="'+d(h.type)+'"';}e+=">";delete h.name;delete h.type;}return this.id(this.createElement(e)).set(h);}});})();Document.implement({newTextNode:function(a){return this.createTextNode(a); },getDocument:function(){return this;},getWindow:function(){return this.window;},id:(function(){var a={string:function(d,c,b){d=Slick.find(b,"#"+d.replace(/(\W)/g,"\\$1")); return(d)?a.element(d,c):null;},element:function(b,c){$uid(b);if(!c&&!b.$family&&!(/^(?:object|embed)$/i).test(b.tagName)){Object.append(b,Element.Prototype); }return b;},object:function(c,d,b){if(c.toElement){return a.element(c.toElement(b),d);}return null;}};a.textnode=a.whitespace=a.window=a.document=function(b){return b; };return function(c,e,d){if(c&&c.$family&&c.uid){return c;}var b=typeOf(c);return(a[b])?a[b](c,e,d||document):null;};})()});if(window.$==null){Window.implement("$",function(a,b){return document.id(a,b,this.document); });}Window.implement({getDocument:function(){return this.document;},getWindow:function(){return this;}});[Document,Element].invoke("implement",{getElements:function(a){return Slick.search(this,a,new Elements); },getElement:function(a){return document.id(Slick.find(this,a));}});if(window.$$==null){Window.implement("$$",function(a){if(arguments.length==1){if(typeof a=="string"){return Slick.search(this.document,a,new Elements); }else{if(Type.isEnumerable(a)){return new Elements(a);}}}return new Elements(arguments);});}(function(){var k={},i={};var n={input:"checked",option:"selected",textarea:"value"}; var e=function(p){return(i[p]||(i[p]={}));};var j=function(q){var p=q.uid;if(q.removeEvents){q.removeEvents();}if(q.clearAttributes){q.clearAttributes(); }if(p!=null){delete k[p];delete i[p];}return q;};var o=["defaultValue","accessKey","cellPadding","cellSpacing","colSpan","frameBorder","maxLength","readOnly","rowSpan","tabIndex","useMap"]; var d=["compact","nowrap","ismap","declare","noshade","checked","disabled","readOnly","multiple","selected","noresize","defer","defaultChecked"];var g={html:"innerHTML","class":"className","for":"htmlFor",text:(function(){var p=document.createElement("div"); return(p.textContent==null)?"innerText":"textContent";})()};var m=["type"];var h=["value","defaultValue"];var l=/^(?:href|src|usemap)$/i;d=d.associate(d); o=o.associate(o.map(String.toLowerCase));m=m.associate(m);Object.append(g,h.associate(h));var c={before:function(q,p){var r=p.parentNode;if(r){r.insertBefore(q,p); }},after:function(q,p){var r=p.parentNode;if(r){r.insertBefore(q,p.nextSibling);}},bottom:function(q,p){p.appendChild(q);},top:function(q,p){p.insertBefore(q,p.firstChild); }};c.inside=c.bottom;var b=function(s,r){if(!s){return r;}s=Object.clone(Slick.parse(s));var q=s.expressions;for(var p=q.length;p--;){q[p][0].combinator=r; }return s;};Element.implement({set:function(r,q){var p=Element.Properties[r];(p&&p.set)?p.set.call(this,q):this.setProperty(r,q);}.overloadSetter(),get:function(q){var p=Element.Properties[q]; return(p&&p.get)?p.get.apply(this):this.getProperty(q);}.overloadGetter(),erase:function(q){var p=Element.Properties[q];(p&&p.erase)?p.erase.apply(this):this.removeProperty(q); return this;},setProperty:function(q,r){q=o[q]||q;if(r==null){return this.removeProperty(q);}var p=g[q];(p)?this[p]=r:(d[q])?this[q]=!!r:this.setAttribute(q,""+r); return this;},setProperties:function(p){for(var q in p){this.setProperty(q,p[q]);}return this;},getProperty:function(q){q=o[q]||q;var p=g[q]||m[q];return(p)?this[p]:(d[q])?!!this[q]:(l.test(q)?this.getAttribute(q,2):(p=this.getAttributeNode(q))?p.nodeValue:null)||null; },getProperties:function(){var p=Array.from(arguments);return p.map(this.getProperty,this).associate(p);},removeProperty:function(q){q=o[q]||q;var p=g[q]; (p)?this[p]="":(d[q])?this[q]=false:this.removeAttribute(q);return this;},removeProperties:function(){Array.each(arguments,this.removeProperty,this);return this; },hasClass:function(p){return this.className.clean().contains(p," ");},addClass:function(p){if(!this.hasClass(p)){this.className=(this.className+" "+p).clean(); }return this;},removeClass:function(p){this.className=this.className.replace(new RegExp("(^|\\s)"+p+"(?:\\s|$)"),"$1");return this;},toggleClass:function(p,q){if(q==null){q=!this.hasClass(p); }return(q)?this.addClass(p):this.removeClass(p);},adopt:function(){var s=this,p,u=Array.flatten(arguments),t=u.length;if(t>1){s=p=document.createDocumentFragment(); }for(var r=0;r"))[0]);},getLast:function(p){return document.id(Slick.search(this,b(p,">")).getLast()); },getParent:function(p){return document.id(Slick.find(this,b(p,"!")));},getParents:function(p){return Slick.search(this,b(p,"!"),new Elements);},getSiblings:function(p){return Slick.search(this,b(p,"~~"),new Elements); },getChildren:function(p){return Slick.search(this,b(p,">"),new Elements);},getWindow:function(){return this.ownerDocument.window;},getDocument:function(){return this.ownerDocument; },getElementById:function(p){return document.id(Slick.find(this,"#"+(""+p).replace(/(\W)/g,"\\$1")));},getSelected:function(){this.selectedIndex;return new Elements(Array.from(this.options).filter(function(p){return p.selected; }));},toQueryString:function(){var p=[];this.getElements("input, select, textarea").each(function(r){var q=r.type;if(!r.name||r.disabled||q=="submit"||q=="reset"||q=="file"||q=="image"){return; }var s=(r.get("tag")=="select")?r.getSelected().map(function(t){return document.id(t).get("value");}):((q=="radio"||q=="checkbox")&&!r.checked)?null:r.get("value"); Array.from(s).each(function(t){if(typeof t!="undefined"){p.push(encodeURIComponent(r.name)+"="+encodeURIComponent(t));}});});return p.join("&");},destroy:function(){var p=j(this).getElementsByTagName("*"); Array.each(p,j);Element.dispose(this);return null;},empty:function(){Array.from(this.childNodes).each(Element.dispose);return this;},dispose:function(){return(this.parentNode)?this.parentNode.removeChild(this):this; },match:function(p){return !p||Slick.match(this,p);}});var a=function(t,s,q){if(!q){t.setAttributeNode(document.createAttribute("id"));}if(t.clearAttributes){t.clearAttributes(); t.mergeAttributes(s);t.removeAttribute("uid");if(t.options){var u=t.options,p=s.options;for(var r=u.length;r--;){u[r].selected=p[r].selected;}}}var v=n[s.tagName.toLowerCase()]; if(v&&s[v]){t[v]=s[v];}};Element.implement("clone",function(r,p){r=r!==false;var w=this.cloneNode(r),q;if(r){var s=w.getElementsByTagName("*"),u=this.getElementsByTagName("*"); for(q=s.length;q--;){a(s[q],u[q],p);}}a(w,this,p);if(Browser.ie){var t=w.getElementsByTagName("object"),v=this.getElementsByTagName("object");for(q=t.length; q--;){t[q].outerHTML=v[q].outerHTML;}}return document.id(w);});var f={contains:function(p){return Slick.contains(this,p);}};if(!document.contains){Document.implement(f); }if(!document.createElement("div").contains){Element.implement(f);}[Element,Window,Document].invoke("implement",{addListener:function(s,r){if(s=="unload"){var p=r,q=this; r=function(){q.removeListener("unload",r);p();};}else{k[$uid(this)]=this;}if(this.addEventListener){this.addEventListener(s,r,!!arguments[2]);}else{this.attachEvent("on"+s,r); }return this;},removeListener:function(q,p){if(this.removeEventListener){this.removeEventListener(q,p,!!arguments[2]);}else{this.detachEvent("on"+q,p); }return this;},retrieve:function(q,p){var s=e($uid(this)),r=s[q];if(p!=null&&r==null){r=s[q]=p;}return r!=null?r:null;},store:function(q,p){var r=e($uid(this)); r[q]=p;return this;},eliminate:function(p){var q=e($uid(this));delete q[p];return this;}});if(window.attachEvent&&!window.addEventListener){window.addListener("unload",function(){Object.each(k,j); if(window.CollectGarbage){CollectGarbage();}});}})();Element.Properties={};Element.Properties.style={set:function(a){this.style.cssText=a;},get:function(){return this.style.cssText; },erase:function(){this.style.cssText="";}};Element.Properties.tag={get:function(){return this.tagName.toLowerCase();}};(function(a){if(a!=null){Element.Properties.maxlength=Element.Properties.maxLength={get:function(){var b=this.getAttribute("maxLength"); return b==a?null:b;}};}})(document.createElement("input").getAttribute("maxLength"));Element.Properties.html=(function(){var c=Function.attempt(function(){var e=document.createElement("table"); e.innerHTML="";});var d=document.createElement("div");var a={table:[1,"","
"],select:[1,""],tbody:[2,"","
"],tr:[3,"","
"]}; a.thead=a.tfoot=a.tbody;var b={set:function(){var f=Array.flatten(arguments).join("");var g=(!c&&a[this.get("tag")]);if(g){var h=d;h.innerHTML=g[1]+f+g[2]; for(var e=g[0];e--;){h=h.firstChild;}this.empty().adopt(h.childNodes);}else{this.innerHTML=f;}}};b.erase=b.set;return b;})();(function(){var c=document.html; Element.Properties.styles={set:function(f){this.setStyles(f);}};var e=(c.style.opacity!=null);var d=/alpha\(opacity=([\d.]+)\)/i;var b=function(g,f){if(!g.currentStyle||!g.currentStyle.hasLayout){g.style.zoom=1; }if(e){g.style.opacity=f;}else{f=(f*100).limit(0,100).round();f=(f==100)?"":"alpha(opacity="+f+")";var h=g.style.filter||g.getComputedStyle("filter")||""; g.style.filter=d.test(h)?h.replace(d,f):h+f;}};Element.Properties.opacity={set:function(g){var f=this.style.visibility;if(g==0&&f!="hidden"){this.style.visibility="hidden"; }else{if(g!=0&&f!="visible"){this.style.visibility="visible";}}b(this,g);},get:(e)?function(){var f=this.style.opacity||this.getComputedStyle("opacity"); return(f=="")?1:f;}:function(){var f,g=(this.style.filter||this.getComputedStyle("filter"));if(g){f=g.match(d);}return(f==null||g==null)?1:(f[1]/100);}}; var a=(c.style.cssFloat==null)?"styleFloat":"cssFloat";Element.implement({getComputedStyle:function(h){if(this.currentStyle){return this.currentStyle[h.camelCase()]; }var g=Element.getDocument(this).defaultView,f=g?g.getComputedStyle(this,null):null;return(f)?f.getPropertyValue((h==a)?"float":h.hyphenate()):null;},setOpacity:function(f){b(this,f); return this;},getOpacity:function(){return this.get("opacity");},setStyle:function(g,f){switch(g){case"opacity":return this.set("opacity",parseFloat(f)); case"float":g=a;}g=g.camelCase();if(typeOf(f)!="string"){var h=(Element.Styles[g]||"@").split(" ");f=Array.from(f).map(function(k,j){if(!h[j]){return""; }return(typeOf(k)=="number")?h[j].replace("@",Math.round(k)):k;}).join(" ");}else{if(f==String(Number(f))){f=Math.round(f);}}this.style[g]=f;return this; },getStyle:function(l){switch(l){case"opacity":return this.get("opacity");case"float":l=a;}l=l.camelCase();var f=this.style[l];if(!f||l=="zIndex"){f=[]; for(var k in Element.ShortStyles){if(l!=k){continue;}for(var j in Element.ShortStyles[k]){f.push(this.getStyle(j));}return f.join(" ");}f=this.getComputedStyle(l); }if(f){f=String(f);var h=f.match(/rgba?\([\d\s,]+\)/);if(h){f=f.replace(h[0],h[0].rgbToHex());}}if(Browser.opera||(Browser.ie&&isNaN(parseFloat(f)))){if((/^(height|width)$/).test(l)){var g=(l=="width")?["left","right"]:["top","bottom"],i=0; g.each(function(m){i+=this.getStyle("border-"+m+"-width").toInt()+this.getStyle("padding-"+m).toInt();},this);return this["offset"+l.capitalize()]-i+"px"; }if(Browser.opera&&String(f).indexOf("px")!=-1){return f;}if((/^border(.+)Width|margin|padding/).test(l)){return"0px";}}return f;},setStyles:function(g){for(var f in g){this.setStyle(f,g[f]); }return this;},getStyles:function(){var f={};Array.flatten(arguments).each(function(g){f[g]=this.getStyle(g);},this);return f;}});Element.Styles={left:"@px",top:"@px",bottom:"@px",right:"@px",width:"@px",height:"@px",maxWidth:"@px",maxHeight:"@px",minWidth:"@px",minHeight:"@px",backgroundColor:"rgb(@, @, @)",backgroundPosition:"@px @px",color:"rgb(@, @, @)",fontSize:"@px",letterSpacing:"@px",lineHeight:"@px",clip:"rect(@px @px @px @px)",margin:"@px @px @px @px",padding:"@px @px @px @px",border:"@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)",borderWidth:"@px @px @px @px",borderStyle:"@ @ @ @",borderColor:"rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)",zIndex:"@",zoom:"@",fontWeight:"@",textIndent:"@px",opacity:"@"}; Element.ShortStyles={margin:{},padding:{},border:{},borderWidth:{},borderStyle:{},borderColor:{}};["Top","Right","Bottom","Left"].each(function(l){var k=Element.ShortStyles; var g=Element.Styles;["margin","padding"].each(function(m){var n=m+l;k[m][n]=g[n]="@px";});var j="border"+l;k.border[j]=g[j]="@px @ rgb(@, @, @)";var i=j+"Width",f=j+"Style",h=j+"Color"; k[j]={};k.borderWidth[i]=k[j][i]=g[i]="@px";k.borderStyle[f]=k[j][f]=g[f]="@";k.borderColor[h]=k[j][h]=g[h]="rgb(@, @, @)";});})();(function(){Element.Properties.events={set:function(b){this.addEvents(b); }};[Element,Window,Document].invoke("implement",{addEvent:function(f,h){var i=this.retrieve("events",{});if(!i[f]){i[f]={keys:[],values:[]};}if(i[f].keys.contains(h)){return this; }i[f].keys.push(h);var g=f,b=Element.Events[f],d=h,j=this;if(b){if(b.onAdd){b.onAdd.call(this,h);}if(b.condition){d=function(k){if(b.condition.call(this,k)){return h.call(this,k); }return true;};}g=b.base||g;}var e=function(){return h.call(j);};var c=Element.NativeEvents[g];if(c){if(c==2){e=function(k){k=new Event(k,j.getWindow()); if(d.call(j,k)===false){k.stop();}};}this.addListener(g,e,arguments[2]);}i[f].values.push(e);return this;},removeEvent:function(e,d){var c=this.retrieve("events"); if(!c||!c[e]){return this;}var h=c[e];var b=h.keys.indexOf(d);if(b==-1){return this;}var g=h.values[b];delete h.keys[b];delete h.values[b];var f=Element.Events[e]; if(f){if(f.onRemove){f.onRemove.call(this,d);}e=f.base||e;}return(Element.NativeEvents[e])?this.removeListener(e,g,arguments[2]):this;},addEvents:function(b){for(var c in b){this.addEvent(c,b[c]); }return this;},removeEvents:function(b){var d;if(typeOf(b)=="object"){for(d in b){this.removeEvent(d,b[d]);}return this;}var c=this.retrieve("events"); if(!c){return this;}if(!b){for(d in c){this.removeEvents(d);}this.eliminate("events");}else{if(c[b]){c[b].keys.each(function(e){this.removeEvent(b,e);},this); delete c[b];}}return this;},fireEvent:function(e,c,b){var d=this.retrieve("events");if(!d||!d[e]){return this;}c=Array.from(c);d[e].keys.each(function(f){if(b){f.delay(b,this,c); }else{f.apply(this,c);}},this);return this;},cloneEvents:function(e,d){e=document.id(e);var c=e.retrieve("events");if(!c){return this;}if(!d){for(var b in c){this.cloneEvents(e,b); }}else{if(c[d]){c[d].keys.each(function(f){this.addEvent(d,f);},this);}}return this;}});Element.NativeEvents={click:2,dblclick:2,mouseup:2,mousedown:2,contextmenu:2,mousewheel:2,DOMMouseScroll:2,mouseover:2,mouseout:2,mousemove:2,selectstart:2,selectend:2,keydown:2,keypress:2,keyup:2,orientationchange:2,touchstart:2,touchmove:2,touchend:2,touchcancel:2,gesturestart:2,gesturechange:2,gestureend:2,focus:2,blur:2,change:2,reset:2,select:2,submit:2,load:2,unload:1,beforeunload:2,resize:1,move:1,DOMContentLoaded:1,readystatechange:1,error:1,abort:1,scroll:1}; var a=function(b){var c=b.relatedTarget;if(c==null){return true;}if(!c){return false;}return(c!=this&&c.prefix!="xul"&&typeOf(this)!="document"&&!this.contains(c)); };Element.Events={mouseenter:{base:"mouseover",condition:a},mouseleave:{base:"mouseout",condition:a},mousewheel:{base:(Browser.firefox)?"DOMMouseScroll":"mousewheel"}}; })();(function(){var h=document.createElement("div"),e=document.createElement("div");h.style.height="0";h.appendChild(e);var d=(e.offsetParent===h);h=e=null; var l=function(m){return k(m,"position")!="static"||a(m);};var i=function(m){return l(m)||(/^(?:table|td|th)$/i).test(m.tagName);};Element.implement({scrollTo:function(m,n){if(a(this)){this.getWindow().scrollTo(m,n); }else{this.scrollLeft=m;this.scrollTop=n;}return this;},getSize:function(){if(a(this)){return this.getWindow().getSize();}return{x:this.offsetWidth,y:this.offsetHeight}; },getScrollSize:function(){if(a(this)){return this.getWindow().getScrollSize();}return{x:this.scrollWidth,y:this.scrollHeight};},getScroll:function(){if(a(this)){return this.getWindow().getScroll(); }return{x:this.scrollLeft,y:this.scrollTop};},getScrolls:function(){var n=this.parentNode,m={x:0,y:0};while(n&&!a(n)){m.x+=n.scrollLeft;m.y+=n.scrollTop; n=n.parentNode;}return m;},getOffsetParent:d?function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;}var n=(k(m,"position")=="static")?i:l; while((m=m.parentNode)){if(n(m)){return m;}}return null;}:function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;}try{return m.offsetParent; }catch(n){}return null;},getOffsets:function(){if(this.getBoundingClientRect&&!Browser.Platform.ios){var r=this.getBoundingClientRect(),o=document.id(this.getDocument().documentElement),q=o.getScroll(),t=this.getScrolls(),s=(k(this,"position")=="fixed"); return{x:r.left.toInt()+t.x+((s)?0:q.x)-o.clientLeft,y:r.top.toInt()+t.y+((s)?0:q.y)-o.clientTop};}var n=this,m={x:0,y:0};if(a(this)){return m;}while(n&&!a(n)){m.x+=n.offsetLeft; m.y+=n.offsetTop;if(Browser.firefox){if(!c(n)){m.x+=b(n);m.y+=g(n);}var p=n.parentNode;if(p&&k(p,"overflow")!="visible"){m.x+=b(p);m.y+=g(p);}}else{if(n!=this&&Browser.safari){m.x+=b(n); m.y+=g(n);}}n=n.offsetParent;}if(Browser.firefox&&!c(this)){m.x-=b(this);m.y-=g(this);}return m;},getPosition:function(p){if(a(this)){return{x:0,y:0};}var q=this.getOffsets(),n=this.getScrolls(); var m={x:q.x-n.x,y:q.y-n.y};if(p&&(p=document.id(p))){var o=p.getPosition();return{x:m.x-o.x-b(p),y:m.y-o.y-g(p)};}return m;},getCoordinates:function(o){if(a(this)){return this.getWindow().getCoordinates(); }var m=this.getPosition(o),n=this.getSize();var p={left:m.x,top:m.y,width:n.x,height:n.y};p.right=p.left+p.width;p.bottom=p.top+p.height;return p;},computePosition:function(m){return{left:m.x-j(this,"margin-left"),top:m.y-j(this,"margin-top")}; },setPosition:function(m){return this.setStyles(this.computePosition(m));}});[Document,Window].invoke("implement",{getSize:function(){var m=f(this);return{x:m.clientWidth,y:m.clientHeight}; },getScroll:function(){var n=this.getWindow(),m=f(this);return{x:n.pageXOffset||m.scrollLeft,y:n.pageYOffset||m.scrollTop};},getScrollSize:function(){var o=f(this),n=this.getSize(),m=this.getDocument().body; return{x:Math.max(o.scrollWidth,m.scrollWidth,n.x),y:Math.max(o.scrollHeight,m.scrollHeight,n.y)};},getPosition:function(){return{x:0,y:0};},getCoordinates:function(){var m=this.getSize(); return{top:0,left:0,bottom:m.y,right:m.x,height:m.y,width:m.x};}});var k=Element.getComputedStyle;function j(m,n){return k(m,n).toInt()||0;}function c(m){return k(m,"-moz-box-sizing")=="border-box"; }function g(m){return j(m,"border-top-width");}function b(m){return j(m,"border-left-width");}function a(m){return(/^(?:body|html)$/i).test(m.tagName); }function f(m){var n=m.getDocument();return(!n.compatMode||n.compatMode=="CSS1Compat")?n.html:n.body;}})();Element.alias({position:"setPosition"});[Window,Document,Element].invoke("implement",{getHeight:function(){return this.getSize().y; },getWidth:function(){return this.getSize().x;},getScrollTop:function(){return this.getScroll().y;},getScrollLeft:function(){return this.getScroll().x; },getScrollHeight:function(){return this.getScrollSize().y;},getScrollWidth:function(){return this.getScrollSize().x;},getTop:function(){return this.getPosition().y; },getLeft:function(){return this.getPosition().x;}});(function(){var f=this.Fx=new Class({Implements:[Chain,Events,Options],options:{fps:60,unit:false,duration:500,frames:null,frameSkip:true,link:"ignore"},initialize:function(g){this.subject=this.subject||this; this.setOptions(g);},getTransition:function(){return function(g){return -(Math.cos(Math.PI*g)-1)/2;};},step:function(g){if(this.options.frameSkip){var h=(this.time!=null)?(g-this.time):0,i=h/this.frameInterval; this.time=g;this.frame+=i;}else{this.frame++;}if(this.frame=(7-4*d)/11){e=c*c-Math.pow((11-6*d-11*f)/4,2);break;}}return e; },Elastic:function(b,a){return Math.pow(2,10*--b)*Math.cos(20*b*Math.PI*(a&&a[0]||1)/3);}});["Quad","Cubic","Quart","Quint"].each(function(b,a){Fx.Transitions[b]=new Fx.Transition(function(c){return Math.pow(c,a+2); });});(function(){var d=function(){},a=("onprogress" in new Browser.Request);var c=this.Request=new Class({Implements:[Chain,Events,Options],options:{url:"",data:"",headers:{"X-Requested-With":"XMLHttpRequest",Accept:"text/javascript, text/html, application/xml, text/xml, */*"},async:true,format:false,method:"post",link:"ignore",isSuccess:null,emulation:true,urlEncoded:true,encoding:"utf-8",evalScripts:false,evalResponse:false,timeout:0,noCache:false},initialize:function(e){this.xhr=new Browser.Request(); this.setOptions(e);this.headers=this.options.headers;},onStateChange:function(){var e=this.xhr;if(e.readyState!=4||!this.running){return;}this.running=false; this.status=0;Function.attempt(function(){var f=e.status;this.status=(f==1223)?204:f;}.bind(this));e.onreadystatechange=d;if(a){e.onprogress=e.onloadstart=d; }clearTimeout(this.timer);this.response={text:this.xhr.responseText||"",xml:this.xhr.responseXML};if(this.options.isSuccess.call(this,this.status)){this.success(this.response.text,this.response.xml); }else{this.failure();}},isSuccess:function(){var e=this.status;return(e>=200&&e<300);},isRunning:function(){return !!this.running;},processScripts:function(e){if(this.options.evalResponse||(/(ecma|java)script/).test(this.getHeader("Content-type"))){return Browser.exec(e); }return e.stripScripts(this.options.evalScripts);},success:function(f,e){this.onSuccess(this.processScripts(f),e);},onSuccess:function(){this.fireEvent("complete",arguments).fireEvent("success",arguments).callChain(); },failure:function(){this.onFailure();},onFailure:function(){this.fireEvent("complete").fireEvent("failure",this.xhr);},loadstart:function(e){this.fireEvent("loadstart",[e,this.xhr]); },progress:function(e){this.fireEvent("progress",[e,this.xhr]);},timeout:function(){this.fireEvent("timeout",this.xhr);},setHeader:function(e,f){this.headers[e]=f; return this;},getHeader:function(e){return Function.attempt(function(){return this.xhr.getResponseHeader(e);}.bind(this));},check:function(){if(!this.running){return true; }switch(this.options.link){case"cancel":this.cancel();return true;case"chain":this.chain(this.caller.pass(arguments,this));return false;}return false;},send:function(o){if(!this.check(o)){return this; }this.options.isSuccess=this.options.isSuccess||this.isSuccess;this.running=true;var l=typeOf(o);if(l=="string"||l=="element"){o={data:o};}var h=this.options; o=Object.append({data:h.data,url:h.url,method:h.method},o);var j=o.data,f=String(o.url),e=o.method.toLowerCase();switch(typeOf(j)){case"element":j=document.id(j).toQueryString(); break;case"object":case"hash":j=Object.toQueryString(j);}if(this.options.format){var m="format="+this.options.format;j=(j)?m+"&"+j:m;}if(this.options.emulation&&!["get","post"].contains(e)){var k="_method="+e; j=(j)?k+"&"+j:k;e="post";}if(this.options.urlEncoded&&["post","put"].contains(e)){var g=(this.options.encoding)?"; charset="+this.options.encoding:"";this.headers["Content-type"]="application/x-www-form-urlencoded"+g; }if(!f){f=document.location.pathname;}var i=f.lastIndexOf("/");if(i>-1&&(i=f.indexOf("#"))>-1){f=f.substr(0,i);}if(this.options.noCache){f+=(f.contains("?")?"&":"?")+String.uniqueID(); }if(j&&e=="get"){f+=(f.contains("?")?"&":"?")+j;j=null;}var n=this.xhr;if(a){n.onloadstart=this.loadstart.bind(this);n.onprogress=this.progress.bind(this); }n.open(e.toUpperCase(),f,this.options.async,this.options.user,this.options.password);if(this.options.user&&"withCredentials" in n){n.withCredentials=true; }n.onreadystatechange=this.onStateChange.bind(this);Object.each(this.headers,function(q,p){try{n.setRequestHeader(p,q);}catch(r){this.fireEvent("exception",[p,q]); }},this);this.fireEvent("request");n.send(j);if(!this.options.async){this.onStateChange();}if(this.options.timeout){this.timer=this.timeout.delay(this.options.timeout,this); }return this;},cancel:function(){if(!this.running){return this;}this.running=false;var e=this.xhr;e.abort();clearTimeout(this.timer);e.onreadystatechange=d; if(a){e.onprogress=e.onloadstart=d;}this.xhr=new Browser.Request();this.fireEvent("cancel");return this;}});var b={};["get","post","put","delete","GET","POST","PUT","DELETE"].each(function(e){b[e]=function(g){var f={method:e}; if(g!=null){f.data=g;}return this.send(f);};});c.implement(b);Element.Properties.send={set:function(e){var f=this.get("send").cancel();f.setOptions(e); return this;},get:function(){var e=this.retrieve("send");if(!e){e=new c({data:this,link:"cancel",method:this.get("method")||"post",url:this.get("action")}); this.store("send",e);}return e;}};Element.implement({send:function(e){var f=this.get("send");f.send({data:this,url:e||f.options.url});return this;}});})(); Request.HTML=new Class({Extends:Request,options:{update:false,append:false,evalScripts:true,filter:false,headers:{Accept:"text/html, application/xml, text/xml, */*"}},success:function(e){var d=this.options,b=this.response; b.html=e.stripScripts(function(f){b.javascript=f;});var c=b.html.match(/]*>([\s\S]*?)<\/body>/i);if(c){b.html=c[1];}var a=new Element("div").set("html",b.html); b.tree=a.childNodes;b.elements=a.getElements("*");if(d.filter){b.tree=b.elements.filter(d.filter);}if(d.update){document.id(d.update).empty().set("html",b.html); }else{if(d.append){document.id(d.append).adopt(a.getChildren());}}if(d.evalScripts){Browser.exec(b.javascript);}this.onSuccess(b.tree,b.elements,b.html,b.javascript); }});Element.Properties.load={set:function(a){var b=this.get("load").cancel();b.setOptions(a);return this;},get:function(){var a=this.retrieve("load");if(!a){a=new Request.HTML({data:this,link:"cancel",update:this,method:"get"}); this.store("load",a);}return a;}};Element.implement({load:function(){this.get("load").send(Array.link(arguments,{data:Type.isObject,url:Type.isString})); return this;}});if(typeof JSON=="undefined"){this.JSON={};}(function(){var special={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"}; var escape=function(chr){return special[chr]||"\\u"+("0000"+chr.charCodeAt(0).toString(16)).slice(-4);};JSON.validate=function(string){string=string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""); return(/^[\],:{}\s]*$/).test(string);};JSON.encode=JSON.stringify?function(obj){return JSON.stringify(obj);}:function(obj){if(obj&&obj.toJSON){obj=obj.toJSON(); }switch(typeOf(obj)){case"string":return'"'+obj.replace(/[\x00-\x1f\\"]/g,escape)+'"';case"array":return"["+obj.map(JSON.encode).clean()+"]";case"object":case"hash":var string=[]; Object.each(obj,function(value,key){var json=JSON.encode(value);if(json){string.push(JSON.encode(key)+":"+json);}});return"{"+string+"}";case"number":case"boolean":return""+obj; case"null":return"null";}return null;};JSON.decode=function(string,secure){if(!string||typeOf(string)!="string"){return null;}if(secure||JSON.secure){if(JSON.parse){return JSON.parse(string); }if(!JSON.validate(string)){throw new Error("JSON could not decode the input; security is enabled and the value is not secure.");}}return eval("("+string+")"); };})();Request.JSON=new Class({Extends:Request,options:{secure:true},initialize:function(a){this.parent(a);Object.append(this.headers,{Accept:"application/json","X-Request":"JSON"}); },success:function(c){var b;try{b=this.response.json=JSON.decode(c,this.options.secure);}catch(a){this.fireEvent("error",[c,a]);return;}if(b==null){this.onFailure(); }else{this.onSuccess(b,c);}}});var Cookie=new Class({Implements:Options,options:{path:"/",domain:false,duration:false,secure:false,document:document,encode:true},initialize:function(b,a){this.key=b; this.setOptions(a);},write:function(b){if(this.options.encode){b=encodeURIComponent(b);}if(this.options.domain){b+="; domain="+this.options.domain;}if(this.options.path){b+="; path="+this.options.path; }if(this.options.duration){var a=new Date();a.setTime(a.getTime()+this.options.duration*24*60*60*1000);b+="; expires="+a.toGMTString();}if(this.options.secure){b+="; secure"; }this.options.document.cookie=this.key+"="+b;return this;},read:function(){var a=this.options.document.cookie.match("(?:^|;)\\s*"+this.key.escapeRegExp()+"=([^;]*)"); return(a)?decodeURIComponent(a[1]):null;},dispose:function(){new Cookie(this.key,Object.merge({},this.options,{duration:-1})).write("");return this;}}); Cookie.write=function(b,c,a){return new Cookie(b,a).write(c);};Cookie.read=function(a){return new Cookie(a).read();};Cookie.dispose=function(b,a){return new Cookie(b,a).dispose(); };(function(i,k){var l,f,e=[],c,b,d=k.createElement("div");var g=function(){clearTimeout(b);if(l){return;}Browser.loaded=l=true;k.removeListener("DOMContentLoaded",g).removeListener("readystatechange",a); k.fireEvent("domready");i.fireEvent("domready");};var a=function(){for(var m=e.length;m--;){if(e[m]()){g();return true;}}return false;};var j=function(){clearTimeout(b); if(!a()){b=setTimeout(j,10);}};k.addListener("DOMContentLoaded",g);var h=function(){try{d.doScroll();return true;}catch(m){}return false;};if(d.doScroll&&!h()){e.push(h); c=true;}if(k.readyState){e.push(function(){var m=k.readyState;return(m=="loaded"||m=="complete");});}if("onreadystatechange" in k){k.addListener("readystatechange",a); }else{c=true;}if(c){j();}Element.Events.domready={onAdd:function(m){if(l){m.call(this);}}};Element.Events.load={base:"load",onAdd:function(m){if(f&&this==i){m.call(this); }},condition:function(){if(this==i){g();delete Element.Events.load;}return true;}};i.addEvent("load",function(){f=true;});})(window,document);(function(){var Swiff=this.Swiff=new Class({Implements:Options,options:{id:null,height:1,width:1,container:null,properties:{},params:{quality:"high",allowScriptAccess:"always",wMode:"window",swLiveConnect:true},callBacks:{},vars:{}},toElement:function(){return this.object; },initialize:function(path,options){this.instance="Swiff_"+String.uniqueID();this.setOptions(options);options=this.options;var id=this.id=options.id||this.instance; var container=document.id(options.container);Swiff.CallBacks[this.instance]={};var params=options.params,vars=options.vars,callBacks=options.callBacks; var properties=Object.append({height:options.height,width:options.width},options.properties);var self=this;for(var callBack in callBacks){Swiff.CallBacks[this.instance][callBack]=(function(option){return function(){return option.apply(self.object,arguments); };})(callBacks[callBack]);vars[callBack]="Swiff.CallBacks."+this.instance+"."+callBack;}params.flashVars=Object.toQueryString(vars);if(Browser.ie){properties.classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"; params.movie=path;}else{properties.type="application/x-shockwave-flash";}properties.data=path;var build='';}}build+="";this.object=((container)?container.empty():new Element("div")).set("html",build).firstChild; },replaces:function(element){element=document.id(element,true);element.parentNode.replaceChild(this.toElement(),element);return this;},inject:function(element){document.id(element,true).appendChild(this.toElement()); return this;},remote:function(){return Swiff.remote.apply(Swiff,[this.toElement()].append(arguments));}});Swiff.CallBacks={};Swiff.remote=function(obj,fn){var rs=obj.CallFunction(''+__flash__argumentsToXML(arguments,2)+""); return eval(rs);};})();ZoneMinder-1.26.5/web/tools/mootools/mootools-more-1.3.2.1-nc.js000066400000000000000000007161041225361755400240370ustar00rootroot00000000000000// MooTools: the javascript framework. // Load this file's selection again by visiting: http://mootools.net/more/09f3e47813269cd5026cbf8c1f828e72 // Or build this file again with packager using: packager build More/Chain.Wait More/Array.Extras More/Date More/Date.Extras More/Number.Format More/String.Extras More/String.QueryString More/URI More/URI.Relative More/Hash More/Hash.Extras More/Element.Forms More/Elements.From More/Element.Event.Pseudos.Keys More/Element.Pin More/Element.Position More/Element.Shortcuts More/Form.Request More/Form.Request.Append More/Form.Validator.Inline More/Form.Validator.Extras More/OverText More/Fx.Accordion More/Fx.Move More/Fx.Reveal More/Fx.Slide More/Fx.SmoothScroll More/Fx.Sort More/Drag.Move More/Slider More/Sortables More/Request.JSONP More/Request.Queue More/Request.Periodical More/Assets More/Color More/Group More/Hash.Cookie More/Table More/HtmlTable.Zebra More/HtmlTable.Sort More/HtmlTable.Select More/Keyboard.Extras More/Mask More/Scroller More/Tips More/Locale.en-GB.Date /* --- script: More.js name: More description: MooTools More license: MIT-style license authors: - Guillermo Rauch - Thomas Aylott - Scott Kyle - Arian Stolwijk - Tim Wienk - Christoph Pojer - Aaron Newton - Jacob Thornton requires: - Core/MooTools provides: [MooTools.More] ... */ MooTools.More = { 'version': '1.3.2.1', 'build': 'e586bcd2496e9b22acfde32e12f84d49ce09e59d' }; /* --- script: Chain.Wait.js name: Chain.Wait description: value, Adds a method to inject pauses between chained events. license: MIT-style license. authors: - Aaron Newton requires: - Core/Chain - Core/Element - Core/Fx - /MooTools.More provides: [Chain.Wait] ... */ (function(){ var wait = { wait: function(duration){ return this.chain(function(){ this.callChain.delay(duration == null ? 500 : duration, this); return this; }.bind(this)); } }; Chain.implement(wait); if (this.Fx) Fx.implement(wait); if (this.Element && Element.implement && this.Fx){ Element.implement({ chains: function(effects){ Array.from(effects || ['tween', 'morph', 'reveal']).each(function(effect){ effect = this.get(effect); if (!effect) return; effect.setOptions({ link:'chain' }); }, this); return this; }, pauseFx: function(duration, effect){ this.chains(effect).get(effect || 'tween').wait(duration); return this; } }); } })(); /* --- script: Array.Extras.js name: Array.Extras description: Extends the Array native object to include useful methods to work with arrays. license: MIT-style license authors: - Christoph Pojer - Sebastian MarkbÃ¥ge requires: - Core/Array - MooTools.More provides: [Array.Extras] ... */ (function(nil){ Array.implement({ min: function(){ return Math.min.apply(null, this); }, max: function(){ return Math.max.apply(null, this); }, average: function(){ return this.length ? this.sum() / this.length : 0; }, sum: function(){ var result = 0, l = this.length; if (l){ while (l--) result += this[l]; } return result; }, unique: function(){ return [].combine(this); }, shuffle: function(){ for (var i = this.length; i && --i;){ var temp = this[i], r = Math.floor(Math.random() * ( i + 1 )); this[i] = this[r]; this[r] = temp; } return this; }, reduce: function(fn, value){ for (var i = 0, l = this.length; i < l; i++){ if (i in this) value = value === nil ? this[i] : fn.call(null, value, this[i], i, this); } return value; }, reduceRight: function(fn, value){ var i = this.length; while (i--){ if (i in this) value = value === nil ? this[i] : fn.call(null, value, this[i], i, this); } return value; } }); })(); /* --- script: Object.Extras.js name: Object.Extras description: Extra Object generics, like getFromPath which allows a path notation to child elements. license: MIT-style license authors: - Aaron Newton requires: - Core/Object - /MooTools.More provides: [Object.Extras] ... */ (function(){ var defined = function(value){ return value != null; }; var hasOwnProperty = Object.prototype.hasOwnProperty; Object.extend({ getFromPath: function(source, parts){ if (typeof parts == 'string') parts = parts.split('.'); for (var i = 0, l = parts.length; i < l; i++){ if (hasOwnProperty.call(source, parts[i])) source = source[parts[i]]; else return null; } return source; }, cleanValues: function(object, method){ method = method || defined; for (var key in object) if (!method(object[key])){ delete object[key]; } return object; }, erase: function(object, key){ if (hasOwnProperty.call(object, key)) delete object[key]; return object; }, run: function(object){ var args = Array.slice(arguments, 1); for (var key in object) if (object[key].apply){ object[key].apply(object, args); } return object; } }); })(); /* --- script: Locale.js name: Locale description: Provides methods for localization. license: MIT-style license authors: - Aaron Newton - Arian Stolwijk requires: - Core/Events - /Object.Extras - /MooTools.More provides: [Locale, Lang] ... */ (function(){ var current = null, locales = {}, inherits = {}; var getSet = function(set){ if (instanceOf(set, Locale.Set)) return set; else return locales[set]; }; var Locale = this.Locale = { define: function(locale, set, key, value){ var name; if (instanceOf(locale, Locale.Set)){ name = locale.name; if (name) locales[name] = locale; } else { name = locale; if (!locales[name]) locales[name] = new Locale.Set(name); locale = locales[name]; } if (set) locale.define(set, key, value); if (!current) current = locale; return locale; }, use: function(locale){ locale = getSet(locale); if (locale){ current = locale; this.fireEvent('change', locale); } return this; }, getCurrent: function(){ return current; }, get: function(key, args){ return (current) ? current.get(key, args) : ''; }, inherit: function(locale, inherits, set){ locale = getSet(locale); if (locale) locale.inherit(inherits, set); return this; }, list: function(){ return Object.keys(locales); } }; Object.append(Locale, new Events); Locale.Set = new Class({ sets: {}, inherits: { locales: [], sets: {} }, initialize: function(name){ this.name = name || ''; }, define: function(set, key, value){ var defineData = this.sets[set]; if (!defineData) defineData = {}; if (key){ if (typeOf(key) == 'object') defineData = Object.merge(defineData, key); else defineData[key] = value; } this.sets[set] = defineData; return this; }, get: function(key, args, _base){ var value = Object.getFromPath(this.sets, key); if (value != null){ var type = typeOf(value); if (type == 'function') value = value.apply(null, Array.from(args)); else if (type == 'object') value = Object.clone(value); return value; } // get value of inherited locales var index = key.indexOf('.'), set = index < 0 ? key : key.substr(0, index), names = (this.inherits.sets[set] || []).combine(this.inherits.locales).include('en-US'); if (!_base) _base = []; for (var i = 0, l = names.length; i < l; i++){ if (_base.contains(names[i])) continue; _base.include(names[i]); var locale = locales[names[i]]; if (!locale) continue; value = locale.get(key, args, _base); if (value != null) return value; } return ''; }, inherit: function(names, set){ names = Array.from(names); if (set && !this.inherits.sets[set]) this.inherits.sets[set] = []; var l = names.length; while (l--) (set ? this.inherits.sets[set] : this.inherits.locales).unshift(names[l]); return this; } }); })(); /* --- name: Locale.en-US.Date description: Date messages for US English. license: MIT-style license authors: - Aaron Newton requires: - /Locale provides: [Locale.en-US.Date] ... */ Locale.define('en-US', 'Date', { months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], months_abbr: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], days_abbr: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // Culture's date order: MM/DD/YYYY dateOrder: ['month', 'date', 'year'], shortDate: '%m/%d/%Y', shortTime: '%I:%M%p', AM: 'AM', PM: 'PM', firstDayOfWeek: 0, // Date.Extras ordinal: function(dayOfMonth){ // 1st, 2nd, 3rd, etc. return (dayOfMonth > 3 && dayOfMonth < 21) ? 'th' : ['th', 'st', 'nd', 'rd', 'th'][Math.min(dayOfMonth % 10, 4)]; }, lessThanMinuteAgo: 'less than a minute ago', minuteAgo: 'about a minute ago', minutesAgo: '{delta} minutes ago', hourAgo: 'about an hour ago', hoursAgo: 'about {delta} hours ago', dayAgo: '1 day ago', daysAgo: '{delta} days ago', weekAgo: '1 week ago', weeksAgo: '{delta} weeks ago', monthAgo: '1 month ago', monthsAgo: '{delta} months ago', yearAgo: '1 year ago', yearsAgo: '{delta} years ago', lessThanMinuteUntil: 'less than a minute from now', minuteUntil: 'about a minute from now', minutesUntil: '{delta} minutes from now', hourUntil: 'about an hour from now', hoursUntil: 'about {delta} hours from now', dayUntil: '1 day from now', daysUntil: '{delta} days from now', weekUntil: '1 week from now', weeksUntil: '{delta} weeks from now', monthUntil: '1 month from now', monthsUntil: '{delta} months from now', yearUntil: '1 year from now', yearsUntil: '{delta} years from now' }); /* --- script: Date.js name: Date description: Extends the Date native object to include methods useful in managing dates. license: MIT-style license authors: - Aaron Newton - Nicholas Barthelemy - https://svn.nbarthelemy.com/date-js/ - Harald Kirshner - mail [at] digitarald.de; http://digitarald.de - Scott Kyle - scott [at] appden.com; http://appden.com requires: - Core/Array - Core/String - Core/Number - MooTools.More - Locale - Locale.en-US.Date provides: [Date] ... */ (function(){ var Date = this.Date; var DateMethods = Date.Methods = { ms: 'Milliseconds', year: 'FullYear', min: 'Minutes', mo: 'Month', sec: 'Seconds', hr: 'Hours' }; ['Date', 'Day', 'FullYear', 'Hours', 'Milliseconds', 'Minutes', 'Month', 'Seconds', 'Time', 'TimezoneOffset', 'Week', 'Timezone', 'GMTOffset', 'DayOfYear', 'LastMonth', 'LastDayOfMonth', 'UTCDate', 'UTCDay', 'UTCFullYear', 'AMPM', 'Ordinal', 'UTCHours', 'UTCMilliseconds', 'UTCMinutes', 'UTCMonth', 'UTCSeconds', 'UTCMilliseconds'].each(function(method){ Date.Methods[method.toLowerCase()] = method; }); var pad = function(n, digits, string){ if (digits == 1) return n; return n < Math.pow(10, digits - 1) ? (string || '0') + pad(n, digits - 1, string) : n; }; Date.implement({ set: function(prop, value){ prop = prop.toLowerCase(); var method = DateMethods[prop] && 'set' + DateMethods[prop]; if (method && this[method]) this[method](value); return this; }.overloadSetter(), get: function(prop){ prop = prop.toLowerCase(); var method = DateMethods[prop] && 'get' + DateMethods[prop]; if (method && this[method]) return this[method](); return null; }.overloadGetter(), clone: function(){ return new Date(this.get('time')); }, increment: function(interval, times){ interval = interval || 'day'; times = times != null ? times : 1; switch (interval){ case 'year': return this.increment('month', times * 12); case 'month': var d = this.get('date'); this.set('date', 1).set('mo', this.get('mo') + times); return this.set('date', d.min(this.get('lastdayofmonth'))); case 'week': return this.increment('day', times * 7); case 'day': return this.set('date', this.get('date') + times); } if (!Date.units[interval]) throw new Error(interval + ' is not a supported interval'); return this.set('time', this.get('time') + times * Date.units[interval]()); }, decrement: function(interval, times){ return this.increment(interval, -1 * (times != null ? times : 1)); }, isLeapYear: function(){ return Date.isLeapYear(this.get('year')); }, clearTime: function(){ return this.set({hr: 0, min: 0, sec: 0, ms: 0}); }, diff: function(date, resolution){ if (typeOf(date) == 'string') date = Date.parse(date); return ((date - this) / Date.units[resolution || 'day'](3, 3)).round(); // non-leap year, 30-day month }, getLastDayOfMonth: function(){ return Date.daysInMonth(this.get('mo'), this.get('year')); }, getDayOfYear: function(){ return (Date.UTC(this.get('year'), this.get('mo'), this.get('date') + 1) - Date.UTC(this.get('year'), 0, 1)) / Date.units.day(); }, setDay: function(day, firstDayOfWeek){ if (firstDayOfWeek == null){ firstDayOfWeek = Date.getMsg('firstDayOfWeek'); if (firstDayOfWeek === '') firstDayOfWeek = 1; } day = (7 + Date.parseDay(day, true) - firstDayOfWeek) % 7; var currentDay = (7 + this.get('day') - firstDayOfWeek) % 7; return this.increment('day', day - currentDay); }, getWeek: function(firstDayOfWeek){ if (firstDayOfWeek == null){ firstDayOfWeek = Date.getMsg('firstDayOfWeek'); if (firstDayOfWeek === '') firstDayOfWeek = 1; } var date = this, dayOfWeek = (7 + date.get('day') - firstDayOfWeek) % 7, dividend = 0, firstDayOfYear; if (firstDayOfWeek == 1){ // ISO-8601, week belongs to year that has the most days of the week (i.e. has the thursday of the week) var month = date.get('month'), startOfWeek = date.get('date') - dayOfWeek; if (month == 11 && startOfWeek > 28) return 1; // Week 1 of next year if (month == 0 && startOfWeek < -2){ // Use a date from last year to determine the week date = new Date(date).decrement('day', dayOfWeek); dayOfWeek = 0; } firstDayOfYear = new Date(date.get('year'), 0, 1).get('day') || 7; if (firstDayOfYear > 4) dividend = -7; // First week of the year is not week 1 } else { // In other cultures the first week of the year is always week 1 and the last week always 53 or 54. // Days in the same week can have a different weeknumber if the week spreads across two years. firstDayOfYear = new Date(date.get('year'), 0, 1).get('day'); } dividend += date.get('dayofyear'); dividend += 6 - dayOfWeek; // Add days so we calculate the current date's week as a full week dividend += (7 + firstDayOfYear - firstDayOfWeek) % 7; // Make up for first week of the year not being a full week return (dividend / 7); }, getOrdinal: function(day){ return Date.getMsg('ordinal', day || this.get('date')); }, getTimezone: function(){ return this.toString() .replace(/^.*? ([A-Z]{3}).[0-9]{4}.*$/, '$1') .replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, '$1$2$3'); }, getGMTOffset: function(){ var off = this.get('timezoneOffset'); return ((off > 0) ? '-' : '+') + pad((off.abs() / 60).floor(), 2) + pad(off % 60, 2); }, setAMPM: function(ampm){ ampm = ampm.toUpperCase(); var hr = this.get('hr'); if (hr > 11 && ampm == 'AM') return this.decrement('hour', 12); else if (hr < 12 && ampm == 'PM') return this.increment('hour', 12); return this; }, getAMPM: function(){ return (this.get('hr') < 12) ? 'AM' : 'PM'; }, parse: function(str){ this.set('time', Date.parse(str)); return this; }, isValid: function(date){ return !isNaN((date || this).valueOf()); }, format: function(f){ if (!this.isValid()) return 'invalid date'; if (!f) f = '%x %X'; var formatLower = f.toLowerCase(); if (formatters[formatLower]) return formatters[formatLower](this); // it's a formatter! f = formats[formatLower] || f; // replace short-hand with actual format var d = this; return f.replace(/%([a-z%])/gi, function($0, $1){ switch ($1){ case 'a': return Date.getMsg('days_abbr')[d.get('day')]; case 'A': return Date.getMsg('days')[d.get('day')]; case 'b': return Date.getMsg('months_abbr')[d.get('month')]; case 'B': return Date.getMsg('months')[d.get('month')]; case 'c': return d.format('%a %b %d %H:%M:%S %Y'); case 'd': return pad(d.get('date'), 2); case 'e': return pad(d.get('date'), 2, ' '); case 'H': return pad(d.get('hr'), 2); case 'I': return pad((d.get('hr') % 12) || 12, 2); case 'j': return pad(d.get('dayofyear'), 3); case 'k': return pad(d.get('hr'), 2, ' '); case 'l': return pad((d.get('hr') % 12) || 12, 2, ' '); case 'L': return pad(d.get('ms'), 3); case 'm': return pad((d.get('mo') + 1), 2); case 'M': return pad(d.get('min'), 2); case 'o': return d.get('ordinal'); case 'p': return Date.getMsg(d.get('ampm')); case 's': return Math.round(d / 1000); case 'S': return pad(d.get('seconds'), 2); case 'T': return d.format('%H:%M:%S'); case 'U': return pad(d.get('week'), 2); case 'w': return d.get('day'); case 'x': return d.format(Date.getMsg('shortDate')); case 'X': return d.format(Date.getMsg('shortTime')); case 'y': return d.get('year').toString().substr(2); case 'Y': return d.get('year'); case 'z': return d.get('GMTOffset'); case 'Z': return d.get('Timezone'); } return $1; } ); }, toISOString: function(){ return this.format('iso8601'); } }).alias({ toJSON: 'toISOString', compare: 'diff', strftime: 'format' }); var formats = { db: '%Y-%m-%d %H:%M:%S', compact: '%Y%m%dT%H%M%S', 'short': '%d %b %H:%M', 'long': '%B %d, %Y %H:%M' }; // The day and month abbreviations are standardized, so we cannot use simply %a and %b because they will get localized var rfcDayAbbr = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], rfcMonthAbbr = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; var formatters = { rfc822: function(date){ return rfcDayAbbr[date.get('day')] + date.format(', %d ') + rfcMonthAbbr[date.get('month')] + date.format(' %Y %H:%M:%S %Z'); }, rfc2822: function(date){ return rfcDayAbbr[date.get('day')] + date.format(', %d ') + rfcMonthAbbr[date.get('month')] + date.format(' %Y %H:%M:%S %z'); }, iso8601: function(date){ return ( date.getUTCFullYear() + '-' + pad(date.getUTCMonth() + 1, 2) + '-' + pad(date.getUTCDate(), 2) + 'T' + pad(date.getUTCHours(), 2) + ':' + pad(date.getUTCMinutes(), 2) + ':' + pad(date.getUTCSeconds(), 2) + '.' + pad(date.getUTCMilliseconds(), 3) + 'Z' ); } }; var parsePatterns = [], nativeParse = Date.parse; var parseWord = function(type, word, num){ var ret = -1, translated = Date.getMsg(type + 's'); switch (typeOf(word)){ case 'object': ret = translated[word.get(type)]; break; case 'number': ret = translated[word]; if (!ret) throw new Error('Invalid ' + type + ' index: ' + word); break; case 'string': var match = translated.filter(function(name){ return this.test(name); }, new RegExp('^' + word, 'i')); if (!match.length) throw new Error('Invalid ' + type + ' string'); if (match.length > 1) throw new Error('Ambiguous ' + type); ret = match[0]; } return (num) ? translated.indexOf(ret) : ret; }; var startCentury = 1900, startYear = 70; Date.extend({ getMsg: function(key, args){ return Locale.get('Date.' + key, args); }, units: { ms: Function.from(1), second: Function.from(1000), minute: Function.from(60000), hour: Function.from(3600000), day: Function.from(86400000), week: Function.from(608400000), month: function(month, year){ var d = new Date; return Date.daysInMonth(month != null ? month : d.get('mo'), year != null ? year : d.get('year')) * 86400000; }, year: function(year){ year = year || new Date().get('year'); return Date.isLeapYear(year) ? 31622400000 : 31536000000; } }, daysInMonth: function(month, year){ return [31, Date.isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; }, isLeapYear: function(year){ return ((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0); }, parse: function(from){ var t = typeOf(from); if (t == 'number') return new Date(from); if (t != 'string') return from; from = from.clean(); if (!from.length) return null; var parsed; parsePatterns.some(function(pattern){ var bits = pattern.re.exec(from); return (bits) ? (parsed = pattern.handler(bits)) : false; }); if (!(parsed && parsed.isValid())){ parsed = new Date(nativeParse(from)); if (!(parsed && parsed.isValid())) parsed = new Date(from.toInt()); } return parsed; }, parseDay: function(day, num){ return parseWord('day', day, num); }, parseMonth: function(month, num){ return parseWord('month', month, num); }, parseUTC: function(value){ var localDate = new Date(value); var utcSeconds = Date.UTC( localDate.get('year'), localDate.get('mo'), localDate.get('date'), localDate.get('hr'), localDate.get('min'), localDate.get('sec'), localDate.get('ms') ); return new Date(utcSeconds); }, orderIndex: function(unit){ return Date.getMsg('dateOrder').indexOf(unit) + 1; }, defineFormat: function(name, format){ formats[name] = format; return this; }, defineFormats: function(formats){ for (var name in formats) Date.defineFormat(name, formats[name]); return this; }, defineParser: function(pattern){ parsePatterns.push((pattern.re && pattern.handler) ? pattern : build(pattern)); return this; }, defineParsers: function(){ Array.flatten(arguments).each(Date.defineParser); return this; }, define2DigitYearStart: function(year){ startYear = year % 100; startCentury = year - startYear; return this; } }); var regexOf = function(type){ return new RegExp('(?:' + Date.getMsg(type).map(function(name){ return name.substr(0, 3); }).join('|') + ')[a-z]*'); }; var replacers = function(key){ switch (key){ case 'T': return '%H:%M:%S'; case 'x': // iso8601 covers yyyy-mm-dd, so just check if month is first return ((Date.orderIndex('month') == 1) ? '%m[-./]%d' : '%d[-./]%m') + '([-./]%y)?'; case 'X': return '%H([.:]%M)?([.:]%S([.:]%s)?)? ?%p? ?%z?'; } return null; }; var keys = { d: /[0-2]?[0-9]|3[01]/, H: /[01]?[0-9]|2[0-3]/, I: /0?[1-9]|1[0-2]/, M: /[0-5]?\d/, s: /\d+/, o: /[a-z]*/, p: /[ap]\.?m\.?/, y: /\d{2}|\d{4}/, Y: /\d{4}/, z: /Z|[+-]\d{2}(?::?\d{2})?/ }; keys.m = keys.I; keys.S = keys.M; var currentLanguage; var recompile = function(language){ currentLanguage = language; keys.a = keys.A = regexOf('days'); keys.b = keys.B = regexOf('months'); parsePatterns.each(function(pattern, i){ if (pattern.format) parsePatterns[i] = build(pattern.format); }); }; var build = function(format){ if (!currentLanguage) return {format: format}; var parsed = []; var re = (format.source || format) // allow format to be regex .replace(/%([a-z])/gi, function($0, $1){ return replacers($1) || $0; } ).replace(/\((?!\?)/g, '(?:') // make all groups non-capturing .replace(/ (?!\?|\*)/g, ',? ') // be forgiving with spaces and commas .replace(/%([a-z%])/gi, function($0, $1){ var p = keys[$1]; if (!p) return $1; parsed.push($1); return '(' + p.source + ')'; } ).replace(/\[a-z\]/gi, '[a-z\\u00c0-\\uffff;\&]'); // handle unicode words return { format: format, re: new RegExp('^' + re + '$', 'i'), handler: function(bits){ bits = bits.slice(1).associate(parsed); var date = new Date().clearTime(), year = bits.y || bits.Y; if (year != null) handle.call(date, 'y', year); // need to start in the right year if ('d' in bits) handle.call(date, 'd', 1); if ('m' in bits || bits.b || bits.B) handle.call(date, 'm', 1); for (var key in bits) handle.call(date, key, bits[key]); return date; } }; }; var handle = function(key, value){ if (!value) return this; switch (key){ case 'a': case 'A': return this.set('day', Date.parseDay(value, true)); case 'b': case 'B': return this.set('mo', Date.parseMonth(value, true)); case 'd': return this.set('date', value); case 'H': case 'I': return this.set('hr', value); case 'm': return this.set('mo', value - 1); case 'M': return this.set('min', value); case 'p': return this.set('ampm', value.replace(/\./g, '')); case 'S': return this.set('sec', value); case 's': return this.set('ms', ('0.' + value) * 1000); case 'w': return this.set('day', value); case 'Y': return this.set('year', value); case 'y': value = +value; if (value < 100) value += startCentury + (value < startYear ? 100 : 0); return this.set('year', value); case 'z': if (value == 'Z') value = '+00'; var offset = value.match(/([+-])(\d{2}):?(\d{2})?/); offset = (offset[1] + '1') * (offset[2] * 60 + (+offset[3] || 0)) + this.getTimezoneOffset(); return this.set('time', this - offset * 60000); } return this; }; Date.defineParsers( '%Y([-./]%m([-./]%d((T| )%X)?)?)?', // "1999-12-31", "1999-12-31 11:59pm", "1999-12-31 23:59:59", ISO8601 '%Y%m%d(T%H(%M%S?)?)?', // "19991231", "19991231T1159", compact '%x( %X)?', // "12/31", "12.31.99", "12-31-1999", "12/31/2008 11:59 PM" '%d%o( %b( %Y)?)?( %X)?', // "31st", "31st December", "31 Dec 1999", "31 Dec 1999 11:59pm" '%b( %d%o)?( %Y)?( %X)?', // Same as above with month and day switched '%Y %b( %d%o( %X)?)?', // Same as above with year coming first '%o %b %d %X %z %Y', // "Thu Oct 22 08:11:23 +0000 2009" '%T', // %H:%M:%S '%H:%M( ?%p)?' // "11:05pm", "11:05 am" and "11:05" ); Locale.addEvent('change', function(language){ if (Locale.get('Date')) recompile(language); }).fireEvent('change', Locale.getCurrent()); })(); /* --- script: Date.Extras.js name: Date.Extras description: Extends the Date native object to include extra methods (on top of those in Date.js). license: MIT-style license authors: - Aaron Newton - Scott Kyle requires: - /Date provides: [Date.Extras] ... */ Date.implement({ timeDiffInWords: function(to){ return Date.distanceOfTimeInWords(this, to || new Date); }, timeDiff: function(to, separator){ if (to == null) to = new Date; var delta = ((to - this) / 1000).floor().abs(); var vals = [], durations = [60, 60, 24, 365, 0], names = ['s', 'm', 'h', 'd', 'y'], value, duration; for (var item = 0; item < durations.length; item++){ if (item && !delta) break; value = delta; if ((duration = durations[item])){ value = (delta % duration); delta = (delta / duration).floor(); } vals.unshift(value + (names[item] || '')); } return vals.join(separator || ':'); } }).extend({ distanceOfTimeInWords: function(from, to){ return Date.getTimePhrase(((to - from) / 1000).toInt()); }, getTimePhrase: function(delta){ var suffix = (delta < 0) ? 'Until' : 'Ago'; if (delta < 0) delta *= -1; var units = { minute: 60, hour: 60, day: 24, week: 7, month: 52 / 12, year: 12, eon: Infinity }; var msg = 'lessThanMinute'; for (var unit in units){ var interval = units[unit]; if (delta < 1.5 * interval){ if (delta > 0.75 * interval) msg = unit; break; } delta /= interval; msg = unit + 's'; } delta = delta.round(); return Date.getMsg(msg + suffix, delta).substitute({delta: delta}); } }).defineParsers( { // "today", "tomorrow", "yesterday" re: /^(?:tod|tom|yes)/i, handler: function(bits){ var d = new Date().clearTime(); switch (bits[0]){ case 'tom': return d.increment(); case 'yes': return d.decrement(); default: return d; } } }, { // "next Wednesday", "last Thursday" re: /^(next|last) ([a-z]+)$/i, handler: function(bits){ var d = new Date().clearTime(); var day = d.getDay(); var newDay = Date.parseDay(bits[2], true); var addDays = newDay - day; if (newDay <= day) addDays += 7; if (bits[1] == 'last') addDays -= 7; return d.set('date', d.getDate() + addDays); } } ).alias('timeAgoInWords', 'timeDiffInWords'); /* --- name: Locale.en-US.Number description: Number messages for US English. license: MIT-style license authors: - Arian Stolwijk requires: - /Locale provides: [Locale.en-US.Number] ... */ Locale.define('en-US', 'Number', { decimal: '.', group: ',', /* Commented properties are the defaults for Number.format decimals: 0, precision: 0, scientific: null, prefix: null, suffic: null, // Negative/Currency/percentage will mixin Number negative: { prefix: '-' },*/ currency: { // decimals: 2, prefix: '$ ' }/*, percentage: { decimals: 2, suffix: '%' }*/ }); /* --- name: Number.Format description: Extends the Number Type object to include a number formatting method. license: MIT-style license authors: [Arian Stolwijk] requires: [Core/Number, Locale.en-US.Number] # Number.Extras is for compatibility provides: [Number.Format, Number.Extras] ... */ Number.implement({ format: function(options){ // Thanks dojo and YUI for some inspiration var value = this; options = options ? Object.clone(options) : {}; var getOption = function(key){ if (options[key] != null) return options[key]; return Locale.get('Number.' + key); }; var negative = value < 0, decimal = getOption('decimal'), precision = getOption('precision'), group = getOption('group'), decimals = getOption('decimals'); if (negative){ var negativeLocale = getOption('negative') || {}; if (negativeLocale.prefix == null && negativeLocale.suffix == null) negativeLocale.prefix = '-'; ['prefix', 'suffix'].each(function(key){ if (negativeLocale[key]) options[key] = getOption(key) + negativeLocale[key]; }); value = -value; } var prefix = getOption('prefix'), suffix = getOption('suffix'); if (decimals !== '' && decimals >= 0 && decimals <= 20) value = value.toFixed(decimals); if (precision >= 1 && precision <= 21) value = (+value).toPrecision(precision); value += ''; var index; if (getOption('scientific') === false && value.indexOf('e') > -1){ var match = value.split('e'), zeros = +match[1]; value = match[0].replace('.', ''); if (zeros < 0){ zeros = -zeros - 1; index = match[0].indexOf('.'); if (index > -1) zeros -= index - 1; while (zeros--) value = '0' + value; value = '0.' + value; } else { index = match[0].lastIndexOf('.'); if (index > -1) zeros -= match[0].length - index - 1; while (zeros--) value += '0'; } } if (decimal != '.') value = value.replace('.', decimal); if (group){ index = value.lastIndexOf(decimal); index = (index > -1) ? index : value.length; var newOutput = value.substring(index), i = index; while (i--){ if ((index - i - 1) % 3 == 0 && i != (index - 1)) newOutput = group + newOutput; newOutput = value.charAt(i) + newOutput; } value = newOutput; } if (prefix) value = prefix + value; if (suffix) value += suffix; return value; }, formatCurrency: function(){ var locale = Locale.get('Number.currency') || {}; if (locale.scientific == null) locale.scientific = false; if (locale.decimals == null) locale.decimals = 2; return this.format(locale); }, formatPercentage: function(){ var locale = Locale.get('Number.percentage') || {}; if (locale.suffix == null) locale.suffix = '%'; if (locale.decimals == null) locale.decimals = 2; return this.format(locale); } }); /* --- script: String.Extras.js name: String.Extras description: Extends the String native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc). license: MIT-style license authors: - Aaron Newton - Guillermo Rauch - Christopher Pitt requires: - Core/String - Core/Array - MooTools.More provides: [String.Extras] ... */ (function(){ var special = { 'a': /[àáâãäåăą]/g, 'A': /[ÀÃÂÃÄÅĂĄ]/g, 'c': /[ćÄç]/g, 'C': /[ĆČÇ]/g, 'd': /[ÄÄ‘]/g, 'D': /[ÄŽÃ]/g, 'e': /[èéêëěę]/g, 'E': /[ÈÉÊËĚĘ]/g, 'g': /[ÄŸ]/g, 'G': /[Äž]/g, 'i': /[ìíîï]/g, 'I': /[ÃŒÃÃŽÃ]/g, 'l': /[ĺľł]/g, 'L': /[ĹĽÅ]/g, 'n': /[ñňń]/g, 'N': /[ÑŇŃ]/g, 'o': /[òóôõöøő]/g, 'O': /[ÒÓÔÕÖØ]/g, 'r': /[řŕ]/g, 'R': /[ŘŔ]/g, 's': /[ššş]/g, 'S': /[ŠŞŚ]/g, 't': /[ťţ]/g, 'T': /[ŤŢ]/g, 'ue': /[ü]/g, 'UE': /[Ü]/g, 'u': /[ùúûůµ]/g, 'U': /[ÙÚÛŮ]/g, 'y': /[ÿý]/g, 'Y': /[ŸÃ]/g, 'z': /[žźż]/g, 'Z': /[ŽŹŻ]/g, 'th': /[þ]/g, 'TH': /[Þ]/g, 'dh': /[ð]/g, 'DH': /[Ã]/g, 'ss': /[ß]/g, 'oe': /[Å“]/g, 'OE': /[Å’]/g, 'ae': /[æ]/g, 'AE': /[Æ]/g }, tidy = { ' ': /[\xa0\u2002\u2003\u2009]/g, '*': /[\xb7]/g, '\'': /[\u2018\u2019]/g, '"': /[\u201c\u201d]/g, '...': /[\u2026]/g, '-': /[\u2013]/g, // '--': /[\u2014]/g, '»': /[\uFFFD]/g }; var walk = function(string, replacements){ var result = string, key; for (key in replacements) result = result.replace(replacements[key], key); return result; }; var getRegexForTag = function(tag, contents){ tag = tag || ''; var regstr = contents ? "<" + tag + "(?!\\w)[^>]*>([\\s\\S]*?)<\/" + tag + "(?!\\w)>" : "<\/?" + tag + "([^>]+)?>", reg = new RegExp(regstr, "gi"); return reg; }; String.implement({ standardize: function(){ return walk(this, special); }, repeat: function(times){ return new Array(times + 1).join(this); }, pad: function(length, str, direction){ if (this.length >= length) return this; var pad = (str == null ? ' ' : '' + str) .repeat(length - this.length) .substr(0, length - this.length); if (!direction || direction == 'right') return this + pad; if (direction == 'left') return pad + this; return pad.substr(0, (pad.length / 2).floor()) + this + pad.substr(0, (pad.length / 2).ceil()); }, getTags: function(tag, contents){ return this.match(getRegexForTag(tag, contents)) || []; }, stripTags: function(tag, contents){ return this.replace(getRegexForTag(tag, contents), ''); }, tidy: function(){ return walk(this, tidy); }, truncate: function(max, trail, atChar){ var string = this; if (trail == null && arguments.length == 1) trail = '…'; if (string.length > max){ string = string.substring(0, max); if (atChar){ var index = string.lastIndexOf(atChar); if (index != -1) string = string.substr(0, index); } if (trail) string += trail; } return string; } }); })(); /* --- script: String.QueryString.js name: String.QueryString description: Methods for dealing with URI query strings. license: MIT-style license authors: - Sebastian MarkbÃ¥ge - Aaron Newton - Lennart Pilon - Valerio Proietti requires: - Core/Array - Core/String - /MooTools.More provides: [String.QueryString] ... */ String.implement({ parseQueryString: function(decodeKeys, decodeValues){ if (decodeKeys == null) decodeKeys = true; if (decodeValues == null) decodeValues = true; var vars = this.split(/[&;]/), object = {}; if (!vars.length) return object; vars.each(function(val){ var index = val.indexOf('=') + 1, value = index ? val.substr(index) : '', keys = index ? val.substr(0, index - 1).match(/([^\]\[]+|(\B)(?=\]))/g) : [val], obj = object; if (!keys) return; if (decodeValues) value = decodeURIComponent(value); keys.each(function(key, i){ if (decodeKeys) key = decodeURIComponent(key); var current = obj[key]; if (i < keys.length - 1) obj = obj[key] = current || {}; else if (typeOf(current) == 'array') current.push(value); else obj[key] = current != null ? [current, value] : value; }); }); return object; }, cleanQueryString: function(method){ return this.split('&').filter(function(val){ var index = val.indexOf('='), key = index < 0 ? '' : val.substr(0, index), value = val.substr(index + 1); return method ? method.call(null, key, value) : (value || value === 0); }).join('&'); } }); /* --- script: URI.js name: URI description: Provides methods useful in managing the window location and uris. license: MIT-style license authors: - Sebastian MarkbÃ¥ge - Aaron Newton requires: - Core/Object - Core/Class - Core/Class.Extras - Core/Element - /String.QueryString provides: [URI] ... */ (function(){ var toString = function(){ return this.get('value'); }; var URI = this.URI = new Class({ Implements: Options, options: { /*base: false*/ }, regex: /^(?:(\w+):)?(?:\/\/(?:(?:([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)?(\.\.?$|(?:[^?#\/]*\/)*)([^?#]*)(?:\?([^#]*))?(?:#(.*))?/, parts: ['scheme', 'user', 'password', 'host', 'port', 'directory', 'file', 'query', 'fragment'], schemes: {http: 80, https: 443, ftp: 21, rtsp: 554, mms: 1755, file: 0}, initialize: function(uri, options){ this.setOptions(options); var base = this.options.base || URI.base; if (!uri) uri = base; if (uri && uri.parsed) this.parsed = Object.clone(uri.parsed); else this.set('value', uri.href || uri.toString(), base ? new URI(base) : false); }, parse: function(value, base){ var bits = value.match(this.regex); if (!bits) return false; bits.shift(); return this.merge(bits.associate(this.parts), base); }, merge: function(bits, base){ if ((!bits || !bits.scheme) && (!base || !base.scheme)) return false; if (base){ this.parts.every(function(part){ if (bits[part]) return false; bits[part] = base[part] || ''; return true; }); } bits.port = bits.port || this.schemes[bits.scheme.toLowerCase()]; bits.directory = bits.directory ? this.parseDirectory(bits.directory, base ? base.directory : '') : '/'; return bits; }, parseDirectory: function(directory, baseDirectory){ directory = (directory.substr(0, 1) == '/' ? '' : (baseDirectory || '/')) + directory; if (!directory.test(URI.regs.directoryDot)) return directory; var result = []; directory.replace(URI.regs.endSlash, '').split('/').each(function(dir){ if (dir == '..' && result.length > 0) result.pop(); else if (dir != '.') result.push(dir); }); return result.join('/') + '/'; }, combine: function(bits){ return bits.value || bits.scheme + '://' + (bits.user ? bits.user + (bits.password ? ':' + bits.password : '') + '@' : '') + (bits.host || '') + (bits.port && bits.port != this.schemes[bits.scheme] ? ':' + bits.port : '') + (bits.directory || '/') + (bits.file || '') + (bits.query ? '?' + bits.query : '') + (bits.fragment ? '#' + bits.fragment : ''); }, set: function(part, value, base){ if (part == 'value'){ var scheme = value.match(URI.regs.scheme); if (scheme) scheme = scheme[1]; if (scheme && this.schemes[scheme.toLowerCase()] == null) this.parsed = { scheme: scheme, value: value }; else this.parsed = this.parse(value, (base || this).parsed) || (scheme ? { scheme: scheme, value: value } : { value: value }); } else if (part == 'data'){ this.setData(value); } else { this.parsed[part] = value; } return this; }, get: function(part, base){ switch (part){ case 'value': return this.combine(this.parsed, base ? base.parsed : false); case 'data' : return this.getData(); } return this.parsed[part] || ''; }, go: function(){ document.location.href = this.toString(); }, toURI: function(){ return this; }, getData: function(key, part){ var qs = this.get(part || 'query'); if (!(qs || qs === 0)) return key ? null : {}; var obj = qs.parseQueryString(); return key ? obj[key] : obj; }, setData: function(values, merge, part){ if (typeof values == 'string'){ var data = this.getData(); data[arguments[0]] = arguments[1]; values = data; } else if (merge){ values = Object.merge(this.getData(), values); } return this.set(part || 'query', Object.toQueryString(values)); }, clearData: function(part){ return this.set(part || 'query', ''); }, toString: toString, valueOf: toString }); URI.regs = { endSlash: /\/$/, scheme: /^(\w+):/, directoryDot: /\.\/|\.$/ }; URI.base = new URI(Array.from(document.getElements('base[href]', true)).getLast(), {base: document.location}); String.implement({ toURI: function(options){ return new URI(this, options); } }); })(); /* --- script: Class.Refactor.js name: Class.Refactor description: Extends a class onto itself with new property, preserving any items attached to the class's namespace. license: MIT-style license authors: - Aaron Newton requires: - Core/Class - /MooTools.More # Some modules declare themselves dependent on Class.Refactor provides: [Class.refactor, Class.Refactor] ... */ Class.refactor = function(original, refactors){ Object.each(refactors, function(item, name){ var origin = original.prototype[name]; origin = (origin && origin.$origin) || origin || function(){}; original.implement(name, (typeof item == 'function') ? function(){ var old = this.previous; this.previous = origin; var value = item.apply(this, arguments); this.previous = old; return value; } : item); }); return original; }; /* --- script: URI.Relative.js name: URI.Relative description: Extends the URI class to add methods for computing relative and absolute urls. license: MIT-style license authors: - Sebastian MarkbÃ¥ge requires: - /Class.refactor - /URI provides: [URI.Relative] ... */ URI = Class.refactor(URI, { combine: function(bits, base){ if (!base || bits.scheme != base.scheme || bits.host != base.host || bits.port != base.port) return this.previous.apply(this, arguments); var end = bits.file + (bits.query ? '?' + bits.query : '') + (bits.fragment ? '#' + bits.fragment : ''); if (!base.directory) return (bits.directory || (bits.file ? '' : './')) + end; var baseDir = base.directory.split('/'), relDir = bits.directory.split('/'), path = '', offset; var i = 0; for (offset = 0; offset < baseDir.length && offset < relDir.length && baseDir[offset] == relDir[offset]; offset++); for (i = 0; i < baseDir.length - offset - 1; i++) path += '../'; for (i = offset; i < relDir.length - 1; i++) path += relDir[i] + '/'; return (path || (bits.file ? '' : './')) + end; }, toAbsolute: function(base){ base = new URI(base); if (base) base.set('directory', '').set('file', ''); return this.toRelative(base); }, toRelative: function(base){ return this.get('value', new URI(base)); } }); /* --- name: Hash description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects. license: MIT-style license. requires: - Core/Object - /MooTools.More provides: [Hash] ... */ (function(){ if (this.Hash) return; var Hash = this.Hash = new Type('Hash', function(object){ if (typeOf(object) == 'hash') object = Object.clone(object.getClean()); for (var key in object) this[key] = object[key]; return this; }); this.$H = function(object){ return new Hash(object); }; Hash.implement({ forEach: function(fn, bind){ Object.forEach(this, fn, bind); }, getClean: function(){ var clean = {}; for (var key in this){ if (this.hasOwnProperty(key)) clean[key] = this[key]; } return clean; }, getLength: function(){ var length = 0; for (var key in this){ if (this.hasOwnProperty(key)) length++; } return length; } }); Hash.alias('each', 'forEach'); Hash.implement({ has: Object.prototype.hasOwnProperty, keyOf: function(value){ return Object.keyOf(this, value); }, hasValue: function(value){ return Object.contains(this, value); }, extend: function(properties){ Hash.each(properties || {}, function(value, key){ Hash.set(this, key, value); }, this); return this; }, combine: function(properties){ Hash.each(properties || {}, function(value, key){ Hash.include(this, key, value); }, this); return this; }, erase: function(key){ if (this.hasOwnProperty(key)) delete this[key]; return this; }, get: function(key){ return (this.hasOwnProperty(key)) ? this[key] : null; }, set: function(key, value){ if (!this[key] || this.hasOwnProperty(key)) this[key] = value; return this; }, empty: function(){ Hash.each(this, function(value, key){ delete this[key]; }, this); return this; }, include: function(key, value){ if (this[key] == undefined) this[key] = value; return this; }, map: function(fn, bind){ return new Hash(Object.map(this, fn, bind)); }, filter: function(fn, bind){ return new Hash(Object.filter(this, fn, bind)); }, every: function(fn, bind){ return Object.every(this, fn, bind); }, some: function(fn, bind){ return Object.some(this, fn, bind); }, getKeys: function(){ return Object.keys(this); }, getValues: function(){ return Object.values(this); }, toQueryString: function(base){ return Object.toQueryString(this, base); } }); Hash.alias({indexOf: 'keyOf', contains: 'hasValue'}); })(); /* --- script: Hash.Extras.js name: Hash.Extras description: Extends the Hash Type to include getFromPath which allows a path notation to child elements. license: MIT-style license authors: - Aaron Newton requires: - /Hash - /Object.Extras provides: [Hash.Extras] ... */ Hash.implement({ getFromPath: function(notation){ return Object.getFromPath(this, notation); }, cleanValues: function(method){ return new Hash(Object.cleanValues(this, method)); }, run: function(){ Object.run(arguments); } }); /* --- script: Element.Forms.js name: Element.Forms description: Extends the Element native object to include methods useful in managing inputs. license: MIT-style license authors: - Aaron Newton requires: - Core/Element - /String.Extras - /MooTools.More provides: [Element.Forms] ... */ Element.implement({ tidy: function(){ this.set('value', this.get('value').tidy()); }, getTextInRange: function(start, end){ return this.get('value').substring(start, end); }, getSelectedText: function(){ if (this.setSelectionRange) return this.getTextInRange(this.getSelectionStart(), this.getSelectionEnd()); return document.selection.createRange().text; }, getSelectedRange: function(){ if (this.selectionStart != null){ return { start: this.selectionStart, end: this.selectionEnd }; } var pos = { start: 0, end: 0 }; var range = this.getDocument().selection.createRange(); if (!range || range.parentElement() != this) return pos; var duplicate = range.duplicate(); if (this.type == 'text'){ pos.start = 0 - duplicate.moveStart('character', -100000); pos.end = pos.start + range.text.length; } else { var value = this.get('value'); var offset = value.length; duplicate.moveToElementText(this); duplicate.setEndPoint('StartToEnd', range); if (duplicate.text.length) offset -= value.match(/[\n\r]*$/)[0].length; pos.end = offset - duplicate.text.length; duplicate.setEndPoint('StartToStart', range); pos.start = offset - duplicate.text.length; } return pos; }, getSelectionStart: function(){ return this.getSelectedRange().start; }, getSelectionEnd: function(){ return this.getSelectedRange().end; }, setCaretPosition: function(pos){ if (pos == 'end') pos = this.get('value').length; this.selectRange(pos, pos); return this; }, getCaretPosition: function(){ return this.getSelectedRange().start; }, selectRange: function(start, end){ if (this.setSelectionRange){ this.focus(); this.setSelectionRange(start, end); } else { var value = this.get('value'); var diff = value.substr(start, end - start).replace(/\r/g, '').length; start = value.substr(0, start).replace(/\r/g, '').length; var range = this.createTextRange(); range.collapse(true); range.moveEnd('character', start + diff); range.moveStart('character', start); range.select(); } return this; }, insertAtCursor: function(value, select){ var pos = this.getSelectedRange(); var text = this.get('value'); this.set('value', text.substring(0, pos.start) + value + text.substring(pos.end, text.length)); if (select !== false) this.selectRange(pos.start, pos.start + value.length); else this.setCaretPosition(pos.start + value.length); return this; }, insertAroundCursor: function(options, select){ options = Object.append({ before: '', defaultMiddle: '', after: '' }, options); var value = this.getSelectedText() || options.defaultMiddle; var pos = this.getSelectedRange(); var text = this.get('value'); if (pos.start == pos.end){ this.set('value', text.substring(0, pos.start) + options.before + value + options.after + text.substring(pos.end, text.length)); this.selectRange(pos.start + options.before.length, pos.end + options.before.length + value.length); } else { var current = text.substring(pos.start, pos.end); this.set('value', text.substring(0, pos.start) + options.before + current + options.after + text.substring(pos.end, text.length)); var selStart = pos.start + options.before.length; if (select !== false) this.selectRange(selStart, selStart + current.length); else this.setCaretPosition(selStart + text.length); } return this; } }); /* --- script: Elements.From.js name: Elements.From description: Returns a collection of elements from a string of html. license: MIT-style license authors: - Aaron Newton requires: - Core/String - Core/Element - /MooTools.More provides: [Elements.from, Elements.From] ... */ Elements.from = function(text, excludeScripts){ if (excludeScripts || excludeScripts == null) text = text.stripScripts(); var container, match = text.match(/^\s*<(t[dhr]|tbody|tfoot|thead)/i); if (match){ container = new Element('table'); var tag = match[1].toLowerCase(); if (['td', 'th', 'tr'].contains(tag)){ container = new Element('tbody').inject(container); if (tag != 'tr') container = new Element('tr').inject(container); } } return (container || new Element('div')).set('html', text).getChildren(); }; /* --- name: Events.Pseudos description: Adds the functionality to add pseudo events license: MIT-style license authors: - Arian Stolwijk requires: [Core/Class.Extras, Core/Slick.Parser, More/MooTools.More] provides: [Events.Pseudos] ... */ Events.Pseudos = function(pseudos, addEvent, removeEvent){ var storeKey = 'monitorEvents:'; var storageOf = function(object){ return { store: object.store ? function(key, value){ object.store(storeKey + key, value); } : function(key, value){ (object.$monitorEvents || (object.$monitorEvents = {}))[key] = value; }, retrieve: object.retrieve ? function(key, dflt){ return object.retrieve(storeKey + key, dflt); } : function(key, dflt){ if (!object.$monitorEvents) return dflt; return object.$monitorEvents[key] || dflt; } }; }; var splitType = function(type){ if (type.indexOf(':') == -1 || !pseudos) return null; var parsed = Slick.parse(type).expressions[0][0], parsedPseudos = parsed.pseudos, l = parsedPseudos.length, splits = []; while (l--) if (pseudos[parsedPseudos[l].key]){ splits.push({ event: parsed.tag, value: parsedPseudos[l].value, pseudo: parsedPseudos[l].key, original: type }); } return splits.length ? splits : null; }; var mergePseudoOptions = function(split){ return Object.merge.apply(this, split.map(function(item){ return pseudos[item.pseudo].options || {}; })); }; return { addEvent: function(type, fn, internal){ var split = splitType(type); if (!split) return addEvent.call(this, type, fn, internal); var storage = storageOf(this), events = storage.retrieve(type, []), eventType = split[0].event, options = mergePseudoOptions(split), stack = fn, eventOptions = options[eventType] || {}, args = Array.slice(arguments, 2), self = this, monitor; if (eventOptions.args) args.append(Array.from(eventOptions.args)); if (eventOptions.base) eventType = eventOptions.base; if (eventOptions.onAdd) eventOptions.onAdd(this); split.each(function(item){ var stackFn = stack; stack = function(){ (eventOptions.listener || pseudos[item.pseudo].listener).call(self, item, stackFn, arguments, monitor, options); }; }); monitor = stack.bind(this); events.include({event: fn, monitor: monitor}); storage.store(type, events); addEvent.apply(this, [type, fn].concat(args)); return addEvent.apply(this, [eventType, monitor].concat(args)); }, removeEvent: function(type, fn){ var split = splitType(type); if (!split) return removeEvent.call(this, type, fn); var storage = storageOf(this), events = storage.retrieve(type); if (!events) return this; var eventType = split[0].event, options = mergePseudoOptions(split), eventOptions = options[eventType] || {}, args = Array.slice(arguments, 2); if (eventOptions.args) args.append(Array.from(eventOptions.args)); if (eventOptions.base) eventType = eventOptions.base; if (eventOptions.onRemove) eventOptions.onRemove(this); removeEvent.apply(this, [type, fn].concat(args)); events.each(function(monitor, i){ if (!fn || monitor.event == fn) removeEvent.apply(this, [eventType, monitor.monitor].concat(args)); delete events[i]; }, this); storage.store(type, events); return this; } }; }; (function(){ var pseudos = { once: { listener: function(split, fn, args, monitor){ fn.apply(this, args); this.removeEvent(split.event, monitor) .removeEvent(split.original, fn); } }, throttle: { listener: function(split, fn, args){ if (!fn._throttled){ fn.apply(this, args); fn._throttled = setTimeout(function(){ fn._throttled = false; }, split.value || 250); } } }, pause: { listener: function(split, fn, args){ clearTimeout(fn._pause); fn._pause = fn.delay(split.value || 250, this, args); } } }; Events.definePseudo = function(key, listener){ pseudos[key] = Type.isFunction(listener) ? {listener: listener} : listener; return this; }; Events.lookupPseudo = function(key){ return pseudos[key]; }; var proto = Events.prototype; Events.implement(Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent)); ['Request', 'Fx'].each(function(klass){ if (this[klass]) this[klass].implement(Events.prototype); }); })(); /* --- name: Element.Event.Pseudos description: Adds the functionality to add pseudo events for Elements license: MIT-style license authors: - Arian Stolwijk requires: [Core/Element.Event, Events.Pseudos] provides: [Element.Event.Pseudos] ... */ (function(){ var pseudos = {}, copyFromEvents = ['once', 'throttle', 'pause'], count = copyFromEvents.length; while (count--) pseudos[copyFromEvents[count]] = Events.lookupPseudo(copyFromEvents[count]); Event.definePseudo = function(key, listener){ pseudos[key] = Type.isFunction(listener) ? {listener: listener} : listener; return this; }; var proto = Element.prototype; [Element, Window, Document].invoke('implement', Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent)); })(); /* --- name: Element.Event.Pseudos.Keys description: Adds functionality fire events if certain keycombinations are pressed license: MIT-style license authors: - Arian Stolwijk requires: [Element.Event.Pseudos] provides: [Element.Event.Pseudos.Keys] ... */ (function(){ var keysStoreKey = '$moo:keys-pressed', keysKeyupStoreKey = '$moo:keys-keyup'; Event.definePseudo('keys', function(split, fn, args){ var event = args[0], keys = [], pressed = this.retrieve(keysStoreKey, []); keys.append(split.value.replace('++', function(){ keys.push('+'); // shift++ and shift+++a return ''; }).split('+')); pressed.include(event.key); if (keys.every(function(key){ return pressed.contains(key); })) fn.apply(this, args); this.store(keysStoreKey, pressed); if (!this.retrieve(keysKeyupStoreKey)){ var keyup = function(event){ (function(){ pressed = this.retrieve(keysStoreKey, []).erase(event.key); this.store(keysStoreKey, pressed); }).delay(0, this); // Fix for IE }; this.store(keysKeyupStoreKey, keyup).addEvent('keyup', keyup); } }); Object.append(Event.Keys, { 'shift': 16, 'control': 17, 'alt': 18, 'capslock': 20, 'pageup': 33, 'pagedown': 34, 'end': 35, 'home': 36, 'numlock': 144, 'scrolllock': 145, ';': 186, '=': 187, ',': 188, '-': Browser.firefox ? 109 : 189, '.': 190, '/': 191, '`': 192, '[': 219, '\\': 220, ']': 221, "'": 222, '+': 107 }); })(); /* --- script: Element.Pin.js name: Element.Pin description: Extends the Element native object to include the pin method useful for fixed positioning for elements. license: MIT-style license authors: - Aaron Newton requires: - Core/Element.Event - Core/Element.Dimensions - Core/Element.Style - /MooTools.More provides: [Element.Pin] ... */ (function(){ var supportsPositionFixed = false, supportTested = false; var testPositionFixed = function(){ var test = new Element('div').setStyles({ position: 'fixed', top: 0, right: 0 }).inject(document.body); supportsPositionFixed = (test.offsetTop === 0); test.dispose(); supportTested = true; }; Element.implement({ pin: function(enable, forceScroll){ if (!supportTested) testPositionFixed(); if (this.getStyle('display') == 'none') return this; var pinnedPosition, scroll = window.getScroll(), parent, scrollFixer; if (enable !== false){ pinnedPosition = this.getPosition(supportsPositionFixed ? document.body : this.getOffsetParent()); if (!this.retrieve('pin:_pinned')){ var currentPosition = { top: pinnedPosition.y - scroll.y, left: pinnedPosition.x - scroll.x }; if (supportsPositionFixed && !forceScroll){ this.setStyle('position', 'fixed').setStyles(currentPosition); } else { parent = this.getOffsetParent(); var position = this.getPosition(parent), styles = this.getStyles('left', 'top'); if (parent && styles.left == 'auto' || styles.top == 'auto') this.setPosition(position); if (this.getStyle('position') == 'static') this.setStyle('position', 'absolute'); position = { x: styles.left.toInt() - scroll.x, y: styles.top.toInt() - scroll.y }; scrollFixer = function(){ if (!this.retrieve('pin:_pinned')) return; var scroll = window.getScroll(); this.setStyles({ left: position.x + scroll.x, top: position.y + scroll.y }); }.bind(this); this.store('pin:_scrollFixer', scrollFixer); window.addEvent('scroll', scrollFixer); } this.store('pin:_pinned', true); } } else { if (!this.retrieve('pin:_pinned')) return this; parent = this.getParent(); var offsetParent = (parent.getComputedStyle('position') != 'static' ? parent : parent.getOffsetParent()); pinnedPosition = this.getPosition(offsetParent); this.store('pin:_pinned', false); scrollFixer = this.retrieve('pin:_scrollFixer'); if (!scrollFixer){ this.setStyles({ position: 'absolute', top: pinnedPosition.y + scroll.y, left: pinnedPosition.x + scroll.x }); } else { this.store('pin:_scrollFixer', null); window.removeEvent('scroll', scrollFixer); } this.removeClass('isPinned'); } return this; }, unpin: function(){ return this.pin(false); }, togglePin: function(){ return this.pin(!this.retrieve('pin:_pinned')); } }); })(); /* --- script: Element.Measure.js name: Element.Measure description: Extends the Element native object to include methods useful in measuring dimensions. credits: "Element.measure / .expose methods by Daniel Steigerwald License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz" license: MIT-style license authors: - Aaron Newton requires: - Core/Element.Style - Core/Element.Dimensions - /MooTools.More provides: [Element.Measure] ... */ (function(){ var getStylesList = function(styles, planes){ var list = []; Object.each(planes, function(directions){ Object.each(directions, function(edge){ styles.each(function(style){ list.push(style + '-' + edge + (style == 'border' ? '-width' : '')); }); }); }); return list; }; var calculateEdgeSize = function(edge, styles){ var total = 0; Object.each(styles, function(value, style){ if (style.test(edge)) total = total + value.toInt(); }); return total; }; var isVisible = function(el){ return !!(!el || el.offsetHeight || el.offsetWidth); }; Element.implement({ measure: function(fn){ if (isVisible(this)) return fn.call(this); var parent = this.getParent(), toMeasure = []; while (!isVisible(parent) && parent != document.body){ toMeasure.push(parent.expose()); parent = parent.getParent(); } var restore = this.expose(), result = fn.call(this); restore(); toMeasure.each(function(restore){ restore(); }); return result; }, expose: function(){ if (this.getStyle('display') != 'none') return function(){}; var before = this.style.cssText; this.setStyles({ display: 'block', position: 'absolute', visibility: 'hidden' }); return function(){ this.style.cssText = before; }.bind(this); }, getDimensions: function(options){ options = Object.merge({computeSize: false}, options); var dim = {x: 0, y: 0}; var getSize = function(el, options){ return (options.computeSize) ? el.getComputedSize(options) : el.getSize(); }; var parent = this.getParent('body'); if (parent && this.getStyle('display') == 'none'){ dim = this.measure(function(){ return getSize(this, options); }); } else if (parent){ try { //safari sometimes crashes here, so catch it dim = getSize(this, options); }catch(e){} } return Object.append(dim, (dim.x || dim.x === 0) ? { width: dim.x, height: dim.y } : { x: dim.width, y: dim.height } ); }, getComputedSize: function(options){ options = Object.merge({ styles: ['padding','border'], planes: { height: ['top','bottom'], width: ['left','right'] }, mode: 'both' }, options); var styles = {}, size = {width: 0, height: 0}, dimensions; if (options.mode == 'vertical'){ delete size.width; delete options.planes.width; } else if (options.mode == 'horizontal'){ delete size.height; delete options.planes.height; } getStylesList(options.styles, options.planes).each(function(style){ styles[style] = this.getStyle(style).toInt(); }, this); Object.each(options.planes, function(edges, plane){ var capitalized = plane.capitalize(), style = this.getStyle(plane); if (style == 'auto' && !dimensions) dimensions = this.getDimensions(); style = styles[plane] = (style == 'auto') ? dimensions[plane] : style.toInt(); size['total' + capitalized] = style; edges.each(function(edge){ var edgesize = calculateEdgeSize(edge, styles); size['computed' + edge.capitalize()] = edgesize; size['total' + capitalized] += edgesize; }); }, this); return Object.append(size, styles); } }); })(); /* --- script: Element.Position.js name: Element.Position description: Extends the Element native object to include methods useful positioning elements relative to others. license: MIT-style license authors: - Aaron Newton - Jacob Thornton requires: - Core/Options - Core/Element.Dimensions - Element.Measure provides: [Element.Position] ... */ (function(original){ var local = Element.Position = { options: {/* edge: false, returnPos: false, minimum: {x: 0, y: 0}, maximum: {x: 0, y: 0}, relFixedPosition: false, ignoreMargins: false, ignoreScroll: false, allowNegative: false,*/ relativeTo: document.body, position: { x: 'center', //left, center, right y: 'center' //top, center, bottom }, offset: {x: 0, y: 0} }, getOptions: function(element, options){ options = Object.merge({}, local.options, options); local.setPositionOption(options); local.setEdgeOption(options); local.setOffsetOption(element, options); local.setDimensionsOption(element, options); return options; }, setPositionOption: function(options){ options.position = local.getCoordinateFromValue(options.position); }, setEdgeOption: function(options){ var edgeOption = local.getCoordinateFromValue(options.edge); options.edge = edgeOption ? edgeOption : (options.position.x == 'center' && options.position.y == 'center') ? {x: 'center', y: 'center'} : {x: 'left', y: 'top'}; }, setOffsetOption: function(element, options){ var parentOffset = {x: 0, y: 0}, offsetParent = element.measure(function(){ return document.id(this.getOffsetParent()); }), parentScroll = offsetParent.getScroll(); if (!offsetParent || offsetParent == element.getDocument().body) return; parentOffset = offsetParent.measure(function(){ var position = this.getPosition(); if (this.getStyle('position') == 'fixed'){ var scroll = window.getScroll(); position.x += scroll.x; position.y += scroll.y; } return position; }); options.offset = { parentPositioned: offsetParent != document.id(options.relativeTo), x: options.offset.x - parentOffset.x + parentScroll.x, y: options.offset.y - parentOffset.y + parentScroll.y }; }, setDimensionsOption: function(element, options){ options.dimensions = element.getDimensions({ computeSize: true, styles: ['padding', 'border', 'margin'] }); }, getPosition: function(element, options){ var position = {}; options = local.getOptions(element, options); var relativeTo = document.id(options.relativeTo) || document.body; local.setPositionCoordinates(options, position, relativeTo); if (options.edge) local.toEdge(position, options); var offset = options.offset; position.left = ((position.x >= 0 || offset.parentPositioned || options.allowNegative) ? position.x : 0).toInt(); position.top = ((position.y >= 0 || offset.parentPositioned || options.allowNegative) ? position.y : 0).toInt(); local.toMinMax(position, options); if (options.relFixedPosition || relativeTo.getStyle('position') == 'fixed') local.toRelFixedPosition(relativeTo, position); if (options.ignoreScroll) local.toIgnoreScroll(relativeTo, position); if (options.ignoreMargins) local.toIgnoreMargins(position, options); position.left = Math.ceil(position.left); position.top = Math.ceil(position.top); delete position.x; delete position.y; return position; }, setPositionCoordinates: function(options, position, relativeTo){ var offsetY = options.offset.y, offsetX = options.offset.x, calc = (relativeTo == document.body) ? window.getScroll() : relativeTo.getPosition(), top = calc.y, left = calc.x, winSize = window.getSize(); switch(options.position.x){ case 'left': position.x = left + offsetX; break; case 'right': position.x = left + offsetX + relativeTo.offsetWidth; break; default: position.x = left + ((relativeTo == document.body ? winSize.x : relativeTo.offsetWidth) / 2) + offsetX; break; } switch(options.position.y){ case 'top': position.y = top + offsetY; break; case 'bottom': position.y = top + offsetY + relativeTo.offsetHeight; break; default: position.y = top + ((relativeTo == document.body ? winSize.y : relativeTo.offsetHeight) / 2) + offsetY; break; } }, toMinMax: function(position, options){ var xy = {left: 'x', top: 'y'}, value; ['minimum', 'maximum'].each(function(minmax){ ['left', 'top'].each(function(lr){ value = options[minmax] ? options[minmax][xy[lr]] : null; if (value != null && ((minmax == 'minimum') ? position[lr] < value : position[lr] > value)) position[lr] = value; }); }); }, toRelFixedPosition: function(relativeTo, position){ var winScroll = window.getScroll(); position.top += winScroll.y; position.left += winScroll.x; }, toIgnoreScroll: function(relativeTo, position){ var relScroll = relativeTo.getScroll(); position.top -= relScroll.y; position.left -= relScroll.x; }, toIgnoreMargins: function(position, options){ position.left += options.edge.x == 'right' ? options.dimensions['margin-right'] : (options.edge.x != 'center' ? -options.dimensions['margin-left'] : -options.dimensions['margin-left'] + ((options.dimensions['margin-right'] + options.dimensions['margin-left']) / 2)); position.top += options.edge.y == 'bottom' ? options.dimensions['margin-bottom'] : (options.edge.y != 'center' ? -options.dimensions['margin-top'] : -options.dimensions['margin-top'] + ((options.dimensions['margin-bottom'] + options.dimensions['margin-top']) / 2)); }, toEdge: function(position, options){ var edgeOffset = {}, dimensions = options.dimensions, edge = options.edge; switch(edge.x){ case 'left': edgeOffset.x = 0; break; case 'right': edgeOffset.x = -dimensions.x - dimensions.computedRight - dimensions.computedLeft; break; // center default: edgeOffset.x = -(Math.round(dimensions.totalWidth / 2)); break; } switch(edge.y){ case 'top': edgeOffset.y = 0; break; case 'bottom': edgeOffset.y = -dimensions.y - dimensions.computedTop - dimensions.computedBottom; break; // center default: edgeOffset.y = -(Math.round(dimensions.totalHeight / 2)); break; } position.x += edgeOffset.x; position.y += edgeOffset.y; }, getCoordinateFromValue: function(option){ if (typeOf(option) != 'string') return option; option = option.toLowerCase(); return { x: option.test('left') ? 'left' : (option.test('right') ? 'right' : 'center'), y: option.test(/upper|top/) ? 'top' : (option.test('bottom') ? 'bottom' : 'center') }; } }; Element.implement({ position: function(options){ if (options && (options.x != null || options.y != null)) { return (original ? original.apply(this, arguments) : this); } var position = this.setStyle('position', 'absolute').calculatePosition(options); return (options && options.returnPos) ? position : this.setStyles(position); }, calculatePosition: function(options){ return local.getPosition(this, options); } }); })(Element.prototype.position); /* --- script: Element.Shortcuts.js name: Element.Shortcuts description: Extends the Element native object to include some shortcut methods. license: MIT-style license authors: - Aaron Newton requires: - Core/Element.Style - /MooTools.More provides: [Element.Shortcuts] ... */ Element.implement({ isDisplayed: function(){ return this.getStyle('display') != 'none'; }, isVisible: function(){ var w = this.offsetWidth, h = this.offsetHeight; return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.style.display != 'none'; }, toggle: function(){ return this[this.isDisplayed() ? 'hide' : 'show'](); }, hide: function(){ var d; try { //IE fails here if the element is not in the dom d = this.getStyle('display'); } catch(e){} if (d == 'none') return this; return this.store('element:_originalDisplay', d || '').setStyle('display', 'none'); }, show: function(display){ if (!display && this.isDisplayed()) return this; display = display || this.retrieve('element:_originalDisplay') || 'block'; return this.setStyle('display', (display == 'none') ? 'block' : display); }, swapClass: function(remove, add){ return this.removeClass(remove).addClass(add); } }); Document.implement({ clearSelection: function(){ if (window.getSelection){ var selection = window.getSelection(); if (selection && selection.removeAllRanges) selection.removeAllRanges(); } else if (document.selection && document.selection.empty){ try { //IE fails here if selected element is not in dom document.selection.empty(); } catch(e){} } } }); /* --- script: Class.Binds.js name: Class.Binds description: Automagically binds specified methods in a class to the instance of the class. license: MIT-style license authors: - Aaron Newton requires: - Core/Class - /MooTools.More provides: [Class.Binds] ... */ Class.Mutators.Binds = function(binds){ if (!this.prototype.initialize) this.implement('initialize', function(){}); return Array.from(binds).concat(this.prototype.Binds || []); }; Class.Mutators.initialize = function(initialize){ return function(){ Array.from(this.Binds).each(function(name){ var original = this[name]; if (original) this[name] = original.bind(this); }, this); return initialize.apply(this, arguments); }; }; /* --- script: Class.Occlude.js name: Class.Occlude description: Prevents a class from being applied to a DOM element twice. license: MIT-style license. authors: - Aaron Newton requires: - Core/Class - Core/Element - /MooTools.More provides: [Class.Occlude] ... */ Class.Occlude = new Class({ occlude: function(property, element){ element = document.id(element || this.element); var instance = element.retrieve(property || this.property); if (instance && !this.occluded) return (this.occluded = instance); this.occluded = false; element.store(property || this.property, this); return this.occluded; } }); /* --- script: IframeShim.js name: IframeShim description: Defines IframeShim, a class for obscuring select lists and flash objects in IE. license: MIT-style license authors: - Aaron Newton requires: - Core/Element.Event - Core/Element.Style - Core/Options - Core/Events - /Element.Position - /Class.Occlude provides: [IframeShim] ... */ var IframeShim = new Class({ Implements: [Options, Events, Class.Occlude], options: { className: 'iframeShim', src: 'javascript:false;document.write("");', display: false, zIndex: null, margin: 0, offset: {x: 0, y: 0}, browsers: (Browser.ie6 || (Browser.firefox && Browser.version < 3 && Browser.Platform.mac)) }, property: 'IframeShim', initialize: function(element, options){ this.element = document.id(element); if (this.occlude()) return this.occluded; this.setOptions(options); this.makeShim(); return this; }, makeShim: function(){ if (this.options.browsers){ var zIndex = this.element.getStyle('zIndex').toInt(); if (!zIndex){ zIndex = 1; var pos = this.element.getStyle('position'); if (pos == 'static' || !pos) this.element.setStyle('position', 'relative'); this.element.setStyle('zIndex', zIndex); } zIndex = ((this.options.zIndex != null || this.options.zIndex === 0) && zIndex > this.options.zIndex) ? this.options.zIndex : zIndex - 1; if (zIndex < 0) zIndex = 1; this.shim = new Element('iframe', { src: this.options.src, scrolling: 'no', frameborder: 0, styles: { zIndex: zIndex, position: 'absolute', border: 'none', filter: 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)' }, 'class': this.options.className }).store('IframeShim', this); var inject = (function(){ this.shim.inject(this.element, 'after'); this[this.options.display ? 'show' : 'hide'](); this.fireEvent('inject'); }).bind(this); if (!IframeShim.ready) window.addEvent('load', inject); else inject(); } else { this.position = this.hide = this.show = this.dispose = Function.from(this); } }, position: function(){ if (!IframeShim.ready || !this.shim) return this; var size = this.element.measure(function(){ return this.getSize(); }); if (this.options.margin != undefined){ size.x = size.x - (this.options.margin * 2); size.y = size.y - (this.options.margin * 2); this.options.offset.x += this.options.margin; this.options.offset.y += this.options.margin; } this.shim.set({width: size.x, height: size.y}).position({ relativeTo: this.element, offset: this.options.offset }); return this; }, hide: function(){ if (this.shim) this.shim.setStyle('display', 'none'); return this; }, show: function(){ if (this.shim) this.shim.setStyle('display', 'block'); return this.position(); }, dispose: function(){ if (this.shim) this.shim.dispose(); return this; }, destroy: function(){ if (this.shim) this.shim.destroy(); return this; } }); window.addEvent('load', function(){ IframeShim.ready = true; }); /* --- script: Mask.js name: Mask description: Creates a mask element to cover another. license: MIT-style license authors: - Aaron Newton requires: - Core/Options - Core/Events - Core/Element.Event - /Class.Binds - /Element.Position - /IframeShim provides: [Mask] ... */ var Mask = new Class({ Implements: [Options, Events], Binds: ['position'], options: {/* onShow: function(){}, onHide: function(){}, onDestroy: function(){}, onClick: function(event){}, inject: { where: 'after', target: null, }, hideOnClick: false, id: null, destroyOnHide: false,*/ style: {}, 'class': 'mask', maskMargins: false, useIframeShim: true, iframeShimOptions: {} }, initialize: function(target, options){ this.target = document.id(target) || document.id(document.body); this.target.store('mask', this); this.setOptions(options); this.render(); this.inject(); }, render: function(){ this.element = new Element('div', { 'class': this.options['class'], id: this.options.id || 'mask-' + String.uniqueID(), styles: Object.merge({}, this.options.style, { display: 'none' }), events: { click: function(event){ this.fireEvent('click', event); if (this.options.hideOnClick) this.hide(); }.bind(this) } }); this.hidden = true; }, toElement: function(){ return this.element; }, inject: function(target, where){ where = where || (this.options.inject ? this.options.inject.where : '') || this.target == document.body ? 'inside' : 'after'; target = target || (this.options.inject && this.options.inject.target) || this.target; this.element.inject(target, where); if (this.options.useIframeShim){ this.shim = new IframeShim(this.element, this.options.iframeShimOptions); this.addEvents({ show: this.shim.show.bind(this.shim), hide: this.shim.hide.bind(this.shim), destroy: this.shim.destroy.bind(this.shim) }); } }, position: function(){ this.resize(this.options.width, this.options.height); this.element.position({ relativeTo: this.target, position: 'topLeft', ignoreMargins: !this.options.maskMargins, ignoreScroll: this.target == document.body }); return this; }, resize: function(x, y){ var opt = { styles: ['padding', 'border'] }; if (this.options.maskMargins) opt.styles.push('margin'); var dim = this.target.getComputedSize(opt); if (this.target == document.body){ this.element.setStyles({width: 0, height: 0}); var win = window.getScrollSize(); if (dim.totalHeight < win.y) dim.totalHeight = win.y; if (dim.totalWidth < win.x) dim.totalWidth = win.x; } this.element.setStyles({ width: Array.pick([x, dim.totalWidth, dim.x]), height: Array.pick([y, dim.totalHeight, dim.y]) }); return this; }, show: function(){ if (!this.hidden) return this; window.addEvent('resize', this.position); this.position(); this.showMask.apply(this, arguments); return this; }, showMask: function(){ this.element.setStyle('display', 'block'); this.hidden = false; this.fireEvent('show'); }, hide: function(){ if (this.hidden) return this; window.removeEvent('resize', this.position); this.hideMask.apply(this, arguments); if (this.options.destroyOnHide) return this.destroy(); return this; }, hideMask: function(){ this.element.setStyle('display', 'none'); this.hidden = true; this.fireEvent('hide'); }, toggle: function(){ this[this.hidden ? 'show' : 'hide'](); }, destroy: function(){ this.hide(); this.element.destroy(); this.fireEvent('destroy'); this.target.eliminate('mask'); } }); Element.Properties.mask = { set: function(options){ var mask = this.retrieve('mask'); if (mask) mask.destroy(); return this.eliminate('mask').store('mask:options', options); }, get: function(){ var mask = this.retrieve('mask'); if (!mask){ mask = new Mask(this, this.retrieve('mask:options')); this.store('mask', mask); } return mask; } }; Element.implement({ mask: function(options){ if (options) this.set('mask', options); this.get('mask').show(); return this; }, unmask: function(){ this.get('mask').hide(); return this; } }); /* --- script: Spinner.js name: Spinner description: Adds a semi-transparent overlay over a dom element with a spinnin ajax icon. license: MIT-style license authors: - Aaron Newton requires: - Core/Fx.Tween - Core/Request - /Class.refactor - /Mask provides: [Spinner] ... */ var Spinner = new Class({ Extends: Mask, Implements: Chain, options: {/* message: false,*/ 'class': 'spinner', containerPosition: {}, content: { 'class': 'spinner-content' }, messageContainer: { 'class': 'spinner-msg' }, img: { 'class': 'spinner-img' }, fxOptions: { link: 'chain' } }, initialize: function(target, options){ this.target = document.id(target) || document.id(document.body); this.target.store('spinner', this); this.setOptions(options); this.render(); this.inject(); // Add this to events for when noFx is true; parent methods handle hide/show. var deactivate = function(){ this.active = false; }.bind(this); this.addEvents({ hide: deactivate, show: deactivate }); }, render: function(){ this.parent(); this.element.set('id', this.options.id || 'spinner-' + String.uniqueID()); this.content = document.id(this.options.content) || new Element('div', this.options.content); this.content.inject(this.element); if (this.options.message){ this.msg = document.id(this.options.message) || new Element('p', this.options.messageContainer).appendText(this.options.message); this.msg.inject(this.content); } if (this.options.img){ this.img = document.id(this.options.img) || new Element('div', this.options.img); this.img.inject(this.content); } this.element.set('tween', this.options.fxOptions); }, show: function(noFx){ if (this.active) return this.chain(this.show.bind(this)); if (!this.hidden){ this.callChain.delay(20, this); return this; } this.active = true; return this.parent(noFx); }, showMask: function(noFx){ var pos = function(){ this.content.position(Object.merge({ relativeTo: this.element }, this.options.containerPosition)); }.bind(this); if (noFx){ this.parent(); pos(); } else { if (!this.options.style.opacity) this.options.style.opacity = this.element.getStyle('opacity').toFloat(); this.element.setStyles({ display: 'block', opacity: 0 }).tween('opacity', this.options.style.opacity); pos(); this.hidden = false; this.fireEvent('show'); this.callChain(); } }, hide: function(noFx){ if (this.active) return this.chain(this.hide.bind(this)); if (this.hidden){ this.callChain.delay(20, this); return this; } this.active = true; return this.parent(noFx); }, hideMask: function(noFx){ if (noFx) return this.parent(); this.element.tween('opacity', 0).get('tween').chain(function(){ this.element.setStyle('display', 'none'); this.hidden = true; this.fireEvent('hide'); this.callChain(); }.bind(this)); }, destroy: function(){ this.content.destroy(); this.parent(); this.target.eliminate('spinner'); } }); Request = Class.refactor(Request, { options: { useSpinner: false, spinnerOptions: {}, spinnerTarget: false }, initialize: function(options){ this._send = this.send; this.send = function(options){ var spinner = this.getSpinner(); if (spinner) spinner.chain(this._send.pass(options, this)).show(); else this._send(options); return this; }; this.previous(options); }, getSpinner: function(){ if (!this.spinner){ var update = document.id(this.options.spinnerTarget) || document.id(this.options.update); if (this.options.useSpinner && update){ update.set('spinner', this.options.spinnerOptions); var spinner = this.spinner = update.get('spinner'); ['complete', 'exception', 'cancel'].each(function(event){ this.addEvent(event, spinner.hide.bind(spinner)); }, this); } } return this.spinner; } }); Element.Properties.spinner = { set: function(options){ var spinner = this.retrieve('spinner'); if (spinner) spinner.destroy(); return this.eliminate('spinner').store('spinner:options', options); }, get: function(){ var spinner = this.retrieve('spinner'); if (!spinner){ spinner = new Spinner(this, this.retrieve('spinner:options')); this.store('spinner', spinner); } return spinner; } }; Element.implement({ spin: function(options){ if (options) this.set('spinner', options); this.get('spinner').show(); return this; }, unspin: function(){ this.get('spinner').hide(); return this; } }); /* --- script: Element.Delegation.js name: Element.Delegation description: Extends the Element native object to include the delegate method for more efficient event management. credits: - "Event checking based on the work of Daniel Steigerwald. License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz" license: MIT-style license authors: - Aaron Newton - Daniel Steigerwald requires: [/MooTools.More, Element.Event.Pseudos] provides: [Element.Delegation] ... */ (function(){ var eventListenerSupport = !(window.attachEvent && !window.addEventListener), nativeEvents = Element.NativeEvents; nativeEvents.focusin = 2; nativeEvents.focusout = 2; var check = function(split, target, event){ var elementEvent = Element.Events[split.event], condition; if (elementEvent) condition = elementEvent.condition; return Slick.match(target, split.value) && (!condition || condition.call(target, event)); }; var bubbleUp = function(split, event, fn){ for (var target = event.target; target && target != this; target = document.id(target.parentNode)){ if (target && check(split, target, event)) return fn.call(target, event, target); } }; var formObserver = function(eventName){ var $delegationKey = '$delegation:'; return { base: 'focusin', onRemove: function(element){ element.retrieve($delegationKey + 'forms', []).each(function(el){ el.retrieve($delegationKey + 'listeners', []).each(function(listener){ el.removeEvent(eventName, listener); }); el.eliminate($delegationKey + eventName + 'listeners') .eliminate($delegationKey + eventName + 'originalFn'); }); }, listener: function(split, fn, args, monitor, options){ var event = args[0], forms = this.retrieve($delegationKey + 'forms', []), target = event.target, form = (target.get('tag') == 'form') ? target : event.target.getParent('form'); if (!form) return; var formEvents = form.retrieve($delegationKey + 'originalFn', []), formListeners = form.retrieve($delegationKey + 'listeners', []), self = this; forms.include(form); this.store($delegationKey + 'forms', forms); if (!formEvents.contains(fn)){ var formListener = function(event){ bubbleUp.call(self, split, event, fn); }; form.addEvent(eventName, formListener); formEvents.push(fn); formListeners.push(formListener); form.store($delegationKey + eventName + 'originalFn', formEvents) .store($delegationKey + eventName + 'listeners', formListeners); } } }; }; var inputObserver = function(eventName){ return { base: 'focusin', listener: function(split, fn, args){ var events = {blur: function(){ this.removeEvents(events); }}, self = this; events[eventName] = function(event){ bubbleUp.call(self, split, event, fn); }; args[0].target.addEvents(events); } }; }; var eventOptions = { mouseenter: { base: 'mouseover' }, mouseleave: { base: 'mouseout' }, focus: { base: 'focus' + (eventListenerSupport ? '' : 'in'), args: [true] }, blur: { base: eventListenerSupport ? 'blur' : 'focusout', args: [true] } }; if (!eventListenerSupport) Object.append(eventOptions, { submit: formObserver('submit'), reset: formObserver('reset'), change: inputObserver('change'), select: inputObserver('select') }); Event.definePseudo('relay', { listener: function(split, fn, args){ bubbleUp.call(this, split, args[0], fn); }, options: eventOptions }); })(); /* --- script: Form.Request.js name: Form.Request description: Handles the basic functionality of submitting a form and updating a dom element with the result. license: MIT-style license authors: - Aaron Newton requires: - Core/Request.HTML - /Class.Binds - /Class.Occlude - /Spinner - /String.QueryString - /Element.Delegation provides: [Form.Request] ... */ if (!window.Form) window.Form = {}; (function(){ Form.Request = new Class({ Binds: ['onSubmit', 'onFormValidate'], Implements: [Options, Events, Class.Occlude], options: {/* onFailure: function(){}, onSuccess: function(){}, // aliased to onComplete, onSend: function(){}*/ requestOptions: { evalScripts: true, useSpinner: true, emulation: false, link: 'ignore' }, sendButtonClicked: true, extraData: {}, resetForm: true }, property: 'form.request', initialize: function(form, target, options){ this.element = document.id(form); if (this.occlude()) return this.occluded; this.setOptions(options) .setTarget(target) .attach(); }, setTarget: function(target){ this.target = document.id(target); if (!this.request){ this.makeRequest(); } else { this.request.setOptions({ update: this.target }); } return this; }, toElement: function(){ return this.element; }, makeRequest: function(){ var self = this; this.request = new Request.HTML(Object.merge({ update: this.target, emulation: false, spinnerTarget: this.element, method: this.element.get('method') || 'post' }, this.options.requestOptions)).addEvents({ success: function(tree, elements, html, javascript){ ['complete', 'success'].each(function(evt){ self.fireEvent(evt, [self.target, tree, elements, html, javascript]); }); }, failure: function(){ self.fireEvent('complete', arguments).fireEvent('failure', arguments); }, exception: function(){ self.fireEvent('failure', arguments); } }); return this.attachReset(); }, attachReset: function(){ if (!this.options.resetForm) return this; this.request.addEvent('success', function(){ Function.attempt(function(){ this.element.reset(); }.bind(this)); if (window.OverText) OverText.update(); }.bind(this)); return this; }, attach: function(attach){ var method = (attach != false) ? 'addEvent' : 'removeEvent'; this.element[method]('click:relay(button, input[type=submit])', this.saveClickedButton.bind(this)); var fv = this.element.retrieve('validator'); if (fv) fv[method]('onFormValidate', this.onFormValidate); else this.element[method]('submit', this.onSubmit); return this; }, detach: function(){ return this.attach(false); }, //public method enable: function(){ return this.attach(); }, //public method disable: function(){ return this.detach(); }, onFormValidate: function(valid, form, event){ //if there's no event, then this wasn't a submit event if (!event) return; var fv = this.element.retrieve('validator'); if (valid || (fv && !fv.options.stopOnFailure)){ event.stop(); this.send(); } }, onSubmit: function(event){ var fv = this.element.retrieve('validator'); if (fv){ //form validator was created after Form.Request this.element.removeEvent('submit', this.onSubmit); fv.addEvent('onFormValidate', this.onFormValidate); this.element.validate(); return; } if (event) event.stop(); this.send(); }, saveClickedButton: function(event, target){ var targetName = target.get('name'); if (!targetName || !this.options.sendButtonClicked) return; this.options.extraData[targetName] = target.get('value') || true; this.clickedCleaner = function(){ delete this.options.extraData[targetName]; this.clickedCleaner = function(){}; }.bind(this); }, clickedCleaner: function(){}, send: function(){ var str = this.element.toQueryString().trim(), data = Object.toQueryString(this.options.extraData); if (str) str += "&" + data; else str = data; this.fireEvent('send', [this.element, str.parseQueryString()]); this.request.send({ data: str, url: this.options.requestOptions.url || this.element.get('action') }); this.clickedCleaner(); return this; } }); Element.implement('formUpdate', function(update, options){ var fq = this.retrieve('form.request'); if (!fq) { fq = new Form.Request(this, update, options); } else { if (update) fq.setTarget(update); if (options) fq.setOptions(options).makeRequest(); } fq.send(); return this; }); })(); /* --- script: Fx.Reveal.js name: Fx.Reveal description: Defines Fx.Reveal, a class that shows and hides elements with a transition. license: MIT-style license authors: - Aaron Newton requires: - Core/Fx.Morph - /Element.Shortcuts - /Element.Measure provides: [Fx.Reveal] ... */ (function(){ var hideTheseOf = function(object){ var hideThese = object.options.hideInputs; if (window.OverText){ var otClasses = [null]; OverText.each(function(ot){ otClasses.include('.' + ot.options.labelClass); }); if (otClasses) hideThese += otClasses.join(', '); } return (hideThese) ? object.element.getElements(hideThese) : null; }; Fx.Reveal = new Class({ Extends: Fx.Morph, options: {/* onShow: function(thisElement){}, onHide: function(thisElement){}, onComplete: function(thisElement){}, heightOverride: null, widthOverride: null,*/ link: 'cancel', styles: ['padding', 'border', 'margin'], transitionOpacity: !Browser.ie6, mode: 'vertical', display: function(){ return this.element.get('tag') != 'tr' ? 'block' : 'table-row'; }, opacity: 1, hideInputs: Browser.ie ? 'select, input, textarea, object, embed' : null }, dissolve: function(){ if (!this.hiding && !this.showing){ if (this.element.getStyle('display') != 'none'){ this.hiding = true; this.showing = false; this.hidden = true; this.cssText = this.element.style.cssText; var startStyles = this.element.getComputedSize({ styles: this.options.styles, mode: this.options.mode }); if (this.options.transitionOpacity) startStyles.opacity = this.options.opacity; var zero = {}; Object.each(startStyles, function(style, name){ zero[name] = [style, 0]; }); this.element.setStyles({ display: Function.from(this.options.display).call(this), overflow: 'hidden' }); var hideThese = hideTheseOf(this); if (hideThese) hideThese.setStyle('visibility', 'hidden'); this.$chain.unshift(function(){ if (this.hidden){ this.hiding = false; this.element.style.cssText = this.cssText; this.element.setStyle('display', 'none'); if (hideThese) hideThese.setStyle('visibility', 'visible'); } this.fireEvent('hide', this.element); this.callChain(); }.bind(this)); this.start(zero); } else { this.callChain.delay(10, this); this.fireEvent('complete', this.element); this.fireEvent('hide', this.element); } } else if (this.options.link == 'chain'){ this.chain(this.dissolve.bind(this)); } else if (this.options.link == 'cancel' && !this.hiding){ this.cancel(); this.dissolve(); } return this; }, reveal: function(){ if (!this.showing && !this.hiding){ if (this.element.getStyle('display') == 'none'){ this.hiding = false; this.showing = true; this.hidden = false; this.cssText = this.element.style.cssText; var startStyles; this.element.measure(function(){ startStyles = this.element.getComputedSize({ styles: this.options.styles, mode: this.options.mode }); }.bind(this)); if (this.options.heightOverride != null) startStyles.height = this.options.heightOverride.toInt(); if (this.options.widthOverride != null) startStyles.width = this.options.widthOverride.toInt(); if (this.options.transitionOpacity){ this.element.setStyle('opacity', 0); startStyles.opacity = this.options.opacity; } var zero = { height: 0, display: Function.from(this.options.display).call(this) }; Object.each(startStyles, function(style, name){ zero[name] = 0; }); zero.overflow = 'hidden'; this.element.setStyles(zero); var hideThese = hideTheseOf(this); if (hideThese) hideThese.setStyle('visibility', 'hidden'); this.$chain.unshift(function(){ this.element.style.cssText = this.cssText; this.element.setStyle('display', Function.from(this.options.display).call(this)); if (!this.hidden) this.showing = false; if (hideThese) hideThese.setStyle('visibility', 'visible'); this.callChain(); this.fireEvent('show', this.element); }.bind(this)); this.start(startStyles); } else { this.callChain(); this.fireEvent('complete', this.element); this.fireEvent('show', this.element); } } else if (this.options.link == 'chain'){ this.chain(this.reveal.bind(this)); } else if (this.options.link == 'cancel' && !this.showing){ this.cancel(); this.reveal(); } return this; }, toggle: function(){ if (this.element.getStyle('display') == 'none'){ this.reveal(); } else { this.dissolve(); } return this; }, cancel: function(){ this.parent.apply(this, arguments); if (this.cssText != null) this.element.style.cssText = this.cssText; this.hiding = false; this.showing = false; return this; } }); Element.Properties.reveal = { set: function(options){ this.get('reveal').cancel().setOptions(options); return this; }, get: function(){ var reveal = this.retrieve('reveal'); if (!reveal){ reveal = new Fx.Reveal(this); this.store('reveal', reveal); } return reveal; } }; Element.Properties.dissolve = Element.Properties.reveal; Element.implement({ reveal: function(options){ this.get('reveal').setOptions(options).reveal(); return this; }, dissolve: function(options){ this.get('reveal').setOptions(options).dissolve(); return this; }, nix: function(options){ var params = Array.link(arguments, {destroy: Type.isBoolean, options: Type.isObject}); this.get('reveal').setOptions(options).dissolve().chain(function(){ this[params.destroy ? 'destroy' : 'dispose'](); }.bind(this)); return this; }, wink: function(){ var params = Array.link(arguments, {duration: Type.isNumber, options: Type.isObject}); var reveal = this.get('reveal').setOptions(params.options); reveal.reveal().chain(function(){ (function(){ reveal.dissolve(); }).delay(params.duration || 2000); }); } }); })(); /* --- script: Form.Request.Append.js name: Form.Request.Append description: Handles the basic functionality of submitting a form and updating a dom element with the result. The result is appended to the DOM element instead of replacing its contents. license: MIT-style license authors: - Aaron Newton requires: - /Form.Request - /Fx.Reveal - /Elements.from provides: [Form.Request.Append] ... */ Form.Request.Append = new Class({ Extends: Form.Request, options: { //onBeforeEffect: function(){}, useReveal: true, revealOptions: {}, inject: 'bottom' }, makeRequest: function(){ this.request = new Request.HTML(Object.merge({ url: this.element.get('action'), method: this.element.get('method') || 'post', spinnerTarget: this.element }, this.options.requestOptions, { evalScripts: false }) ).addEvents({ success: function(tree, elements, html, javascript){ var container; var kids = Elements.from(html); if (kids.length == 1){ container = kids[0]; } else { container = new Element('div', { styles: { display: 'none' } }).adopt(kids); } container.inject(this.target, this.options.inject); if (this.options.requestOptions.evalScripts) Browser.exec(javascript); this.fireEvent('beforeEffect', container); var finish = function(){ this.fireEvent('success', [container, this.target, tree, elements, html, javascript]); }.bind(this); if (this.options.useReveal){ container.set('reveal', this.options.revealOptions).get('reveal').chain(finish); container.reveal(); } else { finish(); } }.bind(this), failure: function(xhr){ this.fireEvent('failure', xhr); }.bind(this) }); this.attachReset(); } }); /* --- name: Locale.en-US.Form.Validator description: Form Validator messages for English. license: MIT-style license authors: - Aaron Newton requires: - /Locale provides: [Locale.en-US.Form.Validator] ... */ Locale.define('en-US', 'FormValidator', { required: 'This field is required.', minLength: 'Please enter at least {minLength} characters (you entered {length} characters).', maxLength: 'Please enter no more than {maxLength} characters (you entered {length} characters).', integer: 'Please enter an integer in this field. Numbers with decimals (e.g. 1.25) are not permitted.', numeric: 'Please enter only numeric values in this field (i.e. "1" or "1.1" or "-1" or "-1.1").', digits: 'Please use numbers and punctuation only in this field (for example, a phone number with dashes or dots is permitted).', alpha: 'Please use only letters (a-z) within this field. No spaces or other characters are allowed.', alphanum: 'Please use only letters (a-z) or numbers (0-9) in this field. No spaces or other characters are allowed.', dateSuchAs: 'Please enter a valid date such as {date}', dateInFormatMDY: 'Please enter a valid date such as MM/DD/YYYY (i.e. "12/31/1999")', email: 'Please enter a valid email address. For example "fred@domain.com".', url: 'Please enter a valid URL such as http://www.example.com.', currencyDollar: 'Please enter a valid $ amount. For example $100.00 .', oneRequired: 'Please enter something for at least one of these inputs.', errorPrefix: 'Error: ', warningPrefix: 'Warning: ', // Form.Validator.Extras noSpace: 'There can be no spaces in this input.', reqChkByNode: 'No items are selected.', requiredChk: 'This field is required.', reqChkByName: 'Please select a {label}.', match: 'This field needs to match the {matchName} field', startDate: 'the start date', endDate: 'the end date', currendDate: 'the current date', afterDate: 'The date should be the same or after {label}.', beforeDate: 'The date should be the same or before {label}.', startMonth: 'Please select a start month', sameMonth: 'These two dates must be in the same month - you must change one or the other.', creditcard: 'The credit card number entered is invalid. Please check the number and try again. {length} digits entered.' }); /* --- script: Form.Validator.js name: Form.Validator description: A css-class based form validation system. license: MIT-style license authors: - Aaron Newton requires: - Core/Options - Core/Events - Core/Slick.Finder - Core/Element.Event - Core/Element.Style - Core/JSON - /Locale - /Class.Binds - /Date - /Element.Forms - /Locale.en-US.Form.Validator - /Element.Shortcuts provides: [Form.Validator, InputValidator, FormValidator.BaseValidators] ... */ if (!window.Form) window.Form = {}; var InputValidator = this.InputValidator = new Class({ Implements: [Options], options: { errorMsg: 'Validation failed.', test: Function.from(true) }, initialize: function(className, options){ this.setOptions(options); this.className = className; }, test: function(field, props){ field = document.id(field); return (field) ? this.options.test(field, props || this.getProps(field)) : false; }, getError: function(field, props){ field = document.id(field); var err = this.options.errorMsg; if (typeOf(err) == 'function') err = err(field, props || this.getProps(field)); return err; }, getProps: function(field){ field = document.id(field); return (field) ? field.get('validatorProps') : {}; } }); Element.Properties.validators = { get: function(){ return (this.get('data-validators') || this.className).clean().split(' '); } }; Element.Properties.validatorProps = { set: function(props){ return this.eliminate('$moo:validatorProps').store('$moo:validatorProps', props); }, get: function(props){ if (props) this.set(props); if (this.retrieve('$moo:validatorProps')) return this.retrieve('$moo:validatorProps'); if (this.getProperty('data-validator-properties') || this.getProperty('validatorProps')){ try { this.store('$moo:validatorProps', JSON.decode(this.getProperty('validatorProps') || this.getProperty('data-validator-properties'))); }catch(e){ return {}; } } else { var vals = this.get('validators').filter(function(cls){ return cls.test(':'); }); if (!vals.length){ this.store('$moo:validatorProps', {}); } else { props = {}; vals.each(function(cls){ var split = cls.split(':'); if (split[1]){ try { props[split[0]] = JSON.decode(split[1]); } catch(e){} } }); this.store('$moo:validatorProps', props); } } return this.retrieve('$moo:validatorProps'); } }; Form.Validator = new Class({ Implements: [Options, Events], Binds: ['onSubmit'], options: {/* onFormValidate: function(isValid, form, event){}, onElementValidate: function(isValid, field, className, warn){}, onElementPass: function(field){}, onElementFail: function(field, validatorsFailed){}, */ fieldSelectors: 'input, select, textarea', ignoreHidden: true, ignoreDisabled: true, useTitles: false, evaluateOnSubmit: true, evaluateFieldsOnBlur: true, evaluateFieldsOnChange: true, serial: true, stopOnFailure: true, warningPrefix: function(){ return Form.Validator.getMsg('warningPrefix') || 'Warning: '; }, errorPrefix: function(){ return Form.Validator.getMsg('errorPrefix') || 'Error: '; } }, initialize: function(form, options){ this.setOptions(options); this.element = document.id(form); this.element.store('validator', this); this.warningPrefix = Function.from(this.options.warningPrefix)(); this.errorPrefix = Function.from(this.options.errorPrefix)(); if (this.options.evaluateOnSubmit) this.element.addEvent('submit', this.onSubmit); if (this.options.evaluateFieldsOnBlur || this.options.evaluateFieldsOnChange) this.watchFields(this.getFields()); }, toElement: function(){ return this.element; }, getFields: function(){ return (this.fields = this.element.getElements(this.options.fieldSelectors)); }, watchFields: function(fields){ fields.each(function(el){ if (this.options.evaluateFieldsOnBlur) el.addEvent('blur', this.validationMonitor.pass([el, false], this)); if (this.options.evaluateFieldsOnChange) el.addEvent('change', this.validationMonitor.pass([el, true], this)); }, this); }, validationMonitor: function(){ clearTimeout(this.timer); this.timer = this.validateField.delay(50, this, arguments); }, onSubmit: function(event){ if (this.validate(event)) this.reset(); }, reset: function(){ this.getFields().each(this.resetField, this); return this; }, validate: function(event){ var result = this.getFields().map(function(field){ return this.validateField(field, true); }, this).every(function(v){ return v; }); this.fireEvent('formValidate', [result, this.element, event]); if (this.options.stopOnFailure && !result && event) event.preventDefault(); return result; }, validateField: function(field, force){ if (this.paused) return true; field = document.id(field); var passed = !field.hasClass('validation-failed'); var failed, warned; if (this.options.serial && !force){ failed = this.element.getElement('.validation-failed'); warned = this.element.getElement('.warning'); } if (field && (!failed || force || field.hasClass('validation-failed') || (failed && !this.options.serial))){ var validationTypes = field.get('validators'); var validators = validationTypes.some(function(cn){ return this.getValidator(cn); }, this); var validatorsFailed = []; validationTypes.each(function(className){ if (className && !this.test(className, field)) validatorsFailed.include(className); }, this); passed = validatorsFailed.length === 0; if (validators && !this.hasValidator(field, 'warnOnly')){ if (passed){ field.addClass('validation-passed').removeClass('validation-failed'); this.fireEvent('elementPass', [field]); } else { field.addClass('validation-failed').removeClass('validation-passed'); this.fireEvent('elementFail', [field, validatorsFailed]); } } if (!warned){ var warnings = validationTypes.some(function(cn){ if (cn.test('^warn')) return this.getValidator(cn.replace(/^warn-/,'')); else return null; }, this); field.removeClass('warning'); var warnResult = validationTypes.map(function(cn){ if (cn.test('^warn')) return this.test(cn.replace(/^warn-/,''), field, true); else return null; }, this); } } return passed; }, test: function(className, field, warn){ field = document.id(field); if ((this.options.ignoreHidden && !field.isVisible()) || (this.options.ignoreDisabled && field.get('disabled'))) return true; var validator = this.getValidator(className); if (warn != null) warn = false; if (this.hasValidator(field, 'warnOnly')) warn = true; var isValid = this.hasValidator(field, 'ignoreValidation') || (validator ? validator.test(field) : true); if (validator && field.isVisible()) this.fireEvent('elementValidate', [isValid, field, className, warn]); if (warn) return true; return isValid; }, hasValidator: function(field, value){ return field.get('validators').contains(value); }, resetField: function(field){ field = document.id(field); if (field){ field.get('validators').each(function(className){ if (className.test('^warn-')) className = className.replace(/^warn-/, ''); field.removeClass('validation-failed'); field.removeClass('warning'); field.removeClass('validation-passed'); }, this); } return this; }, stop: function(){ this.paused = true; return this; }, start: function(){ this.paused = false; return this; }, ignoreField: function(field, warn){ field = document.id(field); if (field){ this.enforceField(field); if (warn) field.addClass('warnOnly'); else field.addClass('ignoreValidation'); } return this; }, enforceField: function(field){ field = document.id(field); if (field) field.removeClass('warnOnly').removeClass('ignoreValidation'); return this; } }); Form.Validator.getMsg = function(key){ return Locale.get('FormValidator.' + key); }; Form.Validator.adders = { validators:{}, add : function(className, options){ this.validators[className] = new InputValidator(className, options); //if this is a class (this method is used by instances of Form.Validator and the Form.Validator namespace) //extend these validators into it //this allows validators to be global and/or per instance if (!this.initialize){ this.implement({ validators: this.validators }); } }, addAllThese : function(validators){ Array.from(validators).each(function(validator){ this.add(validator[0], validator[1]); }, this); }, getValidator: function(className){ return this.validators[className.split(':')[0]]; } }; Object.append(Form.Validator, Form.Validator.adders); Form.Validator.implement(Form.Validator.adders); Form.Validator.add('IsEmpty', { errorMsg: false, test: function(element){ if (element.type == 'select-one' || element.type == 'select') return !(element.selectedIndex >= 0 && element.options[element.selectedIndex].value != ''); else return ((element.get('value') == null) || (element.get('value').length == 0)); } }); Form.Validator.addAllThese([ ['required', { errorMsg: function(){ return Form.Validator.getMsg('required'); }, test: function(element){ return !Form.Validator.getValidator('IsEmpty').test(element); } }], ['minLength', { errorMsg: function(element, props){ if (typeOf(props.minLength) != 'null') return Form.Validator.getMsg('minLength').substitute({minLength:props.minLength,length:element.get('value').length }); else return ''; }, test: function(element, props){ if (typeOf(props.minLength) != 'null') return (element.get('value').length >= (props.minLength || 0)); else return true; } }], ['maxLength', { errorMsg: function(element, props){ //props is {maxLength:10} if (typeOf(props.maxLength) != 'null') return Form.Validator.getMsg('maxLength').substitute({maxLength:props.maxLength,length:element.get('value').length }); else return ''; }, test: function(element, props){ return element.get('value').length <= (props.maxLength || 10000); } }], ['validate-integer', { errorMsg: Form.Validator.getMsg.pass('integer'), test: function(element){ return Form.Validator.getValidator('IsEmpty').test(element) || (/^(-?[1-9]\d*|0)$/).test(element.get('value')); } }], ['validate-numeric', { errorMsg: Form.Validator.getMsg.pass('numeric'), test: function(element){ return Form.Validator.getValidator('IsEmpty').test(element) || (/^-?(?:0$0(?=\d*\.)|[1-9]|0)\d*(\.\d+)?$/).test(element.get('value')); } }], ['validate-digits', { errorMsg: Form.Validator.getMsg.pass('digits'), test: function(element){ return Form.Validator.getValidator('IsEmpty').test(element) || (/^[\d() .:\-\+#]+$/.test(element.get('value'))); } }], ['validate-alpha', { errorMsg: Form.Validator.getMsg.pass('alpha'), test: function(element){ return Form.Validator.getValidator('IsEmpty').test(element) || (/^[a-zA-Z]+$/).test(element.get('value')); } }], ['validate-alphanum', { errorMsg: Form.Validator.getMsg.pass('alphanum'), test: function(element){ return Form.Validator.getValidator('IsEmpty').test(element) || !(/\W/).test(element.get('value')); } }], ['validate-date', { errorMsg: function(element, props){ if (Date.parse){ var format = props.dateFormat || '%x'; return Form.Validator.getMsg('dateSuchAs').substitute({date: new Date().format(format)}); } else { return Form.Validator.getMsg('dateInFormatMDY'); } }, test: function(element, props){ if (Form.Validator.getValidator('IsEmpty').test(element)) return true; var dateLocale = Locale.getCurrent().sets.Date, dateNouns = new RegExp([dateLocale.days, dateLocale.days_abbr, dateLocale.months, dateLocale.months_abbr].flatten().join('|'), 'i'), value = element.get('value'), wordsInValue = value.match(/[a-z]+/gi); if (wordsInValue && !wordsInValue.every(dateNouns.exec, dateNouns)) return false; var date = Date.parse(value), format = props.dateFormat || '%x', formatted = date.format(format); if (formatted != 'invalid date') element.set('value', formatted); return date.isValid(); } }], ['validate-email', { errorMsg: Form.Validator.getMsg.pass('email'), test: function(element){ /* var chars = "[a-z0-9!#$%&'*+/=?^_`{|}~-]", local = '(?:' + chars + '\\.?){0,63}' + chars, label = '[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?', hostname = '(?:' + label + '\\.)*' + label; octet = '(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)', ipv4 = '\\[(?:' + octet + '\\.){3}' + octet + '\\]', domain = '(?:' + hostname + '|' + ipv4 + ')'; var regex = new RegExp('^' + local + '@' + domain + '$', 'i'); */ return Form.Validator.getValidator('IsEmpty').test(element) || (/^(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]\.?){0,63}[a-z0-9!#$%&'*+\/=?^_`{|}~-]@(?:(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\])$/i).test(element.get('value')); } }], ['validate-url', { errorMsg: Form.Validator.getMsg.pass('url'), test: function(element){ return Form.Validator.getValidator('IsEmpty').test(element) || (/^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i).test(element.get('value')); } }], ['validate-currency-dollar', { errorMsg: Form.Validator.getMsg.pass('currencyDollar'), test: function(element){ return Form.Validator.getValidator('IsEmpty').test(element) || (/^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/).test(element.get('value')); } }], ['validate-one-required', { errorMsg: Form.Validator.getMsg.pass('oneRequired'), test: function(element, props){ var p = document.id(props['validate-one-required']) || element.getParent(props['validate-one-required']); return p.getElements('input').some(function(el){ if (['checkbox', 'radio'].contains(el.get('type'))) return el.get('checked'); return el.get('value'); }); } }] ]); Element.Properties.validator = { set: function(options){ this.get('validator').setOptions(options); }, get: function(){ var validator = this.retrieve('validator'); if (!validator){ validator = new Form.Validator(this); this.store('validator', validator); } return validator; } }; Element.implement({ validate: function(options){ if (options) this.set('validator', options); return this.get('validator').validate(); } }); /* --- script: Form.Validator.Inline.js name: Form.Validator.Inline description: Extends Form.Validator to add inline messages. license: MIT-style license authors: - Aaron Newton requires: - /Form.Validator provides: [Form.Validator.Inline] ... */ Form.Validator.Inline = new Class({ Extends: Form.Validator, options: { showError: function(errorElement){ if (errorElement.reveal) errorElement.reveal(); else errorElement.setStyle('display', 'block'); }, hideError: function(errorElement){ if (errorElement.dissolve) errorElement.dissolve(); else errorElement.setStyle('display', 'none'); }, scrollToErrorsOnSubmit: true, scrollToErrorsOnBlur: false, scrollToErrorsOnChange: false, scrollFxOptions: { transition: 'quad:out', offset: { y: -20 } } }, initialize: function(form, options){ this.parent(form, options); this.addEvent('onElementValidate', function(isValid, field, className, warn){ var validator = this.getValidator(className); if (!isValid && validator.getError(field)){ if (warn) field.addClass('warning'); var advice = this.makeAdvice(className, field, validator.getError(field), warn); this.insertAdvice(advice, field); this.showAdvice(className, field); } else { this.hideAdvice(className, field); } }); }, makeAdvice: function(className, field, error, warn){ var errorMsg = (warn) ? this.warningPrefix : this.errorPrefix; errorMsg += (this.options.useTitles) ? field.title || error:error; var cssClass = (warn) ? 'warning-advice' : 'validation-advice'; var advice = this.getAdvice(className, field); if (advice){ advice = advice.set('html', errorMsg); } else { advice = new Element('div', { html: errorMsg, styles: { display: 'none' }, id: 'advice-' + className.split(':')[0] + '-' + this.getFieldId(field) }).addClass(cssClass); } field.store('$moo:advice-' + className, advice); return advice; }, getFieldId : function(field){ return field.id ? field.id : field.id = 'input_' + field.name; }, showAdvice: function(className, field){ var advice = this.getAdvice(className, field); if ( advice && !field.retrieve('$moo:' + this.getPropName(className)) && ( advice.getStyle('display') == 'none' || advice.getStyle('visiblity') == 'hidden' || advice.getStyle('opacity') == 0 ) ){ field.store('$moo:' + this.getPropName(className), true); this.options.showError(advice); this.fireEvent('showAdvice', [field, advice, className]); } }, hideAdvice: function(className, field){ var advice = this.getAdvice(className, field); if (advice && field.retrieve('$moo:' + this.getPropName(className))){ field.store('$moo:' + this.getPropName(className), false); this.options.hideError(advice); this.fireEvent('hideAdvice', [field, advice, className]); } }, getPropName: function(className){ return 'advice' + className; }, resetField: function(field){ field = document.id(field); if (!field) return this; this.parent(field); field.get('validators').each(function(className){ this.hideAdvice(className, field); }, this); return this; }, getAllAdviceMessages: function(field, force){ var advice = []; if (field.hasClass('ignoreValidation') && !force) return advice; var validators = field.get('validators').some(function(cn){ var warner = cn.test('^warn-') || field.hasClass('warnOnly'); if (warner) cn = cn.replace(/^warn-/, ''); var validator = this.getValidator(cn); if (!validator) return; advice.push({ message: validator.getError(field), warnOnly: warner, passed: validator.test(), validator: validator }); }, this); return advice; }, getAdvice: function(className, field){ return field.retrieve('$moo:advice-' + className); }, insertAdvice: function(advice, field){ //Check for error position prop var props = field.get('validatorProps'); //Build advice if (!props.msgPos || !document.id(props.msgPos)){ if (field.type && field.type.toLowerCase() == 'radio') field.getParent().adopt(advice); else advice.inject(document.id(field), 'after'); } else { document.id(props.msgPos).grab(advice); } }, validateField: function(field, force, scroll){ var result = this.parent(field, force); if (((this.options.scrollToErrorsOnSubmit && scroll == null) || scroll) && !result){ var failed = document.id(this).getElement('.validation-failed'); var par = document.id(this).getParent(); while (par != document.body && par.getScrollSize().y == par.getSize().y){ par = par.getParent(); } var fx = par.retrieve('$moo:fvScroller'); if (!fx && window.Fx && Fx.Scroll){ fx = new Fx.Scroll(par, this.options.scrollFxOptions); par.store('$moo:fvScroller', fx); } if (failed){ if (fx) fx.toElement(failed); else par.scrollTo(par.getScroll().x, failed.getPosition(par).y - 20); } } return result; }, watchFields: function(fields){ fields.each(function(el){ if (this.options.evaluateFieldsOnBlur){ el.addEvent('blur', this.validationMonitor.pass([el, false, this.options.scrollToErrorsOnBlur], this)); } if (this.options.evaluateFieldsOnChange){ el.addEvent('change', this.validationMonitor.pass([el, true, this.options.scrollToErrorsOnChange], this)); } }, this); } }); /* --- script: Form.Validator.Extras.js name: Form.Validator.Extras description: Additional validators for the Form.Validator class. license: MIT-style license authors: - Aaron Newton requires: - /Form.Validator provides: [Form.Validator.Extras] ... */ Form.Validator.addAllThese([ ['validate-enforce-oncheck', { test: function(element, props){ var fv = element.getParent('form').retrieve('validator'); if (!fv) return true; (props.toEnforce || document.id(props.enforceChildrenOf).getElements('input, select, textarea')).map(function(item){ if (element.checked){ fv.enforceField(item); } else { fv.ignoreField(item); fv.resetField(item); } }); return true; } }], ['validate-ignore-oncheck', { test: function(element, props){ var fv = element.getParent('form').retrieve('validator'); if (!fv) return true; (props.toIgnore || document.id(props.ignoreChildrenOf).getElements('input, select, textarea')).each(function(item){ if (element.checked){ fv.ignoreField(item); fv.resetField(item); } else { fv.enforceField(item); } }); return true; } }], ['validate-nospace', { errorMsg: function(){ return Form.Validator.getMsg('noSpace'); }, test: function(element, props){ return !element.get('value').test(/\s/); } }], ['validate-toggle-oncheck', { test: function(element, props){ var fv = element.getParent('form').retrieve('validator'); if (!fv) return true; var eleArr = props.toToggle || document.id(props.toToggleChildrenOf).getElements('input, select, textarea'); if (!element.checked){ eleArr.each(function(item){ fv.ignoreField(item); fv.resetField(item); }); } else { eleArr.each(function(item){ fv.enforceField(item); }); } return true; } }], ['validate-reqchk-bynode', { errorMsg: function(){ return Form.Validator.getMsg('reqChkByNode'); }, test: function(element, props){ return (document.id(props.nodeId).getElements(props.selector || 'input[type=checkbox], input[type=radio]')).some(function(item){ return item.checked; }); } }], ['validate-required-check', { errorMsg: function(element, props){ return props.useTitle ? element.get('title') : Form.Validator.getMsg('requiredChk'); }, test: function(element, props){ return !!element.checked; } }], ['validate-reqchk-byname', { errorMsg: function(element, props){ return Form.Validator.getMsg('reqChkByName').substitute({label: props.label || element.get('type')}); }, test: function(element, props){ var grpName = props.groupName || element.get('name'); var oneCheckedItem = $$(document.getElementsByName(grpName)).some(function(item, index){ return item.checked; }); var fv = element.getParent('form').retrieve('validator'); if (oneCheckedItem && fv) fv.resetField(element); return oneCheckedItem; } }], ['validate-match', { errorMsg: function(element, props){ return Form.Validator.getMsg('match').substitute({matchName: props.matchName || document.id(props.matchInput).get('name')}); }, test: function(element, props){ var eleVal = element.get('value'); var matchVal = document.id(props.matchInput) && document.id(props.matchInput).get('value'); return eleVal && matchVal ? eleVal == matchVal : true; } }], ['validate-after-date', { errorMsg: function(element, props){ return Form.Validator.getMsg('afterDate').substitute({ label: props.afterLabel || (props.afterElement ? Form.Validator.getMsg('startDate') : Form.Validator.getMsg('currentDate')) }); }, test: function(element, props){ var start = document.id(props.afterElement) ? Date.parse(document.id(props.afterElement).get('value')) : new Date(); var end = Date.parse(element.get('value')); return end && start ? end >= start : true; } }], ['validate-before-date', { errorMsg: function(element, props){ return Form.Validator.getMsg('beforeDate').substitute({ label: props.beforeLabel || (props.beforeElement ? Form.Validator.getMsg('endDate') : Form.Validator.getMsg('currentDate')) }); }, test: function(element, props){ var start = Date.parse(element.get('value')); var end = document.id(props.beforeElement) ? Date.parse(document.id(props.beforeElement).get('value')) : new Date(); return end && start ? end >= start : true; } }], ['validate-custom-required', { errorMsg: function(){ return Form.Validator.getMsg('required'); }, test: function(element, props){ return element.get('value') != props.emptyValue; } }], ['validate-same-month', { errorMsg: function(element, props){ var startMo = document.id(props.sameMonthAs) && document.id(props.sameMonthAs).get('value'); var eleVal = element.get('value'); if (eleVal != '') return Form.Validator.getMsg(startMo ? 'sameMonth' : 'startMonth'); }, test: function(element, props){ var d1 = Date.parse(element.get('value')); var d2 = Date.parse(document.id(props.sameMonthAs) && document.id(props.sameMonthAs).get('value')); return d1 && d2 ? d1.format('%B') == d2.format('%B') : true; } }], ['validate-cc-num', { errorMsg: function(element){ var ccNum = element.get('value').replace(/[^0-9]/g, ''); return Form.Validator.getMsg('creditcard').substitute({length: ccNum.length}); }, test: function(element){ // required is a different test if (Form.Validator.getValidator('IsEmpty').test(element)) return true; // Clean number value var ccNum = element.get('value'); ccNum = ccNum.replace(/[^0-9]/g, ''); var valid_type = false; if (ccNum.test(/^4[0-9]{12}([0-9]{3})?$/)) valid_type = 'Visa'; else if (ccNum.test(/^5[1-5]([0-9]{14})$/)) valid_type = 'Master Card'; else if (ccNum.test(/^3[47][0-9]{13}$/)) valid_type = 'American Express'; else if (ccNum.test(/^6011[0-9]{12}$/)) valid_type = 'Discover'; if (valid_type){ var sum = 0; var cur = 0; for (var i=ccNum.length-1; i>=0; --i){ cur = ccNum.charAt(i).toInt(); if (cur == 0) continue; if ((ccNum.length-i) % 2 == 0) cur += cur; if (cur > 9){ cur = cur.toString().charAt(0).toInt() + cur.toString().charAt(1).toInt(); } sum += cur; } if ((sum % 10) == 0) return true; } var chunks = ''; while (ccNum != ''){ chunks += ' ' + ccNum.substr(0,4); ccNum = ccNum.substr(4); } element.getParent('form').retrieve('validator').ignoreField(element); element.set('value', chunks.clean()); element.getParent('form').retrieve('validator').enforceField(element); return false; } }] ]); /* --- script: OverText.js name: OverText description: Shows text over an input that disappears when the user clicks into it. The text remains hidden if the user adds a value. license: MIT-style license authors: - Aaron Newton requires: - Core/Options - Core/Events - Core/Element.Event - Class.Binds - Class.Occlude - Element.Position - Element.Shortcuts provides: [OverText] ... */ var OverText = new Class({ Implements: [Options, Events, Class.Occlude], Binds: ['reposition', 'assert', 'focus', 'hide'], options: {/* textOverride: null, onFocus: function(){}, onTextHide: function(textEl, inputEl){}, onTextShow: function(textEl, inputEl){}, */ element: 'label', labelClass: 'overTxtLabel', positionOptions: { position: 'upperLeft', edge: 'upperLeft', offset: { x: 4, y: 2 } }, poll: false, pollInterval: 250, wrap: false }, property: 'OverText', initialize: function(element, options){ element = this.element = document.id(element); if (this.occlude()) return this.occluded; this.setOptions(options); this.attach(element); OverText.instances.push(this); if (this.options.poll) this.poll(); }, toElement: function(){ return this.element; }, attach: function(){ var element = this.element, options = this.options, value = options.textOverride || element.get('alt') || element.get('title'); if (!value) return this; var text = this.text = new Element(options.element, { 'class': options.labelClass, styles: { lineHeight: 'normal', position: 'absolute', cursor: 'text' }, html: value, events: { click: this.hide.pass(options.element == 'label', this) } }).inject(element, 'after'); if (options.element == 'label'){ if (!element.get('id')) element.set('id', 'input_' + String.uniqueID()); text.set('for', element.get('id')); } if (options.wrap){ this.textHolder = new Element('div.overTxtWrapper', { styles: { lineHeight: 'normal', position: 'relative' } }).grab(text).inject(element, 'before'); } return this.enable(); }, destroy: function(){ this.element.eliminate(this.property); // Class.Occlude storage this.disable(); if (this.text) this.text.destroy(); if (this.textHolder) this.textHolder.destroy(); return this; }, disable: function(){ this.element.removeEvents({ focus: this.focus, blur: this.assert, change: this.assert }); window.removeEvent('resize', this.reposition); this.hide(true, true); return this; }, enable: function(){ this.element.addEvents({ focus: this.focus, blur: this.assert, change: this.assert }); window.addEvent('resize', this.reposition); this.assert(true); this.reposition(); return this; }, wrap: function(){ if (this.options.element == 'label'){ if (!this.element.get('id')) this.element.set('id', 'input_' + String.uniqueID()); this.text.set('for', this.element.get('id')); } }, startPolling: function(){ this.pollingPaused = false; return this.poll(); }, poll: function(stop){ //start immediately //pause on focus //resumeon blur if (this.poller && !stop) return this; if (stop){ clearInterval(this.poller); } else { this.poller = (function(){ if (!this.pollingPaused) this.assert(true); }).periodical(this.options.pollInterval, this); } return this; }, stopPolling: function(){ this.pollingPaused = true; return this.poll(true); }, focus: function(){ if (this.text && (!this.text.isDisplayed() || this.element.get('disabled'))) return this; return this.hide(); }, hide: function(suppressFocus, force){ if (this.text && (this.text.isDisplayed() && (!this.element.get('disabled') || force))){ this.text.hide(); this.fireEvent('textHide', [this.text, this.element]); this.pollingPaused = true; if (!suppressFocus){ try { this.element.fireEvent('focus'); this.element.focus(); } catch(e){} //IE barfs if you call focus on hidden elements } } return this; }, show: function(){ if (this.text && !this.text.isDisplayed()){ this.text.show(); this.reposition(); this.fireEvent('textShow', [this.text, this.element]); this.pollingPaused = false; } return this; }, test: function(){ return !this.element.get('value'); }, assert: function(suppressFocus){ return this[this.test() ? 'show' : 'hide'](suppressFocus); }, reposition: function(){ this.assert(true); if (!this.element.isVisible()) return this.stopPolling().hide(); if (this.text && this.test()){ this.text.position(Object.merge(this.options.positionOptions, { relativeTo: this.element })); } return this; } }); OverText.instances = []; Object.append(OverText, { each: function(fn){ return OverText.instances.each(function(ot, i){ if (ot.element && ot.text) fn.call(OverText, ot, i); }); }, update: function(){ return OverText.each(function(ot){ return ot.reposition(); }); }, hideAll: function(){ return OverText.each(function(ot){ return ot.hide(true, true); }); }, showAll: function(){ return OverText.each(function(ot){ return ot.show(); }); } }); /* --- script: Fx.Elements.js name: Fx.Elements description: Effect to change any number of CSS properties of any number of Elements. license: MIT-style license authors: - Valerio Proietti requires: - Core/Fx.CSS - /MooTools.More provides: [Fx.Elements] ... */ Fx.Elements = new Class({ Extends: Fx.CSS, initialize: function(elements, options){ this.elements = this.subject = $$(elements); this.parent(options); }, compute: function(from, to, delta){ var now = {}; for (var i in from){ var iFrom = from[i], iTo = to[i], iNow = now[i] = {}; for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta); } return now; }, set: function(now){ for (var i in now){ if (!this.elements[i]) continue; var iNow = now[i]; for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit); } return this; }, start: function(obj){ if (!this.check(obj)) return this; var from = {}, to = {}; for (var i in obj){ if (!this.elements[i]) continue; var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {}; for (var p in iProps){ var parsed = this.prepare(this.elements[i], p, iProps[p]); iFrom[p] = parsed.from; iTo[p] = parsed.to; } } return this.parent(from, to); } }); /* --- script: Fx.Accordion.js name: Fx.Accordion description: An Fx.Elements extension which allows you to easily create accordion type controls. license: MIT-style license authors: - Valerio Proietti requires: - Core/Element.Event - /Fx.Elements provides: [Fx.Accordion] ... */ Fx.Accordion = new Class({ Extends: Fx.Elements, options: {/* onActive: function(toggler, section){}, onBackground: function(toggler, section){},*/ fixedHeight: false, fixedWidth: false, display: 0, show: false, height: true, width: false, opacity: true, alwaysHide: false, trigger: 'click', initialDisplayFx: true, resetHeight: true }, initialize: function(){ var defined = function(obj){ return obj != null; }; var params = Array.link(arguments, { 'container': Type.isElement, //deprecated 'options': Type.isObject, 'togglers': defined, 'elements': defined }); this.parent(params.elements, params.options); var options = this.options, togglers = this.togglers = $$(params.togglers); this.previous = -1; this.internalChain = new Chain(); if (options.alwaysHide) this.options.link = 'chain'; if (options.show || this.options.show === 0){ options.display = false; this.previous = options.show; } if (options.start){ options.display = false; options.show = false; } var effects = this.effects = {}; if (options.opacity) effects.opacity = 'fullOpacity'; if (options.width) effects.width = options.fixedWidth ? 'fullWidth' : 'offsetWidth'; if (options.height) effects.height = options.fixedHeight ? 'fullHeight' : 'scrollHeight'; for (var i = 0, l = togglers.length; i < l; i++) this.addSection(togglers[i], this.elements[i]); this.elements.each(function(el, i){ if (options.show === i){ this.fireEvent('active', [togglers[i], el]); } else { for (var fx in effects) el.setStyle(fx, 0); } }, this); if (options.display || options.display === 0 || options.initialDisplayFx === false){ this.display(options.display, options.initialDisplayFx); } if (options.fixedHeight !== false) options.resetHeight = false; this.addEvent('complete', this.internalChain.callChain.bind(this.internalChain)); }, addSection: function(toggler, element){ toggler = document.id(toggler); element = document.id(element); this.togglers.include(toggler); this.elements.include(element); var togglers = this.togglers, options = this.options, test = togglers.contains(toggler), idx = togglers.indexOf(toggler), displayer = this.display.pass(idx, this); toggler.store('accordion:display', displayer) .addEvent(options.trigger, displayer); if (options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'}); if (options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'}); element.fullOpacity = 1; if (options.fixedWidth) element.fullWidth = options.fixedWidth; if (options.fixedHeight) element.fullHeight = options.fixedHeight; element.setStyle('overflow', 'hidden'); if (!test) for (var fx in this.effects){ element.setStyle(fx, 0); } return this; }, removeSection: function(toggler, displayIndex){ var togglers = this.togglers, idx = togglers.indexOf(toggler), element = this.elements[idx]; var remover = function(){ togglers.erase(toggler); this.elements.erase(element); this.detach(toggler); }.bind(this); if (this.now == idx || displayIndex != null){ this.display(displayIndex != null ? displayIndex : (idx - 1 >= 0 ? idx - 1 : 0)).chain(remover); } else { remover(); } return this; }, detach: function(toggler){ var remove = function(toggler){ toggler.removeEvent(this.options.trigger, toggler.retrieve('accordion:display')); }.bind(this); if (!toggler) this.togglers.each(remove); else remove(toggler); return this; }, display: function(index, useFx){ if (!this.check(index, useFx)) return this; var obj = {}, elements = this.elements, options = this.options, effects = this.effects; if (useFx == null) useFx = true; if (typeOf(index) == 'element') index = elements.indexOf(index); if (index == this.previous && !options.alwaysHide) return this; if (options.resetHeight){ var prev = elements[this.previous]; if (prev && !this.selfHidden){ for (var fx in effects) prev.setStyle(fx, prev[effects[fx]]); } } if ((this.timer && options.link == 'chain') || (index === this.previous && !options.alwaysHide)) return this; this.previous = index; this.selfHidden = false; elements.each(function(el, i){ obj[i] = {}; var hide; if (i != index){ hide = true; } else if (options.alwaysHide && ((el.offsetHeight > 0 && options.height) || el.offsetWidth > 0 && options.width)){ hide = true; this.selfHidden = true; } this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]); for (var fx in effects) obj[i][fx] = hide ? 0 : el[effects[fx]]; if (!useFx && !hide && options.resetHeight) obj[i].height = 'auto'; }, this); this.internalChain.clearChain(); this.internalChain.chain(function(){ if (options.resetHeight && !this.selfHidden){ var el = elements[index]; if (el) el.setStyle('height', 'auto'); } }.bind(this)); return useFx ? this.start(obj) : this.set(obj).internalChain.callChain(); } }); /* --- script: Fx.Move.js name: Fx.Move description: Defines Fx.Move, a class that works with Element.Position.js to transition an element from one location to another. license: MIT-style license authors: - Aaron Newton requires: - Core/Fx.Morph - /Element.Position provides: [Fx.Move] ... */ Fx.Move = new Class({ Extends: Fx.Morph, options: { relativeTo: document.body, position: 'center', edge: false, offset: {x: 0, y: 0} }, start: function(destination){ var element = this.element, topLeft = element.getStyles('top', 'left'); if (topLeft.top == 'auto' || topLeft.left == 'auto'){ element.setPosition(element.getPosition(element.getOffsetParent())); } return this.parent(element.position(Object.merge({}, this.options, destination, {returnPos: true}))); } }); Element.Properties.move = { set: function(options){ this.get('move').cancel().setOptions(options); return this; }, get: function(){ var move = this.retrieve('move'); if (!move){ move = new Fx.Move(this, {link: 'cancel'}); this.store('move', move); } return move; } }; Element.implement({ move: function(options){ this.get('move').start(options); return this; } }); /* --- script: Fx.Slide.js name: Fx.Slide description: Effect to slide an element in and out of view. license: MIT-style license authors: - Valerio Proietti requires: - Core/Fx - Core/Element.Style - /MooTools.More provides: [Fx.Slide] ... */ Fx.Slide = new Class({ Extends: Fx, options: { mode: 'vertical', wrapper: false, hideOverflow: true, resetHeight: false }, initialize: function(element, options){ element = this.element = this.subject = document.id(element); this.parent(options); options = this.options; var wrapper = element.retrieve('wrapper'), styles = element.getStyles('margin', 'position', 'overflow'); if (options.hideOverflow) styles = Object.append(styles, {overflow: 'hidden'}); if (options.wrapper) wrapper = document.id(options.wrapper).setStyles(styles); if (!wrapper) wrapper = new Element('div', { styles: styles }).wraps(element); element.store('wrapper', wrapper).setStyle('margin', 0); if (element.getStyle('overflow') == 'visible') element.setStyle('overflow', 'hidden'); this.now = []; this.open = true; this.wrapper = wrapper; this.addEvent('complete', function(){ this.open = (wrapper['offset' + this.layout.capitalize()] != 0); if (this.open && this.options.resetHeight) wrapper.setStyle('height', ''); }, true); }, vertical: function(){ this.margin = 'margin-top'; this.layout = 'height'; this.offset = this.element.offsetHeight; }, horizontal: function(){ this.margin = 'margin-left'; this.layout = 'width'; this.offset = this.element.offsetWidth; }, set: function(now){ this.element.setStyle(this.margin, now[0]); this.wrapper.setStyle(this.layout, now[1]); return this; }, compute: function(from, to, delta){ return [0, 1].map(function(i){ return Fx.compute(from[i], to[i], delta); }); }, start: function(how, mode){ if (!this.check(how, mode)) return this; this[mode || this.options.mode](); var margin = this.element.getStyle(this.margin).toInt(), layout = this.wrapper.getStyle(this.layout).toInt(), caseIn = [[margin, layout], [0, this.offset]], caseOut = [[margin, layout], [-this.offset, 0]], start; switch (how){ case 'in': start = caseIn; break; case 'out': start = caseOut; break; case 'toggle': start = (layout == 0) ? caseIn : caseOut; } return this.parent(start[0], start[1]); }, slideIn: function(mode){ return this.start('in', mode); }, slideOut: function(mode){ return this.start('out', mode); }, hide: function(mode){ this[mode || this.options.mode](); this.open = false; return this.set([-this.offset, 0]); }, show: function(mode){ this[mode || this.options.mode](); this.open = true; return this.set([0, this.offset]); }, toggle: function(mode){ return this.start('toggle', mode); } }); Element.Properties.slide = { set: function(options){ this.get('slide').cancel().setOptions(options); return this; }, get: function(){ var slide = this.retrieve('slide'); if (!slide){ slide = new Fx.Slide(this, {link: 'cancel'}); this.store('slide', slide); } return slide; } }; Element.implement({ slide: function(how, mode){ how = how || 'toggle'; var slide = this.get('slide'), toggle; switch (how){ case 'hide': slide.hide(mode); break; case 'show': slide.show(mode); break; case 'toggle': var flag = this.retrieve('slide:flag', slide.open); slide[flag ? 'slideOut' : 'slideIn'](mode); this.store('slide:flag', !flag); toggle = true; break; default: slide.start(how, mode); } if (!toggle) this.eliminate('slide:flag'); return this; } }); /* --- script: Fx.Scroll.js name: Fx.Scroll description: Effect to smoothly scroll any element, including the window. license: MIT-style license authors: - Valerio Proietti requires: - Core/Fx - Core/Element.Event - Core/Element.Dimensions - /MooTools.More provides: [Fx.Scroll] ... */ (function(){ Fx.Scroll = new Class({ Extends: Fx, options: { offset: {x: 0, y: 0}, wheelStops: true }, initialize: function(element, options){ this.element = this.subject = document.id(element); this.parent(options); if (typeOf(this.element) != 'element') this.element = document.id(this.element.getDocument().body); if (this.options.wheelStops){ var stopper = this.element, cancel = this.cancel.pass(false, this); this.addEvent('start', function(){ stopper.addEvent('mousewheel', cancel); }, true); this.addEvent('complete', function(){ stopper.removeEvent('mousewheel', cancel); }, true); } }, set: function(){ var now = Array.flatten(arguments); if (Browser.firefox) now = [Math.round(now[0]), Math.round(now[1])]; // not needed anymore in newer firefox versions this.element.scrollTo(now[0], now[1]); return this; }, compute: function(from, to, delta){ return [0, 1].map(function(i){ return Fx.compute(from[i], to[i], delta); }); }, start: function(x, y){ if (!this.check(x, y)) return this; var scroll = this.element.getScroll(); return this.parent([scroll.x, scroll.y], [x, y]); }, calculateScroll: function(x, y){ var element = this.element, scrollSize = element.getScrollSize(), scroll = element.getScroll(), size = element.getSize(), offset = this.options.offset, values = {x: x, y: y}; for (var z in values){ if (!values[z] && values[z] !== 0) values[z] = scroll[z]; if (typeOf(values[z]) != 'number') values[z] = scrollSize[z] - size[z]; values[z] += offset[z]; } return [values.x, values.y]; }, toTop: function(){ return this.start.apply(this, this.calculateScroll(false, 0)); }, toLeft: function(){ return this.start.apply(this, this.calculateScroll(0, false)); }, toRight: function(){ return this.start.apply(this, this.calculateScroll('right', false)); }, toBottom: function(){ return this.start.apply(this, this.calculateScroll(false, 'bottom')); }, toElement: function(el, axes){ axes = axes ? Array.from(axes) : ['x', 'y']; var scroll = isBody(this.element) ? {x: 0, y: 0} : this.element.getScroll(); var position = Object.map(document.id(el).getPosition(this.element), function(value, axis){ return axes.contains(axis) ? value + scroll[axis] : false; }); return this.start.apply(this, this.calculateScroll(position.x, position.y)); }, toElementEdge: function(el, axes, offset){ axes = axes ? Array.from(axes) : ['x', 'y']; el = document.id(el); var to = {}, position = el.getPosition(this.element), size = el.getSize(), scroll = this.element.getScroll(), containerSize = this.element.getSize(), edge = { x: position.x + size.x, y: position.y + size.y }; ['x', 'y'].each(function(axis){ if (axes.contains(axis)){ if (edge[axis] > scroll[axis] + containerSize[axis]) to[axis] = edge[axis] - containerSize[axis]; if (position[axis] < scroll[axis]) to[axis] = position[axis]; } if (to[axis] == null) to[axis] = scroll[axis]; if (offset && offset[axis]) to[axis] = to[axis] + offset[axis]; }, this); if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y); return this; }, toElementCenter: function(el, axes, offset){ axes = axes ? Array.from(axes) : ['x', 'y']; el = document.id(el); var to = {}, position = el.getPosition(this.element), size = el.getSize(), scroll = this.element.getScroll(), containerSize = this.element.getSize(); ['x', 'y'].each(function(axis){ if (axes.contains(axis)){ to[axis] = position[axis] - (containerSize[axis] - size[axis]) / 2; } if (to[axis] == null) to[axis] = scroll[axis]; if (offset && offset[axis]) to[axis] = to[axis] + offset[axis]; }, this); if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y); return this; } }); function isBody(element){ return (/^(?:body|html)$/i).test(element.tagName); } })(); /* --- script: Fx.SmoothScroll.js name: Fx.SmoothScroll description: Class for creating a smooth scrolling effect to all internal links on the page. license: MIT-style license authors: - Valerio Proietti requires: - Core/Slick.Finder - /Fx.Scroll provides: [Fx.SmoothScroll] ... */ Fx.SmoothScroll = new Class({ Extends: Fx.Scroll, options: { axes: ['x', 'y'] }, initialize: function(options, context){ context = context || document; this.doc = context.getDocument(); this.parent(this.doc, options); var win = context.getWindow(), location = win.location.href.match(/^[^#]*/)[0] + '#', links = $$(this.options.links || this.doc.links); links.each(function(link){ if (link.href.indexOf(location) != 0) return; var anchor = link.href.substr(location.length); if (anchor) this.useLink(link, anchor); }, this); this.addEvent('complete', function(){ win.location.hash = this.anchor; this.element.scrollTo(this.to[0], this.to[1]); }, true); }, useLink: function(link, anchor){ link.addEvent('click', function(event){ var el = document.id(anchor) || this.doc.getElement('a[name=' + anchor + ']'); if (!el) return; event.preventDefault(); this.toElement(el, this.options.axes).chain(function(){ this.fireEvent('scrolledTo', [link, el]); }.bind(this)); this.anchor = anchor; }.bind(this)); return this; } }); /* --- script: Fx.Sort.js name: Fx.Sort description: Defines Fx.Sort, a class that reorders lists with a transition. license: MIT-style license authors: - Aaron Newton requires: - Core/Element.Dimensions - /Fx.Elements - /Element.Measure provides: [Fx.Sort] ... */ Fx.Sort = new Class({ Extends: Fx.Elements, options: { mode: 'vertical' }, initialize: function(elements, options){ this.parent(elements, options); this.elements.each(function(el){ if (el.getStyle('position') == 'static') el.setStyle('position', 'relative'); }); this.setDefaultOrder(); }, setDefaultOrder: function(){ this.currentOrder = this.elements.map(function(el, index){ return index; }); }, sort: function(){ if (!this.check(arguments)) return this; var newOrder = Array.flatten(arguments); var top = 0, left = 0, next = {}, zero = {}, vert = this.options.mode == 'vertical'; var current = this.elements.map(function(el, index){ var size = el.getComputedSize({styles: ['border', 'padding', 'margin']}); var val; if (vert){ val = { top: top, margin: size['margin-top'], height: size.totalHeight }; top += val.height - size['margin-top']; } else { val = { left: left, margin: size['margin-left'], width: size.totalWidth }; left += val.width; } var plane = vert ? 'top' : 'left'; zero[index] = {}; var start = el.getStyle(plane).toInt(); zero[index][plane] = start || 0; return val; }, this); this.set(zero); newOrder = newOrder.map(function(i){ return i.toInt(); }); if (newOrder.length != this.elements.length){ this.currentOrder.each(function(index){ if (!newOrder.contains(index)) newOrder.push(index); }); if (newOrder.length > this.elements.length) newOrder.splice(this.elements.length-1, newOrder.length - this.elements.length); } var margin = 0; top = left = 0; newOrder.each(function(item){ var newPos = {}; if (vert){ newPos.top = top - current[item].top - margin; top += current[item].height; } else { newPos.left = left - current[item].left; left += current[item].width; } margin = margin + current[item].margin; next[item]=newPos; }, this); var mapped = {}; Array.clone(newOrder).sort().each(function(index){ mapped[index] = next[index]; }); this.start(mapped); this.currentOrder = newOrder; return this; }, rearrangeDOM: function(newOrder){ newOrder = newOrder || this.currentOrder; var parent = this.elements[0].getParent(); var rearranged = []; this.elements.setStyle('opacity', 0); //move each element and store the new default order newOrder.each(function(index){ rearranged.push(this.elements[index].inject(parent).setStyles({ top: 0, left: 0 })); }, this); this.elements.setStyle('opacity', 1); this.elements = $$(rearranged); this.setDefaultOrder(); return this; }, getDefaultOrder: function(){ return this.elements.map(function(el, index){ return index; }); }, getCurrentOrder: function(){ return this.currentOrder; }, forward: function(){ return this.sort(this.getDefaultOrder()); }, backward: function(){ return this.sort(this.getDefaultOrder().reverse()); }, reverse: function(){ return this.sort(this.currentOrder.reverse()); }, sortByElements: function(elements){ return this.sort(elements.map(function(el){ return this.elements.indexOf(el); }, this)); }, swap: function(one, two){ if (typeOf(one) == 'element') one = this.elements.indexOf(one); if (typeOf(two) == 'element') two = this.elements.indexOf(two); var newOrder = Array.clone(this.currentOrder); newOrder[this.currentOrder.indexOf(one)] = two; newOrder[this.currentOrder.indexOf(two)] = one; return this.sort(newOrder); } }); /* --- script: Drag.js name: Drag description: The base Drag Class. Can be used to drag and resize Elements using mouse events. license: MIT-style license authors: - Valerio Proietti - Tom Occhinno - Jan Kassens requires: - Core/Events - Core/Options - Core/Element.Event - Core/Element.Style - Core/Element.Dimensions - /MooTools.More provides: [Drag] ... */ var Drag = new Class({ Implements: [Events, Options], options: {/* onBeforeStart: function(thisElement){}, onStart: function(thisElement, event){}, onSnap: function(thisElement){}, onDrag: function(thisElement, event){}, onCancel: function(thisElement){}, onComplete: function(thisElement, event){},*/ snap: 6, unit: 'px', grid: false, style: true, limit: false, handle: false, invert: false, preventDefault: false, stopPropagation: false, modifiers: {x: 'left', y: 'top'} }, initialize: function(){ var params = Array.link(arguments, { 'options': Type.isObject, 'element': function(obj){ return obj != null; } }); this.element = document.id(params.element); this.document = this.element.getDocument(); this.setOptions(params.options || {}); var htype = typeOf(this.options.handle); this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element; this.mouse = {'now': {}, 'pos': {}}; this.value = {'start': {}, 'now': {}}; this.selection = (Browser.ie) ? 'selectstart' : 'mousedown'; if (Browser.ie && !Drag.ondragstartFixed){ document.ondragstart = Function.from(false); Drag.ondragstartFixed = true; } this.bound = { start: this.start.bind(this), check: this.check.bind(this), drag: this.drag.bind(this), stop: this.stop.bind(this), cancel: this.cancel.bind(this), eventStop: Function.from(false) }; this.attach(); }, attach: function(){ this.handles.addEvent('mousedown', this.bound.start); return this; }, detach: function(){ this.handles.removeEvent('mousedown', this.bound.start); return this; }, start: function(event){ var options = this.options; if (event.rightClick) return; if (options.preventDefault) event.preventDefault(); if (options.stopPropagation) event.stopPropagation(); this.mouse.start = event.page; this.fireEvent('beforeStart', this.element); var limit = options.limit; this.limit = {x: [], y: []}; var z, coordinates; for (z in options.modifiers){ if (!options.modifiers[z]) continue; var style = this.element.getStyle(options.modifiers[z]); // Some browsers (IE and Opera) don't always return pixels. if (style && !style.match(/px$/)){ if (!coordinates) coordinates = this.element.getCoordinates(this.element.getOffsetParent()); style = coordinates[options.modifiers[z]]; } if (options.style) this.value.now[z] = (style || 0).toInt(); else this.value.now[z] = this.element[options.modifiers[z]]; if (options.invert) this.value.now[z] *= -1; this.mouse.pos[z] = event.page[z] - this.value.now[z]; if (limit && limit[z]){ var i = 2; while (i--){ var limitZI = limit[z][i]; if (limitZI || limitZI === 0) this.limit[z][i] = (typeof limitZI == 'function') ? limitZI() : limitZI; } } } if (typeOf(this.options.grid) == 'number') this.options.grid = { x: this.options.grid, y: this.options.grid }; var events = { mousemove: this.bound.check, mouseup: this.bound.cancel }; events[this.selection] = this.bound.eventStop; this.document.addEvents(events); }, check: function(event){ if (this.options.preventDefault) event.preventDefault(); var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2))); if (distance > this.options.snap){ this.cancel(); this.document.addEvents({ mousemove: this.bound.drag, mouseup: this.bound.stop }); this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element); } }, drag: function(event){ var options = this.options; if (options.preventDefault) event.preventDefault(); this.mouse.now = event.page; for (var z in options.modifiers){ if (!options.modifiers[z]) continue; this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z]; if (options.invert) this.value.now[z] *= -1; if (options.limit && this.limit[z]){ if ((this.limit[z][1] || this.limit[z][1] === 0) && (this.value.now[z] > this.limit[z][1])){ this.value.now[z] = this.limit[z][1]; } else if ((this.limit[z][0] || this.limit[z][0] === 0) && (this.value.now[z] < this.limit[z][0])){ this.value.now[z] = this.limit[z][0]; } } if (options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % options.grid[z]); if (options.style) this.element.setStyle(options.modifiers[z], this.value.now[z] + options.unit); else this.element[options.modifiers[z]] = this.value.now[z]; } this.fireEvent('drag', [this.element, event]); }, cancel: function(event){ this.document.removeEvents({ mousemove: this.bound.check, mouseup: this.bound.cancel }); if (event){ this.document.removeEvent(this.selection, this.bound.eventStop); this.fireEvent('cancel', this.element); } }, stop: function(event){ var events = { mousemove: this.bound.drag, mouseup: this.bound.stop }; events[this.selection] = this.bound.eventStop; this.document.removeEvents(events); if (event) this.fireEvent('complete', [this.element, event]); } }); Element.implement({ makeResizable: function(options){ var drag = new Drag(this, Object.merge({ modifiers: { x: 'width', y: 'height' } }, options)); this.store('resizer', drag); return drag.addEvent('drag', function(){ this.fireEvent('resize', drag); }.bind(this)); } }); /* --- script: Drag.Move.js name: Drag.Move description: A Drag extension that provides support for the constraining of draggables to containers and droppables. license: MIT-style license authors: - Valerio Proietti - Tom Occhinno - Jan Kassens - Aaron Newton - Scott Kyle requires: - Core/Element.Dimensions - /Drag provides: [Drag.Move] ... */ Drag.Move = new Class({ Extends: Drag, options: {/* onEnter: function(thisElement, overed){}, onLeave: function(thisElement, overed){}, onDrop: function(thisElement, overed, event){},*/ droppables: [], container: false, precalculate: false, includeMargins: true, checkDroppables: true }, initialize: function(element, options){ this.parent(element, options); element = this.element; this.droppables = $$(this.options.droppables); this.container = document.id(this.options.container); if (this.container && typeOf(this.container) != 'element') this.container = document.id(this.container.getDocument().body); if (this.options.style){ if (this.options.modifiers.x == 'left' && this.options.modifiers.y == 'top'){ var parent = element.getOffsetParent(), styles = element.getStyles('left', 'top'); if (parent && (styles.left == 'auto' || styles.top == 'auto')){ element.setPosition(element.getPosition(parent)); } } if (element.getStyle('position') == 'static') element.setStyle('position', 'absolute'); } this.addEvent('start', this.checkDroppables, true); this.overed = null; }, start: function(event){ if (this.container) this.options.limit = this.calculateLimit(); if (this.options.precalculate){ this.positions = this.droppables.map(function(el){ return el.getCoordinates(); }); } this.parent(event); }, calculateLimit: function(){ var element = this.element, container = this.container, offsetParent = document.id(element.getOffsetParent()) || document.body, containerCoordinates = container.getCoordinates(offsetParent), elementMargin = {}, elementBorder = {}, containerMargin = {}, containerBorder = {}, offsetParentPadding = {}; ['top', 'right', 'bottom', 'left'].each(function(pad){ elementMargin[pad] = element.getStyle('margin-' + pad).toInt(); elementBorder[pad] = element.getStyle('border-' + pad).toInt(); containerMargin[pad] = container.getStyle('margin-' + pad).toInt(); containerBorder[pad] = container.getStyle('border-' + pad).toInt(); offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt(); }, this); var width = element.offsetWidth + elementMargin.left + elementMargin.right, height = element.offsetHeight + elementMargin.top + elementMargin.bottom, left = 0, top = 0, right = containerCoordinates.right - containerBorder.right - width, bottom = containerCoordinates.bottom - containerBorder.bottom - height; if (this.options.includeMargins){ left += elementMargin.left; top += elementMargin.top; } else { right += elementMargin.right; bottom += elementMargin.bottom; } if (element.getStyle('position') == 'relative'){ var coords = element.getCoordinates(offsetParent); coords.left -= element.getStyle('left').toInt(); coords.top -= element.getStyle('top').toInt(); left -= coords.left; top -= coords.top; if (container.getStyle('position') != 'relative'){ left += containerBorder.left; top += containerBorder.top; } right += elementMargin.left - coords.left; bottom += elementMargin.top - coords.top; if (container != offsetParent){ left += containerMargin.left + offsetParentPadding.left; top += ((Browser.ie6 || Browser.ie7) ? 0 : containerMargin.top) + offsetParentPadding.top; } } else { left -= elementMargin.left; top -= elementMargin.top; if (container != offsetParent){ left += containerCoordinates.left + containerBorder.left; top += containerCoordinates.top + containerBorder.top; } } return { x: [left, right], y: [top, bottom] }; }, getDroppableCoordinates: function(element){ var position = element.getCoordinates(); if (element.getStyle('position') == 'fixed'){ var scroll = window.getScroll(); position.left += scroll.x; position.right += scroll.x; position.top += scroll.y; position.bottom += scroll.y; } return position; }, checkDroppables: function(){ var overed = this.droppables.filter(function(el, i){ el = this.positions ? this.positions[i] : this.getDroppableCoordinates(el); var now = this.mouse.now; return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top); }, this).getLast(); if (this.overed != overed){ if (this.overed) this.fireEvent('leave', [this.element, this.overed]); if (overed) this.fireEvent('enter', [this.element, overed]); this.overed = overed; } }, drag: function(event){ this.parent(event); if (this.options.checkDroppables && this.droppables.length) this.checkDroppables(); }, stop: function(event){ this.checkDroppables(); this.fireEvent('drop', [this.element, this.overed, event]); this.overed = null; return this.parent(event); } }); Element.implement({ makeDraggable: function(options){ var drag = new Drag.Move(this, options); this.store('dragger', drag); return drag; } }); /* --- script: Slider.js name: Slider description: Class for creating horizontal and vertical slider controls. license: MIT-style license authors: - Valerio Proietti requires: - Core/Element.Dimensions - /Class.Binds - /Drag - /Element.Measure provides: [Slider] ... */ var Slider = new Class({ Implements: [Events, Options], Binds: ['clickedElement', 'draggedKnob', 'scrolledElement'], options: {/* onTick: function(intPosition){}, onChange: function(intStep){}, onComplete: function(strStep){},*/ onTick: function(position){ this.setKnobPosition(position); }, initialStep: 0, snap: false, offset: 0, range: false, wheel: false, steps: 100, mode: 'horizontal' }, initialize: function(element, knob, options){ this.setOptions(options); options = this.options; this.element = document.id(element); knob = this.knob = document.id(knob); this.previousChange = this.previousEnd = this.step = -1; var limit = {}, modifiers = {x: false, y: false}; switch (options.mode){ case 'vertical': this.axis = 'y'; this.property = 'top'; this.offset = 'offsetHeight'; break; case 'horizontal': this.axis = 'x'; this.property = 'left'; this.offset = 'offsetWidth'; } this.setSliderDimensions(); this.setRange(options.range); if (knob.getStyle('position') == 'static') knob.setStyle('position', 'relative'); knob.setStyle(this.property, -options.offset); modifiers[this.axis] = this.property; limit[this.axis] = [-options.offset, this.full - options.offset]; var dragOptions = { snap: 0, limit: limit, modifiers: modifiers, onDrag: this.draggedKnob, onStart: this.draggedKnob, onBeforeStart: (function(){ this.isDragging = true; }).bind(this), onCancel: function(){ this.isDragging = false; }.bind(this), onComplete: function(){ this.isDragging = false; this.draggedKnob(); this.end(); }.bind(this) }; if (options.snap) this.setSnap(dragOptions); this.drag = new Drag(knob, dragOptions); this.attach(); if (options.initialStep != null) this.set(options.initialStep); }, attach: function(){ this.element.addEvent('mousedown', this.clickedElement); if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement); this.drag.attach(); return this; }, detach: function(){ this.element.removeEvent('mousedown', this.clickedElement) .removeEvent('mousewheel', this.scrolledElement); this.drag.detach(); return this; }, autosize: function(){ this.setSliderDimensions() .setKnobPosition(this.toPosition(this.step)); this.drag.options.limit[this.axis] = [-this.options.offset, this.full - this.options.offset]; if (this.options.snap) this.setSnap(); return this; }, setSnap: function(options){ if (!options) options = this.drag.options; options.grid = Math.ceil(this.stepWidth); options.limit[this.axis][1] = this.full; return this; }, setKnobPosition: function(position){ if (this.options.snap) position = this.toPosition(this.step); this.knob.setStyle(this.property, position); return this; }, setSliderDimensions: function(){ this.full = this.element.measure(function(){ this.half = this.knob[this.offset] / 2; return this.element[this.offset] - this.knob[this.offset] + (this.options.offset * 2); }.bind(this)); return this; }, set: function(step){ if (!((this.range > 0) ^ (step < this.min))) step = this.min; if (!((this.range > 0) ^ (step > this.max))) step = this.max; this.step = Math.round(step); return this.checkStep() .fireEvent('tick', this.toPosition(this.step)) .end(); }, setRange: function(range, pos){ this.min = Array.pick([range[0], 0]); this.max = Array.pick([range[1], this.options.steps]); this.range = this.max - this.min; this.steps = this.options.steps || this.full; this.stepSize = Math.abs(this.range) / this.steps; this.stepWidth = this.stepSize * this.full / Math.abs(this.range); if (range) this.set(Array.pick([pos, this.step]).floor(this.min).max(this.max)); return this; }, clickedElement: function(event){ if (this.isDragging || event.target == this.knob) return; var dir = this.range < 0 ? -1 : 1, position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half; position = position.limit(-this.options.offset, this.full - this.options.offset); this.step = Math.round(this.min + dir * this.toStep(position)); this.checkStep() .fireEvent('tick', position) .end(); }, scrolledElement: function(event){ var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0); this.set(this.step + (mode ? -1 : 1) * this.stepSize); event.stop(); }, draggedKnob: function(){ var dir = this.range < 0 ? -1 : 1, position = this.drag.value.now[this.axis]; position = position.limit(-this.options.offset, this.full -this.options.offset); this.step = Math.round(this.min + dir * this.toStep(position)); this.checkStep(); }, checkStep: function(){ var step = this.step; if (this.previousChange != step){ this.previousChange = step; this.fireEvent('change', step); } return this; }, end: function(){ var step = this.step; if (this.previousEnd !== step){ this.previousEnd = step; this.fireEvent('complete', step + ''); } return this; }, toStep: function(position){ var step = (position + this.options.offset) * this.stepSize / this.full * this.steps; return this.options.steps ? Math.round(step -= step % this.stepSize) : step; }, toPosition: function(step){ return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset; } }); /* --- script: Sortables.js name: Sortables description: Class for creating a drag and drop sorting interface for lists of items. license: MIT-style license authors: - Tom Occhino requires: - Core/Fx.Morph - /Drag.Move provides: [Sortables] ... */ var Sortables = new Class({ Implements: [Events, Options], options: {/* onSort: function(element, clone){}, onStart: function(element, clone){}, onComplete: function(element){},*/ opacity: 1, clone: false, revert: false, handle: false, dragOptions: {} }, initialize: function(lists, options){ this.setOptions(options); this.elements = []; this.lists = []; this.idle = true; this.addLists($$(document.id(lists) || lists)); if (!this.options.clone) this.options.revert = false; if (this.options.revert) this.effect = new Fx.Morph(null, Object.merge({ duration: 250, link: 'cancel' }, this.options.revert)); }, attach: function(){ this.addLists(this.lists); return this; }, detach: function(){ this.lists = this.removeLists(this.lists); return this; }, addItems: function(){ Array.flatten(arguments).each(function(element){ this.elements.push(element); var start = element.retrieve('sortables:start', function(event){ this.start.call(this, event, element); }.bind(this)); (this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start); }, this); return this; }, addLists: function(){ Array.flatten(arguments).each(function(list){ this.lists.include(list); this.addItems(list.getChildren()); }, this); return this; }, removeItems: function(){ return $$(Array.flatten(arguments).map(function(element){ this.elements.erase(element); var start = element.retrieve('sortables:start'); (this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start); return element; }, this)); }, removeLists: function(){ return $$(Array.flatten(arguments).map(function(list){ this.lists.erase(list); this.removeItems(list.getChildren()); return list; }, this)); }, getClone: function(event, element){ if (!this.options.clone) return new Element(element.tagName).inject(document.body); if (typeOf(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list); var clone = element.clone(true).setStyles({ margin: 0, position: 'absolute', visibility: 'hidden', width: element.getStyle('width') }).addEvent('mousedown', function(event){ element.fireEvent('mousedown', event); }); //prevent the duplicated radio inputs from unchecking the real one if (clone.get('html').test('radio')){ clone.getElements('input[type=radio]').each(function(input, i){ input.set('name', 'clone_' + i); if (input.get('checked')) element.getElements('input[type=radio]')[i].set('checked', true); }); } return clone.inject(this.list).setPosition(element.getPosition(element.getOffsetParent())); }, getDroppables: function(){ var droppables = this.list.getChildren().erase(this.clone).erase(this.element); if (!this.options.constrain) droppables.append(this.lists).erase(this.list); return droppables; }, insert: function(dragging, element){ var where = 'inside'; if (this.lists.contains(element)){ this.list = element; this.drag.droppables = this.getDroppables(); } else { where = this.element.getAllPrevious().contains(element) ? 'before' : 'after'; } this.element.inject(element, where); this.fireEvent('sort', [this.element, this.clone]); }, start: function(event, element){ if ( !this.idle || event.rightClick || ['button', 'input', 'a'].contains(event.target.get('tag')) ) return; this.idle = false; this.element = element; this.opacity = element.get('opacity'); this.list = element.getParent(); this.clone = this.getClone(event, element); this.drag = new Drag.Move(this.clone, Object.merge({ droppables: this.getDroppables() }, this.options.dragOptions)).addEvents({ onSnap: function(){ event.stop(); this.clone.setStyle('visibility', 'visible'); this.element.set('opacity', this.options.opacity || 0); this.fireEvent('start', [this.element, this.clone]); }.bind(this), onEnter: this.insert.bind(this), onCancel: this.end.bind(this), onComplete: this.end.bind(this) }); this.clone.inject(this.element, 'before'); this.drag.start(event); }, end: function(){ this.drag.detach(); this.element.set('opacity', this.opacity); if (this.effect){ var dim = this.element.getStyles('width', 'height'), clone = this.clone, pos = clone.computePosition(this.element.getPosition(this.clone.getOffsetParent())); var destroy = function(){ this.removeEvent('cancel', destroy); clone.destroy(); }; this.effect.element = clone; this.effect.start({ top: pos.top, left: pos.left, width: dim.width, height: dim.height, opacity: 0.25 }).addEvent('cancel', destroy).chain(destroy); } else { this.clone.destroy(); } this.reset(); }, reset: function(){ this.idle = true; this.fireEvent('complete', this.element); }, serialize: function(){ var params = Array.link(arguments, { modifier: Type.isFunction, index: function(obj){ return obj != null; } }); var serial = this.lists.map(function(list){ return list.getChildren().map(params.modifier || function(element){ return element.get('id'); }, this); }, this); var index = params.index; if (this.lists.length == 1) index = 0; return (index || index === 0) && index >= 0 && index < this.lists.length ? serial[index] : serial; } }); /* --- script: Request.JSONP.js name: Request.JSONP description: Defines Request.JSONP, a class for cross domain javascript via script injection. license: MIT-style license authors: - Aaron Newton - Guillermo Rauch - Arian Stolwijk requires: - Core/Element - Core/Request - MooTools.More provides: [Request.JSONP] ... */ Request.JSONP = new Class({ Implements: [Chain, Events, Options], options: {/* onRequest: function(src, scriptElement){}, onComplete: function(data){}, onSuccess: function(data){}, onCancel: function(){}, onTimeout: function(){}, onError: function(){}, */ onRequest: function(src){ if (this.options.log && window.console && console.log){ console.log('JSONP retrieving script with url:' + src); } }, onError: function(src){ if (this.options.log && window.console && console.warn){ console.warn('JSONP '+ src +' will fail in Internet Explorer, which enforces a 2083 bytes length limit on URIs'); } }, url: '', callbackKey: 'callback', injectScript: document.head, data: '', link: 'ignore', timeout: 0, log: false }, initialize: function(options){ this.setOptions(options); }, send: function(options){ if (!Request.prototype.check.call(this, options)) return this; this.running = true; var type = typeOf(options); if (type == 'string' || type == 'element') options = {data: options}; options = Object.merge(this.options, options || {}); var data = options.data; switch (typeOf(data)){ case 'element': data = document.id(data).toQueryString(); break; case 'object': case 'hash': data = Object.toQueryString(data); } var index = this.index = Request.JSONP.counter++; var src = options.url + (options.url.test('\\?') ? '&' :'?') + (options.callbackKey) + '=Request.JSONP.request_map.request_'+ index + (data ? '&' + data : ''); if (src.length > 2083) this.fireEvent('error', src); Request.JSONP.request_map['request_' + index] = function(){ this.success(arguments, index); }.bind(this); var script = this.getScript(src).inject(options.injectScript); this.fireEvent('request', [src, script]); if (options.timeout) this.timeout.delay(options.timeout, this); return this; }, getScript: function(src){ if (!this.script) this.script = new Element('script', { type: 'text/javascript', async: true, src: src }); return this.script; }, success: function(args, index){ if (!this.running) return; this.clear() .fireEvent('complete', args).fireEvent('success', args) .callChain(); }, cancel: function(){ if (this.running) this.clear().fireEvent('cancel'); return this; }, isRunning: function(){ return !!this.running; }, clear: function(){ this.running = false; if (this.script){ this.script.destroy(); this.script = null; } return this; }, timeout: function(){ if (this.running){ this.running = false; this.fireEvent('timeout', [this.script.get('src'), this.script]).fireEvent('failure').cancel(); } return this; } }); Request.JSONP.counter = 0; Request.JSONP.request_map = {}; /* --- script: Request.Queue.js name: Request.Queue description: Controls several instances of Request and its variants to run only one request at a time. license: MIT-style license authors: - Aaron Newton requires: - Core/Element - Core/Request - /Class.Binds provides: [Request.Queue] ... */ Request.Queue = new Class({ Implements: [Options, Events], Binds: ['attach', 'request', 'complete', 'cancel', 'success', 'failure', 'exception'], options: {/* onRequest: function(argsPassedToOnRequest){}, onSuccess: function(argsPassedToOnSuccess){}, onComplete: function(argsPassedToOnComplete){}, onCancel: function(argsPassedToOnCancel){}, onException: function(argsPassedToOnException){}, onFailure: function(argsPassedToOnFailure){}, onEnd: function(){}, */ stopOnFailure: true, autoAdvance: true, concurrent: 1, requests: {} }, initialize: function(options){ var requests; if (options){ requests = options.requests; delete options.requests; } this.setOptions(options); this.requests = {}; this.queue = []; this.reqBinders = {}; if (requests) this.addRequests(requests); }, addRequest: function(name, request){ this.requests[name] = request; this.attach(name, request); return this; }, addRequests: function(obj){ Object.each(obj, function(req, name){ this.addRequest(name, req); }, this); return this; }, getName: function(req){ return Object.keyOf(this.requests, req); }, attach: function(name, req){ if (req._groupSend) return this; ['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){ if (!this.reqBinders[name]) this.reqBinders[name] = {}; this.reqBinders[name][evt] = function(){ this['on' + evt.capitalize()].apply(this, [name, req].append(arguments)); }.bind(this); req.addEvent(evt, this.reqBinders[name][evt]); }, this); req._groupSend = req.send; req.send = function(options){ this.send(name, options); return req; }.bind(this); return this; }, removeRequest: function(req){ var name = typeOf(req) == 'object' ? this.getName(req) : req; if (!name && typeOf(name) != 'string') return this; req = this.requests[name]; if (!req) return this; ['request', 'complete', 'cancel', 'success', 'failure', 'exception'].each(function(evt){ req.removeEvent(evt, this.reqBinders[name][evt]); }, this); req.send = req._groupSend; delete req._groupSend; return this; }, getRunning: function(){ return Object.filter(this.requests, function(r){ return r.running; }); }, isRunning: function(){ return !!(Object.keys(this.getRunning()).length); }, send: function(name, options){ var q = function(){ this.requests[name]._groupSend(options); this.queue.erase(q); }.bind(this); q.name = name; if (Object.keys(this.getRunning()).length >= this.options.concurrent || (this.error && this.options.stopOnFailure)) this.queue.push(q); else q(); return this; }, hasNext: function(name){ return (!name) ? !!this.queue.length : !!this.queue.filter(function(q){ return q.name == name; }).length; }, resume: function(){ this.error = false; (this.options.concurrent - Object.keys(this.getRunning()).length).times(this.runNext, this); return this; }, runNext: function(name){ if (!this.queue.length) return this; if (!name){ this.queue[0](); } else { var found; this.queue.each(function(q){ if (!found && q.name == name){ found = true; q(); } }); } return this; }, runAll: function(){ this.queue.each(function(q){ q(); }); return this; }, clear: function(name){ if (!name){ this.queue.empty(); } else { this.queue = this.queue.map(function(q){ if (q.name != name) return q; else return false; }).filter(function(q){ return q; }); } return this; }, cancel: function(name){ this.requests[name].cancel(); return this; }, onRequest: function(){ this.fireEvent('request', arguments); }, onComplete: function(){ this.fireEvent('complete', arguments); if (!this.queue.length) this.fireEvent('end'); }, onCancel: function(){ if (this.options.autoAdvance && !this.error) this.runNext(); this.fireEvent('cancel', arguments); }, onSuccess: function(){ if (this.options.autoAdvance && !this.error) this.runNext(); this.fireEvent('success', arguments); }, onFailure: function(){ this.error = true; if (!this.options.stopOnFailure && this.options.autoAdvance) this.runNext(); this.fireEvent('failure', arguments); }, onException: function(){ this.error = true; if (!this.options.stopOnFailure && this.options.autoAdvance) this.runNext(); this.fireEvent('exception', arguments); } }); /* --- script: Request.Periodical.js name: Request.Periodical description: Requests the same URL to pull data from a server but increases the intervals if no data is returned to reduce the load license: MIT-style license authors: - Christoph Pojer requires: - Core/Request - /MooTools.More provides: [Request.Periodical] ... */ Request.implement({ options: { initialDelay: 5000, delay: 5000, limit: 60000 }, startTimer: function(data){ var fn = function(){ if (!this.running) this.send({data: data}); }; this.lastDelay = this.options.initialDelay; this.timer = fn.delay(this.lastDelay, this); this.completeCheck = function(response){ clearTimeout(this.timer); this.lastDelay = (response) ? this.options.delay : (this.lastDelay + this.options.delay).min(this.options.limit); this.timer = fn.delay(this.lastDelay, this); }; return this.addEvent('complete', this.completeCheck); }, stopTimer: function(){ clearTimeout(this.timer); return this.removeEvent('complete', this.completeCheck); } }); /* --- script: Assets.js name: Assets description: Provides methods to dynamically load JavaScript, CSS, and Image files into the document. license: MIT-style license authors: - Valerio Proietti requires: - Core/Element.Event - /MooTools.More provides: [Assets] ... */ var Asset = { javascript: function(source, properties){ if (!properties) properties = {}; var script = new Element('script', {src: source, type: 'text/javascript'}), doc = properties.document || document, loaded = 0, loadEvent = properties.onload || properties.onLoad; var load = loadEvent ? function(){ // make sure we only call the event once if (++loaded == 1) loadEvent.call(this); } : function(){}; delete properties.onload; delete properties.onLoad; delete properties.document; return script.addEvents({ load: load, readystatechange: function(){ if (['loaded', 'complete'].contains(this.readyState)) load.call(this); } }).set(properties).inject(doc.head); }, css: function(source, properties){ if (!properties) properties = {}; var link = new Element('link', { rel: 'stylesheet', media: 'screen', type: 'text/css', href: source }); var load = properties.onload || properties.onLoad, doc = properties.document || document; delete properties.onload; delete properties.onLoad; delete properties.document; if (load) link.addEvent('load', load); return link.set(properties).inject(doc.head); }, image: function(source, properties){ if (!properties) properties = {}; var image = new Image(), element = document.id(image) || new Element('img'); ['load', 'abort', 'error'].each(function(name){ var type = 'on' + name, cap = 'on' + name.capitalize(), event = properties[type] || properties[cap] || function(){}; delete properties[cap]; delete properties[type]; image[type] = function(){ if (!image) return; if (!element.parentNode){ element.width = image.width; element.height = image.height; } image = image.onload = image.onabort = image.onerror = null; event.delay(1, element, element); element.fireEvent(name, element, 1); }; }); image.src = element.src = source; if (image && image.complete) image.onload.delay(1); return element.set(properties); }, images: function(sources, options){ sources = Array.from(sources); var fn = function(){}, counter = 0; options = Object.merge({ onComplete: fn, onProgress: fn, onError: fn, properties: {} }, options); return new Elements(sources.map(function(source, index){ return Asset.image(source, Object.append(options.properties, { onload: function(){ counter++; options.onProgress.call(this, counter, index, source); if (counter == sources.length) options.onComplete(); }, onerror: function(){ counter++; options.onError.call(this, counter, index, source); if (counter == sources.length) options.onComplete(); } })); })); } }; /* --- script: Color.js name: Color description: Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa. license: MIT-style license authors: - Valerio Proietti requires: - Core/Array - Core/String - Core/Number - Core/Hash - Core/Function - MooTools.More provides: [Color] ... */ (function(){ var Color = this.Color = new Type('Color', function(color, type){ if (arguments.length >= 3){ type = 'rgb'; color = Array.slice(arguments, 0, 3); } else if (typeof color == 'string'){ if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true); else if (color.match(/hsb/)) color = color.hsbToRgb(); else color = color.hexToRgb(true); } type = type || 'rgb'; switch (type){ case 'hsb': var old = color; color = color.hsbToRgb(); color.hsb = old; break; case 'hex': color = color.hexToRgb(true); break; } color.rgb = color.slice(0, 3); color.hsb = color.hsb || color.rgbToHsb(); color.hex = color.rgbToHex(); return Object.append(color, this); }); Color.implement({ mix: function(){ var colors = Array.slice(arguments); var alpha = (typeOf(colors.getLast()) == 'number') ? colors.pop() : 50; var rgb = this.slice(); colors.each(function(color){ color = new Color(color); for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha)); }); return new Color(rgb, 'rgb'); }, invert: function(){ return new Color(this.map(function(value){ return 255 - value; })); }, setHue: function(value){ return new Color([value, this.hsb[1], this.hsb[2]], 'hsb'); }, setSaturation: function(percent){ return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb'); }, setBrightness: function(percent){ return new Color([this.hsb[0], this.hsb[1], percent], 'hsb'); } }); this.$RGB = function(r, g, b){ return new Color([r, g, b], 'rgb'); }; this.$HSB = function(h, s, b){ return new Color([h, s, b], 'hsb'); }; this.$HEX = function(hex){ return new Color(hex, 'hex'); }; Array.implement({ rgbToHsb: function(){ var red = this[0], green = this[1], blue = this[2], hue = 0; var max = Math.max(red, green, blue), min = Math.min(red, green, blue); var delta = max - min; var brightness = max / 255, saturation = (max != 0) ? delta / max : 0; if (saturation != 0){ var rr = (max - red) / delta; var gr = (max - green) / delta; var br = (max - blue) / delta; if (red == max) hue = br - gr; else if (green == max) hue = 2 + rr - br; else hue = 4 + gr - rr; hue /= 6; if (hue < 0) hue++; } return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)]; }, hsbToRgb: function(){ var br = Math.round(this[2] / 100 * 255); if (this[1] == 0){ return [br, br, br]; } else { var hue = this[0] % 360; var f = hue % 60; var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255); var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255); var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255); switch (Math.floor(hue / 60)){ case 0: return [br, t, p]; case 1: return [q, br, p]; case 2: return [p, br, t]; case 3: return [p, q, br]; case 4: return [t, p, br]; case 5: return [br, p, q]; } } return false; } }); String.implement({ rgbToHsb: function(){ var rgb = this.match(/\d{1,3}/g); return (rgb) ? rgb.rgbToHsb() : null; }, hsbToRgb: function(){ var hsb = this.match(/\d{1,3}/g); return (hsb) ? hsb.hsbToRgb() : null; } }); })(); /* --- script: Group.js name: Group description: Class for monitoring collections of events license: MIT-style license authors: - Valerio Proietti requires: - Core/Events - /MooTools.More provides: [Group] ... */ (function(){ this.Group = new Class({ initialize: function(){ this.instances = Array.flatten(arguments); this.events = {}; this.checker = {}; }, addEvent: function(type, fn){ this.checker[type] = this.checker[type] || {}; this.events[type] = this.events[type] || []; if (this.events[type].contains(fn)) return false; else this.events[type].push(fn); this.instances.each(function(instance, i){ instance.addEvent(type, this.check.pass([type, instance, i], this)); }, this); return this; }, check: function(type, instance, i){ this.checker[type][i] = true; var every = this.instances.every(function(current, j){ return this.checker[type][j] || false; }, this); if (!every) return; this.checker[type] = {}; this.events[type].each(function(event){ event.call(this, this.instances, instance); }, this); } }); })(); /* --- script: Hash.Cookie.js name: Hash.Cookie description: Class for creating, reading, and deleting Cookies in JSON format. license: MIT-style license authors: - Valerio Proietti - Aaron Newton requires: - Core/Cookie - Core/JSON - /MooTools.More - /Hash provides: [Hash.Cookie] ... */ Hash.Cookie = new Class({ Extends: Cookie, options: { autoSave: true }, initialize: function(name, options){ this.parent(name, options); this.load(); }, save: function(){ var value = JSON.encode(this.hash); if (!value || value.length > 4096) return false; //cookie would be truncated! if (value == '{}') this.dispose(); else this.write(value); return true; }, load: function(){ this.hash = new Hash(JSON.decode(this.read(), true)); return this; } }); Hash.each(Hash.prototype, function(method, name){ if (typeof method == 'function') Hash.Cookie.implement(name, function(){ var value = method.apply(this.hash, arguments); if (this.options.autoSave) this.save(); return value; }); }); /* --- name: Table description: LUA-Style table implementation. license: MIT-style license authors: - Valerio Proietti requires: [Core/Array] provides: [Table] ... */ (function(){ var Table = this.Table = function(){ this.length = 0; var keys = [], values = []; this.set = function(key, value){ var index = keys.indexOf(key); if (index == -1){ var length = keys.length; keys[length] = key; values[length] = value; this.length++; } else { values[index] = value; } return this; }; this.get = function(key){ var index = keys.indexOf(key); return (index == -1) ? null : values[index]; }; this.erase = function(key){ var index = keys.indexOf(key); if (index != -1){ this.length--; keys.splice(index, 1); return values.splice(index, 1)[0]; } return null; }; this.each = this.forEach = function(fn, bind){ for (var i = 0, l = this.length; i < l; i++) fn.call(bind, keys[i], values[i], this); }; }; if (this.Type) new Type('Table', Table); })(); /* --- script: HtmlTable.js name: HtmlTable description: Builds table elements with methods to add rows. license: MIT-style license authors: - Aaron Newton requires: - Core/Options - Core/Events - /Class.Occlude provides: [HtmlTable] ... */ var HtmlTable = new Class({ Implements: [Options, Events, Class.Occlude], options: { properties: { cellpadding: 0, cellspacing: 0, border: 0 }, rows: [], headers: [], footers: [] }, property: 'HtmlTable', initialize: function(){ var params = Array.link(arguments, {options: Type.isObject, table: Type.isElement, id: Type.isString}); this.setOptions(params.options); if (!params.table && params.id) params.table = document.id(params.id); this.element = params.table || new Element('table', this.options.properties); if (this.occlude()) return this.occluded; this.build(); }, build: function(){ this.element.store('HtmlTable', this); this.body = document.id(this.element.tBodies[0]) || new Element('tbody').inject(this.element); $$(this.body.rows); if (this.options.headers.length) this.setHeaders(this.options.headers); else this.thead = document.id(this.element.tHead); if (this.thead) this.head = this.getHead(); if (this.options.footers.length) this.setFooters(this.options.footers); this.tfoot = document.id(this.element.tFoot); if (this.tfoot) this.foot = document.id(this.tfoot.rows[0]); this.options.rows.each(function(row){ this.push(row); }, this); }, toElement: function(){ return this.element; }, empty: function(){ this.body.empty(); return this; }, set: function(what, items){ var target = (what == 'headers') ? 'tHead' : 'tFoot', lower = target.toLowerCase(); this[lower] = (document.id(this.element[target]) || new Element(lower).inject(this.element, 'top')).empty(); var data = this.push(items, {}, this[lower], what == 'headers' ? 'th' : 'td'); if (what == 'headers') this.head = this.getHead(); else this.foot = this.getHead(); return data; }, getHead: function(){ var rows = this.thead.rows; return rows.length > 1 ? $$(rows) : rows.length ? document.id(rows[0]) : false; }, setHeaders: function(headers){ this.set('headers', headers); return this; }, setFooters: function(footers){ this.set('footers', footers); return this; }, push: function(row, rowProperties, target, tag, where){ if (typeOf(row) == 'element' && row.get('tag') == 'tr'){ row.inject(target || this.body, where); return { tr: row, tds: row.getChildren('td') }; } var tds = row.map(function(data){ var td = new Element(tag || 'td', data ? data.properties : {}), content = (data ? data.content : '') || data, type = typeOf(content); if (['element', 'array', 'collection', 'elements'].contains(type)) td.adopt(content); else td.set('html', content); return td; }); return { tr: new Element('tr', rowProperties).inject(target || this.body, where).adopt(tds), tds: tds }; } }); ['adopt', 'inject', 'wraps', 'grab', 'replaces', 'dispose'].each(function(method){ HtmlTable.implement(method, function(){ this.element[method].apply(this.element, arguments); return this; }); }); /* --- script: HtmlTable.Zebra.js name: HtmlTable.Zebra description: Builds a stripy table with methods to add rows. license: MIT-style license authors: - Harald Kirschner - Aaron Newton requires: - /HtmlTable - /Class.refactor provides: [HtmlTable.Zebra] ... */ HtmlTable = Class.refactor(HtmlTable, { options: { classZebra: 'table-tr-odd', zebra: true }, initialize: function(){ this.previous.apply(this, arguments); if (this.occluded) return this.occluded; if (this.options.zebra) this.updateZebras(); }, updateZebras: function(){ Array.each(this.body.rows, this.zebra, this); }, setRowStyle: function(row, i){ if (this.previous) this.previous(row, i); this.zebra(row, i); }, zebra: function(row, i){ return row[((i % 2) ? 'remove' : 'add')+'Class'](this.options.classZebra); }, push: function(){ var pushed = this.previous.apply(this, arguments); if (this.options.zebra) this.updateZebras(); return pushed; } }); /* --- script: HtmlTable.Sort.js name: HtmlTable.Sort description: Builds a stripy, sortable table with methods to add rows. license: MIT-style license authors: - Harald Kirschner - Aaron Newton - Jacob Thornton requires: - Core/Hash - /HtmlTable - /Class.refactor - /Element.Delegation - /String.Extras - /Date provides: [HtmlTable.Sort] ... */ HtmlTable = Class.refactor(HtmlTable, { options: {/* onSort: function(){}, */ sortIndex: 0, sortReverse: false, parsers: [], defaultParser: 'string', classSortable: 'table-sortable', classHeadSort: 'table-th-sort', classHeadSortRev: 'table-th-sort-rev', classNoSort: 'table-th-nosort', classGroupHead: 'table-tr-group-head', classGroup: 'table-tr-group', classCellSort: 'table-td-sort', classSortSpan: 'table-th-sort-span', sortable: false, thSelector: 'th' }, initialize: function (){ this.previous.apply(this, arguments); if (this.occluded) return this.occluded; this.sorted = {index: null, dir: 1}; this.bound = { headClick: this.headClick.bind(this) }; this.sortSpans = new Elements(); if (this.options.sortable){ this.enableSort(); if (this.options.sortIndex != null) this.sort(this.options.sortIndex, this.options.sortReverse); } }, attachSorts: function(attach){ this.detachSorts(); if (attach !== false) this.element.addEvent('click:relay(' + this.options.thSelector + ')', this.bound.headClick); }, detachSorts: function(){ this.element.removeEvents('click:relay(' + this.options.thSelector + ')'); }, setHeaders: function(){ this.previous.apply(this, arguments); if (this.sortEnabled) this.setParsers(); }, setParsers: function(){ this.parsers = this.detectParsers(); }, detectParsers: function(){ return this.head && this.head.getElements(this.options.thSelector).flatten().map(this.detectParser, this); }, detectParser: function(cell, index){ if (cell.hasClass(this.options.classNoSort) || cell.retrieve('htmltable-parser')) return cell.retrieve('htmltable-parser'); var thDiv = new Element('div'); thDiv.adopt(cell.childNodes).inject(cell); var sortSpan = new Element('span', {'class': this.options.classSortSpan}).inject(thDiv, 'top'); this.sortSpans.push(sortSpan); var parser = this.options.parsers[index], rows = this.body.rows, cancel; switch (typeOf(parser)){ case 'function': parser = {convert: parser}; cancel = true; break; case 'string': parser = parser; cancel = true; break; } if (!cancel){ HtmlTable.ParserPriority.some(function(parserName){ var current = HtmlTable.Parsers[parserName], match = current.match; if (!match) return false; for (var i = 0, j = rows.length; i < j; i++){ var cell = document.id(rows[i].cells[index]), text = cell ? cell.get('html').clean() : ''; if (text && match.test(text)){ parser = current; return true; } } }); } if (!parser) parser = this.options.defaultParser; cell.store('htmltable-parser', parser); return parser; }, headClick: function(event, el){ if (!this.head || el.hasClass(this.options.classNoSort)) return; return this.sort(Array.indexOf(this.head.getElements(this.options.thSelector).flatten(), el) % this.body.rows[0].cells.length); }, serialize: function() { var previousSerialization = this.previous.apply(this, arguments) || {}; if (this.options.sortable) { previousSerialization.sortIndex = this.sorted.index; previousSerialization.sortReverse = this.sorted.reverse; } return previousSerialization; }, restore: function(tableState) { if(this.options.sortable && tableState.sortIndex) { this.sort(tableState.sortIndex, tableState.sortReverse); } this.previous.apply(this, arguments); }, setSortedState: function(index, reverse){ if (reverse != null) this.sorted.reverse = reverse; else if (this.sorted.index == index) this.sorted.reverse = !this.sorted.reverse; else this.sorted.reverse = this.sorted.index == null; if (index != null) this.sorted.index = index; }, setHeadSort: function(sorted){ var head = $$(!this.head.length ? this.head.cells[this.sorted.index] : this.head.map(function(row){ return row.getElements(this.options.thSelector)[this.sorted.index]; }, this).clean()); if (!head.length) return; if (sorted){ head.addClass(this.options.classHeadSort); if (this.sorted.reverse) head.addClass(this.options.classHeadSortRev); else head.removeClass(this.options.classHeadSortRev); } else { head.removeClass(this.options.classHeadSort).removeClass(this.options.classHeadSortRev); } }, setRowSort: function(data, pre){ var count = data.length, body = this.body, group, rowIndex; while (count){ var item = data[--count], position = item.position, row = body.rows[position]; if (row.disabled) continue; if (!pre){ group = this.setGroupSort(group, row, item); this.setRowStyle(row, count); } body.appendChild(row); for (rowIndex = 0; rowIndex < count; rowIndex++){ if (data[rowIndex].position > position) data[rowIndex].position--; } } }, setRowStyle: function(row, i){ this.previous(row, i); row.cells[this.sorted.index].addClass(this.options.classCellSort); }, setGroupSort: function(group, row, item){ if (group == item.value) row.removeClass(this.options.classGroupHead).addClass(this.options.classGroup); else row.removeClass(this.options.classGroup).addClass(this.options.classGroupHead); return item.value; }, getParser: function(){ var parser = this.parsers[this.sorted.index]; return typeOf(parser) == 'string' ? HtmlTable.Parsers[parser] : parser; }, sort: function(index, reverse, pre){ if (!this.head) return; if (!pre){ this.clearSort(); this.setSortedState(index, reverse); this.setHeadSort(true); } var parser = this.getParser(); if (!parser) return; var rel; if (!Browser.ie){ rel = this.body.getParent(); this.body.dispose(); } var data = this.parseData(parser).sort(function(a, b){ if (a.value === b.value) return 0; return a.value > b.value ? 1 : -1; }); if (this.sorted.reverse == (parser == HtmlTable.Parsers['input-checked'])) data.reverse(true); this.setRowSort(data, pre); if (rel) rel.grab(this.body); this.fireEvent('stateChanged'); return this.fireEvent('sort', [this.body, this.sorted.index]); }, parseData: function(parser){ return Array.map(this.body.rows, function(row, i){ var value = parser.convert.call(document.id(row.cells[this.sorted.index])); return { position: i, value: value }; }, this); }, clearSort: function(){ this.setHeadSort(false); this.body.getElements('td').removeClass(this.options.classCellSort); }, reSort: function(){ if (this.sortEnabled) this.sort.call(this, this.sorted.index, this.sorted.reverse); return this; }, enableSort: function(){ this.element.addClass(this.options.classSortable); this.attachSorts(true); this.setParsers(); this.sortEnabled = true; return this; }, disableSort: function(){ this.element.removeClass(this.options.classSortable); this.attachSorts(false); this.sortSpans.each(function(span){ span.destroy(); }); this.sortSpans.empty(); this.sortEnabled = false; return this; } }); HtmlTable.ParserPriority = ['date', 'input-checked', 'input-value', 'float', 'number']; HtmlTable.Parsers = { 'date': { match: /^\d{2}[-\/ ]\d{2}[-\/ ]\d{2,4}$/, convert: function(){ var d = Date.parse(this.get('text').stripTags()); return (typeOf(d) == 'date') ? d.format('db') : ''; }, type: 'date' }, 'input-checked': { match: / type="(radio|checkbox)" /, convert: function(){ return this.getElement('input').checked; } }, 'input-value': { match: /= rows.length) index = null; return index; }, attachSelects: function(attach){ attach = attach != null ? attach : true; var method = attach ? 'addEvents' : 'removeEvents'; this.element[method]({ mouseleave: this.bound.mouseleave, click: this.bound.activateKeyboard }); this.body[method]({ 'click:relay(tr)': this.bound.clickRow, 'contextmenu:relay(tr)': this.bound.clickRow }); if (this.options.useKeyboard || this.keyboard){ if (!this.keyboard) this.keyboard = new Keyboard(); if (!this.selectKeysDefined) { this.selectKeysDefined = true; var timer, held; var move = function(offset){ var mover = function(e){ clearTimeout(timer); e.preventDefault(); var to = this.body.rows[this.getRowByOffset(offset)]; if (e.shift && to && this.isSelected(to)){ this.deselectRow(this.focused); this.focused = to; } else { if (to && (!this.options.allowMultiSelect || !e.shift)){ this.selectNone(); } this.shiftFocus(offset, e); } if (held){ timer = mover.delay(100, this, e); } else { timer = (function(){ held = true; mover(e); }).delay(400); } }.bind(this); return mover; }.bind(this); var clear = function(){ clearTimeout(timer); held = false; }; this.keyboard.addEvents({ 'keydown:shift+up': move(-1), 'keydown:shift+down': move(1), 'keyup:shift+up': clear, 'keyup:shift+down': clear, 'keyup:up': clear, 'keyup:down': clear }); var shiftHint = ''; if (this.options.allowMultiSelect && this.options.shiftForMultiSelect && this.options.useKeyboard){ shiftHint = " (Shift multi-selects)."; } this.keyboard.addShortcuts({ 'Select Previous Row': { keys: 'up', shortcut: 'up arrow', handler: move(-1), description: 'Select the previous row in the table.' + shiftHint }, 'Select Next Row': { keys: 'down', shortcut: 'down arrow', handler: move(1), description: 'Select the next row in the table.' + shiftHint } }); } this.keyboard[attach ? 'activate' : 'deactivate'](); } this.updateSelects(); }, mouseleave: function(){ if (this.hovered) this.leaveRow(this.hovered); } }); /* --- script: Scroller.js name: Scroller description: Class which scrolls the contents of any Element (including the window) when the mouse reaches the Element's boundaries. license: MIT-style license authors: - Valerio Proietti requires: - Core/Events - Core/Options - Core/Element.Event - Core/Element.Dimensions - MooTools.More provides: [Scroller] ... */ var Scroller = new Class({ Implements: [Events, Options], options: { area: 20, velocity: 1, onChange: function(x, y){ this.element.scrollTo(x, y); }, fps: 50 }, initialize: function(element, options){ this.setOptions(options); this.element = document.id(element); this.docBody = document.id(this.element.getDocument().body); this.listener = (typeOf(this.element) != 'element') ? this.docBody : this.element; this.timer = null; this.bound = { attach: this.attach.bind(this), detach: this.detach.bind(this), getCoords: this.getCoords.bind(this) }; }, start: function(){ this.listener.addEvents({ mouseover: this.bound.attach, mouseleave: this.bound.detach }); return this; }, stop: function(){ this.listener.removeEvents({ mouseover: this.bound.attach, mouseleave: this.bound.detach }); this.detach(); this.timer = clearInterval(this.timer); return this; }, attach: function(){ this.listener.addEvent('mousemove', this.bound.getCoords); }, detach: function(){ this.listener.removeEvent('mousemove', this.bound.getCoords); this.timer = clearInterval(this.timer); }, getCoords: function(event){ this.page = (this.listener.get('tag') == 'body') ? event.client : event.page; if (!this.timer) this.timer = this.scroll.periodical(Math.round(1000 / this.options.fps), this); }, scroll: function(){ var size = this.element.getSize(), scroll = this.element.getScroll(), pos = this.element != this.docBody ? this.element.getOffsets() : {x: 0, y:0}, scrollSize = this.element.getScrollSize(), change = {x: 0, y: 0}, top = this.options.area.top || this.options.area, bottom = this.options.area.bottom || this.options.area; for (var z in this.page){ if (this.page[z] < (top + pos[z]) && scroll[z] != 0){ change[z] = (this.page[z] - top - pos[z]) * this.options.velocity; } else if (this.page[z] + bottom > (size[z] + pos[z]) && scroll[z] + size[z] != scrollSize[z]){ change[z] = (this.page[z] - size[z] + bottom - pos[z]) * this.options.velocity; } change[z] = change[z].round(); } if (change.y || change.x) this.fireEvent('change', [scroll.x + change.x, scroll.y + change.y]); } }); /* --- script: Tips.js name: Tips description: Class for creating nice tips that follow the mouse cursor when hovering an element. license: MIT-style license authors: - Valerio Proietti - Christoph Pojer - Luis Merino requires: - Core/Options - Core/Events - Core/Element.Event - Core/Element.Style - Core/Element.Dimensions - /MooTools.More provides: [Tips] ... */ (function(){ var read = function(option, element){ return (option) ? (typeOf(option) == 'function' ? option(element) : element.get(option)) : ''; }; this.Tips = new Class({ Implements: [Events, Options], options: {/* onAttach: function(element){}, onDetach: function(element){}, onBound: function(coords){},*/ onShow: function(){ this.tip.setStyle('display', 'block'); }, onHide: function(){ this.tip.setStyle('display', 'none'); }, title: 'title', text: function(element){ return element.get('rel') || element.get('href'); }, showDelay: 100, hideDelay: 100, className: 'tip-wrap', offset: {x: 16, y: 16}, windowPadding: {x:0, y:0}, fixed: false }, initialize: function(){ var params = Array.link(arguments, { options: Type.isObject, elements: function(obj){ return obj != null; } }); this.setOptions(params.options); if (params.elements) this.attach(params.elements); this.container = new Element('div', {'class': 'tip'}); }, toElement: function(){ if (this.tip) return this.tip; this.tip = new Element('div', { 'class': this.options.className, styles: { position: 'absolute', top: 0, left: 0 } }).adopt( new Element('div', {'class': 'tip-top'}), this.container, new Element('div', {'class': 'tip-bottom'}) ); return this.tip; }, attach: function(elements){ $$(elements).each(function(element){ var title = read(this.options.title, element), text = read(this.options.text, element); element.set('title', '').store('tip:native', title).retrieve('tip:title', title); element.retrieve('tip:text', text); this.fireEvent('attach', [element]); var events = ['enter', 'leave']; if (!this.options.fixed) events.push('move'); events.each(function(value){ var event = element.retrieve('tip:' + value); if (!event) event = function(event){ this['element' + value.capitalize()].apply(this, [event, element]); }.bind(this); element.store('tip:' + value, event).addEvent('mouse' + value, event); }, this); }, this); return this; }, detach: function(elements){ $$(elements).each(function(element){ ['enter', 'leave', 'move'].each(function(value){ element.removeEvent('mouse' + value, element.retrieve('tip:' + value)).eliminate('tip:' + value); }); this.fireEvent('detach', [element]); if (this.options.title == 'title'){ // This is necessary to check if we can revert the title var original = element.retrieve('tip:native'); if (original) element.set('title', original); } }, this); return this; }, elementEnter: function(event, element){ clearTimeout(this.timer); this.timer = (function(){ this.container.empty(); ['title', 'text'].each(function(value){ var content = element.retrieve('tip:' + value); var div = this['_' + value + 'Element'] = new Element('div', { 'class': 'tip-' + value }).inject(this.container); if (content) this.fill(div, content); }, this); this.show(element); this.position((this.options.fixed) ? {page: element.getPosition()} : event); }).delay(this.options.showDelay, this); }, elementLeave: function(event, element){ clearTimeout(this.timer); this.timer = this.hide.delay(this.options.hideDelay, this, element); this.fireForParent(event, element); }, setTitle: function(title){ if (this._titleElement){ this._titleElement.empty(); this.fill(this._titleElement, title); } return this; }, setText: function(text){ if (this._textElement){ this._textElement.empty(); this.fill(this._textElement, text); } return this; }, fireForParent: function(event, element){ element = element.getParent(); if (!element || element == document.body) return; if (element.retrieve('tip:enter')) element.fireEvent('mouseenter', event); else this.fireForParent(event, element); }, elementMove: function(event, element){ this.position(event); }, position: function(event){ if (!this.tip) document.id(this); var size = window.getSize(), scroll = window.getScroll(), tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight}, props = {x: 'left', y: 'top'}, bounds = {y: false, x2: false, y2: false, x: false}, obj = {}; for (var z in props){ obj[props[z]] = event.page[z] + this.options.offset[z]; if (obj[props[z]] < 0) bounds[z] = true; if ((obj[props[z]] + tip[z] - scroll[z]) > size[z] - this.options.windowPadding[z]){ obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z]; bounds[z+'2'] = true; } } this.fireEvent('bound', bounds); this.tip.setStyles(obj); }, fill: function(element, contents){ if (typeof contents == 'string') element.set('html', contents); else element.adopt(contents); }, show: function(element){ if (!this.tip) document.id(this); if (!this.tip.getParent()) this.tip.inject(document.body); this.fireEvent('show', [this.tip, element]); }, hide: function(element){ if (!this.tip) document.id(this); this.fireEvent('hide', [this.tip, element]); } }); })(); /* --- name: Locale.en-GB.Date description: Date messages for British English. license: MIT-style license authors: - Aaron Newton requires: - /Locale - /Locale.en-US.Date provides: [Locale.en-GB.Date] ... */ Locale.define('en-GB', 'Date', { // Culture's date order: DD/MM/YYYY dateOrder: ['date', 'month', 'year'], shortDate: '%d/%m/%Y', shortTime: '%H:%M' }).inherit('en-US', 'Date'); ZoneMinder-1.26.5/web/tools/mootools/mootools-more-1.3.2.1-yc.js000066400000000000000000004366611225361755400240610ustar00rootroot00000000000000// MooTools: the javascript framework. // Load this file's selection again by visiting: http://mootools.net/more/09f3e47813269cd5026cbf8c1f828e72 // Or build this file again with packager using: packager build More/Chain.Wait More/Array.Extras More/Date More/Date.Extras More/Number.Format More/String.Extras More/String.QueryString More/URI More/URI.Relative More/Hash More/Hash.Extras More/Element.Forms More/Elements.From More/Element.Event.Pseudos.Keys More/Element.Pin More/Element.Position More/Element.Shortcuts More/Form.Request More/Form.Request.Append More/Form.Validator.Inline More/Form.Validator.Extras More/OverText More/Fx.Accordion More/Fx.Move More/Fx.Reveal More/Fx.Slide More/Fx.SmoothScroll More/Fx.Sort More/Drag.Move More/Slider More/Sortables More/Request.JSONP More/Request.Queue More/Request.Periodical More/Assets More/Color More/Group More/Hash.Cookie More/Table More/HtmlTable.Zebra More/HtmlTable.Sort More/HtmlTable.Select More/Keyboard.Extras More/Mask More/Scroller More/Tips More/Locale.en-GB.Date /* --- copyrights: - [MooTools](http://mootools.net) licenses: - [MIT License](http://mootools.net/license.txt) ... */ MooTools.More={version:"1.3.2.1",build:"e586bcd2496e9b22acfde32e12f84d49ce09e59d"};(function(){var a={wait:function(b){return this.chain(function(){this.callChain.delay(b==null?500:b,this); return this;}.bind(this));}};Chain.implement(a);if(this.Fx){Fx.implement(a);}if(this.Element&&Element.implement&&this.Fx){Element.implement({chains:function(b){Array.from(b||["tween","morph","reveal"]).each(function(c){c=this.get(c); if(!c){return;}c.setOptions({link:"chain"});},this);return this;},pauseFx:function(c,b){this.chains(b).get(b||"tween").wait(c);return this;}});}})();(function(a){Array.implement({min:function(){return Math.min.apply(null,this); },max:function(){return Math.max.apply(null,this);},average:function(){return this.length?this.sum()/this.length:0;},sum:function(){var b=0,c=this.length; if(c){while(c--){b+=this[c];}}return b;},unique:function(){return[].combine(this);},shuffle:function(){for(var c=this.length;c&&--c;){var b=this[c],d=Math.floor(Math.random()*(c+1)); this[c]=this[d];this[d]=b;}return this;},reduce:function(d,e){for(var c=0,b=this.length;c3&&a<21)?"th":["th","st","nd","rd","th"][Math.min(a%10,4)]; },lessThanMinuteAgo:"less than a minute ago",minuteAgo:"about a minute ago",minutesAgo:"{delta} minutes ago",hourAgo:"about an hour ago",hoursAgo:"about {delta} hours ago",dayAgo:"1 day ago",daysAgo:"{delta} days ago",weekAgo:"1 week ago",weeksAgo:"{delta} weeks ago",monthAgo:"1 month ago",monthsAgo:"{delta} months ago",yearAgo:"1 year ago",yearsAgo:"{delta} years ago",lessThanMinuteUntil:"less than a minute from now",minuteUntil:"about a minute from now",minutesUntil:"{delta} minutes from now",hourUntil:"about an hour from now",hoursUntil:"about {delta} hours from now",dayUntil:"1 day from now",daysUntil:"{delta} days from now",weekUntil:"1 week from now",weeksUntil:"{delta} weeks from now",monthUntil:"1 month from now",monthsUntil:"{delta} months from now",yearUntil:"1 year from now",yearsUntil:"{delta} years from now"}); (function(){var a=this.Date;var f=a.Methods={ms:"Milliseconds",year:"FullYear",min:"Minutes",mo:"Month",sec:"Seconds",hr:"Hours"};["Date","Day","FullYear","Hours","Milliseconds","Minutes","Month","Seconds","Time","TimezoneOffset","Week","Timezone","GMTOffset","DayOfYear","LastMonth","LastDayOfMonth","UTCDate","UTCDay","UTCFullYear","AMPM","Ordinal","UTCHours","UTCMilliseconds","UTCMinutes","UTCMonth","UTCSeconds","UTCMilliseconds"].each(function(t){a.Methods[t.toLowerCase()]=t; });var p=function(v,u,t){if(u==1){return v;}return v28){return 1;}if(z==0&&t<-2){y=new a(y).decrement("day",v); v=0;}x=new a(y.get("year"),0,1).get("day")||7;if(x>4){u=-7;}}else{x=new a(y.get("year"),0,1).get("day");}u+=y.get("dayofyear");u+=6-v;u+=(7+x-w)%7;return(u/7); },getOrdinal:function(t){return a.getMsg("ordinal",t||this.get("date"));},getTimezone:function(){return this.toString().replace(/^.*? ([A-Z]{3}).[0-9]{4}.*$/,"$1").replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/,"$1$2$3"); },getGMTOffset:function(){var t=this.get("timezoneOffset");return((t>0)?"-":"+")+p((t.abs()/60).floor(),2)+p(t%60,2);},setAMPM:function(t){t=t.toUpperCase(); var u=this.get("hr");if(u>11&&t=="AM"){return this.decrement("hour",12);}else{if(u<12&&t=="PM"){return this.increment("hour",12);}}return this;},getAMPM:function(){return(this.get("hr")<12)?"AM":"PM"; },parse:function(t){this.set("time",a.parse(t));return this;},isValid:function(t){return !isNaN((t||this).valueOf());},format:function(u){if(!this.isValid()){return"invalid date"; }if(!u){u="%x %X";}var t=u.toLowerCase();if(s[t]){return s[t](this);}u=g[t]||u;var v=this;return u.replace(/%([a-z%])/gi,function(x,w){switch(w){case"a":return a.getMsg("days_abbr")[v.get("day")]; case"A":return a.getMsg("days")[v.get("day")];case"b":return a.getMsg("months_abbr")[v.get("month")];case"B":return a.getMsg("months")[v.get("month")]; case"c":return v.format("%a %b %d %H:%M:%S %Y");case"d":return p(v.get("date"),2);case"e":return p(v.get("date"),2," ");case"H":return p(v.get("hr"),2); case"I":return p((v.get("hr")%12)||12,2);case"j":return p(v.get("dayofyear"),3);case"k":return p(v.get("hr"),2," ");case"l":return p((v.get("hr")%12)||12,2," "); case"L":return p(v.get("ms"),3);case"m":return p((v.get("mo")+1),2);case"M":return p(v.get("min"),2);case"o":return v.get("ordinal");case"p":return a.getMsg(v.get("ampm")); case"s":return Math.round(v/1000);case"S":return p(v.get("seconds"),2);case"T":return v.format("%H:%M:%S");case"U":return p(v.get("week"),2);case"w":return v.get("day"); case"x":return v.format(a.getMsg("shortDate"));case"X":return v.format(a.getMsg("shortTime"));case"y":return v.get("year").toString().substr(2);case"Y":return v.get("year"); case"z":return v.get("GMTOffset");case"Z":return v.get("Timezone");}return w;});},toISOString:function(){return this.format("iso8601");}}).alias({toJSON:"toISOString",compare:"diff",strftime:"format"}); var g={db:"%Y-%m-%d %H:%M:%S",compact:"%Y%m%dT%H%M%S","short":"%d %b %H:%M","long":"%B %d, %Y %H:%M"};var k=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],h=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; var s={rfc822:function(t){return k[t.get("day")]+t.format(", %d ")+h[t.get("month")]+t.format(" %Y %H:%M:%S %Z");},rfc2822:function(t){return k[t.get("day")]+t.format(", %d ")+h[t.get("month")]+t.format(" %Y %H:%M:%S %z"); },iso8601:function(t){return(t.getUTCFullYear()+"-"+p(t.getUTCMonth()+1,2)+"-"+p(t.getUTCDate(),2)+"T"+p(t.getUTCHours(),2)+":"+p(t.getUTCMinutes(),2)+":"+p(t.getUTCSeconds(),2)+"."+p(t.getUTCMilliseconds(),3)+"Z"); }};var c=[],n=a.parse;var r=function(w,y,v){var u=-1,x=a.getMsg(w+"s");switch(typeOf(y)){case"object":u=x[y.get(w)];break;case"number":u=x[y];if(!u){throw new Error("Invalid "+w+" index: "+y); }break;case"string":var t=x.filter(function(z){return this.test(z);},new RegExp("^"+y,"i"));if(!t.length){throw new Error("Invalid "+w+" string");}if(t.length>1){throw new Error("Ambiguous "+w); }u=t[0];}return(v)?x.indexOf(u):u;};var i=1900,o=70;a.extend({getMsg:function(u,t){return Locale.get("Date."+u,t);},units:{ms:Function.from(1),second:Function.from(1000),minute:Function.from(60000),hour:Function.from(3600000),day:Function.from(86400000),week:Function.from(608400000),month:function(u,t){var v=new a; return a.daysInMonth(u!=null?u:v.get("mo"),t!=null?t:v.get("year"))*86400000;},year:function(t){t=t||new a().get("year");return a.isLeapYear(t)?31622400000:31536000000; }},daysInMonth:function(u,t){return[31,a.isLeapYear(t)?29:28,31,30,31,30,31,31,30,31,30,31][u];},isLeapYear:function(t){return((t%4===0)&&(t%100!==0))||(t%400===0); },parse:function(w){var v=typeOf(w);if(v=="number"){return new a(w);}if(v!="string"){return w;}w=w.clean();if(!w.length){return null;}var u;c.some(function(x){var t=x.re.exec(w); return(t)?(u=x.handler(t)):false;});if(!(u&&u.isValid())){u=new a(n(w));if(!(u&&u.isValid())){u=new a(w.toInt());}}return u;},parseDay:function(t,u){return r("day",t,u); },parseMonth:function(u,t){return r("month",u,t);},parseUTC:function(u){var t=new a(u);var v=a.UTC(t.get("year"),t.get("mo"),t.get("date"),t.get("hr"),t.get("min"),t.get("sec"),t.get("ms")); return new a(v);},orderIndex:function(t){return a.getMsg("dateOrder").indexOf(t)+1;},defineFormat:function(t,u){g[t]=u;return this;},defineFormats:function(t){for(var u in t){a.defineFormat(u,t[u]); }return this;},defineParser:function(t){c.push((t.re&&t.handler)?t:l(t));return this;},defineParsers:function(){Array.flatten(arguments).each(a.defineParser); return this;},define2DigitYearStart:function(t){o=t%100;i=t-o;return this;}});var d=function(t){return new RegExp("(?:"+a.getMsg(t).map(function(u){return u.substr(0,3); }).join("|")+")[a-z]*");};var m=function(t){switch(t){case"T":return"%H:%M:%S";case"x":return((a.orderIndex("month")==1)?"%m[-./]%d":"%d[-./]%m")+"([-./]%y)?"; case"X":return"%H([.:]%M)?([.:]%S([.:]%s)?)? ?%p? ?%z?";}return null;};var j={d:/[0-2]?[0-9]|3[01]/,H:/[01]?[0-9]|2[0-3]/,I:/0?[1-9]|1[0-2]/,M:/[0-5]?\d/,s:/\d+/,o:/[a-z]*/,p:/[ap]\.?m\.?/,y:/\d{2}|\d{4}/,Y:/\d{4}/,z:/Z|[+-]\d{2}(?::?\d{2})?/}; j.m=j.I;j.S=j.M;var e;var b=function(t){e=t;j.a=j.A=d("days");j.b=j.B=d("months");c.each(function(v,u){if(v.format){c[u]=l(v.format);}});};var l=function(v){if(!e){return{format:v}; }var t=[];var u=(v.source||v).replace(/%([a-z])/gi,function(x,w){return m(w)||x;}).replace(/\((?!\?)/g,"(?:").replace(/ (?!\?|\*)/g,",? ").replace(/%([a-z%])/gi,function(x,w){var y=j[w]; if(!y){return w;}t.push(w);return"("+y.source+")";}).replace(/\[a-z\]/gi,"[a-z\\u00c0-\\uffff;&]");return{format:v,re:new RegExp("^"+u+"$","i"),handler:function(z){z=z.slice(1).associate(t); var w=new a().clearTime(),y=z.y||z.Y;if(y!=null){q.call(w,"y",y);}if("d" in z){q.call(w,"d",1);}if("m" in z||z.b||z.B){q.call(w,"m",1);}for(var x in z){q.call(w,x,z[x]); }return w;}};};var q=function(t,u){if(!u){return this;}switch(t){case"a":case"A":return this.set("day",a.parseDay(u,true));case"b":case"B":return this.set("mo",a.parseMonth(u,true)); case"d":return this.set("date",u);case"H":case"I":return this.set("hr",u);case"m":return this.set("mo",u-1);case"M":return this.set("min",u);case"p":return this.set("ampm",u.replace(/\./g,"")); case"S":return this.set("sec",u);case"s":return this.set("ms",("0."+u)*1000);case"w":return this.set("day",u);case"Y":return this.set("year",u);case"y":u=+u; if(u<100){u+=i+(u0.75*a){e=c;}break;}f/=a;e=c+"s";}f=f.round();return Date.getMsg(e+d,f).substitute({delta:f});}}).defineParsers({re:/^(?:tod|tom|yes)/i,handler:function(a){var b=new Date().clearTime(); switch(a[0]){case"tom":return b.increment();case"yes":return b.decrement();default:return b;}}},{re:/^(next|last) ([a-z]+)$/i,handler:function(e){var f=new Date().clearTime(); var b=f.getDay();var c=Date.parseDay(e[2],true);var a=c-b;if(c<=b){a+=7;}if(e[1]=="last"){a-=7;}return f.set("date",f.getDate()+a);}}).alias("timeAgoInWords","timeDiffInWords"); Locale.define("en-US","Number",{decimal:".",group:",",currency:{prefix:"$ "}});Number.implement({format:function(q){var n=this;q=q?Object.clone(q):{};var a=function(i){if(q[i]!=null){return q[i]; }return Locale.get("Number."+i);};var f=n<0,h=a("decimal"),k=a("precision"),o=a("group"),c=a("decimals");if(f){var e=a("negative")||{};if(e.prefix==null&&e.suffix==null){e.prefix="-"; }["prefix","suffix"].each(function(i){if(e[i]){q[i]=a(i)+e[i];}});n=-n;}var l=a("prefix"),p=a("suffix");if(c!==""&&c>=0&&c<=20){n=n.toFixed(c);}if(k>=1&&k<=21){n=(+n).toPrecision(k); }n+="";var m;if(a("scientific")===false&&n.indexOf("e")>-1){var j=n.split("e"),b=+j[1];n=j[0].replace(".","");if(b<0){b=-b-1;m=j[0].indexOf(".");if(m>-1){b-=m-1; }while(b--){n="0"+n;}n="0."+n;}else{m=j[0].lastIndexOf(".");if(m>-1){b-=j[0].length-m-1;}while(b--){n+="0";}}}if(h!="."){n=n.replace(".",h);}if(o){m=n.lastIndexOf(h); m=(m>-1)?m:n.length;var d=n.substring(m),g=m;while(g--){if((m-g-1)%3==0&&g!=(m-1)){d=o+d;}d=n.charAt(g)+d;}n=d;}if(l){n=l+n;}if(p){n+=p;}return n;},formatCurrency:function(){var a=Locale.get("Number.currency")||{}; if(a.scientific==null){a.scientific=false;}if(a.decimals==null){a.decimals=2;}return this.format(a);},formatPercentage:function(){var a=Locale.get("Number.percentage")||{}; if(a.suffix==null){a.suffix="%";}if(a.decimals==null){a.decimals=2;}return this.format(a);}});(function(){var c={a:/[àáâãäåăą]/g,A:/[ÀÃÂÃÄÅĂĄ]/g,c:/[ćÄç]/g,C:/[ĆČÇ]/g,d:/[ÄÄ‘]/g,D:/[ÄŽÃ]/g,e:/[èéêëěę]/g,E:/[ÈÉÊËĚĘ]/g,g:/[ÄŸ]/g,G:/[Äž]/g,i:/[ìíîï]/g,I:/[ÃŒÃÃŽÃ]/g,l:/[ĺľł]/g,L:/[ĹĽÅ]/g,n:/[ñňń]/g,N:/[ÑŇŃ]/g,o:/[òóôõöøő]/g,O:/[ÒÓÔÕÖØ]/g,r:/[řŕ]/g,R:/[ŘŔ]/g,s:/[ššş]/g,S:/[ŠŞŚ]/g,t:/[ťţ]/g,T:/[ŤŢ]/g,ue:/[ü]/g,UE:/[Ü]/g,u:/[ùúûůµ]/g,U:/[ÙÚÛŮ]/g,y:/[ÿý]/g,Y:/[ŸÃ]/g,z:/[žźż]/g,Z:/[ŽŹŻ]/g,th:/[þ]/g,TH:/[Þ]/g,dh:/[ð]/g,DH:/[Ã]/g,ss:/[ß]/g,oe:/[Å“]/g,OE:/[Å’]/g,ae:/[æ]/g,AE:/[Æ]/g},b={" ":/[\xa0\u2002\u2003\u2009]/g,"*":/[\xb7]/g,"'":/[\u2018\u2019]/g,'"':/[\u201c\u201d]/g,"...":/[\u2026]/g,"-":/[\u2013]/g,"»":/[\uFFFD]/g}; var a=function(f,h){var e=f,g;for(g in h){e=e.replace(h[g],g);}return e;};var d=function(e,g){e=e||"";var h=g?"<"+e+"(?!\\w)[^>]*>([\\s\\S]*?)":"]+)?>",f=new RegExp(h,"gi"); return f;};String.implement({standardize:function(){return a(this,c);},repeat:function(e){return new Array(e+1).join(this);},pad:function(e,h,g){if(this.length>=e){return this; }var f=(h==null?" ":""+h).repeat(e-this.length).substr(0,e-this.length);if(!g||g=="right"){return this+f;}if(g=="left"){return f+this;}return f.substr(0,(f.length/2).floor())+this+f.substr(0,(f.length/2).ceil()); },getTags:function(e,f){return this.match(d(e,f))||[];},stripTags:function(e,f){return this.replace(d(e,f),"");},tidy:function(){return a(this,b);},truncate:function(e,f,i){var h=this; if(f==null&&arguments.length==1){f="…";}if(h.length>e){h=h.substring(0,e);if(i){var g=h.lastIndexOf(i);if(g!=-1){h=h.substr(0,g);}}if(f){h+=f;}}return h; }});})();String.implement({parseQueryString:function(d,a){if(d==null){d=true;}if(a==null){a=true;}var c=this.split(/[&;]/),b={};if(!c.length){return b; }c.each(function(i){var e=i.indexOf("=")+1,g=e?i.substr(e):"",f=e?i.substr(0,e-1).match(/([^\]\[]+|(\B)(?=\]))/g):[i],h=b;if(!f){return;}if(a){g=decodeURIComponent(g); }f.each(function(k,j){if(d){k=decodeURIComponent(k);}var l=h[k];if(j0){c.pop(); }else{if(f!="."){c.push(f);}}});return c.join("/")+"/";},combine:function(c){return c.value||c.scheme+"://"+(c.user?c.user+(c.password?":"+c.password:"")+"@":"")+(c.host||"")+(c.port&&c.port!=this.schemes[c.scheme]?":"+c.port:"")+(c.directory||"/")+(c.file||"")+(c.query?"?"+c.query:"")+(c.fragment?"#"+c.fragment:""); },set:function(d,f,e){if(d=="value"){var c=f.match(a.regs.scheme);if(c){c=c[1];}if(c&&this.schemes[c.toLowerCase()]==null){this.parsed={scheme:c,value:f}; }else{this.parsed=this.parse(f,(e||this).parsed)||(c?{scheme:c,value:f}:{value:f});}}else{if(d=="data"){this.setData(f);}else{this.parsed[d]=f;}}return this; },get:function(c,d){switch(c){case"value":return this.combine(this.parsed,d?d.parsed:false);case"data":return this.getData();}return this.parsed[c]||""; },go:function(){document.location.href=this.toString();},toURI:function(){return this;},getData:function(e,d){var c=this.get(d||"query");if(!(c||c===0)){return e?null:{}; }var f=c.parseQueryString();return e?f[e]:f;},setData:function(c,f,d){if(typeof c=="string"){var e=this.getData();e[arguments[0]]=arguments[1];c=e;}else{if(f){c=Object.merge(this.getData(),c); }}return this.set(d||"query",Object.toQueryString(c));},clearData:function(c){return this.set(c||"query","");},toString:b,valueOf:b});a.regs={endSlash:/\/$/,scheme:/^(\w+):/,directoryDot:/\.\/|\.$/}; a.base=new a(Array.from(document.getElements("base[href]",true)).getLast(),{base:document.location});String.implement({toURI:function(c){return new a(this,c); }});})();Class.refactor=function(b,a){Object.each(a,function(e,d){var c=b.prototype[d];c=(c&&c.$origin)||c||function(){};b.implement(d,(typeof e=="function")?function(){var f=this.previous; this.previous=c;var g=e.apply(this,arguments);this.previous=f;return g;}:e);});return b;};URI=Class.refactor(URI,{combine:function(f,e){if(!e||f.scheme!=e.scheme||f.host!=e.host||f.port!=e.port){return this.previous.apply(this,arguments); }var a=f.file+(f.query?"?"+f.query:"")+(f.fragment?"#"+f.fragment:"");if(!e.directory){return(f.directory||(f.file?"":"./"))+a;}var d=e.directory.split("/"),c=f.directory.split("/"),g="",h; var b=0;for(h=0;h=0||g.parentPositioned||d.allowNegative)?c.x:0).toInt(); c.top=((c.y>=0||g.parentPositioned||d.allowNegative)?c.y:0).toInt();a.toMinMax(c,d);if(d.relFixedPosition||f.getStyle("position")=="fixed"){a.toRelFixedPosition(f,c); }if(d.ignoreScroll){a.toIgnoreScroll(f,c);}if(d.ignoreMargins){a.toIgnoreMargins(c,d);}c.left=Math.ceil(c.left);c.top=Math.ceil(c.top);delete c.x;delete c.y; return c;},setPositionCoordinates:function(k,g,d){var f=k.offset.y,h=k.offset.x,e=(d==document.body)?window.getScroll():d.getPosition(),j=e.y,c=e.x,i=window.getSize(); switch(k.position.x){case"left":g.x=c+h;break;case"right":g.x=c+h+d.offsetWidth;break;default:g.x=c+((d==document.body?i.x:d.offsetWidth)/2)+h;break;}switch(k.position.y){case"top":g.y=j+f; break;case"bottom":g.y=j+f+d.offsetHeight;break;default:g.y=j+((d==document.body?i.y:d.offsetHeight)/2)+f;break;}},toMinMax:function(c,d){var f={left:"x",top:"y"},e; ["minimum","maximum"].each(function(g){["left","top"].each(function(h){e=d[g]?d[g][f[h]]:null;if(e!=null&&((g=="minimum")?c[h]e)){c[h]=e;}});}); },toRelFixedPosition:function(e,c){var d=window.getScroll();c.top+=d.y;c.left+=d.x;},toIgnoreScroll:function(e,d){var c=e.getScroll();d.top-=c.y;d.left-=c.x; },toIgnoreMargins:function(c,d){c.left+=d.edge.x=="right"?d.dimensions["margin-right"]:(d.edge.x!="center"?-d.dimensions["margin-left"]:-d.dimensions["margin-left"]+((d.dimensions["margin-right"]+d.dimensions["margin-left"])/2)); c.top+=d.edge.y=="bottom"?d.dimensions["margin-bottom"]:(d.edge.y!="center"?-d.dimensions["margin-top"]:-d.dimensions["margin-top"]+((d.dimensions["margin-bottom"]+d.dimensions["margin-top"])/2)); },toEdge:function(c,d){var e={},g=d.dimensions,f=d.edge;switch(f.x){case"left":e.x=0;break;case"right":e.x=-g.x-g.computedRight-g.computedLeft;break;default:e.x=-(Math.round(g.totalWidth/2)); break;}switch(f.y){case"top":e.y=0;break;case"bottom":e.y=-g.y-g.computedTop-g.computedBottom;break;default:e.y=-(Math.round(g.totalHeight/2));break;}c.x+=e.x; c.y+=e.y;},getCoordinateFromValue:function(c){if(typeOf(c)!="string"){return c;}c=c.toLowerCase();return{x:c.test("left")?"left":(c.test("right")?"right":"center"),y:c.test(/upper|top/)?"top":(c.test("bottom")?"bottom":"center")}; }};Element.implement({position:function(d){if(d&&(d.x!=null||d.y!=null)){return(b?b.apply(this,arguments):this);}var c=this.setStyle("position","absolute").calculatePosition(d); return(d&&d.returnPos)?c:this.setStyles(c);},calculatePosition:function(c){return a.getPosition(this,c);}});})(Element.prototype.position);Element.implement({isDisplayed:function(){return this.getStyle("display")!="none"; },isVisible:function(){var a=this.offsetWidth,b=this.offsetHeight;return(a==0&&b==0)?false:(a>0&&b>0)?true:this.style.display!="none";},toggle:function(){return this[this.isDisplayed()?"hide":"show"](); },hide:function(){var b;try{b=this.getStyle("display");}catch(a){}if(b=="none"){return this;}return this.store("element:_originalDisplay",b||"").setStyle("display","none"); },show:function(a){if(!a&&this.isDisplayed()){return this;}a=a||this.retrieve("element:_originalDisplay")||"block";return this.setStyle("display",(a=="none")?"block":a); },swapClass:function(a,b){return this.removeClass(a).addClass(b);}});Document.implement({clearSelection:function(){if(window.getSelection){var a=window.getSelection(); if(a&&a.removeAllRanges){a.removeAllRanges();}}else{if(document.selection&&document.selection.empty){try{document.selection.empty();}catch(b){}}}}});Class.Mutators.Binds=function(a){if(!this.prototype.initialize){this.implement("initialize",function(){}); }return Array.from(a).concat(this.prototype.Binds||[]);};Class.Mutators.initialize=function(a){return function(){Array.from(this.Binds).each(function(b){var c=this[b]; if(c){this[b]=c.bind(this);}},this);return a.apply(this,arguments);};};Class.Occlude=new Class({occlude:function(c,b){b=document.id(b||this.element);var a=b.retrieve(c||this.property); if(a&&!this.occluded){return(this.occluded=a);}this.occluded=false;b.store(c||this.property,this);return this.occluded;}});var IframeShim=new Class({Implements:[Options,Events,Class.Occlude],options:{className:"iframeShim",src:'javascript:false;document.write("");',display:false,zIndex:null,margin:0,offset:{x:0,y:0},browsers:(Browser.ie6||(Browser.firefox&&Browser.version<3&&Browser.Platform.mac))},property:"IframeShim",initialize:function(b,a){this.element=document.id(b); if(this.occlude()){return this.occluded;}this.setOptions(a);this.makeShim();return this;},makeShim:function(){if(this.options.browsers){var c=this.element.getStyle("zIndex").toInt(); if(!c){c=1;var b=this.element.getStyle("position");if(b=="static"||!b){this.element.setStyle("position","relative");}this.element.setStyle("zIndex",c); }c=((this.options.zIndex!=null||this.options.zIndex===0)&&c>this.options.zIndex)?this.options.zIndex:c-1;if(c<0){c=1;}this.shim=new Element("iframe",{src:this.options.src,scrolling:"no",frameborder:0,styles:{zIndex:c,position:"absolute",border:"none",filter:"progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)"},"class":this.options.className}).store("IframeShim",this); var a=(function(){this.shim.inject(this.element,"after");this[this.options.display?"show":"hide"]();this.fireEvent("inject");}).bind(this);if(!IframeShim.ready){window.addEvent("load",a); }else{a();}}else{this.position=this.hide=this.show=this.dispose=Function.from(this);}},position:function(){if(!IframeShim.ready||!this.shim){return this; }var a=this.element.measure(function(){return this.getSize();});if(this.options.margin!=undefined){a.x=a.x-(this.options.margin*2);a.y=a.y-(this.options.margin*2); this.options.offset.x+=this.options.margin;this.options.offset.y+=this.options.margin;}this.shim.set({width:a.x,height:a.y}).position({relativeTo:this.element,offset:this.options.offset}); return this;},hide:function(){if(this.shim){this.shim.setStyle("display","none");}return this;},show:function(){if(this.shim){this.shim.setStyle("display","block"); }return this.position();},dispose:function(){if(this.shim){this.shim.dispose();}return this;},destroy:function(){if(this.shim){this.shim.destroy();}return this; }});window.addEvent("load",function(){IframeShim.ready=true;});var Mask=new Class({Implements:[Options,Events],Binds:["position"],options:{style:{},"class":"mask",maskMargins:false,useIframeShim:true,iframeShimOptions:{}},initialize:function(b,a){this.target=document.id(b)||document.id(document.body); this.target.store("mask",this);this.setOptions(a);this.render();this.inject();},render:function(){this.element=new Element("div",{"class":this.options["class"],id:this.options.id||"mask-"+String.uniqueID(),styles:Object.merge({},this.options.style,{display:"none"}),events:{click:function(a){this.fireEvent("click",a); if(this.options.hideOnClick){this.hide();}}.bind(this)}});this.hidden=true;},toElement:function(){return this.element;},inject:function(b,a){a=a||(this.options.inject?this.options.inject.where:"")||this.target==document.body?"inside":"after"; b=b||(this.options.inject&&this.options.inject.target)||this.target;this.element.inject(b,a);if(this.options.useIframeShim){this.shim=new IframeShim(this.element,this.options.iframeShimOptions); this.addEvents({show:this.shim.show.bind(this.shim),hide:this.shim.hide.bind(this.shim),destroy:this.shim.destroy.bind(this.shim)});}},position:function(){this.resize(this.options.width,this.options.height); this.element.position({relativeTo:this.target,position:"topLeft",ignoreMargins:!this.options.maskMargins,ignoreScroll:this.target==document.body});return this; },resize:function(a,e){var b={styles:["padding","border"]};if(this.options.maskMargins){b.styles.push("margin");}var d=this.target.getComputedSize(b);if(this.target==document.body){this.element.setStyles({width:0,height:0}); var c=window.getScrollSize();if(d.totalHeight=0&&a.options[a.selectedIndex].value!=""); }else{return((a.get("value")==null)||(a.get("value").length==0));}}});Form.Validator.addAllThese([["required",{errorMsg:function(){return Form.Validator.getMsg("required"); },test:function(a){return !Form.Validator.getValidator("IsEmpty").test(a);}}],["minLength",{errorMsg:function(a,b){if(typeOf(b.minLength)!="null"){return Form.Validator.getMsg("minLength").substitute({minLength:b.minLength,length:a.get("value").length}); }else{return"";}},test:function(a,b){if(typeOf(b.minLength)!="null"){return(a.get("value").length>=(b.minLength||0));}else{return true;}}}],["maxLength",{errorMsg:function(a,b){if(typeOf(b.maxLength)!="null"){return Form.Validator.getMsg("maxLength").substitute({maxLength:b.maxLength,length:a.get("value").length}); }else{return"";}},test:function(a,b){return a.get("value").length<=(b.maxLength||10000);}}],["validate-integer",{errorMsg:Form.Validator.getMsg.pass("integer"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||(/^(-?[1-9]\d*|0)$/).test(a.get("value")); }}],["validate-numeric",{errorMsg:Form.Validator.getMsg.pass("numeric"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||(/^-?(?:0$0(?=\d*\.)|[1-9]|0)\d*(\.\d+)?$/).test(a.get("value")); }}],["validate-digits",{errorMsg:Form.Validator.getMsg.pass("digits"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||(/^[\d() .:\-\+#]+$/.test(a.get("value"))); }}],["validate-alpha",{errorMsg:Form.Validator.getMsg.pass("alpha"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||(/^[a-zA-Z]+$/).test(a.get("value")); }}],["validate-alphanum",{errorMsg:Form.Validator.getMsg.pass("alphanum"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||!(/\W/).test(a.get("value")); }}],["validate-date",{errorMsg:function(a,b){if(Date.parse){var c=b.dateFormat||"%x";return Form.Validator.getMsg("dateSuchAs").substitute({date:new Date().format(c)}); }else{return Form.Validator.getMsg("dateInFormatMDY");}},test:function(e,g){if(Form.Validator.getValidator("IsEmpty").test(e)){return true;}var a=Locale.getCurrent().sets.Date,b=new RegExp([a.days,a.days_abbr,a.months,a.months_abbr].flatten().join("|"),"i"),i=e.get("value"),f=i.match(/[a-z]+/gi); if(f&&!f.every(b.exec,b)){return false;}var c=Date.parse(i),h=g.dateFormat||"%x",d=c.format(h);if(d!="invalid date"){e.set("value",d);}return c.isValid(); }}],["validate-email",{errorMsg:Form.Validator.getMsg.pass("email"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||(/^(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]\.?){0,63}[a-z0-9!#$%&'*+\/=?^_`{|}~-]@(?:(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\])$/i).test(a.get("value")); }}],["validate-url",{errorMsg:Form.Validator.getMsg.pass("url"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||(/^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i).test(a.get("value")); }}],["validate-currency-dollar",{errorMsg:Form.Validator.getMsg.pass("currencyDollar"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||(/^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/).test(a.get("value")); }}],["validate-one-required",{errorMsg:Form.Validator.getMsg.pass("oneRequired"),test:function(a,b){var c=document.id(b["validate-one-required"])||a.getParent(b["validate-one-required"]); return c.getElements("input").some(function(d){if(["checkbox","radio"].contains(d.get("type"))){return d.get("checked");}return d.get("value");});}}]]); Element.Properties.validator={set:function(a){this.get("validator").setOptions(a);},get:function(){var a=this.retrieve("validator");if(!a){a=new Form.Validator(this); this.store("validator",a);}return a;}};Element.implement({validate:function(a){if(a){this.set("validator",a);}return this.get("validator").validate();}}); Form.Validator.Inline=new Class({Extends:Form.Validator,options:{showError:function(a){if(a.reveal){a.reveal();}else{a.setStyle("display","block");}},hideError:function(a){if(a.dissolve){a.dissolve(); }else{a.setStyle("display","none");}},scrollToErrorsOnSubmit:true,scrollToErrorsOnBlur:false,scrollToErrorsOnChange:false,scrollFxOptions:{transition:"quad:out",offset:{y:-20}}},initialize:function(b,a){this.parent(b,a); this.addEvent("onElementValidate",function(g,f,e,h){var d=this.getValidator(e);if(!g&&d.getError(f)){if(h){f.addClass("warning");}var c=this.makeAdvice(e,f,d.getError(f),h); this.insertAdvice(c,f);this.showAdvice(e,f);}else{this.hideAdvice(e,f);}});},makeAdvice:function(d,f,c,g){var e=(g)?this.warningPrefix:this.errorPrefix; e+=(this.options.useTitles)?f.title||c:c;var a=(g)?"warning-advice":"validation-advice";var b=this.getAdvice(d,f);if(b){b=b.set("html",e);}else{b=new Element("div",{html:e,styles:{display:"none"},id:"advice-"+d.split(":")[0]+"-"+this.getFieldId(f)}).addClass(a); }f.store("$moo:advice-"+d,b);return b;},getFieldId:function(a){return a.id?a.id:a.id="input_"+a.name;},showAdvice:function(b,c){var a=this.getAdvice(b,c); if(a&&!c.retrieve("$moo:"+this.getPropName(b))&&(a.getStyle("display")=="none"||a.getStyle("visiblity")=="hidden"||a.getStyle("opacity")==0)){c.store("$moo:"+this.getPropName(b),true); this.options.showError(a);this.fireEvent("showAdvice",[c,a,b]);}},hideAdvice:function(b,c){var a=this.getAdvice(b,c);if(a&&c.retrieve("$moo:"+this.getPropName(b))){c.store("$moo:"+this.getPropName(b),false); this.options.hideError(a);this.fireEvent("hideAdvice",[c,a,b]);}},getPropName:function(a){return"advice"+a;},resetField:function(a){a=document.id(a);if(!a){return this; }this.parent(a);a.get("validators").each(function(b){this.hideAdvice(b,a);},this);return this;},getAllAdviceMessages:function(d,c){var b=[];if(d.hasClass("ignoreValidation")&&!c){return b; }var a=d.get("validators").some(function(g){var e=g.test("^warn-")||d.hasClass("warnOnly");if(e){g=g.replace(/^warn-/,"");}var f=this.getValidator(g);if(!f){return; }b.push({message:f.getError(d),warnOnly:e,passed:f.test(),validator:f});},this);return b;},getAdvice:function(a,b){return b.retrieve("$moo:advice-"+a); },insertAdvice:function(a,c){var b=c.get("validatorProps");if(!b.msgPos||!document.id(b.msgPos)){if(c.type&&c.type.toLowerCase()=="radio"){c.getParent().adopt(a); }else{a.inject(document.id(c),"after");}}else{document.id(b.msgPos).grab(a);}},validateField:function(g,f,b){var a=this.parent(g,f);if(((this.options.scrollToErrorsOnSubmit&&b==null)||b)&&!a){var c=document.id(this).getElement(".validation-failed"); var d=document.id(this).getParent();while(d!=document.body&&d.getScrollSize().y==d.getSize().y){d=d.getParent();}var e=d.retrieve("$moo:fvScroller");if(!e&&window.Fx&&Fx.Scroll){e=new Fx.Scroll(d,this.options.scrollFxOptions); d.store("$moo:fvScroller",e);}if(c){if(e){e.toElement(c);}else{d.scrollTo(d.getScroll().x,c.getPosition(d).y-20);}}}return a;},watchFields:function(a){a.each(function(b){if(this.options.evaluateFieldsOnBlur){b.addEvent("blur",this.validationMonitor.pass([b,false,this.options.scrollToErrorsOnBlur],this)); }if(this.options.evaluateFieldsOnChange){b.addEvent("change",this.validationMonitor.pass([b,true,this.options.scrollToErrorsOnChange],this));}},this);}}); Form.Validator.addAllThese([["validate-enforce-oncheck",{test:function(a,b){var c=a.getParent("form").retrieve("validator");if(!c){return true;}(b.toEnforce||document.id(b.enforceChildrenOf).getElements("input, select, textarea")).map(function(d){if(a.checked){c.enforceField(d); }else{c.ignoreField(d);c.resetField(d);}});return true;}}],["validate-ignore-oncheck",{test:function(a,b){var c=a.getParent("form").retrieve("validator"); if(!c){return true;}(b.toIgnore||document.id(b.ignoreChildrenOf).getElements("input, select, textarea")).each(function(d){if(a.checked){c.ignoreField(d); c.resetField(d);}else{c.enforceField(d);}});return true;}}],["validate-nospace",{errorMsg:function(){return Form.Validator.getMsg("noSpace");},test:function(a,b){return !a.get("value").test(/\s/); }}],["validate-toggle-oncheck",{test:function(b,c){var d=b.getParent("form").retrieve("validator");if(!d){return true;}var a=c.toToggle||document.id(c.toToggleChildrenOf).getElements("input, select, textarea"); if(!b.checked){a.each(function(e){d.ignoreField(e);d.resetField(e);});}else{a.each(function(e){d.enforceField(e);});}return true;}}],["validate-reqchk-bynode",{errorMsg:function(){return Form.Validator.getMsg("reqChkByNode"); },test:function(a,b){return(document.id(b.nodeId).getElements(b.selector||"input[type=checkbox], input[type=radio]")).some(function(c){return c.checked; });}}],["validate-required-check",{errorMsg:function(a,b){return b.useTitle?a.get("title"):Form.Validator.getMsg("requiredChk");},test:function(a,b){return !!a.checked; }}],["validate-reqchk-byname",{errorMsg:function(a,b){return Form.Validator.getMsg("reqChkByName").substitute({label:b.label||a.get("type")});},test:function(b,d){var c=d.groupName||b.get("name"); var a=$$(document.getElementsByName(c)).some(function(g,f){return g.checked;});var e=b.getParent("form").retrieve("validator");if(a&&e){e.resetField(b); }return a;}}],["validate-match",{errorMsg:function(a,b){return Form.Validator.getMsg("match").substitute({matchName:b.matchName||document.id(b.matchInput).get("name")}); },test:function(b,c){var d=b.get("value");var a=document.id(c.matchInput)&&document.id(c.matchInput).get("value");return d&&a?d==a:true;}}],["validate-after-date",{errorMsg:function(a,b){return Form.Validator.getMsg("afterDate").substitute({label:b.afterLabel||(b.afterElement?Form.Validator.getMsg("startDate"):Form.Validator.getMsg("currentDate"))}); },test:function(b,c){var d=document.id(c.afterElement)?Date.parse(document.id(c.afterElement).get("value")):new Date();var a=Date.parse(b.get("value")); return a&&d?a>=d:true;}}],["validate-before-date",{errorMsg:function(a,b){return Form.Validator.getMsg("beforeDate").substitute({label:b.beforeLabel||(b.beforeElement?Form.Validator.getMsg("endDate"):Form.Validator.getMsg("currentDate"))}); },test:function(b,c){var d=Date.parse(b.get("value"));var a=document.id(c.beforeElement)?Date.parse(document.id(c.beforeElement).get("value")):new Date(); return a&&d?a>=d:true;}}],["validate-custom-required",{errorMsg:function(){return Form.Validator.getMsg("required");},test:function(a,b){return a.get("value")!=b.emptyValue; }}],["validate-same-month",{errorMsg:function(a,b){var c=document.id(b.sameMonthAs)&&document.id(b.sameMonthAs).get("value");var d=a.get("value");if(d!=""){return Form.Validator.getMsg(c?"sameMonth":"startMonth"); }},test:function(a,b){var d=Date.parse(a.get("value"));var c=Date.parse(document.id(b.sameMonthAs)&&document.id(b.sameMonthAs).get("value"));return d&&c?d.format("%B")==c.format("%B"):true; }}],["validate-cc-num",{errorMsg:function(a){var b=a.get("value").replace(/[^0-9]/g,"");return Form.Validator.getMsg("creditcard").substitute({length:b.length}); },test:function(c){if(Form.Validator.getValidator("IsEmpty").test(c)){return true;}var g=c.get("value");g=g.replace(/[^0-9]/g,"");var a=false;if(g.test(/^4[0-9]{12}([0-9]{3})?$/)){a="Visa"; }else{if(g.test(/^5[1-5]([0-9]{14})$/)){a="Master Card";}else{if(g.test(/^3[47][0-9]{13}$/)){a="American Express";}else{if(g.test(/^6011[0-9]{12}$/)){a="Discover"; }}}}if(a){var d=0;var e=0;for(var b=g.length-1;b>=0;--b){e=g.charAt(b).toInt();if(e==0){continue;}if((g.length-b)%2==0){e+=e;}if(e>9){e=e.toString().charAt(0).toInt()+e.toString().charAt(1).toInt(); }d+=e;}if((d%10)==0){return true;}}var f="";while(g!=""){f+=" "+g.substr(0,4);g=g.substr(4);}c.getParent("form").retrieve("validator").ignoreField(c);c.set("value",f.clean()); c.getParent("form").retrieve("validator").enforceField(c);return false;}}]]);var OverText=new Class({Implements:[Options,Events,Class.Occlude],Binds:["reposition","assert","focus","hide"],options:{element:"label",labelClass:"overTxtLabel",positionOptions:{position:"upperLeft",edge:"upperLeft",offset:{x:4,y:2}},poll:false,pollInterval:250,wrap:false},property:"OverText",initialize:function(b,a){b=this.element=document.id(b); if(this.occlude()){return this.occluded;}this.setOptions(a);this.attach(b);OverText.instances.push(this);if(this.options.poll){this.poll();}},toElement:function(){return this.element; },attach:function(){var b=this.element,a=this.options,c=a.textOverride||b.get("alt")||b.get("title");if(!c){return this;}var d=this.text=new Element(a.element,{"class":a.labelClass,styles:{lineHeight:"normal",position:"absolute",cursor:"text"},html:c,events:{click:this.hide.pass(a.element=="label",this)}}).inject(b,"after"); if(a.element=="label"){if(!b.get("id")){b.set("id","input_"+String.uniqueID());}d.set("for",b.get("id"));}if(a.wrap){this.textHolder=new Element("div.overTxtWrapper",{styles:{lineHeight:"normal",position:"relative"}}).grab(d).inject(b,"before"); }return this.enable();},destroy:function(){this.element.eliminate(this.property);this.disable();if(this.text){this.text.destroy();}if(this.textHolder){this.textHolder.destroy(); }return this;},disable:function(){this.element.removeEvents({focus:this.focus,blur:this.assert,change:this.assert});window.removeEvent("resize",this.reposition); this.hide(true,true);return this;},enable:function(){this.element.addEvents({focus:this.focus,blur:this.assert,change:this.assert});window.addEvent("resize",this.reposition); this.assert(true);this.reposition();return this;},wrap:function(){if(this.options.element=="label"){if(!this.element.get("id")){this.element.set("id","input_"+String.uniqueID()); }this.text.set("for",this.element.get("id"));}},startPolling:function(){this.pollingPaused=false;return this.poll();},poll:function(a){if(this.poller&&!a){return this; }if(a){clearInterval(this.poller);}else{this.poller=(function(){if(!this.pollingPaused){this.assert(true);}}).periodical(this.options.pollInterval,this); }return this;},stopPolling:function(){this.pollingPaused=true;return this.poll(true);},focus:function(){if(this.text&&(!this.text.isDisplayed()||this.element.get("disabled"))){return this; }return this.hide();},hide:function(c,a){if(this.text&&(this.text.isDisplayed()&&(!this.element.get("disabled")||a))){this.text.hide();this.fireEvent("textHide",[this.text,this.element]); this.pollingPaused=true;if(!c){try{this.element.fireEvent("focus");this.element.focus();}catch(b){}}}return this;},show:function(){if(this.text&&!this.text.isDisplayed()){this.text.show(); this.reposition();this.fireEvent("textShow",[this.text,this.element]);this.pollingPaused=false;}return this;},test:function(){return !this.element.get("value"); },assert:function(a){return this[this.test()?"show":"hide"](a);},reposition:function(){this.assert(true);if(!this.element.isVisible()){return this.stopPolling().hide(); }if(this.text&&this.test()){this.text.position(Object.merge(this.options.positionOptions,{relativeTo:this.element}));}return this;}});OverText.instances=[]; Object.append(OverText,{each:function(a){return OverText.instances.each(function(c,b){if(c.element&&c.text){a.call(OverText,c,b);}});},update:function(){return OverText.each(function(a){return a.reposition(); });},hideAll:function(){return OverText.each(function(a){return a.hide(true,true);});},showAll:function(){return OverText.each(function(a){return a.show(); });}});Fx.Elements=new Class({Extends:Fx.CSS,initialize:function(b,a){this.elements=this.subject=$$(b);this.parent(a);},compute:function(g,h,j){var c={}; for(var d in g){var a=g[d],e=h[d],f=c[d]={};for(var b in a){f[b]=this.parent(a[b],e[b],j);}}return c;},set:function(b){for(var c in b){if(!this.elements[c]){continue; }var a=b[c];for(var d in a){this.render(this.elements[c],d,a[d],this.options.unit);}}return this;},start:function(c){if(!this.check(c)){return this;}var h={},j={}; for(var d in c){if(!this.elements[d]){continue;}var f=c[d],a=h[d]={},g=j[d]={};for(var b in f){var e=this.prepare(this.elements[d],b,f[b]);a[b]=e.from; g[b]=e.to;}}return this.parent(h,j);}});Fx.Accordion=new Class({Extends:Fx.Elements,options:{fixedHeight:false,fixedWidth:false,display:0,show:false,height:true,width:false,opacity:true,alwaysHide:false,trigger:"click",initialDisplayFx:true,resetHeight:true},initialize:function(){var g=function(h){return h!=null; };var f=Array.link(arguments,{container:Type.isElement,options:Type.isObject,togglers:g,elements:g});this.parent(f.elements,f.options);var b=this.options,e=this.togglers=$$(f.togglers); this.previous=-1;this.internalChain=new Chain();if(b.alwaysHide){this.options.link="chain";}if(b.show||this.options.show===0){b.display=false;this.previous=b.show; }if(b.start){b.display=false;b.show=false;}var d=this.effects={};if(b.opacity){d.opacity="fullOpacity";}if(b.width){d.width=b.fixedWidth?"fullWidth":"offsetWidth"; }if(b.height){d.height=b.fixedHeight?"fullHeight":"scrollHeight";}for(var c=0,a=e.length;c=0?a-1:0)).chain(d);}else{d();}return this;},detach:function(b){var a=function(c){c.removeEvent(this.options.trigger,c.retrieve("accordion:display")); }.bind(this);if(!b){this.togglers.each(a);}else{a(b);}return this;},display:function(b,c){if(!this.check(b,c)){return this;}var h={},g=this.elements,a=this.options,f=this.effects; if(c==null){c=true;}if(typeOf(b)=="element"){b=g.indexOf(b);}if(b==this.previous&&!a.alwaysHide){return this;}if(a.resetHeight){var e=g[this.previous]; if(e&&!this.selfHidden){for(var d in f){e.setStyle(d,e[f[d]]);}}}if((this.timer&&a.link=="chain")||(b===this.previous&&!a.alwaysHide)){return this;}this.previous=b; this.selfHidden=false;g.each(function(l,k){h[k]={};var j;if(k!=b){j=true;}else{if(a.alwaysHide&&((l.offsetHeight>0&&a.height)||l.offsetWidth>0&&a.width)){j=true; this.selfHidden=true;}}this.fireEvent(j?"background":"active",[this.togglers[k],l]);for(var m in f){h[k][m]=j?0:l[f[m]];}if(!c&&!j&&a.resetHeight){h[k].height="auto"; }},this);this.internalChain.clearChain();this.internalChain.chain(function(){if(a.resetHeight&&!this.selfHidden){var i=g[b];if(i){i.setStyle("height","auto"); }}}.bind(this));return c?this.start(h):this.set(h).internalChain.callChain();}});Fx.Move=new Class({Extends:Fx.Morph,options:{relativeTo:document.body,position:"center",edge:false,offset:{x:0,y:0}},start:function(a){var b=this.element,c=b.getStyles("top","left"); if(c.top=="auto"||c.left=="auto"){b.setPosition(b.getPosition(b.getOffsetParent()));}return this.parent(b.position(Object.merge({},this.options,a,{returnPos:true}))); }});Element.Properties.move={set:function(a){this.get("move").cancel().setOptions(a);return this;},get:function(){var a=this.retrieve("move");if(!a){a=new Fx.Move(this,{link:"cancel"}); this.store("move",a);}return a;}};Element.implement({move:function(a){this.get("move").start(a);return this;}});Fx.Slide=new Class({Extends:Fx,options:{mode:"vertical",wrapper:false,hideOverflow:true,resetHeight:false},initialize:function(b,a){b=this.element=this.subject=document.id(b); this.parent(a);a=this.options;var d=b.retrieve("wrapper"),c=b.getStyles("margin","position","overflow");if(a.hideOverflow){c=Object.append(c,{overflow:"hidden"}); }if(a.wrapper){d=document.id(a.wrapper).setStyles(c);}if(!d){d=new Element("div",{styles:c}).wraps(b);}b.store("wrapper",d).setStyle("margin",0);if(b.getStyle("overflow")=="visible"){b.setStyle("overflow","hidden"); }this.now=[];this.open=true;this.wrapper=d;this.addEvent("complete",function(){this.open=(d["offset"+this.layout.capitalize()]!=0);if(this.open&&this.options.resetHeight){d.setStyle("height",""); }},true);},vertical:function(){this.margin="margin-top";this.layout="height";this.offset=this.element.offsetHeight;},horizontal:function(){this.margin="margin-left"; this.layout="width";this.offset=this.element.offsetWidth;},set:function(a){this.element.setStyle(this.margin,a[0]);this.wrapper.setStyle(this.layout,a[1]); return this;},compute:function(c,b,a){return[0,1].map(function(d){return Fx.compute(c[d],b[d],a);});},start:function(b,e){if(!this.check(b,e)){return this; }this[e||this.options.mode]();var d=this.element.getStyle(this.margin).toInt(),c=this.wrapper.getStyle(this.layout).toInt(),a=[[d,c],[0,this.offset]],g=[[d,c],[-this.offset,0]],f; switch(b){case"in":f=a;break;case"out":f=g;break;case"toggle":f=(c==0)?a:g;}return this.parent(f[0],f[1]);},slideIn:function(a){return this.start("in",a); },slideOut:function(a){return this.start("out",a);},hide:function(a){this[a||this.options.mode]();this.open=false;return this.set([-this.offset,0]);},show:function(a){this[a||this.options.mode](); this.open=true;return this.set([0,this.offset]);},toggle:function(a){return this.start("toggle",a);}});Element.Properties.slide={set:function(a){this.get("slide").cancel().setOptions(a); return this;},get:function(){var a=this.retrieve("slide");if(!a){a=new Fx.Slide(this,{link:"cancel"});this.store("slide",a);}return a;}};Element.implement({slide:function(d,e){d=d||"toggle"; var b=this.get("slide"),a;switch(d){case"hide":b.hide(e);break;case"show":b.show(e);break;case"toggle":var c=this.retrieve("slide:flag",b.open);b[c?"slideOut":"slideIn"](e); this.store("slide:flag",!c);a=true;break;default:b.start(d,e);}if(!a){this.eliminate("slide:flag");}return this;}});(function(){Fx.Scroll=new Class({Extends:Fx,options:{offset:{x:0,y:0},wheelStops:true},initialize:function(c,b){this.element=this.subject=document.id(c); this.parent(b);if(typeOf(this.element)!="element"){this.element=document.id(this.element.getDocument().body);}if(this.options.wheelStops){var d=this.element,e=this.cancel.pass(false,this); this.addEvent("start",function(){d.addEvent("mousewheel",e);},true);this.addEvent("complete",function(){d.removeEvent("mousewheel",e);},true);}},set:function(){var b=Array.flatten(arguments); if(Browser.firefox){b=[Math.round(b[0]),Math.round(b[1])];}this.element.scrollTo(b[0],b[1]);return this;},compute:function(d,c,b){return[0,1].map(function(e){return Fx.compute(d[e],c[e],b); });},start:function(c,d){if(!this.check(c,d)){return this;}var b=this.element.getScroll();return this.parent([b.x,b.y],[c,d]);},calculateScroll:function(g,f){var d=this.element,b=d.getScrollSize(),h=d.getScroll(),j=d.getSize(),c=this.options.offset,i={x:g,y:f}; for(var e in i){if(!i[e]&&i[e]!==0){i[e]=h[e];}if(typeOf(i[e])!="number"){i[e]=b[e]-j[e];}i[e]+=c[e];}return[i.x,i.y];},toTop:function(){return this.start.apply(this,this.calculateScroll(false,0)); },toLeft:function(){return this.start.apply(this,this.calculateScroll(0,false));},toRight:function(){return this.start.apply(this,this.calculateScroll("right",false)); },toBottom:function(){return this.start.apply(this,this.calculateScroll(false,"bottom"));},toElement:function(d,e){e=e?Array.from(e):["x","y"];var c=a(this.element)?{x:0,y:0}:this.element.getScroll(); var b=Object.map(document.id(d).getPosition(this.element),function(g,f){return e.contains(f)?g+c[f]:false;});return this.start.apply(this,this.calculateScroll(b.x,b.y)); },toElementEdge:function(d,g,e){g=g?Array.from(g):["x","y"];d=document.id(d);var i={},f=d.getPosition(this.element),j=d.getSize(),h=this.element.getScroll(),b=this.element.getSize(),c={x:f.x+j.x,y:f.y+j.y}; ["x","y"].each(function(k){if(g.contains(k)){if(c[k]>h[k]+b[k]){i[k]=c[k]-b[k];}if(f[k]this.elements.length){e.splice(this.elements.length-1,e.length-this.elements.length); }}var b=0;i=a=0;e.each(function(k){var j={};if(d){j.top=i-f[k].top-b;i+=f[k].height;}else{j.left=a-f[k].left;a+=f[k].width;}b=b+f[k].margin;c[k]=j;},this); var g={};Array.clone(e).sort().each(function(j){g[j]=c[j];});this.start(g);this.currentOrder=e;return this;},rearrangeDOM:function(a){a=a||this.currentOrder; var b=this.elements[0].getParent();var c=[];this.elements.setStyle("opacity",0);a.each(function(d){c.push(this.elements[d].inject(b).setStyles({top:0,left:0})); },this);this.elements.setStyle("opacity",1);this.elements=$$(c);this.setDefaultOrder();return this;},getDefaultOrder:function(){return this.elements.map(function(b,a){return a; });},getCurrentOrder:function(){return this.currentOrder;},forward:function(){return this.sort(this.getDefaultOrder());},backward:function(){return this.sort(this.getDefaultOrder().reverse()); },reverse:function(){return this.sort(this.currentOrder.reverse());},sortByElements:function(a){return this.sort(a.map(function(b){return this.elements.indexOf(b); },this));},swap:function(c,b){if(typeOf(c)=="element"){c=this.elements.indexOf(c);}if(typeOf(b)=="element"){b=this.elements.indexOf(b);}var a=Array.clone(this.currentOrder); a[this.currentOrder.indexOf(c)]=b;a[this.currentOrder.indexOf(b)]=c;return this.sort(a);}});var Drag=new Class({Implements:[Events,Options],options:{snap:6,unit:"px",grid:false,style:true,limit:false,handle:false,invert:false,preventDefault:false,stopPropagation:false,modifiers:{x:"left",y:"top"}},initialize:function(){var b=Array.link(arguments,{options:Type.isObject,element:function(c){return c!=null; }});this.element=document.id(b.element);this.document=this.element.getDocument();this.setOptions(b.options||{});var a=typeOf(this.options.handle);this.handles=((a=="array"||a=="collection")?$$(this.options.handle):document.id(this.options.handle))||this.element; this.mouse={now:{},pos:{}};this.value={start:{},now:{}};this.selection=(Browser.ie)?"selectstart":"mousedown";if(Browser.ie&&!Drag.ondragstartFixed){document.ondragstart=Function.from(false); Drag.ondragstartFixed=true;}this.bound={start:this.start.bind(this),check:this.check.bind(this),drag:this.drag.bind(this),stop:this.stop.bind(this),cancel:this.cancel.bind(this),eventStop:Function.from(false)}; this.attach();},attach:function(){this.handles.addEvent("mousedown",this.bound.start);return this;},detach:function(){this.handles.removeEvent("mousedown",this.bound.start); return this;},start:function(a){var j=this.options;if(a.rightClick){return;}if(j.preventDefault){a.preventDefault();}if(j.stopPropagation){a.stopPropagation(); }this.mouse.start=a.page;this.fireEvent("beforeStart",this.element);var c=j.limit;this.limit={x:[],y:[]};var e,g;for(e in j.modifiers){if(!j.modifiers[e]){continue; }var b=this.element.getStyle(j.modifiers[e]);if(b&&!b.match(/px$/)){if(!g){g=this.element.getCoordinates(this.element.getOffsetParent());}b=g[j.modifiers[e]]; }if(j.style){this.value.now[e]=(b||0).toInt();}else{this.value.now[e]=this.element[j.modifiers[e]];}if(j.invert){this.value.now[e]*=-1;}this.mouse.pos[e]=a.page[e]-this.value.now[e]; if(c&&c[e]){var d=2;while(d--){var f=c[e][d];if(f||f===0){this.limit[e][d]=(typeof f=="function")?f():f;}}}}if(typeOf(this.options.grid)=="number"){this.options.grid={x:this.options.grid,y:this.options.grid}; }var h={mousemove:this.bound.check,mouseup:this.bound.cancel};h[this.selection]=this.bound.eventStop;this.document.addEvents(h);},check:function(a){if(this.options.preventDefault){a.preventDefault(); }var b=Math.round(Math.sqrt(Math.pow(a.page.x-this.mouse.start.x,2)+Math.pow(a.page.y-this.mouse.start.y,2)));if(b>this.options.snap){this.cancel();this.document.addEvents({mousemove:this.bound.drag,mouseup:this.bound.stop}); this.fireEvent("start",[this.element,a]).fireEvent("snap",this.element);}},drag:function(b){var a=this.options;if(a.preventDefault){b.preventDefault(); }this.mouse.now=b.page;for(var c in a.modifiers){if(!a.modifiers[c]){continue;}this.value.now[c]=this.mouse.now[c]-this.mouse.pos[c];if(a.invert){this.value.now[c]*=-1; }if(a.limit&&this.limit[c]){if((this.limit[c][1]||this.limit[c][1]===0)&&(this.value.now[c]>this.limit[c][1])){this.value.now[c]=this.limit[c][1];}else{if((this.limit[c][0]||this.limit[c][0]===0)&&(this.value.now[c]d.left&&b.xd.top);},this).getLast();if(this.overed!=a){if(this.overed){this.fireEvent("leave",[this.element,this.overed]); }if(a){this.fireEvent("enter",[this.element,a]);}this.overed=a;}},drag:function(a){this.parent(a);if(this.options.checkDroppables&&this.droppables.length){this.checkDroppables(); }},stop:function(a){this.checkDroppables();this.fireEvent("drop",[this.element,this.overed,a]);this.overed=null;return this.parent(a);}});Element.implement({makeDraggable:function(a){var b=new Drag.Move(this,a); this.store("dragger",b);return b;}});var Slider=new Class({Implements:[Events,Options],Binds:["clickedElement","draggedKnob","scrolledElement"],options:{onTick:function(a){this.setKnobPosition(a); },initialStep:0,snap:false,offset:0,range:false,wheel:false,steps:100,mode:"horizontal"},initialize:function(f,a,e){this.setOptions(e);e=this.options;this.element=document.id(f); a=this.knob=document.id(a);this.previousChange=this.previousEnd=this.step=-1;var b={},d={x:false,y:false};switch(e.mode){case"vertical":this.axis="y";this.property="top"; this.offset="offsetHeight";break;case"horizontal":this.axis="x";this.property="left";this.offset="offsetWidth";}this.setSliderDimensions();this.setRange(e.range); if(a.getStyle("position")=="static"){a.setStyle("position","relative");}a.setStyle(this.property,-e.offset);d[this.axis]=this.property;b[this.axis]=[-e.offset,this.full-e.offset]; var c={snap:0,limit:b,modifiers:d,onDrag:this.draggedKnob,onStart:this.draggedKnob,onBeforeStart:(function(){this.isDragging=true;}).bind(this),onCancel:function(){this.isDragging=false; }.bind(this),onComplete:function(){this.isDragging=false;this.draggedKnob();this.end();}.bind(this)};if(e.snap){this.setSnap(c);}this.drag=new Drag(a,c); this.attach();if(e.initialStep!=null){this.set(e.initialStep);}},attach:function(){this.element.addEvent("mousedown",this.clickedElement);if(this.options.wheel){this.element.addEvent("mousewheel",this.scrolledElement); }this.drag.attach();return this;},detach:function(){this.element.removeEvent("mousedown",this.clickedElement).removeEvent("mousewheel",this.scrolledElement); this.drag.detach();return this;},autosize:function(){this.setSliderDimensions().setKnobPosition(this.toPosition(this.step));this.drag.options.limit[this.axis]=[-this.options.offset,this.full-this.options.offset]; if(this.options.snap){this.setSnap();}return this;},setSnap:function(a){if(!a){a=this.drag.options;}a.grid=Math.ceil(this.stepWidth);a.limit[this.axis][1]=this.full; return this;},setKnobPosition:function(a){if(this.options.snap){a=this.toPosition(this.step);}this.knob.setStyle(this.property,a);return this;},setSliderDimensions:function(){this.full=this.element.measure(function(){this.half=this.knob[this.offset]/2; return this.element[this.offset]-this.knob[this.offset]+(this.options.offset*2);}.bind(this));return this;},set:function(a){if(!((this.range>0)^(a0)^(a>this.max))){a=this.max;}this.step=Math.round(a);return this.checkStep().fireEvent("tick",this.toPosition(this.step)).end();},setRange:function(a,b){this.min=Array.pick([a[0],0]); this.max=Array.pick([a[1],this.options.steps]);this.range=this.max-this.min;this.steps=this.options.steps||this.full;this.stepSize=Math.abs(this.range)/this.steps; this.stepWidth=this.stepSize*this.full/Math.abs(this.range);if(a){this.set(Array.pick([b,this.step]).floor(this.min).max(this.max));}return this;},clickedElement:function(c){if(this.isDragging||c.target==this.knob){return; }var b=this.range<0?-1:1,a=c.page[this.axis]-this.element.getPosition()[this.axis]-this.half;a=a.limit(-this.options.offset,this.full-this.options.offset); this.step=Math.round(this.min+b*this.toStep(a));this.checkStep().fireEvent("tick",a).end();},scrolledElement:function(a){var b=(this.options.mode=="horizontal")?(a.wheel<0):(a.wheel>0); this.set(this.step+(b?-1:1)*this.stepSize);a.stop();},draggedKnob:function(){var b=this.range<0?-1:1,a=this.drag.value.now[this.axis];a=a.limit(-this.options.offset,this.full-this.options.offset); this.step=Math.round(this.min+b*this.toStep(a));this.checkStep();},checkStep:function(){var a=this.step;if(this.previousChange!=a){this.previousChange=a; this.fireEvent("change",a);}return this;},end:function(){var a=this.step;if(this.previousEnd!==a){this.previousEnd=a;this.fireEvent("complete",a+"");}return this; },toStep:function(a){var b=(a+this.options.offset)*this.stepSize/this.full*this.steps;return this.options.steps?Math.round(b-=b%this.stepSize):b;},toPosition:function(a){return(this.full*Math.abs(this.min-a))/(this.steps*this.stepSize)-this.options.offset; }});var Sortables=new Class({Implements:[Events,Options],options:{opacity:1,clone:false,revert:false,handle:false,dragOptions:{}},initialize:function(a,b){this.setOptions(b); this.elements=[];this.lists=[];this.idle=true;this.addLists($$(document.id(a)||a));if(!this.options.clone){this.options.revert=false;}if(this.options.revert){this.effect=new Fx.Morph(null,Object.merge({duration:250,link:"cancel"},this.options.revert)); }},attach:function(){this.addLists(this.lists);return this;},detach:function(){this.lists=this.removeLists(this.lists);return this;},addItems:function(){Array.flatten(arguments).each(function(a){this.elements.push(a); var b=a.retrieve("sortables:start",function(c){this.start.call(this,c,a);}.bind(this));(this.options.handle?a.getElement(this.options.handle)||a:a).addEvent("mousedown",b); },this);return this;},addLists:function(){Array.flatten(arguments).each(function(a){this.lists.include(a);this.addItems(a.getChildren());},this);return this; },removeItems:function(){return $$(Array.flatten(arguments).map(function(a){this.elements.erase(a);var b=a.retrieve("sortables:start");(this.options.handle?a.getElement(this.options.handle)||a:a).removeEvent("mousedown",b); return a;},this));},removeLists:function(){return $$(Array.flatten(arguments).map(function(a){this.lists.erase(a);this.removeItems(a.getChildren());return a; },this));},getClone:function(b,a){if(!this.options.clone){return new Element(a.tagName).inject(document.body);}if(typeOf(this.options.clone)=="function"){return this.options.clone.call(this,b,a,this.list); }var c=a.clone(true).setStyles({margin:0,position:"absolute",visibility:"hidden",width:a.getStyle("width")}).addEvent("mousedown",function(d){a.fireEvent("mousedown",d); });if(c.get("html").test("radio")){c.getElements("input[type=radio]").each(function(d,e){d.set("name","clone_"+e);if(d.get("checked")){a.getElements("input[type=radio]")[e].set("checked",true); }});}return c.inject(this.list).setPosition(a.getPosition(a.getOffsetParent()));},getDroppables:function(){var a=this.list.getChildren().erase(this.clone).erase(this.element); if(!this.options.constrain){a.append(this.lists).erase(this.list);}return a;},insert:function(c,b){var a="inside";if(this.lists.contains(b)){this.list=b; this.drag.droppables=this.getDroppables();}else{a=this.element.getAllPrevious().contains(b)?"before":"after";}this.element.inject(b,a);this.fireEvent("sort",[this.element,this.clone]); },start:function(b,a){if(!this.idle||b.rightClick||["button","input","a"].contains(b.target.get("tag"))){return;}this.idle=false;this.element=a;this.opacity=a.get("opacity"); this.list=a.getParent();this.clone=this.getClone(b,a);this.drag=new Drag.Move(this.clone,Object.merge({droppables:this.getDroppables()},this.options.dragOptions)).addEvents({onSnap:function(){b.stop(); this.clone.setStyle("visibility","visible");this.element.set("opacity",this.options.opacity||0);this.fireEvent("start",[this.element,this.clone]);}.bind(this),onEnter:this.insert.bind(this),onCancel:this.end.bind(this),onComplete:this.end.bind(this)}); this.clone.inject(this.element,"before");this.drag.start(b);},end:function(){this.drag.detach();this.element.set("opacity",this.opacity);if(this.effect){var b=this.element.getStyles("width","height"),d=this.clone,c=d.computePosition(this.element.getPosition(this.clone.getOffsetParent())); var a=function(){this.removeEvent("cancel",a);d.destroy();};this.effect.element=d;this.effect.start({top:c.top,left:c.left,width:b.width,height:b.height,opacity:0.25}).addEvent("cancel",a).chain(a); }else{this.clone.destroy();}this.reset();},reset:function(){this.idle=true;this.fireEvent("complete",this.element);},serialize:function(){var c=Array.link(arguments,{modifier:Type.isFunction,index:function(d){return d!=null; }});var b=this.lists.map(function(d){return d.getChildren().map(c.modifier||function(e){return e.get("id");},this);},this);var a=c.index;if(this.lists.length==1){a=0; }return(a||a===0)&&a>=0&&a2083){this.fireEvent("error",f);}Request.JSONP.request_map["request_"+b]=function(){this.success(arguments,b);}.bind(this);var a=this.getScript(f).inject(c.injectScript); this.fireEvent("request",[f,a]);if(c.timeout){this.timeout.delay(c.timeout,this);}return this;},getScript:function(a){if(!this.script){this.script=new Element("script",{type:"text/javascript",async:true,src:a}); }return this.script;},success:function(b,a){if(!this.running){return;}this.clear().fireEvent("complete",b).fireEvent("success",b).callChain();},cancel:function(){if(this.running){this.clear().fireEvent("cancel"); }return this;},isRunning:function(){return !!this.running;},clear:function(){this.running=false;if(this.script){this.script.destroy();this.script=null; }return this;},timeout:function(){if(this.running){this.running=false;this.fireEvent("timeout",[this.script.get("src"),this.script]).fireEvent("failure").cancel(); }return this;}});Request.JSONP.counter=0;Request.JSONP.request_map={};Request.Queue=new Class({Implements:[Options,Events],Binds:["attach","request","complete","cancel","success","failure","exception"],options:{stopOnFailure:true,autoAdvance:true,concurrent:1,requests:{}},initialize:function(a){var b; if(a){b=a.requests;delete a.requests;}this.setOptions(a);this.requests={};this.queue=[];this.reqBinders={};if(b){this.addRequests(b);}},addRequest:function(a,b){this.requests[a]=b; this.attach(a,b);return this;},addRequests:function(a){Object.each(a,function(c,b){this.addRequest(b,c);},this);return this;},getName:function(a){return Object.keyOf(this.requests,a); },attach:function(a,b){if(b._groupSend){return this;}["request","complete","cancel","success","failure","exception"].each(function(c){if(!this.reqBinders[a]){this.reqBinders[a]={}; }this.reqBinders[a][c]=function(){this["on"+c.capitalize()].apply(this,[a,b].append(arguments));}.bind(this);b.addEvent(c,this.reqBinders[a][c]);},this); b._groupSend=b.send;b.send=function(c){this.send(a,c);return b;}.bind(this);return this;},removeRequest:function(b){var a=typeOf(b)=="object"?this.getName(b):b; if(!a&&typeOf(a)!="string"){return this;}b=this.requests[a];if(!b){return this;}["request","complete","cancel","success","failure","exception"].each(function(c){b.removeEvent(c,this.reqBinders[a][c]); },this);b.send=b._groupSend;delete b._groupSend;return this;},getRunning:function(){return Object.filter(this.requests,function(a){return a.running;}); },isRunning:function(){return !!(Object.keys(this.getRunning()).length);},send:function(b,a){var c=function(){this.requests[b]._groupSend(a);this.queue.erase(c); }.bind(this);c.name=b;if(Object.keys(this.getRunning()).length>=this.options.concurrent||(this.error&&this.options.stopOnFailure)){this.queue.push(c);}else{c(); }return this;},hasNext:function(a){return(!a)?!!this.queue.length:!!this.queue.filter(function(b){return b.name==a;}).length;},resume:function(){this.error=false; (this.options.concurrent-Object.keys(this.getRunning()).length).times(this.runNext,this);return this;},runNext:function(a){if(!this.queue.length){return this; }if(!a){this.queue[0]();}else{var b;this.queue.each(function(c){if(!b&&c.name==a){b=true;c();}});}return this;},runAll:function(){this.queue.each(function(a){a(); });return this;},clear:function(a){if(!a){this.queue.empty();}else{this.queue=this.queue.map(function(b){if(b.name!=a){return b;}else{return false;}}).filter(function(b){return b; });}return this;},cancel:function(a){this.requests[a].cancel();return this;},onRequest:function(){this.fireEvent("request",arguments);},onComplete:function(){this.fireEvent("complete",arguments); if(!this.queue.length){this.fireEvent("end");}},onCancel:function(){if(this.options.autoAdvance&&!this.error){this.runNext();}this.fireEvent("cancel",arguments); },onSuccess:function(){if(this.options.autoAdvance&&!this.error){this.runNext();}this.fireEvent("success",arguments);},onFailure:function(){this.error=true; if(!this.options.stopOnFailure&&this.options.autoAdvance){this.runNext();}this.fireEvent("failure",arguments);},onException:function(){this.error=true; if(!this.options.stopOnFailure&&this.options.autoAdvance){this.runNext();}this.fireEvent("exception",arguments);}});Request.implement({options:{initialDelay:5000,delay:5000,limit:60000},startTimer:function(b){var a=function(){if(!this.running){this.send({data:b}); }};this.lastDelay=this.options.initialDelay;this.timer=a.delay(this.lastDelay,this);this.completeCheck=function(c){clearTimeout(this.timer);this.lastDelay=(c)?this.options.delay:(this.lastDelay+this.options.delay).min(this.options.limit); this.timer=a.delay(this.lastDelay,this);};return this.addEvent("complete",this.completeCheck);},stopTimer:function(){clearTimeout(this.timer);return this.removeEvent("complete",this.completeCheck); }});var Asset={javascript:function(f,c){if(!c){c={};}var a=new Element("script",{src:f,type:"text/javascript"}),g=c.document||document,b=0,d=c.onload||c.onLoad; var e=d?function(){if(++b==1){d.call(this);}}:function(){};delete c.onload;delete c.onLoad;delete c.document;return a.addEvents({load:e,readystatechange:function(){if(["loaded","complete"].contains(this.readyState)){e.call(this); }}}).set(c).inject(g.head);},css:function(d,a){if(!a){a={};}var b=new Element("link",{rel:"stylesheet",media:"screen",type:"text/css",href:d});var c=a.onload||a.onLoad,e=a.document||document; delete a.onload;delete a.onLoad;delete a.document;if(c){b.addEvent("load",c);}return b.set(a).inject(e.head);},image:function(c,b){if(!b){b={};}var d=new Image(),a=document.id(d)||new Element("img"); ["load","abort","error"].each(function(e){var g="on"+e,f="on"+e.capitalize(),h=b[g]||b[f]||function(){};delete b[f];delete b[g];d[g]=function(){if(!d){return; }if(!a.parentNode){a.width=d.width;a.height=d.height;}d=d.onload=d.onabort=d.onerror=null;h.delay(1,a,a);a.fireEvent(e,a,1);};});d.src=a.src=c;if(d&&d.complete){d.onload.delay(1); }return a.set(b);},images:function(c,b){c=Array.from(c);var d=function(){},a=0;b=Object.merge({onComplete:d,onProgress:d,onError:d,properties:{}},b);return new Elements(c.map(function(f,e){return Asset.image(f,Object.append(b.properties,{onload:function(){a++; b.onProgress.call(this,a,e,f);if(a==c.length){b.onComplete();}},onerror:function(){a++;b.onError.call(this,a,e,f);if(a==c.length){b.onComplete();}}})); }));}};(function(){var a=this.Color=new Type("Color",function(c,d){if(arguments.length>=3){d="rgb";c=Array.slice(arguments,0,3);}else{if(typeof c=="string"){if(c.match(/rgb/)){c=c.rgbToHex().hexToRgb(true); }else{if(c.match(/hsb/)){c=c.hsbToRgb();}else{c=c.hexToRgb(true);}}}}d=d||"rgb";switch(d){case"hsb":var b=c;c=c.hsbToRgb();c.hsb=b;break;case"hex":c=c.hexToRgb(true); break;}c.rgb=c.slice(0,3);c.hsb=c.hsb||c.rgbToHsb();c.hex=c.rgbToHex();return Object.append(c,this);});a.implement({mix:function(){var b=Array.slice(arguments); var d=(typeOf(b.getLast())=="number")?b.pop():50;var c=this.slice();b.each(function(e){e=new a(e);for(var f=0;f<3;f++){c[f]=Math.round((c[f]/100*(100-d))+(e[f]/100*d)); }});return new a(c,"rgb");},invert:function(){return new a(this.map(function(b){return 255-b;}));},setHue:function(b){return new a([b,this.hsb[1],this.hsb[2]],"hsb"); },setSaturation:function(b){return new a([this.hsb[0],b,this.hsb[2]],"hsb");},setBrightness:function(b){return new a([this.hsb[0],this.hsb[1],b],"hsb"); }});this.$RGB=function(e,d,c){return new a([e,d,c],"rgb");};this.$HSB=function(e,d,c){return new a([e,d,c],"hsb");};this.$HEX=function(b){return new a(b,"hex"); };Array.implement({rgbToHsb:function(){var c=this[0],d=this[1],k=this[2],h=0;var j=Math.max(c,d,k),f=Math.min(c,d,k);var l=j-f;var i=j/255,g=(j!=0)?l/j:0; if(g!=0){var e=(j-c)/l;var b=(j-d)/l;var m=(j-k)/l;if(c==j){h=m-b;}else{if(d==j){h=2+e-m;}else{h=4+b-e;}}h/=6;if(h<0){h++;}}return[Math.round(h*360),Math.round(g*100),Math.round(i*100)]; },hsbToRgb:function(){var d=Math.round(this[2]/100*255);if(this[1]==0){return[d,d,d];}else{var b=this[0]%360;var g=b%60;var h=Math.round((this[2]*(100-this[1]))/10000*255); var e=Math.round((this[2]*(6000-this[1]*g))/600000*255);var c=Math.round((this[2]*(6000-this[1]*(60-g)))/600000*255);switch(Math.floor(b/60)){case 0:return[d,c,h]; case 1:return[e,d,h];case 2:return[h,d,c];case 3:return[h,e,d];case 4:return[c,h,d];case 5:return[d,h,e];}}return false;}});String.implement({rgbToHsb:function(){var b=this.match(/\d{1,3}/g); return(b)?b.rgbToHsb():null;},hsbToRgb:function(){var b=this.match(/\d{1,3}/g);return(b)?b.hsbToRgb():null;}});})();(function(){this.Group=new Class({initialize:function(){this.instances=Array.flatten(arguments); this.events={};this.checker={};},addEvent:function(b,a){this.checker[b]=this.checker[b]||{};this.events[b]=this.events[b]||[];if(this.events[b].contains(a)){return false; }else{this.events[b].push(a);}this.instances.each(function(c,d){c.addEvent(b,this.check.pass([b,c,d],this));},this);return this;},check:function(c,a,b){this.checker[c][b]=true; var d=this.instances.every(function(f,e){return this.checker[c][e]||false;},this);if(!d){return;}this.checker[c]={};this.events[c].each(function(e){e.call(this,this.instances,a); },this);}});})();Hash.Cookie=new Class({Extends:Cookie,options:{autoSave:true},initialize:function(b,a){this.parent(b,a);this.load();},save:function(){var a=JSON.encode(this.hash); if(!a||a.length>4096){return false;}if(a=="{}"){this.dispose();}else{this.write(a);}return true;},load:function(){this.hash=new Hash(JSON.decode(this.read(),true)); return this;}});Hash.each(Hash.prototype,function(b,a){if(typeof b=="function"){Hash.Cookie.implement(a,function(){var c=b.apply(this.hash,arguments);if(this.options.autoSave){this.save(); }return c;});}});(function(){var a=this.Table=function(){this.length=0;var c=[],b=[];this.set=function(e,g){var d=c.indexOf(e);if(d==-1){var f=c.length; c[f]=e;b[f]=g;this.length++;}else{b[d]=g;}return this;};this.get=function(e){var d=c.indexOf(e);return(d==-1)?null:b[d];};this.erase=function(e){var d=c.indexOf(e); if(d!=-1){this.length--;c.splice(d,1);return b.splice(d,1)[0];}return null;};this.each=this.forEach=function(f,g){for(var e=0,d=this.length;e1?$$(a):a.length?document.id(a[0]):false;},setHeaders:function(a){this.set("headers",a); return this;},setFooters:function(a){this.set("footers",a);return this;},push:function(f,c,e,a,b){if(typeOf(f)=="element"&&f.get("tag")=="tr"){f.inject(e||this.body,b); return{tr:f,tds:f.getChildren("td")};}var d=f.map(function(i){var j=new Element(a||"td",i?i.properties:{}),h=(i?i.content:"")||i,g=typeOf(h);if(["element","array","collection","elements"].contains(g)){j.adopt(h); }else{j.set("html",h);}return j;});return{tr:new Element("tr",c).inject(e||this.body,b).adopt(d),tds:d};}});["adopt","inject","wraps","grab","replaces","dispose"].each(function(a){HtmlTable.implement(a,function(){this.element[a].apply(this.element,arguments); return this;});});HtmlTable=Class.refactor(HtmlTable,{options:{classZebra:"table-tr-odd",zebra:true},initialize:function(){this.previous.apply(this,arguments); if(this.occluded){return this.occluded;}if(this.options.zebra){this.updateZebras();}},updateZebras:function(){Array.each(this.body.rows,this.zebra,this); },setRowStyle:function(b,a){if(this.previous){this.previous(b,a);}this.zebra(b,a);},zebra:function(b,a){return b[((a%2)?"remove":"add")+"Class"](this.options.classZebra); },push:function(){var a=this.previous.apply(this,arguments);if(this.options.zebra){this.updateZebras();}return a;}});HtmlTable=Class.refactor(HtmlTable,{options:{sortIndex:0,sortReverse:false,parsers:[],defaultParser:"string",classSortable:"table-sortable",classHeadSort:"table-th-sort",classHeadSortRev:"table-th-sort-rev",classNoSort:"table-th-nosort",classGroupHead:"table-tr-group-head",classGroup:"table-tr-group",classCellSort:"table-td-sort",classSortSpan:"table-th-sort-span",sortable:false,thSelector:"th"},initialize:function(){this.previous.apply(this,arguments); if(this.occluded){return this.occluded;}this.sorted={index:null,dir:1};this.bound={headClick:this.headClick.bind(this)};this.sortSpans=new Elements();if(this.options.sortable){this.enableSort(); if(this.options.sortIndex!=null){this.sort(this.options.sortIndex,this.options.sortReverse);}}},attachSorts:function(a){this.detachSorts();if(a!==false){this.element.addEvent("click:relay("+this.options.thSelector+")",this.bound.headClick); }},detachSorts:function(){this.element.removeEvents("click:relay("+this.options.thSelector+")");},setHeaders:function(){this.previous.apply(this,arguments); if(this.sortEnabled){this.setParsers();}},setParsers:function(){this.parsers=this.detectParsers();},detectParsers:function(){return this.head&&this.head.getElements(this.options.thSelector).flatten().map(this.detectParser,this); },detectParser:function(a,b){if(a.hasClass(this.options.classNoSort)||a.retrieve("htmltable-parser")){return a.retrieve("htmltable-parser");}var c=new Element("div"); c.adopt(a.childNodes).inject(a);var f=new Element("span",{"class":this.options.classSortSpan}).inject(c,"top");this.sortSpans.push(f);var g=this.options.parsers[b],e=this.body.rows,d; switch(typeOf(g)){case"function":g={convert:g};d=true;break;case"string":g=g;d=true;break;}if(!d){HtmlTable.ParserPriority.some(function(k){var o=HtmlTable.Parsers[k],m=o.match; if(!m){return false;}for(var n=0,l=e.length;nc){b[f].position--;}}}},setRowStyle:function(b,a){this.previous(b,a);b.cells[this.sorted.index].addClass(this.options.classCellSort); },setGroupSort:function(b,c,a){if(b==a.value){c.removeClass(this.options.classGroupHead).addClass(this.options.classGroup);}else{c.removeClass(this.options.classGroup).addClass(this.options.classGroupHead); }return a.value;},getParser:function(){var a=this.parsers[this.sorted.index];return typeOf(a)=="string"?HtmlTable.Parsers[a]:a;},sort:function(c,b,e){if(!this.head){return; }if(!e){this.clearSort();this.setSortedState(c,b);this.setHeadSort(true);}var f=this.getParser();if(!f){return;}var a;if(!Browser.ie){a=this.body.getParent(); this.body.dispose();}var d=this.parseData(f).sort(function(h,g){if(h.value===g.value){return 0;}return h.value>g.value?1:-1;});if(this.sorted.reverse==(f==HtmlTable.Parsers["input-checked"])){d.reverse(true); }this.setRowSort(d,e);if(a){a.grab(this.body);}this.fireEvent("stateChanged");return this.fireEvent("sort",[this.body,this.sorted.index]);},parseData:function(a){return Array.map(this.body.rows,function(d,b){var c=a.convert.call(document.id(d.cells[this.sorted.index])); return{position:b,value:c};},this);},clearSort:function(){this.setHeadSort(false);this.body.getElements("td").removeClass(this.options.classCellSort);},reSort:function(){if(this.sortEnabled){this.sort.call(this,this.sorted.index,this.sorted.reverse); }return this;},enableSort:function(){this.element.addClass(this.options.classSortable);this.attachSorts(true);this.setParsers();this.sortEnabled=true;return this; },disableSort:function(){this.element.removeClass(this.options.classSortable);this.attachSorts(false);this.sortSpans.each(function(a){a.destroy();});this.sortSpans.empty(); this.sortEnabled=false;return this;}});HtmlTable.ParserPriority=["date","input-checked","input-value","float","number"];HtmlTable.Parsers={date:{match:/^\d{2}[-\/ ]\d{2}[-\/ ]\d{2,4}$/,convert:function(){var a=Date.parse(this.get("text").stripTags()); return(typeOf(a)=="date")?a.format("db"):"";},type:"date"},"input-checked":{match:/ type="(radio|checkbox)" /,convert:function(){return this.getElement("input").checked; }},"input-value":{match:/=b.length){a=null;}return a;},attachSelects:function(d){d=d!=null?d:true; var g=d?"addEvents":"removeEvents";this.element[g]({mouseleave:this.bound.mouseleave,click:this.bound.activateKeyboard});this.body[g]({"click:relay(tr)":this.bound.clickRow,"contextmenu:relay(tr)":this.bound.clickRow}); if(this.options.useKeyboard||this.keyboard){if(!this.keyboard){this.keyboard=new Keyboard();}if(!this.selectKeysDefined){this.selectKeysDefined=true;var f,e; var c=function(i){var h=function(j){clearTimeout(f);j.preventDefault();var k=this.body.rows[this.getRowByOffset(i)];if(j.shift&&k&&this.isSelected(k)){this.deselectRow(this.focused); this.focused=k;}else{if(k&&(!this.options.allowMultiSelect||!j.shift)){this.selectNone();}this.shiftFocus(i,j);}if(e){f=h.delay(100,this,j);}else{f=(function(){e=true; h(j);}).delay(400);}}.bind(this);return h;}.bind(this);var b=function(){clearTimeout(f);e=false;};this.keyboard.addEvents({"keydown:shift+up":c(-1),"keydown:shift+down":c(1),"keyup:shift+up":b,"keyup:shift+down":b,"keyup:up":b,"keyup:down":b}); var a="";if(this.options.allowMultiSelect&&this.options.shiftForMultiSelect&&this.options.useKeyboard){a=" (Shift multi-selects).";}this.keyboard.addShortcuts({"Select Previous Row":{keys:"up",shortcut:"up arrow",handler:c(-1),description:"Select the previous row in the table."+a},"Select Next Row":{keys:"down",shortcut:"down arrow",handler:c(1),description:"Select the next row in the table."+a}}); }this.keyboard[d?"activate":"deactivate"]();}this.updateSelects();},mouseleave:function(){if(this.hovered){this.leaveRow(this.hovered);}}});var Scroller=new Class({Implements:[Events,Options],options:{area:20,velocity:1,onChange:function(a,b){this.element.scrollTo(a,b); },fps:50},initialize:function(b,a){this.setOptions(a);this.element=document.id(b);this.docBody=document.id(this.element.getDocument().body);this.listener=(typeOf(this.element)!="element")?this.docBody:this.element; this.timer=null;this.bound={attach:this.attach.bind(this),detach:this.detach.bind(this),getCoords:this.getCoords.bind(this)};},start:function(){this.listener.addEvents({mouseover:this.bound.attach,mouseleave:this.bound.detach}); return this;},stop:function(){this.listener.removeEvents({mouseover:this.bound.attach,mouseleave:this.bound.detach});this.detach();this.timer=clearInterval(this.timer); return this;},attach:function(){this.listener.addEvent("mousemove",this.bound.getCoords);},detach:function(){this.listener.removeEvent("mousemove",this.bound.getCoords); this.timer=clearInterval(this.timer);},getCoords:function(a){this.page=(this.listener.get("tag")=="body")?a.client:a.page;if(!this.timer){this.timer=this.scroll.periodical(Math.round(1000/this.options.fps),this); }},scroll:function(){var c=this.element.getSize(),a=this.element.getScroll(),h=this.element!=this.docBody?this.element.getOffsets():{x:0,y:0},d=this.element.getScrollSize(),g={x:0,y:0},e=this.options.area.top||this.options.area,b=this.options.area.bottom||this.options.area; for(var f in this.page){if(this.page[f]<(e+h[f])&&a[f]!=0){g[f]=(this.page[f]-e-h[f])*this.options.velocity;}else{if(this.page[f]+b>(c[f]+h[f])&&a[f]+c[f]!=d[f]){g[f]=(this.page[f]-c[f]+b-h[f])*this.options.velocity; }}g[f]=g[f].round();}if(g.y||g.x){this.fireEvent("change",[a.x+g.x,a.y+g.y]);}}});(function(){var a=function(c,b){return(c)?(typeOf(c)=="function"?c(b):b.get(c)):""; };this.Tips=new Class({Implements:[Events,Options],options:{onShow:function(){this.tip.setStyle("display","block");},onHide:function(){this.tip.setStyle("display","none"); },title:"title",text:function(b){return b.get("rel")||b.get("href");},showDelay:100,hideDelay:100,className:"tip-wrap",offset:{x:16,y:16},windowPadding:{x:0,y:0},fixed:false},initialize:function(){var b=Array.link(arguments,{options:Type.isObject,elements:function(c){return c!=null; }});this.setOptions(b.options);if(b.elements){this.attach(b.elements);}this.container=new Element("div",{"class":"tip"});},toElement:function(){if(this.tip){return this.tip; }this.tip=new Element("div",{"class":this.options.className,styles:{position:"absolute",top:0,left:0}}).adopt(new Element("div",{"class":"tip-top"}),this.container,new Element("div",{"class":"tip-bottom"})); return this.tip;},attach:function(b){$$(b).each(function(d){var f=a(this.options.title,d),e=a(this.options.text,d);d.set("title","").store("tip:native",f).retrieve("tip:title",f); d.retrieve("tip:text",e);this.fireEvent("attach",[d]);var c=["enter","leave"];if(!this.options.fixed){c.push("move");}c.each(function(h){var g=d.retrieve("tip:"+h); if(!g){g=function(i){this["element"+h.capitalize()].apply(this,[i,d]);}.bind(this);}d.store("tip:"+h,g).addEvent("mouse"+h,g);},this);},this);return this; },detach:function(b){$$(b).each(function(d){["enter","leave","move"].each(function(e){d.removeEvent("mouse"+e,d.retrieve("tip:"+e)).eliminate("tip:"+e); });this.fireEvent("detach",[d]);if(this.options.title=="title"){var c=d.retrieve("tip:native");if(c){d.set("title",c);}}},this);return this;},elementEnter:function(c,b){clearTimeout(this.timer); this.timer=(function(){this.container.empty();["title","text"].each(function(e){var d=b.retrieve("tip:"+e);var f=this["_"+e+"Element"]=new Element("div",{"class":"tip-"+e}).inject(this.container); if(d){this.fill(f,d);}},this);this.show(b);this.position((this.options.fixed)?{page:b.getPosition()}:c);}).delay(this.options.showDelay,this);},elementLeave:function(c,b){clearTimeout(this.timer); this.timer=this.hide.delay(this.options.hideDelay,this,b);this.fireForParent(c,b);},setTitle:function(b){if(this._titleElement){this._titleElement.empty(); this.fill(this._titleElement,b);}return this;},setText:function(b){if(this._textElement){this._textElement.empty();this.fill(this._textElement,b);}return this; },fireForParent:function(c,b){b=b.getParent();if(!b||b==document.body){return;}if(b.retrieve("tip:enter")){b.fireEvent("mouseenter",c);}else{this.fireForParent(c,b); }},elementMove:function(c,b){this.position(c);},position:function(f){if(!this.tip){document.id(this);}var c=window.getSize(),b=window.getScroll(),g={x:this.tip.offsetWidth,y:this.tip.offsetHeight},d={x:"left",y:"top"},e={y:false,x2:false,y2:false,x:false},h={}; for(var i in d){h[d[i]]=f.page[i]+this.options.offset[i];if(h[d[i]]<0){e[i]=true;}if((h[d[i]]+g[i]-b[i])>c[i]-this.options.windowPadding[i]){h[d[i]]=f.page[i]-this.options.offset[i]-g[i]; e[i+"2"]=true;}}this.fireEvent("bound",e);this.tip.setStyles(h);},fill:function(b,c){if(typeof c=="string"){b.set("html",c);}else{b.adopt(c);}},show:function(b){if(!this.tip){document.id(this); }if(!this.tip.getParent()){this.tip.inject(document.body);}this.fireEvent("show",[this.tip,b]);},hide:function(b){if(!this.tip){document.id(this);}this.fireEvent("hide",[this.tip,b]); }});})();Locale.define("en-GB","Date",{dateOrder:["date","month","year"],shortDate:"%d/%m/%Y",shortTime:"%H:%M"}).inherit("en-US","Date");ZoneMinder-1.26.5/web/views/000077500000000000000000000000001225361755400156035ustar00rootroot00000000000000ZoneMinder-1.26.5/web/views/Makefile.am000066400000000000000000000001331225361755400176340ustar00rootroot00000000000000AUTOMAKE_OPTIONS = gnu webdir = @WEB_PREFIX@/views dist_web_DATA = file.php \ image.php ZoneMinder-1.26.5/web/views/file.php000066400000000000000000000031511225361755400172330ustar00rootroot00000000000000 ZoneMinder-1.26.5/web/views/image.php000066400000000000000000000053431225361755400174030ustar00rootroot00000000000000 ZoneMinder-1.26.5/zm.conf.in000066400000000000000000000023651225361755400156010ustar00rootroot00000000000000# ========================================================================== # # ZoneMinder Base Configuration, $Date$, $Revision$ # # ========================================================================== # # This file is generated by 'configure'. Care should be taken if manually # editing this file as an changes may be overwritten by subsequent configuration # or installations. # # Path to installed data directory, used mostly for finding DB upgrade scripts ZM_PATH_DATA=@PKGDATADIR@ # Path to ZoneMinder binaries ZM_PATH_BIN=@BINDIR@ # Path to ZoneMinder libraries (none at present, for future use) ZM_PATH_LIB=@LIBDIR@ # Path to ZoneMinder configuration (this file only at present) ZM_PATH_CONF=@SYSCONFDIR@ # Path to ZoneMinder web files ZM_PATH_WEB=@WEB_PREFIX@ # Path to ZoneMinder cgi files ZM_PATH_CGI=@CGI_PREFIX@ # Username and group that web daemon (httpd/apache) runs as ZM_WEB_USER=@WEB_USER@ ZM_WEB_GROUP=@WEB_GROUP@ # ZoneMinder database type ZM_DB_TYPE=@ZM_DB_TYPE@ # ZoneMinder database hostname or ip address ZM_DB_HOST=@ZM_DB_HOST@ # ZoneMinder database name ZM_DB_NAME=@ZM_DB_NAME@ # ZoneMinder database user ZM_DB_USER=@ZM_DB_USER@ # ZoneMinder database password ZM_DB_PASS=@ZM_DB_PASS@ # Host of this machine ZM_SERVER_HOST= ZoneMinder-1.26.5/zmconfgen.pl.in000066400000000000000000000136251225361755400166300ustar00rootroot00000000000000#!/usr/bin/perl -w # # ========================================================================== # # Zone Minder Configuration Script, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== # # This script is used to generate initial config headers and database data. # use strict; use lib './scripts/ZoneMinder/lib'; use ZoneMinder::ConfigData qw/:data/; $| = 1; my $config_header = "src/zm_config_defines.h"; my $config_sql = "db/zm_create.sql"; generateConfigFiles(); exit; sub generateConfigFiles { generateConfigHeader(); generateConfigSQL(); } sub generateConfigHeader { print( "Generating '$config_header'\n" ); open( CFG_HDR_FILE, ">$config_header" ) or die( "Can't open '$config_header' for writing" ); print( CFG_HDR_FILE "// The file is autogenerated by zmconfgen.pl\n" ); print( CFG_HDR_FILE "// Do not edit this file as any changes will be overwritten\n\n" ); my $last_id = 0; my $define_list = ""; my $declare_list = ""; my $assign_list = ""; foreach my $option ( @options ) { next if ( !defined($option->{id}) ); my $opt_id = $option->{id}; my $opt_name = $option->{name}; my $opt_type = $option->{type}; my $var_name = substr( lc($opt_name), 3 ); $define_list .= sprintf( "#define $opt_name $opt_id\n" ); $declare_list .= sprintf( "\t" ); if ( $opt_type->{db_type} eq 'boolean' ) { $declare_list .= sprintf( "bool " ); } elsif ( $opt_type->{db_type} eq 'integer' || $opt_type->{db_type} eq 'hexadecimal' ) { $declare_list .= sprintf( "int " ); } elsif ( $opt_type->{db_type} eq 'decimal' ) { $declare_list .= sprintf( "double " ); } else { $declare_list .= sprintf( "const char *" ); } $declare_list .= sprintf( $var_name.";\\\n" ); $assign_list .= sprintf( "\t" ); $assign_list .= sprintf( $var_name." = " ); if ( $opt_type->{db_type} eq 'boolean' ) { $assign_list .= sprintf( "(bool)" ); } elsif ( $opt_type->{db_type} eq 'integer' || $opt_type->{db_type} eq 'hexadecimal' ) { $assign_list .= sprintf( "(int)" ); } elsif ( $opt_type->{db_type} eq 'decimal' ) { $assign_list .= sprintf( "(double) " ); } else { $assign_list .= sprintf( "(const char *)" ); } $assign_list .= sprintf( "config.Item( ".$opt_name." );\\\n" ); $last_id = $option->{id}; } print( CFG_HDR_FILE $define_list."\n\n" ); print( CFG_HDR_FILE "#define ZM_MAX_CFG_ID $last_id\n\n" ); print( CFG_HDR_FILE "#define ZM_CFG_DECLARE_LIST \\\n" ); print( CFG_HDR_FILE $declare_list."\n\n" ); print( CFG_HDR_FILE "#define ZM_CFG_ASSIGN_LIST \\\n" ); print( CFG_HDR_FILE $assign_list."\n\n" ); close( CFG_HDR_FILE ); } sub generateConfigSQL { print( "Updating '$config_sql'\n" ); my $config_sql_temp = $config_sql.".temp"; open( CFG_SQL_FILE, "<$config_sql" ) or die( "Can't open '$config_sql' for reading" ); open( CFG_TEMP_SQL_FILE, ">$config_sql_temp" ) or die( "Can't open '$config_sql_temp' for writing" ); while ( my $line = ) { last if ( $line =~ /^-- This section is autogenerated/ ); print( CFG_TEMP_SQL_FILE $line ); } close( CFG_SQL_FILE ); print( CFG_TEMP_SQL_FILE "-- This section is autogenerated by zmconfgen.pl\n" ); print( CFG_TEMP_SQL_FILE "-- Do not edit this file as any changes will be overwritten\n" ); print( CFG_TEMP_SQL_FILE "--\n\n" ); print( CFG_TEMP_SQL_FILE "delete from Config;\n\n" ); foreach my $option ( @options ) { #print( $option->{name}."\n" ) if ( !$option->{category} ); $option->{db_type} = $option->{type}->{db_type}; $option->{db_hint} = $option->{type}->{hint}; $option->{db_pattern} = $option->{type}->{pattern}; $option->{db_format} = $option->{type}->{format}; if ( $option->{db_type} eq "boolean" ) { $option->{db_value} = ($option->{value} eq "yes")?"1":"0"; } else { $option->{db_value} = $option->{value}; } if ( $option->{name} eq "ZM_DYN_CURR_VERSION" || $option->{name} eq "ZM_DYN_DB_VERSION" ) { $option->{db_value} = '@VERSION@'; } if ( my $requires = $option->{requires} ) { $option->{db_requires} = join( ";", map { my $value = $_->{value}; $value = ($value eq "yes")?1:0 if ( $options_hash{$_->{name}}->{db_type} eq "boolean" ); ( "$_->{name}=$value" ) } @$requires ); } else { $option->{db_requires} = ""; } printf( CFG_TEMP_SQL_FILE "insert into Config set Id = %d, Name = '%s', Value = '%s', Type = '%s', DefaultValue = '%s', Hint = '%s', Pattern = '%s', Format = '%s', Prompt = '%s', Help = '%s', Category = '%s', Readonly = '%s', Requires = '%s';\n", $option->{id}, $option->{name}, addSlashes($option->{db_value}), $option->{db_type}, addSlashes($option->{default}), addSlashes($option->{db_hint}), addSlashes($option->{db_pattern}), addSlashes($option->{db_format}), addSlashes($option->{description}), addSlashes($option->{help}), $option->{category}, $option->{readonly}?1:0, $option->{db_requires} ); } print( CFG_TEMP_SQL_FILE "\n" ); close( CFG_TEMP_SQL_FILE ); rename( $config_sql_temp, $config_sql ) or die( "Can't rename '$config_sql_temp' to '$config_sql': $!" ); } sub addSlashes { my $string = shift; return( "" ) if ( !defined($string) ); $string =~ s|(['"])|\\$1|g; return( $string ); } ZoneMinder-1.26.5/zmlinkcontent.sh.in000077500000000000000000000147351225361755400175460ustar00rootroot00000000000000#!/bin/bash # The purpose of this file is to create the symlinks in the web folder to the content folder. It can use an existing content folder or create a new one. # Set the content dir default to be the one supplied to cmake ZM_PATH_CONTENT="@ZM_CONTENTDIR@" echo "*** This bash script creates the nessecary symlinks for the zoneminder content" echo "*** It can use an existing content folder or create a new one" echo "*** For usage: use -h" echo "*** The default content directory is: $ZM_PATH_CONTENT" echo "" usage() { cat <