Merge branch 'storageareas' into work_on_zms
This commit is contained in:
commit
98590b99f8
|
@ -26,9 +26,10 @@ addons:
|
||||||
env:
|
env:
|
||||||
matrix:
|
matrix:
|
||||||
- OS=el DIST=6
|
- OS=el DIST=6
|
||||||
|
- OS=el DIST=6 ARCH=i386 DOCKER_REPO=knnniggett/packpack
|
||||||
- OS=el DIST=7
|
- OS=el DIST=7
|
||||||
- OS=fedora DIST=24
|
|
||||||
- OS=fedora DIST=25
|
- OS=fedora DIST=25
|
||||||
|
- OS=fedora DIST=26 DOCKER_REPO=knnniggett/packpack
|
||||||
- OS=ubuntu DIST=trusty
|
- OS=ubuntu DIST=trusty
|
||||||
- OS=ubuntu DIST=xenial
|
- OS=ubuntu DIST=xenial
|
||||||
- OS=ubuntu DIST=trusty ARCH=i386
|
- OS=ubuntu DIST=trusty ARCH=i386
|
||||||
|
|
|
@ -69,7 +69,24 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
|
||||||
# GCC below 6.0 doesn't support __target__("fpu=neon") attribute, required for compiling ARM Neon code, otherwise compilation fails.
|
# GCC below 6.0 doesn't support __target__("fpu=neon") attribute, required for compiling ARM Neon code, otherwise compilation fails.
|
||||||
# Must use -mfpu=neon compiler flag instead, but only do that for processors that support neon, otherwise strip the neon code alltogether,
|
# Must use -mfpu=neon compiler flag instead, but only do that for processors that support neon, otherwise strip the neon code alltogether,
|
||||||
# because passing -fmpu=neon is unsafe to processors that don't support neon
|
# because passing -fmpu=neon is unsafe to processors that don't support neon
|
||||||
IF(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" AND CMAKE_SYSTEM_NAME MATCHES "Linux")
|
|
||||||
|
# Arm neon support only tested on Linux. If your arm hardware is running a non-Linux distro and is using gcc then contact us.
|
||||||
|
IF (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||||
|
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" ZM_SYSTEM_PROC)
|
||||||
|
IF((ZM_SYSTEM_PROC STREQUAL "") OR (ZM_SYSTEM_PROC STREQUAL "unknown"))
|
||||||
|
execute_process(COMMAND uname -m OUTPUT_VARIABLE ZM_SYSTEM_PROC ERROR_VARIABLE ZM_SYSTEM_PROC_ERR)
|
||||||
|
|
||||||
|
# maybe make the following error checks fatal
|
||||||
|
IF(ZM_SYSTEM_PROC_ERR)
|
||||||
|
message(WARNING "\nAn error occurred while attempting to determine the system processor:\n${ZM_SYSTEM_PROC_ERR}")
|
||||||
|
ENDIF(ZM_SYSTEM_PROC_ERR)
|
||||||
|
IF(NOT ZM_SYSTEM_PROC)
|
||||||
|
message(WARNING "\nUnable to determine the system processor. This may cause a build failure.\n")
|
||||||
|
ENDIF(ZM_SYSTEM_PROC)
|
||||||
|
|
||||||
|
ENDIF((ZM_SYSTEM_PROC STREQUAL "") OR (ZM_SYSTEM_PROC STREQUAL "unknown"))
|
||||||
|
|
||||||
|
IF(ZM_SYSTEM_PROC MATCHES "^arm")
|
||||||
IF(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
|
IF(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
|
||||||
EXEC_PROGRAM(grep ARGS " neon " "/proc/cpuinfo" OUTPUT_VARIABLE neonoutput RETURN_VALUE neonresult)
|
EXEC_PROGRAM(grep ARGS " neon " "/proc/cpuinfo" OUTPUT_VARIABLE neonoutput RETURN_VALUE neonresult)
|
||||||
IF(neonresult EQUAL 0)
|
IF(neonresult EQUAL 0)
|
||||||
|
@ -82,7 +99,8 @@ IF(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" AND CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||||
message(STATUS "ARM Neon is not available on this processor. Neon functions will be absent")
|
message(STATUS "ARM Neon is not available on this processor. Neon functions will be absent")
|
||||||
ENDIF(neonresult EQUAL 0)
|
ENDIF(neonresult EQUAL 0)
|
||||||
ENDIF(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
|
ENDIF(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
|
||||||
ENDIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" AND CMAKE_SYSTEM_NAME MATCHES "Linux")
|
ENDIF(ZM_SYSTEM_PROC MATCHES "^arm")
|
||||||
|
ENDIF (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||||
|
|
||||||
# Modules that we need:
|
# Modules that we need:
|
||||||
include (GNUInstallDirs)
|
include (GNUInstallDirs)
|
||||||
|
@ -137,11 +155,11 @@ set(ZM_WEB_USER "" CACHE STRING
|
||||||
set(ZM_WEB_GROUP "" CACHE STRING
|
set(ZM_WEB_GROUP "" CACHE STRING
|
||||||
"The group apache or the local web server runs on,
|
"The group apache or the local web server runs on,
|
||||||
Leave empty to be the same as the web user")
|
Leave empty to be the same as the web user")
|
||||||
set(ZM_DIR_EVENTS "events" CACHE PATH
|
set(ZM_DIR_EVENTS "${ZM_CONTENTDIR}/events" CACHE PATH
|
||||||
"Location where events are recorded to, default: events")
|
"Location where events are recorded to, default: ZM_CONTENTDIR/events")
|
||||||
set(ZM_DIR_IMAGES "events" CACHE PATH
|
set(ZM_DIR_IMAGES "${ZM_CONTENTDIR}/images" CACHE PATH
|
||||||
"Location where images, not directly associated with events,
|
"Location where images, not directly associated with events,
|
||||||
are recorded to, default: images")
|
are recorded to, default: ZM_CONTENTDIR/images")
|
||||||
set(ZM_DIR_SOUNDS "sounds" CACHE PATH
|
set(ZM_DIR_SOUNDS "sounds" CACHE PATH
|
||||||
"Location to look for optional sound files, default: sounds")
|
"Location to look for optional sound files, default: sounds")
|
||||||
set(ZM_PATH_ZMS "/cgi-bin/nph-zms" CACHE PATH
|
set(ZM_PATH_ZMS "/cgi-bin/nph-zms" CACHE PATH
|
||||||
|
@ -154,7 +172,7 @@ set(ZM_PATH_ARP "" CACHE PATH
|
||||||
"Full path to compatible arp binary. Leave empty for automatic detection.")
|
"Full path to compatible arp binary. Leave empty for automatic detection.")
|
||||||
set(ZM_CONFIG_DIR "/${CMAKE_INSTALL_SYSCONFDIR}" CACHE PATH
|
set(ZM_CONFIG_DIR "/${CMAKE_INSTALL_SYSCONFDIR}" CACHE PATH
|
||||||
"Location of ZoneMinder configuration, default system config directory")
|
"Location of ZoneMinder configuration, default system config directory")
|
||||||
set(ZM_CONFIG_SUBDIR "${ZM_CONFIG_DIR}/conf.d" CACHE PATH
|
set(ZM_CONFIG_SUBDIR "${ZM_CONFIG_DIR}/zm/conf.d" CACHE PATH
|
||||||
"Location of ZoneMinder configuration subfolder, default: ZM_CONFIG_DIR/conf.d")
|
"Location of ZoneMinder configuration subfolder, default: ZM_CONFIG_DIR/conf.d")
|
||||||
set(ZM_EXTRA_LIBS "" CACHE STRING
|
set(ZM_EXTRA_LIBS "" CACHE STRING
|
||||||
"A list of optional libraries, separated by semicolons, e.g. ssl;theora")
|
"A list of optional libraries, separated by semicolons, e.g. ssl;theora")
|
||||||
|
@ -557,6 +575,7 @@ if(NOT ZM_NO_FFMPEG)
|
||||||
mark_as_advanced(FORCE AVUTIL_LIBRARIES 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/avutil.h" HAVE_LIBAVUTIL_AVUTIL_H)
|
||||||
check_include_file("libavutil/mathematics.h" HAVE_LIBAVUTIL_MATHEMATICS_H)
|
check_include_file("libavutil/mathematics.h" HAVE_LIBAVUTIL_MATHEMATICS_H)
|
||||||
|
check_include_file("libavutil/hwcontext.h" HAVE_LIBAVUTIL_HWCONTEXT_H)
|
||||||
set(optlibsfound "${optlibsfound} AVUtil")
|
set(optlibsfound "${optlibsfound} AVUtil")
|
||||||
else(AVUTIL_LIBRARIES)
|
else(AVUTIL_LIBRARIES)
|
||||||
set(optlibsnotfound "${optlibsnotfound} AVUtil")
|
set(optlibsnotfound "${optlibsnotfound} AVUtil")
|
||||||
|
@ -801,11 +820,12 @@ endif(ZM_PERL_SEARCH_PATH)
|
||||||
|
|
||||||
# If this is an out-of-source build, copy the files we need to the binary directory
|
# If this is an out-of-source build, copy the files we need to the binary directory
|
||||||
if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
|
if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
|
||||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/conf.d" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/conf.d")
|
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/conf.d" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" PATTERN "*.in" EXCLUDE)
|
||||||
endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
|
endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR))
|
||||||
|
|
||||||
# Generate files from the .in files
|
# Generate files from the .in files
|
||||||
configure_file(zm.conf.in "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" @ONLY)
|
configure_file(zm.conf.in "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" @ONLY)
|
||||||
|
configure_file(conf.d/01-system-paths.conf.in "${CMAKE_CURRENT_BINARY_DIR}/conf.d/01-system-paths.conf" @ONLY)
|
||||||
configure_file(zoneminder-config.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY)
|
configure_file(zoneminder-config.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h" @ONLY)
|
||||||
configure_file(zmconfgen.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmconfgen.pl" @ONLY)
|
configure_file(zmconfgen.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmconfgen.pl" @ONLY)
|
||||||
configure_file(zmlinkcontent.sh.in "${CMAKE_CURRENT_BINARY_DIR}/zmlinkcontent.sh" @ONLY)
|
configure_file(zmlinkcontent.sh.in "${CMAKE_CURRENT_BINARY_DIR}/zmlinkcontent.sh" @ONLY)
|
||||||
|
@ -850,18 +870,7 @@ endif(zmconfgen_result EQUAL 0)
|
||||||
|
|
||||||
# Install zm.conf
|
# Install zm.conf
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" DESTINATION "${ZM_CONFIG_DIR}")
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" DESTINATION "${ZM_CONFIG_DIR}")
|
||||||
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/conf.d/" DESTINATION "${ZM_CONFIG_SUBDIR}")
|
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/conf.d/" DESTINATION "${ZM_CONFIG_SUBDIR}" PATTERN "*.in" EXCLUDE)
|
||||||
#install(CODE "
|
|
||||||
#if (NOT EXISTS \"${ZM_CONFIG_DIR}/zm.conf\")
|
|
||||||
#message(STATUS \"No zm.conf at ${CMAKE_CURRENT_BINARY_DIR}/zm.conf. Will install a new zm.conf\")
|
|
||||||
#install(FILES \"${CMAKE_CURRENT_BINARY_DIR}/zm.conf\" DESTINATION \"${ZM_CONFIG_DIR}\")
|
|
||||||
#else (NOT EXISTS \"${ZM_CONFIG_DIR}/zm.conf\")
|
|
||||||
#message(STATUS \"Found zm.conf at ${CMAKE_CURRENT_BINARY_DIR}/zm.conf. Not overwriting. Installing zm.conf.new instead.\")
|
|
||||||
#file(RENAME \"${CMAKE_CURRENT_BINARY_DIR}/zm.conf\" \"${CMAKE_CURRENT_BINARY_DIR}/zm.conf.new\")
|
|
||||||
#install(FILES \"${CMAKE_CURRENT_BINARY_DIR}/zm.conf.new\" DESTINATION \"${ZM_CONFIG_DIR}\")
|
|
||||||
#endif(NOT EXISTS \"${ZM_CONFIG_DIR}/zm.conf\")
|
|
||||||
#")
|
|
||||||
|
|
||||||
|
|
||||||
# Uninstall target
|
# Uninstall target
|
||||||
configure_file(
|
configure_file(
|
||||||
|
|
11
INSTALL
11
INSTALL
|
@ -45,9 +45,20 @@ Possible configuration options:
|
||||||
ZM_DB_NAME Name of ZoneMinder database, default: zm
|
ZM_DB_NAME Name of ZoneMinder database, default: zm
|
||||||
ZM_DB_USER Name of ZoneMinder database user, default: zmuser
|
ZM_DB_USER Name of ZoneMinder database user, default: zmuser
|
||||||
ZM_DB_PASS Password of ZoneMinder database user, default: zmpass
|
ZM_DB_PASS Password of ZoneMinder database user, default: zmpass
|
||||||
|
ZM_DB_SSL_CA_CERT Path to SSL CA certificate, default: empty; SSL not enabled
|
||||||
|
ZM_DB_SSL_CLIENT_KEY Path to SSL client key, default: empty; SSL not enabled
|
||||||
|
ZM_DB_SSL_CLIENT_CERT Path to SSL client certificate, default: empty; SSL not enabled
|
||||||
ZM_WEB_USER The user apache or the local web server runs on. Leave empty for automatic detection. If that fails, you can use this variable to force
|
ZM_WEB_USER The user apache or the local web server runs on. Leave empty for automatic detection. If that fails, you can use this variable to force
|
||||||
ZM_WEB_GROUP The group apache or the local web server runs on, Leave empty to be the same as the web user
|
ZM_WEB_GROUP The group apache or the local web server runs on, Leave empty to be the same as the web user
|
||||||
|
ZM_DIR_EVENTS Location where events are recorded to, default: ZM_CONTENTDIR/events
|
||||||
|
ZM_DIR_IMAGES Location where images, not directly associated with events, are recorded to, default: ZM_CONTENTDIR/images
|
||||||
|
ZM_DIR_SOUNDS Location to look for optional sound files, default: sounds
|
||||||
|
ZM_PATH_ZMS Web url to zms streaming server, default: /cgi-bin/nph-zms
|
||||||
Advanced:
|
Advanced:
|
||||||
|
ZM_PATH_MAP Location to save mapped memory files, default: /dev/shm
|
||||||
|
ZM_PATH_ARP Full path to compatible arp binary. Leave empty for automatic detection.
|
||||||
|
ZM_CONFIG_DIR Location of the main ZoneMinder config file, zm.conf. default: /etc/zm
|
||||||
|
ZM_CONFIG_SUBDIR Location of custom config files. default: ZM_CONFIG_DIR/conf.d
|
||||||
ZM_EXTRA_LIBS A list of optional libraries, separated by semicolons, e.g. ssl;theora
|
ZM_EXTRA_LIBS A list of optional libraries, separated by semicolons, e.g. ssl;theora
|
||||||
ZM_MYSQL_ENGINE MySQL engine to use with database, default: InnoDB
|
ZM_MYSQL_ENGINE MySQL engine to use with database, default: InnoDB
|
||||||
ZM_NO_MMAP Set to ON to not use mmap shared memory. Shouldn't be enabled unless you experience problems with the shared memory. default: OFF
|
ZM_NO_MMAP Set to ON to not use mmap shared memory. Shouldn't be enabled unless you experience problems with the shared memory. default: OFF
|
||||||
|
|
|
@ -27,8 +27,8 @@ This is the recommended method to install ZoneMinder onto your system. ZoneMinde
|
||||||
|
|
||||||
- Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor/+archive/ubuntu/zoneminder)
|
- Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor/+archive/ubuntu/zoneminder)
|
||||||
- Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder)
|
- Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder)
|
||||||
- RHEL/CentOS and clones via [zmrepo](http://zmrepo.zoneminder.com/)
|
- RHEL/CentOS and clones via [RPM Fusion](http://rpmfusion.org)
|
||||||
- Fedora via [zmrepo](http://zmrepo.zoneminder.com/)
|
- Fedora via [RPM Fusion](http://rpmfusion.org)
|
||||||
- OpenSuse via [third party repository](http://www.zoneminder.com/wiki/index.php/Installing_using_ZoneMinder_RPMs_for_SuSE)
|
- OpenSuse via [third party repository](http://www.zoneminder.com/wiki/index.php/Installing_using_ZoneMinder_RPMs_for_SuSE)
|
||||||
- Mageia from their default repository
|
- Mageia from their default repository
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,9 @@ echo "Database host : $ZM_DB_HOST"
|
||||||
echo "Database name : $ZM_DB_NAME"
|
echo "Database name : $ZM_DB_NAME"
|
||||||
echo "Database user : $ZM_DB_USER"
|
echo "Database user : $ZM_DB_USER"
|
||||||
echo "Database password : Not shown"
|
echo "Database password : Not shown"
|
||||||
|
echo "Database SSL CA Cert : $ZM_DB_SSL_CA_CERT"
|
||||||
|
echo "Database SSL Client Key : $ZM_DB_SSL_CLIENT_KEY"
|
||||||
|
echo "Database SSL Client Cert : $ZM_DB_SSL_CLIENT_CERT"
|
||||||
|
|
||||||
|
|
||||||
CMPATH="CACHE PATH \"Imported by cmakecacheimport.sh\" FORCE"
|
CMPATH="CACHE PATH \"Imported by cmakecacheimport.sh\" FORCE"
|
||||||
|
@ -72,6 +75,9 @@ echo "set(ZM_DB_HOST \"$ZM_DB_HOST\" $CMSTRING)">>zm_conf.cmake
|
||||||
echo "set(ZM_DB_NAME \"$ZM_DB_NAME\" $CMSTRING)">>zm_conf.cmake
|
echo "set(ZM_DB_NAME \"$ZM_DB_NAME\" $CMSTRING)">>zm_conf.cmake
|
||||||
echo "set(ZM_DB_USER \"$ZM_DB_USER\" $CMSTRING)">>zm_conf.cmake
|
echo "set(ZM_DB_USER \"$ZM_DB_USER\" $CMSTRING)">>zm_conf.cmake
|
||||||
echo "set(ZM_DB_PASS \"$ZM_DB_PASS\" $CMSTRING)">>zm_conf.cmake
|
echo "set(ZM_DB_PASS \"$ZM_DB_PASS\" $CMSTRING)">>zm_conf.cmake
|
||||||
|
echo "set(ZM_DB_SSL_CA_CERT \"$ZM_DB_SSL_CA_CERT\" $CMSTRING)">>zm_conf.cmake
|
||||||
|
echo "set(ZM_DB_SSL_CLIENT_KEY \"$ZM_DB_SSL_CLIENT_KEY\" $CMSTRING)">>zm_conf.cmake
|
||||||
|
echo "set(ZM_DB_SSL_CLIENT_CERT \"$ZM_DB_SSL_CLIENT_CERT\" $CMSTRING)">>zm_conf.cmake
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Wrote zm_conf.cmake"
|
echo "Wrote zm_conf.cmake"
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# ZoneMinder System Paths Configuration
|
||||||
|
#
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# This config file contains the variables previously found under Options -> Paths
|
||||||
|
#
|
||||||
|
# *** DO NOT EDIT THIS FILE ***
|
||||||
|
#
|
||||||
|
# To make custom changes to the variables below, create a new configuration
|
||||||
|
# file, with an extention of .conf, containing your desired modifications.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Full path to the folder events are recorded to.
|
||||||
|
# The web account user must have full read/write permission to this folder.
|
||||||
|
ZM_DIR_EVENTS=@ZM_DIR_EVENTS@
|
||||||
|
|
||||||
|
# Full path to the folder images, not directly associated with events,
|
||||||
|
# are recorded to.
|
||||||
|
# The web account user must have full read/write permission to this folder.
|
||||||
|
ZM_DIR_IMAGES=@ZM_DIR_IMAGES@
|
||||||
|
|
||||||
|
# Foldername under the webroot where ZoneMinder looks for optional sound files
|
||||||
|
# to play when an alarm is detected.
|
||||||
|
ZM_DIR_SOUNDS=@ZM_DIR_SOUNDS@
|
||||||
|
|
||||||
|
# Full path to the folder where exported archives are stored
|
||||||
|
# The web account user must have full read/write permission to this folder.
|
||||||
|
ZM_DIR_EXPORTS=@ZM_TMPDIR@
|
||||||
|
|
||||||
|
# ZoneMinder url path to the zms streaming server
|
||||||
|
ZM_PATH_ZMS=@ZM_PATH_ZMS@
|
||||||
|
|
||||||
|
# Full Path to ZoneMinder's mapped memory files
|
||||||
|
# The web account user must have full read/write permission to this folder.
|
||||||
|
ZM_PATH_MAP=@ZM_PATH_MAP@
|
||||||
|
|
||||||
|
# Full Path to ZoneMinder's socket folder
|
||||||
|
# The web account user must have full read/write permission to this folder.
|
||||||
|
ZM_PATH_SOCKS=@ZM_SOCKDIR@
|
||||||
|
|
||||||
|
# Full path to ZoneMinder's log folder
|
||||||
|
# The web account user must have full read/write permission to this folder.
|
||||||
|
ZM_PATH_LOGS=@ZM_LOGDIR@
|
||||||
|
|
||||||
|
# Full path to ZoneMinder's swap folder
|
||||||
|
# The web account user must have full read/write permission to this folder.
|
||||||
|
ZM_PATH_SWAP=@ZM_TMPDIR@
|
||||||
|
|
||||||
|
# Full path to optional arp binary
|
||||||
|
# ZoneMinder will find the arp binary automatically on most systems
|
||||||
|
ZM_PATH_ARP=@ZM_PATH_ARP@
|
|
@ -0,0 +1,12 @@
|
||||||
|
# ==========================================================================
|
||||||
|
#
|
||||||
|
# ZoneMinder Multiserver Configuration
|
||||||
|
#
|
||||||
|
# ==========================================================================
|
||||||
|
# Do NOT set ZM_SERVER_HOST if you are not using Multi-Server
|
||||||
|
# You have been warned
|
||||||
|
#
|
||||||
|
# The name specified here must have a corresponding entry
|
||||||
|
# in the Servers tab under Options
|
||||||
|
#ZM_SERVER_HOST=
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
# Do NOT set ZM_SERVER_HOST if you are not using Multi-Server
|
|
||||||
# You have been warned
|
|
||||||
#
|
|
||||||
# The name specified here must have a corresponding entry
|
|
||||||
# in the Servers tab under Options
|
|
||||||
ZM_SERVER_HOST=
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ override_dh_auto_configure:
|
||||||
-DZM_TMPDIR=/var/tmp/zm \
|
-DZM_TMPDIR=/var/tmp/zm \
|
||||||
-DZM_LOGDIR=/var/log/zm \
|
-DZM_LOGDIR=/var/log/zm \
|
||||||
-DZM_WEBDIR=/usr/share/zoneminder/www \
|
-DZM_WEBDIR=/usr/share/zoneminder/www \
|
||||||
-DZM_CONTENTDIR=/var/cache/zoneminder \
|
|
||||||
-DZM_CGIDIR=/usr/lib/zoneminder/cgi-bin \
|
-DZM_CGIDIR=/usr/lib/zoneminder/cgi-bin \
|
||||||
-DZM_WEB_USER=www-data \
|
-DZM_WEB_USER=www-data \
|
||||||
-DZM_WEB_GROUP=www-data \
|
-DZM_WEB_GROUP=www-data \
|
||||||
|
|
|
@ -9,15 +9,15 @@ else(ZM_TARGET_DISTRO MATCHES "^el")
|
||||||
message([WARNING] "Unknown Build Option Detected" ...)
|
message([WARNING] "Unknown Build Option Detected" ...)
|
||||||
endif(ZM_TARGET_DISTRO MATCHES "^el")
|
endif(ZM_TARGET_DISTRO MATCHES "^el")
|
||||||
|
|
||||||
if((ZM_TARGET_DISTRO STREQUAL "el6") AND (ZM_WEB_USER STREQUAL "nginx"))
|
if((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx"))
|
||||||
message([FATAL_ERROR] "Nginx is Not a Supported Build Option on EL6 Target Distro" ...)
|
message([FATAL_ERROR] "Experimental Nginx support is currently only supported on Fedora" ...)
|
||||||
endif((ZM_TARGET_DISTRO STREQUAL "el6") AND (ZM_WEB_USER STREQUAL "nginx"))
|
endif((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx"))
|
||||||
|
|
||||||
# Configure the zoneminder service files
|
# Configure the zoneminder service files
|
||||||
if(ZM_TARGET_DISTRO STREQUAL "el6")
|
if(ZM_TARGET_DISTRO STREQUAL "el6")
|
||||||
configure_file(sysvinit/zoneminder.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.sysvinit @ONLY)
|
configure_file(sysvinit/zoneminder.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.sysvinit @ONLY)
|
||||||
configure_file(sysvinit/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY)
|
configure_file(sysvinit/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY)
|
||||||
configure_file(sysvinit/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
|
configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
|
||||||
else(ZM_TARGET_DISTRO STREQUAL "el6")
|
else(ZM_TARGET_DISTRO STREQUAL "el6")
|
||||||
configure_file(systemd/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY)
|
configure_file(systemd/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY)
|
||||||
if(ZM_WEB_USER STREQUAL "nginx")
|
if(ZM_WEB_USER STREQUAL "nginx")
|
||||||
|
@ -28,7 +28,7 @@ else(ZM_TARGET_DISTRO STREQUAL "el6")
|
||||||
configure_file(nginx/README.Fedora ${CMAKE_CURRENT_SOURCE_DIR}/readme/README.Fedora COPYONLY)
|
configure_file(nginx/README.Fedora ${CMAKE_CURRENT_SOURCE_DIR}/readme/README.Fedora COPYONLY)
|
||||||
else(ZM_WEB_USER STREQUAL "nginx")
|
else(ZM_WEB_USER STREQUAL "nginx")
|
||||||
configure_file(systemd/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
|
configure_file(systemd/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
|
||||||
configure_file(systemd/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
|
configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
|
||||||
configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
|
configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
|
||||||
endif(ZM_WEB_USER STREQUAL "nginx")
|
endif(ZM_WEB_USER STREQUAL "nginx")
|
||||||
endif(ZM_TARGET_DISTRO STREQUAL "el6")
|
endif(ZM_TARGET_DISTRO STREQUAL "el6")
|
||||||
|
@ -55,10 +55,7 @@ install(DIRECTORY zoneminder DESTINATION /var/run DIRECTORY_PERMISSIONS OWNER_WR
|
||||||
install(DIRECTORY zoneminder-upload DESTINATION /var/spool DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
install(DIRECTORY zoneminder-upload DESTINATION /var/spool DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||||
install(DIRECTORY events images temp DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
install(DIRECTORY events images temp DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||||
|
|
||||||
# Create symlinks
|
# Symlink the cake php temp folder to the ZoneMinder temp folder
|
||||||
install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/events \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/events\")")
|
|
||||||
install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/images \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/images\")")
|
|
||||||
install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/temp \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/temp\")")
|
|
||||||
install(CODE "execute_process(COMMAND ln -sf ../../../../../../var/lib/zoneminder/temp \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/api/app/tmp\")")
|
install(CODE "execute_process(COMMAND ln -sf ../../../../../../var/lib/zoneminder/temp \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/api/app/tmp\")")
|
||||||
|
|
||||||
# Link to Cambozola
|
# Link to Cambozola
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# ZoneMinder systemd unit file for CentOS 7
|
# ZoneMinder systemd unit file for RedHat distros and clones
|
||||||
|
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=ZoneMinder CCTV recording and security system
|
Description=ZoneMinder CCTV recording and security system
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
#
|
|
||||||
# ZoneMinder Apache configuration file
|
|
||||||
# With SSLRequire and HTTPS auto redirect
|
|
||||||
# Modify this configuration to suit your requirements
|
|
||||||
#
|
|
||||||
|
|
||||||
# Auto Redirect HTTP requests to HTTPS
|
|
||||||
RewriteEngine On
|
|
||||||
RewriteCond %{HTTPS} !=on
|
|
||||||
RewriteRule ^/?(zm)(.*) https://%{SERVER_NAME}/$1$2 [R,L]
|
|
||||||
|
|
||||||
Alias /zm "@ZM_WEBDIR@"
|
|
||||||
<Directory "@ZM_WEBDIR@">
|
|
||||||
# explicitly set index.php as the only directoryindex
|
|
||||||
DirectoryIndex disabled
|
|
||||||
DirectoryIndex index.php
|
|
||||||
SSLRequireSSL
|
|
||||||
Options -Indexes +MultiViews +FollowSymLinks
|
|
||||||
AllowOverride None
|
|
||||||
<IfModule mod_authz_core.c>
|
|
||||||
# Apache 2.4
|
|
||||||
Require all granted
|
|
||||||
</IfModule>
|
|
||||||
<IfModule !mod_authz_core.c>
|
|
||||||
# Apache 2.2
|
|
||||||
Order deny,allow
|
|
||||||
Allow from all
|
|
||||||
</IfModule>
|
|
||||||
</Directory>
|
|
||||||
|
|
||||||
ScriptAlias /cgi-bin-zm "@ZM_CGIDIR@"
|
|
||||||
<Directory "@ZM_CGIDIR@">
|
|
||||||
SSLRequireSSL
|
|
||||||
AllowOverride None
|
|
||||||
Options +ExecCGI +FollowSymLinks
|
|
||||||
<IfModule mod_authz_core.c>
|
|
||||||
# Apache 2.4
|
|
||||||
Require all granted
|
|
||||||
</IfModule>
|
|
||||||
<IfModule !mod_authz_core.c>
|
|
||||||
# Apache 2.2
|
|
||||||
Order deny,allow
|
|
||||||
Allow from all
|
|
||||||
</IfModule>
|
|
||||||
</Directory>
|
|
||||||
|
|
||||||
# For better visibility, the following directives have been migrated from the
|
|
||||||
# default .htaccess files included with the CakePHP project.
|
|
||||||
# Parameters not set here are inherited from the parent directive above.
|
|
||||||
<Directory "@ZM_WEBDIR@/api">
|
|
||||||
RewriteEngine on
|
|
||||||
RewriteRule ^$ app/webroot/ [L]
|
|
||||||
RewriteRule (.*) app/webroot/$1 [L]
|
|
||||||
RewriteBase /zm/api
|
|
||||||
</Directory>
|
|
||||||
|
|
||||||
<Directory "@ZM_WEBDIR@/api/app">
|
|
||||||
RewriteEngine on
|
|
||||||
RewriteRule ^$ webroot/ [L]
|
|
||||||
RewriteRule (.*) webroot/$1 [L]
|
|
||||||
RewriteBase /zm/api
|
|
||||||
</Directory>
|
|
||||||
|
|
||||||
<Directory "@ZM_WEBDIR@/api/app/webroot">
|
|
||||||
RewriteEngine On
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-d
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-f
|
|
||||||
RewriteRule ^ index.php [L]
|
|
||||||
RewriteBase /zm/api
|
|
||||||
</Directory>
|
|
||||||
|
|
|
@ -286,12 +286,18 @@ rm -rf %{_docdir}/%{name}-%{version}
|
||||||
%files
|
%files
|
||||||
%license COPYING
|
%license COPYING
|
||||||
%doc AUTHORS README.md distros/redhat/readme/README.%{readme_suffix} distros/redhat/readme/README.https distros/redhat/jscalendar-doc
|
%doc AUTHORS README.md distros/redhat/readme/README.%{readme_suffix} distros/redhat/readme/README.https distros/redhat/jscalendar-doc
|
||||||
|
|
||||||
|
# We want these two folders to have "normal" read permission
|
||||||
|
# compared to the folder contents
|
||||||
%dir %{_sysconfdir}/zm
|
%dir %{_sysconfdir}/zm
|
||||||
%dir %{_sysconfdir}/zm/conf.d
|
%dir %{_sysconfdir}/zm/conf.d
|
||||||
|
|
||||||
|
# Config folder contents contain sensitive info
|
||||||
|
# and should not be readable by normal users
|
||||||
%{_sysconfdir}/zm/conf.d/README
|
%{_sysconfdir}/zm/conf.d/README
|
||||||
# Always overwrite zm.conf now that ZoneMinder supports conf.d folder
|
%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/zm.conf
|
||||||
%attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/zm.conf
|
|
||||||
%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/*.conf
|
%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/*.conf
|
||||||
|
%ghost %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/zmcustom.conf
|
||||||
|
|
||||||
%config(noreplace) %attr(644,root,root) %{wwwconfdir}/zoneminder.conf
|
%config(noreplace) %attr(644,root,root) %{wwwconfdir}/zoneminder.conf
|
||||||
%config(noreplace) %{_sysconfdir}/logrotate.d/zoneminder
|
%config(noreplace) %{_sysconfdir}/logrotate.d/zoneminder
|
||||||
|
|
|
@ -25,7 +25,6 @@ override_dh_auto_configure:
|
||||||
-DZM_SOCKDIR="/var/run/zm" \
|
-DZM_SOCKDIR="/var/run/zm" \
|
||||||
-DZM_TMPDIR="/tmp/zm" \
|
-DZM_TMPDIR="/tmp/zm" \
|
||||||
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
|
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
|
||||||
-DZM_CONTENTDIR="/var/cache/zoneminder" \
|
|
||||||
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
|
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
|
||||||
-DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
|
-DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
|
||||||
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" \
|
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" \
|
||||||
|
|
|
@ -1,4 +1 @@
|
||||||
/var/cache/zoneminder/events /usr/share/zoneminder/www/events
|
|
||||||
/var/cache/zoneminder/images /usr/share/zoneminder/www/images
|
|
||||||
/var/cache/zoneminder/temp /usr/share/zoneminder/www/temp
|
|
||||||
/tmp/zm /usr/share/zoneminder/www/api/app/tmp
|
/tmp/zm /usr/share/zoneminder/www/api/app/tmp
|
||||||
|
|
|
@ -58,7 +58,6 @@ override_dh_auto_configure:
|
||||||
-DZM_TMPDIR=/var/tmp/zm \
|
-DZM_TMPDIR=/var/tmp/zm \
|
||||||
-DZM_LOGDIR=/var/log/zm \
|
-DZM_LOGDIR=/var/log/zm \
|
||||||
-DZM_WEBDIR=/usr/share/zoneminder \
|
-DZM_WEBDIR=/usr/share/zoneminder \
|
||||||
-DZM_CONTENTDIR=/var/cache/zoneminder \
|
|
||||||
-DZM_CGIDIR=/usr/lib/cgi-bin \
|
-DZM_CGIDIR=/usr/lib/cgi-bin \
|
||||||
-DZM_WEB_USER=www-data \
|
-DZM_WEB_USER=www-data \
|
||||||
-DZM_WEB_GROUP=www-data \
|
-DZM_WEB_GROUP=www-data \
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
var/cache/zoneminder/events usr/share/zoneminder/www/events
|
|
||||||
var/cache/zoneminder/images usr/share/zoneminder/www/images
|
|
||||||
var/cache/zoneminder/temp usr/share/zoneminder/www/temp
|
|
|
@ -1 +0,0 @@
|
||||||
usr/lib/cgi-bin usr/share/zoneminder/cgi-bin
|
|
|
@ -25,7 +25,6 @@ override_dh_auto_configure:
|
||||||
-DZM_SOCKDIR="/var/run/zm" \
|
-DZM_SOCKDIR="/var/run/zm" \
|
||||||
-DZM_TMPDIR="/tmp/zm" \
|
-DZM_TMPDIR="/tmp/zm" \
|
||||||
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
|
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
|
||||||
-DZM_CONTENTDIR="/var/cache/zoneminder" \
|
|
||||||
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
|
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
|
||||||
-DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
|
-DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
|
||||||
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms"
|
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms"
|
||||||
|
|
|
@ -1,4 +1 @@
|
||||||
/var/cache/zoneminder/events /usr/share/zoneminder/www/events
|
|
||||||
/var/cache/zoneminder/images /usr/share/zoneminder/www/images
|
|
||||||
/var/cache/zoneminder/temp /usr/share/zoneminder/www/temp
|
|
||||||
/var/tmp /usr/share/zoneminder/www/api/app/tmp
|
/var/tmp /usr/share/zoneminder/www/api/app/tmp
|
||||||
|
|
17
docs/faq.rst
17
docs/faq.rst
|
@ -61,7 +61,7 @@ In *general* a good estimate of memory required would be:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
Min Memory = 1.2 * ((image-width*image-height*image buffer size*target color space*number of cameras/8/1024/1024 )
|
Min Bits of Memory = 20% overhead * (image-width*image-height*image buffer size*target color space*number of cameras)
|
||||||
|
|
||||||
Where:
|
Where:
|
||||||
* image-width and image-height are the width and height of images that your camera is configured for (in my case, 1280x960). This value is in the Source tab for each monitor
|
* image-width and image-height are the width and height of images that your camera is configured for (in my case, 1280x960). This value is in the Source tab for each monitor
|
||||||
|
@ -69,11 +69,18 @@ Where:
|
||||||
* target color space is the color depth - 8bit, 24bit or 32bit. It's again in the source tab of each monitor
|
* target color space is the color depth - 8bit, 24bit or 32bit. It's again in the source tab of each monitor
|
||||||
The 1.2 at the start is basically adding 20% on top of the calculation to account for image/stream overheads (this is an estimate)
|
The 1.2 at the start is basically adding 20% on top of the calculation to account for image/stream overheads (this is an estimate)
|
||||||
|
|
||||||
So let's do the math. If we have 4 cameras running at 1280x960 with 32bit color space and one camera running at 640x480 with 8bit greyscale color space, the system would require:
|
The math breakdown for 4 cameras running at 1280x960 capture, 50 frame buffer, 24 bit color space:
|
||||||
|
::
|
||||||
|
1280*960 = 1,228,800 (bits)
|
||||||
|
1,228,800 * 24 = 2,359,296,000 (bits)
|
||||||
|
2,359,296,000 * 50 = 5,898,240,000 (bits)
|
||||||
|
5,898,240,000 * 4 = 7,077,888,000 (bits)
|
||||||
|
7,077,888,000 / 8 = 884,736,000 (bytes)
|
||||||
|
884,736,000 / 1000 = 884,736 (Kilobytes)
|
||||||
|
884,736 / 1000 = 864 (Megabytes)
|
||||||
|
864 / 1000 = 0.9 (Gigabyte)
|
||||||
|
|
||||||
``1.2 * ((1280*960*50*32*4/8/1024/1024 ) + (640 *480 *50*8/8 /1024/1024))``
|
Around 900MB of memory.
|
||||||
|
|
||||||
Or, around 900MB of memory.
|
|
||||||
|
|
||||||
So if you have 2GB of memory, you should be all set. Right? **Not, really**:
|
So if you have 2GB of memory, you should be all set. Right? **Not, really**:
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ Contents:
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
|
packpack
|
||||||
ubuntu
|
ubuntu
|
||||||
debian
|
debian
|
||||||
redhat
|
redhat
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
All Distros - A Simpler Way to Build ZoneMinder
|
||||||
|
===============================================
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
These instructions represent an alternative way to build ZoneMinder for any supported distro.
|
||||||
|
|
||||||
|
Advantages:
|
||||||
|
|
||||||
|
- Fewer steps and therefore much simpler
|
||||||
|
- Target distro agnostic - the steps are the same regardless of the target distro
|
||||||
|
- Host distro agnostic - the steps described here should work on any host Linux distro capable of running Bash and Docker
|
||||||
|
|
||||||
|
Background
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
These instructions leverage the power of the automated build system recently implemented in the ZoneMinder project. Behind the scenes, a project called `packpack <https://github.com/packpack/packpack>`_ is utilized, to build ZoneMinder inside a Docker container.
|
||||||
|
|
||||||
|
Procedure
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
**Step 1:** Verify the target distro.
|
||||||
|
|
||||||
|
- Open the project's `.travis.yml file <https://github.com/ZoneMinder/ZoneMinder/blob/master/.travis.yml#L27>`_ and verify the distro you want to build ZoneMinder for appears in the build matrix. The distros shown in the matrix are those known to build on ZoneMinder. If the distro you desire is in the list then continue to step two.
|
||||||
|
|
||||||
|
- If the desired distro is not in the first list, then open the `packpack project README <https://github.com/packpack/packpack/blob/master/README.md>`_ and check if the desired distro is theoretically supported. If it is, then continue to step 2 with the understanding that you are heading out into uncharted territory. There could be problems.
|
||||||
|
|
||||||
|
- If the desired distro does not appear in either list, then unfortuantely you cannot use the procedure described here.
|
||||||
|
|
||||||
|
**Step 2:** Install Docker.
|
||||||
|
|
||||||
|
You need to have a working installation of Docker so head over to the `Docker site <https://docs.docker.com/engine/installation/>`_ and get it working. Before continuing to the next step, verify you can run the Docker "Hello World" container as a normal user. To run a Docker container as a normal user, issue the following:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo gpasswd -a <username> docker
|
||||||
|
newgrp docker
|
||||||
|
|
||||||
|
Where <username> is, you guessed it, the user name you log in with.
|
||||||
|
|
||||||
|
**Step 3:** Git clone the ZoneMinder project.
|
||||||
|
|
||||||
|
Clone the ZoneMinder project if you have not done so already.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
git clone ZoneMinder
|
||||||
|
cd ZoneMinder
|
||||||
|
|
||||||
|
Alternatively, if you have already cloned the repo and wish to update it, do the following.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
cd ZoneMinder
|
||||||
|
git checkout master
|
||||||
|
git pull origin master
|
||||||
|
|
||||||
|
**Step 4:** Checkout the revision of ZoneMinder you wish to build.
|
||||||
|
|
||||||
|
A freshly cloned ZoneMinder git repo already points to the most recent commit in the master branch. If you want the latest development code then continue to the next step. If instead, you want to build a stable release then perform the following step.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
git checkout <releasename>
|
||||||
|
|
||||||
|
Where <releasename> is one of the official ZoneMinder releases shown on the `releases page <https://github.com/ZoneMinder/ZoneMinder/releases>`_, such as 1.30.4.
|
||||||
|
|
||||||
|
**Step 5:** Build ZoneMinder
|
||||||
|
|
||||||
|
To start the build, simply execute the following command from the root folder of the local git repo:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
OS=<distroname> DIST=<distrorel> utils/packpack/startpackpack.sh
|
||||||
|
|
||||||
|
Where <distroname> is the name of the distro you wish to build on, such as fedora, and <distrorev> is release name or number of the distro you wish to build on. Redhat distros expect a number for <distrorev> while Debian and Ubuntu distros expect a name. For example:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
OS=fedora DIST=25 utils/packpack/startpackpack.sh
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
OS=ubuntu DIST=xenial utils/packpack/startpackpack.sh
|
||||||
|
|
||||||
|
Once you enter the appropriate command, go get a coffee while a ZoneMinder package is built. When the build finished, you can find the resulting packages under a subfolder called "build".
|
||||||
|
|
||||||
|
Note that this will build packages with x86_64 architecture. This build method can also build on some distros (debian & ubuntu only at the moment) using i386 architecture. You can do that by adding "ARCH=i386" parameter.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
OS=ubuntu DIST=xenial ARCH=i386 utils/packpack/startpackpack.sh
|
||||||
|
|
||||||
|
For advanced users who really want to go out into uncharted waters, it is theoretically possible to build arm packages as well, as long as the host architecture is compatible.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
OS=ubuntu DIST=xenial ARCH=armhfp utils/packpack/startpackpack.sh
|
||||||
|
|
||||||
|
Building arm packages in this manner has not been tested by us, however.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ Background: RHEL, CentOS, and Clones
|
||||||
|
|
||||||
These distributions are classified as enterprise operating systems and have a long operating lifetime of many years. By design, they will not have the latest and greatest versions of any package. Instead, stable packages are the emphasis.
|
These distributions are classified as enterprise operating systems and have a long operating lifetime of many years. By design, they will not have the latest and greatest versions of any package. Instead, stable packages are the emphasis.
|
||||||
|
|
||||||
Replacing any core package in these distributions with a newer package from a third party is expressly verboten. The ZoneMinder development team will not do this, and neither should you. If you have the perception that you've got to have a newer version of mysql, gnome, apache, etc. then, rather than upgrade these packages, you should instead consider using a different distribution such as Fedora.
|
Replacing any core package in these distributions with a newer package from a third party is expressly verboten. The ZoneMinder development team will not do this, and neither should you. If you have the perception that you've got to have a newer version of php, mysql, gnome, apache, etc. then, rather than upgrade these packages, you should instead consider using a different distribution such as Fedora.
|
||||||
|
|
||||||
The ZoneMinder team will not provide support for systems which have had any core package replaced with a package from a third party.
|
The ZoneMinder team will not provide support for systems which have had any core package replaced with a package from a third party.
|
||||||
|
|
||||||
|
@ -23,39 +23,77 @@ Fedora has a short life-cycle of just 6 months. However, Fedora, and consequentl
|
||||||
|
|
||||||
If you desire newer packages than what is available in RHEL or CentOS, you should consider using Fedora.
|
If you desire newer packages than what is available in RHEL or CentOS, you should consider using Fedora.
|
||||||
|
|
||||||
Zmrepo – A ZoneMinder RPM Repository
|
How To Avoid Known Installation Problems
|
||||||
------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
Zmrepo is a turn key solution. It will install all of ZoneMinder's dependencies for you. This is the easiest and the recommended way to install ZoneMinder on any system running a Redhat based distribution.
|
The following notes are based on real problems which have occurred by those who came before you:
|
||||||
|
|
||||||
Zmrepo supports the two most recent, major releases of each Redhat based distro.
|
- Zmrepo assumes you have installed the underlying distribution **using the official installation media for that distro**. Third party "Spins" may not work correctly.
|
||||||
|
|
||||||
The following notes are based on real problems which have occurred:
|
|
||||||
|
|
||||||
- Zmrepo assumes you have installed the underlying distribution **using the official installation media for that distro**. Third party "Spins" are not supported and may not work correctly.
|
|
||||||
|
|
||||||
- ZoneMinder is intended to be installed in an environment dedicated to ZoneMinder. While ZoneMinder will play well with many applications, some invariably will not. Asterisk is one such example.
|
- ZoneMinder is intended to be installed in an environment dedicated to ZoneMinder. While ZoneMinder will play well with many applications, some invariably will not. Asterisk is one such example.
|
||||||
|
|
||||||
- Be advised that you need to start with a clean system before using zmrepo.
|
- Be advised that you need to start with a clean system before installing ZoneMinder.
|
||||||
|
|
||||||
- If you have previously installed ZoneMinder from-source, then your system is **NOT** clean. You must manually search for and delete all ZoneMinder related files before using zmrepo (look under /usr/local). Make uninstall helps, but it will not do this for you correctly. You **WILL** have problems if you ignore this step.
|
- If you have previously installed ZoneMinder from-source, then your system is **NOT** clean. You must manually search for and delete all ZoneMinder related files first (look under /usr/local). Issuing a "make uninstall" helps, but it will not do this for you correctly. You **WILL** have problems if you ignore this step.
|
||||||
|
|
||||||
- It is not necessary, and not recommended, to install a LAMP stack ahead of time.
|
- Unlike Debian/Ubuntu distros, it is not necessary, and not recommended, to install a LAMP stack ahead of time.
|
||||||
|
|
||||||
- Disable other third party repos and uninstall any of ZoneMinder's third party dependencies, which might already be on the system, especially ffmpeg and vlc. Attempting to install dependencies yourself often causes problems.
|
- Disable any other third party repos and uninstall any of ZoneMinder's third party dependencies, which might already be on the system, especially ffmpeg and vlc. Attempting to install dependencies yourself often causes problems.
|
||||||
|
|
||||||
- Each ZoneMinder rpm includes a README file under /usr/share/doc. You must follow the all steps in this README file, precisely, each and every time ZoneMinder is installed or upgraded. **Failure to do so is guaranteed to result in a non-functional system.**
|
- Each ZoneMinder rpm includes a README file under /usr/share/doc. You must follow all the steps in this README file, precisely, each and every time ZoneMinder is installed or upgraded. **Failure to do so is guaranteed to result in a non-functional system.**
|
||||||
|
|
||||||
To begin the installation of ZoneMinder on your Redhat based distro, please navigate to: http://zmrepo.zoneminder.com
|
How to Install ZoneMinder
|
||||||
|
-------------------------
|
||||||
|
|
||||||
How to Build a (Custom) ZoneMinder Package
|
These instructions apply to all redhat distros and compatible clones, except for RHEL/CentOS 6.
|
||||||
|
|
||||||
|
ZoneMinder releases are now being hosted at RPM Fusion. New users should navigate the `RPM Fusion site <https://rpmfusion.org>`_ then follow the instructions to enable that repo. RHEL/CentOS users must also navaigate to the `EPEL Site <https://fedoraproject.org/wiki/EPEL>`_ and enable that repo as well. Once enabled, install ZoneMinder from the commandline:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo dnf install zoneminder
|
||||||
|
|
||||||
|
Note that RHEL/CentOS 7 users should use yum instead of dnf.
|
||||||
|
|
||||||
|
Once ZoneMinder has been installed, it is critically important that you read the README file under /usr/share/doc/zoneminder. ZoneMinder will not run without completing the steps outlined in the README.
|
||||||
|
|
||||||
|
How to Install ZoneMinder on RHEL/CentOS 6
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
If you are looking to do development or the packages in zmrepo just don't suit you, then you should follow these steps to learn how to build your own ZoneMinder RPM.
|
We continue to encounter build problems, caused by the age of this distro. It is unforuntate, but we can see the writing on the wall. We do not have a date set, but the end of the line for this distros is near.
|
||||||
|
|
||||||
|
Please be advised that we do not recommend any new ZoneMinder installations using CentOS 6. However, for the time being, ZoneMinder rpms will continue to be hosted at `zmrepo <https://www.zoneminder.com>`_.
|
||||||
|
|
||||||
|
How to Install Nightly Development Builds
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
ZoneMinder development packages, which represent the most recent build from our master branch, are available from `zmrepo <https://www.zoneminder.com>`_.
|
||||||
|
|
||||||
|
The feedback we get from those who use these development packages is extremely helpful. However, please understand these packages are intended for testing the latest master branch only. They are not intended to be used on any production system. There will be new bugs, and new features may not be documented. This is bleeding edge, and there might be breakage. Please keep that in mind when using this repo. We know from our user forum that this can't be stated enough.
|
||||||
|
|
||||||
|
How to Change from Zmrepo to RPM Fusion
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
As mentioned above, the place to get the latest ZoneMinder release is now `RPM Fusion <https://rpmfusion.org>`_. If you are currently using ZoneMinder release packages from Zmrepo, then the following steps will change you over to RPM Fusion:
|
||||||
|
|
||||||
|
- Navigate to the `RPM Fusion site <https://rpmfusion.org>`_ and enable RPM Fusion on your system
|
||||||
|
- Now issue the following from the command line:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo dnf remove zmrepo
|
||||||
|
sudo dnf update
|
||||||
|
|
||||||
|
Note that RHEL/CentOS 7 users should use yum instead of dnf.
|
||||||
|
|
||||||
|
How to Build Your Own ZoneMinder Package
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
If you are looking to do development or the available packages just don't suit you, then you can follow these steps to build your own ZoneMinder RPM.
|
||||||
|
|
||||||
Background
|
Background
|
||||||
**********
|
**********
|
||||||
The following method documents how to build ZoneMinder into an RPM package, compatible with Fedora, Redhat, CentOS, and other compatible clones. This is exactly how the RPMS in zmrepo are built.
|
The following method documents how to build ZoneMinder into an RPM package, for Fedora, Redhat, CentOS, and other compatible clones. This is exactly how the RPMS in zmrepo are built.
|
||||||
|
|
||||||
The method documented below was chosen because:
|
The method documented below was chosen because:
|
||||||
|
|
||||||
|
@ -67,22 +105,20 @@ The method documented below was chosen because:
|
||||||
|
|
||||||
- Troubleshooting becomes easier if we are all building ZoneMinder the same way.
|
- Troubleshooting becomes easier if we are all building ZoneMinder the same way.
|
||||||
|
|
||||||
The build instructions below make use of a custom script called "buildzm.sh". Advanced users are encouraged to view the contents of this script. Notice that the script doesn't really do a whole lot. The goal of the script is to simply make the process a little easier for the first time user. Once you become familar with the build process, you can issue the mock commands found in the buildzm.sh script yourself if you so desire.
|
|
||||||
|
|
||||||
***IMPORTANT***
|
***IMPORTANT***
|
||||||
Certain commands in these instructions require root privileges while other commands do not. Pay close attention to this. If the instructions below state to issue a command without a “sudo” prefix, then you should *not* be root while issuing the command. Getting this incorrect will result in a failed build.
|
Certain commands in these instructions require root privileges while other commands do not. Pay close attention to this. If the instructions below state to issue a command without a “sudo” prefix, then you should *not* be root while issuing the command. Getting this incorrect will result in a failed build, or worse a broken system.
|
||||||
|
|
||||||
Set Up Your Environment
|
Set Up Your Environment
|
||||||
***********************
|
***********************
|
||||||
Before you begin, set up an rpmbuild environment by following `this guide <http://wiki.centos.org/HowTos/SetupRpmBuildEnvironment>`_ by the CentOS developers.
|
Before you begin, set up an rpmbuild environment by following `this guide <http://wiki.centos.org/HowTos/SetupRpmBuildEnvironment>`_ by the CentOS developers.
|
||||||
|
|
||||||
Next, navigate to `Zmrepo <http://zmrepo.zoneminder.com/>`_, and follow the instructions to enable zmrepo on your system.
|
In addition, make sure RPM Fusion is enabled as described in the previous section `How to Install ZoneMinder`_.
|
||||||
|
|
||||||
With zmrepo enabled, issue the following command:
|
With RPM Fusion enabled, issue the following command:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
sudo yum install zmrepo-mock-configs mock
|
sudo yum install mock-rpmfusion-free mock
|
||||||
|
|
||||||
|
|
||||||
Add your user account to the group mock:
|
Add your user account to the group mock:
|
||||||
|
@ -96,73 +132,67 @@ Your build environment is now set up.
|
||||||
|
|
||||||
Build from SRPM
|
Build from SRPM
|
||||||
***************
|
***************
|
||||||
To continue, you need a ZoneMinder SRPM. For starters, let's use one of the SRPMS from zmrepo. Go browse the `Zmrepo <http://zmrepo.zoneminder.com/>`_ site and choose an appropriate SRPM and place it into the ~/rpmbuild/SRPMS folder.
|
To continue, you need a ZoneMinder SRPM. If you wish to rebuild a ZoneMinder release, then browse the `RPM Fusion site <https://rpmfusion.org/>`_. If instead you wish to rebuild the latest source rpm from our master branch then browse the `Zmrepo site <http://zmrepo.zoneminder.com/>`_.
|
||||||
|
|
||||||
For CentOS 7, I have chosen the following SRPM:
|
For this example, I'll use one of the source rpms from zmrepo:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
wget -P ~/rpmbuild/SRPMS http://zmrepo.zoneminder.com/el/7/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm
|
wget -P ~/rpmbuild/SRPMS http://zmrepo.zoneminder.com/el/7/SRPMS/zoneminder-1.31.1-1.el7.centos.src.rpm
|
||||||
|
|
||||||
|
|
||||||
Now comes the fun part. To build ZoneMinder, issue the following command:
|
Now comes the fun part. To build ZoneMinder, issue the following command:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
buildzm.sh zmrepo-el7-x86_64 ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm
|
mock -r epel-7-x86_64-rpmfusion_free ~/rpmbuild/SRPMS/zoneminder-1.31.1-1.el7.centos.src.rpm
|
||||||
|
|
||||||
|
|
||||||
Want to build ZoneMinder for Fedora, instead of CentOS, from the same host? Once you download the Fedora SRPM, issue the following:
|
Want to build ZoneMinder for Fedora, instead of CentOS, from the same host? Once you download the Fedora SRPM, issue the following:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
buildzm.sh zmrepo-f21-x86_64 ~/rpmbuild/SRPMS/zoneminder-1.28.1-1.fc21.src.rpm
|
mock -r fedora-26-x86_64-rpmfusion_free ~/rpmbuild/SRPMS/zoneminder-1.31.1-1.el7.centos.src.rpm
|
||||||
|
|
||||||
Notice that the buildzm.sh tool requires the following parameters:
|
Notice that the mock tool requires the following parameters:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
buildzm.sh MOCKCONFIG ZONEMINDER_SRPM
|
mock -r MOCKCONFIG ZONEMINDER_SRPM
|
||||||
|
|
||||||
The list of available Mock config files are available here:
|
The list of available Mock config files are available here:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
ls /etc/mock/zmrepo*.cfg
|
ls /etc/mock/*rpmfusion_free.cfg
|
||||||
|
|
||||||
|
|
||||||
You choose the config file based on the desired distro (e.g. el6, el7, f20, f21) and basearch (e.g. x86, x86_64, arhmhfp). Notice that, when specifying the Mock config as a commandline parameter, you should leave off the ".cfg" filename extension.
|
You choose the config file based on the desired distro (e.g. el6, el7, f20, f21) and basearch (e.g. x86, x86_64, arhmhfp). Notice that, when specifying the Mock config as a commandline parameter, you should leave off the ".cfg" filename extension.
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
************
|
************
|
||||||
Once the build completes, you will be presented with a folder containing the RPM's that were built. Copy the newly built ZoneMinder RPM to the desired system, enable zmrepo per the instruction on the `Zmrepo <http://zmrepo.zoneminder.com/>`_
|
Once the build completes, you will be presented with a message stating where the newly built rpms can be found. It will look similar to this:
|
||||||
website, and then install the rpm by issuing the appropriate yum install command. Finish the installation by following the zoneminder setup instructions in the distro specific readme file, named README.{distroname}, which will be installed into the /usr/share/doc/zoneminder* folder.
|
|
||||||
|
|
||||||
Finally, you may want to consider editing the zmrepo repo file under /etc/yum.repos.d and placing an “exclude=zoneminder*” line into the config file. This will prevent your system from overwriting your manually built RPM with the ZoneMinder RPM found in the repo.
|
|
||||||
|
|
||||||
How to Modify the Source Prior to Build
|
|
||||||
***************************************
|
|
||||||
Before attempting this part of the instructions, make sure and follow the previous instructions for building one of the unmodified SRPMS from zmrepo. Knowing this part works will assist in troubleshooting should something go wrong.
|
|
||||||
|
|
||||||
These instructions may vary depending on what exactly you want to do. The following example assumes you want to build a development snapshot from the master branch.
|
|
||||||
|
|
||||||
From the previous instructions, we downloaded a CentOS 7 ZoneMinder SRPM and placed it into ~/rpmbuild/SRPMS. For this example, install it onto your system:
|
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
rpm -ivh ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm
|
INFO: Results and/or logs in: /var/lib/mock/fedora-26-x86_64/result
|
||||||
|
|
||||||
|
Copy the newly built ZoneMinder RPMs to the desired system, enable RPM Fusion as described in `How to Install ZoneMinder`_, and then install the rpm by issuing the appropriate yum/dnf install command. Finish the installation by following the zoneminder setup instructions in the distro specific readme file, named README.{distroname}, which will be installed into the /usr/share/doc/zoneminder* folder.
|
||||||
|
|
||||||
IMPORTANT: This operation must be done with your normal user account. Do *not* perform this command as root.
|
Finally, you may want to consider editing the rpmfusion repo file under /etc/yum.repos.d and placing an “exclude=zoneminder*” line into the config file. This will prevent your system from overwriting your manually built RPM with the ZoneMinder RPM found in the repo.
|
||||||
|
|
||||||
Make sure you have git installed:
|
How to Create Your Own Source RPM
|
||||||
|
*********************************
|
||||||
|
In the previous section we described how to rebuild an existing ZoneMinder SRPM. The instructions which follow show how to build the ZoneMinder git source tree into a source rpm, which can be used in the previous section to build an rpm.
|
||||||
|
|
||||||
|
Make sure git and rpmdevtools are installed:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
sudo yum install git
|
sudo yum install git rpmdevtools
|
||||||
|
|
||||||
|
|
||||||
Now clone the ZoneMinder git repository:
|
Now clone the ZoneMinder git repository from your home folder:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
@ -170,32 +200,62 @@ Now clone the ZoneMinder git repository:
|
||||||
git clone https://github.com/ZoneMinder/ZoneMinder
|
git clone https://github.com/ZoneMinder/ZoneMinder
|
||||||
cd ZoneMinder
|
cd ZoneMinder
|
||||||
|
|
||||||
This will create a sub-folder called ZoneMinder, which will contain the latest development.
|
This will create a sub-folder called ZoneMinder, which will contain the latest development source code.
|
||||||
|
|
||||||
We want to turn this into a tarball, but first we need to figure out what to name it. Look here:
|
If you have previsouly cloned the ZoneMinder git repo and wish to update it to the most recent, then issue these commands instead:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
ls ~/rpmbuild/SOURCES
|
cd ~\ZoneMinder
|
||||||
|
git pull origin master
|
||||||
|
|
||||||
The tarball from the previsouly installed SRPM should be there. This is the name we will use. For this example, the name is ZoneMinder-1.28.1.tar.gz. From the root folder of the local ZoneMinder git repository, execute the following:
|
Get the crud submodule tarball:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
git archive --prefix=ZoneMinder-1.28.1/ -o ~/rpmbuild/SOURCES/zoneminder-1.28.1.tar.gz HEAD
|
spectool -f -g -R -s 1 ~/ZoneMinder/distros/redhat/zoneminder.spec
|
||||||
|
|
||||||
Note that we are overwriting the original tarball. If you wish to keep the original tarball then create a copy prior to creating the new tarball.
|
At this point, you can make changes to the source code. Depending on what you want to do with those changes, you generally want to create a new branch first:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
cd ~\ZoneMinder
|
||||||
|
git checkout -b mynewbranch
|
||||||
|
|
||||||
|
Again, depending on what you want to do with those changes, you may want to commit your changes:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
cd ~\ZoneMinder
|
||||||
|
git add .
|
||||||
|
git commit
|
||||||
|
|
||||||
|
Once you have made your changes, it is time to turn your work into a new tarball, but first we need to look in the rpm specfile:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
less ~/ZoneMinder/distros/redhat/zoneminder.spec
|
||||||
|
|
||||||
|
Scroll down until you see the Version field. Note the value, which will be in the format x.xx.x. Now create the tarball with the following command:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
cd ~\ZoneMinder
|
||||||
|
git archive --prefix=ZoneMinder-1.31.1/ -o ~/rpmbuild/SOURCES/zoneminder-1.31.1.tar.gz HEAD
|
||||||
|
|
||||||
|
Replace "1.31.1" with the Version shown in the rpm specfile.
|
||||||
|
|
||||||
From the root of the local ZoneMinder git repo, execute the following:
|
From the root of the local ZoneMinder git repo, execute the following:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
cd ~\ZoneMinder
|
||||||
rpmbuild -bs --nodeps distros/redhat/zoneminder.spec
|
rpmbuild -bs --nodeps distros/redhat/zoneminder.spec
|
||||||
|
|
||||||
Notice we used the rpm specfile that is part of the latest master branch you just downloaded, rather than the one that may be in your ~/rpmbbuild/SOURCES folder.
|
This step will create a source rpm and it will tell you where it was saved. For example:
|
||||||
|
|
||||||
This step will overwrite the SRPM you originally downloaded, so you may want to back it up prior to completing this step. Note that the name of the specfile will vary slightly depending on the target distro.
|
::
|
||||||
|
|
||||||
You should now have a new SRPM under ~/rpmbuild/SRPMS. In our example, the SRPM is called zoneminder-1.28.1-2.el7.centos.src.rpm. Now follow the previous instructions that describe how to use the buildzm script, using ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm as the path to your SRPM.
|
|
||||||
|
|
||||||
|
Wrote: /home/abauer/rpmbuild/SRPMS/zoneminder-1.31.1-1.fc26.src.rpm
|
||||||
|
|
||||||
|
Now follow the previous instructions `Build from SRPM`_ which describe how to build that source rpm into an rpm.
|
||||||
|
|
|
@ -16,6 +16,7 @@ configure_file(lib/ZoneMinder/Base.pm.in "${CMAKE_CURRENT_BINARY_DIR}/lib/ZoneMi
|
||||||
configure_file(lib/ZoneMinder/Config.pm.in "${CMAKE_CURRENT_BINARY_DIR}/lib/ZoneMinder/Config.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/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)
|
configure_file(lib/ZoneMinder/ConfigData.pm.in "${CMAKE_CURRENT_BINARY_DIR}/lib/ZoneMinder/ConfigData.pm" @ONLY)
|
||||||
|
configure_file(lib/ZoneMinder/ONVIF.pm.in "${CMAKE_CURRENT_BINARY_DIR}/lib/ZoneMinder/ONVIF.pm" @ONLY)
|
||||||
|
|
||||||
if(CMAKE_VERBOSE_MAKEFILE)
|
if(CMAKE_VERBOSE_MAKEFILE)
|
||||||
set(MAKEMAKER_NOECHO_COMMAND "")
|
set(MAKEMAKER_NOECHO_COMMAND "")
|
||||||
|
|
|
@ -101,8 +101,17 @@ BEGIN {
|
||||||
} else {
|
} else {
|
||||||
$socket = ";host=".$Config{ZM_DB_HOST};
|
$socket = ";host=".$Config{ZM_DB_HOST};
|
||||||
}
|
}
|
||||||
|
my $sslOptions = "";
|
||||||
|
if ( $Config{ZM_DB_SSL_CA_CERT} ) {
|
||||||
|
$sslOptions = ';'.join(';',
|
||||||
|
"mysql_ssl=1",
|
||||||
|
"mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT},
|
||||||
|
"mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY},
|
||||||
|
"mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT}
|
||||||
|
);
|
||||||
|
}
|
||||||
my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
||||||
.$socket
|
.$socket.$sslOptions
|
||||||
, $Config{ZM_DB_USER}
|
, $Config{ZM_DB_USER}
|
||||||
, $Config{ZM_DB_PASS}
|
, $Config{ZM_DB_PASS}
|
||||||
) or croak( "Can't connect to db" );
|
) or croak( "Can't connect to db" );
|
||||||
|
|
|
@ -715,6 +715,25 @@ our @options = (
|
||||||
type => $types{boolean},
|
type => $types{boolean},
|
||||||
category => 'config',
|
category => 'config',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name => 'ZM_TIMESTAMP_CODE_CHAR',
|
||||||
|
default => '%',
|
||||||
|
description => 'Character to used to identify timestamp codes',
|
||||||
|
help => q`
|
||||||
|
There are a few codes one can use to tell ZoneMinder to insert
|
||||||
|
data into the timestamp of each image. Traditionally, the
|
||||||
|
percent (%) character has been used to identify these codes since
|
||||||
|
the current character codes do not conflict with the strftime
|
||||||
|
codes, which can also be used in the timestamp. While this works
|
||||||
|
well for Linux, this does not work well for BSD operating systems.
|
||||||
|
Changing the default character to something else, such as an
|
||||||
|
exclamation point (!), resolves the issue. Note this only affects
|
||||||
|
the timestamp codes built into ZoneMinder. It has no effect on
|
||||||
|
the family of strftime codes one can use.
|
||||||
|
`,
|
||||||
|
type => $types{string},
|
||||||
|
category => 'config',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name => 'ZM_CPU_EXTENSIONS',
|
name => 'ZM_CPU_EXTENSIONS',
|
||||||
default => 'yes',
|
default => 'yes',
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
#
|
#
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
|
|
|
@ -90,8 +90,19 @@ sub zmDbConnect {
|
||||||
} else {
|
} else {
|
||||||
$socket = ";host=".$Config{ZM_DB_HOST};
|
$socket = ";host=".$Config{ZM_DB_HOST};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $sslOptions = "";
|
||||||
|
if ( $Config{ZM_DB_SSL_CA_CERT} ) {
|
||||||
|
$sslOptions = ';'.join(';',
|
||||||
|
"mysql_ssl=1",
|
||||||
|
"mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT},
|
||||||
|
"mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY},
|
||||||
|
"mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
$dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
||||||
.$socket . ($options?';'.join(';', map { $_.'='.$$options{$_} } keys %{$options} ) : '' )
|
.$socket . $sslOptions . ($options?';'.join(';', map { $_.'='.$$options{$_} } keys %{$options} ) : '' )
|
||||||
, $Config{ZM_DB_USER}
|
, $Config{ZM_DB_USER}
|
||||||
, $Config{ZM_DB_PASS}
|
, $Config{ZM_DB_PASS}
|
||||||
);
|
);
|
||||||
|
|
|
@ -238,6 +238,11 @@ sub GenerateVideo {
|
||||||
|
|
||||||
sub delete {
|
sub delete {
|
||||||
my $event = $_[0];
|
my $event = $_[0];
|
||||||
|
if ( ! ( $event->{Id} and $event->{MonitorId} and $event->{StartTime} ) ) {
|
||||||
|
my ( $caller, undef, $line ) = caller;
|
||||||
|
Warning( "Can't Delete event $event->{Id} from Monitor $event->{MonitorId} $event->{StartTime} from $caller:$line\n" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId} $event->{StartTime}\n" );
|
Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId} $event->{StartTime}\n" );
|
||||||
$ZoneMinder::Database::dbh->ping();
|
$ZoneMinder::Database::dbh->ping();
|
||||||
# Do it individually to avoid locking up the table for new events
|
# Do it individually to avoid locking up the table for new events
|
||||||
|
|
|
@ -438,8 +438,17 @@ sub databaseLevel {
|
||||||
} else {
|
} else {
|
||||||
$socket = ";host=".$Config{ZM_DB_HOST};
|
$socket = ";host=".$Config{ZM_DB_HOST};
|
||||||
}
|
}
|
||||||
|
my $sslOptions = "";
|
||||||
|
if ( $Config{ZM_DB_SSL_CA_CERT} ) {
|
||||||
|
$sslOptions = ';'.join(';',
|
||||||
|
"mysql_ssl=1",
|
||||||
|
"mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT},
|
||||||
|
"mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY},
|
||||||
|
"mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT}
|
||||||
|
);
|
||||||
|
}
|
||||||
$this->{dbh} = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
$this->{dbh} = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
||||||
.$socket
|
.$socket.$sslOptions
|
||||||
, $Config{ZM_DB_USER}
|
, $Config{ZM_DB_USER}
|
||||||
, $Config{ZM_DB_PASS}
|
, $Config{ZM_DB_PASS}
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
#
|
#
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
|
|
|
@ -48,6 +48,8 @@ our $VERSION = $ZoneMinder::Base::VERSION;
|
||||||
use Getopt::Std;
|
use Getopt::Std;
|
||||||
use Data::UUID;
|
use Data::UUID;
|
||||||
|
|
||||||
|
use vars qw( $verbose $soap_version );
|
||||||
|
|
||||||
require ONVIF::Client;
|
require ONVIF::Client;
|
||||||
|
|
||||||
require WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort;
|
require WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort;
|
||||||
|
@ -77,7 +79,10 @@ sub deserialize_message {
|
||||||
|
|
||||||
# Try deserializing response - there may be some,
|
# Try deserializing response - there may be some,
|
||||||
# even if transport did not succeed (got a 500 response)
|
# even if transport did not succeed (got a 500 response)
|
||||||
if ( $response ) {
|
if ( ! $response ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
# as our faults are false, returning a success marker is the only
|
# as our faults are false, returning a success marker is the only
|
||||||
# reliable way of determining whether the deserializer succeeded.
|
# reliable way of determining whether the deserializer succeeded.
|
||||||
# Custom deserializers may return an empty list, or undef,
|
# Custom deserializers may return an empty list, or undef,
|
||||||
|
@ -89,21 +94,20 @@ sub deserialize_message {
|
||||||
return wantarray
|
return wantarray
|
||||||
? ($result_body, $result_header)
|
? ($result_body, $result_header)
|
||||||
: $result_body;
|
: $result_body;
|
||||||
}
|
} elsif (blessed $@) { #}&& $@->isa('SOAP::WSDL::SOAP::Typelib::Fault11')) {
|
||||||
elsif (blessed $@) { #}&& $@->isa('SOAP::WSDL::SOAP::Typelib::Fault11')) {
|
|
||||||
return $@;
|
return $@;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
|
#else
|
||||||
return $deserializer->generate_fault({
|
return $deserializer->generate_fault({
|
||||||
code => 'soap:Server',
|
code => 'soap:Server',
|
||||||
role => 'urn:localhost',
|
role => 'urn:localhost',
|
||||||
message => "Error deserializing message: $@. \n"
|
message => "Error deserializing message: $@. \n"
|
||||||
. "Message was: \n$response"
|
. "Message was: \n$response"
|
||||||
});
|
});
|
||||||
}
|
} # end sub deserialize_message
|
||||||
};
|
|
||||||
}
|
sub interpret_messages {
|
||||||
ub interpret_messages {
|
|
||||||
my ($svc_discover, $services, @responses ) = @_;
|
my ($svc_discover, $services, @responses ) = @_;
|
||||||
|
|
||||||
my @results;
|
my @results;
|
||||||
|
@ -167,6 +171,7 @@ ub interpret_messages {
|
||||||
# functions
|
# functions
|
||||||
|
|
||||||
sub discover {
|
sub discover {
|
||||||
|
my ( $soap_version ) = @_;
|
||||||
my @results;
|
my @results;
|
||||||
|
|
||||||
## collect all responses
|
## collect all responses
|
||||||
|
@ -246,6 +251,8 @@ sub discover {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub profiles {
|
sub profiles {
|
||||||
|
my ( $client ) = @_;
|
||||||
|
|
||||||
my $result = $client->get_endpoint('media')->GetProfiles( { } ,, );
|
my $result = $client->get_endpoint('media')->GetProfiles( { } ,, );
|
||||||
die $result if not $result;
|
die $result if not $result;
|
||||||
if($verbose) {
|
if($verbose) {
|
||||||
|
@ -293,7 +300,7 @@ sub profiles {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub move {
|
sub move {
|
||||||
my ($dir) = @_;
|
my ($client, $dir) = @_;
|
||||||
|
|
||||||
my $result = $client->get_endpoint('ptz')->GetNodes( { } ,, );
|
my $result = $client->get_endpoint('ptz')->GetNodes( { } ,, );
|
||||||
|
|
||||||
|
@ -302,6 +309,7 @@ sub move {
|
||||||
} # end sub move
|
} # end sub move
|
||||||
|
|
||||||
sub metadata {
|
sub metadata {
|
||||||
|
my ( $client ) = @_;
|
||||||
my $result = $client->get_endpoint('media')->GetMetadataConfigurations( { } ,, );
|
my $result = $client->get_endpoint('media')->GetMetadataConfigurations( { } ,, );
|
||||||
die $result if not $result;
|
die $result if not $result;
|
||||||
print $result . "\n";
|
print $result . "\n";
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
#
|
#
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
|
|
|
@ -144,16 +144,17 @@ MAIN: while( $loop ) {
|
||||||
# After a long sleep, we may need to reconnect to the db
|
# After a long sleep, we may need to reconnect to the db
|
||||||
while ( ! ( $dbh and $dbh->ping() ) ) {
|
while ( ! ( $dbh and $dbh->ping() ) ) {
|
||||||
$dbh = zmDbConnect();
|
$dbh = zmDbConnect();
|
||||||
|
if ( ! $dbh ) {
|
||||||
if ( $continuous ) {
|
if ( $continuous ) {
|
||||||
Error('Unable to connect to database');
|
Error('Unable to connect to database');
|
||||||
# if we are running continuously, then just skip to the next
|
# if we are running continuously, then just skip to the next
|
||||||
# interval, otherwise we are a one off run, so wait a second and
|
# interval, otherwise we are a one off run, so wait a second and
|
||||||
# retry until someone kills us.
|
# retry until someone kills us.
|
||||||
sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} );
|
sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} );
|
||||||
} else {
|
} else {
|
||||||
Fatal('Unable to connect to database');
|
Fatal('Unable to connect to database');
|
||||||
} # end if
|
} # end if
|
||||||
|
} # end if
|
||||||
} # end while can't connect to the db
|
} # end while can't connect to the db
|
||||||
|
|
||||||
my %Monitors;
|
my %Monitors;
|
||||||
|
@ -270,7 +271,7 @@ MAIN: while( $loop ) {
|
||||||
} # end foreach event
|
} # end foreach event
|
||||||
chdir( $Storage->Path() );
|
chdir( $Storage->Path() );
|
||||||
} # if USE_DEEP_STORAGE
|
} # if USE_DEEP_STORAGE
|
||||||
Debug( 'Got '.int(keys(%$fs_events))." events for monitor $monitor_dir\n" );
|
Debug( 'Got '.int(keys(%$fs_events))." filesystem events for monitor $monitor_dir\n" );
|
||||||
|
|
||||||
#delete_empty_directories( $monitor_dir );
|
#delete_empty_directories( $monitor_dir );
|
||||||
} # end foreach monitor
|
} # end foreach monitor
|
||||||
|
@ -349,7 +350,11 @@ MAIN: while( $loop ) {
|
||||||
while ( my ( $db_event, $age ) = each( %$db_events ) ) {
|
while ( my ( $db_event, $age ) = each( %$db_events ) ) {
|
||||||
if ( ! defined( $fs_events->{$db_event} ) ) {
|
if ( ! defined( $fs_events->{$db_event} ) ) {
|
||||||
Debug("Event $db_event is not in fs.");
|
Debug("Event $db_event is not in fs.");
|
||||||
my $Event = new ZoneMinder::Event( $db_event );
|
my $Event = ZoneMinder::Event->find_one( Id=>$db_event );
|
||||||
|
if ( ! $Event ) {
|
||||||
|
Debug("Event $db_event is no longer in db. Filter probably deleted it while we were auditing.");
|
||||||
|
next;
|
||||||
|
}
|
||||||
if ( ! $Event->StartTime() ) {
|
if ( ! $Event->StartTime() ) {
|
||||||
Debug("Event $$Event{Id} has no start time. deleting it.");
|
Debug("Event $$Event{Id} has no start time. deleting it.");
|
||||||
if ( confirm() ) {
|
if ( confirm() ) {
|
||||||
|
|
|
@ -143,11 +143,11 @@ while( 1 ) {
|
||||||
if ( !defined($image_time) ) {
|
if ( !defined($image_time) ) {
|
||||||
# Can't read from shared data
|
# Can't read from shared data
|
||||||
$restart = 1;
|
$restart = 1;
|
||||||
Error( "Error reading shared data for $$monitor{id} $$monitor{Name}\n");
|
Error( "Error reading shared data for $$monitor{Id} $$monitor{Name}\n");
|
||||||
} elsif ( !$image_time ) {
|
} elsif ( !$image_time ) {
|
||||||
# We can't get the last capture time so can't be sure it's died.
|
# We can't get the last capture time so can't be sure it's died.
|
||||||
$restart = 1;
|
$restart = 1;
|
||||||
Error( "Error getting last capture time for $$monitor{id} $$monitor{Name}\n");
|
Error( "Error getting last capture time for $$monitor{Id} $$monitor{Name}\n");
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
my $max_image_delay = ( $monitor->{MaxFPS}
|
my $max_image_delay = ( $monitor->{MaxFPS}
|
||||||
|
@ -159,7 +159,7 @@ while( 1 ) {
|
||||||
my $image_delay = $now-$image_time;
|
my $image_delay = $now-$image_time;
|
||||||
Debug( "Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay\n" );
|
Debug( "Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay\n" );
|
||||||
if ( $image_delay > $max_image_delay ) {
|
if ( $image_delay > $max_image_delay ) {
|
||||||
Info( "Analysis daemon for $$monitor{id} $$monitor{Name} needs restarting,"
|
Info( "Analysis daemon for $$monitor{Id} $$monitor{Name} needs restarting,"
|
||||||
." time since last analysis $image_delay seconds ($now-$image_time)\n"
|
." time since last analysis $image_delay seconds ($now-$image_time)\n"
|
||||||
);
|
);
|
||||||
$restart = 1;
|
$restart = 1;
|
||||||
|
@ -167,7 +167,7 @@ while( 1 ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $restart ) {
|
if ( $restart ) {
|
||||||
Info( "Restarting analysis daemon for $$monitor{id} $$monitor{Name}\n");
|
Info( "Restarting analysis daemon for $$monitor{Id} $$monitor{Name}\n");
|
||||||
my $command = "zmdc.pl restart zma -m ".$monitor->{Id};
|
my $command = "zmdc.pl restart zma -m ".$monitor->{Id};
|
||||||
runCommand( $command );
|
runCommand( $command );
|
||||||
} # end if restart
|
} # end if restart
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY)
|
configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY)
|
||||||
|
|
||||||
# Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc)
|
# Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc)
|
||||||
set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp)
|
set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp)
|
||||||
|
|
||||||
# A fix for cmake recompiling the source files for every target.
|
# A fix for cmake recompiling the source files for every target.
|
||||||
add_library(zm STATIC ${ZM_BIN_SRC_FILES})
|
add_library(zm STATIC ${ZM_BIN_SRC_FILES})
|
||||||
|
|
|
@ -153,6 +153,12 @@ void process_configfile( char* configFile) {
|
||||||
staticConfig.DB_USER = std::string(val_ptr);
|
staticConfig.DB_USER = std::string(val_ptr);
|
||||||
else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 )
|
else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 )
|
||||||
staticConfig.DB_PASS = std::string(val_ptr);
|
staticConfig.DB_PASS = std::string(val_ptr);
|
||||||
|
else if ( strcasecmp( name_ptr, "ZM_DB_SSL_CA_CERT" ) == 0 )
|
||||||
|
staticConfig.DB_SSL_CA_CERT = std::string(val_ptr);
|
||||||
|
else if ( strcasecmp( name_ptr, "ZM_DB_SSL_CLIENT_KEY" ) == 0 )
|
||||||
|
staticConfig.DB_SSL_CLIENT_KEY = std::string(val_ptr);
|
||||||
|
else if ( strcasecmp( name_ptr, "ZM_DB_SSL_CLIENT_CERT" ) == 0 )
|
||||||
|
staticConfig.DB_SSL_CLIENT_CERT = std::string(val_ptr);
|
||||||
else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 )
|
else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 )
|
||||||
staticConfig.PATH_WEB = std::string(val_ptr);
|
staticConfig.PATH_WEB = std::string(val_ptr);
|
||||||
else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 )
|
else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 )
|
||||||
|
@ -288,8 +294,10 @@ Config::~Config() {
|
||||||
if ( items ) {
|
if ( items ) {
|
||||||
for ( int i = 0; i < n_items; i++ ) {
|
for ( int i = 0; i < n_items; i++ ) {
|
||||||
delete items[i];
|
delete items[i];
|
||||||
|
items[i] = NULL;
|
||||||
}
|
}
|
||||||
delete[] items;
|
delete[] items;
|
||||||
|
items = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,9 @@ struct StaticConfig
|
||||||
std::string DB_NAME;
|
std::string DB_NAME;
|
||||||
std::string DB_USER;
|
std::string DB_USER;
|
||||||
std::string DB_PASS;
|
std::string DB_PASS;
|
||||||
|
std::string DB_SSL_CA_CERT;
|
||||||
|
std::string DB_SSL_CLIENT_KEY;
|
||||||
|
std::string DB_SSL_CLIENT_CERT;
|
||||||
std::string PATH_WEB;
|
std::string PATH_WEB;
|
||||||
std::string SERVER_NAME;
|
std::string SERVER_NAME;
|
||||||
unsigned int SERVER_ID;
|
unsigned int SERVER_ID;
|
||||||
|
|
|
@ -38,6 +38,8 @@ void zmDbConnect() {
|
||||||
my_bool reconnect = 1;
|
my_bool reconnect = 1;
|
||||||
if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) )
|
if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) )
|
||||||
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) );
|
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) );
|
||||||
|
if ( !staticConfig.DB_SSL_CA_CERT.empty() )
|
||||||
|
mysql_ssl_set( &dbconn, staticConfig.DB_SSL_CLIENT_KEY.c_str(), staticConfig.DB_SSL_CLIENT_CERT.c_str(), staticConfig.DB_SSL_CA_CERT.c_str(), NULL, NULL );
|
||||||
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":" );
|
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":" );
|
||||||
if ( colonIndex == std::string::npos ) {
|
if ( colonIndex == std::string::npos ) {
|
||||||
if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, NULL, 0 ) ) {
|
if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, NULL, 0 ) ) {
|
||||||
|
|
|
@ -204,6 +204,7 @@ bool EventStream::loadEventData( int event_id ) {
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit( mysql_errno( &dbconn ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mysql_free_result( result );
|
||||||
//for ( int i = 0; i < 250; i++ )
|
//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 );
|
//Info( "%d -> %d @ %f (%d)", i+1, event_data->frames[i].timestamp, event_data->frames[i].delta, event_data->frames[i].in_db );
|
||||||
|
@ -213,13 +214,13 @@ bool EventStream::loadEventData( int event_id ) {
|
||||||
char filepath[PATH_MAX];
|
char filepath[PATH_MAX];
|
||||||
snprintf( filepath, sizeof(filepath), "%s/%s", event_data->path, event_data->video_file );
|
snprintf( filepath, sizeof(filepath), "%s/%s", event_data->path, event_data->video_file );
|
||||||
ffmpeg_input = new FFmpeg_Input();
|
ffmpeg_input = new FFmpeg_Input();
|
||||||
if ( ! ffmpeg_input->Open( filepath ) ) {
|
if ( 0 > ffmpeg_input->Open( filepath ) ) {
|
||||||
|
Warning("Unable to open ffmpeg_input %s/%s", event_data->path, event_data->video_file );
|
||||||
delete ffmpeg_input;
|
delete ffmpeg_input;
|
||||||
ffmpeg_input = NULL;
|
ffmpeg_input = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mysql_free_result( result );
|
|
||||||
|
|
||||||
if ( forceEventChange || mode == MODE_ALL_GAPLESS ) {
|
if ( forceEventChange || mode == MODE_ALL_GAPLESS ) {
|
||||||
if ( replay_rate > 0 )
|
if ( replay_rate > 0 )
|
||||||
|
@ -230,7 +231,7 @@ bool EventStream::loadEventData( int event_id ) {
|
||||||
Debug( 2, "Event:%ld, Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration );
|
Debug( 2, "Event:%ld, Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration );
|
||||||
|
|
||||||
return( true );
|
return( true );
|
||||||
}
|
} // bool EventStream::loadEventData( int event_id )
|
||||||
|
|
||||||
void EventStream::processCommand( const CmdMsg *msg ) {
|
void EventStream::processCommand( const CmdMsg *msg ) {
|
||||||
Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] );
|
Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] );
|
||||||
|
@ -412,6 +413,7 @@ void EventStream::processCommand( const CmdMsg *msg ) {
|
||||||
}
|
}
|
||||||
send_frame = true;
|
send_frame = true;
|
||||||
break;
|
break;
|
||||||
|
send_frame = true;
|
||||||
}
|
}
|
||||||
case CMD_PAN :
|
case CMD_PAN :
|
||||||
{
|
{
|
||||||
|
@ -598,8 +600,9 @@ bool EventStream::sendFrame( int delta_us ) {
|
||||||
} else if ( monitor->GetOptSaveJPEGs() & 2 ) {
|
} else if ( monitor->GetOptSaveJPEGs() & 2 ) {
|
||||||
snprintf( filepath, sizeof(filepath), Event::analyse_file_format, event_data->path, curr_frame_id );
|
snprintf( filepath, sizeof(filepath), Event::analyse_file_format, event_data->path, curr_frame_id );
|
||||||
if ( stat( filepath, &filestat ) < 0 ) {
|
if ( stat( filepath, &filestat ) < 0 ) {
|
||||||
Debug(1, "%s not found, dalling back to capture");
|
Debug(1, "analyze file %s not found will try to stream from other", filepath);
|
||||||
snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id );
|
snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id );
|
||||||
|
filepath[0] = 0;
|
||||||
}
|
}
|
||||||
} else if ( ! ffmpeg_input ) {
|
} else if ( ! ffmpeg_input ) {
|
||||||
Fatal("JPEGS not saved.zms is not capable of streaming jpegs from mp4 yet");
|
Fatal("JPEGS not saved.zms is not capable of streaming jpegs from mp4 yet");
|
||||||
|
@ -666,12 +669,40 @@ bool EventStream::sendFrame( int delta_us ) {
|
||||||
int img_buffer_size = 0;
|
int img_buffer_size = 0;
|
||||||
uint8_t *img_buffer = temp_img_buffer;
|
uint8_t *img_buffer = temp_img_buffer;
|
||||||
|
|
||||||
|
bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)) && filepath[0];
|
||||||
|
|
||||||
|
fprintf( stdout, "--ZoneMinderFrame\r\n" );
|
||||||
|
|
||||||
|
if ( type != STREAM_JPEG )
|
||||||
|
send_raw = false;
|
||||||
|
|
||||||
if ( send_raw ) {
|
if ( send_raw ) {
|
||||||
if ( ! send_file( filepath ) ) {
|
if ( ! send_file( filepath ) ) {
|
||||||
Error( "Can't send %s: %s", filepath, strerror(errno) );
|
Error( "Can't send %s: %s", filepath, strerror(errno) );
|
||||||
return( false );
|
return( false );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Image *image = NULL;
|
||||||
|
|
||||||
|
if ( filepath[0] ) {
|
||||||
|
image = new Image( filepath );
|
||||||
|
} else if ( ffmpeg_input ) {
|
||||||
|
// Get the frame from the mp4 input
|
||||||
|
Debug(1,"Getting frame from ffmpeg");
|
||||||
|
AVFrame *frame = ffmpeg_input->get_frame( ffmpeg_input->get_video_stream_id() );
|
||||||
|
if ( frame ) {
|
||||||
|
image = new Image( frame );
|
||||||
|
av_frame_free(&frame);
|
||||||
|
} else {
|
||||||
|
Error("Failed getting a frame.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Error("Unable to get a frame");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Image *send_image = prepareImage( image );
|
||||||
|
|
||||||
switch( type ) {
|
switch( type ) {
|
||||||
case STREAM_JPEG :
|
case STREAM_JPEG :
|
||||||
|
@ -692,25 +723,74 @@ bool EventStream::sendFrame( int delta_us ) {
|
||||||
default:
|
default:
|
||||||
Fatal( "Unexpected frame type %d", type );
|
Fatal( "Unexpected frame type %d", type );
|
||||||
break;
|
break;
|
||||||
} // end switch type
|
}
|
||||||
send_buffer( img_buffer, img_buffer_size );
|
delete image;
|
||||||
|
image = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
} // endif send_raw or not
|
} // endif send_raw or not
|
||||||
} // mpeg_output or jpeg
|
} // mpeg_output or jpeg
|
||||||
|
|
||||||
|
if ( send_raw ) {
|
||||||
|
#if HAVE_SENDFILE
|
||||||
|
fprintf( stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size );
|
||||||
|
if ( zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size ) {
|
||||||
|
/* sendfile() failed, use standard way instead */
|
||||||
|
img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj );
|
||||||
|
if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) {
|
||||||
|
Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno));
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size );
|
||||||
|
if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) {
|
||||||
|
Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno));
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
fclose(fdj); /* Close the file handle */
|
||||||
|
} else {
|
||||||
|
Debug(3, "Content length: %d", img_buffer_size );
|
||||||
|
fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size );
|
||||||
|
if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) {
|
||||||
|
Error( "Unable to send stream frame: %s", strerror(errno) );
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fprintf( stdout, "\r\n\r\n" );
|
fprintf( stdout, "\r\n\r\n" );
|
||||||
fflush( stdout );
|
fflush( stdout );
|
||||||
|
}
|
||||||
last_frame_sent = TV_2_FLOAT( now );
|
last_frame_sent = TV_2_FLOAT( now );
|
||||||
return( true );
|
return( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventStream::runStream() {
|
void EventStream::runStream() {
|
||||||
Event::Initialise();
|
Event::Initialise();
|
||||||
|
Debug(3, "Initialized");
|
||||||
|
|
||||||
openComms();
|
openComms();
|
||||||
|
Debug(3, "Comms open");
|
||||||
|
|
||||||
checkInitialised();
|
checkInitialised();
|
||||||
|
|
||||||
|
Debug(3, "frame rate is: (%f)", (double)event_data->frame_count/event_data->duration );
|
||||||
updateFrameRate( (double)event_data->frame_count/event_data->duration );
|
updateFrameRate( (double)event_data->frame_count/event_data->duration );
|
||||||
|
|
||||||
if ( type == STREAM_JPEG )
|
if ( type == STREAM_JPEG )
|
||||||
|
@ -733,6 +813,7 @@ void EventStream::runStream() {
|
||||||
if ( step != 0 )
|
if ( step != 0 )
|
||||||
curr_frame_id += step;
|
curr_frame_id += step;
|
||||||
|
|
||||||
|
// Detects when we hit end of event and will load the next event or previous event
|
||||||
checkEventLoaded();
|
checkEventLoaded();
|
||||||
|
|
||||||
// Get current frame data
|
// Get current frame data
|
||||||
|
@ -771,6 +852,7 @@ void EventStream::runStream() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Figure out if we should send this frame
|
// Figure out if we should send this frame
|
||||||
|
|
||||||
// If we are streaming and this frame is due to be sent
|
// If we are streaming and this frame is due to be sent
|
||||||
if ( ((curr_frame_id-1)%frame_mod) == 0 ) {
|
if ( ((curr_frame_id-1)%frame_mod) == 0 ) {
|
||||||
delta_us = (unsigned int)(frame_data->delta * 1000000);
|
delta_us = (unsigned int)(frame_data->delta * 1000000);
|
||||||
|
@ -784,7 +866,7 @@ void EventStream::runStream() {
|
||||||
// We are paused and are just stepping forward or backward one frame
|
// We are paused and are just stepping forward or backward one frame
|
||||||
step = 0;
|
step = 0;
|
||||||
send_frame = true;
|
send_frame = true;
|
||||||
} else {
|
} else if ( !send_frame ) {
|
||||||
// We are paused, and doing nothing
|
// We are paused, and doing nothing
|
||||||
double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent;
|
double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent;
|
||||||
if ( actual_delta_time > MAX_STREAM_DELAY ) {
|
if ( actual_delta_time > MAX_STREAM_DELAY ) {
|
||||||
|
|
|
@ -130,181 +130,6 @@ int av_dict_parse_string(AVDictionary **pm, const char *str,
|
||||||
#endif
|
#endif
|
||||||
#endif // HAVE_LIBAVUTIL
|
#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 */
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
|
||||||
input_avframe = av_frame_alloc();
|
|
||||||
#else
|
|
||||||
input_avframe = avcodec_alloc_frame();
|
|
||||||
#endif
|
|
||||||
if(input_avframe == NULL) {
|
|
||||||
Fatal("Failed allocating AVFrame for the input");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate AVFrame for the output */
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
|
||||||
output_avframe = av_frame_alloc();
|
|
||||||
#else
|
|
||||||
output_avframe = avcodec_alloc_frame();
|
|
||||||
#endif
|
|
||||||
if(output_avframe == NULL) {
|
|
||||||
Fatal("Failed allocating AVFrame for the output");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SWScale::~SWScale() {
|
|
||||||
|
|
||||||
/* Free up everything */
|
|
||||||
av_frame_free( &input_avframe );
|
|
||||||
//input_avframe = NULL;
|
|
||||||
|
|
||||||
av_frame_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 _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT 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 _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0)
|
|
||||||
/* 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));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Check the buffer sizes */
|
|
||||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
|
||||||
size_t insize = av_image_get_buffer_size(in_pf, width, height,1);
|
|
||||||
#else
|
|
||||||
size_t insize = avpicture_get_size(in_pf, width, height);
|
|
||||||
#endif
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
|
||||||
size_t outsize = av_image_get_buffer_size(out_pf, width, height,1);
|
|
||||||
#else
|
|
||||||
size_t outsize = avpicture_get_size(out_pf, width, height);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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( swscale_ctx, width, height, in_pf, width, height, out_pf, SWS_FAST_BILINEAR, NULL, NULL, NULL );
|
|
||||||
if(swscale_ctx == NULL) {
|
|
||||||
Error("Failed getting swscale context");
|
|
||||||
return -6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill in the buffers */
|
|
||||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
|
||||||
if (av_image_fill_arrays(input_avframe->data, input_avframe->linesize,
|
|
||||||
(uint8_t*) in_buffer, in_pf, width, height, 1) <= 0) {
|
|
||||||
#else
|
|
||||||
if (avpicture_fill((AVPicture*) input_avframe, (uint8_t*) in_buffer,
|
|
||||||
in_pf, width, height) <= 0) {
|
|
||||||
#endif
|
|
||||||
Error("Failed filling input frame with input buffer");
|
|
||||||
return -7;
|
|
||||||
}
|
|
||||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
|
||||||
if (av_image_fill_arrays(output_avframe->data, output_avframe->linesize,
|
|
||||||
out_buffer, out_pf, width, height, 1) <= 0) {
|
|
||||||
#else
|
|
||||||
if (avpicture_fill((AVPicture*) output_avframe, out_buffer, out_pf, width,
|
|
||||||
height) <= 0) {
|
|
||||||
#endif
|
|
||||||
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 _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT 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
|
#endif // HAVE_LIBAVCODEC || HAVE_LIBAVUTIL || HAVE_LIBSWSCALE
|
||||||
|
|
||||||
#if HAVE_LIBAVUTIL
|
#if HAVE_LIBAVUTIL
|
||||||
|
@ -560,5 +385,3 @@ bool is_audio_stream( AVStream * stream ) {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,8 @@
|
||||||
#define ZM_FFMPEG_H
|
#define ZM_FFMPEG_H
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "zm.h"
|
#include "zm.h"
|
||||||
#include "zm_image.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
|
||||||
|
|
||||||
// AVUTIL
|
// AVUTIL
|
||||||
#if HAVE_LIBAVUTIL_AVUTIL_H
|
#if HAVE_LIBAVUTIL_AVUTIL_H
|
||||||
|
@ -207,31 +204,6 @@ void FFMPEGInit();
|
||||||
enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder);
|
enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder);
|
||||||
#endif // HAVE_LIBAVUTIL
|
#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 _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT 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 _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT 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 _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool gotdefaults;
|
|
||||||
struct SwsContext* swscale_ctx;
|
|
||||||
AVFrame* input_avframe;
|
|
||||||
AVFrame* output_avframe;
|
|
||||||
enum _AVPIXELFORMAT default_input_pf;
|
|
||||||
enum _AVPIXELFORMAT default_output_pf;
|
|
||||||
unsigned int default_width;
|
|
||||||
unsigned int default_height;
|
|
||||||
};
|
|
||||||
#endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
|
|
||||||
|
|
||||||
#if !LIBAVCODEC_VERSION_CHECK(54, 25, 0, 51, 100)
|
#if !LIBAVCODEC_VERSION_CHECK(54, 25, 0, 51, 100)
|
||||||
#define AV_CODEC_ID_NONE CODEC_ID_NONE
|
#define AV_CODEC_ID_NONE CODEC_ID_NONE
|
||||||
#define AV_CODEC_ID_PCM_MULAW CODEC_ID_PCM_MULAW
|
#define AV_CODEC_ID_PCM_MULAW CODEC_ID_PCM_MULAW
|
||||||
|
|
|
@ -25,6 +25,10 @@
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "libavutil/time.h"
|
#include "libavutil/time.h"
|
||||||
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
|
#include "libavutil/hwcontext.h"
|
||||||
|
#include "libavutil/hwcontext_qsv.h"
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#ifndef AV_ERROR_MAX_STRING_SIZE
|
#ifndef AV_ERROR_MAX_STRING_SIZE
|
||||||
#define AV_ERROR_MAX_STRING_SIZE 64
|
#define AV_ERROR_MAX_STRING_SIZE 64
|
||||||
|
@ -36,6 +40,47 @@ extern "C" {
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
|
static AVPixelFormat get_format(AVCodecContext *avctx, const enum AVPixelFormat *pix_fmts) {
|
||||||
|
while (*pix_fmts != AV_PIX_FMT_NONE) {
|
||||||
|
if (*pix_fmts == AV_PIX_FMT_QSV) {
|
||||||
|
DecodeContext *decode = (DecodeContext *)avctx->opaque;
|
||||||
|
AVHWFramesContext *frames_ctx;
|
||||||
|
AVQSVFramesContext *frames_hwctx;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* create a pool of surfaces to be used by the decoder */
|
||||||
|
avctx->hw_frames_ctx = av_hwframe_ctx_alloc(decode->hw_device_ref);
|
||||||
|
if (!avctx->hw_frames_ctx)
|
||||||
|
return AV_PIX_FMT_NONE;
|
||||||
|
frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
|
||||||
|
frames_hwctx = (AVQSVFramesContext*)frames_ctx->hwctx;
|
||||||
|
|
||||||
|
frames_ctx->format = AV_PIX_FMT_QSV;
|
||||||
|
frames_ctx->sw_format = avctx->sw_pix_fmt;
|
||||||
|
frames_ctx->width = FFALIGN(avctx->coded_width, 32);
|
||||||
|
frames_ctx->height = FFALIGN(avctx->coded_height, 32);
|
||||||
|
frames_ctx->initial_pool_size = 32;
|
||||||
|
|
||||||
|
frames_hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET;
|
||||||
|
|
||||||
|
ret = av_hwframe_ctx_init(avctx->hw_frames_ctx);
|
||||||
|
if (ret < 0)
|
||||||
|
return AV_PIX_FMT_NONE;
|
||||||
|
|
||||||
|
return AV_PIX_FMT_QSV;
|
||||||
|
}
|
||||||
|
|
||||||
|
pix_fmts++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error( "The QSV pixel format not offered in get_format()");
|
||||||
|
|
||||||
|
return AV_PIX_FMT_NONE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
|
FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
|
||||||
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, p_record_audio ),
|
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, p_record_audio ),
|
||||||
mPath( p_path ),
|
mPath( p_path ),
|
||||||
|
@ -46,6 +91,12 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
|
||||||
Initialise();
|
Initialise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hwaccel = false;
|
||||||
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
|
decode = { NULL };
|
||||||
|
hwFrame = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
mFormatContext = NULL;
|
mFormatContext = NULL;
|
||||||
mVideoStreamId = -1;
|
mVideoStreamId = -1;
|
||||||
mAudioStreamId = -1;
|
mAudioStreamId = -1;
|
||||||
|
@ -63,18 +114,19 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
|
||||||
mReopenThread = 0;
|
mReopenThread = 0;
|
||||||
videoStore = NULL;
|
videoStore = NULL;
|
||||||
video_last_pts = 0;
|
video_last_pts = 0;
|
||||||
|
have_video_keyframe = false;
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
mConvertContext = NULL;
|
mConvertContext = NULL;
|
||||||
#endif
|
#endif
|
||||||
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
/* 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) {
|
if ( colours == ZM_COLOUR_RGB32 ) {
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||||
imagePixFormat = AV_PIX_FMT_RGBA;
|
imagePixFormat = AV_PIX_FMT_RGBA;
|
||||||
} else if(colours == ZM_COLOUR_RGB24) {
|
} else if ( colours == ZM_COLOUR_RGB24 ) {
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||||
imagePixFormat = AV_PIX_FMT_RGB24;
|
imagePixFormat = AV_PIX_FMT_RGB24;
|
||||||
} else if(colours == ZM_COLOUR_GRAY8) {
|
} else if ( colours == ZM_COLOUR_GRAY8 ) {
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||||
imagePixFormat = AV_PIX_FMT_GRAY8;
|
imagePixFormat = AV_PIX_FMT_GRAY8;
|
||||||
} else {
|
} else {
|
||||||
|
@ -93,6 +145,7 @@ FfmpegCamera::~FfmpegCamera() {
|
||||||
if ( capture ) {
|
if ( capture ) {
|
||||||
Terminate();
|
Terminate();
|
||||||
}
|
}
|
||||||
|
avformat_network_deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FfmpegCamera::Initialise() {
|
void FfmpegCamera::Initialise() {
|
||||||
|
@ -113,30 +166,36 @@ int FfmpegCamera::PrimeCapture() {
|
||||||
mAudioStreamId = -1;
|
mAudioStreamId = -1;
|
||||||
Info( "Priming capture from %s", mPath.c_str() );
|
Info( "Priming capture from %s", mPath.c_str() );
|
||||||
|
|
||||||
if (OpenFfmpeg() != 0){
|
#if THREAD
|
||||||
|
if ( OpenFfmpeg() != 0 ) {
|
||||||
ReopenFfmpeg();
|
ReopenFfmpeg();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
#else
|
||||||
|
return OpenFfmpeg();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int FfmpegCamera::PreCapture()
|
int FfmpegCamera::PreCapture() {
|
||||||
{
|
// If Reopen was called, then ffmpeg is closed and we need to reopen it.
|
||||||
|
if ( ! mCanCapture )
|
||||||
|
return OpenFfmpeg();
|
||||||
// Nothing to do here
|
// Nothing to do here
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int FfmpegCamera::Capture( Image &image ) {
|
int FfmpegCamera::Capture( Image &image ) {
|
||||||
if (!mCanCapture){
|
if ( ! mCanCapture ) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread.
|
// If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread.
|
||||||
if (mReopenThread != 0) {
|
if ( mReopenThread != 0 ) {
|
||||||
void *retval = 0;
|
void *retval = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = pthread_join(mReopenThread, &retval);
|
ret = pthread_join(mReopenThread, &retval);
|
||||||
if (ret != 0){
|
if ( ret != 0 ) {
|
||||||
Error("Could not join reopen thread.");
|
Error("Could not join reopen thread.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,9 +223,14 @@ int FfmpegCamera::Capture( Image &image ) {
|
||||||
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf );
|
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int keyframe = packet.flags & AV_PKT_FLAG_KEY;
|
||||||
|
if ( keyframe )
|
||||||
|
have_video_keyframe = true;
|
||||||
|
|
||||||
Debug( 5, "Got packet from stream %d dts (%d) pts(%d)", packet.stream_index, packet.pts, packet.dts );
|
Debug( 5, "Got packet from stream %d dts (%d) pts(%d)", packet.stream_index, packet.pts, packet.dts );
|
||||||
// What about audio stream? Maybe someday we could do sound detection...
|
// What about audio stream? Maybe someday we could do sound detection...
|
||||||
if ( packet.stream_index == mVideoStreamId ) {
|
if ( ( packet.stream_index == mVideoStreamId ) && ( keyframe || have_video_keyframe ) ) {
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
ret = avcodec_send_packet( mVideoCodecContext, &packet );
|
ret = avcodec_send_packet( mVideoCodecContext, &packet );
|
||||||
if ( ret < 0 ) {
|
if ( ret < 0 ) {
|
||||||
|
@ -175,6 +239,25 @@ int FfmpegCamera::Capture( Image &image ) {
|
||||||
zm_av_packet_unref( &packet );
|
zm_av_packet_unref( &packet );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
|
if ( hwaccel ) {
|
||||||
|
ret = avcodec_receive_frame( mVideoCodecContext, hwFrame );
|
||||||
|
if ( ret < 0 ) {
|
||||||
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
|
Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ret = av_hwframe_transfer_data(mRawFrame, hwFrame, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
|
Error( "Unable to transfer frame at frame %d: %s, continuing", frameCount, errbuf );
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
ret = avcodec_receive_frame( mVideoCodecContext, mRawFrame );
|
ret = avcodec_receive_frame( mVideoCodecContext, mRawFrame );
|
||||||
if ( ret < 0 ) {
|
if ( ret < 0 ) {
|
||||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
|
@ -182,6 +265,11 @@ int FfmpegCamera::Capture( Image &image ) {
|
||||||
zm_av_packet_unref( &packet );
|
zm_av_packet_unref( &packet );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
frameComplete = 1;
|
frameComplete = 1;
|
||||||
# else
|
# else
|
||||||
ret = zm_avcodec_decode_video( mVideoCodecContext, mRawFrame, &frameComplete, &packet );
|
ret = zm_avcodec_decode_video( mVideoCodecContext, mRawFrame, &frameComplete, &packet );
|
||||||
|
@ -202,7 +290,7 @@ int FfmpegCamera::Capture( Image &image ) {
|
||||||
|
|
||||||
/* Request a writeable buffer of the target image */
|
/* Request a writeable buffer of the target image */
|
||||||
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||||
if(directbuffer == NULL) {
|
if ( directbuffer == NULL ) {
|
||||||
Error("Failed requesting writeable buffer for the captured image.");
|
Error("Failed requesting writeable buffer for the captured image.");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
@ -216,21 +304,10 @@ int FfmpegCamera::Capture( Image &image ) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
if(mConvertContext == NULL) {
|
if ( sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0 )
|
||||||
mConvertContext = sws_getContext(mVideoCodecContext->width,
|
|
||||||
mVideoCodecContext->height,
|
|
||||||
mVideoCodecContext->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, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0)
|
|
||||||
Fatal("Unable to convert raw format %u to target format %u at frame %d", mVideoCodecContext->pix_fmt, imagePixFormat, frameCount);
|
Fatal("Unable to convert raw format %u to target format %u at frame %d", mVideoCodecContext->pix_fmt, imagePixFormat, frameCount);
|
||||||
#else // HAVE_LIBSWSCALE
|
#else // HAVE_LIBSWSCALE
|
||||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
|
Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras");
|
||||||
#endif // HAVE_LIBSWSCALE
|
#endif // HAVE_LIBSWSCALE
|
||||||
|
|
||||||
frameCount++;
|
frameCount++;
|
||||||
|
@ -256,39 +333,47 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
|
|
||||||
mOpenStart = time(NULL);
|
mOpenStart = time(NULL);
|
||||||
mIsOpening = true;
|
mIsOpening = true;
|
||||||
|
have_video_keyframe = false;
|
||||||
|
|
||||||
// Open the input, not necessarily a file
|
// Open the input, not necessarily a file
|
||||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0)
|
#if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0)
|
||||||
Debug ( 1, "Calling av_open_input_file" );
|
Debug ( 1, "Calling av_open_input_file" );
|
||||||
if ( av_open_input_file( &mFormatContext, mPath.c_str(), NULL, 0, NULL ) !=0 )
|
if ( av_open_input_file( &mFormatContext, mPath.c_str(), NULL, 0, NULL ) != 0 )
|
||||||
#else
|
#else
|
||||||
// Handle options
|
// Handle options
|
||||||
AVDictionary *opts = 0;
|
AVDictionary *opts = 0;
|
||||||
ret = av_dict_parse_string(&opts, Options().c_str(), "=", ",", 0);
|
ret = av_dict_parse_string(&opts, Options().c_str(), "=", ",", 0);
|
||||||
if (ret < 0) {
|
if ( ret < 0 ) {
|
||||||
Warning("Could not parse ffmpeg input options list '%s'\n", Options().c_str());
|
Warning("Could not parse ffmpeg input options list '%s'\n", Options().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set transport method as specified by method field, rtpUni is default
|
// Set transport method as specified by method field, rtpUni is default
|
||||||
if (Method() == "rtpMulti") {
|
const std::string method = Method();
|
||||||
|
if ( method == "rtpMulti" ) {
|
||||||
ret = av_dict_set(&opts, "rtsp_transport", "udp_multicast", 0);
|
ret = av_dict_set(&opts, "rtsp_transport", "udp_multicast", 0);
|
||||||
} else if (Method() == "rtpRtsp") {
|
} else if ( method == "rtpRtsp" ) {
|
||||||
ret = av_dict_set(&opts, "rtsp_transport", "tcp", 0);
|
ret = av_dict_set(&opts, "rtsp_transport", "tcp", 0);
|
||||||
} else if (Method() == "rtpRtspHttp") {
|
} else if ( method == "rtpRtspHttp" ) {
|
||||||
ret = av_dict_set(&opts, "rtsp_transport", "http", 0);
|
ret = av_dict_set(&opts, "rtsp_transport", "http", 0);
|
||||||
|
} else {
|
||||||
|
Warning("Unknown method (%s)", method.c_str() );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0) {
|
if ( ret < 0 ) {
|
||||||
Warning("Could not set rtsp_transport method '%s'\n", Method().c_str());
|
Warning("Could not set rtsp_transport method '%s'\n", method.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug ( 1, "Calling avformat_open_input" );
|
Debug ( 1, "Calling avformat_open_input for %s", mPath.c_str() );
|
||||||
|
|
||||||
mFormatContext = avformat_alloc_context( );
|
mFormatContext = avformat_alloc_context( );
|
||||||
mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback;
|
//mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback;
|
||||||
mFormatContext->interrupt_callback.opaque = this;
|
//mFormatContext->interrupt_callback.opaque = this;
|
||||||
|
// Speed up find_stream_info
|
||||||
|
//FIXME can speed up initial analysis but need sensible parameters...
|
||||||
|
//mFormatContext->probesize = 32;
|
||||||
|
//mFormatContext->max_analyze_duration = 32;
|
||||||
|
|
||||||
if ( avformat_open_input( &mFormatContext, mPath.c_str(), NULL, &opts ) !=0 )
|
if ( avformat_open_input( &mFormatContext, mPath.c_str(), NULL, &opts ) != 0 )
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
mIsOpening = false;
|
mIsOpening = false;
|
||||||
|
@ -297,7 +382,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
}
|
}
|
||||||
|
|
||||||
AVDictionaryEntry *e;
|
AVDictionaryEntry *e;
|
||||||
if ((e = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX)) != NULL) {
|
if ( (e = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
|
||||||
Warning( "Option %s not recognized by ffmpeg", e->key);
|
Warning( "Option %s not recognized by ffmpeg", e->key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,10 +391,6 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
|
|
||||||
Info( "Stream open %s", mPath.c_str() );
|
Info( "Stream open %s", mPath.c_str() );
|
||||||
|
|
||||||
//FIXME can speed up initial analysis but need sensible parameters...
|
|
||||||
//mFormatContext->probesize = 32;
|
|
||||||
//mFormatContext->max_analyze_duration = 32;
|
|
||||||
// Locate stream info from avformat_open_input
|
|
||||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0)
|
#if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0)
|
||||||
Debug ( 1, "Calling av_find_stream_info" );
|
Debug ( 1, "Calling av_find_stream_info" );
|
||||||
if ( av_find_stream_info( mFormatContext ) < 0 )
|
if ( av_find_stream_info( mFormatContext ) < 0 )
|
||||||
|
@ -326,7 +407,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
// The one we want Might not be the first
|
// The one we want Might not be the first
|
||||||
mVideoStreamId = -1;
|
mVideoStreamId = -1;
|
||||||
mAudioStreamId = -1;
|
mAudioStreamId = -1;
|
||||||
for (unsigned int i=0; i < mFormatContext->nb_streams; i++ ) {
|
for ( unsigned int i=0; i < mFormatContext->nb_streams; i++ ) {
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
if ( mFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ) {
|
if ( mFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ) {
|
||||||
#else
|
#else
|
||||||
|
@ -379,8 +460,47 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
//mVideoCodecContext->flags2 |= CODEC_FLAG2_FAST | CODEC_FLAG2_CHUNKS | CODEC_FLAG_LOW_DELAY; // Enable faster H264 decode.
|
//mVideoCodecContext->flags2 |= CODEC_FLAG2_FAST | CODEC_FLAG2_CHUNKS | CODEC_FLAG_LOW_DELAY; // Enable faster H264 decode.
|
||||||
mVideoCodecContext->flags2 |= CODEC_FLAG2_FAST | CODEC_FLAG_LOW_DELAY;
|
mVideoCodecContext->flags2 |= CODEC_FLAG2_FAST | CODEC_FLAG_LOW_DELAY;
|
||||||
|
|
||||||
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
|
if ( mVideoCodecContext->codec_id == AV_CODEC_ID_H264 ) {
|
||||||
|
|
||||||
|
//vaapi_decoder = new VAAPIDecoder();
|
||||||
|
//mVideoCodecContext->opaque = vaapi_decoder;
|
||||||
|
//mVideoCodec = vaapi_decoder->openCodec( mVideoCodecContext );
|
||||||
|
|
||||||
|
if ( ! mVideoCodec ) {
|
||||||
|
// Try to open an hwaccel codec.
|
||||||
|
if ( (mVideoCodec = avcodec_find_decoder_by_name("h264_vaapi")) == NULL ) {
|
||||||
|
Debug(1, "Failed to find decoder (h264_vaapi)" );
|
||||||
|
} else {
|
||||||
|
Debug(1, "Success finding decoder (h264_vaapi)" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( ! mVideoCodec ) {
|
||||||
|
// Try to open an hwaccel codec.
|
||||||
|
if ( (mVideoCodec = avcodec_find_decoder_by_name("h264_qsv")) == NULL ) {
|
||||||
|
Debug(1, "Failed to find decoder (h264_qsv)" );
|
||||||
|
} else {
|
||||||
|
Debug(1, "Success finding decoder (h264_qsv)" );
|
||||||
|
/* open the hardware device */
|
||||||
|
ret = av_hwdevice_ctx_create(&decode.hw_device_ref, AV_HWDEVICE_TYPE_QSV,
|
||||||
|
"auto", NULL, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
Error("Failed to open the hardware device");
|
||||||
|
mVideoCodec = NULL;
|
||||||
|
} else {
|
||||||
|
mVideoCodecContext->opaque = &decode;
|
||||||
|
mVideoCodecContext->get_format = get_format;
|
||||||
|
hwaccel = true;
|
||||||
|
hwFrame = zm_av_frame_alloc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end if h264
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ( (!mVideoCodec) and ( (mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id)) == NULL ) ) {
|
||||||
// Try and get the codec from the codec context
|
// Try and get the codec from the codec context
|
||||||
if ((mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id)) == NULL) {
|
|
||||||
Fatal("Can't find codec for video stream from %s", mPath.c_str());
|
Fatal("Can't find codec for video stream from %s", mPath.c_str());
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Video Found decoder");
|
Debug(1, "Video Found decoder");
|
||||||
|
@ -388,14 +508,30 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
// Open the codec
|
// Open the codec
|
||||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
||||||
Debug ( 1, "Calling avcodec_open" );
|
Debug ( 1, "Calling avcodec_open" );
|
||||||
if (avcodec_open(mVideoCodecContext, mVideoCodec) < 0)
|
if ( avcodec_open(mVideoCodecContext, mVideoCodec) < 0 ){
|
||||||
#else
|
#else
|
||||||
Debug ( 1, "Calling avcodec_open2" );
|
Debug ( 1, "Calling avcodec_open2" );
|
||||||
if (avcodec_open2(mVideoCodecContext, mVideoCodec, 0) < 0)
|
if ( avcodec_open2(mVideoCodecContext, mVideoCodec, &opts) < 0 ) {
|
||||||
#endif
|
#endif
|
||||||
|
AVDictionaryEntry *e;
|
||||||
|
if ( (e = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
|
||||||
|
Warning( "Option %s not recognized by ffmpeg", e->key);
|
||||||
|
}
|
||||||
Fatal( "Unable to open codec for video stream from %s", mPath.c_str() );
|
Fatal( "Unable to open codec for video stream from %s", mPath.c_str() );
|
||||||
|
} else {
|
||||||
|
|
||||||
|
AVDictionaryEntry *e;
|
||||||
|
if ( (e = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
|
||||||
|
Warning( "Option %s not recognized by ffmpeg", e->key);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mVideoCodecContext->hwaccel != NULL) {
|
||||||
|
Debug(1, "HWACCEL in use");
|
||||||
|
} else {
|
||||||
|
Debug(1, "HWACCEL not in use");
|
||||||
|
}
|
||||||
if ( mAudioStreamId >= 0 ) {
|
if ( mAudioStreamId >= 0 ) {
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
mAudioCodecContext = avcodec_alloc_context3( NULL );
|
mAudioCodecContext = avcodec_alloc_context3( NULL );
|
||||||
|
@ -403,7 +539,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
#else
|
#else
|
||||||
mAudioCodecContext = mFormatContext->streams[mAudioStreamId]->codec;
|
mAudioCodecContext = mFormatContext->streams[mAudioStreamId]->codec;
|
||||||
#endif
|
#endif
|
||||||
if ((mAudioCodec = avcodec_find_decoder(mAudioCodecContext->codec_id)) == NULL) {
|
if ( (mAudioCodec = avcodec_find_decoder(mAudioCodecContext->codec_id)) == NULL ) {
|
||||||
Debug(1, "Can't find codec for audio stream from %s", mPath.c_str());
|
Debug(1, "Can't find codec for audio stream from %s", mPath.c_str());
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Audio Found decoder");
|
Debug(1, "Audio Found decoder");
|
||||||
|
@ -411,10 +547,10 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
// Open the codec
|
// Open the codec
|
||||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
|
||||||
Debug ( 1, "Calling avcodec_open" );
|
Debug ( 1, "Calling avcodec_open" );
|
||||||
if (avcodec_open(mAudioCodecContext, mAudioCodec) < 0)
|
if ( avcodec_open(mAudioCodecContext, mAudioCodec) < 0 )
|
||||||
#else
|
#else
|
||||||
Debug ( 1, "Calling avcodec_open2" );
|
Debug ( 1, "Calling avcodec_open2" );
|
||||||
if (avcodec_open2(mAudioCodecContext, mAudioCodec, 0) < 0)
|
if ( avcodec_open2(mAudioCodecContext, mAudioCodec, 0) < 0 )
|
||||||
#endif
|
#endif
|
||||||
Fatal( "Unable to open codec for video stream from %s", mPath.c_str() );
|
Fatal( "Unable to open codec for video stream from %s", mPath.c_str() );
|
||||||
}
|
}
|
||||||
|
@ -428,7 +564,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
// Allocate space for the converted video frame
|
// Allocate space for the converted video frame
|
||||||
mFrame = zm_av_frame_alloc();
|
mFrame = zm_av_frame_alloc();
|
||||||
|
|
||||||
if(mRawFrame == NULL || mFrame == NULL)
|
if ( mRawFrame == NULL || mFrame == NULL )
|
||||||
Fatal( "Unable to allocate frame for %s", mPath.c_str() );
|
Fatal( "Unable to allocate frame for %s", mPath.c_str() );
|
||||||
|
|
||||||
Debug ( 1, "Allocated frames" );
|
Debug ( 1, "Allocated frames" );
|
||||||
|
@ -439,7 +575,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
int pSize = avpicture_get_size( imagePixFormat, width, height );
|
int pSize = avpicture_get_size( imagePixFormat, width, height );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if( (unsigned int)pSize != imagesize) {
|
if ( (unsigned int)pSize != imagesize ) {
|
||||||
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,11 +583,11 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
Debug ( 1, "Calling sws_isSupportedInput" );
|
Debug ( 1, "Calling sws_isSupportedInput" );
|
||||||
if (!sws_isSupportedInput(mVideoCodecContext->pix_fmt)) {
|
if ( !sws_isSupportedInput(mVideoCodecContext->pix_fmt) ) {
|
||||||
Fatal("swscale does not support the codec format: %c%c%c%c", (mVideoCodecContext->pix_fmt)&0xff, ((mVideoCodecContext->pix_fmt >> 8)&0xff), ((mVideoCodecContext->pix_fmt >> 16)&0xff), ((mVideoCodecContext->pix_fmt >> 24)&0xff));
|
Fatal("swscale does not support the codec format: %c%c%c%c", (mVideoCodecContext->pix_fmt)&0xff, ((mVideoCodecContext->pix_fmt >> 8)&0xff), ((mVideoCodecContext->pix_fmt >> 16)&0xff), ((mVideoCodecContext->pix_fmt >> 24)&0xff));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!sws_isSupportedOutput(imagePixFormat)) {
|
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));
|
Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,23 +616,35 @@ int FfmpegCamera::ReopenFfmpeg() {
|
||||||
|
|
||||||
Debug(2, "ReopenFfmpeg called.");
|
Debug(2, "ReopenFfmpeg called.");
|
||||||
|
|
||||||
|
#if THREAD
|
||||||
mCanCapture = false;
|
mCanCapture = false;
|
||||||
if (pthread_create( &mReopenThread, NULL, ReopenFfmpegThreadCallback, (void*) this) != 0){
|
if ( pthread_create( &mReopenThread, NULL, ReopenFfmpegThreadCallback, (void*) this) != 0 ) {
|
||||||
// Log a fatal error and exit the process.
|
// Log a fatal error and exit the process.
|
||||||
Fatal( "ReopenFfmpeg failed to create worker thread." );
|
Fatal( "ReopenFfmpeg failed to create worker thread." );
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
CloseFfmpeg();
|
||||||
|
OpenFfmpeg();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FfmpegCamera::CloseFfmpeg(){
|
int FfmpegCamera::CloseFfmpeg() {
|
||||||
|
|
||||||
Debug(2, "CloseFfmpeg called.");
|
Debug(2, "CloseFfmpeg called.");
|
||||||
|
|
||||||
mCanCapture = false;
|
mCanCapture = false;
|
||||||
|
|
||||||
|
if ( mFrame ) {
|
||||||
av_frame_free( &mFrame );
|
av_frame_free( &mFrame );
|
||||||
|
mFrame = NULL;
|
||||||
|
}
|
||||||
|
if ( mRawFrame ) {
|
||||||
av_frame_free( &mRawFrame );
|
av_frame_free( &mRawFrame );
|
||||||
|
mRawFrame = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
if ( mConvertContext ) {
|
if ( mConvertContext ) {
|
||||||
|
@ -505,12 +653,14 @@ int FfmpegCamera::CloseFfmpeg(){
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (mVideoCodecContext) {
|
if ( mVideoCodecContext ) {
|
||||||
avcodec_close(mVideoCodecContext);
|
avcodec_close(mVideoCodecContext);
|
||||||
|
//av_free(mVideoCodecContext);
|
||||||
mVideoCodecContext = NULL; // Freed by av_close_input_file
|
mVideoCodecContext = NULL; // Freed by av_close_input_file
|
||||||
}
|
}
|
||||||
if (mAudioCodecContext) {
|
if ( mAudioCodecContext ) {
|
||||||
avcodec_close(mAudioCodecContext);
|
avcodec_close(mAudioCodecContext);
|
||||||
|
//av_free(mAudioCodecContext);
|
||||||
mAudioCodecContext = NULL; // Freed by av_close_input_file
|
mAudioCodecContext = NULL; // Freed by av_close_input_file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,11 +677,12 @@ int FfmpegCamera::CloseFfmpeg(){
|
||||||
}
|
}
|
||||||
|
|
||||||
int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
|
int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
|
||||||
|
Debug(3,"FfmpegInteruptCallback");
|
||||||
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
||||||
if (camera->mIsOpening){
|
if ( camera->mIsOpening ) {
|
||||||
int now = time(NULL);
|
int now = time(NULL);
|
||||||
if ((now - camera->mOpenStart) > config.ffmpeg_open_timeout) {
|
if ( (now - camera->mOpenStart) > config.ffmpeg_open_timeout ) {
|
||||||
Error ( "Open video took more than %d seconds.", config.ffmpeg_open_timeout );
|
Error( "Open video took more than %d seconds.", config.ffmpeg_open_timeout );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -540,23 +691,24 @@ int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){
|
void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){
|
||||||
if (ctx == NULL) return NULL;
|
Debug(3,"FfmpegReopenThreadtCallback");
|
||||||
|
if ( ctx == NULL ) return NULL;
|
||||||
|
|
||||||
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
||||||
|
|
||||||
while (1){
|
while (1) {
|
||||||
// Close current stream.
|
// Close current stream.
|
||||||
camera->CloseFfmpeg();
|
camera->CloseFfmpeg();
|
||||||
|
|
||||||
// Sleep if necessary to not reconnect too fast.
|
// Sleep if necessary to not reconnect too fast.
|
||||||
int wait = config.ffmpeg_open_timeout - (time(NULL) - camera->mOpenStart);
|
int wait = config.ffmpeg_open_timeout - (time(NULL) - camera->mOpenStart);
|
||||||
wait = wait < 0 ? 0 : wait;
|
wait = wait < 0 ? 0 : wait;
|
||||||
if (wait > 0){
|
if ( wait > 0 ) {
|
||||||
Debug( 1, "Sleeping %d seconds before reopening stream.", wait );
|
Debug( 1, "Sleeping %d seconds before reopening stream.", wait );
|
||||||
sleep(wait);
|
sleep(wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (camera->OpenFfmpeg() == 0){
|
if ( camera->OpenFfmpeg() == 0 ) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -584,7 +736,15 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mVideoCodecContext->codec_id != AV_CODEC_ID_H264 ) {
|
if ( mVideoCodecContext->codec_id != AV_CODEC_ID_H264 ) {
|
||||||
|
#ifdef AV_CODEC_ID_H265
|
||||||
|
if ( mVideoCodecContext->codec_id == AV_CODEC_ID_H265 ) {
|
||||||
|
Debug( 1, "Input stream appears to be h265. The stored event file may not be viewable in browser." );
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
Error( "Input stream is not h264. The stored event file may not be viewable in browser." );
|
Error( "Input stream is not h264. The stored event file may not be viewable in browser." );
|
||||||
|
#ifdef AV_CODEC_ID_H265
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int frameComplete = false;
|
int frameComplete = false;
|
||||||
|
@ -608,11 +768,11 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
int key_frame = packet.flags & AV_PKT_FLAG_KEY;
|
int keyframe = packet.flags & AV_PKT_FLAG_KEY;
|
||||||
|
|
||||||
Debug( 4, "Got packet from stream %d packet pts (%d) dts(%d), key?(%d)",
|
Debug( 4, "Got packet from stream %d packet pts (%u) dts(%u), key?(%d)",
|
||||||
packet.stream_index, packet.pts, packet.dts,
|
packet.stream_index, packet.pts, packet.dts,
|
||||||
key_frame
|
keyframe
|
||||||
);
|
);
|
||||||
|
|
||||||
//Video recording
|
//Video recording
|
||||||
|
@ -638,6 +798,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
|
||||||
|
|
||||||
delete videoStore;
|
delete videoStore;
|
||||||
videoStore = NULL;
|
videoStore = NULL;
|
||||||
|
have_video_keyframe = false;
|
||||||
|
|
||||||
monitor->SetVideoWriterEventId( 0 );
|
monitor->SetVideoWriterEventId( 0 );
|
||||||
} // end if videoStore
|
} // end if videoStore
|
||||||
|
@ -646,8 +807,8 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
|
||||||
if ( last_event_id and ! videoStore ) {
|
if ( last_event_id and ! videoStore ) {
|
||||||
//Instantiate the video storage module
|
//Instantiate the video storage module
|
||||||
|
|
||||||
if (record_audio) {
|
if ( record_audio ) {
|
||||||
if (mAudioStreamId == -1) {
|
if ( mAudioStreamId == -1 ) {
|
||||||
Debug(3, "Record Audio on but no audio stream found");
|
Debug(3, "Record Audio on but no audio stream found");
|
||||||
videoStore = new VideoStore((const char *) event_file, "mp4",
|
videoStore = new VideoStore((const char *) event_file, "mp4",
|
||||||
mFormatContext->streams[mVideoStreamId],
|
mFormatContext->streams[mVideoStreamId],
|
||||||
|
@ -690,6 +851,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
|
||||||
Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, packetqueue.size() );
|
Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, packetqueue.size() );
|
||||||
if ( avp->stream_index == mVideoStreamId ) {
|
if ( avp->stream_index == mVideoStreamId ) {
|
||||||
ret = videoStore->writeVideoFramePacket( avp );
|
ret = videoStore->writeVideoFramePacket( avp );
|
||||||
|
have_video_keyframe = true;
|
||||||
} else if ( avp->stream_index == mAudioStreamId ) {
|
} else if ( avp->stream_index == mAudioStreamId ) {
|
||||||
ret = videoStore->writeAudioFramePacket( avp );
|
ret = videoStore->writeAudioFramePacket( avp );
|
||||||
} else {
|
} else {
|
||||||
|
@ -710,13 +872,14 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
|
||||||
Info("Deleting videoStore instance");
|
Info("Deleting videoStore instance");
|
||||||
delete videoStore;
|
delete videoStore;
|
||||||
videoStore = NULL;
|
videoStore = NULL;
|
||||||
|
have_video_keyframe = false;
|
||||||
monitor->SetVideoWriterEventId( 0 );
|
monitor->SetVideoWriterEventId( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buffer video packets, since we are not recording.
|
// Buffer video packets, since we are not recording.
|
||||||
// All audio packets are keyframes, so only if it's a video keyframe
|
// All audio packets are keyframes, so only if it's a video keyframe
|
||||||
if ( packet.stream_index == mVideoStreamId ) {
|
if ( packet.stream_index == mVideoStreamId ) {
|
||||||
if ( key_frame ) {
|
if ( keyframe ) {
|
||||||
Debug(3, "Clearing queue");
|
Debug(3, "Clearing queue");
|
||||||
packetqueue.clearQueue( monitor->GetPreEventCount(), mVideoStreamId );
|
packetqueue.clearQueue( monitor->GetPreEventCount(), mVideoStreamId );
|
||||||
}
|
}
|
||||||
|
@ -739,20 +902,27 @@ else if ( packet.pts && video_last_pts > packet.pts ) {
|
||||||
packetqueue.queuePacket( &packet );
|
packetqueue.queuePacket( &packet );
|
||||||
}
|
}
|
||||||
} else if ( packet.stream_index == mVideoStreamId ) {
|
} else if ( packet.stream_index == mVideoStreamId ) {
|
||||||
if ( key_frame || packetqueue.size() ) // it's a keyframe or we already have something in the queue
|
if ( keyframe || packetqueue.size() ) // it's a keyframe or we already have something in the queue
|
||||||
packetqueue.queuePacket( &packet );
|
packetqueue.queuePacket( &packet );
|
||||||
}
|
}
|
||||||
} // end if recording or not
|
} // end if recording or not
|
||||||
|
|
||||||
if ( packet.stream_index == mVideoStreamId ) {
|
if ( packet.stream_index == mVideoStreamId ) {
|
||||||
|
// only do decode if we have had a keyframe, should save a few cycles.
|
||||||
|
if ( have_video_keyframe || keyframe ) {
|
||||||
|
|
||||||
if ( videoStore ) {
|
if ( videoStore ) {
|
||||||
|
|
||||||
//Write the packet to our video store
|
//Write the packet to our video store
|
||||||
int ret = videoStore->writeVideoFramePacket( &packet );
|
int ret = videoStore->writeVideoFramePacket( &packet );
|
||||||
if ( ret < 0 ) { //Less than zero and we skipped a frame
|
if ( ret < 0 ) { //Less than zero and we skipped a frame
|
||||||
zm_av_packet_unref( &packet );
|
zm_av_packet_unref( &packet );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
have_video_keyframe = true;
|
||||||
}
|
}
|
||||||
|
} // end if keyframe or have_video_keyframe
|
||||||
|
|
||||||
Debug(4, "about to decode video" );
|
Debug(4, "about to decode video" );
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
|
@ -763,13 +933,36 @@ else if ( packet.pts && video_last_pts > packet.pts ) {
|
||||||
zm_av_packet_unref( &packet );
|
zm_av_packet_unref( &packet );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ret = avcodec_receive_frame( mVideoCodecContext, mRawFrame );
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
|
if ( hwaccel ) {
|
||||||
|
ret = avcodec_receive_frame( mVideoCodecContext, hwFrame );
|
||||||
if ( ret < 0 ) {
|
if ( ret < 0 ) {
|
||||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
Debug( 1, "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
|
Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
|
||||||
zm_av_packet_unref( &packet );
|
zm_av_packet_unref( &packet );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
ret = av_hwframe_transfer_data(mRawFrame, hwFrame, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
|
Error( "Unable to transfer frame at frame %d: %s, continuing", frameCount, errbuf );
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
|
ret = avcodec_receive_frame( mVideoCodecContext, mRawFrame );
|
||||||
|
if ( ret < 0 ) {
|
||||||
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
|
Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
frameComplete = 1;
|
frameComplete = 1;
|
||||||
# else
|
# else
|
||||||
ret = zm_avcodec_decode_video( mVideoCodecContext, mRawFrame, &frameComplete, &packet );
|
ret = zm_avcodec_decode_video( mVideoCodecContext, mRawFrame, &frameComplete, &packet );
|
||||||
|
@ -815,6 +1008,7 @@ else if ( packet.pts && video_last_pts > packet.pts ) {
|
||||||
} else if ( packet.stream_index == mAudioStreamId ) { //FIXME best way to copy all other streams
|
} else if ( packet.stream_index == mAudioStreamId ) { //FIXME best way to copy all other streams
|
||||||
if ( videoStore ) {
|
if ( videoStore ) {
|
||||||
if ( record_audio ) {
|
if ( record_audio ) {
|
||||||
|
if ( have_video_keyframe ) {
|
||||||
Debug(3, "Recording audio packet streamindex(%d) packetstreamindex(%d)", mAudioStreamId, packet.stream_index );
|
Debug(3, "Recording audio packet streamindex(%d) packetstreamindex(%d)", mAudioStreamId, packet.stream_index );
|
||||||
//Write the packet to our video store
|
//Write the packet to our video store
|
||||||
//FIXME no relevance of last key frame
|
//FIXME no relevance of last key frame
|
||||||
|
@ -824,6 +1018,9 @@ else if ( packet.pts && video_last_pts > packet.pts ) {
|
||||||
zm_av_packet_unref( &packet );
|
zm_av_packet_unref( &packet );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Debug(3, "Not recording audio yet because we don't have a video keyframe yet");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Debug(4, "Not doing recording of audio packet" );
|
Debug(4, "Not doing recording of audio packet" );
|
||||||
}
|
}
|
||||||
|
@ -836,14 +1033,14 @@ else if ( packet.pts && video_last_pts > packet.pts ) {
|
||||||
#else
|
#else
|
||||||
Debug( 3, "Some other stream index %d", packet.stream_index );
|
Debug( 3, "Some other stream index %d", packet.stream_index );
|
||||||
#endif
|
#endif
|
||||||
}
|
} // end if is video or audio or something else
|
||||||
//if ( videoStore ) {
|
|
||||||
|
|
||||||
// the packet contents are ref counted... when queuing, we allocate another packet and reference it with that one, so we should always need to unref here, which should not affect the queued version.
|
// the packet contents are ref counted... when queuing, we allocate another packet and reference it with that one, so we should always need to unref here, which should not affect the queued version.
|
||||||
zm_av_packet_unref( &packet );
|
zm_av_packet_unref( &packet );
|
||||||
//}
|
|
||||||
} // end while ! frameComplete
|
} // end while ! frameComplete
|
||||||
return (frameCount);
|
return (frameCount);
|
||||||
} // end FfmpegCamera::CaptureAndRecord
|
} // end FfmpegCamera::CaptureAndRecord
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
|
@ -23,11 +23,15 @@
|
||||||
#include "zm_camera.h"
|
#include "zm_camera.h"
|
||||||
|
|
||||||
#include "zm_buffer.h"
|
#include "zm_buffer.h"
|
||||||
//#include "zm_utils.h"
|
|
||||||
#include "zm_ffmpeg.h"
|
#include "zm_ffmpeg.h"
|
||||||
#include "zm_videostore.h"
|
#include "zm_videostore.h"
|
||||||
#include "zm_packetqueue.h"
|
#include "zm_packetqueue.h"
|
||||||
|
|
||||||
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
|
typedef struct DecodeContext {
|
||||||
|
AVBufferRef *hw_device_ref;
|
||||||
|
} DecodeContext;
|
||||||
|
#endif
|
||||||
//
|
//
|
||||||
// Class representing 'ffmpeg' cameras, i.e. those which are
|
// Class representing 'ffmpeg' cameras, i.e. those which are
|
||||||
// accessed using ffmpeg multimedia framework
|
// accessed using ffmpeg multimedia framework
|
||||||
|
@ -52,6 +56,12 @@ class FfmpegCamera : public Camera {
|
||||||
AVFrame *mFrame;
|
AVFrame *mFrame;
|
||||||
_AVPIXELFORMAT imagePixFormat;
|
_AVPIXELFORMAT imagePixFormat;
|
||||||
|
|
||||||
|
bool hwaccel;
|
||||||
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
|
AVFrame *hwFrame;
|
||||||
|
DecodeContext decode;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Need to keep track of these because apparently the stream can start with values for pts/dts and then subsequent packets start at zero.
|
// Need to keep track of these because apparently the stream can start with values for pts/dts and then subsequent packets start at zero.
|
||||||
int64_t audio_last_pts;
|
int64_t audio_last_pts;
|
||||||
int64_t audio_last_dts;
|
int64_t audio_last_dts;
|
||||||
|
@ -78,6 +88,7 @@ class FfmpegCamera : public Camera {
|
||||||
char oldDirectory[4096];
|
char oldDirectory[4096];
|
||||||
unsigned int old_event_id;
|
unsigned int old_event_id;
|
||||||
zm_packetqueue packetqueue;
|
zm_packetqueue packetqueue;
|
||||||
|
bool have_video_keyframe;
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
struct SwsContext *mConvertContext;
|
struct SwsContext *mConvertContext;
|
||||||
|
|
|
@ -7,6 +7,9 @@ FFmpeg_Input::FFmpeg_Input() {
|
||||||
input_format_context = NULL;
|
input_format_context = NULL;
|
||||||
video_stream_id = -1;
|
video_stream_id = -1;
|
||||||
audio_stream_id = -1;
|
audio_stream_id = -1;
|
||||||
|
av_register_all();
|
||||||
|
avcodec_register_all();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FFmpeg_Input::~FFmpeg_Input() {
|
FFmpeg_Input::~FFmpeg_Input() {
|
||||||
|
@ -17,7 +20,7 @@ int FFmpeg_Input::Open( const char *filepath ) {
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
/** Open the input file to read from it. */
|
/** Open the input file to read from it. */
|
||||||
if ((error = avformat_open_input( &input_format_context, filepath, NULL, NULL)) < 0) {
|
if ( (error = avformat_open_input( &input_format_context, filepath, NULL, NULL)) < 0 ) {
|
||||||
|
|
||||||
Error("Could not open input file '%s' (error '%s')\n",
|
Error("Could not open input file '%s' (error '%s')\n",
|
||||||
filepath, av_make_error_string(error).c_str() );
|
filepath, av_make_error_string(error).c_str() );
|
||||||
|
@ -26,7 +29,7 @@ int FFmpeg_Input::Open( const char *filepath ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get information on the input file (number of streams etc.). */
|
/** Get information on the input file (number of streams etc.). */
|
||||||
if ((error = avformat_find_stream_info( input_format_context, NULL)) < 0) {
|
if ( (error = avformat_find_stream_info(input_format_context, NULL)) < 0 ) {
|
||||||
Error( "Could not open find stream info (error '%s')\n",
|
Error( "Could not open find stream info (error '%s')\n",
|
||||||
av_make_error_string(error).c_str() );
|
av_make_error_string(error).c_str() );
|
||||||
avformat_close_input(&input_format_context);
|
avformat_close_input(&input_format_context);
|
||||||
|
@ -35,15 +38,14 @@ int FFmpeg_Input::Open( const char *filepath ) {
|
||||||
|
|
||||||
for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) {
|
for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) {
|
||||||
if ( is_video_stream( input_format_context->streams[i] ) ) {
|
if ( is_video_stream( input_format_context->streams[i] ) ) {
|
||||||
|
zm_dump_stream_format(input_format_context, i, 0, 0);
|
||||||
if ( video_stream_id == -1 ) {
|
if ( video_stream_id == -1 ) {
|
||||||
video_stream_id = i;
|
video_stream_id = i;
|
||||||
// if we break, then we won't find the audio stream
|
// if we break, then we won't find the audio stream
|
||||||
continue;
|
|
||||||
} else {
|
} else {
|
||||||
Warning( "Have another video stream." );
|
Warning( "Have another video stream." );
|
||||||
}
|
}
|
||||||
}
|
} else if ( is_audio_stream( input_format_context->streams[i] ) ) {
|
||||||
if ( is_audio_stream( input_format_context->streams[i] ) ) {
|
|
||||||
if ( audio_stream_id == -1 ) {
|
if ( audio_stream_id == -1 ) {
|
||||||
audio_stream_id = i;
|
audio_stream_id = i;
|
||||||
} else {
|
} else {
|
||||||
|
@ -51,6 +53,7 @@ int FFmpeg_Input::Open( const char *filepath ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
streams[i].frame_count = 0;
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
streams[i].context = avcodec_alloc_context3( NULL );
|
streams[i].context = avcodec_alloc_context3( NULL );
|
||||||
avcodec_parameters_to_context( streams[i].context, input_format_context->streams[i]->codecpar );
|
avcodec_parameters_to_context( streams[i].context, input_format_context->streams[i]->codecpar );
|
||||||
|
@ -58,30 +61,123 @@ int FFmpeg_Input::Open( const char *filepath ) {
|
||||||
streams[i].context = input_format_context->streams[i]->codec;
|
streams[i].context = input_format_context->streams[i]->codec;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Find a decoder for the audio stream. */
|
if ( !(streams[i].codec = avcodec_find_decoder(streams[i].context->codec_id)) ) {
|
||||||
if (!(streams[i].codec = avcodec_find_decoder(input_format_context->streams[i]->codecpar->codec_id))) {
|
|
||||||
Error( "Could not find input codec\n");
|
Error( "Could not find input codec\n");
|
||||||
avformat_close_input(&input_format_context);
|
avformat_close_input(&input_format_context);
|
||||||
return AVERROR_EXIT;
|
return AVERROR_EXIT;
|
||||||
|
} else {
|
||||||
|
Debug(1, "Using codec (%s) for stream %d", streams[i].codec->name, i );
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Open the decoder for the audio stream to use it later. */
|
|
||||||
if ((error = avcodec_open2( streams[i].context, streams[i].codec, NULL)) < 0) {
|
if ((error = avcodec_open2( streams[i].context, streams[i].codec, NULL)) < 0) {
|
||||||
Error( "Could not open input codec (error '%s')\n",
|
Error( "Could not open input codec (error '%s')\n",
|
||||||
av_make_error_string(error).c_str() );
|
av_make_error_string(error).c_str() );
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
avcodec_free_context( &streams[i].context );
|
avcodec_free_context( &streams[i].context );
|
||||||
|
#endif
|
||||||
avformat_close_input(&input_format_context);
|
avformat_close_input(&input_format_context);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end foreach stream
|
} // end foreach stream
|
||||||
|
|
||||||
if ( video_stream_id == -1 )
|
if ( video_stream_id == -1 )
|
||||||
Error( "Unable to locate video stream in %s", filepath );
|
Error( "Unable to locate video stream in %s", filepath );
|
||||||
if ( audio_stream_id == -1 )
|
if ( audio_stream_id == -1 )
|
||||||
Debug( 3, "Unable to locate audio stream in %s", filepath );
|
Debug( 3, "Unable to locate audio stream in %s", filepath );
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} // end int FFmpeg::Open( const char * filepath )
|
} // end int FFmpeg_Input::Open( const char * filepath )
|
||||||
|
|
||||||
|
AVFrame *FFmpeg_Input::get_frame( int stream_id ) {
|
||||||
|
Debug(1, "Getting frame from stream %d", stream_id );
|
||||||
|
|
||||||
|
int frameComplete = false;
|
||||||
|
AVPacket packet;
|
||||||
|
av_init_packet( &packet );
|
||||||
|
AVFrame *frame = zm_av_frame_alloc();
|
||||||
|
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||||
|
|
||||||
|
while ( !frameComplete ) {
|
||||||
|
int ret = av_read_frame( input_format_context, &packet );
|
||||||
|
if ( ret < 0 ) {
|
||||||
|
av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||||
|
if (
|
||||||
|
// Check if EOF.
|
||||||
|
(ret == AVERROR_EOF || (input_format_context->pb && input_format_context->pb->eof_reached)) ||
|
||||||
|
// Check for Connection failure.
|
||||||
|
(ret == -110)
|
||||||
|
) {
|
||||||
|
Info( "av_read_frame returned %s.", errbuf );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( (stream_id < 0 ) || ( packet.stream_index == stream_id ) ) {
|
||||||
|
Debug(1,"Packet is for our stream (%d)", packet.stream_index );
|
||||||
|
|
||||||
|
AVCodecContext *context = streams[packet.stream_index].context;
|
||||||
|
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
|
ret = avcodec_send_packet( context, &packet );
|
||||||
|
if ( ret < 0 ) {
|
||||||
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
|
Error( "Unable to send packet at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
Debug(1, "Success getting a packet");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
|
if ( hwaccel ) {
|
||||||
|
ret = avcodec_receive_frame( context, hwFrame );
|
||||||
|
if ( ret < 0 ) {
|
||||||
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
|
Error( "Unable to receive frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ret = av_hwframe_transfer_data(frame, hwFrame, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
|
Error( "Unable to transfer frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
|
Debug(1,"Getting a frame?");
|
||||||
|
ret = avcodec_receive_frame( context, frame );
|
||||||
|
if ( ret < 0 ) {
|
||||||
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
|
Error( "Unable to send packet at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
frameComplete = 1;
|
||||||
|
# else
|
||||||
|
ret = zm_avcodec_decode_video( streams[packet.stream_index].context, frame, &frameComplete, &packet );
|
||||||
|
if ( ret < 0 ) {
|
||||||
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
|
Error( "Unable to decode frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // end if it's the right stream
|
||||||
|
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
|
||||||
|
} // end while ! frameComplete
|
||||||
|
return frame;
|
||||||
|
} // end AVFrame *FFmpeg_Input::get_frame
|
||||||
|
|
||||||
AVPacket * FFmpeg_Input::read_packet() {
|
AVPacket * FFmpeg_Input::read_packet() {
|
||||||
AVPacket *packet = new AVPacket();
|
AVPacket *packet = new AVPacket();
|
||||||
|
@ -153,4 +249,4 @@ int FFmpeg_Input::decode_packet( AVPacket *packet, AVFrame *frame ) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return 1;
|
return 1;
|
||||||
}
|
} // int FFmpeg_Input::decode_packet( AVPacket *packet, AVFrame *frame );
|
||||||
|
|
|
@ -21,6 +21,13 @@ class FFmpeg_Input {
|
||||||
|
|
||||||
int Open( const char *filename );
|
int Open( const char *filename );
|
||||||
int Close();
|
int Close();
|
||||||
|
AVFrame *get_frame( int stream_id=-1 );
|
||||||
|
int get_video_stream_id() {
|
||||||
|
return video_stream_id;
|
||||||
|
}
|
||||||
|
int get_audio_stream_id() {
|
||||||
|
return audio_stream_id;
|
||||||
|
}
|
||||||
|
|
||||||
AVPacket * read_packet();
|
AVPacket * read_packet();
|
||||||
int read_packet( AVPacket *packet );
|
int read_packet( AVPacket *packet );
|
||||||
|
@ -31,6 +38,7 @@ class FFmpeg_Input {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
AVCodecContext *context;
|
AVCodecContext *context;
|
||||||
AVCodec *codec;
|
AVCodec *codec;
|
||||||
|
int frame_count;
|
||||||
} stream;
|
} stream;
|
||||||
|
|
||||||
stream streams[2];
|
stream streams[2];
|
||||||
|
|
130
src/zm_image.cpp
130
src/zm_image.cpp
|
@ -22,6 +22,7 @@
|
||||||
#include "zm_image.h"
|
#include "zm_image.h"
|
||||||
#include "zm_utils.h"
|
#include "zm_utils.h"
|
||||||
#include "zm_rgb.h"
|
#include "zm_rgb.h"
|
||||||
|
#include "zm_ffmpeg.h"
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
@ -107,8 +108,7 @@ Image::Image( const char *filename ) {
|
||||||
text[0] = '\0';
|
text[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer )
|
Image::Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer ) {
|
||||||
{
|
|
||||||
if ( !initialised )
|
if ( !initialised )
|
||||||
Initialise();
|
Initialise();
|
||||||
width = p_width;
|
width = p_width;
|
||||||
|
@ -119,21 +119,57 @@ Image::Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uin
|
||||||
size = pixels*colours;
|
size = pixels*colours;
|
||||||
buffer = 0;
|
buffer = 0;
|
||||||
holdbuffer = 0;
|
holdbuffer = 0;
|
||||||
if ( p_buffer )
|
if ( p_buffer ) {
|
||||||
{
|
|
||||||
allocation = size;
|
allocation = size;
|
||||||
buffertype = ZM_BUFTYPE_DONTFREE;
|
buffertype = ZM_BUFTYPE_DONTFREE;
|
||||||
buffer = p_buffer;
|
buffer = p_buffer;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
AllocImgBuffer(size);
|
AllocImgBuffer(size);
|
||||||
}
|
}
|
||||||
text[0] = '\0';
|
text[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::Image( const Image &p_image )
|
Image::Image( const AVFrame *frame ) {
|
||||||
{
|
AVFrame *dest_frame = zm_av_frame_alloc();
|
||||||
|
|
||||||
|
width = frame->width;
|
||||||
|
height = frame->height;
|
||||||
|
pixels = width*height;
|
||||||
|
colours = ZM_COLOUR_RGB32;
|
||||||
|
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||||
|
size = pixels*colours;
|
||||||
|
buffer = 0;
|
||||||
|
holdbuffer = 0;
|
||||||
|
AllocImgBuffer(size);
|
||||||
|
|
||||||
|
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||||
|
av_image_fill_arrays(dest_frame->data, dest_frame->linesize,
|
||||||
|
buffer, AV_PIX_FMT_RGBA, width, height, 1);
|
||||||
|
#else
|
||||||
|
avpicture_fill( (AVPicture *)dest_frame, buffer,
|
||||||
|
AV_PIX_FMT_RGBA, width, height);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if HAVE_LIBSWSCALE
|
||||||
|
struct SwsContext *mConvertContext = sws_getContext(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
(AVPixelFormat)frame->format,
|
||||||
|
width, height,
|
||||||
|
AV_PIX_FMT_RGBA, SWS_BICUBIC, NULL,
|
||||||
|
NULL, NULL);
|
||||||
|
if ( mConvertContext == NULL )
|
||||||
|
Fatal( "Unable to create conversion context" );
|
||||||
|
|
||||||
|
if ( sws_scale(mConvertContext, frame->data, frame->linesize, 0, frame->height, dest_frame->data, dest_frame->linesize) < 0 )
|
||||||
|
Fatal("Unable to convert raw format %u to target format %u", frame->format, AV_PIX_FMT_RGBA);
|
||||||
|
#else // HAVE_LIBSWSCALE
|
||||||
|
Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras");
|
||||||
|
#endif // HAVE_LIBSWSCALE
|
||||||
|
av_frame_free( &dest_frame );
|
||||||
|
}
|
||||||
|
|
||||||
|
Image::Image( const Image &p_image ) {
|
||||||
if ( !initialised )
|
if ( !initialised )
|
||||||
Initialise();
|
Initialise();
|
||||||
width = p_image.width;
|
width = p_image.width;
|
||||||
|
@ -170,8 +206,7 @@ void Image::Deinitialise() {
|
||||||
delete readjpg_dcinfo;
|
delete readjpg_dcinfo;
|
||||||
readjpg_dcinfo = 0;
|
readjpg_dcinfo = 0;
|
||||||
}
|
}
|
||||||
if ( decodejpg_dcinfo )
|
if ( decodejpg_dcinfo ) {
|
||||||
{
|
|
||||||
jpeg_destroy_decompress( decodejpg_dcinfo );
|
jpeg_destroy_decompress( decodejpg_dcinfo );
|
||||||
delete decodejpg_dcinfo;
|
delete decodejpg_dcinfo;
|
||||||
decodejpg_dcinfo = 0;
|
decodejpg_dcinfo = 0;
|
||||||
|
@ -186,14 +221,13 @@ void Image::Deinitialise() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::Initialise()
|
void Image::Initialise() {
|
||||||
{
|
|
||||||
/* Assign the blend pointer to function */
|
/* Assign the blend pointer to function */
|
||||||
if(config.fast_image_blends) {
|
if ( config.fast_image_blends ) {
|
||||||
if(config.cpu_extensions && sseversion >= 20) {
|
if ( config.cpu_extensions && sseversion >= 20 ) {
|
||||||
fptr_blend = &sse2_fastblend; /* SSE2 fast blend */
|
fptr_blend = &sse2_fastblend; /* SSE2 fast blend */
|
||||||
Debug(4,"Blend: Using SSE2 fast blend function");
|
Debug(4,"Blend: Using SSE2 fast blend function");
|
||||||
} else if(config.cpu_extensions && neonversion >= 1) {
|
} else if ( config.cpu_extensions && neonversion >= 1 ) {
|
||||||
#if defined(__aarch64__)
|
#if defined(__aarch64__)
|
||||||
fptr_blend = &neon64_armv8_fastblend; /* ARM Neon (AArch64) fast blend */
|
fptr_blend = &neon64_armv8_fastblend; /* ARM Neon (AArch64) fast blend */
|
||||||
Debug(4,"Blend: Using ARM Neon (AArch64) fast blend function");
|
Debug(4,"Blend: Using ARM Neon (AArch64) fast blend function");
|
||||||
|
@ -236,8 +270,8 @@ void Image::Initialise()
|
||||||
(*fptr_blend)(blend1,blend2,blendres,128,12.0);
|
(*fptr_blend)(blend1,blend2,blendres,128,12.0);
|
||||||
|
|
||||||
/* Compare results with expected results */
|
/* Compare results with expected results */
|
||||||
for(int i=0;i<128;i++) {
|
for ( int i=0; i < 128; i ++ ) {
|
||||||
if(abs(blendexp[i] - blendres[i]) > 3) {
|
if ( abs(blendexp[i] - blendres[i]) > 3 ) {
|
||||||
Panic("Blend function failed self-test: Results differ from the expected results. Column %u Expected %u Got %u",i,blendexp[i],blendres[i]);
|
Panic("Blend function failed self-test: Results differ from the expected results. Column %u Expected %u Got %u",i,blendexp[i],blendres[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,8 +280,8 @@ void Image::Initialise()
|
||||||
fptr_delta8_bgr = &std_delta8_bgr;
|
fptr_delta8_bgr = &std_delta8_bgr;
|
||||||
|
|
||||||
/* Assign the delta functions */
|
/* Assign the delta functions */
|
||||||
if(config.cpu_extensions) {
|
if ( config.cpu_extensions ) {
|
||||||
if(sseversion >= 35) {
|
if ( sseversion >= 35 ) {
|
||||||
/* SSSE3 available */
|
/* SSSE3 available */
|
||||||
fptr_delta8_rgba = &ssse3_delta8_rgba;
|
fptr_delta8_rgba = &ssse3_delta8_rgba;
|
||||||
fptr_delta8_bgra = &ssse3_delta8_bgra;
|
fptr_delta8_bgra = &ssse3_delta8_bgra;
|
||||||
|
@ -255,7 +289,7 @@ void Image::Initialise()
|
||||||
fptr_delta8_abgr = &ssse3_delta8_abgr;
|
fptr_delta8_abgr = &ssse3_delta8_abgr;
|
||||||
fptr_delta8_gray8 = &sse2_delta8_gray8;
|
fptr_delta8_gray8 = &sse2_delta8_gray8;
|
||||||
Debug(4,"Delta: Using SSSE3 delta functions");
|
Debug(4,"Delta: Using SSSE3 delta functions");
|
||||||
} else if(sseversion >= 20) {
|
} else if ( sseversion >= 20 ) {
|
||||||
/* SSE2 available */
|
/* SSE2 available */
|
||||||
fptr_delta8_rgba = &sse2_delta8_rgba;
|
fptr_delta8_rgba = &sse2_delta8_rgba;
|
||||||
fptr_delta8_bgra = &sse2_delta8_bgra;
|
fptr_delta8_bgra = &sse2_delta8_bgra;
|
||||||
|
@ -263,7 +297,7 @@ void Image::Initialise()
|
||||||
fptr_delta8_abgr = &sse2_delta8_abgr;
|
fptr_delta8_abgr = &sse2_delta8_abgr;
|
||||||
fptr_delta8_gray8 = &sse2_delta8_gray8;
|
fptr_delta8_gray8 = &sse2_delta8_gray8;
|
||||||
Debug(4,"Delta: Using SSE2 delta functions");
|
Debug(4,"Delta: Using SSE2 delta functions");
|
||||||
} else if(neonversion >= 1) {
|
} else if ( neonversion >= 1 ) {
|
||||||
/* ARM Neon available */
|
/* ARM Neon available */
|
||||||
#if defined(__aarch64__)
|
#if defined(__aarch64__)
|
||||||
fptr_delta8_rgba = &neon64_armv8_delta8_rgba;
|
fptr_delta8_rgba = &neon64_armv8_delta8_rgba;
|
||||||
|
@ -329,8 +363,8 @@ void Image::Initialise()
|
||||||
(*fptr_delta8_gray8)(delta8_1,delta8_2,delta8_gray8_res,128);
|
(*fptr_delta8_gray8)(delta8_1,delta8_2,delta8_gray8_res,128);
|
||||||
|
|
||||||
/* Compare results with expected results */
|
/* Compare results with expected results */
|
||||||
for(int i=0;i<128;i++) {
|
for ( int i=0; i < 128; i++ ) {
|
||||||
if(abs(delta8_gray8_exp[i] - delta8_gray8_res[i]) > 7) {
|
if ( abs(delta8_gray8_exp[i] - delta8_gray8_res[i]) > 7 ) {
|
||||||
Panic("Delta grayscale function failed self-test: Results differ from the expected results. Column %u Expected %u Got %u",i,delta8_gray8_exp[i],delta8_gray8_res[i]);
|
Panic("Delta grayscale function failed self-test: Results differ from the expected results. Column %u Expected %u Got %u",i,delta8_gray8_exp[i],delta8_gray8_res[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,8 +373,8 @@ void Image::Initialise()
|
||||||
(*fptr_delta8_rgba)(delta8_1,delta8_2,delta8_rgba_res,32);
|
(*fptr_delta8_rgba)(delta8_1,delta8_2,delta8_rgba_res,32);
|
||||||
|
|
||||||
/* Compare results with expected results */
|
/* Compare results with expected results */
|
||||||
for(int i=0;i<32;i++) {
|
for ( int i=0; i < 32; i++ ) {
|
||||||
if(abs(delta8_rgba_exp[i] - delta8_rgba_res[i]) > 7) {
|
if ( abs(delta8_rgba_exp[i] - delta8_rgba_res[i]) > 7 ) {
|
||||||
Panic("Delta RGBA function failed self-test: Results differ from the expected results. Column %u Expected %u Got %u",i,delta8_rgba_exp[i],delta8_rgba_res[i]);
|
Panic("Delta RGBA function failed self-test: Results differ from the expected results. Column %u Expected %u Got %u",i,delta8_rgba_exp[i],delta8_rgba_res[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,7 +393,7 @@ void Image::Initialise()
|
||||||
|
|
||||||
#if defined(__i386__) && !defined(__x86_64__)
|
#if defined(__i386__) && !defined(__x86_64__)
|
||||||
/* Use SSE2 aligned memory copy? */
|
/* Use SSE2 aligned memory copy? */
|
||||||
if(config.cpu_extensions && sseversion >= 20) {
|
if ( config.cpu_extensions && sseversion >= 20 ) {
|
||||||
fptr_imgbufcpy = &sse2_aligned_memcpy;
|
fptr_imgbufcpy = &sse2_aligned_memcpy;
|
||||||
Debug(4,"Image buffer copy: Using SSE2 aligned memcpy");
|
Debug(4,"Image buffer copy: Using SSE2 aligned memcpy");
|
||||||
} else {
|
} else {
|
||||||
|
@ -425,24 +459,24 @@ void Image::Initialise()
|
||||||
uint8_t* Image::WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder) {
|
uint8_t* Image::WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder) {
|
||||||
unsigned int newsize;
|
unsigned int newsize;
|
||||||
|
|
||||||
if(p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32) {
|
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);
|
Error("WriteBuffer called with unexpected colours: %d",p_colours);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!p_height || !p_width) {
|
if ( !p_height || !p_width ) {
|
||||||
Error("WriteBuffer called with invalid width or height: %d %d",p_width,p_height);
|
Error("WriteBuffer called with invalid width or height: %d %d",p_width,p_height);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(p_width != width || p_height != height || p_colours != colours || p_subpixelorder != subpixelorder) {
|
if ( p_width != width || p_height != height || p_colours != colours || p_subpixelorder != subpixelorder ) {
|
||||||
newsize = (p_width * p_height) * p_colours;
|
newsize = (p_width * p_height) * p_colours;
|
||||||
|
|
||||||
if(buffer == NULL) {
|
if ( buffer == NULL ) {
|
||||||
AllocImgBuffer(newsize);
|
AllocImgBuffer(newsize);
|
||||||
} else {
|
} else {
|
||||||
if(allocation < newsize) {
|
if ( allocation < newsize ) {
|
||||||
if(holdbuffer) {
|
if ( holdbuffer ) {
|
||||||
Error("Held buffer is undersized for requested buffer");
|
Error("Held buffer is undersized for requested buffer");
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
|
@ -467,30 +501,30 @@ uint8_t* Image::WriteBuffer(const unsigned int p_width, const unsigned int p_hei
|
||||||
|
|
||||||
/* 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. */
|
/* 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) {
|
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) {
|
if ( new_buffer == NULL ) {
|
||||||
Error("Attempt to directly assign buffer from a NULL pointer");
|
Error("Attempt to directly assign buffer from a NULL pointer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!p_height || !p_width) {
|
if ( !p_height || !p_width ) {
|
||||||
Error("Attempt to directly assign buffer with invalid width or height: %d %d",p_width,p_height);
|
Error("Attempt to directly assign buffer with invalid width or height: %d %d",p_width,p_height);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32) {
|
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);
|
Error("Attempt to directly assign buffer with unexpected colours per pixel: %d",p_colours);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int new_buffer_size = ((p_width*p_height)*p_colours);
|
unsigned int new_buffer_size = ((p_width*p_height)*p_colours);
|
||||||
|
|
||||||
if(buffer_size < new_buffer_size) {
|
if ( buffer_size < new_buffer_size ) {
|
||||||
Error("Attempt to directly assign buffer from an undersized buffer of size: %zu, needed %dx%d*%d colours = %zu",buffer_size, p_width, p_height, p_colours, new_buffer_size );
|
Error("Attempt to directly assign buffer from an undersized buffer of size: %zu, needed %dx%d*%d colours = %zu",buffer_size, p_width, p_height, p_colours, new_buffer_size );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(holdbuffer && buffer) {
|
if ( holdbuffer && buffer ) {
|
||||||
if(new_buffer_size > allocation) {
|
if ( new_buffer_size > allocation ) {
|
||||||
Error("Held buffer is undersized for assigned buffer");
|
Error("Held buffer is undersized for assigned buffer");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
@ -502,7 +536,7 @@ void Image::AssignDirect( const unsigned int p_width, const unsigned int p_heigh
|
||||||
size = new_buffer_size; // was pixels*colours, but we already calculated it above as new_buffer_size
|
size = new_buffer_size; // was pixels*colours, but we already calculated it above as new_buffer_size
|
||||||
|
|
||||||
/* Copy into the held buffer */
|
/* Copy into the held buffer */
|
||||||
if(new_buffer != buffer)
|
if ( new_buffer != buffer )
|
||||||
(*fptr_imgbufcpy)(buffer, new_buffer, size);
|
(*fptr_imgbufcpy)(buffer, new_buffer, size);
|
||||||
|
|
||||||
/* Free the new buffer */
|
/* Free the new buffer */
|
||||||
|
@ -529,35 +563,35 @@ void Image::AssignDirect( const unsigned int p_width, const unsigned int p_heigh
|
||||||
void Image::Assign(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, const uint8_t* new_buffer, const size_t buffer_size) {
|
void Image::Assign(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, const uint8_t* new_buffer, const size_t buffer_size) {
|
||||||
unsigned int new_size = (p_width * p_height) * p_colours;
|
unsigned int new_size = (p_width * p_height) * p_colours;
|
||||||
|
|
||||||
if(new_buffer == NULL) {
|
if ( new_buffer == NULL ) {
|
||||||
Error("Attempt to assign buffer from a NULL pointer");
|
Error("Attempt to assign buffer from a NULL pointer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(buffer_size < new_size) {
|
if ( buffer_size < new_size ) {
|
||||||
Error("Attempt to assign buffer from an undersized buffer of size: %zu",buffer_size);
|
Error("Attempt to assign buffer from an undersized buffer of size: %zu",buffer_size);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!p_height || !p_width) {
|
if ( !p_height || !p_width ) {
|
||||||
Error("Attempt to assign buffer with invalid width or height: %d %d",p_width,p_height);
|
Error("Attempt to assign buffer with invalid width or height: %d %d",p_width,p_height);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32) {
|
if ( p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32 ) {
|
||||||
Error("Attempt to assign buffer with unexpected colours per pixel: %d",p_colours);
|
Error("Attempt to assign buffer with unexpected colours per pixel: %d",p_colours);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !buffer || p_width != width || p_height != height || p_colours != colours || p_subpixelorder != subpixelorder) {
|
if ( !buffer || p_width != width || p_height != height || p_colours != colours || p_subpixelorder != subpixelorder ) {
|
||||||
|
|
||||||
if (holdbuffer && buffer) {
|
if ( holdbuffer && buffer ) {
|
||||||
if (new_size > allocation) {
|
if ( new_size > allocation ) {
|
||||||
Error("Held buffer is undersized for assigned buffer");
|
Error("Held buffer is undersized for assigned buffer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(new_size > allocation || !buffer) {
|
if ( new_size > allocation || !buffer ) {
|
||||||
DumpImgBuffer();
|
DumpImgBuffer();
|
||||||
AllocImgBuffer(new_size);
|
AllocImgBuffer(new_size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,7 @@
|
||||||
#define ZM_IMAGE_H
|
#define ZM_IMAGE_H
|
||||||
|
|
||||||
#include "zm.h"
|
#include "zm.h"
|
||||||
extern "C"
|
extern "C" {
|
||||||
{
|
|
||||||
#include "zm_jpeg.h"
|
#include "zm_jpeg.h"
|
||||||
}
|
}
|
||||||
#include "zm_rgb.h"
|
#include "zm_rgb.h"
|
||||||
|
@ -32,6 +31,9 @@ extern "C"
|
||||||
#include "zm_mem_utils.h"
|
#include "zm_mem_utils.h"
|
||||||
#include "zm_utils.h"
|
#include "zm_utils.h"
|
||||||
|
|
||||||
|
class Image;
|
||||||
|
#include "zm_ffmpeg.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#if HAVE_ZLIB_H
|
#if HAVE_ZLIB_H
|
||||||
|
@ -55,23 +57,26 @@ extern imgbufcpy_fptr_t fptr_imgbufcpy;
|
||||||
/* Should be called from Image class functions */
|
/* Should be called from Image class functions */
|
||||||
inline static uint8_t* AllocBuffer(size_t p_bufsize) {
|
inline static uint8_t* AllocBuffer(size_t p_bufsize) {
|
||||||
uint8_t* buffer = (uint8_t*)zm_mallocaligned(64,p_bufsize);
|
uint8_t* buffer = (uint8_t*)zm_mallocaligned(64,p_bufsize);
|
||||||
if(buffer == NULL)
|
if ( buffer == NULL )
|
||||||
Fatal("Memory allocation failed: %s",strerror(errno));
|
Fatal("Memory allocation failed: %s",strerror(errno));
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void DumpBuffer(uint8_t* buffer, int buffertype) {
|
inline static void DumpBuffer(uint8_t* buffer, int buffertype) {
|
||||||
if (buffer && buffertype != ZM_BUFTYPE_DONTFREE) {
|
if ( buffer && buffertype != ZM_BUFTYPE_DONTFREE ) {
|
||||||
if(buffertype == ZM_BUFTYPE_ZM)
|
if ( buffertype == ZM_BUFTYPE_ZM ) {
|
||||||
zm_freealigned(buffer);
|
zm_freealigned(buffer);
|
||||||
else if(buffertype == ZM_BUFTYPE_MALLOC)
|
} else if ( buffertype == ZM_BUFTYPE_MALLOC ) {
|
||||||
free(buffer);
|
free(buffer);
|
||||||
else if(buffertype == ZM_BUFTYPE_NEW)
|
} else if ( buffertype == ZM_BUFTYPE_NEW ) {
|
||||||
delete buffer;
|
delete buffer;
|
||||||
/*else if(buffertype == ZM_BUFTYPE_AVMALLOC)
|
/*else if(buffertype == ZM_BUFTYPE_AVMALLOC)
|
||||||
av_free(buffer);
|
av_free(buffer);
|
||||||
*/
|
*/
|
||||||
|
} else {
|
||||||
|
Error( "Unknown buffer type in DumpBuffer(%d)", buffertype );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,27 +85,23 @@ inline static void DumpBuffer(uint8_t* buffer, int buffertype) {
|
||||||
// This is image class, and represents a frame captured from a
|
// This is image class, and represents a frame captured from a
|
||||||
// camera in raw form.
|
// camera in raw form.
|
||||||
//
|
//
|
||||||
class Image
|
class Image {
|
||||||
{
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
struct Edge
|
struct Edge {
|
||||||
{
|
|
||||||
int min_y;
|
int min_y;
|
||||||
int max_y;
|
int max_y;
|
||||||
double min_x;
|
double min_x;
|
||||||
double _1_m;
|
double _1_m;
|
||||||
|
|
||||||
static int CompareYX( const void *p1, const void *p2 )
|
static int CompareYX( const void *p1, const void *p2 ) {
|
||||||
{
|
|
||||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||||
if ( e1->min_y == e2->min_y )
|
if ( e1->min_y == e2->min_y )
|
||||||
return( int(e1->min_x - e2->min_x) );
|
return( int(e1->min_x - e2->min_x) );
|
||||||
else
|
else
|
||||||
return( int(e1->min_y - e2->min_y) );
|
return( int(e1->min_y - e2->min_y) );
|
||||||
}
|
}
|
||||||
static int CompareX( const void *p1, const void *p2 )
|
static int CompareX( const void *p1, const void *p2 ) {
|
||||||
{
|
|
||||||
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
|
||||||
return( int(e1->min_x - e2->min_x) );
|
return( int(e1->min_x - e2->min_x) );
|
||||||
}
|
}
|
||||||
|
@ -113,7 +114,7 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void AllocImgBuffer(size_t p_bufsize) {
|
inline void AllocImgBuffer(size_t p_bufsize) {
|
||||||
if(buffer)
|
if ( buffer )
|
||||||
DumpImgBuffer();
|
DumpImgBuffer();
|
||||||
|
|
||||||
buffer = AllocBuffer(p_bufsize);
|
buffer = AllocBuffer(p_bufsize);
|
||||||
|
@ -149,12 +150,12 @@ protected:
|
||||||
int holdbuffer; /* Hold the buffer instead of replacing it with new one */
|
int holdbuffer; /* Hold the buffer instead of replacing it with new one */
|
||||||
char text[1024];
|
char text[1024];
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Image();
|
Image();
|
||||||
Image( const char *filename );
|
Image( const char *filename );
|
||||||
Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer=0);
|
Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer=0);
|
||||||
Image( const Image &p_image );
|
Image( const Image &p_image );
|
||||||
|
Image( const AVFrame *frame );
|
||||||
~Image();
|
~Image();
|
||||||
static void Initialise();
|
static void Initialise();
|
||||||
static void Deinitialise();
|
static void Deinitialise();
|
||||||
|
@ -176,7 +177,7 @@ public:
|
||||||
inline void HoldBuffer(int tohold) { holdbuffer = tohold; }
|
inline void HoldBuffer(int tohold) { holdbuffer = tohold; }
|
||||||
|
|
||||||
inline void Empty() {
|
inline void Empty() {
|
||||||
if(!holdbuffer)
|
if ( !holdbuffer )
|
||||||
DumpImgBuffer();
|
DumpImgBuffer();
|
||||||
|
|
||||||
width = height = colours = size = pixels = subpixelorder = 0;
|
width = height = colours = size = pixels = subpixelorder = 0;
|
||||||
|
@ -186,17 +187,14 @@ public:
|
||||||
void Assign( const Image &image );
|
void Assign( const Image &image );
|
||||||
void AssignDirect( const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, uint8_t *new_buffer, const size_t buffer_size, const int p_buffertype);
|
void AssignDirect( const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, uint8_t *new_buffer, const size_t buffer_size, const int p_buffertype);
|
||||||
|
|
||||||
inline void CopyBuffer( const Image &image )
|
inline void CopyBuffer( const Image &image ) {
|
||||||
{
|
|
||||||
Assign(image);
|
Assign(image);
|
||||||
}
|
}
|
||||||
inline Image &operator=( const Image &image )
|
inline Image &operator=( const Image &image ) {
|
||||||
{
|
|
||||||
Assign(image);
|
Assign(image);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
inline Image &operator=( const unsigned char *new_buffer )
|
inline Image &operator=( const unsigned char *new_buffer ) {
|
||||||
{
|
|
||||||
(*fptr_imgbufcpy)(buffer, new_buffer, size);
|
(*fptr_imgbufcpy)(buffer, new_buffer, size);
|
||||||
return( *this );
|
return( *this );
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,16 @@ Logger::Logger() :
|
||||||
|
|
||||||
Logger::~Logger() {
|
Logger::~Logger() {
|
||||||
terminate();
|
terminate();
|
||||||
|
smCodes.clear();
|
||||||
|
smSyslogPriorities.clear();
|
||||||
|
#if 0
|
||||||
|
for ( StringMap::iterator itr = smCodes.begin(); itr != smCodes.end(); itr ++ ) {
|
||||||
|
smCodes.erase( itr );
|
||||||
|
}
|
||||||
|
for ( IntMap::iterator itr = smSyslogPriorities.begin(); itr != smSyslogPriorities.end(); itr ++ ) {
|
||||||
|
smSyslogPriorities.erase(itr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::initialise( const std::string &id, const Options &options ) {
|
void Logger::initialise( const std::string &id, const Options &options ) {
|
||||||
|
@ -414,7 +424,8 @@ void Logger::closeSyslog() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... ) {
|
void Logger::logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... ) {
|
||||||
if ( level <= mEffectiveLevel ) {
|
if ( level > mEffectiveLevel )
|
||||||
|
return;
|
||||||
char timeString[64];
|
char timeString[64];
|
||||||
char logString[8192];
|
char logString[8192];
|
||||||
va_list argPtr;
|
va_list argPtr;
|
||||||
|
@ -495,13 +506,17 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
|
||||||
strncpy( logPtr, "]\n", sizeof(logString)-(logPtr-logString) );
|
strncpy( logPtr, "]\n", sizeof(logString)-(logPtr-logString) );
|
||||||
|
|
||||||
if ( level <= mTermLevel ) {
|
if ( level <= mTermLevel ) {
|
||||||
printf( "%s", logString );
|
puts( logString );
|
||||||
fflush( stdout );
|
fflush( stdout );
|
||||||
}
|
}
|
||||||
if ( level <= mFileLevel ) {
|
if ( level <= mFileLevel ) {
|
||||||
fprintf( mLogFileFP, "%s", logString );
|
if ( mLogFileFP ) {
|
||||||
|
fputs( logString, mLogFileFP );
|
||||||
if ( mFlush )
|
if ( mFlush )
|
||||||
fflush( mLogFileFP );
|
fflush( mLogFileFP );
|
||||||
|
} else {
|
||||||
|
puts("Logging to file, but file not open\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*syslogEnd = '\0';
|
*syslogEnd = '\0';
|
||||||
if ( level <= mDatabaseLevel ) {
|
if ( level <= mDatabaseLevel ) {
|
||||||
|
@ -532,7 +547,6 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
|
||||||
abort();
|
abort();
|
||||||
exit( -1 );
|
exit( -1 );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void logInit( const char *name, const Logger::Options &options ) {
|
void logInit( const char *name, const Logger::Options &options ) {
|
||||||
|
@ -544,6 +558,8 @@ void logInit( const char *name, const Logger::Options &options ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void logTerm() {
|
void logTerm() {
|
||||||
if ( Logger::smInstance )
|
if ( Logger::smInstance ) {
|
||||||
delete Logger::smInstance;
|
delete Logger::smInstance;
|
||||||
|
Logger::smInstance = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,7 @@
|
||||||
#endif // HAVE_SYS_SYSCALL_H
|
#endif // HAVE_SYS_SYSCALL_H
|
||||||
#include <mysql/mysql.h>
|
#include <mysql/mysql.h>
|
||||||
|
|
||||||
class Logger
|
class Logger {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
enum {
|
enum {
|
||||||
NOOPT=-6,
|
NOOPT=-6,
|
||||||
|
|
|
@ -397,8 +397,10 @@ Monitor::Monitor(
|
||||||
|
|
||||||
if ( purpose == CAPTURE ) {
|
if ( purpose == CAPTURE ) {
|
||||||
|
|
||||||
this->connect();
|
if ( ! this->connect() ) {
|
||||||
if ( ! mem_ptr ) exit(-1);
|
Error("unable to connect, but doing capture");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
memset( mem_ptr, 0, mem_size );
|
memset( mem_ptr, 0, mem_size );
|
||||||
shared_data->size = sizeof(SharedData);
|
shared_data->size = sizeof(SharedData);
|
||||||
shared_data->active = enabled;
|
shared_data->active = enabled;
|
||||||
|
@ -487,13 +489,18 @@ bool Monitor::connect() {
|
||||||
#if ZM_MEM_MAPPED
|
#if ZM_MEM_MAPPED
|
||||||
snprintf( mem_file, sizeof(mem_file), "%s/zm.mmap.%d", staticConfig.PATH_MAP.c_str(), id );
|
snprintf( mem_file, sizeof(mem_file), "%s/zm.mmap.%d", staticConfig.PATH_MAP.c_str(), id );
|
||||||
map_fd = open( mem_file, O_RDWR|O_CREAT, (mode_t)0600 );
|
map_fd = open( mem_file, O_RDWR|O_CREAT, (mode_t)0600 );
|
||||||
if ( map_fd < 0 )
|
if ( map_fd < 0 ) {
|
||||||
Fatal( "Can't open memory map file %s, probably not enough space free: %s", mem_file, strerror(errno) );
|
Fatal( "Can't open memory map file %s, probably not enough space free: %s", mem_file, strerror(errno) );
|
||||||
|
} else {
|
||||||
|
Debug(3, "Success opening mmap file at (%s)", mem_file );
|
||||||
|
}
|
||||||
|
|
||||||
struct stat map_stat;
|
struct stat map_stat;
|
||||||
if ( fstat( map_fd, &map_stat ) < 0 )
|
if ( fstat( map_fd, &map_stat ) < 0 )
|
||||||
Fatal( "Can't stat memory map file %s: %s, is the zmc process for this monitor running?", mem_file, strerror(errno) );
|
Fatal( "Can't stat memory map file %s: %s, is the zmc process for this monitor running?", mem_file, strerror(errno) );
|
||||||
if ( map_stat.st_size != mem_size && purpose == CAPTURE ) {
|
|
||||||
|
if ( map_stat.st_size != mem_size ) {
|
||||||
|
if ( purpose == CAPTURE ) {
|
||||||
// Allocate the size
|
// Allocate the size
|
||||||
if ( ftruncate( map_fd, mem_size ) < 0 ) {
|
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) );
|
Fatal( "Can't extend memory map file %s to %d bytes: %s", mem_file, mem_size, strerror(errno) );
|
||||||
|
@ -501,24 +508,34 @@ bool Monitor::connect() {
|
||||||
} else if ( map_stat.st_size == 0 ) {
|
} else if ( map_stat.st_size == 0 ) {
|
||||||
Error( "Got empty memory map file size %ld, is the zmc process for this monitor running?", map_stat.st_size, mem_size );
|
Error( "Got empty memory map file size %ld, is the zmc process for this monitor running?", map_stat.st_size, mem_size );
|
||||||
return false;
|
return false;
|
||||||
} else if ( map_stat.st_size != mem_size ) {
|
} else {
|
||||||
Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size );
|
Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size );
|
||||||
return false;
|
return false;
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug(3, "MMap file size is %ld", map_stat.st_size );
|
||||||
#ifdef MAP_LOCKED
|
#ifdef MAP_LOCKED
|
||||||
mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, map_fd, 0 );
|
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 ( mem_ptr == MAP_FAILED ) {
|
||||||
if ( errno == EAGAIN ) {
|
if ( errno == EAGAIN ) {
|
||||||
Debug( 1, "Unable to map file %s (%d bytes) to locked memory, trying unlocked", mem_file, mem_size );
|
Debug( 1, "Unable to map file %s (%d bytes) to locked memory, trying unlocked", mem_file, mem_size );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 );
|
mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 );
|
||||||
Debug( 1, "Mapped file %s (%d bytes) to locked memory, unlocked", mem_file, mem_size );
|
Debug( 1, "Mapped file %s (%d bytes) to unlocked memory", mem_file, mem_size );
|
||||||
#ifdef MAP_LOCKED
|
#ifdef MAP_LOCKED
|
||||||
|
} else {
|
||||||
|
Error( "Unable to map file %s (%d bytes) to locked memory (%s)", mem_file, mem_size , strerror(errno) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if ( mem_ptr == MAP_FAILED )
|
if ( mem_ptr == MAP_FAILED )
|
||||||
Fatal( "Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno );
|
Fatal( "Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno );
|
||||||
|
if ( mem_ptr == NULL ) {
|
||||||
|
Error( "mmap gave a null address:" );
|
||||||
|
} else {
|
||||||
|
Debug(3, "mmapped to %p", mem_ptr );
|
||||||
}
|
}
|
||||||
#else // ZM_MEM_MAPPED
|
#else // ZM_MEM_MAPPED
|
||||||
shm_id = shmget( (config.shm_key&0xffff0000)|id, mem_size, IPC_CREAT|0700 );
|
shm_id = shmget( (config.shm_key&0xffff0000)|id, mem_size, IPC_CREAT|0700 );
|
||||||
|
@ -538,7 +555,8 @@ bool Monitor::connect() {
|
||||||
struct timeval *shared_timestamps = (struct timeval *)((char *)video_store_data + sizeof(VideoStoreData));
|
struct timeval *shared_timestamps = (struct timeval *)((char *)video_store_data + sizeof(VideoStoreData));
|
||||||
unsigned char *shared_images = (unsigned char *)((char *)shared_timestamps + (image_buffer_count*sizeof(struct timeval)));
|
unsigned char *shared_images = (unsigned char *)((char *)shared_timestamps + (image_buffer_count*sizeof(struct timeval)));
|
||||||
|
|
||||||
if(((unsigned long)shared_images % 64) != 0) {
|
|
||||||
|
if ( ((unsigned long)shared_images % 64) != 0 ) {
|
||||||
/* Align images buffer to nearest 64 byte boundary */
|
/* Align images buffer to nearest 64 byte boundary */
|
||||||
Debug(3,"Aligning shared memory images to the next 64 byte boundary");
|
Debug(3,"Aligning shared memory images to the next 64 byte boundary");
|
||||||
shared_images = (uint8_t*)((unsigned long)shared_images + (64 - ((unsigned long)shared_images % 64)));
|
shared_images = (uint8_t*)((unsigned long)shared_images + (64 - ((unsigned long)shared_images % 64)));
|
||||||
|
@ -556,7 +574,6 @@ bool Monitor::connect() {
|
||||||
next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
|
next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
|
||||||
next_buffer.timestamp = new struct timeval;
|
next_buffer.timestamp = new struct timeval;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ( purpose == ANALYSIS ) && analysis_fps ) {
|
if ( ( purpose == ANALYSIS ) && analysis_fps ) {
|
||||||
// Size of pre event buffer must be greater than pre_event_count
|
// Size of pre event buffer must be greater than pre_event_count
|
||||||
// if alarm_frame_count > 1, because in this case the buffer contains
|
// if alarm_frame_count > 1, because in this case the buffer contains
|
||||||
|
@ -568,7 +585,7 @@ bool Monitor::connect() {
|
||||||
pre_event_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
|
pre_event_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Debug(3, "Success connecting");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1320,14 +1337,15 @@ bool Monitor::Analyse() {
|
||||||
score += motion_score;
|
score += motion_score;
|
||||||
}
|
}
|
||||||
noteSetMap[MOTION_CAUSE] = zoneSet;
|
noteSetMap[MOTION_CAUSE] = zoneSet;
|
||||||
|
} // end if motion_score
|
||||||
}
|
|
||||||
shared_data->active = signal;
|
shared_data->active = signal;
|
||||||
}
|
} // end if signal change
|
||||||
|
|
||||||
if ( (!signal_change && signal) && n_linked_monitors > 0 ) {
|
if ( (!signal_change && signal) && n_linked_monitors > 0 ) {
|
||||||
bool first_link = true;
|
bool first_link = true;
|
||||||
Event::StringSet noteSet;
|
Event::StringSet noteSet;
|
||||||
for ( int i = 0; i < n_linked_monitors; i++ ) {
|
for ( int i = 0; i < n_linked_monitors; i++ ) {
|
||||||
|
// TODO: Shouldn't we try to connect?
|
||||||
if ( linked_monitors[i]->isConnected() ) {
|
if ( linked_monitors[i]->isConnected() ) {
|
||||||
if ( linked_monitors[i]->hasAlarmed() ) {
|
if ( linked_monitors[i]->hasAlarmed() ) {
|
||||||
if ( !event ) {
|
if ( !event ) {
|
||||||
|
@ -1353,11 +1371,12 @@ bool Monitor::Analyse() {
|
||||||
if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) {
|
if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) {
|
||||||
if ( event ) {
|
if ( event ) {
|
||||||
//TODO: We shouldn't have to do this every time. Not sure why it clears itself if this isn't here??
|
//TODO: We shouldn't have to do this every time. Not sure why it clears itself if this isn't here??
|
||||||
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
|
//snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
|
||||||
Debug( 3, "Detected new event at (%d.%d)", timestamp->tv_sec,timestamp->tv_usec );
|
Debug( 3, "Detected new event at (%d.%d)", timestamp->tv_sec,timestamp->tv_usec );
|
||||||
|
|
||||||
if ( section_length ) {
|
if ( section_length ) {
|
||||||
int section_mod = timestamp->tv_sec%section_length;
|
// TODO: Wouldn't this be clearer if we just did something like if now - event->start > section_length ?
|
||||||
|
int section_mod = timestamp->tv_sec % section_length;
|
||||||
Debug( 3, "Section length (%d) Last Section Mod(%d), new section mod(%d)", section_length, last_section_mod, section_mod );
|
Debug( 3, "Section length (%d) Last Section Mod(%d), new section mod(%d)", section_length, last_section_mod, section_mod );
|
||||||
if ( section_mod < last_section_mod ) {
|
if ( section_mod < last_section_mod ) {
|
||||||
//if ( state == IDLE || state == TAPE || event_close_mode == CLOSE_TIME ) {
|
//if ( state == IDLE || state == TAPE || event_close_mode == CLOSE_TIME ) {
|
||||||
|
@ -1375,8 +1394,8 @@ bool Monitor::Analyse() {
|
||||||
} else {
|
} else {
|
||||||
last_section_mod = section_mod;
|
last_section_mod = section_mod;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} // end if section_length
|
} // end if section_length
|
||||||
|
} // end if event
|
||||||
|
|
||||||
if ( ! event ) {
|
if ( ! event ) {
|
||||||
|
|
||||||
|
@ -2978,7 +2997,7 @@ void Monitor::TimestampImage( Image *ts_image, const struct timeval *ts_time ) c
|
||||||
const char *s_ptr = label_time_text;
|
const char *s_ptr = label_time_text;
|
||||||
char *d_ptr = label_text;
|
char *d_ptr = label_text;
|
||||||
while ( *s_ptr && ((d_ptr-label_text) < (unsigned int)sizeof(label_text)) ) {
|
while ( *s_ptr && ((d_ptr-label_text) < (unsigned int)sizeof(label_text)) ) {
|
||||||
if ( *s_ptr == '%' ) {
|
if ( *s_ptr == config.timestamp_code_char[0] ) {
|
||||||
bool found_macro = false;
|
bool found_macro = false;
|
||||||
switch ( *(s_ptr+1) ) {
|
switch ( *(s_ptr+1) ) {
|
||||||
case 'N' :
|
case 'N' :
|
||||||
|
|
|
@ -484,7 +484,10 @@ void MonitorStream::runStream() {
|
||||||
|
|
||||||
openComms();
|
openComms();
|
||||||
|
|
||||||
checkInitialised();
|
if ( ! checkInitialised() ) {
|
||||||
|
Error("Not initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
updateFrameRate( monitor->GetFPS() );
|
updateFrameRate( monitor->GetFPS() );
|
||||||
|
|
||||||
|
@ -732,8 +735,9 @@ void MonitorStream::runStream() {
|
||||||
if ( rmdir( swap_path ) < 0 ) {
|
if ( rmdir( swap_path ) < 0 ) {
|
||||||
Error( "Can't rmdir '%s': %s", swap_path, strerror(errno) );
|
Error( "Can't rmdir '%s': %s", swap_path, strerror(errno) );
|
||||||
}
|
}
|
||||||
}
|
} // end if checking for swap_path
|
||||||
}
|
} // end if buffered_playback
|
||||||
|
|
||||||
if ( swap_path ) free( swap_path );
|
if ( swap_path ) free( swap_path );
|
||||||
closeComms();
|
closeComms();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
#include "zm_packet.h"
|
#include "zm_packet.h"
|
||||||
#include "zm_ffmpeg.h"
|
#include "zm_ffmpeg.h"
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
ZMPacket::ZMPacket( AVPacket *p ) {
|
ZMPacket::ZMPacket( AVPacket *p ) {
|
||||||
|
|
|
@ -19,18 +19,17 @@
|
||||||
|
|
||||||
#include "zm_packetqueue.h"
|
#include "zm_packetqueue.h"
|
||||||
#include "zm_ffmpeg.h"
|
#include "zm_ffmpeg.h"
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
#define VIDEO_QUEUESIZE 200
|
#define VIDEO_QUEUESIZE 200
|
||||||
#define AUDIO_QUEUESIZE 50
|
#define AUDIO_QUEUESIZE 50
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
zm_packetqueue::zm_packetqueue(){
|
zm_packetqueue::zm_packetqueue(){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
zm_packetqueue::~zm_packetqueue() {
|
zm_packetqueue::~zm_packetqueue() {
|
||||||
|
clearQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool zm_packetqueue::queuePacket( ZMPacket* zm_packet ) {
|
bool zm_packetqueue::queuePacket( ZMPacket* zm_packet ) {
|
||||||
|
@ -68,7 +67,7 @@ unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
list<ZMPacket *>::reverse_iterator it;
|
std::list<ZMPacket *>::reverse_iterator it;
|
||||||
ZMPacket *packet = NULL;
|
ZMPacket *packet = NULL;
|
||||||
|
|
||||||
for ( it = pktQueue.rbegin(); it != pktQueue.rend() && frames_to_keep; ++it ) {
|
for ( it = pktQueue.rbegin(); it != pktQueue.rend() && frames_to_keep; ++it ) {
|
||||||
|
@ -121,7 +120,7 @@ void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVi
|
||||||
|
|
||||||
// Step 1 - find keyframe < recording_started.
|
// Step 1 - find keyframe < recording_started.
|
||||||
// Step 2 - pop packets until we get to the packet in step 2
|
// Step 2 - pop packets until we get to the packet in step 2
|
||||||
list<ZMPacket *>::reverse_iterator it;
|
std::list<ZMPacket *>::reverse_iterator it;
|
||||||
|
|
||||||
Debug(3, "Looking for keyframe after start recording stream id (%d)", mVideoStreamId );
|
Debug(3, "Looking for keyframe after start recording stream id (%d)", mVideoStreamId );
|
||||||
for ( it = pktQueue.rbegin(); it != pktQueue.rend(); ++ it ) {
|
for ( it = pktQueue.rbegin(); it != pktQueue.rend(); ++ it ) {
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
/*
|
||||||
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "zm_ffmpeg.h"
|
||||||
|
#include "zm_image.h"
|
||||||
|
#include "zm_rgb.h"
|
||||||
|
|
||||||
|
#include "zm_swscale.h"
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||||
|
input_avframe = av_frame_alloc();
|
||||||
|
#else
|
||||||
|
input_avframe = avcodec_alloc_frame();
|
||||||
|
#endif
|
||||||
|
if(input_avframe == NULL) {
|
||||||
|
Fatal("Failed allocating AVFrame for the input");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate AVFrame for the output */
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
|
||||||
|
output_avframe = av_frame_alloc();
|
||||||
|
#else
|
||||||
|
output_avframe = avcodec_alloc_frame();
|
||||||
|
#endif
|
||||||
|
if(output_avframe == NULL) {
|
||||||
|
Fatal("Failed allocating AVFrame for the output");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SWScale::~SWScale() {
|
||||||
|
|
||||||
|
/* Free up everything */
|
||||||
|
av_frame_free( &input_avframe );
|
||||||
|
//input_avframe = NULL;
|
||||||
|
|
||||||
|
av_frame_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 _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT 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 _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0)
|
||||||
|
/* 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));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Check the buffer sizes */
|
||||||
|
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||||
|
size_t insize = av_image_get_buffer_size(in_pf, width, height,1);
|
||||||
|
#else
|
||||||
|
size_t insize = avpicture_get_size(in_pf, width, height);
|
||||||
|
#endif
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||||
|
size_t outsize = av_image_get_buffer_size(out_pf, width, height,1);
|
||||||
|
#else
|
||||||
|
size_t outsize = avpicture_get_size(out_pf, width, height);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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( swscale_ctx, width, height, in_pf, width, height, out_pf, SWS_FAST_BILINEAR, NULL, NULL, NULL );
|
||||||
|
if(swscale_ctx == NULL) {
|
||||||
|
Error("Failed getting swscale context");
|
||||||
|
return -6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill in the buffers */
|
||||||
|
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||||
|
if (av_image_fill_arrays(input_avframe->data, input_avframe->linesize,
|
||||||
|
(uint8_t*) in_buffer, in_pf, width, height, 1) <= 0) {
|
||||||
|
#else
|
||||||
|
if (avpicture_fill((AVPicture*) input_avframe, (uint8_t*) in_buffer,
|
||||||
|
in_pf, width, height) <= 0) {
|
||||||
|
#endif
|
||||||
|
Error("Failed filling input frame with input buffer");
|
||||||
|
return -7;
|
||||||
|
}
|
||||||
|
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||||
|
if (av_image_fill_arrays(output_avframe->data, output_avframe->linesize,
|
||||||
|
out_buffer, out_pf, width, height, 1) <= 0) {
|
||||||
|
#else
|
||||||
|
if (avpicture_fill((AVPicture*) output_avframe, out_buffer, out_pf, width,
|
||||||
|
height) <= 0) {
|
||||||
|
#endif
|
||||||
|
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 _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT 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
|
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef ZM_SWSCALE_H
|
||||||
|
#define ZM_SWSCALE_H
|
||||||
|
|
||||||
|
#include "zm_image.h"
|
||||||
|
#include "zm_ffmpeg.h"
|
||||||
|
|
||||||
|
/* 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 _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT 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 _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT 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 _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool gotdefaults;
|
||||||
|
struct SwsContext* swscale_ctx;
|
||||||
|
AVFrame* input_avframe;
|
||||||
|
AVFrame* output_avframe;
|
||||||
|
enum _AVPIXELFORMAT default_input_pf;
|
||||||
|
enum _AVPIXELFORMAT default_output_pf;
|
||||||
|
unsigned int default_width;
|
||||||
|
unsigned int default_height;
|
||||||
|
};
|
||||||
|
#endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
|
||||||
|
|
||||||
|
#endif
|
|
@ -21,6 +21,7 @@
|
||||||
#include "zm_utils.h"
|
#include "zm_utils.h"
|
||||||
#include "zm_ffmpeg.h"
|
#include "zm_ffmpeg.h"
|
||||||
#include "zm_buffer.h"
|
#include "zm_buffer.h"
|
||||||
|
#include "zm_swscale.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#define HAVE_LIBX264 1
|
#define HAVE_LIBX264 1
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -18,35 +18,35 @@ class VideoStore {
|
||||||
private:
|
private:
|
||||||
unsigned int packets_written;
|
unsigned int packets_written;
|
||||||
|
|
||||||
AVOutputFormat *output_format;
|
AVOutputFormat *out_format;
|
||||||
AVFormatContext *oc;
|
AVFormatContext *oc;
|
||||||
AVStream *video_output_stream;
|
AVStream *video_out_stream;
|
||||||
AVStream *audio_output_stream;
|
AVStream *audio_out_stream;
|
||||||
AVCodecContext *video_output_context;
|
AVCodecContext *video_out_ctx;
|
||||||
|
|
||||||
AVStream *video_input_stream;
|
AVStream *video_in_stream;
|
||||||
AVStream *audio_input_stream;
|
AVStream *audio_in_stream;
|
||||||
|
|
||||||
// Move this into the object so that we aren't constantly allocating/deallocating it on the stack
|
// Move this into the object so that we aren't constantly allocating/deallocating it on the stack
|
||||||
AVPacket opkt;
|
AVPacket opkt;
|
||||||
// we are transcoding
|
// we are transcoding
|
||||||
AVFrame *input_frame;
|
AVFrame *in_frame;
|
||||||
AVFrame *output_frame;
|
AVFrame *out_frame;
|
||||||
|
|
||||||
AVCodecContext *video_input_context;
|
AVCodecContext *video_in_ctx;
|
||||||
AVCodecContext *audio_input_context;
|
AVCodecContext *audio_in_ctx;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
// The following are used when encoding the audio stream to AAC
|
// The following are used when encoding the audio stream to AAC
|
||||||
AVCodec *audio_output_codec;
|
AVCodec *audio_out_codec;
|
||||||
AVCodecContext *audio_output_context;
|
AVCodecContext *audio_out_ctx;
|
||||||
int data_present;
|
int data_present;
|
||||||
AVAudioFifo *fifo;
|
AVAudioFifo *fifo;
|
||||||
int output_frame_size;
|
int out_frame_size;
|
||||||
#ifdef HAVE_LIBAVRESAMPLE
|
#ifdef HAVE_LIBAVRESAMPLE
|
||||||
AVAudioResampleContext* resample_context;
|
AVAudioResampleContext* resample_ctx;
|
||||||
#endif
|
#endif
|
||||||
uint8_t *converted_input_samples;
|
uint8_t *converted_in_samples;
|
||||||
|
|
||||||
const char *filename;
|
const char *filename;
|
||||||
const char *format;
|
const char *format;
|
||||||
|
@ -54,24 +54,30 @@ AVAudioResampleContext* resample_context;
|
||||||
bool keyframeMessage;
|
bool keyframeMessage;
|
||||||
int keyframeSkipNumber;
|
int keyframeSkipNumber;
|
||||||
|
|
||||||
// These are for input
|
// These are for in
|
||||||
int64_t video_last_pts;
|
int64_t video_last_pts;
|
||||||
int64_t video_last_dts;
|
int64_t video_last_dts;
|
||||||
int64_t audio_last_pts;
|
int64_t audio_last_pts;
|
||||||
int64_t audio_last_dts;
|
int64_t audio_last_dts;
|
||||||
|
|
||||||
// These are for output, should start at zero. We assume they do not wrap because we just aren't going to save files that big.
|
// These are for out, should start at zero. We assume they do not wrap because we just aren't going to save files that big.
|
||||||
int64_t video_previous_pts;
|
int64_t video_next_pts;
|
||||||
int64_t video_previous_dts;
|
int64_t video_next_dts;
|
||||||
int64_t audio_previous_pts;
|
int64_t audio_next_pts;
|
||||||
int64_t audio_previous_dts;
|
int64_t audio_next_dts;
|
||||||
|
|
||||||
int64_t filter_in_rescale_delta_last;
|
int64_t filter_in_rescale_delta_last;
|
||||||
|
|
||||||
bool setup_resampler();
|
bool setup_resampler();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VideoStore(const char *filename_in, const char *format_in, AVStream *video_input_stream, AVStream *audio_input_stream, int64_t nStartTime, Monitor * p_monitor );
|
VideoStore(
|
||||||
|
const char *filename_in,
|
||||||
|
const char *format_in,
|
||||||
|
AVStream *video_in_stream,
|
||||||
|
AVStream *audio_in_stream,
|
||||||
|
int64_t nStartTime,
|
||||||
|
Monitor * p_monitor);
|
||||||
~VideoStore();
|
~VideoStore();
|
||||||
|
|
||||||
int writeVideoFramePacket( AVPacket *pkt );
|
int writeVideoFramePacket( AVPacket *pkt );
|
||||||
|
|
|
@ -68,7 +68,7 @@ void Zone::Setup(
|
||||||
overload_frames = p_overload_frames;
|
overload_frames = p_overload_frames;
|
||||||
extend_alarm_frames = p_extend_alarm_frames;
|
extend_alarm_frames = p_extend_alarm_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, AF: %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, extend_alarm_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, AF: %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, extend_alarm_frames );
|
||||||
|
|
||||||
alarmed = false;
|
alarmed = false;
|
||||||
was_alarmed = false;
|
was_alarmed = false;
|
||||||
|
|
10
src/zms.cpp
10
src/zms.cpp
|
@ -52,7 +52,7 @@ int main( int argc, const char *argv[] ) {
|
||||||
|
|
||||||
srand( getpid() * time( 0 ) );
|
srand( getpid() * time( 0 ) );
|
||||||
|
|
||||||
enum { ZMS_MONITOR, ZMS_EVENT } source = ZMS_MONITOR;
|
enum { ZMS_UNKNOWN, ZMS_MONITOR, ZMS_EVENT } source = ZMS_UNKNOWN;
|
||||||
enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, ZMS_ZIP, ZMS_SINGLE } mode = ZMS_JPEG;
|
enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, ZMS_ZIP, ZMS_SINGLE } mode = ZMS_JPEG;
|
||||||
char format[32] = "";
|
char format[32] = "";
|
||||||
int monitor_id = 0;
|
int monitor_id = 0;
|
||||||
|
@ -121,12 +121,16 @@ int main( int argc, const char *argv[] ) {
|
||||||
strncpy( format, value, sizeof(format) );
|
strncpy( format, value, sizeof(format) );
|
||||||
} else if ( !strcmp( name, "monitor" ) ) {
|
} else if ( !strcmp( name, "monitor" ) ) {
|
||||||
monitor_id = atoi( value );
|
monitor_id = atoi( value );
|
||||||
|
if ( source == ZMS_UNKNOWN )
|
||||||
|
source = ZMS_MONITOR;
|
||||||
} else if ( !strcmp( name, "time" ) ) {
|
} else if ( !strcmp( name, "time" ) ) {
|
||||||
event_time = atoi( value );
|
event_time = atoi( value );
|
||||||
} else if ( !strcmp( name, "event" ) ) {
|
} else if ( !strcmp( name, "event" ) ) {
|
||||||
event_id = strtoull( value, (char **)NULL, 10 );
|
event_id = strtoull( value, (char **)NULL, 10 );
|
||||||
|
source = ZMS_EVENT;
|
||||||
} else if ( !strcmp( name, "frame" ) ) {
|
} else if ( !strcmp( name, "frame" ) ) {
|
||||||
frame_id = strtoull( value, (char **)NULL, 10 );
|
frame_id = strtoull( value, (char **)NULL, 10 );
|
||||||
|
source = ZMS_EVENT;
|
||||||
} else if ( !strcmp( name, "scale" ) ) {
|
} else if ( !strcmp( name, "scale" ) ) {
|
||||||
scale = atoi( value );
|
scale = atoi( value );
|
||||||
} else if ( !strcmp( name, "rate" ) ) {
|
} else if ( !strcmp( name, "rate" ) ) {
|
||||||
|
@ -264,6 +268,7 @@ int main( int argc, const char *argv[] ) {
|
||||||
if ( ! event_id ) {
|
if ( ! event_id ) {
|
||||||
Fatal( "Can't view an event without specifying an event_id." );
|
Fatal( "Can't view an event without specifying an event_id." );
|
||||||
}
|
}
|
||||||
|
Debug(3,"Doing event stream scale(%d)", scale );
|
||||||
EventStream stream;
|
EventStream stream;
|
||||||
stream.setStreamScale( scale );
|
stream.setStreamScale( scale );
|
||||||
stream.setStreamReplayRate( rate );
|
stream.setStreamReplayRate( rate );
|
||||||
|
@ -273,6 +278,7 @@ int main( int argc, const char *argv[] ) {
|
||||||
if ( monitor_id && event_time ) {
|
if ( monitor_id && event_time ) {
|
||||||
stream.setStreamStart( monitor_id, event_time );
|
stream.setStreamStart( monitor_id, event_time );
|
||||||
} else {
|
} else {
|
||||||
|
Debug(3, "Setting stream start to frame (%d)", frame_id);
|
||||||
stream.setStreamStart( event_id, frame_id );
|
stream.setStreamStart( event_id, frame_id );
|
||||||
}
|
}
|
||||||
if ( mode == ZMS_JPEG ) {
|
if ( mode == ZMS_JPEG ) {
|
||||||
|
@ -291,6 +297,8 @@ int main( int argc, const char *argv[] ) {
|
||||||
#endif // HAVE_LIBAVCODEC
|
#endif // HAVE_LIBAVCODEC
|
||||||
} // end if jpeg or mpeg
|
} // end if jpeg or mpeg
|
||||||
stream.runStream();
|
stream.runStream();
|
||||||
|
} else {
|
||||||
|
Error("Neither a monitor or event was specified.");
|
||||||
} // end if monitor or event
|
} // end if monitor or event
|
||||||
|
|
||||||
logTerm();
|
logTerm();
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
redhat_package: redhat_bootstrap package
|
redhat_package: redhat_bootstrap package
|
||||||
|
|
||||||
redhat_bootstrap:
|
redhat_bootstrap:
|
||||||
sudo yum install -y --nogpgcheck build/zmrepo.noarch.rpm
|
sudo yum install -y --nogpgcheck build/external-repo.noarch.rpm
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
# General sanity checks
|
# General sanity checks
|
||||||
checksanity () {
|
checksanity () {
|
||||||
# Check to see if this script has access to all the commands it needs
|
# Check to see if this script has access to all the commands it needs
|
||||||
for CMD in set echo curl repoquery git ln mkdir rmdir cat patch; do
|
for CMD in set echo curl git ln mkdir rmdir cat patch; do
|
||||||
type $CMD 2>&1 > /dev/null
|
type $CMD 2>&1 > /dev/null
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
|
@ -20,6 +20,19 @@ checksanity () {
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if [ "${OS}" == "el" ] && [ "${DIST}" == "6" ]; then
|
||||||
|
type repoquery 2>&1 > /dev/null
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo
|
||||||
|
echo "ERROR: The script cannot find the required command \"reqoquery\"."
|
||||||
|
echo "This command is required in order to build ZoneMinder on el6."
|
||||||
|
echo "Please install the \"yum-utils\" package then try again."
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Verify OS & DIST environment variables have been set before calling this script
|
# Verify OS & DIST environment variables have been set before calling this script
|
||||||
if [ -z "${OS}" ] || [ -z "${DIST}" ]; then
|
if [ -z "${OS}" ] || [ -z "${DIST}" ]; then
|
||||||
echo "ERROR: both OS and DIST environment variables must be set"
|
echo "ERROR: both OS and DIST environment variables must be set"
|
||||||
|
@ -211,6 +224,26 @@ zoneminder ($VERSION-${DIST}-1) unstable; urgency=low
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# start packpack, filter the output if we are running in travis
|
||||||
|
execpackpack () {
|
||||||
|
|
||||||
|
if [ "${OS}" == "el" ] || [ "${OS}" == "fedora" ]; then
|
||||||
|
parms="-f utils/packpack/redhat_package.mk redhat_package"
|
||||||
|
else
|
||||||
|
parms=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${TRAVIS}" == "true" ]; then
|
||||||
|
utils/packpack/heartbeat.sh &
|
||||||
|
mypid=$!
|
||||||
|
packpack/packpack $parms > buildlog.txt 2>&1
|
||||||
|
kill $mypid
|
||||||
|
tail -n 3000 buildlog.txt | grep -v ONVIF
|
||||||
|
else
|
||||||
|
packpack/packpack $parms
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
################
|
################
|
||||||
# MAIN PROGRAM #
|
# MAIN PROGRAM #
|
||||||
################
|
################
|
||||||
|
@ -235,31 +268,31 @@ if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${TRAVIS}" != "true" ]; then
|
||||||
rm -rf web/api/app/Plugin/Crud
|
rm -rf web/api/app/Plugin/Crud
|
||||||
mkdir web/api/app/Plugin/Crud
|
mkdir web/api/app/Plugin/Crud
|
||||||
|
|
||||||
if [ "${OS}" == "el" ]; then
|
# We use zmrepo to build el6 only. All other redhat distros use rpm fusion
|
||||||
zmrepodistro=${OS}
|
if [ "${OS}" == "el" ] && [ "${DIST}" == "6" ]; then
|
||||||
|
baseurl="https://zmrepo.zoneminder.com/el/${DIST}/x86_64/"
|
||||||
|
reporpm="zmrepo"
|
||||||
|
# Let repoquery determine the full url and filename to the latest zmrepo package
|
||||||
|
dlurl=`repoquery --archlist=noarch --repofrompath=zmpackpack,${baseurl} --repoid=zmpackpack --qf="%{location}" ${reporpm} 2> /dev/null`
|
||||||
else
|
else
|
||||||
zmrepodistro="f"
|
reporpm="rpmfusion-free-release"
|
||||||
|
dlurl="https://download1.rpmfusion.org/free/${OS}/${reporpm}-${DIST}.noarch.rpm"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Let repoquery determine the full url and filename of the zmrepo rpm we are interested in
|
# Give our downloaded repo rpm a common name so redhat_package.mk can find it
|
||||||
result=`repoquery --repofrompath=zmpackpack,https://zmrepo.zoneminder.com/${zmrepodistro}/"${DIST}"/x86_64/ --repoid=zmpackpack --qf="%{location}" zmrepo 2> /dev/null`
|
if [ -n "$dlurl" ] && [ $? -eq 0 ]; then
|
||||||
|
echo "Retrieving ${reporpm} repo rpm..."gd
|
||||||
if [ -n "$result" ] && [ $? -eq 0 ]; then
|
curl $dlurl > build/external-repo.noarch.rpm
|
||||||
echo "Retrieving ZMREPO rpm..."
|
|
||||||
curl $result > build/zmrepo.noarch.rpm
|
|
||||||
else
|
else
|
||||||
echo "ERROR: Failed to retrieve zmrepo rpm..."
|
echo "ERROR: Failed to retrieve ${reporpm} repo rpm..."
|
||||||
|
echo "Download url was: $dlurl"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
setrpmchangelog
|
setrpmchangelog
|
||||||
|
|
||||||
echo "Starting packpack..."
|
echo "Starting packpack..."
|
||||||
utils/packpack/heartbeat.sh &
|
execpackpack
|
||||||
mypid=$!
|
|
||||||
packpack/packpack -f utils/packpack/redhat_package.mk redhat_package > buildlog.txt 2>&1
|
|
||||||
kill $mypid
|
|
||||||
tail -n 3000 buildlog.txt | grep -v ONVIF
|
|
||||||
|
|
||||||
# Steps common to Debian based distros
|
# Steps common to Debian based distros
|
||||||
elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; then
|
elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; then
|
||||||
|
@ -279,11 +312,7 @@ if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${TRAVIS}" != "true" ]; then
|
||||||
setdebchangelog
|
setdebchangelog
|
||||||
|
|
||||||
echo "Starting packpack..."
|
echo "Starting packpack..."
|
||||||
utils/packpack/heartbeat.sh &
|
execpackpack
|
||||||
mypid=$!
|
|
||||||
packpack/packpack > buildlog.txt 2>&1
|
|
||||||
kill $mypid
|
|
||||||
tail -n 3000 buildlog.txt | grep -v ONVIF
|
|
||||||
|
|
||||||
if [ "${OS}" == "ubuntu" ] && [ "${DIST}" == "trusty" ] && [ "${ARCH}" == "x86_64" ] && [ "${TRAVIS}" == "true" ]; then
|
if [ "${OS}" == "ubuntu" ] && [ "${DIST}" == "trusty" ] && [ "${ARCH}" == "x86_64" ] && [ "${TRAVIS}" == "true" ]; then
|
||||||
installtrusty
|
installtrusty
|
||||||
|
@ -303,11 +332,7 @@ elif [ "${OS}" == "ubuntu" ] && [ "${DIST}" == "trusty" ] && [ "${ARCH}" == "x86
|
||||||
setdebchangelog
|
setdebchangelog
|
||||||
|
|
||||||
echo "Starting packpack..."
|
echo "Starting packpack..."
|
||||||
utils/packpack/heartbeat.sh &
|
execpackpack
|
||||||
mypid=$!
|
|
||||||
packpack/packpack > buildlog.txt 2>&1
|
|
||||||
kill $mypid
|
|
||||||
tail -n 3000 buildlog.txt | grep -v ONVIF
|
|
||||||
|
|
||||||
# If we are running inside Travis then attempt to install the deb we just built
|
# If we are running inside Travis then attempt to install the deb we just built
|
||||||
if [ "${TRAVIS}" == "true" ]; then
|
if [ "${TRAVIS}" == "true" ]; then
|
||||||
|
|
|
@ -139,14 +139,14 @@ if ( is_dir($configSubFolder) ) {
|
||||||
# pair in the array as a constant
|
# pair in the array as a constant
|
||||||
foreach( $configvals as $key => $value) {
|
foreach( $configvals as $key => $value) {
|
||||||
define( $key, $value );
|
define( $key, $value );
|
||||||
Configure::write( $matches[1], $matches[2] );
|
Configure::write( $key, $value );
|
||||||
}
|
}
|
||||||
|
|
||||||
function process_configfile($configFile) {
|
function process_configfile($configFile) {
|
||||||
if ( is_readable( $configFile ) ) {
|
if ( is_readable( $configFile ) ) {
|
||||||
$configvals = array();
|
$configvals = array();
|
||||||
|
|
||||||
$cfg = fopen( $configFile, "r") or die("Could not open config file.");
|
$cfg = fopen( $configFile, 'r') or die('Could not open config file.');
|
||||||
while ( !feof($cfg) ) {
|
while ( !feof($cfg) ) {
|
||||||
$str = fgets( $cfg, 256 );
|
$str = fgets( $cfg, 256 );
|
||||||
if ( preg_match( '/^\s*$/', $str ))
|
if ( preg_match( '/^\s*$/', $str ))
|
||||||
|
|
|
@ -70,6 +70,9 @@ class DATABASE_CONFIG {
|
||||||
'login' => ZM_DB_USER,
|
'login' => ZM_DB_USER,
|
||||||
'password' => ZM_DB_PASS,
|
'password' => ZM_DB_PASS,
|
||||||
'database' => ZM_DB_NAME,
|
'database' => ZM_DB_NAME,
|
||||||
|
'ssl_ca' => ZM_DB_SSL_CA_CERT,
|
||||||
|
'ssl_key' => ZM_DB_SSL_CLIENT_KEY,
|
||||||
|
'ssl_cert' => ZM_DB_SSL_CLIENT_CERT,
|
||||||
'prefix' => '',
|
'prefix' => '',
|
||||||
'encoding' => 'utf8',
|
'encoding' => 'utf8',
|
||||||
);
|
);
|
||||||
|
|
|
@ -131,7 +131,7 @@ class Event {
|
||||||
} # end Event->delete
|
} # end Event->delete
|
||||||
|
|
||||||
public function getStreamSrc( $args=array(), $querySep='&' ) {
|
public function getStreamSrc( $args=array(), $querySep='&' ) {
|
||||||
if ( $this->{'DefaultVideo'} ) {
|
if ( $this->{'DefaultVideo'} and $args['mode'] != 'jpeg' ) {
|
||||||
return ( ZM_BASE_PATH != '/' ? ZM_BASE_PATH : '' ).'/index.php?view=view_video&eid='.$this->{'Id'};
|
return ( ZM_BASE_PATH != '/' ? ZM_BASE_PATH : '' ).'/index.php?view=view_video&eid='.$this->{'Id'};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,6 +217,8 @@ class Event {
|
||||||
$captImage = 'snapshot.jpg';
|
$captImage = 'snapshot.jpg';
|
||||||
Debug("Frame not specified, using snapshot");
|
Debug("Frame not specified, using snapshot");
|
||||||
} else {
|
} else {
|
||||||
|
$captImage = sprintf( '%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyze.jpg', $frame['FrameId'] );
|
||||||
|
if ( ! file_exists( $eventPath.'/'.$captImage ) ) {
|
||||||
$captImage = sprintf( '%0'.ZM_EVENT_IMAGE_DIGITS.'d-capture.jpg', $frame['FrameId'] );
|
$captImage = sprintf( '%0'.ZM_EVENT_IMAGE_DIGITS.'d-capture.jpg', $frame['FrameId'] );
|
||||||
if ( ! file_exists( $eventPath.'/'.$captImage ) ) {
|
if ( ! file_exists( $eventPath.'/'.$captImage ) ) {
|
||||||
# Generate the frame JPG
|
# Generate the frame JPG
|
||||||
|
@ -238,7 +240,8 @@ class Event {
|
||||||
} else {
|
} else {
|
||||||
Error("Can't create frame images from video becuase there is no video file for this event (".$Event->DefaultVideo() );
|
Error("Can't create frame images from video becuase there is no video file for this event (".$Event->DefaultVideo() );
|
||||||
}
|
}
|
||||||
}
|
} // end if capture file exists
|
||||||
|
} // end if analyze file exists
|
||||||
}
|
}
|
||||||
|
|
||||||
$captPath = $eventPath.'/'.$captImage;
|
$captPath = $eventPath.'/'.$captImage;
|
||||||
|
|
|
@ -435,11 +435,11 @@ Warning("Addterm");
|
||||||
# If we change anything that changes the shared mem size, zma can complain. So let's stop first.
|
# If we change anything that changes the shared mem size, zma can complain. So let's stop first.
|
||||||
zmaControl( $monitor, 'stop' );
|
zmaControl( $monitor, 'stop' );
|
||||||
zmcControl( $monitor, 'stop' );
|
zmcControl( $monitor, 'stop' );
|
||||||
dbQuery( 'UPDATE Monitors SET '.implode( ", ", $changes ).' WHERE Id =?', array($mid) );
|
dbQuery( 'UPDATE Monitors SET '.implode( ', ', $changes ).' WHERE Id=?', array($mid) );
|
||||||
if ( isset($changes['Name']) ) {
|
if ( isset($changes['Name']) ) {
|
||||||
$saferOldName = basename( $monitor['Name'] );
|
$saferOldName = basename( $monitor['Name'] );
|
||||||
$saferNewName = basename( $_REQUEST['newMonitor']['Name'] );
|
$saferNewName = basename( $_REQUEST['newMonitor']['Name'] );
|
||||||
rename( ZM_DIR_EVENTS."/".$saferOldName, ZM_DIR_EVENTS."/".$saferNewName);
|
rename( ZM_DIR_EVENTS.'/'.$saferOldName, ZM_DIR_EVENTS.'/'.$saferNewName);
|
||||||
}
|
}
|
||||||
if ( isset($changes['Width']) || isset($changes['Height']) ) {
|
if ( isset($changes['Width']) || isset($changes['Height']) ) {
|
||||||
$newW = $_REQUEST['newMonitor']['Width'];
|
$newW = $_REQUEST['newMonitor']['Width'];
|
||||||
|
@ -493,7 +493,7 @@ Warning("Addterm");
|
||||||
Error("Users with Monitors restrictions cannot create new monitors.");
|
Error("Users with Monitors restrictions cannot create new monitors.");
|
||||||
}
|
}
|
||||||
$restart = true;
|
$restart = true;
|
||||||
}
|
} # end if count(changes)
|
||||||
|
|
||||||
if ( ZM_OPT_X10 ) {
|
if ( ZM_OPT_X10 ) {
|
||||||
$x10Changes = getFormChanges( $x10Monitor, $_REQUEST['newX10Monitor'] );
|
$x10Changes = getFormChanges( $x10Monitor, $_REQUEST['newX10Monitor'] );
|
||||||
|
@ -521,7 +521,7 @@ Warning("Addterm");
|
||||||
zmcControl( $new_monitor, 'start' );
|
zmcControl( $new_monitor, 'start' );
|
||||||
zmaControl( $new_monitor, 'start' );
|
zmaControl( $new_monitor, 'start' );
|
||||||
|
|
||||||
if ( $monitor['Controllable'] ) {
|
if ( $new_monitor['Controllable'] ) {
|
||||||
require_once( 'control_functions.php' );
|
require_once( 'control_functions.php' );
|
||||||
sendControlCommand( $mid, 'quit' );
|
sendControlCommand( $mid, 'quit' );
|
||||||
}
|
}
|
||||||
|
@ -530,8 +530,7 @@ Warning("Addterm");
|
||||||
$refreshParent = true;
|
$refreshParent = true;
|
||||||
} // end if restart
|
} // end if restart
|
||||||
$view = 'none';
|
$view = 'none';
|
||||||
}
|
} elseif ( $action == 'delete' ) {
|
||||||
if ( $action == 'delete' ) {
|
|
||||||
if ( isset($_REQUEST['markMids']) && !$user['MonitorIds'] ) {
|
if ( isset($_REQUEST['markMids']) && !$user['MonitorIds'] ) {
|
||||||
foreach( $_REQUEST['markMids'] as $markMid ) {
|
foreach( $_REQUEST['markMids'] as $markMid ) {
|
||||||
if ( canEdit( 'Monitors', $markMid ) ) {
|
if ( canEdit( 'Monitors', $markMid ) ) {
|
||||||
|
@ -541,6 +540,18 @@ Warning("Addterm");
|
||||||
zmcControl( $monitor, 'stop' );
|
zmcControl( $monitor, 'stop' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 ) {
|
||||||
|
$markEids = dbFetchAll( 'SELECT Id FROM Events WHERE MonitorId=?', 'Id', array($markMid) );
|
||||||
|
foreach( $markEids as $markEid )
|
||||||
|
deleteEvent( $markEid );
|
||||||
|
|
||||||
|
deletePath( ZM_DIR_EVENTS.'/'.basename($monitor['Name']) );
|
||||||
|
deletePath( ZM_DIR_EVENTS.'/'.$monitor['Id'] ); // I'm trusting the Id.
|
||||||
|
} // end if ZM_OPT_FAST_DELETE
|
||||||
|
|
||||||
// This is the important stuff
|
// This is the important stuff
|
||||||
dbQuery( 'DELETE FROM Monitors WHERE Id = ?', array($markMid) );
|
dbQuery( 'DELETE FROM Monitors WHERE Id = ?', array($markMid) );
|
||||||
dbQuery( 'DELETE FROM Zones WHERE MonitorId = ?', array($markMid) );
|
dbQuery( 'DELETE FROM Zones WHERE MonitorId = ?', array($markMid) );
|
||||||
|
@ -549,18 +560,6 @@ Warning("Addterm");
|
||||||
|
|
||||||
fixSequences();
|
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 ) {
|
|
||||||
// Slight hack, we maybe should load *, but we happen to know that the deleteEvent function uses Id and StartTime.
|
|
||||||
$markEids = dbFetchAll( 'SELECT Id,StartTime FROM Events WHERE MonitorId=?', NULL, array($markMid) );
|
|
||||||
foreach( $markEids as $markEid )
|
|
||||||
deleteEvent( $markEid, $markMid );
|
|
||||||
|
|
||||||
deletePath( ZM_DIR_EVENTS.'/'.basename($monitor['Name']) );
|
|
||||||
deletePath( ZM_DIR_EVENTS.'/'.$monitor['Id'] ); // I'm trusting the Id.
|
|
||||||
} // end if ZM_OPT_FAST_DELETE
|
|
||||||
} // end if found the monitor in the db
|
} // end if found the monitor in the db
|
||||||
} // end if canedit this monitor
|
} // end if canedit this monitor
|
||||||
} // end foreach monitor in MarkMid
|
} // end foreach monitor in MarkMid
|
||||||
|
|
|
@ -29,8 +29,7 @@ define( "ZM_DIR_TEMP", "@ZM_TMPDIR@" );
|
||||||
|
|
||||||
$configFile = ZM_CONFIG;
|
$configFile = ZM_CONFIG;
|
||||||
$localConfigFile = basename($configFile);
|
$localConfigFile = basename($configFile);
|
||||||
if ( file_exists( $localConfigFile ) && filesize( $localConfigFile ) > 0 )
|
if ( file_exists( $localConfigFile ) && filesize( $localConfigFile ) > 0 ) {
|
||||||
{
|
|
||||||
if ( php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']) )
|
if ( php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']) )
|
||||||
print( "Warning, overriding installed $localConfigFile file with local copy\n" );
|
print( "Warning, overriding installed $localConfigFile file with local copy\n" );
|
||||||
else
|
else
|
||||||
|
@ -150,8 +149,7 @@ $GLOBALS['defaultUser'] = array(
|
||||||
"MonitorIds" => false
|
"MonitorIds" => false
|
||||||
);
|
);
|
||||||
|
|
||||||
function loadConfig( $defineConsts=true )
|
function loadConfig( $defineConsts=true ) {
|
||||||
{
|
|
||||||
global $config;
|
global $config;
|
||||||
global $configCats;
|
global $configCats;
|
||||||
global $dbConn;
|
global $dbConn;
|
||||||
|
@ -163,13 +161,11 @@ function loadConfig( $defineConsts=true )
|
||||||
if ( !$result )
|
if ( !$result )
|
||||||
echo mysql_error();
|
echo mysql_error();
|
||||||
$monitors = array();
|
$monitors = array();
|
||||||
while( $row = dbFetchNext( $result ) )
|
while( $row = dbFetchNext( $result ) ) {
|
||||||
{
|
|
||||||
if ( $defineConsts )
|
if ( $defineConsts )
|
||||||
define( $row['Name'], $row['Value'] );
|
define( $row['Name'], $row['Value'] );
|
||||||
$config[$row['Name']] = $row;
|
$config[$row['Name']] = $row;
|
||||||
if ( !($configCat = &$configCats[$row['Category']]) )
|
if ( !($configCat = &$configCats[$row['Category']]) ) {
|
||||||
{
|
|
||||||
$configCats[$row['Category']] = array();
|
$configCats[$row['Category']] = array();
|
||||||
$configCat = &$configCats[$row['Category']];
|
$configCat = &$configCats[$row['Category']];
|
||||||
}
|
}
|
||||||
|
@ -203,9 +199,8 @@ function process_configfile($configFile) {
|
||||||
if ( is_readable( $configFile ) ) {
|
if ( is_readable( $configFile ) ) {
|
||||||
$configvals = array();
|
$configvals = array();
|
||||||
|
|
||||||
$cfg = fopen( $configFile, "r") or die("Could not open config file.");
|
$cfg = fopen( $configFile, 'r') or Error("Could not open config file: $configFile.");
|
||||||
while ( !feof($cfg) )
|
while ( !feof($cfg) ) {
|
||||||
{
|
|
||||||
$str = fgets( $cfg, 256 );
|
$str = fgets( $cfg, 256 );
|
||||||
if ( preg_match( '/^\s*$/', $str ))
|
if ( preg_match( '/^\s*$/', $str ))
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
//
|
//
|
||||||
|
|
||||||
define( "DB_LOG_OFF", 0 );
|
define( 'DB_LOG_OFF', 0 );
|
||||||
define( "DB_LOG_ONLY", 1 );
|
define( 'DB_LOG_ONLY', 1 );
|
||||||
define( "DB_LOG_DEBUG", 2 );
|
define( 'DB_LOG_DEBUG', 2 );
|
||||||
|
|
||||||
$GLOBALS['dbLogLevel'] = DB_LOG_OFF;
|
$GLOBALS['dbLogLevel'] = DB_LOG_OFF;
|
||||||
|
|
||||||
|
@ -42,11 +42,23 @@ function dbConnect() {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$dbOptions = null;
|
||||||
|
if ( defined( 'ZM_DB_SSL_CA_CERT' ) and ZM_DB_SSL_CA_CERT ) {
|
||||||
|
$dbOptions = array(
|
||||||
|
PDO::MYSQL_ATTR_SSL_CA => ZM_DB_SSL_CA_CERT,
|
||||||
|
PDO::MYSQL_ATTR_SSL_KEY => ZM_DB_SSL_CLIENT_KEY,
|
||||||
|
PDO::MYSQL_ATTR_SSL_CERT => ZM_DB_SSL_CLIENT_CERT,
|
||||||
|
);
|
||||||
|
$dbConn = new PDO( ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS, $dbOptions );
|
||||||
|
} else {
|
||||||
$dbConn = new PDO( ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS );
|
$dbConn = new PDO( ZM_DB_TYPE . $socket . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS );
|
||||||
|
}
|
||||||
|
|
||||||
$dbConn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
|
$dbConn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
|
||||||
$dbConn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
$dbConn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
} catch(PDOException $ex ) {
|
} catch(PDOException $ex ) {
|
||||||
echo "Unable to connect to ZM db." . $ex->getMessage();
|
echo 'Unable to connect to ZM db.' . $ex->getMessage();
|
||||||
|
error_log('Unable to connect to ZM DB ' . $ex->getMessage() );
|
||||||
$dbConn = null;
|
$dbConn = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,7 +103,7 @@ function dbError( $sql ) {
|
||||||
|
|
||||||
function dbEscape( $string ) {
|
function dbEscape( $string ) {
|
||||||
global $dbConn;
|
global $dbConn;
|
||||||
if ( version_compare( phpversion(), "4.3.0", "<") )
|
if ( version_compare( phpversion(), '4.3.0', '<') )
|
||||||
if ( get_magic_quotes_gpc() )
|
if ( get_magic_quotes_gpc() )
|
||||||
return( $dbConn->quote( stripslashes( $string ) ) );
|
return( $dbConn->quote( stripslashes( $string ) ) );
|
||||||
else
|
else
|
||||||
|
@ -217,7 +229,7 @@ function getTableColumns( $table, $asString=1 ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTableAutoInc( $table ) {
|
function getTableAutoInc( $table ) {
|
||||||
$row = dbFetchOne( "show table status where Name=?", NULL, array($table) );
|
$row = dbFetchOne( 'show table status where Name=?', NULL, array($table) );
|
||||||
return( $row['Auto_increment'] );
|
return( $row['Auto_increment'] );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,11 +341,11 @@ function getTableDescription( $table, $asString=1 ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function dbFetchMonitor( $mid ) {
|
function dbFetchMonitor( $mid ) {
|
||||||
return( dbFetchOne( "select * from Monitors where Id = ?", NULL, array($mid) ) );
|
return( dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($mid) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
function dbFetchGroup( $gid ) {
|
function dbFetchGroup( $gid ) {
|
||||||
return( dbFetchOne( "select * from Groups where Id = ?", NULL, array($gid) ) );
|
return( dbFetchOne( 'select * from Groups where Id = ?', NULL, array($gid) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -194,7 +194,7 @@ isset($view) || $view = NULL;
|
||||||
isset($request) || $request = NULL;
|
isset($request) || $request = NULL;
|
||||||
isset($action) || $action = NULL;
|
isset($action) || $action = NULL;
|
||||||
|
|
||||||
if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' && $request != 'control' ) {
|
if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' && $view != 'video' && $request != 'control' ) {
|
||||||
require_once( 'includes/csrf/csrf-magic.php' );
|
require_once( 'includes/csrf/csrf-magic.php' );
|
||||||
Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
|
Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
|
||||||
csrf_check();
|
csrf_check();
|
||||||
|
|
|
@ -52,15 +52,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#imageFrame div {
|
#imageFrame div {
|
||||||
background-image: url(../../../graphics/point-g.gif);
|
background-image: url(../../../graphics/point-g.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
#imageFrame div.highlight {
|
#imageFrame div.highlight {
|
||||||
background-image: url(../../../graphics/point-o.gif);
|
background-image: url(../../../graphics/point-o.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
#imageFrame div.active {
|
#imageFrame div.active {
|
||||||
background-image: url(../../../graphics/point-r.gif);
|
background-image: url(../../../graphics/point-r.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
#zonePoints {
|
#zonePoints {
|
||||||
|
|
|
@ -52,15 +52,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#imageFrame div {
|
#imageFrame div {
|
||||||
background-image: url(../../../graphics/point-g.gif);
|
background-image: url(../../../graphics/point-g.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
#imageFrame div.highlight {
|
#imageFrame div.highlight {
|
||||||
background-image: url(../../../graphics/point-o.gif);
|
background-image: url(../../../graphics/point-o.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
#imageFrame div.active {
|
#imageFrame div.active {
|
||||||
background-image: url(../../../graphics/point-r.gif);
|
background-image: url(../../../graphics/point-r.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
#zonePoints {
|
#zonePoints {
|
||||||
|
|
|
@ -52,15 +52,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#imageFrame div {
|
#imageFrame div {
|
||||||
background-image: url(../../../graphics/point-g.gif);
|
background-image: url(../../../graphics/point-g.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
#imageFrame div.highlight {
|
#imageFrame div.highlight {
|
||||||
background-image: url(../../../graphics/point-o.gif);
|
background-image: url(../../../graphics/point-o.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
#imageFrame div.active {
|
#imageFrame div.active {
|
||||||
background-image: url(../../../graphics/point-r.gif);
|
background-image: url(../../../graphics/point-r.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
#zonePoints {
|
#zonePoints {
|
||||||
|
|
|
@ -51,7 +51,7 @@ $eventCounts = array(
|
||||||
'filter' => array(
|
'filter' => array(
|
||||||
'Query' => array(
|
'Query' => array(
|
||||||
'terms' => array(
|
'terms' => array(
|
||||||
array( 'attr' => "DateTime", 'op' => '>=', 'val' => '-1 day' ),
|
array( 'attr' => 'DateTime', 'op' => '>=', 'val' => '-1 day' ),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
@ -62,7 +62,7 @@ $eventCounts = array(
|
||||||
'filter' => array(
|
'filter' => array(
|
||||||
'Query' => array(
|
'Query' => array(
|
||||||
'terms' => array(
|
'terms' => array(
|
||||||
array( 'attr' => "DateTime", 'op' => '>=', 'val' => '-7 day' ),
|
array( 'attr' => 'DateTime', 'op' => '>=', 'val' => '-7 day' ),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
@ -73,7 +73,7 @@ $eventCounts = array(
|
||||||
'filter' => array(
|
'filter' => array(
|
||||||
'Query' => array(
|
'Query' => array(
|
||||||
'terms' => array(
|
'terms' => array(
|
||||||
array( 'attr' => "DateTime", 'op' => '>=', 'val' => '-1 month' ),
|
array( 'attr' => 'DateTime', 'op' => '>=', 'val' => '-1 month' ),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
@ -84,7 +84,7 @@ $eventCounts = array(
|
||||||
'filter' => array(
|
'filter' => array(
|
||||||
'Query' => array(
|
'Query' => array(
|
||||||
'terms' => array(
|
'terms' => array(
|
||||||
array( 'attr' => "Archived", 'op' => '=', 'val' => '1' ),
|
array( 'attr' => 'Archived', 'op' => '=', 'val' => '1' ),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
@ -107,7 +107,7 @@ for( $i = 0; $i < count($displayMonitors); $i += 1 ) {
|
||||||
for ( $j = 0; $j < count($eventCounts); $j += 1 ) {
|
for ( $j = 0; $j < count($eventCounts); $j += 1 ) {
|
||||||
$filter = addFilterTerm( $eventCounts[$j]['filter'], count($eventCounts[$j]['filter']['Query']['terms']), array( 'cnj' => 'and', 'attr' => 'MonitorId', 'op' => '=', 'val' => $monitor['Id'] ) );
|
$filter = addFilterTerm( $eventCounts[$j]['filter'], count($eventCounts[$j]['filter']['Query']['terms']), array( 'cnj' => 'and', 'attr' => 'MonitorId', 'op' => '=', 'val' => $monitor['Id'] ) );
|
||||||
parseFilter( $filter );
|
parseFilter( $filter );
|
||||||
$counts[] = "count(if(1".$filter['sql'].",1,NULL)) as EventCount$j";
|
$counts[] = 'count(if(1'.$filter['sql'].",1,NULL)) as EventCount$j";
|
||||||
$monitor['eventCounts'][$j]['filter'] = $filter;
|
$monitor['eventCounts'][$j]['filter'] = $filter;
|
||||||
}
|
}
|
||||||
$sql = 'select '.join($counts,', ').' from Events as E where MonitorId = ?';
|
$sql = 'select '.join($counts,', ').' from Events as E where MonitorId = ?';
|
||||||
|
@ -203,45 +203,27 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
|
||||||
<td class="colServer"><?php $Server = new Server( $monitor['ServerId'] ); echo $Server->Name(); ?></td>
|
<td class="colServer"><?php $Server = new Server( $monitor['ServerId'] ); echo $Server->Name(); ?></td>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
$source = '';
|
||||||
<?php
|
|
||||||
if ( $monitor['Type'] == 'Local' ) {
|
if ( $monitor['Type'] == 'Local' ) {
|
||||||
?>
|
$source = $monitor['Device'].' ('.$monitor['Channel'].')';
|
||||||
<td class="colSource"><?php echo makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.$monitor['Device'].' ('.$monitor['Channel'].')</span>', canEdit( 'Monitors' ) ) ?></td>
|
|
||||||
<?php
|
|
||||||
} elseif ( $monitor['Type'] == 'Remote' ) {
|
} elseif ( $monitor['Type'] == 'Remote' ) {
|
||||||
?>
|
$source = preg_replace( '/^.*@/', '', $monitor['Host'] );
|
||||||
<td class="colSource"><?php echo makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.preg_replace( '/^.*@/', '', $monitor['Host'] ).'</span>', canEdit( 'Monitors' ) ) ?></td>
|
} elseif ( $monitor['Type'] == 'File' || $monitor['Type'] == 'cURL' ) {
|
||||||
<?php
|
$source = preg_replace( '/^.*\//', '', $monitor['Path'] );
|
||||||
} elseif ( $monitor['Type'] == 'File' ) {
|
|
||||||
?>
|
|
||||||
<td class="colSource"><?php echo makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.preg_replace( '/^.*\//', '', $monitor['Path'] ).'</span>', canEdit( 'Monitors' ) ) ?></td>
|
|
||||||
<?php
|
|
||||||
} elseif ( $monitor['Type'] == 'Ffmpeg' || $monitor['Type'] == 'Libvlc' ) {
|
} elseif ( $monitor['Type'] == 'Ffmpeg' || $monitor['Type'] == 'Libvlc' ) {
|
||||||
$domain = parse_url( $monitor['Path'], PHP_URL_HOST );
|
$domain = parse_url( $monitor['Path'], PHP_URL_HOST );
|
||||||
$shortpath = $domain ? $domain : preg_replace( '/^.*\//', '', $monitor['Path'] );
|
$source = $domain ? $domain : preg_replace( '/^.*\//', '', $monitor['Path'] );
|
||||||
if ( $shortpath == '' ) {
|
|
||||||
$shortpath = 'Monitor ' . $monitor['Id'];
|
|
||||||
}
|
}
|
||||||
?>
|
if ( $source == '' ) {
|
||||||
<td class="colSource"><?php echo makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.$shortpath.'</span>', canEdit( 'Monitors' ) ) ?></td>
|
$source = 'Monitor ' . $monitor['Id'];
|
||||||
<?php
|
|
||||||
} elseif ( $monitor['Type'] == 'cURL' ) {
|
|
||||||
?>
|
|
||||||
<td class="colSource"><?php echo makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.preg_replace( '/^.*\//', '', $monitor['Path'] ).'</span>', canEdit( 'Monitors' ) ) ?></td>
|
|
||||||
<?php
|
|
||||||
} else {
|
|
||||||
?>
|
|
||||||
<td class="colSource"> </td>
|
|
||||||
<?php
|
|
||||||
}
|
}
|
||||||
?>
|
echo '<td class="colSource">'. makePopupLink( '?view=monitor&mid='.$monitor['Id'], 'zmMonitor'.$monitor['Id'], 'monitor', '<span class="'.$dclass.'">'.$source.'</span>', canEdit( 'Monitors' ) ).'</td>';
|
||||||
<?php
|
|
||||||
if ( $show_storage_areas ) {
|
if ( $show_storage_areas ) {
|
||||||
?>
|
?>
|
||||||
<td class="colStorage"><?php $Storage = new Storage( $monitor['StorageId'] ); echo $Storage->Name(); ?></td>
|
<td class="colStorage"><?php $Storage = new Storage( $monitor['StorageId'] ); echo $Storage->Name(); ?></td>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( $i = 0; $i < count($eventCounts); $i++ ) {
|
for ( $i = 0; $i < count($eventCounts); $i++ ) {
|
||||||
?>
|
?>
|
||||||
<td class="colEvents"><?php echo makePopupLink( '?view='.$eventsView.'&page=1'.$monitor['eventCounts'][$i]['filter']['query'], $eventsWindow, $eventsView, $monitor['EventCount'.$i], canView( 'Events' ) ) ?></td>
|
<td class="colEvents"><?php echo makePopupLink( '?view='.$eventsView.'&page=1'.$monitor['eventCounts'][$i]['filter']['query'], $eventsWindow, $eventsView, $monitor['EventCount'.$i], canView( 'Events' ) ) ?></td>
|
||||||
|
|
|
@ -193,7 +193,9 @@ foreach ( $events as $event ) {
|
||||||
<tr>
|
<tr>
|
||||||
<td class="colId"><?php echo makePopupLink( '?view=event&eid='.$event->Id().$filterQuery.$sortQuery.'&page=1', 'zmEvent', array( 'event', reScale( $event->Width(), $scale ), reScale( $event->Height(), $scale ) ), $event->Id().($event->Archived()?'*':'') ) ?></td>
|
<td class="colId"><?php echo makePopupLink( '?view=event&eid='.$event->Id().$filterQuery.$sortQuery.'&page=1', 'zmEvent', array( 'event', reScale( $event->Width(), $scale ), reScale( $event->Height(), $scale ) ), $event->Id().($event->Archived()?'*':'') ) ?></td>
|
||||||
<td class="colName"><?php echo makePopupLink( '?view=event&eid='.$event->Id().$filterQuery.$sortQuery.'&page=1', 'zmEvent', array( 'event', reScale( $event->Width(), $event->DefaultScale(), ZM_WEB_DEFAULT_SCALE ), reScale( $event->Height(), $event->DefaultScale(), ZM_WEB_DEFAULT_SCALE ) ), validHtmlStr($event->Name()).($event->Archived()?'*':'' ) ) ?></td>
|
<td class="colName"><?php echo makePopupLink( '?view=event&eid='.$event->Id().$filterQuery.$sortQuery.'&page=1', 'zmEvent', array( 'event', reScale( $event->Width(), $event->DefaultScale(), ZM_WEB_DEFAULT_SCALE ), reScale( $event->Height(), $event->DefaultScale(), ZM_WEB_DEFAULT_SCALE ) ), validHtmlStr($event->Name()).($event->Archived()?'*':'' ) ) ?></td>
|
||||||
<td class="colMonitorName"><?php echo $event->MonitorName() ?></td>
|
<td class="colMonitorName"><?php echo
|
||||||
|
makePopupLink( '?view=monitor&mid='.$event->MonitorId(), 'zmMonitor'.$event->Monitorid(), 'monitor', $event->MonitorName(), canEdit( 'Monitors' ) )
|
||||||
|
?></td>
|
||||||
<td class="colCause"><?php echo makePopupLink( '?view=eventdetail&eid='.$event->Id(), 'zmEventDetail', 'eventdetail', validHtmlStr($event->Cause()), canEdit( 'Events' ), 'title="'.htmlspecialchars($event->Notes()).'"' ) ?></td>
|
<td class="colCause"><?php echo makePopupLink( '?view=eventdetail&eid='.$event->Id(), 'zmEventDetail', 'eventdetail', validHtmlStr($event->Cause()), canEdit( 'Events' ), 'title="'.htmlspecialchars($event->Notes()).'"' ) ?></td>
|
||||||
<td class="colTime"><?php echo strftime( STRF_FMT_DATETIME_SHORTER, strtotime($event->StartTime()) ) ?></td>
|
<td class="colTime"><?php echo strftime( STRF_FMT_DATETIME_SHORTER, strtotime($event->StartTime()) ) ?></td>
|
||||||
<td class="colDuration"><?php echo gmdate("H:i:s", $event->Length() ) ?></td>
|
<td class="colDuration"><?php echo gmdate("H:i:s", $event->Length() ) ?></td>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program; if not, write to the Free Software
|
// along with this program; if not, write to the Free Software
|
||||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
//
|
//
|
||||||
|
|
||||||
if ( !canView( 'Events' ) )
|
if ( !canView( 'Events' ) )
|
||||||
|
|
|
@ -88,7 +88,7 @@ $j( function() {
|
||||||
function applySort(event, ui) {
|
function applySort(event, ui) {
|
||||||
var monitor_ids = $j(this).sortable('toArray');
|
var monitor_ids = $j(this).sortable('toArray');
|
||||||
var ajax = new Request.JSON( {
|
var ajax = new Request.JSON( {
|
||||||
url: '/index.php?request=console',
|
url: '?request=console',
|
||||||
data: { monitor_ids: monitor_ids, action: 'sort' },
|
data: { monitor_ids: monitor_ids, action: 'sort' },
|
||||||
method: 'post',
|
method: 'post',
|
||||||
timeout: AJAX_TIMEOUT
|
timeout: AJAX_TIMEOUT
|
||||||
|
|
|
@ -31,7 +31,7 @@ function changeScale() {
|
||||||
Cookie.write( 'zmWatchScale'+monitorId, scale, { duration: 10*365 } );
|
Cookie.write( 'zmWatchScale'+monitorId, scale, { duration: 10*365 } );
|
||||||
|
|
||||||
/*Stream could be an applet so can't use moo tools*/
|
/*Stream could be an applet so can't use moo tools*/
|
||||||
var streamImg = $('#liveStream'+monitorId);
|
var streamImg = $('liveStream'+monitorId);
|
||||||
if ( streamImg ) {
|
if ( streamImg ) {
|
||||||
streamImg.style.width = newWidth + "px";
|
streamImg.style.width = newWidth + "px";
|
||||||
streamImg.style.height = newHeight + "px";
|
streamImg.style.height = newHeight + "px";
|
||||||
|
@ -197,7 +197,7 @@ function getStreamCmdResponse( respObj, respText ) {
|
||||||
var streamImg = $('liveStream'+monitorId);
|
var streamImg = $('liveStream'+monitorId);
|
||||||
if ( streamImg ) {
|
if ( streamImg ) {
|
||||||
streamImg.src = streamImg.src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) ));
|
streamImg.src = streamImg.src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) ));
|
||||||
console.log("Changing lviestream src to " + streamImg.src);
|
console.log("Changing livestream src to " + streamImg.src);
|
||||||
} else {
|
} else {
|
||||||
console.log("Unable to find streamImg liveStream");
|
console.log("Unable to find streamImg liveStream");
|
||||||
}
|
}
|
||||||
|
@ -357,7 +357,14 @@ function statusCmdQuery() {
|
||||||
var alarmCmdParms = "view=request&request=alarm&id="+monitorId;
|
var alarmCmdParms = "view=request&request=alarm&id="+monitorId;
|
||||||
if ( auth_hash )
|
if ( auth_hash )
|
||||||
alarmCmdParms += '&auth='+auth_hash;
|
alarmCmdParms += '&auth='+auth_hash;
|
||||||
var alarmCmdReq = new Request.JSON( { url: monitorUrl+thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getAlarmCmdResponse, onTimeout: streamCmdQuery } );
|
var alarmCmdReq = new Request.JSON( {
|
||||||
|
url: monitorUrl+thisUrl,
|
||||||
|
method: 'post',
|
||||||
|
timeout: AJAX_TIMEOUT,
|
||||||
|
link: 'cancel',
|
||||||
|
onSuccess: getAlarmCmdResponse,
|
||||||
|
onTimeout: streamCmdQuery
|
||||||
|
} );
|
||||||
var alarmCmdFirst = true;
|
var alarmCmdFirst = true;
|
||||||
|
|
||||||
function getAlarmCmdResponse( respObj, respText ) {
|
function getAlarmCmdResponse( respObj, respText ) {
|
||||||
|
@ -378,11 +385,13 @@ function cmdForceAlarm() {
|
||||||
|
|
||||||
function cmdCancelForcedAlarm() {
|
function cmdCancelForcedAlarm() {
|
||||||
alarmCmdReq.send( alarmCmdParms+"&command=cancelForcedAlarm" );
|
alarmCmdReq.send( alarmCmdParms+"&command=cancelForcedAlarm" );
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActResponse( respObj, respText ) {
|
function getActResponse( respObj, respText ) {
|
||||||
if ( respObj.result == 'Ok' ) {
|
if ( respObj.result == 'Ok' ) {
|
||||||
if ( respObj.refreshParent ) {
|
if ( respObj.refreshParent ) {
|
||||||
|
console.log('refreshing');
|
||||||
window.opener.location.reload();
|
window.opener.location.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,7 @@
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
//
|
//
|
||||||
|
|
||||||
if ( !canView( 'Events' ) )
|
if ( !canView( 'Events' ) ) {
|
||||||
{
|
|
||||||
$view = "error";
|
$view = "error";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -52,32 +51,23 @@ $eventPath = $Event->Path();
|
||||||
|
|
||||||
$videoFormats = array();
|
$videoFormats = array();
|
||||||
$ffmpegFormats = preg_split( '/\s+/', ZM_FFMPEG_FORMATS );
|
$ffmpegFormats = preg_split( '/\s+/', ZM_FFMPEG_FORMATS );
|
||||||
foreach ( $ffmpegFormats as $ffmpegFormat )
|
foreach ( $ffmpegFormats as $ffmpegFormat ) {
|
||||||
{
|
if ( preg_match( '/^([^*]+)(\*\*?)$/', $ffmpegFormat, $matches ) ) {
|
||||||
if ( preg_match( '/^([^*]+)(\*\*?)$/', $ffmpegFormat, $matches ) )
|
|
||||||
{
|
|
||||||
$videoFormats[$matches[1]] = $matches[1];
|
$videoFormats[$matches[1]] = $matches[1];
|
||||||
if ( !isset($videoFormat) && $matches[2] == "*" )
|
if ( !isset($videoFormat) && $matches[2] == '*' ) {
|
||||||
{
|
|
||||||
$videoFormat = $matches[1];
|
$videoFormat = $matches[1];
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
$videoFormats[$ffmpegFormat] = $ffmpegFormat;
|
$videoFormats[$ffmpegFormat] = $ffmpegFormat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$videoFiles = array();
|
$videoFiles = array();
|
||||||
if ( $dir = opendir( $eventPath ) )
|
if ( $dir = opendir( $eventPath ) ) {
|
||||||
{
|
while ( ($file = readdir( $dir )) !== false ) {
|
||||||
while ( ($file = readdir( $dir )) !== false )
|
|
||||||
{
|
|
||||||
$file = $eventPath.'/'.$file;
|
$file = $eventPath.'/'.$file;
|
||||||
if ( is_file( $file ) )
|
if ( is_file( $file ) ) {
|
||||||
{
|
if ( preg_match( '/\.(?:'.join( '|', $videoFormats ).')$/', $file ) ) {
|
||||||
if ( preg_match( '/\.(?:'.join( '|', $videoFormats ).')$/', $file ) )
|
|
||||||
{
|
|
||||||
$videoFiles[] = $file;
|
$videoFiles[] = $file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,25 +75,23 @@ if ( $dir = opendir( $eventPath ) )
|
||||||
closedir( $dir );
|
closedir( $dir );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isset($_REQUEST['deleteIndex']) )
|
if ( isset($_REQUEST['deleteIndex']) ) {
|
||||||
{
|
|
||||||
$deleteIndex = validInt($_REQUEST['deleteIndex']);
|
$deleteIndex = validInt($_REQUEST['deleteIndex']);
|
||||||
unlink( $videoFiles[$deleteIndex] );
|
unlink( $videoFiles[$deleteIndex] );
|
||||||
unset( $videoFiles[$deleteIndex] );
|
unset( $videoFiles[$deleteIndex] );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isset($_REQUEST['downloadIndex']) )
|
if ( isset($_REQUEST['downloadIndex']) ) {
|
||||||
{
|
|
||||||
$downloadIndex = validInt($_REQUEST['downloadIndex']);
|
$downloadIndex = validInt($_REQUEST['downloadIndex']);
|
||||||
header( "Pragma: public" );
|
header( 'Pragma: public' );
|
||||||
header( "Expires: 0" );
|
header( 'Expires: 0' );
|
||||||
header( "Cache-Control: must-revalidate, post-check=0, pre-check=0" );
|
header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
|
||||||
header( "Cache-Control: private", false ); // required by certain browsers
|
header( 'Cache-Control: private', false ); // required by certain browsers
|
||||||
header( "Content-Description: File Transfer" );
|
header( 'Content-Description: File Transfer' );
|
||||||
header( 'Content-disposition: attachment; filename="'.basename($videoFiles[$downloadIndex]).'"' ); // basename is required because the video index contains the path and firefox doesn't strip the path but simply replaces the slashes with an underscore.
|
header( 'Content-disposition: attachment; filename="'.basename($videoFiles[$downloadIndex]).'"' ); // basename is required because the video index contains the path and firefox doesn't strip the path but simply replaces the slashes with an underscore.
|
||||||
header( "Content-Transfer-Encoding: binary" );
|
header( 'Content-Transfer-Encoding: binary' );
|
||||||
header( "Content-Type: application/force-download" );
|
header( 'Content-Type: application/force-download' );
|
||||||
header( "Content-Length: ".filesize($videoFiles[$downloadIndex]) );
|
header( 'Content-Length: '.filesize($videoFiles[$downloadIndex]) );
|
||||||
readfile( $videoFiles[$downloadIndex] );
|
readfile( $videoFiles[$downloadIndex] );
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
@ -122,8 +110,7 @@ xhtmlHeaders(__FILE__, translate('Video') );
|
||||||
</div>
|
</div>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<?php
|
<?php
|
||||||
if ( isset($_REQUEST['showIndex']) )
|
if ( isset($_REQUEST['showIndex']) ) {
|
||||||
{
|
|
||||||
$showIndex = validInt($_REQUEST['showIndex']);
|
$showIndex = validInt($_REQUEST['showIndex']);
|
||||||
preg_match( '/([^\/]+)\.([^.]+)$/', $videoFiles[$showIndex], $matches );
|
preg_match( '/([^\/]+)\.([^.]+)$/', $videoFiles[$showIndex], $matches );
|
||||||
$name = $matches[1];
|
$name = $matches[1];
|
||||||
|
@ -132,9 +119,7 @@ if ( isset($_REQUEST['showIndex']) )
|
||||||
<h3 id="videoFile"><?php echo substr( $videoFiles[$showIndex], strlen(ZM_DIR_EVENTS)+1 ) ?></h3>
|
<h3 id="videoFile"><?php echo substr( $videoFiles[$showIndex], strlen(ZM_DIR_EVENTS)+1 ) ?></h3>
|
||||||
<div id="imageFeed"><?php outputVideoStream( 'videoStream', $videoFiles[$showIndex], validInt($_REQUEST['width']), validInt($_REQUEST['height']), $videoFormat, $name ) ?></div>
|
<div id="imageFeed"><?php outputVideoStream( 'videoStream', $videoFiles[$showIndex], validInt($_REQUEST['width']), validInt($_REQUEST['height']), $videoFormat, $name ) ?></div>
|
||||||
<?php
|
<?php
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
<form name="contentForm" id="contentForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
|
<form name="contentForm" id="contentForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
|
||||||
<input type="hidden" name="id" value="<?php echo $event['Id'] ?>"/>
|
<input type="hidden" name="id" value="<?php echo $event['Id'] ?>"/>
|
||||||
|
@ -161,14 +146,11 @@ else
|
||||||
<input type="button" value="<?php echo translate('GenerateVideo') ?>" onclick="generateVideo( this.form );"<?php if ( !ZM_OPT_FFMPEG ) { ?> disabled="disabled"<?php } ?>/>
|
<input type="button" value="<?php echo translate('GenerateVideo') ?>" onclick="generateVideo( this.form );"<?php if ( !ZM_OPT_FFMPEG ) { ?> disabled="disabled"<?php } ?>/>
|
||||||
</form>
|
</form>
|
||||||
<?php
|
<?php
|
||||||
if ( isset($_REQUEST['generated']) )
|
if ( isset($_REQUEST['generated']) ) {
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
<h2 id="videoProgress" class="<?php echo $_REQUEST['generated']?'infoText':'errorText' ?>"><span id="videoProgressText"><?php echo $_REQUEST['generated']?translate('VideoGenSucceeded'):translate('VideoGenFailed') ?></span><span id="videoProgressTicker"></span></h2>
|
<h2 id="videoProgress" class="<?php echo $_REQUEST['generated']?'infoText':'errorText' ?>"><span id="videoProgressText"><?php echo $_REQUEST['generated']?translate('VideoGenSucceeded'):translate('VideoGenFailed') ?></span><span id="videoProgressTicker"></span></h2>
|
||||||
<?php
|
<?php
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
<h2 id="videoProgress" class="hidden warnText"><span id="videoProgressText"><?php echo translate('GeneratingVideo') ?></span><span id="videoProgressTicker"></span></h2>
|
<h2 id="videoProgress" class="hidden warnText"><span id="videoProgressText"><?php echo translate('GeneratingVideo') ?></span><span id="videoProgressTicker"></span></h2>
|
||||||
<?php
|
<?php
|
||||||
|
@ -176,14 +158,11 @@ else
|
||||||
?>
|
?>
|
||||||
<h2 id="videoFilesHeader"><?php echo translate('VideoGenFiles') ?></h2>
|
<h2 id="videoFilesHeader"><?php echo translate('VideoGenFiles') ?></h2>
|
||||||
<?php
|
<?php
|
||||||
if ( count($videoFiles) == 0 )
|
if ( count($videoFiles) == 0 ) {
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
<h3 id="videoNoFiles"><?php echo translate('VideoGenNoFiles') ?></h3>
|
<h3 id="videoNoFiles"><?php echo translate('VideoGenNoFiles') ?></h3>
|
||||||
<?php
|
<?php
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
?>
|
?>
|
||||||
<table id="videoTable" class="major" cellspacing="0">
|
<table id="videoTable" class="major" cellspacing="0">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -198,27 +177,19 @@ else
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?php
|
||||||
$index = 0;
|
$index = 0;
|
||||||
foreach ( $videoFiles as $file )
|
foreach ( $videoFiles as $file ) {
|
||||||
{
|
if ( filesize( $file ) > 0 ) {
|
||||||
if ( filesize( $file ) > 0 )
|
|
||||||
{
|
|
||||||
preg_match( '/^(.+)-((?:r[_\d]+)|(?:F[_\d]+))-((?:s[_\d]+)|(?:S[0-9a-z]+))\.([^.]+)$/', $file, $matches );
|
preg_match( '/^(.+)-((?:r[_\d]+)|(?:F[_\d]+))-((?:s[_\d]+)|(?:S[0-9a-z]+))\.([^.]+)$/', $file, $matches );
|
||||||
if ( preg_match( '/^r(.+)$/', $matches[2], $temp_matches ) )
|
if ( preg_match( '/^r(.+)$/', $matches[2], $temp_matches ) ) {
|
||||||
{
|
|
||||||
$rate = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) );
|
$rate = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) );
|
||||||
$rateText = isset($rates[$rate])?$rates[$rate]:($rate."x");
|
$rateText = isset($rates[$rate])?$rates[$rate]:($rate."x");
|
||||||
}
|
} elseif ( preg_match( '/^F(.+)$/', $matches[2], $temp_matches ) ) {
|
||||||
elseif ( preg_match( '/^F(.+)$/', $matches[2], $temp_matches ) )
|
|
||||||
{
|
|
||||||
$rateText = $temp_matches[1]."fps";
|
$rateText = $temp_matches[1]."fps";
|
||||||
}
|
}
|
||||||
if ( preg_match( '/^s(.+)$/', $matches[3], $temp_matches ) )
|
if ( preg_match( '/^s(.+)$/', $matches[3], $temp_matches ) ) {
|
||||||
{
|
|
||||||
$scale = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) );
|
$scale = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) );
|
||||||
$scaleText = isset($scales[$scale])?$scales[$scale]:($scale."x");
|
$scaleText = isset($scales[$scale])?$scales[$scale]:($scale."x");
|
||||||
}
|
} elseif ( preg_match( '/^S(.+)$/', $matches[3], $temp_matches ) ) {
|
||||||
elseif ( preg_match( '/^S(.+)$/', $matches[3], $temp_matches ) )
|
|
||||||
{
|
|
||||||
$scaleText = $temp_matches[1];
|
$scaleText = $temp_matches[1];
|
||||||
}
|
}
|
||||||
$width = $scale?reScale( $event['Width'], $scale ):$event['Width'];
|
$width = $scale?reScale( $event['Width'], $scale ):$event['Width'];
|
||||||
|
|
|
@ -59,7 +59,11 @@ xhtmlHeaders( __FILE__, $monitor->Name()." - ".translate('Feed') );
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<div id="menuBar">
|
<div id="menuBar">
|
||||||
<div id="monitorName"><?php echo $monitor->Name() ?></div>
|
<div id="monitorName"><?php echo $monitor->Name() ?></div>
|
||||||
<div id="closeControl"><a href="#" onclick="closeWindow(); return( false );"><?php echo translate('Close') ?></a></div>
|
<script type="text/javascript">
|
||||||
|
if ( window.opener ) {
|
||||||
|
document.write('<div id="closeControl"><a href="#" onclick="closeWindow(); return( false );"><?php echo translate('Close') ?></a></div>');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
<div id="menuControls">
|
<div id="menuControls">
|
||||||
<?php
|
<?php
|
||||||
if ( canView( 'Control' ) && $monitor->Type() == 'Local' ) {
|
if ( canView( 'Control' ) && $monitor->Type() == 'Local' ) {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program; if not, write to the Free Software
|
// along with this program; if not, write to the Free Software
|
||||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
//
|
//
|
||||||
|
|
||||||
// Calling sequence: ... /zm/index.php?view=video&event_id=123
|
// Calling sequence: ... /zm/index.php?view=video&event_id=123
|
||||||
|
|
57
zm.conf.in
57
zm.conf.in
|
@ -1,15 +1,14 @@
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
# ZoneMinder Base Configuration File
|
# ZoneMinder Base Configuration
|
||||||
#
|
#
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
# *** DO NOT EDIT THIS FILE ***
|
# *** DO NOT EDIT THIS FILE ***
|
||||||
# Changes made directly to this configuration file are no longer supported.
|
|
||||||
# They will be overwritten during an upgrade.
|
|
||||||
#
|
#
|
||||||
# Instead, create a custom configuration file, with an extention of ".conf"
|
# To make custom changes to the variables below, create a new configuration
|
||||||
# under the @ZM_CONFIG_SUBDIR@ subfolder containing your desired modifications.
|
# file, with an extention of .conf, under the @ZM_CONFIG_SUBDIR@
|
||||||
|
# folder, containing your desired modifications.
|
||||||
#
|
#
|
||||||
|
|
||||||
# Path to installed data directory, used mostly for finding DB upgrade scripts
|
# Path to installed data directory, used mostly for finding DB upgrade scripts
|
||||||
|
@ -50,43 +49,19 @@ ZM_DB_USER=@ZM_DB_USER@
|
||||||
# ZoneMinder database password
|
# ZoneMinder database password
|
||||||
ZM_DB_PASS=@ZM_DB_PASS@
|
ZM_DB_PASS=@ZM_DB_PASS@
|
||||||
|
|
||||||
# Full path to the folder events are recorded to.
|
# SSL CA certificate for ZoneMinder database
|
||||||
# The web account user must have full read/write permission to this folder.
|
ZM_DB_SSL_CA_CERT=@ZM_DB_SSL_CA_CERT@
|
||||||
ZM_DIR_EVENTS=@ZM_DIR_EVENTS@
|
|
||||||
|
|
||||||
# Full path to the folder images, not directly associated with events,
|
# SSL client key for ZoneMinder database
|
||||||
# are recorded to.
|
ZM_DB_SSL_CLIENT_KEY=@ZM_DB_SSL_CLIENT_KEY@
|
||||||
# The web account user must have full read/write permission to this folder.
|
|
||||||
ZM_DIR_IMAGES=@ZM_DIR_IMAGES@
|
|
||||||
|
|
||||||
# Foldername under the webroot where ZoneMinder looks for optional sound files
|
# SSL client cert for ZoneMinder database
|
||||||
# to play when an alarm is detected.
|
ZM_DB_SSL_CLIENT_CERT=@ZM_DB_SSL_CLIENT_CERT@
|
||||||
ZM_DIR_SOUNDS=@ZM_DIR_SOUNDS@
|
|
||||||
|
|
||||||
# Full path to the folder where exported archives are stored
|
# Do NOT set ZM_SERVER_HOST if you are not using Multi-Server
|
||||||
# The web account user must have full read/write permission to this folder.
|
# You have been warned
|
||||||
ZM_DIR_EXPORTS=@ZM_TMPDIR@
|
#
|
||||||
|
# The name specified here must have a corresponding entry
|
||||||
# ZoneMinder url path to the zms streaming server
|
# in the Servers tab under Options
|
||||||
ZM_PATH_ZMS=@ZM_PATH_ZMS@
|
ZM_SERVER_HOST=
|
||||||
|
|
||||||
# Full Path to ZoneMinder's mapped memory files
|
|
||||||
# The web account user must have full read/write permission to this folder.
|
|
||||||
ZM_PATH_MAP=@ZM_PATH_MAP@
|
|
||||||
|
|
||||||
# Full Path to ZoneMinder's socket folder
|
|
||||||
# The web account user must have full read/write permission to this folder.
|
|
||||||
ZM_PATH_SOCKS=@ZM_SOCKDIR@
|
|
||||||
|
|
||||||
# Full path to ZoneMinder's log folder
|
|
||||||
# The web account user must have full read/write permission to this folder.
|
|
||||||
ZM_PATH_LOGS=@ZM_LOGDIR@
|
|
||||||
|
|
||||||
# Full path to ZoneMinder's swap folder
|
|
||||||
# The web account user must have full read/write permission to this folder.
|
|
||||||
ZM_PATH_SWAP=@ZM_TMPDIR@
|
|
||||||
|
|
||||||
# Full path to optional arp binary
|
|
||||||
# ZoneMinder will find the arp binary automatically on most systems
|
|
||||||
ZM_PATH_ARP=@ZM_PATH_ARP@
|
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
#cmakedefine HAVE_LIBAVUTIL 1
|
#cmakedefine HAVE_LIBAVUTIL 1
|
||||||
#cmakedefine HAVE_LIBAVUTIL_AVUTIL_H 1
|
#cmakedefine HAVE_LIBAVUTIL_AVUTIL_H 1
|
||||||
#cmakedefine HAVE_LIBAVUTIL_MATHEMATICS_H 1
|
#cmakedefine HAVE_LIBAVUTIL_MATHEMATICS_H 1
|
||||||
|
#cmakedefine HAVE_LIBAVUTIL_HWCONTEXT_H 0
|
||||||
#cmakedefine HAVE_LIBSWSCALE 1
|
#cmakedefine HAVE_LIBSWSCALE 1
|
||||||
#cmakedefine HAVE_LIBSWSCALE_SWSCALE_H 1
|
#cmakedefine HAVE_LIBSWSCALE_SWSCALE_H 1
|
||||||
#cmakedefine HAVE_LIBAVRESAMPLE 1
|
#cmakedefine HAVE_LIBAVRESAMPLE 1
|
||||||
|
|
Loading…
Reference in New Issue