Merge branch 'master' into fmt
This commit is contained in:
commit
cad57df0bb
|
@ -5,6 +5,7 @@ web/api/lib
|
||||||
web/includes/csrf/
|
web/includes/csrf/
|
||||||
web/js/videojs.zoomrotate.js
|
web/js/videojs.zoomrotate.js
|
||||||
web/skins/classic/js/bootstrap-4.5.0.js
|
web/skins/classic/js/bootstrap-4.5.0.js
|
||||||
|
web/skins/classic/js/bootstrap.bundle.min.js
|
||||||
web/skins/classic/js/chosen
|
web/skins/classic/js/chosen
|
||||||
web/skins/classic/js/dateTimePicker
|
web/skins/classic/js/dateTimePicker
|
||||||
web/skins/classic/js/jquery-*.js
|
web/skins/classic/js/jquery-*.js
|
||||||
|
@ -13,6 +14,8 @@ web/skins/classic/js/jquery.js
|
||||||
web/skins/classic/js/moment.js
|
web/skins/classic/js/moment.js
|
||||||
web/skins/classic/js/video.js
|
web/skins/classic/js/video.js
|
||||||
web/tools/mootools
|
web/tools/mootools
|
||||||
|
web/js/janus.js
|
||||||
|
web/js/ajaxQueue.js
|
||||||
|
|
||||||
# Cannot be parsed as JS
|
# Cannot be parsed as JS
|
||||||
web/skins/classic/includes/export_functions.php
|
web/skins/classic/includes/export_functions.php
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"es6": true,
|
"es2017": true,
|
||||||
},
|
},
|
||||||
"extends": ["google"],
|
"extends": ["google"],
|
||||||
"overrides": [{
|
"overrides": [{
|
||||||
|
|
|
@ -19,7 +19,7 @@ jobs:
|
||||||
- crypto_backend: gnutls
|
- crypto_backend: gnutls
|
||||||
jwt_backend: libjwt
|
jwt_backend: libjwt
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: centos:8
|
container: rockylinux:8
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Enable RPMFusion, EPEL and PowerTools
|
- name: Enable RPMFusion, EPEL and PowerTools
|
||||||
|
|
|
@ -16,6 +16,6 @@ jobs:
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Install ESLint
|
- name: Install ESLint
|
||||||
run: npm install eslint@5.12.0 eslint-config-google@0.11.0 eslint-plugin-html@5.0.0 eslint-plugin-php-markup@0.2.5
|
run: npm install eslint@8.7.0 eslint-config-google@0.14.0 eslint-plugin-html@6.2.0 eslint-plugin-php-markup@6.0.0
|
||||||
- name: Run ESLint
|
- name: Run ESLint
|
||||||
run: npx eslint --ext .php,.js .
|
run: npx eslint --ext .php,.js .
|
||||||
|
|
|
@ -2,8 +2,7 @@ name: Create packages
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [ master ]
|
||||||
- '*'
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
|
|
||||||
|
@ -21,6 +20,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
|
fetch-depth: '0'
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Run packpack
|
- name: Run packpack
|
||||||
env:
|
env:
|
||||||
|
@ -29,3 +29,13 @@ jobs:
|
||||||
DIST: ${{ matrix.os_dist.dist }}
|
DIST: ${{ matrix.os_dist.dist }}
|
||||||
DOCKER_REPO: iconzm/packpack
|
DOCKER_REPO: iconzm/packpack
|
||||||
run: utils/packpack/startpackpack.sh
|
run: utils/packpack/startpackpack.sh
|
||||||
|
|
||||||
|
- name: Publish
|
||||||
|
uses: easingthemes/ssh-deploy@main
|
||||||
|
env:
|
||||||
|
SSH_PRIVATE_KEY: ${{ secrets.ZMREPO_SSH_KEY }}
|
||||||
|
ARGS: "-rltgoDzvO"
|
||||||
|
SOURCE: build/
|
||||||
|
REMOTE_HOST: ${{ secrets.ZMREPO_HOST }}
|
||||||
|
REMOTE_USER: ${{ secrets.ZMREPO_SSH_USER }}
|
||||||
|
TARGET: debian/master/mini-dinstall/incoming/
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
build:
|
||||||
|
os: "ubuntu-20.04"
|
||||||
|
tools:
|
||||||
|
python: "3.8"
|
||||||
|
|
||||||
|
sphinx:
|
||||||
|
fail_on_warning: true
|
|
@ -83,6 +83,7 @@ mark_as_advanced(
|
||||||
ZM_TARGET_DISTRO
|
ZM_TARGET_DISTRO
|
||||||
ZM_PATH_MAP
|
ZM_PATH_MAP
|
||||||
ZM_PATH_ARP
|
ZM_PATH_ARP
|
||||||
|
ZM_PATH_ARP_SCAN
|
||||||
ZM_CONFIG_DIR
|
ZM_CONFIG_DIR
|
||||||
ZM_CONFIG_SUBDIR
|
ZM_CONFIG_SUBDIR
|
||||||
ZM_SYSTEMD
|
ZM_SYSTEMD
|
||||||
|
@ -145,6 +146,8 @@ set(ZM_PATH_MAP "/dev/shm" CACHE PATH
|
||||||
"Location to save mapped memory files, default: /dev/shm")
|
"Location to save mapped memory files, default: /dev/shm")
|
||||||
set(ZM_PATH_ARP "" CACHE PATH
|
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_PATH_ARP_SCAN "" CACHE PATH
|
||||||
|
"Full path to compatible scan_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}/conf.d" CACHE PATH
|
||||||
|
@ -167,6 +170,8 @@ set(ZM_NO_X10 "OFF" CACHE BOOL
|
||||||
set(ZM_ONVIF "ON" CACHE BOOL
|
set(ZM_ONVIF "ON" CACHE BOOL
|
||||||
"Set to ON to enable basic ONVIF support. This is EXPERIMENTAL and may not
|
"Set to ON to enable basic ONVIF support. This is EXPERIMENTAL and may not
|
||||||
work with all cameras claiming to be ONVIF compliant. default: ON")
|
work with all cameras claiming to be ONVIF compliant. default: ON")
|
||||||
|
set(ZM_NO_PCRE "OFF" CACHE BOOL
|
||||||
|
"Set to ON to skip libpcre3 checks and force building ZM without libpcre3. default: OFF")
|
||||||
set(ZM_NO_RTSPSERVER "OFF" CACHE BOOL
|
set(ZM_NO_RTSPSERVER "OFF" CACHE BOOL
|
||||||
"Set to ON to skip building ZM with rtsp server support. default: OFF")
|
"Set to ON to skip building ZM with rtsp server support. default: OFF")
|
||||||
set(ZM_PERL_MM_PARMS INSTALLDIRS=vendor NO_PACKLIST=1 NO_PERLLOCAL=1 CACHE STRING
|
set(ZM_PERL_MM_PARMS INSTALLDIRS=vendor NO_PACKLIST=1 NO_PERLLOCAL=1 CACHE STRING
|
||||||
|
@ -316,10 +321,10 @@ endif()
|
||||||
# Do not check for cURL if ZM_NO_CURL is on
|
# Do not check for cURL if ZM_NO_CURL is on
|
||||||
if(NOT ZM_NO_CURL)
|
if(NOT ZM_NO_CURL)
|
||||||
# cURL
|
# cURL
|
||||||
find_package(CURL)
|
find_package(CURL REQUIRED)
|
||||||
if(CURL_FOUND)
|
if(CURL_FOUND)
|
||||||
set(HAVE_LIBCURL 1)
|
set(HAVE_LIBCURL 1)
|
||||||
#list(APPEND ZM_BIN_LIBS ${CURL_LIBRARIES})
|
list(APPEND ZM_BIN_LIBS ${CURL_LIBRARIES})
|
||||||
include_directories(${CURL_INCLUDE_DIRS})
|
include_directories(${CURL_INCLUDE_DIRS})
|
||||||
set(CMAKE_REQUIRED_INCLUDES ${CURL_INCLUDE_DIRS})
|
set(CMAKE_REQUIRED_INCLUDES ${CURL_INCLUDE_DIRS})
|
||||||
check_include_file("curl/curl.h" HAVE_CURL_CURL_H)
|
check_include_file("curl/curl.h" HAVE_CURL_CURL_H)
|
||||||
|
@ -407,21 +412,24 @@ else()
|
||||||
message(FATAL_ERROR "ZoneMinder requires pthread but it was not found on your system")
|
message(FATAL_ERROR "ZoneMinder requires pthread but it was not found on your system")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# pcre (using find_library and find_path)
|
# Do not check for cURL if ZM_NO_CURL is on
|
||||||
find_library(PCRE_LIBRARIES pcre)
|
if(NOT ZM_NO_PRCE)
|
||||||
if(PCRE_LIBRARIES)
|
# pcre (using find_library and find_path)
|
||||||
set(HAVE_LIBPCRE 1)
|
find_library(PCRE_LIBRARIES pcre)
|
||||||
list(APPEND ZM_BIN_LIBS "${PCRE_LIBRARIES}")
|
if(PCRE_LIBRARIES)
|
||||||
find_path(PCRE_INCLUDE_DIR pcre.h)
|
set(HAVE_LIBPCRE 1)
|
||||||
if(PCRE_INCLUDE_DIR)
|
list(APPEND ZM_BIN_LIBS "${PCRE_LIBRARIES}")
|
||||||
include_directories("${PCRE_INCLUDE_DIR}")
|
find_path(PCRE_INCLUDE_DIR pcre.h)
|
||||||
set(CMAKE_REQUIRED_INCLUDES "${PCRE_INCLUDE_DIR}")
|
if(PCRE_INCLUDE_DIR)
|
||||||
|
include_directories("${PCRE_INCLUDE_DIR}")
|
||||||
|
set(CMAKE_REQUIRED_INCLUDES "${PCRE_INCLUDE_DIR}")
|
||||||
|
endif()
|
||||||
|
mark_as_advanced(FORCE PCRE_LIBRARIES PCRE_INCLUDE_DIR)
|
||||||
|
check_include_file("pcre.h" HAVE_PCRE_H)
|
||||||
|
set(optlibsfound "${optlibsfound} PCRE")
|
||||||
|
else()
|
||||||
|
set(optlibsnotfound "${optlibsnotfound} PCRE")
|
||||||
endif()
|
endif()
|
||||||
mark_as_advanced(FORCE PCRE_LIBRARIES PCRE_INCLUDE_DIR)
|
|
||||||
check_include_file("pcre.h" HAVE_PCRE_H)
|
|
||||||
set(optlibsfound "${optlibsfound} PCRE")
|
|
||||||
else()
|
|
||||||
set(optlibsnotfound "${optlibsnotfound} PCRE")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# mysqlclient (using find_library and find_path)
|
# mysqlclient (using find_library and find_path)
|
||||||
|
@ -513,6 +521,15 @@ endif()
|
||||||
#list(APPEND ZM_BIN_LIBS "${Boost_LIBRARIES}")
|
#list(APPEND ZM_BIN_LIBS "${Boost_LIBRARIES}")
|
||||||
#endif()
|
#endif()
|
||||||
|
|
||||||
|
|
||||||
|
find_package(GSOAP 2.0.0)
|
||||||
|
if (GSOAP_FOUND)
|
||||||
|
set(optlibsfound "${optlibsfound} gsoap")
|
||||||
|
add_compile_definitions(WITH_GSOAP)
|
||||||
|
else()
|
||||||
|
set(optlibsnotfound "${optlibsnotfound} gsoap")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT ZM_NO_RTSPSERVER)
|
if(NOT ZM_NO_RTSPSERVER)
|
||||||
set(HAVE_RTSP_SERVER 1)
|
set(HAVE_RTSP_SERVER 1)
|
||||||
else()
|
else()
|
||||||
|
@ -542,6 +559,7 @@ set(ZM_PCRE 0)
|
||||||
if(HAVE_LIBPCRE AND HAVE_PCRE_H)
|
if(HAVE_LIBPCRE AND HAVE_PCRE_H)
|
||||||
set(ZM_PCRE 1)
|
set(ZM_PCRE 1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Check for mmap and enable in all components
|
# Check for mmap and enable in all components
|
||||||
set(ZM_MEM_MAPPED 0)
|
set(ZM_MEM_MAPPED 0)
|
||||||
set(ENABLE_MMAP no)
|
set(ENABLE_MMAP no)
|
||||||
|
@ -628,6 +646,18 @@ if(ZM_PATH_ARP STREQUAL "")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Find the path to an arp-scan compatible executable
|
||||||
|
if(ZM_PATH_ARP_SCAN STREQUAL "")
|
||||||
|
find_program(ARP_SCAN_EXECUTABLE arp-scan)
|
||||||
|
if(ARP_SCAN_EXECUTABLE)
|
||||||
|
set(ZM_PATH_ARP_SCAN "${ARP_SCAN_EXECUTABLE}")
|
||||||
|
mark_as_advanced(ARP_SCAN_EXECUTABLE)
|
||||||
|
endif()
|
||||||
|
if(ARP_SCAN_EXECUTABLE-NOTFOUND)
|
||||||
|
message(WARNING "Unable to find a compatible arp-scan binary. Monitor probe will be less powerful.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# Some variables that zm expects
|
# Some variables that zm expects
|
||||||
set(ZM_PID "${ZM_RUNDIR}/zm.pid")
|
set(ZM_PID "${ZM_RUNDIR}/zm.pid")
|
||||||
set(ZM_CONFIG "${ZM_CONFIG_DIR}/zm.conf")
|
set(ZM_CONFIG "${ZM_CONFIG_DIR}/zm.conf")
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
ZoneMinder
|
ZoneMinder
|
||||||
==========
|
==========
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder)
|
|
||||||
[![Bounty Source](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
|
[![Bounty Source](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
|
||||||
[![Join Slack](https://github.com/ozonesecurity/ozonebase/blob/master/img/slacksm.png?raw=true)](https://join.slack.com/t/zoneminder-chat/shared_invite/enQtNTU0NDkxMDM5NDQwLTdhZmQ5Y2M2NWQyN2JkYTBiN2ZkMzIzZGQ0MDliMTRmM2FjZWRlYzUwYTQ2MjMwMTVjMzQ1NjYxOTdmMjE2MTE)
|
[![Join Slack](https://github.com/ozonesecurity/ozonebase/blob/master/img/slacksm.png?raw=true)](https://join.slack.com/t/zoneminder-chat/shared_invite/enQtNTU0NDkxMDM5NDQwLTdhZmQ5Y2M2NWQyN2JkYTBiN2ZkMzIzZGQ0MDliMTRmM2FjZWRlYzUwYTQ2MjMwMTVjMzQ1NjYxOTdmMjE2MTE)
|
||||||
[![IRC Network](https://img.shields.io/badge/irc-%23zoneminder-blue.svg "IRC Freenode")](https://webchat.freenode.net/?channels=zoneminder)
|
|
||||||
|
|
||||||
All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org
|
All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org
|
||||||
|
|
||||||
|
@ -26,7 +24,7 @@ https://github.com/ZoneMinder/zmdockerfiles
|
||||||
|
|
||||||
This is the recommended method to install ZoneMinder onto your system. ZoneMinder packages are maintained for the following distros:
|
This is the recommended method to install ZoneMinder onto your system. ZoneMinder packages are maintained for the following distros:
|
||||||
|
|
||||||
- Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor)
|
- Ubuntu via [Isaac Connor's PPA](https://launchpad.net/~iconnor)
|
||||||
- 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 [RPM Fusion](http://rpmfusion.org)
|
- RHEL/CentOS and clones via [RPM Fusion](http://rpmfusion.org)
|
||||||
- Fedora via [RPM Fusion](http://rpmfusion.org)
|
- Fedora via [RPM Fusion](http://rpmfusion.org)
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
# FindFmt
|
||||||
|
# -------
|
||||||
|
# Finds the Fmt library
|
||||||
|
#
|
||||||
|
# This will define the following variables::
|
||||||
|
#
|
||||||
|
# FMT_FOUND - system has Fmt
|
||||||
|
# FMT_INCLUDE_DIRS - the Fmt include directory
|
||||||
|
# FMT_LIBRARIES - the Fmt libraries
|
||||||
|
#
|
||||||
|
# and the following imported targets::
|
||||||
|
#
|
||||||
|
# Fmt::Fmt - The Fmt library
|
||||||
|
|
||||||
|
if(ENABLE_INTERNAL_FMT)
|
||||||
|
include(ExternalProject)
|
||||||
|
file(STRINGS ${CMAKE_SOURCE_DIR}/tools/depends/target/libfmt/Makefile VER REGEX "^[ ]*VERSION[ ]*=.+$")
|
||||||
|
string(REGEX REPLACE "^[ ]*VERSION[ ]*=[ ]*" "" FMT_VERSION "${VER}")
|
||||||
|
|
||||||
|
# allow user to override the download URL with a local tarball
|
||||||
|
# needed for offline build envs
|
||||||
|
if(FMT_URL)
|
||||||
|
get_filename_component(FMT_URL "${FMT_URL}" ABSOLUTE)
|
||||||
|
else()
|
||||||
|
set(FMT_URL http://mirrors.kodi.tv/build-deps/sources/fmt-${FMT_VERSION}.tar.gz)
|
||||||
|
endif()
|
||||||
|
if(VERBOSE)
|
||||||
|
message(STATUS "FMT_URL: ${FMT_URL}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
set(EXTRA_ARGS "-DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(FMT_LIBRARY ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/lib/libfmt.a)
|
||||||
|
set(FMT_INCLUDE_DIR ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/include)
|
||||||
|
externalproject_add(fmt
|
||||||
|
URL ${FMT_URL}
|
||||||
|
DOWNLOAD_DIR ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/download
|
||||||
|
PREFIX ${CORE_BUILD_DIR}/fmt
|
||||||
|
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}
|
||||||
|
-DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}
|
||||||
|
-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}
|
||||||
|
-DCMAKE_INSTALL_LIBDIR=lib
|
||||||
|
-DFMT_DOC=OFF
|
||||||
|
-DFMT_TEST=OFF
|
||||||
|
"${EXTRA_ARGS}"
|
||||||
|
BUILD_BYPRODUCTS ${FMT_LIBRARY})
|
||||||
|
set_target_properties(fmt PROPERTIES FOLDER "External Projects")
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Fmt
|
||||||
|
REQUIRED_VARS FMT_LIBRARY FMT_INCLUDE_DIR
|
||||||
|
VERSION_VAR FMT_VERSION)
|
||||||
|
|
||||||
|
set(FMT_LIBRARIES ${FMT_LIBRARY})
|
||||||
|
set(FMT_INCLUDE_DIRS ${FMT_INCLUDE_DIR})
|
||||||
|
|
||||||
|
else()
|
||||||
|
|
||||||
|
find_package(FMT 6.1.2 CONFIG REQUIRED QUIET)
|
||||||
|
|
||||||
|
if(PKG_CONFIG_FOUND)
|
||||||
|
pkg_check_modules(PC_FMT libfmt QUIET)
|
||||||
|
if(PC_FMT_VERSION AND NOT FMT_VERSION)
|
||||||
|
set(FMT_VERSION ${PC_FMT_VERSION})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_path(FMT_INCLUDE_DIR NAMES fmt/format.h
|
||||||
|
PATHS ${PC_FMT_INCLUDEDIR})
|
||||||
|
|
||||||
|
find_library(FMT_LIBRARY_RELEASE NAMES fmt
|
||||||
|
PATHS ${PC_FMT_LIBDIR})
|
||||||
|
find_library(FMT_LIBRARY_DEBUG NAMES fmtd
|
||||||
|
PATHS ${PC_FMT_LIBDIR})
|
||||||
|
|
||||||
|
include(SelectLibraryConfigurations)
|
||||||
|
select_library_configurations(FMT)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Fmt
|
||||||
|
REQUIRED_VARS FMT_LIBRARY FMT_INCLUDE_DIR FMT_VERSION
|
||||||
|
VERSION_VAR FMT_VERSION)
|
||||||
|
|
||||||
|
if(FMT_FOUND)
|
||||||
|
set(FMT_LIBRARIES ${FMT_LIBRARY})
|
||||||
|
set(FMT_INCLUDE_DIRS ${FMT_INCLUDE_DIR})
|
||||||
|
|
||||||
|
if(NOT TARGET fmt)
|
||||||
|
add_library(fmt UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(fmt PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${FMT_LIBRARY}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${FMT_INCLUDE_DIR}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endif()
|
||||||
|
mark_as_advanced(FMT_INCLUDE_DIR FMT_LIBRARY)
|
|
@ -0,0 +1,113 @@
|
||||||
|
#
|
||||||
|
# This module detects if gsoap is installed and determines where the
|
||||||
|
# include files and libraries are.
|
||||||
|
#
|
||||||
|
# This code sets the following variables:
|
||||||
|
#
|
||||||
|
# GSOAP_IMPORT_DIR = full path to the gsoap import directory
|
||||||
|
# GSOAP_LIBRARIES = full path to the gsoap libraries
|
||||||
|
# GSOAP_SSL_LIBRARIES = full path to the gsoap ssl libraries
|
||||||
|
# GSOAP_INCLUDE_DIR = include dir to be used when using the gsoap library
|
||||||
|
# GSOAP_PLUGIN_DIR = gsoap plugins directory
|
||||||
|
# GSOAP_WSDL2H = wsdl2h binary
|
||||||
|
# GSOAP_SOAPCPP2 = soapcpp2 binary
|
||||||
|
# GSOAP_FOUND = set to true if gsoap was found successfully
|
||||||
|
#
|
||||||
|
# GSOAP_ROOT
|
||||||
|
# setting this enables search for gsoap libraries / headers in this location
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# GSOAP Import Directories
|
||||||
|
# -----------------------------------------------------
|
||||||
|
find_path(GSOAP_IMPORT_DIR
|
||||||
|
NAMES wsa.h
|
||||||
|
PATHS ${GSOAP_ROOT}/import ${GSOAP_ROOT}/share/gsoap/import
|
||||||
|
)
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# GSOAP Libraries
|
||||||
|
# -----------------------------------------------------
|
||||||
|
find_library(GSOAP_CXX_LIBRARIES
|
||||||
|
NAMES gsoap++
|
||||||
|
HINTS ${GSOAP_ROOT}/lib ${GSOAP_ROOT}/lib64
|
||||||
|
${GSOAP_ROOT}/lib32
|
||||||
|
DOC "The main gsoap library"
|
||||||
|
)
|
||||||
|
find_library(GSOAP_SSL_CXX_LIBRARIES
|
||||||
|
NAMES gsoapssl++
|
||||||
|
HINTS ${GSOAP_ROOT}/lib ${GSOAP_ROOT}/lib64
|
||||||
|
${GSOAP_ROOT}/lib32
|
||||||
|
DOC "The ssl gsoap library"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# GSOAP Include Directories
|
||||||
|
# -----------------------------------------------------
|
||||||
|
find_path(GSOAP_INCLUDE_DIR
|
||||||
|
NAMES stdsoap2.h
|
||||||
|
HINTS ${GSOAP_ROOT} ${GSOAP_ROOT}/include ${GSOAP_ROOT}/include/*
|
||||||
|
DOC "The gsoap include directory"
|
||||||
|
)
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# GSOAP plugin Directories
|
||||||
|
# -----------------------------------------------------
|
||||||
|
find_path(GSOAP_PLUGIN_DIR
|
||||||
|
NAMES wsseapi.c
|
||||||
|
HINTS ${GSOAP_ROOT} /usr/share/gsoap/plugin
|
||||||
|
DOC "The gsoap plugin directory"
|
||||||
|
)
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# GSOAP Binaries
|
||||||
|
# ----------------------------------------------------
|
||||||
|
if(NOT GSOAP_TOOL_DIR)
|
||||||
|
set(GSOAP_TOOL_DIR GSOAP_ROOT)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_program(GSOAP_WSDL2H
|
||||||
|
NAMES wsdl2h
|
||||||
|
HINTS ${GSOAP_TOOL_DIR}/bin
|
||||||
|
DOC "The gsoap bin directory"
|
||||||
|
)
|
||||||
|
find_program(GSOAP_SOAPCPP2
|
||||||
|
NAMES soapcpp2
|
||||||
|
HINTS ${GSOAP_TOOL_DIR}/bin
|
||||||
|
DOC "The gsoap bin directory"
|
||||||
|
)
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# GSOAP version
|
||||||
|
# try to determine the flagfor the 2.7.6 compatiblity, break with 2.7.13 and re-break with 2.7.16
|
||||||
|
# ----------------------------------------------------
|
||||||
|
if(GSOAP_SOAPCPP2)
|
||||||
|
execute_process(COMMAND ${GSOAP_SOAPCPP2} "-V" OUTPUT_VARIABLE GSOAP_STRING_VERSION ERROR_VARIABLE GSOAP_STRING_VERSION )
|
||||||
|
string(REGEX MATCH "[0-9]*\\.[0-9]*\\.[0-9]*" GSOAP_VERSION ${GSOAP_STRING_VERSION})
|
||||||
|
endif()
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# GSOAP_276_COMPAT_FLAGS and GSOAPVERSION
|
||||||
|
# try to determine the flagfor the 2.7.6 compatiblity, break with 2.7.13 and re-break with 2.7.16
|
||||||
|
# ----------------------------------------------------
|
||||||
|
if( "${GSOAP_VERSION}" VERSION_LESS "2.7.6")
|
||||||
|
set(GSOAP_276_COMPAT_FLAGS "")
|
||||||
|
elseif ( "${GSOAP_VERSION}" VERSION_LESS "2.7.14")
|
||||||
|
set(GSOAP_276_COMPAT_FLAGS "-z")
|
||||||
|
else ( "${GSOAP_VERSION}" VERSION_LESS "2.7.14")
|
||||||
|
set(GSOAP_276_COMPAT_FLAGS "-z1 -z2")
|
||||||
|
endif ( "${GSOAP_VERSION}" VERSION_LESS "2.7.6")
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# handle the QUIETLY and REQUIRED arguments and set GSOAP_FOUND to TRUE if
|
||||||
|
# all listed variables are TRUE
|
||||||
|
# -----------------------------------------------------
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(GSOAP DEFAULT_MSG GSOAP_CXX_LIBRARIES
|
||||||
|
GSOAP_INCLUDE_DIR GSOAP_WSDL2H GSOAP_SOAPCPP2)
|
||||||
|
mark_as_advanced(GSOAP_INCLUDE_DIR GSOAP_LIBRARIES GSOAP_WSDL2H GSOAP_SOAPCPP2)
|
||||||
|
|
||||||
|
if(GSOAP_FOUND)
|
||||||
|
if(GSOAP_FIND_REQUIRED AND GSOAP_FIND_VERSION AND ${GSOAP_VERSION} VERSION_LESS ${GSOAP_FIND_VERSION})
|
||||||
|
message(SEND_ERROR "Found GSOAP version ${GSOAP_VERSION} less then required ${GSOAP_FIND_VERSION}.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
|
@ -47,5 +47,9 @@ ZM_PATH_SWAP=@ZM_TMPDIR@
|
||||||
# ZoneMinder will find the arp binary automatically on most systems
|
# ZoneMinder will find the arp binary automatically on most systems
|
||||||
ZM_PATH_ARP="@ZM_PATH_ARP@"
|
ZM_PATH_ARP="@ZM_PATH_ARP@"
|
||||||
|
|
||||||
|
# Full path to optional arp-scan binary
|
||||||
|
# ZoneMinder will find the arp-scan binary automatically on most systems
|
||||||
|
ZM_PATH_ARP_SCAN="@ZM_PATH_ARP_SCAN@"
|
||||||
|
|
||||||
#Full path to shutdown binary
|
#Full path to shutdown binary
|
||||||
ZM_PATH_SHUTDOWN="@ZM_PATH_SHUTDOWN@"
|
ZM_PATH_SHUTDOWN="@ZM_PATH_SHUTDOWN@"
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
configure_file(zm_create.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" @ONLY)
|
configure_file(zm_create.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" @ONLY)
|
||||||
configure_file(zm_update-1.31.30.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.31.30.sql" @ONLY)
|
configure_file(zm_update-1.31.30.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.31.30.sql" @ONLY)
|
||||||
configure_file(zm_update-1.35.24.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.35.24.sql" @ONLY)
|
configure_file(zm_update-1.35.24.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.35.24.sql" @ONLY)
|
||||||
|
configure_file(zm_update-1.37.4.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.37.4.sql" @ONLY)
|
||||||
|
|
||||||
# Glob all database upgrade scripts
|
# Glob all database upgrade scripts
|
||||||
file(GLOB dbfileslist RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "zm_update-*.sql")
|
file(GLOB dbfileslist RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "zm_update-*.sql")
|
||||||
|
@ -15,6 +16,8 @@ install(FILES ${dbfileslist} DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.31.30.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.31.30.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
|
||||||
# install zm_update-1.35.24.sql
|
# install zm_update-1.35.24.sql
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.35.24.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.35.24.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
|
||||||
|
# install zm_update-1.37.4.sql
|
||||||
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.37.4.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
|
||||||
|
|
||||||
# install zm_create.sql
|
# install zm_create.sql
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
|
||||||
|
@ -22,3 +25,8 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" DESTINATION "${CMAKE_I
|
||||||
# install triggers.sql
|
# install triggers.sql
|
||||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/triggers.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/triggers.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
|
||||||
|
|
||||||
|
# install manufacturers.sql
|
||||||
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/manufacturers.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
|
||||||
|
|
||||||
|
# install models.sql
|
||||||
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/models.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (1, 'Acti');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (2, 'Amcrest');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (3, 'Airlink101');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (4, 'Arecont Vision');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (5, 'Axis');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (6, 'Dahua');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (7, 'D-Link');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (8, 'Edimax');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (9, 'Foscam');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (10, 'Gadspot');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (11, 'GrandStream');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (12, 'HikVision');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (13, 'JVC');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (14, 'Maginon');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (15, 'Mobotix');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (16, 'Oncam Grandeye');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (17, 'Panasonic');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (18, 'Pelco');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (19, 'Sony');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (20, 'TP-Link');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (21, 'Trendnet');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (22, 'VisionTek');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (23, 'Vivotek');
|
||||||
|
INSERT IGNORE INTO Manufacturers VALUES (24, 'Wansview');
|
|
@ -0,0 +1,56 @@
|
||||||
|
/* INSERT INTO Manufacturers VALUES (1, 'Acti'); */
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A21');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A23');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A24');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A28');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A31');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A310');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A311');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A32');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A41');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A415');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A416');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A418');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A42');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A421');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A43');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A45');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A46');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A48');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (1, 'A74');
|
||||||
|
/*
|
||||||
|
INSERT INTO Manufacturers VALUES (2, 'Amcrest');
|
||||||
|
*/
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (2, 'IP8M-T2499EW');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (2, 'ASH42-B');
|
||||||
|
/*
|
||||||
|
INSERT INTO Manufacturers VALUES (3, 'Airlink101');
|
||||||
|
INSERT INTO Manufacturers VALUES (4, 'Arecont Vision');
|
||||||
|
INSERT INTO Manufacturers VALUES (5, 'Axis');
|
||||||
|
INSERT INTO Manufacturers VALUES (6, 'Dahua');
|
||||||
|
INSERT INTO Manufacturers VALUES (7, 'D-Link');
|
||||||
|
*/
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (7, 'DCS-930L');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (7, 'DCS-932L');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (7, 'DCS-933L');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (7, 'DCS-942L');
|
||||||
|
INSERT IGNORE INTO Models (ManufacturerId,Name) VALUES (7, 'DCS-5020L');
|
||||||
|
/*
|
||||||
|
INSERT INTO Manufacturers VALUES (8, 'Edimax');
|
||||||
|
INSERT INTO Manufacturers VALUES (9, 'Foscam');
|
||||||
|
INSERT INTO Manufacturers VALUES (10, 'Gadspot');
|
||||||
|
INSERT INTO Manufacturers VALUES (11, 'GrandStream');
|
||||||
|
INSERT INTO Manufacturers VALUES (12, 'HikVision');
|
||||||
|
INSERT INTO Manufacturers VALUES (13, 'JVC');
|
||||||
|
INSERT INTO Manufacturers VALUES (14, 'Maginon');
|
||||||
|
INSERT INTO Manufacturers VALUES (15, 'Mobotix');
|
||||||
|
INSERT INTO Manufacturers VALUES (16, 'Oncam Grandeye');
|
||||||
|
INSERT INTO Manufacturers VALUES (17, 'Panasonic');
|
||||||
|
INSERT INTO Manufacturers VALUES (18, 'Pelco');
|
||||||
|
INSERT INTO Manufacturers VALUES (19, 'Sony');
|
||||||
|
INSERT INTO Manufacturers VALUES (20, 'TP-Link');
|
||||||
|
INSERT INTO Manufacturers VALUES (21, 'Trendnet');
|
||||||
|
INSERT INTO Manufacturers VALUES (22, 'VisionTek');
|
||||||
|
INSERT INTO Manufacturers VALUES (23, 'Vivotek');
|
||||||
|
INSERT INTO Manufacturers VALUES (24, 'Wansview');
|
||||||
|
*/
|
|
@ -39,6 +39,7 @@ CREATE TABLE `Config` (
|
||||||
`Help` text,
|
`Help` text,
|
||||||
`Category` varchar(32) NOT NULL default '',
|
`Category` varchar(32) NOT NULL default '',
|
||||||
`Readonly` tinyint(3) unsigned NOT NULL default '0',
|
`Readonly` tinyint(3) unsigned NOT NULL default '0',
|
||||||
|
`Private` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
`Requires` text,
|
`Requires` text,
|
||||||
PRIMARY KEY (`Name`)
|
PRIMARY KEY (`Name`)
|
||||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||||
|
@ -188,7 +189,7 @@ CREATE TABLE `Events` (
|
||||||
`StorageId` smallint(5) unsigned default 0,
|
`StorageId` smallint(5) unsigned default 0,
|
||||||
`SecondaryStorageId` smallint(5) unsigned default 0,
|
`SecondaryStorageId` smallint(5) unsigned default 0,
|
||||||
`Name` varchar(64) NOT NULL default '',
|
`Name` varchar(64) NOT NULL default '',
|
||||||
`Cause` varchar(32) NOT NULL default '',
|
`Cause` TEXT,
|
||||||
`StartDateTime` datetime default NULL,
|
`StartDateTime` datetime default NULL,
|
||||||
`EndDateTime` datetime default NULL,
|
`EndDateTime` datetime default NULL,
|
||||||
`Width` smallint(5) unsigned NOT NULL default '0',
|
`Width` smallint(5) unsigned NOT NULL default '0',
|
||||||
|
@ -283,6 +284,7 @@ CREATE TABLE `Filters` (
|
||||||
`Id` int(10) unsigned NOT NULL auto_increment,
|
`Id` int(10) unsigned NOT NULL auto_increment,
|
||||||
`Name` varchar(64) NOT NULL default '',
|
`Name` varchar(64) NOT NULL default '',
|
||||||
`UserId` int(10) unsigned,
|
`UserId` int(10) unsigned,
|
||||||
|
`ExecuteInterval` int(10) unsigned NOT NULL default '60',
|
||||||
`Query_json` text NOT NULL,
|
`Query_json` text NOT NULL,
|
||||||
`AutoArchive` tinyint(3) unsigned NOT NULL default '0',
|
`AutoArchive` tinyint(3) unsigned NOT NULL default '0',
|
||||||
`AutoUnarchive` tinyint(3) unsigned NOT NULL default '0',
|
`AutoUnarchive` tinyint(3) unsigned NOT NULL default '0',
|
||||||
|
@ -412,6 +414,7 @@ CREATE TABLE `Models` (
|
||||||
DROP TABLE IF EXISTS `MonitorPresets`;
|
DROP TABLE IF EXISTS `MonitorPresets`;
|
||||||
CREATE TABLE `MonitorPresets` (
|
CREATE TABLE `MonitorPresets` (
|
||||||
`Id` int(10) unsigned NOT NULL auto_increment,
|
`Id` int(10) unsigned NOT NULL auto_increment,
|
||||||
|
`ModelId` int unsigned, FOREIGN KEY (`ModelId`) REFERENCES `Models` (Id),
|
||||||
`Name` varchar(64) NOT NULL default '',
|
`Name` varchar(64) NOT NULL default '',
|
||||||
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local',
|
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local',
|
||||||
`Device` tinytext,
|
`Device` tinytext,
|
||||||
|
@ -447,16 +450,24 @@ CREATE TABLE `Monitors` (
|
||||||
`Notes` TEXT,
|
`Notes` TEXT,
|
||||||
`ServerId` int(10) unsigned,
|
`ServerId` int(10) unsigned,
|
||||||
`StorageId` smallint(5) unsigned default 0,
|
`StorageId` smallint(5) unsigned default 0,
|
||||||
|
`ManufacturerId` int unsigned, FOREIGN KEY (`ManufacturerId`) REFERENCES `Manufacturers` (Id),
|
||||||
|
`ModelId` int unsigned, FOREIGN KEY (`ModelId`) REFERENCES `Models` (Id),
|
||||||
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local',
|
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local',
|
||||||
`Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor',
|
`Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor',
|
||||||
`Enabled` tinyint(3) unsigned NOT NULL default '1',
|
`Enabled` tinyint(3) unsigned NOT NULL default '1',
|
||||||
`DecodingEnabled` tinyint(3) unsigned NOT NULL default '1',
|
`DecodingEnabled` tinyint(3) unsigned NOT NULL default '1',
|
||||||
|
`JanusEnabled` BOOLEAN NOT NULL default false,
|
||||||
|
`JanusAudioEnabled` BOOLEAN NOT NULL default false,
|
||||||
`LinkedMonitors` varchar(255),
|
`LinkedMonitors` varchar(255),
|
||||||
`Triggers` set('X10') NOT NULL default '',
|
`Triggers` set('X10') NOT NULL default '',
|
||||||
|
`EventStartCommand` VARCHAR(255) NOT NULL DEFAULT '',
|
||||||
|
`EventEndCommand` VARCHAR(255) NOT NULL DEFAULT '',
|
||||||
`ONVIF_URL` VARCHAR(255) NOT NULL DEFAULT '',
|
`ONVIF_URL` VARCHAR(255) NOT NULL DEFAULT '',
|
||||||
`ONVIF_Username` VARCHAR(64) NOT NULL DEFAULT '',
|
`ONVIF_Username` VARCHAR(64) NOT NULL DEFAULT '',
|
||||||
`ONVIF_Password` VARCHAR(64) NOT NULL DEFAULT '',
|
`ONVIF_Password` VARCHAR(64) NOT NULL DEFAULT '',
|
||||||
`ONVIF_Options` VARCHAR(64) NOT NULL DEFAULT '',
|
`ONVIF_Options` VARCHAR(64) NOT NULL DEFAULT '',
|
||||||
|
`ONVIF_Event_Listener` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
`use_Amcrest_API` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
`Device` tinytext NOT NULL default '',
|
`Device` tinytext NOT NULL default '',
|
||||||
`Channel` tinyint(3) unsigned NOT NULL default '0',
|
`Channel` tinyint(3) unsigned NOT NULL default '0',
|
||||||
`Format` int(10) unsigned NOT NULL default '0',
|
`Format` int(10) unsigned NOT NULL default '0',
|
||||||
|
@ -971,81 +982,81 @@ INSERT INTO `Controls` VALUES (NULL,'Amcrest HTTP API','Ffmpeg','Amcrest_HTTP',0
|
||||||
-- Add some monitor preset values
|
-- Add some monitor preset values
|
||||||
--
|
--
|
||||||
|
|
||||||
INSERT into MonitorPresets VALUES (NULL,'Amcrest, IP8M-T2499EW 640x480, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp','NULL',554,'rtsp://<username>:<password>@<ip-address>/cam/realmonitor?channel=1&subtype=1',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT into MonitorPresets VALUES (NULL,NULL,'Amcrest, IP8M-T2499EW 640x480, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp','NULL',554,'rtsp://<username>:<password>@<ip-address>/cam/realmonitor?channel=1&subtype=1',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT into MonitorPresets VALUES (NULL,'Amcrest, IP8M-T2499EW 3840x2160, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp','NULL',554,'rtsp://<username>:<password>@<ip-address>/cam/realmonitor?channel=1&subtype=0',NULL,3840,2160,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT into MonitorPresets VALUES (NULL,NULL,'Amcrest, IP8M-T2499EW 3840x2160, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp','NULL',554,'rtsp://<username>:<password>@<ip-address>/cam/realmonitor?channel=1&subtype=0',NULL,3840,2160,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 320x240, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 640x480, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg, B&W','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&color=0',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 320x240, mpjpeg, B&W','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&color=0',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, mpjpeg, B&W','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&color=0',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 640x480, mpjpeg, B&W','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&color=0',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 320x240, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,1,4,NULL,'<ip-address>:<port>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,1,4,NULL,'<ip-address>:<port>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 640x480, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,1,4,NULL,'<ip-address>:<port>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,1,4,NULL,'<ip-address>:<port>',100,100);
|
||||||
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, unicast','Remote','rtsp',0,255,'rtsp','rtpUni','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT into MonitorPresets VALUES (NULL,NULL,'Axis IP, mpeg4, unicast','Remote','rtsp',0,255,'rtsp','rtpUni','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, multicast','Remote','rtsp',0,255,'rtsp','rtpMulti','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT into MonitorPresets VALUES (NULL,NULL,'Axis IP, mpeg4, multicast','Remote','rtsp',0,255,'rtsp','rtpMulti','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP','Remote','rtsp',0,255,'rtsp','rtpRtsp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT into MonitorPresets VALUES (NULL,NULL,'Axis IP, mpeg4, RTP/RTSP','Remote','rtsp',0,255,'rtsp','rtpRtsp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP/HTTP','Remote',NULL,NULL,NULL,'rtsp','rtpRtspHttp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT into MonitorPresets VALUES (NULL,NULL,'Axis IP, mpeg4, RTP/RTSP/HTTP','Remote',NULL,NULL,NULL,'rtsp','rtpRtspHttp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'D-link DCS-930L, 640x480, mjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/mjpeg.cgi',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'D-link DCS-930L, 640x480, mjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/mjpeg.cgi',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'D-Link DCS-5020L, 640x480, mjpeg','Remote','http',0,0,'http','simple','<username>:<pwd>@<ip-address>','80','/video.cgi',NULL,640,480,0,NULL,1,'34',NULL,'<username>:<pwd>@<ip-address>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'D-Link DCS-5020L, 640x480, mjpeg','Remote','http',0,0,'http','simple','<username>:<pwd>@<ip-address>','80','/video.cgi',NULL,640,480,0,NULL,1,'34',NULL,'<username>:<pwd>@<ip-address>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP PTZ, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP PTZ, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,1,5,NULL,'<ip-address>:<port>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,1,5,NULL,'<ip-address>:<port>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP PTZ, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP PTZ, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,1,5,NULL,'<ip-address>:<port>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,1,5,NULL,'<ip-address>:<port>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Gadspot IP, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Gadspot IP, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/GetData.cgi',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/GetData.cgi',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'IP Webcam by Pavel Khlebovich 1920x1080','Remote','/dev/video<?>','0',255,'http','simple','<ip-address>','8080','/video','',1920,1080,0,NULL,0,'0','','',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'IP Webcam by Pavel Khlebovich 1920x1080','Remote','/dev/video<?>','0',255,'http','simple','<ip-address>','8080','/video','',1920,1080,0,NULL,0,'0','','',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'VEO Observer, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'VEO Observer, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Blue Net Video Server, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/cgi-bin/image.cgi?control=0&id=admin&passwd=admin',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Blue Net Video Server, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/cgi-bin/image.cgi?control=0&id=admin&passwd=admin',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT into MonitorPresets VALUES (NULL,'ACTi IP, mpeg4, unicast','Remote',NULL,NULL,NULL,'rtsp','rtpUni','<ip-address>',7070,'','/track',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT into MonitorPresets VALUES (NULL,NULL,'ACTi IP, mpeg4, unicast','Remote',NULL,NULL,NULL,'rtsp','rtpUni','<ip-address>',7070,'','/track',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://<host/address>/axis-media/media.amp?videocodec=h264',NULL,NULL,NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://<host/address>/axis-media/media.amp?videocodec=h264',NULL,NULL,NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Vivotek FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://<host/address>:554/live.sdp',NULL,NULL,NULL,352,240,NULL,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Vivotek FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://<host/address>:554/live.sdp',NULL,NULL,NULL,352,240,NULL,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Axis FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://<host/address>/axis-media/media.amp',NULL,NULL,NULL,640,480,NULL,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://<host/address>/axis-media/media.amp',NULL,NULL,NULL,640,480,NULL,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'ACTi TCM FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://admin:123456@<host/address>:7070',NULL,NULL,NULL,320,240,NULL,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'ACTi TCM FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://admin:123456@<host/address>:7070',NULL,NULL,NULL,320,240,NULL,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 320x240','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), PAL, 320x240','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 320x240, max 5 FPS','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), PAL, 320x240, max 5 FPS','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 640x480','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), PAL, 640x480','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 640x480, max 5 FPS','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), PAL, 640x480, max 5 FPS','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 320x240','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), NTSC, 320x240','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 320x240, max 5 FPS','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), NTSC, 320x240, max 5 FPS','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 640x480','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), NTSC, 640x480','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 640x480, max 5 FPS','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), NTSC, 640x480, max 5 FPS','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 320x240','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), PAL, 320x240','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 320x240, max 5 FPS','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), PAL, 320x240, max 5 FPS','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 640x480','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), PAL, 640x480','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 640x480, max 5 FPS','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), PAL, 640x480, max 5 FPS','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 320x240','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), NTSC, 320x240','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 320x240, max 5 FPS','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), NTSC, 320x240, max 5 FPS','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 640x480','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), NTSC, 640x480','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 640x480, max 5 FPS','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), NTSC, 640x480, max 5 FPS','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Remote ZoneMinder','Remote',NULL,NULL,NULL,'http','simple','<ip-address>',80,'/cgi-bin/nph-zms?mode=jpeg&monitor=<monitor-id>&scale=100&maxfps=5&buffer=0',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Remote ZoneMinder','Remote',NULL,NULL,NULL,'http','simple','<ip-address>',80,'/cgi-bin/nph-zms?mode=jpeg&monitor=<monitor-id>&scale=100&maxfps=5&buffer=0',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Foscam FI8620 FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://<username>:<pwd>@<ip-address>:554/11',NULL,704,576,0,NULL,1,'10','<admin_pwd>','<ip-address>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Foscam FI8620 FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://<username>:<pwd>@<ip-address>:554/11',NULL,704,576,0,NULL,1,'10','<admin_pwd>','<ip-address>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Foscam FI8608W FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://<username>:<pwd>@<ip-address>:554/11',NULL,640,480,0,NULL,1,'11','<admin_pwd>','<ip-address>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Foscam FI8608W FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://<username>:<pwd>@<ip-address>:554/11',NULL,640,480,0,NULL,1,'11','<admin_pwd>','<ip-address>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Foscam FI9821W FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://<username>:<pwd>@<ip-address>:88/videoMain',NULL,1280,720,0,NULL,1,'12','<admin_pwd>','<ip-address>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Foscam FI9821W FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://<username>:<pwd>@<ip-address>:88/videoMain',NULL,1280,720,0,NULL,1,'12','<admin_pwd>','<ip-address>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Loftek Sentinel PTZ, 640x480, mjpeg','Remote','http',0,0,NULL,NULL,'<ip-address>','80','/videostream.cgi?user=<username>&pwd=<password>&resolution=32&rate=11',NULL,640,480,4,NULL,1,'13','','<username>:<pwd>@<ip-address>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Loftek Sentinel PTZ, 640x480, mjpeg','Remote','http',0,0,NULL,NULL,'<ip-address>','80','/videostream.cgi?user=<username>&pwd=<password>&resolution=32&rate=11',NULL,640,480,4,NULL,1,'13','','<username>:<pwd>@<ip-address>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Airlink 777W PTZ, 640x480, mjpeg','Remote','http',0,0,NULL,NULL,'<username>:<password>@<ip-address>','80','/cgi/mjpg/mjpg.cgi',NULL,640,480,4,NULL,1,'7','','<username>:<pwd>@<ip-address>',100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Airlink 777W PTZ, 640x480, mjpeg','Remote','http',0,0,NULL,NULL,'<username>:<password>@<ip-address>','80','/cgi/mjpg/mjpg.cgi',NULL,640,480,4,NULL,1,'7','','<username>:<pwd>@<ip-address>',100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'SunEyes SP-P1802SWPTZ','Libvlc','/dev/video<?>','0',255,'','rtpMulti','','80','rtsp://<ip-address>:554/11','',1920,1080,0,0.00,1,'16','-speed=64','<ip-address>:<port>',100,33);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'SunEyes SP-P1802SWPTZ','Libvlc','/dev/video<?>','0',255,'','rtpMulti','','80','rtsp://<ip-address>:554/11','',1920,1080,0,0.00,1,'16','-speed=64','<ip-address>:<port>',100,33);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Qihan IP, 1280x720, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp',NULL,554,'rtsp://<ip-address>/tcp_live/ch0_0',NULL,1280,720,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Qihan IP, 1280x720, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp',NULL,554,'rtsp://<ip-address>/tcp_live/ch0_0',NULL,1280,720,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
INSERT INTO MonitorPresets VALUES (NULL,'Qihan IP, 1920x1080, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp',NULL,554,'rtsp://<ip-address>/tcp_live/ch0_0',NULL,1920,1080,3,NULL,0,NULL,NULL,NULL,100,100);
|
INSERT INTO MonitorPresets VALUES (NULL,NULL,'Qihan IP, 1920x1080, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp',NULL,554,'rtsp://<ip-address>/tcp_live/ch0_0',NULL,1920,1080,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Add some zone preset values
|
-- Add some zone preset values
|
||||||
|
@ -1115,6 +1126,9 @@ CREATE TABLE Snapshot_Events (
|
||||||
|
|
||||||
-- We generally don't alter triggers, we drop and re-create them, so let's keep them in a separate file that we can just source in update scripts.
|
-- We generally don't alter triggers, we drop and re-create them, so let's keep them in a separate file that we can just source in update scripts.
|
||||||
source @PKGDATADIR@/db/triggers.sql
|
source @PKGDATADIR@/db/triggers.sql
|
||||||
|
|
||||||
|
source @PKGDATADIR@/db/manufacturers.sql
|
||||||
|
source @PKGDATADIR@/db/models.sql
|
||||||
--
|
--
|
||||||
-- Apply the initial configuration
|
-- Apply the initial configuration
|
||||||
--
|
--
|
||||||
|
|
|
@ -56,7 +56,7 @@ EXECUTE stmt;
|
||||||
SET @s = (SELECT IF(
|
SET @s = (SELECT IF(
|
||||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||||
AND table_name = 'Monitor_Status'
|
AND table_name = 'Monitor_Status'
|
||||||
AND column_name = 'DayEvents'
|
AND column_name = 'DayEventDiskSpace'
|
||||||
) > 0,
|
) > 0,
|
||||||
"ALTER TABLE `Monitor_Status` DROP `DayEventDiskSpace`",
|
"ALTER TABLE `Monitor_Status` DROP `DayEventDiskSpace`",
|
||||||
"SELECT 'Column DayEventDiskSpace already removed from Monitor_Status'"
|
"SELECT 'Column DayEventDiskSpace already removed from Monitor_Status'"
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||||
|
AND table_name = 'Monitors'
|
||||||
|
AND column_name = 'ManufacturerId'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column ManufacturerId already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD `ManufacturerId` int(10) unsigned AFTER `StorageId`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
||||||
|
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE table_schema = DATABASE()
|
||||||
|
AND table_name = 'Monitors'
|
||||||
|
AND column_name = 'ManufacturerId'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'FOREIGN KEY for ManufacturerId already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD FOREIGN KEY (`ManufacturerId`) REFERENCES `Manufacturers` (Id)"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
||||||
|
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||||
|
AND table_name = 'Monitors'
|
||||||
|
AND column_name = 'ModelId'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column ModelId already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD `ModelId` int(10) unsigned AFTER `ManufacturerId`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
||||||
|
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE table_schema = DATABASE()
|
||||||
|
AND table_name = 'Monitors'
|
||||||
|
AND column_name = 'ModelId'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'FOREIGN KEY for ModelId already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD FOREIGN KEY (`ModelId`) REFERENCES `Models` (Id)"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
|
@ -0,0 +1,20 @@
|
||||||
|
--
|
||||||
|
-- Update Config table to have Private
|
||||||
|
--
|
||||||
|
|
||||||
|
SELECT 'Checking for Private in Config';
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*)
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE table_name = 'Config'
|
||||||
|
AND table_schema = DATABASE()
|
||||||
|
AND column_name = 'Private'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column Private already exists in Config'",
|
||||||
|
"ALTER TABLE `Config` ADD COLUMN `Private` BOOLEAN NOT NULL DEFAULT FALSE AFTER `Readonly`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
--
|
||||||
|
-- Update Monitors table to have use_Amcrest_API
|
||||||
|
--
|
||||||
|
|
||||||
|
SELECT 'Checking for use_Amcrest_API in Monitors';
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*)
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE table_name = 'Monitors'
|
||||||
|
AND table_schema = DATABASE()
|
||||||
|
AND column_name = 'use_Amcrest_API'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column use_Amcrest_API already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD COLUMN `use_Amcrest_API` BOOLEAN NOT NULL default false AFTER `ONVIF_Event_Listener`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
|
@ -0,0 +1,73 @@
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||||
|
AND table_name = 'Monitors'
|
||||||
|
AND column_name = 'ManufacturerId'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column ManufacturerId already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD `ManufacturerId` int(10) unsigned AFTER `StorageId`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
||||||
|
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE table_schema = DATABASE()
|
||||||
|
AND table_name = 'Monitors'
|
||||||
|
AND column_name = 'ManufacturerId'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'FOREIGN KEY for ManufacturerId already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD FOREIGN KEY (`ManufacturerId`) REFERENCES `Manufacturers` (Id)"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
||||||
|
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||||
|
AND table_name = 'Monitors'
|
||||||
|
AND column_name = 'ModelId'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column ModelId already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD `ModelId` int(10) unsigned AFTER `ManufacturerId`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
||||||
|
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE table_schema = DATABASE()
|
||||||
|
AND table_name = 'Monitors'
|
||||||
|
AND column_name = 'ModelId'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'FOREIGN KEY for ModelId already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD FOREIGN KEY (`ModelId`) REFERENCES `Models` (Id)"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
||||||
|
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||||
|
AND table_name = 'MonitorPresets'
|
||||||
|
AND column_name = 'ModelId'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column ModelId already exists in MonitorPresets'",
|
||||||
|
"ALTER TABLE `MonitorPresets` ADD `ModelId` int(10) unsigned AFTER `Id`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE table_schema = DATABASE()
|
||||||
|
AND table_name = 'MonitorPresets'
|
||||||
|
AND column_name = 'ModelId'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'FOREIGN KEY for ModelId already exists in MonitorPresets'",
|
||||||
|
"ALTER TABLE `MonitorPresets` ADD FOREIGN KEY (`ModelId`) REFERENCES `Models` (Id)"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
||||||
|
|
||||||
|
UPDATE `MonitorPresets` SET `ModelId`=(SELECT `Id` FROM `Models` WHERE `Name`='IP8M-T2499EW') WHERE `Name` like 'Amcrest, IP8M-T2499EW
|
||||||
|
%';
|
|
@ -0,0 +1,2 @@
|
||||||
|
source @PKGDATADIR@/db/manufacturers.sql
|
||||||
|
source @PKGDATADIR@/db/models.sql
|
|
@ -0,0 +1,31 @@
|
||||||
|
--
|
||||||
|
-- This update adds EventStartCommand and EventEndCommand
|
||||||
|
--
|
||||||
|
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*)
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE table_name = 'Monitors'
|
||||||
|
AND table_schema = DATABASE()
|
||||||
|
AND column_name = 'EventEndCommand'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column EventEndCommand already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD COLUMN `EventEndCommand` VARCHAR(255) NOT NULL DEFAULT '' AFTER `Triggers`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
||||||
|
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*)
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE table_name = 'Monitors'
|
||||||
|
AND table_schema = DATABASE()
|
||||||
|
AND column_name = 'EventStartCommand'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column EventStartCommand already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD COLUMN `EventStartCommand` VARCHAR(255) NOT NULL DEFAULT '' AFTER `Triggers`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
|
@ -0,0 +1,18 @@
|
||||||
|
--
|
||||||
|
-- Update Filters table to have a ExecuteInterval Column
|
||||||
|
--
|
||||||
|
|
||||||
|
SELECT 'Checking for ExecuteInterval in Filters';
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*)
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE table_name = 'Filters'
|
||||||
|
AND table_schema = DATABASE()
|
||||||
|
AND column_name = 'ExecuteInterval'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column ExecuteInterval already exists in Filters'",
|
||||||
|
"ALTER TABLE Filters ADD COLUMN `ExecuteInterval` int(10) unsigned NOT NULL default '60' AFTER `UserId`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
|
@ -0,0 +1,21 @@
|
||||||
|
/* Change Cause from varchar(32) to TEXT. We now include alarmed zone name */
|
||||||
|
ALTER TABLE `Events` MODIFY `Cause` TEXT;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Update Monitors table to have a ONVIF_Event_Listener Column
|
||||||
|
--
|
||||||
|
|
||||||
|
SELECT 'Checking for ONVIF_Event_Listener in Monitors';
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*)
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE table_name = 'Monitors'
|
||||||
|
AND table_schema = DATABASE()
|
||||||
|
AND column_name = 'ONVIF_Event_Listener'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column ONVIF_Event_Listener already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD COLUMN `ONVIF_Event_Listener` BOOLEAN NOT NULL default false AFTER `ONVIF_Options`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
|
@ -0,0 +1,18 @@
|
||||||
|
--
|
||||||
|
-- Update Monitors table to have JanusEnabled
|
||||||
|
--
|
||||||
|
|
||||||
|
SELECT 'Checking for JanusEnabled in Monitors';
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*)
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE table_name = 'Monitors'
|
||||||
|
AND table_schema = DATABASE()
|
||||||
|
AND column_name = 'JanusEnabled'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column JanusEnabled already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD COLUMN `JanusEnabled` BOOLEAN NOT NULL default false AFTER `DecodingEnabled`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
|
@ -0,0 +1,18 @@
|
||||||
|
--
|
||||||
|
-- Update Monitors table to have JanusEnabled
|
||||||
|
--
|
||||||
|
|
||||||
|
SELECT 'Checking for JanusAudioEnabled in Monitors';
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*)
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE table_name = 'Monitors'
|
||||||
|
AND table_schema = DATABASE()
|
||||||
|
AND column_name = 'JanusAudioEnabled'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column JanusAudioEnabled already exists in Monitors'",
|
||||||
|
"ALTER TABLE `Monitors` ADD COLUMN `JanusAudioEnabled` BOOLEAN NOT NULL default false AFTER `JanusEnabled`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
|
@ -1 +1 @@
|
||||||
Subproject commit cd7fd49becad6010a1b8466bfebbd93999a39878
|
Subproject commit eab32851421ffe54fec0229c3efc44c642bc8d46
|
|
@ -9,7 +9,7 @@
|
||||||
%global ceb_version 1.0-zm
|
%global ceb_version 1.0-zm
|
||||||
|
|
||||||
# RtspServer is configured as a git submodule
|
# RtspServer is configured as a git submodule
|
||||||
%global rtspserver_commit cd7fd49becad6010a1b8466bfebbd93999a39878
|
%global rtspserver_commit eab32851421ffe54fec0229c3efc44c642bc8d46
|
||||||
|
|
||||||
%global sslcert %{_sysconfdir}/pki/tls/certs/localhost.crt
|
%global sslcert %{_sysconfdir}/pki/tls/certs/localhost.crt
|
||||||
%global sslkey %{_sysconfdir}/pki/tls/private/localhost.key
|
%global sslkey %{_sysconfdir}/pki/tls/private/localhost.key
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
%global _hardened_build 1
|
%global _hardened_build 1
|
||||||
|
|
||||||
Name: zoneminder
|
Name: zoneminder
|
||||||
Version: 1.37.1
|
Version: 1.37.11
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: A camera monitoring and analysis tool
|
Summary: A camera monitoring and analysis tool
|
||||||
Group: System Environment/Daemons
|
Group: System Environment/Daemons
|
||||||
|
@ -354,7 +354,8 @@ ln -sf %{_sysconfdir}/zm/www/zoneminder.nginx.conf %{_sysconfdir}/zm/www/zonemin
|
||||||
%config(noreplace) %{_sysconfdir}/logrotate.d/zoneminder
|
%config(noreplace) %{_sysconfdir}/logrotate.d/zoneminder
|
||||||
|
|
||||||
%{_unitdir}/zoneminder.service
|
%{_unitdir}/zoneminder.service
|
||||||
%{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy
|
%{_datadir}/polkit-1/actions/com.zoneminder.*
|
||||||
|
%{_datadir}/polkit-1/rules.d/com.zoneminder.arp-scan.rules
|
||||||
%{_bindir}/zmsystemctl.pl
|
%{_bindir}/zmsystemctl.pl
|
||||||
|
|
||||||
%{_bindir}/zmaudit.pl
|
%{_bindir}/zmaudit.pl
|
||||||
|
|
|
@ -16,7 +16,6 @@ Build-Depends: debhelper (>= 11), sphinx-doc, python3-sphinx, dh-linktree, dh-ap
|
||||||
,libjpeg-turbo8-dev | libjpeg62-turbo-dev | libjpeg8-dev | libjpeg9-dev
|
,libjpeg-turbo8-dev | libjpeg62-turbo-dev | libjpeg8-dev | libjpeg9-dev
|
||||||
,libturbojpeg0-dev
|
,libturbojpeg0-dev
|
||||||
,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat
|
,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat
|
||||||
,libpcre3-dev
|
|
||||||
,libpolkit-gobject-1-dev
|
,libpolkit-gobject-1-dev
|
||||||
,libv4l-dev [!hurd-any]
|
,libv4l-dev [!hurd-any]
|
||||||
,libvlc-dev
|
,libvlc-dev
|
||||||
|
@ -32,6 +31,7 @@ Build-Depends: debhelper (>= 11), sphinx-doc, python3-sphinx, dh-linktree, dh-ap
|
||||||
,libvncserver-dev
|
,libvncserver-dev
|
||||||
,libfmt-dev
|
,libfmt-dev
|
||||||
,libjwt-gnutls-dev|libjwt-dev
|
,libjwt-gnutls-dev|libjwt-dev
|
||||||
|
,libgsoap-dev
|
||||||
|
|
||||||
Standards-Version: 4.5.0
|
Standards-Version: 4.5.0
|
||||||
Homepage: https://www.zoneminder.com/
|
Homepage: https://www.zoneminder.com/
|
||||||
|
@ -44,7 +44,8 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
||||||
,libswscale5|libswscale4
|
,libswscale5|libswscale4
|
||||||
,libswresample3|libswresample2
|
,libswresample3|libswresample2
|
||||||
,ffmpeg
|
,ffmpeg
|
||||||
,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl
|
,libcurl4, libcurl4-gnutls-dev
|
||||||
|
,libdatetime-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl
|
||||||
,libdbd-mysql-perl
|
,libdbd-mysql-perl
|
||||||
,libphp-serialization-perl
|
,libphp-serialization-perl
|
||||||
,libmodule-load-conditional-perl
|
,libmodule-load-conditional-perl
|
||||||
|
@ -72,12 +73,12 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
||||||
,policykit-1
|
,policykit-1
|
||||||
,rsyslog | system-log-daemon
|
,rsyslog | system-log-daemon
|
||||||
,zip
|
,zip
|
||||||
,libpcre3
|
|
||||||
,libcrypt-eksblowfish-perl
|
,libcrypt-eksblowfish-perl
|
||||||
,libdata-entropy-perl
|
,libdata-entropy-perl
|
||||||
,libvncclient1|libvncclient0
|
,libvncclient1|libvncclient0
|
||||||
,libfmt
|
,libfmt
|
||||||
,libjwt-gnutls0|libjwt0
|
,libjwt-gnutls0|libjwt0
|
||||||
|
,libgsoap-2.8.104|libgsoap-2.8.91|libgsoap-2.8.75|libgsoap-2.8.60|libgsoap10
|
||||||
|
|
||||||
Recommends: ${misc:Recommends}
|
Recommends: ${misc:Recommends}
|
||||||
,libapache2-mod-php | php-fpm
|
,libapache2-mod-php | php-fpm
|
||||||
|
|
|
@ -19,6 +19,7 @@ override_dh_auto_configure:
|
||||||
-DCMAKE_VERBOSE_MAKEFILE=ON \
|
-DCMAKE_VERBOSE_MAKEFILE=ON \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DBUILD_MAN=0 \
|
-DBUILD_MAN=0 \
|
||||||
|
-DZM_NO_PCRE=ON \
|
||||||
-DZM_CONFIG_DIR="/etc/zm" \
|
-DZM_CONFIG_DIR="/etc/zm" \
|
||||||
-DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \
|
-DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \
|
||||||
-DZM_RUNDIR="/run/zm" \
|
-DZM_RUNDIR="/run/zm" \
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
Description=ZoneMinder CCTV recording and surveillance system
|
Description=ZoneMinder CCTV recording and surveillance system
|
||||||
After=network.target mysql.service
|
After=network.target mysql.service
|
||||||
# Remarked out so that it will start ZM on machines that don't have mysql installed
|
# Remarked out so that it will start ZM on machines that don't have mysql installed
|
||||||
#Requires=mysql.service
|
# Override it by placing an override.conf in /etc/systemd/system/zoneminder.service.d
|
||||||
|
#BindsTo=mysql.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
#User=www-data
|
#User=www-data
|
||||||
|
|
|
@ -4,7 +4,7 @@ Debian
|
||||||
.. contents::
|
.. contents::
|
||||||
|
|
||||||
Easy Way: Debian 11 (Bullseye)
|
Easy Way: Debian 11 (Bullseye)
|
||||||
------------------------
|
------------------------------
|
||||||
|
|
||||||
This procedure will guide you through the installation of ZoneMinder on Debian 11 (Bullseye).
|
This procedure will guide you through the installation of ZoneMinder on Debian 11 (Bullseye).
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ Run the following commands.
|
||||||
sudo apt install mariadb-server
|
sudo apt install mariadb-server
|
||||||
sudo apt install zoneminder
|
sudo apt install zoneminder
|
||||||
|
|
||||||
When mariadb is installed for the first time, it doesn't add a password to the root user. Therefore, for security, it is recommended to run ``mysql secure installation``.
|
By default MariaDB uses `unix socket authentication`_, so no root user password is required (root MariaDB user access only available to local root Linux user). If you wish, you can set a root MariaDB password (and apply other security tweaks) by running `mariadb-secure-installation`_.
|
||||||
|
|
||||||
**Step 3:** Setup permissions for zm.conf
|
**Step 3:** Setup permissions for zm.conf
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ Add the following to the /etc/apt/sources.list.d/zoneminder.list file
|
||||||
|
|
||||||
You can do this using:
|
You can do this using:
|
||||||
|
|
||||||
.. code-block::
|
::
|
||||||
|
|
||||||
echo "deb https://zmrepo.zoneminder.com/debian/release-1.36 buster/" | sudo tee /etc/apt/sources.list.d/zoneminder.list
|
echo "deb https://zmrepo.zoneminder.com/debian/release-1.36 buster/" | sudo tee /etc/apt/sources.list.d/zoneminder.list
|
||||||
|
|
||||||
|
@ -337,3 +337,6 @@ Reload Apache to enable your changes and then start ZoneMinder.
|
||||||
sudo systemctl start zoneminder
|
sudo systemctl start zoneminder
|
||||||
|
|
||||||
You are now ready to go with ZoneMinder. Open a browser and type either ``localhost/zm`` one the local machine or ``{IP-OF-ZM-SERVER}/zm`` if you connect from a remote computer.
|
You are now ready to go with ZoneMinder. Open a browser and type either ``localhost/zm`` one the local machine or ``{IP-OF-ZM-SERVER}/zm`` if you connect from a remote computer.
|
||||||
|
|
||||||
|
.. _unix socket authentication: https://mariadb.com/kb/en/authentication-plugin-unix-socket/
|
||||||
|
.. _mariadb-secure-installation: https://mariadb.com/kb/en/mysql_secure_installation/
|
||||||
|
|
|
@ -7,6 +7,8 @@ configure_file(logrotate.conf.in "${CMAKE_CURRENT_BINARY_DIR}/logrotate.conf" @O
|
||||||
configure_file(syslog.conf.in "${CMAKE_CURRENT_BINARY_DIR}/syslog.conf" @ONLY)
|
configure_file(syslog.conf.in "${CMAKE_CURRENT_BINARY_DIR}/syslog.conf" @ONLY)
|
||||||
configure_file(com.zoneminder.systemctl.policy.in "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.policy" @ONLY)
|
configure_file(com.zoneminder.systemctl.policy.in "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.policy" @ONLY)
|
||||||
configure_file(com.zoneminder.systemctl.rules.in "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.rules" @ONLY)
|
configure_file(com.zoneminder.systemctl.rules.in "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.rules" @ONLY)
|
||||||
|
configure_file(com.zoneminder.arp-scan.policy.in "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.arp-scan.policy" @ONLY)
|
||||||
|
configure_file(com.zoneminder.arp-scan.rules.in "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.arp-scan.rules" @ONLY)
|
||||||
configure_file(zoneminder.service.in "${CMAKE_CURRENT_BINARY_DIR}/zoneminder.service" @ONLY)
|
configure_file(zoneminder.service.in "${CMAKE_CURRENT_BINARY_DIR}/zoneminder.service" @ONLY)
|
||||||
configure_file(zoneminder-tmpfiles.conf.in "${CMAKE_CURRENT_BINARY_DIR}/zoneminder-tmpfiles.conf" @ONLY)
|
configure_file(zoneminder-tmpfiles.conf.in "${CMAKE_CURRENT_BINARY_DIR}/zoneminder-tmpfiles.conf" @ONLY)
|
||||||
configure_file(zoneminder.desktop.in "${CMAKE_CURRENT_BINARY_DIR}/zoneminder.desktop" @ONLY)
|
configure_file(zoneminder.desktop.in "${CMAKE_CURRENT_BINARY_DIR}/zoneminder.desktop" @ONLY)
|
||||||
|
@ -19,6 +21,8 @@ configure_file(zm-sudo.in "${CMAKE_CURRENT_BINARY_DIR}/zm-sudo" @ONLY)
|
||||||
if(WITH_SYSTEMD)
|
if(WITH_SYSTEMD)
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.policy" DESTINATION "${PC_POLKIT_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/polkit-1/actions")
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.policy" DESTINATION "${PC_POLKIT_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/polkit-1/actions")
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.rules" DESTINATION "${PC_POLKIT_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/polkit-1/rules.d")
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.rules" DESTINATION "${PC_POLKIT_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/polkit-1/rules.d")
|
||||||
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.arp-scan.policy" DESTINATION "${PC_POLKIT_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/polkit-1/actions")
|
||||||
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.arp-scan.rules" DESTINATION "${PC_POLKIT_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/polkit-1/rules.d")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zoneminder.desktop" DESTINATION "${CMAKE_INSTALL_DATADIR}/applications")
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zoneminder.desktop" DESTINATION "${CMAKE_INSTALL_DATADIR}/applications")
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE policyconfig PUBLIC
|
||||||
|
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
||||||
|
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
|
||||||
|
<policyconfig>
|
||||||
|
|
||||||
|
<vendor>The ZoneMinder Project</vendor>
|
||||||
|
<vendor_url>http://www.zoneminder.com/</vendor_url>
|
||||||
|
|
||||||
|
<action id="com.zoneminder.policykit.pkexec.run-arp-scan">
|
||||||
|
<description>Allow the ZoneMinder webuser to run arp-scan</description>
|
||||||
|
<message>The ZoneMinder webuser is trusted to run arp-scan</message>
|
||||||
|
<defaults>
|
||||||
|
<allow_any>yes</allow_any>
|
||||||
|
<allow_inactive>yes</allow_inactive>
|
||||||
|
<allow_active>yes</allow_active>
|
||||||
|
</defaults>
|
||||||
|
<annotate key="org.freedesktop.policykit.exec.path">/usr/sbin/arp-scan</annotate>
|
||||||
|
</action>
|
||||||
|
|
||||||
|
</policyconfig>
|
|
@ -0,0 +1,7 @@
|
||||||
|
polkit.addRule(function(action, subject) {
|
||||||
|
if (action.id == "com.zoneminder.policykit.pkexec.run-arp-scan" &&
|
||||||
|
subject.user != "@WEB_USER@") {
|
||||||
|
return polkit.Result.NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,50 @@
|
||||||
|
general: {
|
||||||
|
configs_folder = "/usr/local/etc/janus" # Configuration files folder
|
||||||
|
plugins_folder = "/usr/local/lib/janus/plugins" # Plugins folder
|
||||||
|
transports_folder = "/usr/local/lib/janus/transports" # Transports folder
|
||||||
|
events_folder = "/usr/local/lib/janus/events" # Event handlers folder
|
||||||
|
loggers_folder = "/usr/local/lib/janus/loggers" # External loggers folder
|
||||||
|
debug_level = 4 # Debug/logging level, valid values are 0-7
|
||||||
|
admin_secret = "janusoverlord" # String that all Janus requests must contain
|
||||||
|
protected_folders = [
|
||||||
|
"/bin",
|
||||||
|
"/boot",
|
||||||
|
"/dev",
|
||||||
|
"/etc",
|
||||||
|
"/initrd",
|
||||||
|
"/lib",
|
||||||
|
"/lib32",
|
||||||
|
"/lib64",
|
||||||
|
"/proc",
|
||||||
|
"/sbin",
|
||||||
|
"/sys",
|
||||||
|
"/usr",
|
||||||
|
"/var",
|
||||||
|
"/opt/janus/bin",
|
||||||
|
"/opt/janus/etc",
|
||||||
|
"/opt/janus/include",
|
||||||
|
"/opt/janus/lib",
|
||||||
|
"/opt/janus/lib32",
|
||||||
|
"/opt/janus/lib64",
|
||||||
|
"/opt/janus/sbin"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
media: {
|
||||||
|
#ipv6 = true
|
||||||
|
#ipv6_linklocal = true
|
||||||
|
rtp_port_range = "20000-40000"
|
||||||
|
}
|
||||||
|
nat: {
|
||||||
|
nice_debug = false
|
||||||
|
ignore_mdns = true
|
||||||
|
keep_private_host = true
|
||||||
|
ice_ignore_list = "vmnet"
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins: {
|
||||||
|
disable = "libjanus_audiobridge.so,libjanus_echotest.so,libjanus_recordplay.so,libjanus_sip.so,libjanus_textroom.so,libjanus_videocall.so,libjanus_videoroom.so,libjanus_voicemail.so,
|
||||||
|
libjanus_nosip.so"
|
||||||
|
}
|
||||||
|
transports: {
|
||||||
|
disable = "libjanus_rabbitmq.so, libjanus_pfunix.so,libjanus_websockets.so"
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
general: {
|
||||||
|
admin_key = "supersecret"
|
||||||
|
rtp_port_range = "20000-40000"
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
general: {
|
||||||
|
json = "indented" # Whether the JSON messages should be indented (default),
|
||||||
|
base_path = "/janus" # Base path to bind to in the web server (plain HTTP only)
|
||||||
|
http = true # Whether to enable the plain HTTP interface
|
||||||
|
port = 8088 # Web server HTTP port
|
||||||
|
https = false # Whether to enable HTTPS (default=false)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Janus can also expose an admin/monitor endpoint, to allow you to check
|
||||||
|
# which sessions are up, which handles they're managing, their current
|
||||||
|
# status and so on. This provides a useful aid when debugging potential
|
||||||
|
# issues in Janus. The configuration is pretty much the same as the one
|
||||||
|
# already presented above for the webserver stuff, as the API is very
|
||||||
|
# similar: choose the base bath for the admin/monitor endpoint (/admin
|
||||||
|
# by default), ports, etc. Besides, you can specify
|
||||||
|
# a secret that must be provided in all requests as a crude form of
|
||||||
|
# authorization mechanism, and partial or full source IPs if you want to
|
||||||
|
# limit access basing on IP addresses. For security reasons, this
|
||||||
|
# endpoint is disabled by default, enable it by setting admin_http=true.
|
||||||
|
admin: {
|
||||||
|
admin_base_path = "/admin" # Base path to bind to in the admin/monitor web server (plain HTTP only)
|
||||||
|
admin_http = false # Whether to enable the plain HTTP interface
|
||||||
|
admin_port = 7088 # Admin/monitor web server HTTP port
|
||||||
|
admin_https = false # Whether to enable HTTPS (default=false)
|
||||||
|
}
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=ZoneMinder CCTV recording and security system
|
Description=ZoneMinder CCTV recording and security system
|
||||||
After=network.target mysqld.service httpd.service
|
After=network.target mysqld.service httpd.service janus.service
|
||||||
Requires=mysqld.service httpd.service
|
Requires=mysqld.service httpd.service janus.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
User=@WEB_USER@
|
User=@WEB_USER@
|
||||||
|
|
|
@ -156,7 +156,7 @@ sub loadConfigFromDB {
|
||||||
print("Error: unable to load options from database: $DBI::errstr\n");
|
print("Error: unable to load options from database: $DBI::errstr\n");
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
my $sql = "select * from Config";
|
my $sql = 'SELECT * FROM Config';
|
||||||
my $sth = $dbh->prepare_cached( $sql )
|
my $sth = $dbh->prepare_cached( $sql )
|
||||||
or croak( "Can't prepare '$sql': ".$dbh->errstr() );
|
or croak( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||||
my $res = $sth->execute()
|
my $res = $sth->execute()
|
||||||
|
@ -203,7 +203,7 @@ sub saveConfigToDB {
|
||||||
my $res = $dbh->do( $sql )
|
my $res = $dbh->do( $sql )
|
||||||
or croak( "Can't do '$sql': ".$dbh->errstr() );
|
or croak( "Can't do '$sql': ".$dbh->errstr() );
|
||||||
|
|
||||||
$sql = "replace into Config set Id = ?, Name = ?, Value = ?, Type = ?, DefaultValue = ?, Hint = ?, Pattern = ?, Format = ?, Prompt = ?, Help = ?, Category = ?, Readonly = ?, Requires = ?";
|
$sql = "replace into Config set Id = ?, Name = ?, Value = ?, Type = ?, DefaultValue = ?, Hint = ?, Pattern = ?, Format = ?, Prompt = ?, Help = ?, Category = ?, Readonly = ?, Private = ?, Requires = ?";
|
||||||
my $sth = $dbh->prepare_cached( $sql )
|
my $sth = $dbh->prepare_cached( $sql )
|
||||||
or croak( "Can't prepare '$sql': ".$dbh->errstr() );
|
or croak( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||||
foreach my $option ( @options ) {
|
foreach my $option ( @options ) {
|
||||||
|
@ -240,6 +240,7 @@ sub saveConfigToDB {
|
||||||
$option->{help},
|
$option->{help},
|
||||||
$option->{category},
|
$option->{category},
|
||||||
$option->{readonly} ? 1 : 0,
|
$option->{readonly} ? 1 : 0,
|
||||||
|
$option->{private} ? 1 : 0,
|
||||||
$option->{db_requires}
|
$option->{db_requires}
|
||||||
) or croak("Can't execute when updating config entry $$option{name}: ".$sth->errstr() );
|
) or croak("Can't execute when updating config entry $$option{name}: ".$sth->errstr() );
|
||||||
} # end foreach option
|
} # end foreach option
|
||||||
|
|
|
@ -304,6 +304,7 @@ our @options = (
|
||||||
{ name=>'ZM_AUTH_RELAY', value=>'hashed' }
|
{ name=>'ZM_AUTH_RELAY', value=>'hashed' }
|
||||||
],
|
],
|
||||||
type => $types{string},
|
type => $types{string},
|
||||||
|
private => 1,
|
||||||
category => 'system',
|
category => 'system',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -370,6 +371,28 @@ our @options = (
|
||||||
type => $types{boolean},
|
type => $types{boolean},
|
||||||
category => 'system',
|
category => 'system',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name => 'ZM_JANUS_SECRET',
|
||||||
|
default => '',
|
||||||
|
description => 'Password for Janus streaming administration.',
|
||||||
|
help => q`This value should be set to a secure password,
|
||||||
|
and match the admin_key value in janus.plugin.streaming.config.
|
||||||
|
`,
|
||||||
|
type => $types{string},
|
||||||
|
category => 'system',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name => 'ZM_JANUS_PATH',
|
||||||
|
default => '',
|
||||||
|
description => 'URL for Janus HTTP/S port',
|
||||||
|
help => q`Janus requires HTTP/S communication to administer
|
||||||
|
and initiate h.264 streams. If left blank, this will default to
|
||||||
|
the ZM hostname, port 8088/janus. This setting is particularly
|
||||||
|
useful for putting janus behind a reverse proxy.
|
||||||
|
`,
|
||||||
|
type => $types{string},
|
||||||
|
category => 'system',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name => 'ZM_ENABLE_CSRF_MAGIC',
|
name => 'ZM_ENABLE_CSRF_MAGIC',
|
||||||
default => 'yes',
|
default => 'yes',
|
||||||
|
@ -462,6 +485,7 @@ our @options = (
|
||||||
{name=>'ZM_OPT_USE_GOOG_RECAPTCHA', value=>'yes'}
|
{name=>'ZM_OPT_USE_GOOG_RECAPTCHA', value=>'yes'}
|
||||||
],
|
],
|
||||||
type => $types{string},
|
type => $types{string},
|
||||||
|
private => 1,
|
||||||
category => 'system',
|
category => 'system',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -477,6 +501,7 @@ our @options = (
|
||||||
{name=>'ZM_OPT_USE_GOOG_RECAPTCHA', value=>'yes'}
|
{name=>'ZM_OPT_USE_GOOG_RECAPTCHA', value=>'yes'}
|
||||||
],
|
],
|
||||||
type => $types{string},
|
type => $types{string},
|
||||||
|
private => 1,
|
||||||
category => 'system',
|
category => 'system',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -51,11 +51,21 @@ sub open {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
$self->loadMonitor();
|
$self->loadMonitor();
|
||||||
if ( $self->{Monitor}->{ControlAddress} !~ /^\w+:\/\// ) {
|
|
||||||
# Has no scheme at the beginning, so won't parse as a URI
|
if ($self->{Monitor}->{ControlAddress} and ($self->{Monitor}->{ControlAddress} ne 'user:pass@ip')) {
|
||||||
$self->{Monitor}->{ControlAddress} = 'http://'.$self->{Monitor}->{ControlAddress};
|
Debug("Getting connection details from Control Address " . $self->{Monitor}->{ControlAddress});
|
||||||
}
|
if ( $self->{Monitor}->{ControlAddress} !~ /^\w+:\/\// ) {
|
||||||
$uri = URI->new($self->{Monitor}->{ControlAddress});
|
# Has no scheme at the beginning, so won't parse as a URI
|
||||||
|
$self->{Monitor}->{ControlAddress} = 'http://'.$self->{Monitor}->{ControlAddress};
|
||||||
|
}
|
||||||
|
$uri = URI->new($self->{Monitor}->{ControlAddress});
|
||||||
|
} elsif ($self->{Monitor}->{Path}) {
|
||||||
|
Debug("Getting connection details from Path " . $self->{Monitor}->{Path});
|
||||||
|
$uri = URI->new($self->{Monitor}->{Path});
|
||||||
|
$uri->scheme('http');
|
||||||
|
$uri->port(80);
|
||||||
|
$uri->path('');
|
||||||
|
}
|
||||||
|
|
||||||
use LWP::UserAgent;
|
use LWP::UserAgent;
|
||||||
$self->{ua} = LWP::UserAgent->new;
|
$self->{ua} = LWP::UserAgent->new;
|
||||||
|
@ -64,6 +74,7 @@ sub open {
|
||||||
$self->{state} = 'closed';
|
$self->{state} = 'closed';
|
||||||
|
|
||||||
my ( $username, $password, $host ) = ( $uri->authority() =~ /^([^:]+):([^@]*)@(.+)$/ );
|
my ( $username, $password, $host ) = ( $uri->authority() =~ /^([^:]+):([^@]*)@(.+)$/ );
|
||||||
|
Debug("Have username: $username password: $password host: $host from authority:" . $uri->authority());
|
||||||
|
|
||||||
$uri->userinfo(undef);
|
$uri->userinfo(undef);
|
||||||
|
|
||||||
|
@ -75,40 +86,47 @@ sub open {
|
||||||
# test auth
|
# test auth
|
||||||
my $res = $self->{ua}->get($uri->canonical().$url);
|
my $res = $self->{ua}->get($uri->canonical().$url);
|
||||||
|
|
||||||
if ( $res->is_success ) {
|
if ($res->is_success) {
|
||||||
if ( $res->content() ne "Properties.PTZ.PTZ=yes\n" ) {
|
if ($res->content() ne "Properties.PTZ.PTZ=yes\n") {
|
||||||
Warning('Response suggests that camera doesn\'t support PTZ. Content:('.$res->content().')');
|
Warning('Response suggests that camera doesn\'t support PTZ. Content:('.$res->content().')');
|
||||||
}
|
}
|
||||||
$self->{state} = 'open';
|
$self->{state} = 'open';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if ($res->status_line() eq '404 Not Found') {
|
||||||
|
#older style
|
||||||
|
$url = 'axis-cgi/com/ptz.cgi';
|
||||||
|
$res = $self->{ua}->get($uri->canonical().$url);
|
||||||
|
Debug("Result from getting ".$uri->canonical().$url . ':' . $res->status_line());
|
||||||
|
}
|
||||||
|
|
||||||
if ( $res->status_line() eq '401 Unauthorized' ) {
|
if ($res->status_line() eq '401 Unauthorized') {
|
||||||
|
|
||||||
my $headers = $res->headers();
|
my $headers = $res->headers();
|
||||||
foreach my $k ( keys %$headers ) {
|
foreach my $k ( keys %$headers ) {
|
||||||
Debug("Initial Header $k => $$headers{$k}");
|
Debug("Initial Header $k => $$headers{$k}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $$headers{'www-authenticate'} ) {
|
if ( $$headers{'www-authenticate'} ) {
|
||||||
my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
|
foreach my $auth_header ( ref $$headers{'www-authenticate'} eq 'ARRAY' ? @{$$headers{'www-authenticate'}} : ($$headers{'www-authenticate'})) {
|
||||||
if ( $tokens =~ /\w+="([^"]+)"/i ) {
|
my ( $auth, $tokens ) = $auth_header =~ /^(\w+)\s+(.*)$/;
|
||||||
if ( $realm ne $1 ) {
|
if ( $tokens =~ /\w+="([^"]+)"/i ) {
|
||||||
$realm = $1;
|
if ( $realm ne $1 ) {
|
||||||
$self->{ua}->credentials($uri->host_port(), $realm, $username, $password);
|
$realm = $1;
|
||||||
$res = $self->{ua}->get($uri->canonical().$url);
|
$self->{ua}->credentials($uri->host_port(), $realm, $username, $password);
|
||||||
if ( $res->is_success() ) {
|
$res = $self->{ua}->get($uri->canonical().$url);
|
||||||
Info("Auth succeeded after setting realm to $realm. You can set this value in the Control Device field to speed up connections and remove these log entries.");
|
if ( $res->is_success() ) {
|
||||||
$self->{state} = 'open';
|
Info("Auth succeeded after setting realm to $realm. You can set this value in the Control Device field to speed up connections and remove these log entries.");
|
||||||
return;
|
$self->{state} = 'open';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Error('Authentication still failed after updating REALM status: '.$res->status_line);
|
||||||
|
} else {
|
||||||
|
Error('Authentication failed, not a REALM problem');
|
||||||
}
|
}
|
||||||
Error('Authentication still failed after updating REALM status: '.$res->status_line);
|
|
||||||
} else {
|
} else {
|
||||||
Error('Authentication failed, not a REALM problem');
|
Error('Failed to match realm in tokens');
|
||||||
}
|
} # end if
|
||||||
} else {
|
} # end foreach auth header
|
||||||
Error('Failed to match realm in tokens');
|
|
||||||
} # end if
|
|
||||||
} else {
|
} else {
|
||||||
Debug('No headers line');
|
Debug('No headers line');
|
||||||
} # end if headers
|
} # end if headers
|
||||||
|
|
|
@ -194,7 +194,6 @@ sub getCamParams {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#autoStop
|
|
||||||
#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
|
#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
|
||||||
sub autoStop {
|
sub autoStop {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
@ -202,13 +201,19 @@ sub autoStop {
|
||||||
|
|
||||||
if ( $autostop ) {
|
if ( $autostop ) {
|
||||||
Debug('Auto Stop');
|
Debug('Auto Stop');
|
||||||
my $cmd = 'onvif/PTZ';
|
|
||||||
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>' . $profileToken . '</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
|
|
||||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
|
||||||
usleep($autostop);
|
usleep($autostop);
|
||||||
|
|
||||||
|
my $cmd = 'onvif/PTZ';
|
||||||
|
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||||
|
|
||||||
|
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">'.((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '').'<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>' . $profileToken . '</ProfileToken><Velocity><PanTilt x="0" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||||
|
$self->sendCmd($cmd, $msg, $content_type);
|
||||||
|
|
||||||
|
# Reported to not work, so superceded by the cmd above
|
||||||
|
$msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>' . $profileToken . '</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
|
||||||
$self->sendCmd($cmd, $msg, $content_type);
|
$self->sendCmd($cmd, $msg, $content_type);
|
||||||
}
|
}
|
||||||
}
|
} # end sub autoStop
|
||||||
|
|
||||||
# Reset the Camera
|
# Reset the Camera
|
||||||
sub reset {
|
sub reset {
|
||||||
|
|
|
@ -374,6 +374,25 @@ sub reset {
|
||||||
$self->sendCmdPost($url,$cmd);
|
$self->sendCmdPost($url,$cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub reboot {
|
||||||
|
my $self = shift;
|
||||||
|
Debug('Camera Reboot');
|
||||||
|
$self->sendCmdPost('/eng/admin/reboot.cgi', { reboot => 'true' });
|
||||||
|
#$referer = 'http://'.$HI->ip().'/eng/admin/tools_default.cgi';
|
||||||
|
#$initial_url = $HI->ip().'/eng/admin/tools_default.cgi';
|
||||||
|
}
|
||||||
|
|
||||||
|
sub ping {
|
||||||
|
return -1 if ! $ADDRESS;
|
||||||
|
|
||||||
|
require Net::Ping;
|
||||||
|
|
||||||
|
my $p = Net::Ping->new();
|
||||||
|
my $rv = $p->ping($ADDRESS);
|
||||||
|
$p->close();
|
||||||
|
return $rv;
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
__END__
|
__END__
|
||||||
|
|
||||||
|
|
|
@ -273,6 +273,9 @@ sub zmDbDo {
|
||||||
if ( ! defined $rows ) {
|
if ( ! defined $rows ) {
|
||||||
$sql =~ s/\?/'%s'/;
|
$sql =~ s/\?/'%s'/;
|
||||||
Error(sprintf("Failed $sql :", @_).$dbh->errstr());
|
Error(sprintf("Failed $sql :", @_).$dbh->errstr());
|
||||||
|
} elsif ( ZoneMinder::Logger::logLevel() > INFO ) {
|
||||||
|
$sql =~ s/\?/'%s'/;
|
||||||
|
Debug(sprintf("Succeeded $sql : $rows rows affected", @_));
|
||||||
}
|
}
|
||||||
return $rows;
|
return $rows;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ require Date::Parse;
|
||||||
require POSIX;
|
require POSIX;
|
||||||
use Date::Format qw(time2str);
|
use Date::Format qw(time2str);
|
||||||
use Time::HiRes qw(gettimeofday tv_interval stat);
|
use Time::HiRes qw(gettimeofday tv_interval stat);
|
||||||
|
use Scalar::Util qw(looks_like_number);
|
||||||
|
|
||||||
#our @ISA = qw(ZoneMinder::Object);
|
#our @ISA = qw(ZoneMinder::Object);
|
||||||
use parent qw(ZoneMinder::Object);
|
use parent qw(ZoneMinder::Object);
|
||||||
|
@ -601,7 +602,7 @@ sub CopyTo {
|
||||||
# First determine if we can move it to the dest.
|
# First determine if we can move it to the dest.
|
||||||
# We do this before bothering to lock the event
|
# We do this before bothering to lock the event
|
||||||
my ( $NewPath ) = ( $NewStorage->Path() =~ /^(.*)$/ ); # De-taint
|
my ( $NewPath ) = ( $NewStorage->Path() =~ /^(.*)$/ ); # De-taint
|
||||||
if ( ! $$NewStorage{Id} ) {
|
if ( ! looks_like_number($$NewStorage{Id}) ) {
|
||||||
return 'New storage does not have an id. Moving will not happen.';
|
return 'New storage does not have an id. Moving will not happen.';
|
||||||
} elsif ( $$NewStorage{Id} == $$self{StorageId} ) {
|
} elsif ( $$NewStorage{Id} == $$self{StorageId} ) {
|
||||||
return 'Event is already located at ' . $NewPath;
|
return 'Event is already located at ' . $NewPath;
|
||||||
|
@ -733,19 +734,22 @@ sub MoveTo {
|
||||||
|
|
||||||
my $was_in_transaction = !$ZoneMinder::Database::dbh->{AutoCommit};
|
my $was_in_transaction = !$ZoneMinder::Database::dbh->{AutoCommit};
|
||||||
$ZoneMinder::Database::dbh->begin_work() if !$was_in_transaction;
|
$ZoneMinder::Database::dbh->begin_work() if !$was_in_transaction;
|
||||||
$self->lock_and_load(); # The fact that we are in a transaction might not imply locking
|
if (!$self->lock_and_load()) {
|
||||||
|
Warning('Unable to lock event record '.$$self{Id}); # The fact that we are in a transaction might not imply locking
|
||||||
|
$ZoneMinder::Database::dbh->commit() if !$was_in_transaction;
|
||||||
|
return 'Unable to lock event record';
|
||||||
|
}
|
||||||
|
|
||||||
my $OldStorage = $self->Storage(undef);
|
my $OldStorage = $self->Storage(undef);
|
||||||
|
|
||||||
my $error = $self->CopyTo($NewStorage);
|
my $error = $self->CopyTo($NewStorage);
|
||||||
return $error if $error;
|
if (!$error) {
|
||||||
|
# Succeeded in copying all files, so we may now update the Event.
|
||||||
|
$$self{StorageId} = $$NewStorage{Id};
|
||||||
|
$self->Storage($NewStorage);
|
||||||
|
$error .= $self->save();
|
||||||
|
|
||||||
# Succeeded in copying all files, so we may now update the Event.
|
# Going to leave it to upper layer as to whether we rollback or not
|
||||||
$$self{StorageId} = $$NewStorage{Id};
|
}
|
||||||
$self->Storage($NewStorage);
|
|
||||||
$error .= $self->save();
|
|
||||||
|
|
||||||
# Going to leave it to upper layer as to whether we rollback or not
|
|
||||||
$ZoneMinder::Database::dbh->commit() if !$was_in_transaction;
|
$ZoneMinder::Database::dbh->commit() if !$was_in_transaction;
|
||||||
return $error if $error;
|
return $error if $error;
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ $primary_key = 'Id';
|
||||||
%fields = map { $_ => $_ } qw(
|
%fields = map { $_ => $_ } qw(
|
||||||
Id
|
Id
|
||||||
Name
|
Name
|
||||||
|
ExecuteInterval
|
||||||
Query_json
|
Query_json
|
||||||
AutoArchive
|
AutoArchive
|
||||||
AutoUnarchive
|
AutoUnarchive
|
||||||
|
@ -106,7 +107,6 @@ sub Execute {
|
||||||
$sql =~ s/zmSystemLoad/$load/g;
|
$sql =~ s/zmSystemLoad/$load/g;
|
||||||
}
|
}
|
||||||
|
|
||||||
$sql .= ' FOR UPDATE' if $$self{LockRows};
|
|
||||||
|
|
||||||
Debug("Filter::Execute SQL ($sql)");
|
Debug("Filter::Execute SQL ($sql)");
|
||||||
my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql)
|
my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql)
|
||||||
|
@ -230,8 +230,8 @@ sub Sql {
|
||||||
# PostCondition, so no further SQL
|
# PostCondition, so no further SQL
|
||||||
} else {
|
} else {
|
||||||
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
|
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
|
||||||
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) {
|
# Empty value will result in () from split
|
||||||
|
foreach my $temp_value ( $stripped_value ne '' ? split( /["'\s]*?,["'\s]*?/, $stripped_value ) : $stripped_value ) {
|
||||||
if ( $term->{attr} eq 'AlarmedZoneId' ) {
|
if ( $term->{attr} eq 'AlarmedZoneId' ) {
|
||||||
$value = '(SELECT * FROM Stats WHERE EventId=E.Id AND Score > 0 AND ZoneId='.$value.')';
|
$value = '(SELECT * FROM Stats WHERE EventId=E.Id AND Score > 0 AND ZoneId='.$value.')';
|
||||||
} elsif ( $term->{attr} =~ /^MonitorName/ ) {
|
} elsif ( $term->{attr} =~ /^MonitorName/ ) {
|
||||||
|
@ -250,7 +250,8 @@ sub Sql {
|
||||||
$$self{Server} = new ZoneMinder::Server($temp_value);
|
$$self{Server} = new ZoneMinder::Server($temp_value);
|
||||||
}
|
}
|
||||||
} elsif ( $term->{attr} eq 'StorageId' ) {
|
} elsif ( $term->{attr} eq 'StorageId' ) {
|
||||||
$value = "'$temp_value'";
|
# Empty means NULL, otherwise must be an integer
|
||||||
|
$value = $temp_value ne '' ? int($temp_value) : 'NULL';
|
||||||
$$self{Storage} = new ZoneMinder::Storage($temp_value);
|
$$self{Storage} = new ZoneMinder::Storage($temp_value);
|
||||||
} elsif ( $term->{attr} eq 'Name'
|
} elsif ( $term->{attr} eq 'Name'
|
||||||
|| $term->{attr} eq 'Cause'
|
|| $term->{attr} eq 'Cause'
|
||||||
|
@ -322,7 +323,7 @@ sub Sql {
|
||||||
$self->{Sql} .= ' IS NOT '.$value;
|
$self->{Sql} .= ' IS NOT '.$value;
|
||||||
} elsif ( $term->{op} eq '=[]' or $term->{op} eq 'IN' ) {
|
} elsif ( $term->{op} eq '=[]' or $term->{op} eq 'IN' ) {
|
||||||
$self->{Sql} .= ' IN ('.join(',', @value_list).")";
|
$self->{Sql} .= ' IN ('.join(',', @value_list).")";
|
||||||
} elsif ( $term->{op} eq '![]' ) {
|
} elsif ( $term->{op} eq '![]' or $term->{op} eq 'NOT IN') {
|
||||||
$self->{Sql} .= ' NOT IN ('.join(',', @value_list).')';
|
$self->{Sql} .= ' NOT IN ('.join(',', @value_list).')';
|
||||||
} elsif ( $term->{op} eq 'LIKE' ) {
|
} elsif ( $term->{op} eq 'LIKE' ) {
|
||||||
$self->{Sql} .= ' LIKE '.$value;
|
$self->{Sql} .= ' LIKE '.$value;
|
||||||
|
@ -370,10 +371,7 @@ sub Sql {
|
||||||
if ( @auto_terms ) {
|
if ( @auto_terms ) {
|
||||||
$sql .= ' AND ( '.join(' or ', @auto_terms).' )';
|
$sql .= ' AND ( '.join(' or ', @auto_terms).' )';
|
||||||
}
|
}
|
||||||
if ( !$filter_expr->{sort_field} ) {
|
|
||||||
$filter_expr->{sort_field} = 'StartDateTime';
|
|
||||||
$filter_expr->{sort_asc} = 0;
|
|
||||||
}
|
|
||||||
my $sort_column = '';
|
my $sort_column = '';
|
||||||
if ( $filter_expr->{sort_field} eq 'Id' ) {
|
if ( $filter_expr->{sort_field} eq 'Id' ) {
|
||||||
$sort_column = 'E.Id';
|
$sort_column = 'E.Id';
|
||||||
|
@ -405,14 +403,21 @@ sub Sql {
|
||||||
$sort_column = 'E.MaxScore';
|
$sort_column = 'E.MaxScore';
|
||||||
} elsif ( $filter_expr->{sort_field} eq 'DiskSpace' ) {
|
} elsif ( $filter_expr->{sort_field} eq 'DiskSpace' ) {
|
||||||
$sort_column = 'E.DiskSpace';
|
$sort_column = 'E.DiskSpace';
|
||||||
} else {
|
} elsif ( $filter_expr->{sort_field} ne '' ) {
|
||||||
$sort_column = 'E.StartDateTime';
|
$sort_column = 'E.'.$filter_expr->{sort_field};
|
||||||
}
|
}
|
||||||
my $sort_order = $filter_expr->{sort_asc} ? 'ASC' : 'DESC';
|
if ( $sort_column ne '' ) {
|
||||||
$sql .= ' ORDER BY '.$sort_column.' '.$sort_order;
|
$sql .= ' ORDER BY '.$sort_column.' '.($filter_expr->{sort_asc} ? 'ASC' : 'DESC');
|
||||||
if ( $filter_expr->{limit} ) {
|
}
|
||||||
|
if ($filter_expr->{limit}) {
|
||||||
$sql .= ' LIMIT 0,'.$filter_expr->{limit};
|
$sql .= ' LIMIT 0,'.$filter_expr->{limit};
|
||||||
}
|
}
|
||||||
|
if ($$self{LockRows}) {
|
||||||
|
$sql .= ' FOR UPDATE';
|
||||||
|
if ($filter_expr->{skip_locked}) {
|
||||||
|
$sql .= ' SKIP LOCKED';
|
||||||
|
}
|
||||||
|
}
|
||||||
$self->{Sql} = $sql;
|
$self->{Sql} = $sql;
|
||||||
} # end if has Sql
|
} # end if has Sql
|
||||||
return $self->{Sql};
|
return $self->{Sql};
|
||||||
|
|
|
@ -28,6 +28,7 @@ our %EXPORT_TAGS = (
|
||||||
makePath
|
makePath
|
||||||
jsonEncode
|
jsonEncode
|
||||||
jsonDecode
|
jsonDecode
|
||||||
|
jsonLoad
|
||||||
systemStatus
|
systemStatus
|
||||||
packageControl
|
packageControl
|
||||||
daemonControl
|
daemonControl
|
||||||
|
@ -536,6 +537,23 @@ sub jsonDecode {
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub jsonLoad {
|
||||||
|
my $file = shift;
|
||||||
|
my $json = undef;
|
||||||
|
eval {
|
||||||
|
require File::Slurp;
|
||||||
|
my $contents = File::Slurp::read_file($file);
|
||||||
|
if (!$contents) {
|
||||||
|
Error("No contents for $file");
|
||||||
|
return $json;
|
||||||
|
}
|
||||||
|
require JSON;
|
||||||
|
$json = JSON::decode_json($contents);
|
||||||
|
};
|
||||||
|
Error($@) if $@;
|
||||||
|
return $json;
|
||||||
|
}
|
||||||
|
|
||||||
sub parseNameEqualsValueToHash {
|
sub parseNameEqualsValueToHash {
|
||||||
my %settings;
|
my %settings;
|
||||||
foreach my $line ( split ( /\r?\n/, $_[0] ) ) {
|
foreach my $line ( split ( /\r?\n/, $_[0] ) ) {
|
||||||
|
|
|
@ -638,6 +638,7 @@ sub logInit( ;@ ) {
|
||||||
$logger = ZoneMinder::Logger->new() if !$logger;
|
$logger = ZoneMinder::Logger->new() if !$logger;
|
||||||
$logger->initialise(%options);
|
$logger->initialise(%options);
|
||||||
logSetSignal();
|
logSetSignal();
|
||||||
|
return $logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub logReinit {
|
sub logReinit {
|
||||||
|
|
|
@ -42,6 +42,7 @@ our @ISA = qw(Exporter ZoneMinder::Base);
|
||||||
# will save memory.
|
# will save memory.
|
||||||
our %EXPORT_TAGS = (
|
our %EXPORT_TAGS = (
|
||||||
constants => [ qw(
|
constants => [ qw(
|
||||||
|
STATE_UNKNOWN
|
||||||
STATE_IDLE
|
STATE_IDLE
|
||||||
STATE_PREALARM
|
STATE_PREALARM
|
||||||
STATE_ALARM
|
STATE_ALARM
|
||||||
|
@ -98,11 +99,12 @@ our $VERSION = $ZoneMinder::Base::VERSION;
|
||||||
use ZoneMinder::Config qw(:all);
|
use ZoneMinder::Config qw(:all);
|
||||||
use ZoneMinder::Logger qw(:all);
|
use ZoneMinder::Logger qw(:all);
|
||||||
|
|
||||||
use constant STATE_IDLE => 0;
|
use constant STATE_UNKNOWN => 0;
|
||||||
use constant STATE_PREALARM => 1;
|
use constant STATE_IDLE => 1;
|
||||||
use constant STATE_ALARM => 2;
|
use constant STATE_PREALARM => 2;
|
||||||
use constant STATE_ALERT => 3;
|
use constant STATE_ALARM => 3;
|
||||||
use constant STATE_TAPE => 4;
|
use constant STATE_ALERT => 4;
|
||||||
|
use constant STATE_TAPE => 5;
|
||||||
|
|
||||||
use constant ACTION_GET => 1;
|
use constant ACTION_GET => 1;
|
||||||
use constant ACTION_SET => 2;
|
use constant ACTION_SET => 2;
|
||||||
|
|
|
@ -56,6 +56,14 @@ $serial = $primary_key = 'Id';
|
||||||
Enabled
|
Enabled
|
||||||
LinkedMonitors
|
LinkedMonitors
|
||||||
Triggers
|
Triggers
|
||||||
|
EventStartCommand
|
||||||
|
EventEndCommand
|
||||||
|
ONVIF_URL
|
||||||
|
ONVIF_Username
|
||||||
|
ONVIF_Password
|
||||||
|
ONVIF_Options
|
||||||
|
ONVIF_Event_Listener
|
||||||
|
use_Amcrest_API
|
||||||
Device
|
Device
|
||||||
Channel
|
Channel
|
||||||
Format
|
Format
|
||||||
|
@ -133,6 +141,9 @@ $serial = $primary_key = 'Id';
|
||||||
DefaultCodec
|
DefaultCodec
|
||||||
Latitude
|
Latitude
|
||||||
Longitude
|
Longitude
|
||||||
|
RTSPServer
|
||||||
|
RTSPStreamName
|
||||||
|
Importance
|
||||||
);
|
);
|
||||||
|
|
||||||
%defaults = (
|
%defaults = (
|
||||||
|
@ -241,20 +252,26 @@ sub control {
|
||||||
my $command = shift;
|
my $command = shift;
|
||||||
my $process = shift;
|
my $process = shift;
|
||||||
|
|
||||||
if ( $command eq 'stop' or $command eq 'restart' ) {
|
if ($command eq 'stop' or $command eq 'restart') {
|
||||||
if ( $process ) {
|
if ($process) {
|
||||||
`/usr/bin/zmdc.pl stop $process -m $$monitor{Id}`;
|
ZoneMinder::General::runCommand("zmdc.pl stop $process -m $$monitor{Id}");
|
||||||
} else {
|
} else {
|
||||||
`/usr/bin/zmdc.pl stop zma -m $$monitor{Id}`;
|
if ($monitor->{Type} eq 'Local') {
|
||||||
`/usr/bin/zmdc.pl stop zmc -m $$monitor{Id}`;
|
ZoneMinder::General::runCommand('zmdc.pl stop zmc -d '.$monitor->{Device});
|
||||||
|
} else {
|
||||||
|
ZoneMinder::General::runCommand('zmdc.pl stop zmc -m '.$monitor->{Id});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( $command eq 'start' or $command eq 'restart' ) {
|
if ( $command eq 'start' or $command eq 'restart' ) {
|
||||||
if ( $process ) {
|
if ( $process ) {
|
||||||
`/usr/bin/zmdc.pl start $process -m $$monitor{Id}`;
|
ZoneMinder::General::runCommand("zmdc.pl start $process -m $$monitor{Id}");
|
||||||
} else {
|
} else {
|
||||||
`/usr/bin/zmdc.pl start zmc -m $$monitor{Id}`;
|
if ($monitor->{Type} eq 'Local') {
|
||||||
`/usr/bin/zmdc.pl start zma -m $$monitor{Id}`;
|
ZoneMinder::General::runCommand('zmdc.pl start zmc -d '.$monitor->{Device});
|
||||||
|
} else {
|
||||||
|
ZoneMinder::General::runCommand('zmdc.pl start zmc -m '.$monitor->{Id});
|
||||||
|
}
|
||||||
} # end if
|
} # end if
|
||||||
}
|
}
|
||||||
} # end sub control
|
} # end sub control
|
||||||
|
@ -326,18 +343,30 @@ sub resumeMotionDetection {
|
||||||
|
|
||||||
sub Control {
|
sub Control {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
if ( ! exists $$self{Control}) {
|
if (!exists $$self{Control}) {
|
||||||
require ZoneMinder::Control;
|
if ($$self{ControlId}) {
|
||||||
my $Control = ZoneMinder::Control->find_one(Id=>$$self{ControlId});
|
require ZoneMinder::Control;
|
||||||
if ($Control) {
|
my $Control = ZoneMinder::Control->find_one(Id=>$$self{ControlId});
|
||||||
require Module::Load::Conditional;
|
if ($Control) {
|
||||||
if (!Module::Load::Conditional::can_load(modules => {'ZoneMinder::Control::'.$$Control{Protocol} => undef})) {
|
my $Protocol = $$Control{Protocol};
|
||||||
Error("Can't load ZoneMinder::Control::$$Control{Protocol}\n$Module::Load::Conditional::ERROR");
|
|
||||||
return undef;
|
if (!$Protocol) {
|
||||||
|
Error("No protocol set in control $$Control{Id}, trying Name $$Control{Name}");
|
||||||
|
$Protocol = $$Control{Name};
|
||||||
|
}
|
||||||
|
require Module::Load::Conditional;
|
||||||
|
if (!Module::Load::Conditional::can_load(modules => {'ZoneMinder::Control::'.$Protocol => undef})) {
|
||||||
|
Error("Can't load ZoneMinder::Control::$Protocol\n$Module::Load::Conditional::ERROR");
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
bless $Control, 'ZoneMinder::Control::'.$Protocol;
|
||||||
|
$$Control{MonitorId} = $$self{Id};
|
||||||
|
$$self{Control} = $Control;
|
||||||
|
} else {
|
||||||
|
Error("Unable to load control for control $$self{ControlId} for monitor $$self{Id}");
|
||||||
}
|
}
|
||||||
bless $Control, 'ZoneMinder::Control::'.$$Control{Protocol};
|
} else {
|
||||||
$$Control{MonitorId} = $$self{Id};
|
Info("No ControlId set in monitor $$self{Id}")
|
||||||
$$self{Control} = $Control;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $$self{Control};
|
return $$self{Control};
|
||||||
|
|
|
@ -639,9 +639,9 @@ $log->debug("Have array for $k $$search{$k}") if DEBUG_ALL;
|
||||||
|
|
||||||
if ( ! ( $db_field =~ /\?/ ) ) {
|
if ( ! ( $db_field =~ /\?/ ) ) {
|
||||||
if ( @{$$search{$k}} != 1 ) {
|
if ( @{$$search{$k}} != 1 ) {
|
||||||
push @where, $db_field .' IN ('.join(',', map {'?'} @{$$search{$k}} ) . ')';
|
push @where, '`'.$db_field .'` IN ('.join(',', map {'?'} @{$$search{$k}} ) . ')';
|
||||||
} else {
|
} else {
|
||||||
push @where, $db_field.'=?';
|
push @where, '`'.$db_field.'`=?';
|
||||||
} # end if
|
} # end if
|
||||||
} else {
|
} else {
|
||||||
$log->debug("Have question ? for $k $$search{$k} $db_field") if DEBUG_ALL;
|
$log->debug("Have question ? for $k $$search{$k} $db_field") if DEBUG_ALL;
|
||||||
|
@ -656,10 +656,10 @@ $log->debug("Have question ? for $k $$search{$k} $db_field") if DEBUG_ALL;
|
||||||
foreach my $p_k ( keys %{$$search{$k}} ) {
|
foreach my $p_k ( keys %{$$search{$k}} ) {
|
||||||
my $v = $$search{$k}{$p_k};
|
my $v = $$search{$k}{$p_k};
|
||||||
if ( ref $v eq 'ARRAY' ) {
|
if ( ref $v eq 'ARRAY' ) {
|
||||||
push @where, $db_field.' IN ('.join(',', map {'?'} @{$v} ) . ')';
|
push @where, '`'.$db_field.'` IN ('.join(',', map {'?'} @{$v} ) . ')';
|
||||||
push @values, $p_k, @{$v};
|
push @values, $p_k, @{$v};
|
||||||
} else {
|
} else {
|
||||||
push @where, $db_field.'=?';
|
push @where, '`'.$db_field.'`=?';
|
||||||
push @values, $p_k, $v;
|
push @values, $p_k, $v;
|
||||||
} # end if
|
} # end if
|
||||||
} # end foreach p_k
|
} # end foreach p_k
|
||||||
|
@ -667,7 +667,7 @@ $log->debug("Have question ? for $k $$search{$k} $db_field") if DEBUG_ALL;
|
||||||
push @where, $db_field.' IS NULL';
|
push @where, $db_field.' IS NULL';
|
||||||
} else {
|
} else {
|
||||||
if ( ! ( $db_field =~ /\?/ ) ) {
|
if ( ! ( $db_field =~ /\?/ ) ) {
|
||||||
push @where, $db_field .'=?';
|
push @where, '`'.$db_field .'`=?';
|
||||||
} else {
|
} else {
|
||||||
push @where, $db_field;
|
push @where, $db_field;
|
||||||
}
|
}
|
||||||
|
|
|
@ -429,10 +429,20 @@ sub start {
|
||||||
# It's not running, or at least it's not been started by us
|
# It's not running, or at least it's not been started by us
|
||||||
$process = { daemon=>$daemon, args=>\@args, command=>$command, keepalive=>!undef };
|
$process = { daemon=>$daemon, args=>\@args, command=>$command, keepalive=>!undef };
|
||||||
} elsif ( $process->{pid} && $pid_hash{$process->{pid}} ) {
|
} elsif ( $process->{pid} && $pid_hash{$process->{pid}} ) {
|
||||||
dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' already running at "
|
if ($process->{term_sent_at}) {
|
||||||
|
dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' was told to term at "
|
||||||
|
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{term_sent_at}))
|
||||||
|
.", pid = $process->{pid}\n"
|
||||||
|
);
|
||||||
|
$process->{keepalive} = !undef;
|
||||||
|
$process->{delay} = 0;
|
||||||
|
delete $terminating_processes{$command};
|
||||||
|
} else {
|
||||||
|
dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' already running at "
|
||||||
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
|
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
|
||||||
.", pid = $process->{pid}\n"
|
.", pid = $process->{pid}\n"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,7 +533,7 @@ sub send_stop {
|
||||||
."\n"
|
."\n"
|
||||||
);
|
);
|
||||||
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
|
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
|
||||||
return();
|
return ();
|
||||||
}
|
}
|
||||||
|
|
||||||
my $pid = $process->{pid};
|
my $pid = $process->{pid};
|
||||||
|
@ -586,7 +596,7 @@ sub check_for_processes_to_kill {
|
||||||
|
|
||||||
sub stop {
|
sub stop {
|
||||||
my ( $daemon, @args ) = @_;
|
my ( $daemon, @args ) = @_;
|
||||||
my $command = join(' ', $daemon, @args );
|
my $command = join(' ', $daemon, @args);
|
||||||
my $process = $cmd_hash{$command};
|
my $process = $cmd_hash{$command};
|
||||||
if ( !$process ) {
|
if ( !$process ) {
|
||||||
dPrint(ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'");
|
dPrint(ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'");
|
||||||
|
|
|
@ -21,28 +21,6 @@
|
||||||
#
|
#
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
|
|
||||||
=head1 NAME
|
|
||||||
|
|
||||||
zmfilter.pl - ZoneMinder tool to filter events
|
|
||||||
|
|
||||||
=head1 SYNOPSIS
|
|
||||||
|
|
||||||
zmfilter.pl [-f <filter name>,--filter=<filter name>] [--filter_id=<filter id>] | -v, --version
|
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
|
||||||
|
|
||||||
This script continuously monitors the recorded events for the given
|
|
||||||
monitor and applies any filters which would delete and/or upload
|
|
||||||
matching events.
|
|
||||||
|
|
||||||
=head1 OPTIONS
|
|
||||||
|
|
||||||
|
|
||||||
-f{filter name}, --filter={filter name} - The name of a specific filter to run
|
|
||||||
--filter_id={filter id} - The id of a specific filter to run
|
|
||||||
-v, --version - Print ZoneMinder version
|
|
||||||
|
|
||||||
=cut
|
|
||||||
use strict;
|
use strict;
|
||||||
use bytes;
|
use bytes;
|
||||||
|
|
||||||
|
@ -160,10 +138,9 @@ $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
|
||||||
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
|
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
|
||||||
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
||||||
|
|
||||||
my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL};
|
|
||||||
my $event_id = 0;
|
my $event_id = 0;
|
||||||
|
|
||||||
if ( !EVENT_PATH ) {
|
if (!EVENT_PATH) {
|
||||||
Error('No event path defined. Config was '.$Config{ZM_DIR_EVENTS});
|
Error('No event path defined. Config was '.$Config{ZM_DIR_EVENTS});
|
||||||
die;
|
die;
|
||||||
}
|
}
|
||||||
|
@ -195,26 +172,37 @@ if ( ! ( $filter_name or $filter_id ) ) {
|
||||||
my @filters;
|
my @filters;
|
||||||
my $last_action = 0;
|
my $last_action = 0;
|
||||||
|
|
||||||
while( !$zm_terminate ) {
|
while (!$zm_terminate) {
|
||||||
|
my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL};
|
||||||
my $now = time;
|
my $now = time;
|
||||||
if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) {
|
if (($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY}) {
|
||||||
Debug('Reloading filters');
|
Debug('Reloading filters');
|
||||||
$last_action = $now;
|
$last_action = $now;
|
||||||
@filters = getFilters({ Name=>$filter_name, Id=>$filter_id });
|
@filters = getFilters({ Name=>$filter_name, Id=>$filter_id });
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach my $filter ( @filters ) {
|
foreach my $filter (@filters) {
|
||||||
last if $zm_terminate;
|
last if $zm_terminate;
|
||||||
if ( $$filter{Concurrent} and ! ( $filter_id or $filter_name ) ) {
|
|
||||||
|
my $elapsed = ($now - ($$filter{last_ran} ? $$filter{last_ran} : 0));
|
||||||
|
if ($$filter{last_ran} and ($elapsed < $$filter{ExecuteInterval})) {
|
||||||
|
my $filter_delay = $$filter{ExecuteInterval} - ($now - $$filter{last_ran});
|
||||||
|
$delay = $filter_delay if $filter_delay < $delay;
|
||||||
|
Debug("Setting delay to $delay because ExecuteInterval=$$filter{ExecuteInterval} and $elapsed have elapsed");
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($$filter{Concurrent} and !($filter_id or $filter_name)) {
|
||||||
my ( $proc ) = $0 =~ /(\S+)/;
|
my ( $proc ) = $0 =~ /(\S+)/;
|
||||||
my ( $id ) = $$filter{Id} =~ /(\d+)/;
|
my ( $id ) = $$filter{Id} =~ /(\d+)/;
|
||||||
Debug("Running concurrent filter process $proc --filter_id $$filter{Id} => $id for $$filter{Name}");
|
Debug("Running concurrent filter process $proc --filter_id $$filter{Id} => $id for $$filter{Name}");
|
||||||
|
|
||||||
system(qq`$proc --filter "$$filter{Name}" &`);
|
system(qq`$proc --filter_id $id &`);
|
||||||
} else {
|
} else {
|
||||||
checkFilter($filter);
|
checkFilter($filter);
|
||||||
|
$$filter{last_ran} = $now;
|
||||||
}
|
}
|
||||||
}
|
} # end foreach filter
|
||||||
|
|
||||||
last if (!$daemon and ($filter_name or $filter_id)) or $zm_terminate;
|
last if (!$daemon and ($filter_name or $filter_id)) or $zm_terminate;
|
||||||
|
|
||||||
|
@ -384,11 +372,6 @@ sub checkFilter {
|
||||||
} # end if AutoCopy
|
} # end if AutoCopy
|
||||||
|
|
||||||
if ( $filter->{UpdateDiskSpace} ) {
|
if ( $filter->{UpdateDiskSpace} ) {
|
||||||
if ( $$filter{LockRows} ) {
|
|
||||||
$ZoneMinder::Database::dbh->begin_work();
|
|
||||||
$Event->lock_and_load();
|
|
||||||
}
|
|
||||||
|
|
||||||
my $old_diskspace = $$Event{DiskSpace};
|
my $old_diskspace = $$Event{DiskSpace};
|
||||||
my $new_diskspace = $Event->DiskSpace(undef);
|
my $new_diskspace = $Event->DiskSpace(undef);
|
||||||
|
|
||||||
|
@ -481,15 +464,18 @@ sub generateImage {
|
||||||
my $event_path = $Event->Path();
|
my $event_path = $Event->Path();
|
||||||
my $capture_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-capture.jpg', $event_path, $frame->{FrameId});
|
my $capture_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-capture.jpg', $event_path, $frame->{FrameId});
|
||||||
my $analyse_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-analyse.jpg', $event_path, $frame->{FrameId}) if $analyse;
|
my $analyse_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-analyse.jpg', $event_path, $frame->{FrameId}) if $analyse;
|
||||||
my $video_path = sprintf('%s/%d-video.mp4', $event_path, $Event->{Id});
|
my $video_path = sprintf('%s/%s', $event_path, $Event->{DefaultVideo});
|
||||||
my $image_path = '';
|
my $image_path = '';
|
||||||
|
|
||||||
# check if the image file exists. If the file doesn't exist and we use H264 try to extract it from .mp4 video
|
# check if the image file exists. If the file doesn't exist and we use H264 try to extract it from .mp4 video
|
||||||
if ( $analyse && -r $analyse_image_path ) {
|
if ( $analyse && -r $analyse_image_path ) {
|
||||||
|
Debug("Using analysis and jpeg exists $analyse_image_path");
|
||||||
$image_path = $analyse_image_path;
|
$image_path = $analyse_image_path;
|
||||||
} elsif ( -r $capture_image_path ) {
|
} elsif ( -r $capture_image_path ) {
|
||||||
|
Debug("Using captures and jpeg exists $capture_image_path");
|
||||||
$image_path = $capture_image_path;
|
$image_path = $capture_image_path;
|
||||||
} elsif ( -r $video_path ) {
|
} elsif ( -r $video_path ) {
|
||||||
|
Debug("mp4 exists $video_path");
|
||||||
my $command ="ffmpeg -nostdin -ss $$frame{Delta} -i '$video_path' -frames:v 1 '$capture_image_path'";
|
my $command ="ffmpeg -nostdin -ss $$frame{Delta} -i '$video_path' -frames:v 1 '$capture_image_path'";
|
||||||
#$command = "ffmpeg -y -v 0 -i $video_path -vf 'select=gte(n\\,$$frame{FrameId}),setpts=PTS-STARTPTS' -vframes 1 -f image2 $capture_image_path";
|
#$command = "ffmpeg -y -v 0 -i $video_path -vf 'select=gte(n\\,$$frame{FrameId}),setpts=PTS-STARTPTS' -vframes 1 -f image2 $capture_image_path";
|
||||||
my $output = qx($command);
|
my $output = qx($command);
|
||||||
|
@ -503,6 +489,8 @@ sub generateImage {
|
||||||
} else {
|
} else {
|
||||||
$image_path = $capture_image_path;
|
$image_path = $capture_image_path;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Debug("No files found at $analyse_image_path, $capture_image_path or $video_path");
|
||||||
}
|
}
|
||||||
return $image_path;
|
return $image_path;
|
||||||
}
|
}
|
||||||
|
@ -740,7 +728,7 @@ sub substituteTags {
|
||||||
if ( -e $path ) {
|
if ( -e $path ) {
|
||||||
push @$attachments_ref, { type=>'image/jpeg', path=>$path };
|
push @$attachments_ref, { type=>'image/jpeg', path=>$path };
|
||||||
} else {
|
} else {
|
||||||
Warning("Path to first image does not exist at $path");
|
Warning("Path to first image does not exist at $path for image $first_alarm_frame");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1051,9 +1039,7 @@ sub executeCommand {
|
||||||
my $filter = shift;
|
my $filter = shift;
|
||||||
my $Event = shift;
|
my $Event = shift;
|
||||||
|
|
||||||
my $event_path = $Event->Path();
|
my $command = $filter->{AutoExecuteCmd}.' '.$Event->Path();
|
||||||
|
|
||||||
my $command = $filter->{AutoExecuteCmd}.' '.$event_path;
|
|
||||||
$command = substituteTags($command, $filter, $Event);
|
$command = substituteTags($command, $filter, $Event);
|
||||||
|
|
||||||
Info("Executing '$command'");
|
Info("Executing '$command'");
|
||||||
|
@ -1063,15 +1049,37 @@ sub executeCommand {
|
||||||
chomp($output);
|
chomp($output);
|
||||||
Debug("Output: $output");
|
Debug("Output: $output");
|
||||||
}
|
}
|
||||||
if ( $status ) {
|
if ($status) {
|
||||||
Error("Command '$command' exited with status: $status");
|
Error("Command '$command' exited with status: $status");
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
my $sql = 'UPDATE `Events` SET `Executed` = 1 WHERE `Id` = ?';
|
ZoneMinder::Database::zmSQLExecute('UPDATE `Events` SET `Executed` = 1 WHERE `Id` = ?', $Event->{Id});
|
||||||
my $sth = $dbh->prepare_cached($sql)
|
|
||||||
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
|
|
||||||
my $res = $sth->execute( $Event->{Id} )
|
|
||||||
or Fatal("Unable to execute '$sql': ".$dbh->errstr());
|
|
||||||
}
|
}
|
||||||
return( 1 );
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
__END__
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
zmfilter.pl - ZoneMinder tool to select events and perform actions on them
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
zmfilter.pl [-f <filter name>,--filter=<filter name>] [--filter_id=<filter id>] [--daemon] | -v, --version
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
This script performs a specified database query to select recorded events and performs specified actions on them
|
||||||
|
such as email reporting, deleting, moving, etc. If the --daemon option is given it will remain resident, repeating
|
||||||
|
the query and applying actions. This is normally managed by zmdc.pl however it can be used manually as well.
|
||||||
|
|
||||||
|
=head1 OPTIONS
|
||||||
|
|
||||||
|
-f{filter name}, --filter={filter name} - The name of a specific filter to run
|
||||||
|
--filter_id={filter id} - The id of a specific filter to run
|
||||||
|
--daemon - Causes zmfilter.pl to stay running endlessly repeating the filter(s).
|
||||||
|
-v, --version - Print ZoneMinder version
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
|
@ -263,7 +263,10 @@ sub countQuery {
|
||||||
sub getMonitorRef {
|
sub getMonitorRef {
|
||||||
my $dbh = shift;
|
my $dbh = shift;
|
||||||
|
|
||||||
my $sql = 'SELECT `Id`,`Name`,`Type`,`Function`,`Width`,`Height`,`Colours`,`MaxFPS`,`AlarmMaxFPS` FROM `Monitors`';
|
my $sql = 'SELECT `Id`,`Name`,`Type`,`Function`,`Width`,`Height`,`Colours`,`MaxFPS`,`AlarmMaxFPS`,
|
||||||
|
(SELECT Name FROM Manufacturers WHERE Manufacturers.Id = ManufacturerId),
|
||||||
|
(SELECT Name FROM Models WHERE Models.Id = ModelId)
|
||||||
|
FROM `Monitors`';
|
||||||
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||||
my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
|
my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
|
||||||
my $arrayref = $sth->fetchall_arrayref({});
|
my $arrayref = $sth->fetchall_arrayref({});
|
||||||
|
|
|
@ -42,6 +42,7 @@ use constant SELECT_TIMEOUT => 0.25;
|
||||||
|
|
||||||
@EXTRA_PERL_LIB@
|
@EXTRA_PERL_LIB@
|
||||||
use ZoneMinder;
|
use ZoneMinder;
|
||||||
|
use ZoneMinder::Monitor;
|
||||||
use ZoneMinder::Trigger::Channel::Inet;
|
use ZoneMinder::Trigger::Channel::Inet;
|
||||||
use ZoneMinder::Trigger::Channel::Unix;
|
use ZoneMinder::Trigger::Channel::Unix;
|
||||||
use ZoneMinder::Trigger::Channel::Serial;
|
use ZoneMinder::Trigger::Channel::Serial;
|
||||||
|
@ -166,13 +167,9 @@ while (!$zm_terminate) {
|
||||||
foreach my $connection ( values(%spawned_connections) ) {
|
foreach my $connection ( values(%spawned_connections) ) {
|
||||||
if ( vec($rout, $connection->fileno(), 1) ) {
|
if ( vec($rout, $connection->fileno(), 1) ) {
|
||||||
Debug('Got input from spawned connection '
|
Debug('Got input from spawned connection '
|
||||||
.$connection->name()
|
.$connection->name().' ('.$connection->fileno().')');
|
||||||
.' ('
|
|
||||||
.$connection->fileno()
|
|
||||||
.')'
|
|
||||||
);
|
|
||||||
my $messages = $connection->getMessages();
|
my $messages = $connection->getMessages();
|
||||||
if ( defined($messages) ) {
|
if (defined($messages)) {
|
||||||
foreach my $message ( @$messages ) {
|
foreach my $message ( @$messages ) {
|
||||||
handleMessage($connection, $message);
|
handleMessage($connection, $message);
|
||||||
}
|
}
|
||||||
|
@ -199,34 +196,28 @@ while (!$zm_terminate) {
|
||||||
# Check polled connections
|
# Check polled connections
|
||||||
foreach my $connection ( @in_poll_connections ) {
|
foreach my $connection ( @in_poll_connections ) {
|
||||||
my $messages = $connection->getMessages();
|
my $messages = $connection->getMessages();
|
||||||
if ( defined($messages) ) {
|
if (defined($messages)) {
|
||||||
foreach my $message ( @$messages ) {
|
foreach my $message (@$messages) { handleMessage($connection, $message) };
|
||||||
handleMessage($connection, $message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check for alarms that might have happened
|
# Check for alarms that might have happened
|
||||||
my @out_messages;
|
my @out_messages;
|
||||||
foreach my $monitor ( values %monitors ) {
|
foreach my $monitor ( values %monitors ) {
|
||||||
|
if (!$monitor->connect()) {
|
||||||
if ( ! zmMemVerify($monitor) ) {
|
|
||||||
# Our attempt to verify the memory handle failed. We should reload the monitors.
|
# Our attempt to verify the memory handle failed. We should reload the monitors.
|
||||||
# Don't need to zmMemInvalidate because the monitor reload will do it.
|
# Don't need to zmMemInvalidate because the monitor reload will do it.
|
||||||
|
Debug("Failed connect, putting on reloads");
|
||||||
push @needsReload, $monitor;
|
push @needsReload, $monitor;
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
|
|
||||||
my ( $state, $last_event ) = zmMemRead( $monitor,
|
my ($state, $last_event) = zmMemRead($monitor, [
|
||||||
[
|
|
||||||
'shared_data:state',
|
'shared_data:state',
|
||||||
'shared_data:last_event'
|
'shared_data:last_event'
|
||||||
]
|
]);
|
||||||
);
|
|
||||||
|
|
||||||
#print( "$monitor->{Id}: S:$state, LE:$last_event" );
|
if ($state == STATE_ALARM or $state == STATE_ALERT) {
|
||||||
#print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}" );
|
|
||||||
if ( $state == STATE_ALARM or $state == STATE_ALERT ) {
|
|
||||||
# In alarm state
|
# In alarm state
|
||||||
if ( !defined($monitor->{LastEvent})
|
if ( !defined($monitor->{LastEvent})
|
||||||
or ($last_event != $monitor->{LastEvent})
|
or ($last_event != $monitor->{LastEvent})
|
||||||
|
@ -255,6 +246,7 @@ while (!$zm_terminate) {
|
||||||
}
|
}
|
||||||
$monitor->{LastState} = $state;
|
$monitor->{LastState} = $state;
|
||||||
$monitor->{LastEvent} = $last_event;
|
$monitor->{LastEvent} = $last_event;
|
||||||
|
$monitor->disconnect();
|
||||||
} # end foreach monitor
|
} # end foreach monitor
|
||||||
|
|
||||||
foreach my $connection ( @out_connections ) {
|
foreach my $connection ( @out_connections ) {
|
||||||
|
@ -304,15 +296,22 @@ while (!$zm_terminate) {
|
||||||
|
|
||||||
# Reload all monitors from the dB every MONITOR_RELOAD_INTERVAL
|
# Reload all monitors from the dB every MONITOR_RELOAD_INTERVAL
|
||||||
if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL ) {
|
if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL ) {
|
||||||
foreach my $monitor ( values(%monitors) ) {
|
|
||||||
zmMemInvalidate( $monitor ); # Free up any used memory handle
|
|
||||||
}
|
|
||||||
loadMonitors();
|
loadMonitors();
|
||||||
@needsReload = (); # We just reloaded all monitors so no need reload a specific monitor
|
@needsReload = (); # We just reloaded all monitors so no need reload a specific monitor
|
||||||
# If we have NOT just reloaded all monitors, reload a specific monitor if its shared mem changed
|
# If we have NOT just reloaded all monitors, reload a specific monitor if its shared mem changed
|
||||||
} elsif ( @needsReload ) {
|
} elsif (@needsReload) {
|
||||||
foreach my $monitor ( @needsReload ) {
|
foreach my $monitor (@needsReload) {
|
||||||
loadMonitor($monitor);
|
$monitor = $monitors{$monitor->Id()} = ZoneMinder::Monitor->find_one(Id=>$monitor->Id());
|
||||||
|
if ( $$monitor{Function} eq 'None' ) {
|
||||||
|
delete $monitors{$monitor->Id()};
|
||||||
|
} elsif ( $Config{ZM_SERVER_ID} and ($$monitor{ServerId} != $Config{ZM_SERVER_ID})) {
|
||||||
|
delete $monitors{$monitor->Id()};
|
||||||
|
} else {
|
||||||
|
if ($monitor->connect()) {
|
||||||
|
$monitor->{LastState} = zmGetMonitorState($monitor);
|
||||||
|
$monitor->{LastEvent} = zmGetLastEvent($monitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@needsReload = ();
|
@needsReload = ();
|
||||||
}
|
}
|
||||||
|
@ -323,40 +322,21 @@ while (!$zm_terminate) {
|
||||||
Info('Trigger daemon exiting');
|
Info('Trigger daemon exiting');
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
sub loadMonitor {
|
|
||||||
my $monitor = shift;
|
|
||||||
|
|
||||||
Debug('Loading monitor '.$monitor);
|
|
||||||
zmMemInvalidate($monitor);
|
|
||||||
|
|
||||||
if ( zmMemVerify($monitor) ) { # This will re-init shared memory
|
|
||||||
$monitor->{LastState} = zmGetMonitorState($monitor);
|
|
||||||
$monitor->{LastEvent} = zmGetLastEvent($monitor);
|
|
||||||
}
|
|
||||||
} # end sub loadMonitor
|
|
||||||
|
|
||||||
sub loadMonitors {
|
sub loadMonitors {
|
||||||
$monitor_reload_time = time();
|
$monitor_reload_time = time();
|
||||||
|
|
||||||
my %new_monitors = ();
|
%monitors = ();
|
||||||
|
|
||||||
my $sql = 'SELECT * FROM `Monitors`
|
foreach my $monitor ( ZoneMinder::Monitor->find(
|
||||||
WHERE find_in_set( `Function`, \'Modect,Mocord,Nodect,Record\' )'.
|
Function=>['Modect','Mocord','Nodect','Record'],
|
||||||
( $Config{ZM_SERVER_ID} ? ' AND `ServerId`=?' : '' )
|
($Config{ZM_SERVER_ID} ? (ServerId=>$Config{ZM_SERVER_ID}) : ()),
|
||||||
;
|
)) {
|
||||||
my $sth = $dbh->prepare_cached( $sql )
|
if ($monitor->connect()) { # This will re-init shared memory
|
||||||
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
|
|
||||||
my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () )
|
|
||||||
or Fatal( "Can't execute: ".$sth->errstr() );
|
|
||||||
|
|
||||||
while ( my $monitor = $sth->fetchrow_hashref() ) {
|
|
||||||
if ( zmMemVerify($monitor) ) { # This will re-init shared memory
|
|
||||||
$monitor->{LastState} = zmGetMonitorState($monitor);
|
$monitor->{LastState} = zmGetMonitorState($monitor);
|
||||||
$monitor->{LastEvent} = zmGetLastEvent($monitor);
|
$monitor->{LastEvent} = zmGetLastEvent($monitor);
|
||||||
}
|
}
|
||||||
$new_monitors{$monitor->{Id}} = $monitor;
|
$monitors{$monitor->{Id}} = $monitor;
|
||||||
} # end while fetchrow
|
} # end while fetchrow
|
||||||
%monitors = %new_monitors;
|
|
||||||
} # end sub loadMonitors
|
} # end sub loadMonitors
|
||||||
|
|
||||||
sub handleMessage {
|
sub handleMessage {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#
|
#
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
# ZoneMinder Update Script, $Date$, $Revision$
|
# ZoneMinder Update Script
|
||||||
# Copyright (C) 2001-2008 Philip Coombes
|
# Copyright (C) 2001-2008 Philip Coombes
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or
|
# This program is free software; you can redistribute it and/or
|
||||||
|
@ -31,29 +31,30 @@ zmupdate.pl -c,--check | -f,--freshen | -v<version>,--version=<version> [-u <dbu
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
This script just checks what the most recent release of ZoneMinder is
|
This script checks what the most recent release of ZoneMinder is
|
||||||
at the the moment. It will eventually be responsible for applying and
|
at the the moment by downloading https://update.zoneminder.com/version.txt.
|
||||||
configuring upgrades etc, including on the fly upgrades.
|
It can also apply and configure upgrades etc, including on the fly upgrades.
|
||||||
|
|
||||||
=head1 OPTIONS
|
=head1 OPTIONS
|
||||||
|
|
||||||
-c, --check - Check for updated versions of ZoneMinder
|
-c, --check - Check for updated versions of ZoneMinder.
|
||||||
-f, --freshen - Freshen the configuration in the database. Equivalent of old zmconfig.pl -noi
|
If not interactive zmupdate.pl will stay running, checking every hour.
|
||||||
--migrate-events - Update database structures as per USE_DEEP_STORAGE setting.
|
If interactive will try once, print out result and quit.
|
||||||
-v <version>, --version=<version> - Force upgrade to the current version from <version>
|
-f, --freshen - Freshen the configuration in the database. Equivalent of old zmconfig.pl -noi
|
||||||
-u <dbuser>, --user=<dbuser> - Alternate DB user with privileges to alter DB
|
--migrate-events - Update database structures as per USE_DEEP_STORAGE setting.
|
||||||
-p <dbpass>, --pass=<dbpass> - Password of alternate DB user with privileges to alter DB
|
-v <version>, --version=<version> - Force upgrade to the current version from <version>
|
||||||
-s, --super - Use system maintenance account on debian based systems instead of unprivileged account
|
-u <dbuser>, --user=<dbuser> - Alternate DB user with privileges to alter DB
|
||||||
-d <dir>, --dir=<dir> - Directory containing update files if not in default build location
|
-p <dbpass>, --pass=<dbpass> - Password of alternate DB user with privileges to alter DB
|
||||||
-interactive - interact with the user
|
-s, --super - Use system maintenance account on debian based systems instead of unprivileged account
|
||||||
-nointeractive - do not interact with the user
|
-d <dir>, --dir=<dir> - Directory containing update files if not in default build location
|
||||||
|
-interactive - interact with the user
|
||||||
|
-nointeractive - do not interact with the user
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
use strict;
|
use strict;
|
||||||
|
use warnings;
|
||||||
use bytes;
|
use bytes;
|
||||||
use version;
|
use version;
|
||||||
use Crypt::Eksblowfish::Bcrypt;
|
|
||||||
use Data::Entropy::Algorithms qw(rand_bits);
|
|
||||||
|
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
#
|
#
|
||||||
|
@ -95,7 +96,7 @@ my $use_log = (($> == 0) || ($> == $web_uid));
|
||||||
logInit( toFile=>$use_log?DEBUG:NOLOG );
|
logInit( toFile=>$use_log?DEBUG:NOLOG );
|
||||||
logSetSignal();
|
logSetSignal();
|
||||||
|
|
||||||
my $interactive = 1;
|
my $interactive = -t STDERR; # interactive if we have IO
|
||||||
my $check = 0;
|
my $check = 0;
|
||||||
my $freshen = 0;
|
my $freshen = 0;
|
||||||
my $rename = 0;
|
my $rename = 0;
|
||||||
|
@ -122,9 +123,8 @@ GetOptions(
|
||||||
) or pod2usage(-exitstatus => -1);
|
) or pod2usage(-exitstatus => -1);
|
||||||
|
|
||||||
my $dbh = zmDbConnect(undef, { mysql_multi_statements=>1 } );
|
my $dbh = zmDbConnect(undef, { mysql_multi_statements=>1 } );
|
||||||
if ( !$dbh ) {
|
die "Unable to connect to db\n" if !$dbh;
|
||||||
die "Unable to connect to db\n";
|
|
||||||
}
|
|
||||||
$Config{ZM_DB_USER} = $dbUser;
|
$Config{ZM_DB_USER} = $dbUser;
|
||||||
$Config{ZM_DB_PASS} = $dbPass;
|
$Config{ZM_DB_PASS} = $dbPass;
|
||||||
# we escape dbpass with single quotes so that $ in the password has no effect, but dbpass could have a ' in it.
|
# we escape dbpass with single quotes so that $ in the password has no effect, but dbpass could have a ' in it.
|
||||||
|
@ -144,8 +144,10 @@ if ( ($check + $freshen + $rename + $zoneFix + $migrateEvents + ($version?1:0))
|
||||||
pod2usage(-exitstatus => -1);
|
pod2usage(-exitstatus => -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) {
|
if ($check) {
|
||||||
print('Update agent starting at '.strftime('%y/%m/%d %H:%M:%S', localtime() )."\n");
|
if (!$interactive) {
|
||||||
|
Info('Update agent starting at '.strftime('%y/%m/%d %H:%M:%S', localtime() )."\n");
|
||||||
|
}
|
||||||
|
|
||||||
my $currVersion = $Config{ZM_DYN_CURR_VERSION};
|
my $currVersion = $Config{ZM_DYN_CURR_VERSION};
|
||||||
my $lastVersion = $Config{ZM_DYN_LAST_VERSION};
|
my $lastVersion = $Config{ZM_DYN_LAST_VERSION};
|
||||||
|
@ -153,16 +155,14 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) {
|
||||||
|
|
||||||
if ( !$currVersion ) {
|
if ( !$currVersion ) {
|
||||||
$currVersion = $Config{ZM_VERSION};
|
$currVersion = $Config{ZM_VERSION};
|
||||||
|
zmDbDo("UPDATE `Config` SET `Value` = ? WHERE `Name` = 'ZM_DYN_CURR_VERSION'", $currVersion);
|
||||||
my $sql = "update Config set Value = ? where Name = 'ZM_DYN_CURR_VERSION'";
|
|
||||||
my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr());
|
|
||||||
my $res = $sth->execute($currVersion) or die("Can't execute: ".$sth->errstr());
|
|
||||||
$sth->finish();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( 1 ) {
|
while ( 1 ) {
|
||||||
my $now = time();
|
my $now = time();
|
||||||
if ( !$lastVersion || !$lastCheck || (($now-$lastCheck) > CHECK_INTERVAL) ) {
|
if ( !$interactive and $lastVersion and $lastCheck and (($now-$lastCheck) <= CHECK_INTERVAL) ) {
|
||||||
|
Debug("Not checking for updates since we already have less than " . CHECK_INTERVAL . " seconds ago.");
|
||||||
|
} else {
|
||||||
Info('Checking for updates');
|
Info('Checking for updates');
|
||||||
|
|
||||||
use LWP::UserAgent;
|
use LWP::UserAgent;
|
||||||
|
@ -175,21 +175,18 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) {
|
||||||
my $res = $ua->request($req);
|
my $res = $ua->request($req);
|
||||||
|
|
||||||
if ( $res->is_success ) {
|
if ( $res->is_success ) {
|
||||||
$lastVersion = $res->content;
|
my $latestVersion = $res->content;
|
||||||
chomp($lastVersion);
|
chomp($latestVersion);
|
||||||
$lastCheck = $now;
|
$lastCheck = $now;
|
||||||
|
|
||||||
Info('Got version: '.$lastVersion);
|
Info('Got version: '.$latestVersion);
|
||||||
|
|
||||||
my $lv_sql = 'UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_VERSION\'';
|
zmDbDo('UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_VERSION\'', $latestVersion);
|
||||||
my $lv_sth = $dbh->prepare_cached($lv_sql) or die("Can't prepare '$lv_sql': ".$dbh->errstr());
|
zmDbDo('UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_CHECK\'', $lastCheck);
|
||||||
my $lv_res = $lv_sth->execute($lastVersion) or die("Can't execute: ".$lv_sth->errstr());
|
if ($interactive) {
|
||||||
$lv_sth->finish();
|
print("Last version $lastVersion, Latest version $latestVersion, our version " . ZM_VERSION."\n");
|
||||||
|
exit(0);
|
||||||
my $lc_sql = 'UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_CHECK\'';
|
}
|
||||||
my $lc_sth = $dbh->prepare_cached($lc_sql) or die("Can't prepare '$lc_sql': ".$dbh->errstr());
|
|
||||||
my $lc_res = $lc_sth->execute($lastCheck) or die("Can't execute: ".$lc_sth->errstr());
|
|
||||||
$lc_sth->finish();
|
|
||||||
} else {
|
} else {
|
||||||
Error('Error check failed: \''.$res->status_line().'\'');
|
Error('Error check failed: \''.$res->status_line().'\'');
|
||||||
}
|
}
|
||||||
|
@ -447,11 +444,6 @@ if ( $version ) {
|
||||||
|
|
||||||
print( "\nUpgrading database to version ".ZM_VERSION."\n" );
|
print( "\nUpgrading database to version ".ZM_VERSION."\n" );
|
||||||
|
|
||||||
# Update config first of all
|
|
||||||
migratePaths();
|
|
||||||
ZoneMinder::Config::loadConfigFromDB();
|
|
||||||
ZoneMinder::Config::saveConfigToDB();
|
|
||||||
|
|
||||||
my $cascade = undef;
|
my $cascade = undef;
|
||||||
if ( $cascade || $version eq "1.19.0" ) {
|
if ( $cascade || $version eq "1.19.0" ) {
|
||||||
# Patch the database
|
# Patch the database
|
||||||
|
@ -1044,14 +1036,16 @@ sub patchDB {
|
||||||
} # end sub patchDB
|
} # end sub patchDB
|
||||||
|
|
||||||
sub migratePasswords {
|
sub migratePasswords {
|
||||||
print ("Migratings passwords, if any...\n");
|
use Crypt::Eksblowfish::Bcrypt;
|
||||||
|
use Data::Entropy::Algorithms qw(rand_bits);
|
||||||
|
print("Migratings passwords, if any...\n");
|
||||||
my $sql = 'SELECT * FROM `Users`';
|
my $sql = 'SELECT * FROM `Users`';
|
||||||
my $sth = $dbh->prepare_cached($sql) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
my $sth = $dbh->prepare_cached($sql) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||||
my $res = $sth->execute() or die("Can't execute: ".$sth->errstr());
|
my $res = $sth->execute() or die("Can't execute: ".$sth->errstr());
|
||||||
while( my $user = $sth->fetchrow_hashref() ) {
|
while ( my $user = $sth->fetchrow_hashref() ) {
|
||||||
my $scheme = substr($user->{Password}, 0, 1);
|
my $scheme = substr($user->{Password}, 0, 1);
|
||||||
if ($scheme eq '*') {
|
if ($scheme eq '*') {
|
||||||
print ('-->'.$user->{Username}." password will be migrated\n");
|
print('-->'.$user->{Username}." password will be migrated\n");
|
||||||
my $salt = Crypt::Eksblowfish::Bcrypt::en_base64(rand_bits(16*8));
|
my $salt = Crypt::Eksblowfish::Bcrypt::en_base64(rand_bits(16*8));
|
||||||
my $settings = '$2a$10$'.$salt;
|
my $settings = '$2a$10$'.$salt;
|
||||||
my $pass_hash = Crypt::Eksblowfish::Bcrypt::bcrypt($user->{Password},$settings);
|
my $pass_hash = Crypt::Eksblowfish::Bcrypt::bcrypt($user->{Password},$settings);
|
||||||
|
|
|
@ -56,6 +56,7 @@ use constant START_DELAY => 30; # To give everything else time to start
|
||||||
@EXTRA_PERL_LIB@
|
@EXTRA_PERL_LIB@
|
||||||
use ZoneMinder;
|
use ZoneMinder;
|
||||||
use ZoneMinder::Storage;
|
use ZoneMinder::Storage;
|
||||||
|
use ZoneMinder::Monitor;
|
||||||
use POSIX;
|
use POSIX;
|
||||||
use DBI;
|
use DBI;
|
||||||
use autouse 'Data::Dumper'=>qw(Dumper);
|
use autouse 'Data::Dumper'=>qw(Dumper);
|
||||||
|
@ -66,7 +67,7 @@ $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
|
||||||
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
|
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
|
||||||
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
||||||
|
|
||||||
logInit();
|
my $log = logInit();
|
||||||
logSetSignal();
|
logSetSignal();
|
||||||
my $zm_terminate = 0;
|
my $zm_terminate = 0;
|
||||||
sub TermHandler {
|
sub TermHandler {
|
||||||
|
@ -80,91 +81,76 @@ Info('Watchdog starting, pausing for '.START_DELAY.' seconds');
|
||||||
sleep(START_DELAY);
|
sleep(START_DELAY);
|
||||||
|
|
||||||
my $dbh = zmDbConnect();
|
my $dbh = zmDbConnect();
|
||||||
my $sql = $Config{ZM_SERVER_ID} ? 'SELECT * FROM Monitors WHERE ServerId=?' : 'SELECT * FROM Monitors';
|
|
||||||
my $sth = $dbh->prepare_cached($sql)
|
|
||||||
or Fatal("Can't prepare '$sql': ".$dbh->errstr());
|
|
||||||
|
|
||||||
while (!$zm_terminate) {
|
while (!$zm_terminate) {
|
||||||
while ( ! ( $dbh and $dbh->ping() ) ) {
|
while (!($dbh and $dbh->ping())) {
|
||||||
if ( ! ( $dbh = zmDbConnect() ) ) {
|
if (!($dbh = zmDbConnect())) {
|
||||||
sleep($Config{ZM_WATCH_CHECK_INTERVAL});
|
sleep($Config{ZM_WATCH_CHECK_INTERVAL});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () )
|
foreach my $monitor (ZoneMinder::Monitor->find($Config{ZM_SERVER_ID} ? (ServerId=>$Config{ZM_SERVER_ID}) : ())) {
|
||||||
or Fatal('Can\'t execute: '.$sth->errstr());
|
|
||||||
while( my $monitor = $sth->fetchrow_hashref() ) {
|
|
||||||
next if $monitor->{Function} eq 'None';
|
next if $monitor->{Function} eq 'None';
|
||||||
next if $monitor->{Type} eq 'WebSite';
|
next if $monitor->{Type} eq 'WebSite';
|
||||||
my $now = time();
|
my $now = time();
|
||||||
my $restart = 0;
|
my $restart = 0;
|
||||||
if (zmMemVerify($monitor)) {
|
|
||||||
# Check we have got an image recently
|
zmMemInvalidate($monitor);
|
||||||
my $capture_time = zmGetLastWriteTime($monitor);
|
if (!zmMemVerify($monitor)) {
|
||||||
if (!defined($capture_time)) {
|
|
||||||
# Can't read from shared data
|
|
||||||
Debug('LastWriteTime is not defined.');
|
|
||||||
zmMemInvalidate($monitor);
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
Debug("Monitor $$monitor{Id} LastWriteTime is $capture_time.");
|
|
||||||
if (!$capture_time) {
|
|
||||||
my $startup_time = zmGetStartupTime($monitor);
|
|
||||||
if (($now - $startup_time) > $Config{ZM_WATCH_MAX_DELAY}) {
|
|
||||||
Warning(
|
|
||||||
"Restarting capture daemon for $$monitor{Name}, no image since startup. ".
|
|
||||||
"Startup time was $startup_time - now $now > $Config{ZM_WATCH_MAX_DELAY}"
|
|
||||||
);
|
|
||||||
$restart = 1;
|
|
||||||
} else {
|
|
||||||
# We can't get the last capture time so can't be sure it's died, it might just be starting up.
|
|
||||||
zmMemInvalidate($monitor);
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!$restart) {
|
|
||||||
my $max_image_delay = (
|
|
||||||
$monitor->{MaxFPS}
|
|
||||||
&&($monitor->{MaxFPS}>0)
|
|
||||||
&&($monitor->{MaxFPS}<1)
|
|
||||||
) ? (3/$monitor->{MaxFPS})
|
|
||||||
: $Config{ZM_WATCH_MAX_DELAY}
|
|
||||||
;
|
|
||||||
my $image_delay = $now - $capture_time;
|
|
||||||
Debug("Monitor $monitor->{Id} last captured $image_delay seconds ago, max is $max_image_delay");
|
|
||||||
if ( $image_delay > $max_image_delay ) {
|
|
||||||
Warning("Restarting capture daemon for "
|
|
||||||
.$monitor->{Name}.", time since last capture $image_delay seconds ($now-$capture_time)"
|
|
||||||
);
|
|
||||||
$restart = 1;
|
|
||||||
}
|
|
||||||
} # end if ! restart
|
|
||||||
} else {
|
|
||||||
Info("Restarting capture daemon for $monitor->{Name}, shared data not valid");
|
Info("Restarting capture daemon for $monitor->{Name}, shared data not valid");
|
||||||
$restart = 1;
|
$monitor->control('restart');
|
||||||
|
next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($restart) {
|
# Check we have got an image recently
|
||||||
my $command;
|
my $capture_time = zmGetLastWriteTime($monitor);
|
||||||
if ($monitor->{Type} eq 'Local') {
|
if (!defined($capture_time)) {
|
||||||
$command = 'zmdc.pl restart zmc -d '.$monitor->{Device};
|
# Can't read from shared data
|
||||||
} else {
|
Warning('LastWriteTime is not defined.');
|
||||||
$command = 'zmdc.pl restart zmc -m '.$monitor->{Id};
|
next;
|
||||||
|
}
|
||||||
|
Debug("Monitor $$monitor{Id} LastWriteTime is $capture_time.");
|
||||||
|
if (!$capture_time) {
|
||||||
|
# We can't get the last capture time so can't be sure it's died, it might just be starting up.
|
||||||
|
my $startup_time = zmGetStartupTime($monitor);
|
||||||
|
if (($now - $startup_time) > $Config{ZM_WATCH_MAX_DELAY}) {
|
||||||
|
$log->logPrint(ZoneMinder::Logger::WARNING+$monitor->Importance(),
|
||||||
|
"Restarting capture daemon for $$monitor{Name}, no image since startup. ".
|
||||||
|
"Startup time was $startup_time - now $now > $Config{ZM_WATCH_MAX_DELAY}"
|
||||||
|
);
|
||||||
|
$monitor->control('restart');
|
||||||
}
|
}
|
||||||
runCommand($command);
|
next;
|
||||||
} elsif ($monitor->{Function} ne 'Monitor') {
|
}
|
||||||
# Now check analysis daemon
|
|
||||||
$restart = 0;
|
my $max_image_delay = (
|
||||||
|
$monitor->{MaxFPS}
|
||||||
|
&&($monitor->{MaxFPS}>0)
|
||||||
|
&&($monitor->{MaxFPS}<1)
|
||||||
|
) ? (3/$monitor->{MaxFPS})
|
||||||
|
: $Config{ZM_WATCH_MAX_DELAY}
|
||||||
|
;
|
||||||
|
my $image_delay = $now - $capture_time;
|
||||||
|
Debug("Monitor $monitor->{Id} last captured $image_delay seconds ago, max is $max_image_delay");
|
||||||
|
if ($image_delay > $max_image_delay) {
|
||||||
|
$log->logPrint(ZoneMinder::Logger::WARNING+$monitor->Importance(),
|
||||||
|
'Restarting capture daemon for '.$monitor->{Name}.
|
||||||
|
", time since last capture $image_delay seconds ($now-$capture_time)");
|
||||||
|
$monitor->control('restart');
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($monitor->{Function} ne 'Monitor') {
|
||||||
|
# Now check analysis thread
|
||||||
# Check we have got an image recently
|
# Check we have got an image recently
|
||||||
my $image_time = zmGetLastReadTime($monitor);
|
my $image_time = zmGetLastReadTime($monitor);
|
||||||
if (!defined($image_time)) {
|
if (!defined($image_time)) {
|
||||||
# Can't read from shared data
|
# Can't read from shared data
|
||||||
$restart = 1;
|
|
||||||
Error("Error reading shared data for $$monitor{Id} $$monitor{Name}");
|
Error("Error reading shared data for $$monitor{Id} $$monitor{Name}");
|
||||||
|
$monitor->control('restart');
|
||||||
|
next;
|
||||||
} elsif (!$image_time) {
|
} elsif (!$image_time) {
|
||||||
# We can't get the last capture time so can't be sure it's died.
|
Debug("Last analyse time for $$monitor{Id} $$monitor{Name} was zero.");
|
||||||
#$restart = 1;
|
|
||||||
Error("Last analyse time for $$monitor{Id} $$monitor{Name} was zero.");
|
|
||||||
} else {
|
} else {
|
||||||
my $max_image_delay = ( $monitor->{MaxFPS}
|
my $max_image_delay = ( $monitor->{MaxFPS}
|
||||||
&&($monitor->{MaxFPS}>0)
|
&&($monitor->{MaxFPS}>0)
|
||||||
|
@ -175,26 +161,16 @@ while (!$zm_terminate) {
|
||||||
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");
|
Debug("Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay");
|
||||||
if ($image_delay > $max_image_delay) {
|
if ($image_delay > $max_image_delay) {
|
||||||
Warning("Analysis daemon for $$monitor{Id} $$monitor{Name} needs restarting,"
|
$log->logPrint(ZoneMinder::Logger::WARNING+$monitor->Importance(),
|
||||||
." time since last analysis $image_delay seconds ($now-$image_time)"
|
"daemon for $$monitor{Id} $$monitor{Name} needs restarting,"
|
||||||
);
|
." time since last analysis $image_delay seconds ($now-$image_time)");
|
||||||
$restart = 1;
|
$monitor->control('restart');
|
||||||
|
next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($restart) {
|
|
||||||
Info("Restarting analysis daemon for $$monitor{Id} $$monitor{Name}");
|
|
||||||
my $command;
|
|
||||||
if ( $monitor->{Type} eq 'Local' ) {
|
|
||||||
$command = 'zmdc.pl restart zmc -d '.$monitor->{Device};
|
|
||||||
} else {
|
|
||||||
$command = 'zmdc.pl restart zmc -m '.$monitor->{Id};
|
|
||||||
}
|
|
||||||
runCommand($command);
|
|
||||||
} # end if restart
|
|
||||||
} # end if check analysis daemon
|
} # end if check analysis daemon
|
||||||
# Prevent open handles building up if we have connect to shared memory
|
|
||||||
zmMemInvalidate($monitor); # Close our file handle to the zmc process we are about to end
|
|
||||||
} # end foreach monitor
|
} # end foreach monitor
|
||||||
|
|
||||||
sleep($Config{ZM_WATCH_CHECK_INTERVAL});
|
sleep($Config{ZM_WATCH_CHECK_INTERVAL});
|
||||||
|
|
|
@ -6,6 +6,7 @@ configure_file(zm_config_data.h.in "${CMAKE_BINARY_DIR}/zm_config_data.h" @ONLY)
|
||||||
# Group together all the source files that are used by all the binaries (zmc, zmu, zms etc)
|
# Group together all the source files that are used by all the binaries (zmc, zmu, zms etc)
|
||||||
set(ZM_BIN_SRC_FILES
|
set(ZM_BIN_SRC_FILES
|
||||||
zm_analysis_thread.cpp
|
zm_analysis_thread.cpp
|
||||||
|
zm_poll_thread.cpp
|
||||||
zm_buffer.cpp
|
zm_buffer.cpp
|
||||||
zm_camera.cpp
|
zm_camera.cpp
|
||||||
zm_comms.cpp
|
zm_comms.cpp
|
||||||
|
@ -32,6 +33,9 @@ set(ZM_BIN_SRC_FILES
|
||||||
zm_libvnc_camera.cpp
|
zm_libvnc_camera.cpp
|
||||||
zm_local_camera.cpp
|
zm_local_camera.cpp
|
||||||
zm_monitor.cpp
|
zm_monitor.cpp
|
||||||
|
zm_monitor_monitorlink.cpp
|
||||||
|
zm_monitor_janus.cpp
|
||||||
|
zm_monitor_amcrest.cpp
|
||||||
zm_monitorstream.cpp
|
zm_monitorstream.cpp
|
||||||
zm_ffmpeg.cpp
|
zm_ffmpeg.cpp
|
||||||
zm_ffmpeg_camera.cpp
|
zm_ffmpeg_camera.cpp
|
||||||
|
@ -60,12 +64,64 @@ set(ZM_BIN_SRC_FILES
|
||||||
zm_signal.cpp
|
zm_signal.cpp
|
||||||
zm_stream.cpp
|
zm_stream.cpp
|
||||||
zm_swscale.cpp
|
zm_swscale.cpp
|
||||||
|
zm_time.cpp
|
||||||
zm_user.cpp
|
zm_user.cpp
|
||||||
zm_utils.cpp
|
zm_utils.cpp
|
||||||
zm_videostore.cpp
|
zm_videostore.cpp
|
||||||
zm_zone.cpp
|
zm_zone.cpp
|
||||||
zm_storage.cpp)
|
zm_storage.cpp)
|
||||||
|
|
||||||
|
if(GSOAP_FOUND)
|
||||||
|
set(ZM_BIN_SRC_FILES
|
||||||
|
${ZM_BIN_SRC_FILES}
|
||||||
|
${CMAKE_BINARY_DIR}/generated/soapPullPointSubscriptionBindingProxy.cpp
|
||||||
|
${CMAKE_BINARY_DIR}/generated/soapC.cpp
|
||||||
|
${GSOAP_PLUGIN_DIR}/smdevp.c
|
||||||
|
${GSOAP_PLUGIN_DIR}/mecevp.c
|
||||||
|
${GSOAP_PLUGIN_DIR}/wsaapi.c
|
||||||
|
${GSOAP_PLUGIN_DIR}/wsseapi.c
|
||||||
|
${GSOAP_PLUGIN_DIR}/../custom/struct_timeval.c
|
||||||
|
)
|
||||||
|
|
||||||
|
SET(GCC_COMPILE_FLAGS "-DWITH_OPENSSL -DWITH_DOM")
|
||||||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COMPILE_FLAGS}")
|
||||||
|
|
||||||
|
#Create the directory that will host files generated by GSOAP
|
||||||
|
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/generated)
|
||||||
|
|
||||||
|
#some files are generated by gsoap
|
||||||
|
set_source_files_properties( ${CMAKE_BINARY_DIR}/generated/soapClientLib.c PROPERTIES GENERATED TRUE )
|
||||||
|
set_source_files_properties( ${CMAKE_BINARY_DIR}/generated/soapC.c PROPERTIES GENERATED TRUE )
|
||||||
|
set_source_files_properties( ${CMAKE_BINARY_DIR}/generated/soapPullPointSubscriptionBindingProxy.cpp PROPERTIES GENERATED TRUE )
|
||||||
|
set_source_files_properties( ${GSOAP_PLUGIN_DIR}/smdevp.c PROPERTIES LANGUAGE CXX)
|
||||||
|
set_source_files_properties( ${GSOAP_PLUGIN_DIR}/mecevp.c PROPERTIES LANGUAGE CXX)
|
||||||
|
set_source_files_properties( ${GSOAP_PLUGIN_DIR}/wsaapi.c PROPERTIES LANGUAGE CXX)
|
||||||
|
set_source_files_properties( ${GSOAP_PLUGIN_DIR}/wsseapi.c PROPERTIES LANGUAGE CXX)
|
||||||
|
set_source_files_properties( ${GSOAP_PLUGIN_DIR}/../custom/struct_timeval.c PROPERTIES LANGUAGE CXX)
|
||||||
|
|
||||||
|
#Create a cmake target that generate gsoap files
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${CMAKE_BINARY_DIR}/generated/soapC.cpp
|
||||||
|
OUTPUT ${CMAKE_BINARY_DIR}/generated/soapPullPointSubscriptionBindingProxy.cpp
|
||||||
|
COMMAND ${GSOAP_WSDL2H} -d -P -O2 -o ${CMAKE_BINARY_DIR}/generated/bindings.h http://www.onvif.org/onvif/ver10/events/wsdl/event.wsdl
|
||||||
|
COMMAND echo '\#import \"wsse.h\"' >> ${CMAKE_BINARY_DIR}/generated/bindings.h
|
||||||
|
COMMAND echo '\#import \"struct_timeval.h\"' >> ${CMAKE_BINARY_DIR}/generated/bindings.h
|
||||||
|
COMMAND ${GSOAP_SOAPCPP2} -n -2 -C -I ${GSOAP_PLUGIN_DIR}/.. -I ${GSOAP_PLUGIN_DIR}/../import/ -I ${GSOAP_PLUGIN_DIR}/../custom/ -d ${CMAKE_BINARY_DIR}/generated -j -x ${CMAKE_BINARY_DIR}/generated/bindings.h
|
||||||
|
COMMENT "CREATING STUBS AND GLUE CODE"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(GSOAP_GENERATION_TARGET
|
||||||
|
DEPENDS ${CMAKE_BINARY_DIR}/generated/soapC.cpp
|
||||||
|
DEPENDS ${CMAKE_BINARY_DIR}/generated/soapPullPointSubscriptionBindingProxy.cpp
|
||||||
|
DEPENDS ${GSOAP_PLUGIN_DIR}/smdevp.c
|
||||||
|
DEPENDS ${GSOAP_PLUGIN_DIR}/mecevp.c
|
||||||
|
DEPENDS ${GSOAP_PLUGIN_DIR}/wsaapi.c
|
||||||
|
DEPENDS ${GSOAP_PLUGIN_DIR}/wsseapi.c
|
||||||
|
DEPENDS ${GSOAP_PLUGIN_DIR}/../custom/struct_timeval.c
|
||||||
|
)
|
||||||
|
|
||||||
|
endif()
|
||||||
|
|
||||||
# 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})
|
||||||
|
|
||||||
|
@ -74,6 +130,15 @@ target_include_directories(zm
|
||||||
${CMAKE_BINARY_DIR}
|
${CMAKE_BINARY_DIR}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR})
|
${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
if(GSOAP_FOUND)
|
||||||
|
target_include_directories(zm
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_BINARY_DIR}/generated
|
||||||
|
${GSOAP_PLUGIN_DIR}/..
|
||||||
|
${GSOAP_INCLUDE_DIR})
|
||||||
|
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(zm
|
target_link_libraries(zm
|
||||||
PUBLIC
|
PUBLIC
|
||||||
FFMPEG::avcodec
|
FFMPEG::avcodec
|
||||||
|
@ -88,6 +153,15 @@ target_link_libraries(zm
|
||||||
PRIVATE
|
PRIVATE
|
||||||
zm-core-interface)
|
zm-core-interface)
|
||||||
|
|
||||||
|
if(GSOAP_FOUND)
|
||||||
|
target_link_libraries(zm
|
||||||
|
PUBLIC
|
||||||
|
${GSOAP_CXX_LIBRARIES}
|
||||||
|
${GSOAP_SSL_CXX_LIBRARIES}
|
||||||
|
${OPENSSL_SSL_LIBRARY}
|
||||||
|
${OPENSSL_CRYPTO_LIBRARY})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(${ZM_JWT_BACKEND} STREQUAL "jwt_cpp")
|
if(${ZM_JWT_BACKEND} STREQUAL "jwt_cpp")
|
||||||
target_link_libraries(zm
|
target_link_libraries(zm
|
||||||
PUBLIC
|
PUBLIC
|
||||||
|
@ -109,6 +183,11 @@ add_executable(zms zms.cpp)
|
||||||
add_executable(zmu zmu.cpp)
|
add_executable(zmu zmu.cpp)
|
||||||
add_executable(zmbenchmark zmbenchmark.cpp)
|
add_executable(zmbenchmark zmbenchmark.cpp)
|
||||||
|
|
||||||
|
if(GSOAP_FOUND)
|
||||||
|
#Make sure that the client is compiled only after gsoap has been processed
|
||||||
|
add_dependencies(zmc GSOAP_GENERATION_TARGET)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(zmc
|
target_link_libraries(zmc
|
||||||
PRIVATE
|
PRIVATE
|
||||||
zm-core-interface
|
zm-core-interface
|
||||||
|
|
|
@ -23,6 +23,14 @@ void AnalysisThread::Start() {
|
||||||
|
|
||||||
void AnalysisThread::Run() {
|
void AnalysisThread::Run() {
|
||||||
while (!(terminate_ or zm_terminate)) {
|
while (!(terminate_ or zm_terminate)) {
|
||||||
monitor_->Analyse();
|
// Some periodic updates are required for variable capturing framerate
|
||||||
|
if (!monitor_->Analyse()) {
|
||||||
|
if (!(terminate_ or zm_terminate)) {
|
||||||
|
// We only sleep when Analyse returns false because it is an error condition and we will spin like mad if it persists.
|
||||||
|
Microseconds sleep_for = monitor_->Active() ? Microseconds(ZM_SAMPLE_RATE) : Microseconds(ZM_SUSPENDED_RATE);
|
||||||
|
Debug(2, "Sleeping for %" PRId64 "us", int64(sleep_for.count()));
|
||||||
|
std::this_thread::sleep_for(sleep_for);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ unsigned int Buffer::expand(unsigned int count) {
|
||||||
int Buffer::read_into(int sd, unsigned int bytes) {
|
int Buffer::read_into(int sd, unsigned int bytes) {
|
||||||
// Make sure there is enough space
|
// Make sure there is enough space
|
||||||
this->expand(bytes);
|
this->expand(bytes);
|
||||||
Debug(3, "Reading %u btes", bytes);
|
Debug(3, "Reading %u bytes", bytes);
|
||||||
int bytes_read = ::read(sd, mTail, bytes);
|
int bytes_read = ::read(sd, mTail, bytes);
|
||||||
if (bytes_read > 0) {
|
if (bytes_read > 0) {
|
||||||
mTail += bytes_read;
|
mTail += bytes_read;
|
||||||
|
|
|
@ -245,6 +245,7 @@ class Socket : public CommsBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ssize_t recv(std::string &msg) const {
|
virtual ssize_t recv(std::string &msg) const {
|
||||||
|
msg.reserve(ZM_NETWORK_BUFSIZ);
|
||||||
std::vector<char> buffer(msg.capacity());
|
std::vector<char> buffer(msg.capacity());
|
||||||
ssize_t nBytes;
|
ssize_t nBytes;
|
||||||
if ((nBytes = ::recv(mSd, buffer.data(), buffer.size(), 0)) < 0) {
|
if ((nBytes = ::recv(mSd, buffer.data(), buffer.size(), 0)) < 0) {
|
||||||
|
|
|
@ -108,8 +108,8 @@ bool zmDbConnect() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void zmDbClose() {
|
void zmDbClose() {
|
||||||
|
std::lock_guard<std::mutex> lck(db_mutex);
|
||||||
if (zmDbConnected) {
|
if (zmDbConnected) {
|
||||||
std::lock_guard<std::mutex> lck(db_mutex);
|
|
||||||
mysql_close(&dbconn);
|
mysql_close(&dbconn);
|
||||||
// mysql_init() call implicitly mysql_library_init() but
|
// mysql_init() call implicitly mysql_library_init() but
|
||||||
// mysql_close() does not call mysql_library_end()
|
// mysql_close() does not call mysql_library_end()
|
||||||
|
@ -238,8 +238,13 @@ zmDbQueue::~zmDbQueue() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void zmDbQueue::stop() {
|
void zmDbQueue::stop() {
|
||||||
mTerminate = true;
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
|
mTerminate = true;
|
||||||
|
}
|
||||||
mCondition.notify_all();
|
mCondition.notify_all();
|
||||||
|
|
||||||
if (mThread.joinable()) mThread.join();
|
if (mThread.joinable()) mThread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,11 +256,11 @@ void zmDbQueue::process() {
|
||||||
mCondition.wait(lock);
|
mCondition.wait(lock);
|
||||||
}
|
}
|
||||||
while (!mQueue.empty()) {
|
while (!mQueue.empty()) {
|
||||||
if (mQueue.size() > 10) {
|
if (mQueue.size() > 30) {
|
||||||
Logger *log = Logger::fetch();
|
Logger *log = Logger::fetch();
|
||||||
Logger::Level db_level = log->databaseLevel();
|
Logger::Level db_level = log->databaseLevel();
|
||||||
log->databaseLevel(Logger::NOLOG);
|
log->databaseLevel(Logger::NOLOG);
|
||||||
Warning("db queue size has grown larger %zu than 10 entries", mQueue.size());
|
Warning("db queue size has grown larger %zu than 20 entries", mQueue.size());
|
||||||
log->databaseLevel(db_level);
|
log->databaseLevel(db_level);
|
||||||
}
|
}
|
||||||
std::string sql = mQueue.front();
|
std::string sql = mQueue.front();
|
||||||
|
@ -271,8 +276,10 @@ void zmDbQueue::process() {
|
||||||
|
|
||||||
void zmDbQueue::push(std::string &&sql) {
|
void zmDbQueue::push(std::string &&sql) {
|
||||||
if (mTerminate) return;
|
if (mTerminate) return;
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
{
|
||||||
mQueue.push(std::move(sql));
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
mQueue.push(std::move(sql));
|
||||||
|
}
|
||||||
mCondition.notify_all();
|
mCondition.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
256
src/zm_event.cpp
256
src/zm_event.cpp
|
@ -55,7 +55,7 @@ Event::Event(
|
||||||
alarm_frames(0),
|
alarm_frames(0),
|
||||||
alarm_frame_written(false),
|
alarm_frame_written(false),
|
||||||
tot_score(0),
|
tot_score(0),
|
||||||
max_score(0),
|
max_score(-1),
|
||||||
//path(""),
|
//path(""),
|
||||||
//snapshit_file(),
|
//snapshit_file(),
|
||||||
//alarm_file(""),
|
//alarm_file(""),
|
||||||
|
@ -65,7 +65,8 @@ Event::Event(
|
||||||
last_db_frame(0),
|
last_db_frame(0),
|
||||||
have_video_keyframe(false),
|
have_video_keyframe(false),
|
||||||
//scheme
|
//scheme
|
||||||
save_jpegs(0)
|
save_jpegs(0),
|
||||||
|
terminate_(false)
|
||||||
{
|
{
|
||||||
std::string notes;
|
std::string notes;
|
||||||
createNotes(notes);
|
createNotes(notes);
|
||||||
|
@ -133,98 +134,22 @@ Event::Event(
|
||||||
);
|
);
|
||||||
id = zmDbDoInsert(sql);
|
id = zmDbDoInsert(sql);
|
||||||
|
|
||||||
if (!SetPath(storage)) {
|
thread_ = std::thread(&Event::Run, this);
|
||||||
// Try another
|
|
||||||
Warning("Failed creating event dir at %s", storage->Path());
|
|
||||||
|
|
||||||
sql = stringtf("SELECT `Id` FROM `Storage` WHERE `Id` != %u", storage->Id());
|
|
||||||
if (monitor->ServerId())
|
|
||||||
sql += stringtf(" AND ServerId=%u", monitor->ServerId());
|
|
||||||
|
|
||||||
storage = nullptr;
|
|
||||||
|
|
||||||
MYSQL_RES *result = zmDbFetch(sql);
|
|
||||||
if (result) {
|
|
||||||
for (int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++) {
|
|
||||||
storage = new Storage(atoi(dbrow[0]));
|
|
||||||
if (SetPath(storage))
|
|
||||||
break;
|
|
||||||
delete storage;
|
|
||||||
storage = nullptr;
|
|
||||||
} // end foreach row of Storage
|
|
||||||
mysql_free_result(result);
|
|
||||||
result = nullptr;
|
|
||||||
}
|
|
||||||
if (!storage) {
|
|
||||||
Info("No valid local storage area found. Trying all other areas.");
|
|
||||||
// Try remote
|
|
||||||
sql = "SELECT `Id` FROM `Storage` WHERE ServerId IS NULL";
|
|
||||||
if (monitor->ServerId())
|
|
||||||
sql += stringtf(" OR ServerId != %u", monitor->ServerId());
|
|
||||||
|
|
||||||
result = zmDbFetch(sql);
|
|
||||||
if (result) {
|
|
||||||
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
|
|
||||||
storage = new Storage(atoi(dbrow[0]));
|
|
||||||
if (SetPath(storage))
|
|
||||||
break;
|
|
||||||
delete storage;
|
|
||||||
storage = nullptr;
|
|
||||||
} // end foreach row of Storage
|
|
||||||
mysql_free_result(result);
|
|
||||||
result = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!storage) {
|
|
||||||
storage = new Storage();
|
|
||||||
Warning("Failed to find a storage area to save events.");
|
|
||||||
}
|
|
||||||
sql = stringtf("UPDATE Events SET StorageId = '%d' WHERE Id=%" PRIu64, storage->Id(), id);
|
|
||||||
zmDbDo(sql);
|
|
||||||
} // end if ! setPath(Storage)
|
|
||||||
Debug(1, "Using storage area at %s", path.c_str());
|
|
||||||
|
|
||||||
snapshot_file = path + "/snapshot.jpg";
|
|
||||||
alarm_file = path + "/alarm.jpg";
|
|
||||||
|
|
||||||
video_incomplete_path = path + "/" + video_incomplete_file;
|
|
||||||
|
|
||||||
if (monitor->GetOptVideoWriter() != 0) {
|
|
||||||
/* Save as video */
|
|
||||||
|
|
||||||
videoStore = new VideoStore(
|
|
||||||
video_incomplete_path.c_str(),
|
|
||||||
container.c_str(),
|
|
||||||
monitor->GetVideoStream(),
|
|
||||||
monitor->GetVideoCodecContext(),
|
|
||||||
( monitor->RecordAudio() ? monitor->GetAudioStream() : nullptr ),
|
|
||||||
( monitor->RecordAudio() ? monitor->GetAudioCodecContext() : nullptr ),
|
|
||||||
monitor );
|
|
||||||
|
|
||||||
if ( !videoStore->open() ) {
|
|
||||||
Warning("Failed to open videostore, turning on jpegs");
|
|
||||||
delete videoStore;
|
|
||||||
videoStore = nullptr;
|
|
||||||
if ( ! ( save_jpegs & 1 ) ) {
|
|
||||||
save_jpegs |= 1; // Turn on jpeg storage
|
|
||||||
sql = stringtf("UPDATE Events SET SaveJpegs=%d WHERE Id=%" PRIu64, save_jpegs, id);
|
|
||||||
zmDbDo(sql);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::string codec = videoStore->get_codec();
|
|
||||||
video_file = stringtf("%" PRIu64 "-%s.%s.%s", id, "video", codec.c_str(), container.c_str());
|
|
||||||
video_path = path + "/" + video_file;
|
|
||||||
Debug(1, "Video file is %s", video_file.c_str());
|
|
||||||
}
|
|
||||||
} // end if GetOptVideoWriter
|
|
||||||
if (storage != monitor->getStorage())
|
|
||||||
delete storage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::~Event() {
|
Event::~Event() {
|
||||||
// We close the videowriter first, because if we finish the event, we might try to view the file, but we aren't done writing it yet.
|
Debug(1, "Deleting event, calling stop");
|
||||||
|
Stop();
|
||||||
|
if (thread_.joinable()) {
|
||||||
|
// Should be. Issuing the stop and then getting the lock
|
||||||
|
Debug(1, "Joinable");
|
||||||
|
thread_.join();
|
||||||
|
} else {
|
||||||
|
Debug(1, "Not Joinable");
|
||||||
|
}
|
||||||
|
|
||||||
/* Close the video file */
|
/* Close the video file */
|
||||||
|
// We close the videowriter first, because if we finish the event, we might try to view the file, but we aren't done writing it yet.
|
||||||
if (videoStore != nullptr) {
|
if (videoStore != nullptr) {
|
||||||
Debug(4, "Deleting video store");
|
Debug(4, "Deleting video store");
|
||||||
delete videoStore;
|
delete videoStore;
|
||||||
|
@ -291,6 +216,10 @@ void Event::createNotes(std::string ¬es) {
|
||||||
}
|
}
|
||||||
} // void Event::createNotes(std::string ¬es)
|
} // void Event::createNotes(std::string ¬es)
|
||||||
|
|
||||||
|
void Event::addNote(const char *cause, const std::string ¬e) {
|
||||||
|
noteSetMap[cause].insert(note);
|
||||||
|
}
|
||||||
|
|
||||||
bool Event::WriteFrameImage(Image *image, SystemTimePoint timestamp, const char *event_file, bool alarm_frame) const {
|
bool Event::WriteFrameImage(Image *image, SystemTimePoint timestamp, const char *event_file, bool alarm_frame) const {
|
||||||
int thisquality =
|
int thisquality =
|
||||||
(alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality)) ?
|
(alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality)) ?
|
||||||
|
@ -372,7 +301,13 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) {
|
||||||
} // end if update
|
} // end if update
|
||||||
} // void Event::updateNotes(const StringSetMap &newNoteSetMap)
|
} // void Event::updateNotes(const StringSetMap &newNoteSetMap)
|
||||||
|
|
||||||
void Event::AddPacket(const std::shared_ptr<ZMPacket>&packet) {
|
void Event::AddPacket(ZMLockedPacket *packetlock) {
|
||||||
|
std::unique_lock<std::mutex> lck(packet_queue_mutex);
|
||||||
|
packet_queue.push(packetlock);
|
||||||
|
packet_queue_condition.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Event::AddPacket_(const std::shared_ptr<ZMPacket>&packet) {
|
||||||
have_video_keyframe = have_video_keyframe ||
|
have_video_keyframe = have_video_keyframe ||
|
||||||
( ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) &&
|
( ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) &&
|
||||||
( packet->keyframe || monitor->GetOptVideoWriter() == Monitor::ENCODE) );
|
( packet->keyframe || monitor->GetOptVideoWriter() == Monitor::ENCODE) );
|
||||||
|
@ -486,12 +421,12 @@ void Event::AddFrame(Image *image,
|
||||||
|
|
||||||
Debug(1, "frames %d, score %d max_score %d", frames, score, max_score);
|
Debug(1, "frames %d, score %d max_score %d", frames, score, max_score);
|
||||||
// If this is the first frame, we should add a thumbnail to the event directory
|
// If this is the first frame, we should add a thumbnail to the event directory
|
||||||
if ((frames == 1) || (score > (int)max_score)) {
|
if ((frames == 1) || (score > max_score)) {
|
||||||
write_to_db = true; // web ui might show this as thumbnail, so db needs to know about it.
|
write_to_db = true; // web ui might show this as thumbnail, so db needs to know about it.
|
||||||
Debug(1, "Writing snapshot");
|
Debug(1, "Writing snapshot to %s", snapshot_file.c_str());
|
||||||
WriteFrameImage(image, timestamp, snapshot_file.c_str());
|
WriteFrameImage(image, timestamp, snapshot_file.c_str());
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Not Writing snapshot because score %d > max %d", score, max_score);
|
Debug(1, "Not Writing snapshot because frames %d score %d > max %d", frames, score, max_score);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We are writing an Alarm frame
|
// We are writing an Alarm frame
|
||||||
|
@ -500,17 +435,19 @@ void Event::AddFrame(Image *image,
|
||||||
if (!alarm_frame_written) {
|
if (!alarm_frame_written) {
|
||||||
write_to_db = true; // OD processing will need it, so the db needs to know about it
|
write_to_db = true; // OD processing will need it, so the db needs to know about it
|
||||||
alarm_frame_written = true;
|
alarm_frame_written = true;
|
||||||
Debug(1, "Writing alarm image");
|
Debug(1, "Writing alarm image to %s", alarm_file.c_str());
|
||||||
WriteFrameImage(image, timestamp, alarm_file.c_str());
|
if (!WriteFrameImage(image, timestamp, alarm_file.c_str())) {
|
||||||
|
Error("Failed to write alarm frame image to %s", alarm_file.c_str());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Debug(3, "Not Writing alarm image because alarm frame already written");
|
Debug(3, "Not Writing alarm image because alarm frame already written");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alarm_image and (save_jpegs & 2)) {
|
if (alarm_image and (save_jpegs & 2)) {
|
||||||
std::string event_file = stringtf(staticConfig.analyse_file_format.c_str(), path.c_str(), frames);
|
std::string event_file = stringtf(staticConfig.analyse_file_format.c_str(), path.c_str(), frames);
|
||||||
Debug(1, "Writing analysis frame %d", frames);
|
Debug(1, "Writing analysis frame %d to %s", frames, event_file.c_str());
|
||||||
if (!WriteFrameImage(alarm_image, timestamp, event_file.c_str(), true)) {
|
if (!WriteFrameImage(alarm_image, timestamp, event_file.c_str(), true)) {
|
||||||
Error("Failed to write analysis frame image");
|
Error("Failed to write analysis frame image to %s", event_file.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // end if is an alarm frame
|
} // end if is an alarm frame
|
||||||
|
@ -523,11 +460,15 @@ void Event::AddFrame(Image *image,
|
||||||
bool db_frame = ( frame_type == BULK )
|
bool db_frame = ( frame_type == BULK )
|
||||||
or ( frame_type == ALARM )
|
or ( frame_type == ALARM )
|
||||||
or ( frames == 1 )
|
or ( frames == 1 )
|
||||||
or ( score > (int)max_score )
|
or ( score > max_score )
|
||||||
or ( monitor_state == Monitor::ALERT )
|
or ( monitor_state == Monitor::ALERT )
|
||||||
or ( monitor_state == Monitor::ALARM )
|
or ( monitor_state == Monitor::ALARM )
|
||||||
or ( monitor_state == Monitor::PREALARM );
|
or ( monitor_state == Monitor::PREALARM );
|
||||||
|
|
||||||
|
if (score > max_score) {
|
||||||
|
max_score = score;
|
||||||
|
}
|
||||||
|
|
||||||
if (db_frame) {
|
if (db_frame) {
|
||||||
Microseconds delta_time = std::chrono::duration_cast<Microseconds>(timestamp - start_time);
|
Microseconds delta_time = std::chrono::duration_cast<Microseconds>(timestamp - start_time);
|
||||||
Debug(1, "Frame delta is %.2f s - %.2f s = %.2f s, score %u zone_stats.size %zu",
|
Debug(1, "Frame delta is %.2f s - %.2f s = %.2f s, score %u zone_stats.size %zu",
|
||||||
|
@ -546,7 +487,7 @@ void Event::AddFrame(Image *image,
|
||||||
or
|
or
|
||||||
(frame_type == BULK)
|
(frame_type == BULK)
|
||||||
or
|
or
|
||||||
(fps and (frame_data.size() > fps))) {
|
(fps and (frame_data.size() > 5*fps))) {
|
||||||
Debug(1, "Adding %zu frames to DB because write_to_db:%d or frames > analysis fps %f or BULK(%d)",
|
Debug(1, "Adding %zu frames to DB because write_to_db:%d or frames > analysis fps %f or BULK(%d)",
|
||||||
frame_data.size(), write_to_db, fps, (frame_type == BULK));
|
frame_data.size(), write_to_db, fps, (frame_type == BULK));
|
||||||
WriteDbFrames();
|
WriteDbFrames();
|
||||||
|
@ -568,9 +509,6 @@ void Event::AddFrame(Image *image,
|
||||||
} // end if frame_type == BULK
|
} // end if frame_type == BULK
|
||||||
} // end if db_frame
|
} // end if db_frame
|
||||||
|
|
||||||
if (score > (int) max_score) {
|
|
||||||
max_score = score;
|
|
||||||
}
|
|
||||||
end_time = timestamp;
|
end_time = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,3 +588,117 @@ bool Event::SetPath(Storage *storage) {
|
||||||
} // deep storage or not
|
} // deep storage or not
|
||||||
return true;
|
return true;
|
||||||
} // end bool Event::SetPath
|
} // end bool Event::SetPath
|
||||||
|
|
||||||
|
void Event::Run() {
|
||||||
|
Storage *storage = monitor->getStorage();
|
||||||
|
if (!SetPath(storage)) {
|
||||||
|
// Try another
|
||||||
|
Warning("Failed creating event dir at %s", storage->Path());
|
||||||
|
|
||||||
|
std::string sql = stringtf("SELECT `Id` FROM `Storage` WHERE `Id` != %u", storage->Id());
|
||||||
|
if (monitor->ServerId())
|
||||||
|
sql += stringtf(" AND ServerId=%u", monitor->ServerId());
|
||||||
|
|
||||||
|
storage = nullptr;
|
||||||
|
|
||||||
|
MYSQL_RES *result = zmDbFetch(sql);
|
||||||
|
if (result) {
|
||||||
|
for (int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++) {
|
||||||
|
storage = new Storage(atoi(dbrow[0]));
|
||||||
|
if (SetPath(storage))
|
||||||
|
break;
|
||||||
|
delete storage;
|
||||||
|
storage = nullptr;
|
||||||
|
} // end foreach row of Storage
|
||||||
|
mysql_free_result(result);
|
||||||
|
result = nullptr;
|
||||||
|
}
|
||||||
|
if (!storage) {
|
||||||
|
Info("No valid local storage area found. Trying all other areas.");
|
||||||
|
// Try remote
|
||||||
|
sql = "SELECT `Id` FROM `Storage` WHERE ServerId IS NULL";
|
||||||
|
if (monitor->ServerId())
|
||||||
|
sql += stringtf(" OR ServerId != %u", monitor->ServerId());
|
||||||
|
|
||||||
|
result = zmDbFetch(sql);
|
||||||
|
if (result) {
|
||||||
|
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
|
||||||
|
storage = new Storage(atoi(dbrow[0]));
|
||||||
|
if (SetPath(storage))
|
||||||
|
break;
|
||||||
|
delete storage;
|
||||||
|
storage = nullptr;
|
||||||
|
} // end foreach row of Storage
|
||||||
|
mysql_free_result(result);
|
||||||
|
result = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!storage) {
|
||||||
|
storage = new Storage();
|
||||||
|
Warning("Failed to find a storage area to save events.");
|
||||||
|
}
|
||||||
|
sql = stringtf("UPDATE Events SET StorageId = '%d' WHERE Id=%" PRIu64, storage->Id(), id);
|
||||||
|
zmDbDo(sql);
|
||||||
|
} // end if ! setPath(Storage)
|
||||||
|
Debug(1, "Using storage area at %s", path.c_str());
|
||||||
|
|
||||||
|
snapshot_file = path + "/snapshot.jpg";
|
||||||
|
alarm_file = path + "/alarm.jpg";
|
||||||
|
|
||||||
|
video_incomplete_path = path + "/" + video_incomplete_file;
|
||||||
|
|
||||||
|
if (monitor->GetOptVideoWriter() != 0) {
|
||||||
|
/* Save as video */
|
||||||
|
|
||||||
|
videoStore = new VideoStore(
|
||||||
|
video_incomplete_path.c_str(),
|
||||||
|
container.c_str(),
|
||||||
|
monitor->GetVideoStream(),
|
||||||
|
monitor->GetVideoCodecContext(),
|
||||||
|
( monitor->RecordAudio() ? monitor->GetAudioStream() : nullptr ),
|
||||||
|
( monitor->RecordAudio() ? monitor->GetAudioCodecContext() : nullptr ),
|
||||||
|
monitor );
|
||||||
|
|
||||||
|
if ( !videoStore->open() ) {
|
||||||
|
Warning("Failed to open videostore, turning on jpegs");
|
||||||
|
delete videoStore;
|
||||||
|
videoStore = nullptr;
|
||||||
|
if ( ! ( save_jpegs & 1 ) ) {
|
||||||
|
save_jpegs |= 1; // Turn on jpeg storage
|
||||||
|
zmDbDo(stringtf("UPDATE Events SET SaveJpegs=%d WHERE Id=%" PRIu64, save_jpegs, id));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::string codec = videoStore->get_codec();
|
||||||
|
video_file = stringtf("%" PRIu64 "-%s.%s.%s", id, "video", codec.c_str(), container.c_str());
|
||||||
|
video_path = path + "/" + video_file;
|
||||||
|
Debug(1, "Video file is %s", video_file.c_str());
|
||||||
|
}
|
||||||
|
} // end if GetOptVideoWriter
|
||||||
|
if (storage != monitor->getStorage())
|
||||||
|
delete storage;
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lck(packet_queue_mutex);
|
||||||
|
|
||||||
|
// The idea is to process the queue no matter what so that all packets get processed.
|
||||||
|
// We only break if the queue is empty
|
||||||
|
while (true) {
|
||||||
|
if (!packet_queue.empty()) {
|
||||||
|
Debug(1, "adding packet");
|
||||||
|
const ZMLockedPacket * packet_lock = packet_queue.front();
|
||||||
|
this->AddPacket_(packet_lock->packet_);
|
||||||
|
delete packet_lock;
|
||||||
|
packet_queue.pop();
|
||||||
|
} else {
|
||||||
|
if (terminate_ or zm_terminate) {
|
||||||
|
Debug(1, "terminating");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Debug(1, "waiting");
|
||||||
|
packet_queue_condition.wait(lck);
|
||||||
|
Debug(1, "wakeing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int Event::MonitorId() {
|
||||||
|
return monitor->Id();
|
||||||
|
}
|
||||||
|
|
|
@ -22,14 +22,21 @@
|
||||||
|
|
||||||
#include "zm_config.h"
|
#include "zm_config.h"
|
||||||
#include "zm_define.h"
|
#include "zm_define.h"
|
||||||
|
#include "zm_packet.h"
|
||||||
#include "zm_storage.h"
|
#include "zm_storage.h"
|
||||||
#include "zm_time.h"
|
#include "zm_time.h"
|
||||||
#include "zm_utils.h"
|
#include "zm_utils.h"
|
||||||
#include "zm_zone.h"
|
#include "zm_zone.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
|
||||||
class EventStream;
|
class EventStream;
|
||||||
class Frame;
|
class Frame;
|
||||||
|
@ -77,8 +84,8 @@ class Event {
|
||||||
int frames;
|
int frames;
|
||||||
int alarm_frames;
|
int alarm_frames;
|
||||||
bool alarm_frame_written;
|
bool alarm_frame_written;
|
||||||
unsigned int tot_score;
|
int tot_score;
|
||||||
unsigned int max_score;
|
int max_score;
|
||||||
std::string path;
|
std::string path;
|
||||||
std::string snapshot_file;
|
std::string snapshot_file;
|
||||||
std::string alarm_file;
|
std::string alarm_file;
|
||||||
|
@ -98,6 +105,15 @@ class Event {
|
||||||
|
|
||||||
void createNotes(std::string ¬es);
|
void createNotes(std::string ¬es);
|
||||||
|
|
||||||
|
std::queue<ZMLockedPacket *> packet_queue;
|
||||||
|
std::mutex packet_queue_mutex;
|
||||||
|
std::condition_variable packet_queue_condition;
|
||||||
|
|
||||||
|
void Run();
|
||||||
|
|
||||||
|
std::atomic<bool> terminate_;
|
||||||
|
std::thread thread_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static bool OpenFrameSocket(int);
|
static bool OpenFrameSocket(int);
|
||||||
static bool ValidateFrameSocket(int);
|
static bool ValidateFrameSocket(int);
|
||||||
|
@ -110,41 +126,50 @@ class Event {
|
||||||
|
|
||||||
uint64_t Id() const { return id; }
|
uint64_t Id() const { return id; }
|
||||||
const std::string &Cause() const { return cause; }
|
const std::string &Cause() const { return cause; }
|
||||||
|
void addNote(const char *cause, const std::string ¬e);
|
||||||
int Frames() const { return frames; }
|
int Frames() const { return frames; }
|
||||||
int AlarmFrames() const { return alarm_frames; }
|
int AlarmFrames() const { return alarm_frames; }
|
||||||
|
|
||||||
SystemTimePoint StartTime() const { return start_time; }
|
SystemTimePoint StartTime() const { return start_time; }
|
||||||
SystemTimePoint EndTime() const { return end_time; }
|
SystemTimePoint EndTime() const { return end_time; }
|
||||||
|
TimePoint::duration Duration() const { return end_time - start_time; };
|
||||||
|
|
||||||
void AddPacket(const std::shared_ptr<ZMPacket> &p);
|
void AddPacket(ZMLockedPacket *);
|
||||||
|
void AddPacket_(const std::shared_ptr<ZMPacket> &p);
|
||||||
bool WritePacket(const std::shared_ptr<ZMPacket> &p);
|
bool WritePacket(const std::shared_ptr<ZMPacket> &p);
|
||||||
bool SendFrameImage(const Image *image, bool alarm_frame=false);
|
bool SendFrameImage(const Image *image, bool alarm_frame=false);
|
||||||
bool WriteFrameImage(Image *image, SystemTimePoint timestamp, const char *event_file, bool alarm_frame = false) const;
|
bool WriteFrameImage(Image *image, SystemTimePoint timestamp, const char *event_file, bool alarm_frame = false) const;
|
||||||
|
|
||||||
void updateNotes(const StringSetMap &stringSetMap);
|
void updateNotes(const StringSetMap &stringSetMap);
|
||||||
|
|
||||||
void AddFrame(Image *image,
|
void AddFrame(Image *image,
|
||||||
SystemTimePoint timestamp,
|
SystemTimePoint timestamp,
|
||||||
const std::vector<ZoneStats> &stats,
|
const std::vector<ZoneStats> &stats,
|
||||||
int score = 0,
|
int score = 0,
|
||||||
Image *alarm_image = nullptr);
|
Image *alarm_image = nullptr);
|
||||||
|
|
||||||
|
void Stop() {
|
||||||
|
terminate_ = true;
|
||||||
|
packet_queue_condition.notify_all();
|
||||||
|
}
|
||||||
|
bool Stopped() const { return terminate_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void WriteDbFrames();
|
void WriteDbFrames();
|
||||||
bool SetPath(Storage *storage);
|
bool SetPath(Storage *storage);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static std::string getSubPath(tm time) {
|
static std::string getSubPath(tm time) {
|
||||||
std::string subpath = stringtf("%02d/%02d/%02d/%02d/%02d/%02d",
|
std::string subpath = stringtf("%02d/%02d/%02d/%02d/%02d/%02d",
|
||||||
time.tm_year - 100, time.tm_mon + 1, time.tm_mday,
|
time.tm_year - 100, time.tm_mon + 1, time.tm_mday,
|
||||||
time.tm_hour, time.tm_min, time.tm_sec);
|
time.tm_hour, time.tm_min, time.tm_sec);
|
||||||
return subpath;
|
return subpath;
|
||||||
}
|
}
|
||||||
static std::string getSubPath(time_t *time) {
|
static std::string getSubPath(time_t *time) {
|
||||||
tm time_tm = {};
|
tm time_tm = {};
|
||||||
localtime_r(time, &time_tm);
|
localtime_r(time, &time_tm);
|
||||||
return Event::getSubPath(time_tm);
|
return Event::getSubPath(time_tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* getEventFile() const {
|
const char* getEventFile() const {
|
||||||
return video_file.c_str();
|
return video_file.c_str();
|
||||||
|
@ -154,18 +179,6 @@ class Event {
|
||||||
return pre_alarm_count;
|
return pre_alarm_count;
|
||||||
}
|
}
|
||||||
static void EmptyPreAlarmFrames() {
|
static void EmptyPreAlarmFrames() {
|
||||||
#if 0
|
|
||||||
while ( pre_alarm_count > 0 ) {
|
|
||||||
int i = pre_alarm_count - 1;
|
|
||||||
delete pre_alarm_data[i].image;
|
|
||||||
pre_alarm_data[i].image = nullptr;
|
|
||||||
if ( pre_alarm_data[i].alarm_frame ) {
|
|
||||||
delete pre_alarm_data[i].alarm_frame;
|
|
||||||
pre_alarm_data[i].alarm_frame = nullptr;
|
|
||||||
}
|
|
||||||
pre_alarm_count--;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
pre_alarm_count = 0;
|
pre_alarm_count = 0;
|
||||||
}
|
}
|
||||||
static void AddPreAlarmFrame(
|
static void AddPreAlarmFrame(
|
||||||
|
@ -174,28 +187,11 @@ class Event {
|
||||||
int score=0,
|
int score=0,
|
||||||
Image *alarm_frame=nullptr
|
Image *alarm_frame=nullptr
|
||||||
) {
|
) {
|
||||||
#if 0
|
|
||||||
pre_alarm_data[pre_alarm_count].image = new Image(*image);
|
|
||||||
pre_alarm_data[pre_alarm_count].timestamp = timestamp;
|
|
||||||
pre_alarm_data[pre_alarm_count].score = score;
|
|
||||||
if ( alarm_frame ) {
|
|
||||||
pre_alarm_data[pre_alarm_count].alarm_frame = new Image(*alarm_frame);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
pre_alarm_count++;
|
pre_alarm_count++;
|
||||||
}
|
}
|
||||||
void SavePreAlarmFrames() {
|
void SavePreAlarmFrames() {
|
||||||
#if 0
|
|
||||||
for ( int i = 0; i < pre_alarm_count; i++ ) {
|
|
||||||
AddFrame(
|
|
||||||
pre_alarm_data[i].image,
|
|
||||||
pre_alarm_data[i].timestamp,
|
|
||||||
pre_alarm_data[i].score,
|
|
||||||
pre_alarm_data[i].alarm_frame);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
EmptyPreAlarmFrames();
|
EmptyPreAlarmFrames();
|
||||||
}
|
}
|
||||||
|
int MonitorId();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_EVENT_H
|
#endif // ZM_EVENT_H
|
||||||
|
|
|
@ -68,17 +68,17 @@ bool EventStream::loadInitialEventData(int monitor_id, SystemTimePoint event_tim
|
||||||
curr_frame_id = 1; // curr_frame_id is 1-based
|
curr_frame_id = 1; // curr_frame_id is 1-based
|
||||||
if (event_time >= event_data->start_time) {
|
if (event_time >= event_data->start_time) {
|
||||||
Debug(2, "event time is after event start");
|
Debug(2, "event time is after event start");
|
||||||
for (unsigned int i = 0; i < event_data->frame_count; i++) {
|
for (int i = 0; i < event_data->frame_count; i++) {
|
||||||
//Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time );
|
//Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time );
|
||||||
if (event_data->frames[i].timestamp >= event_time) {
|
if (event_data->frames[i].timestamp >= event_time) {
|
||||||
curr_frame_id = i + 1;
|
curr_frame_id = i + 1;
|
||||||
Debug(3, "Set curr_stream_time: %.2f, curr_frame_id: %ld",
|
Debug(3, "Set curr_stream_time: %.2f, curr_frame_id: %d",
|
||||||
FPSeconds(curr_stream_time.time_since_epoch()).count(),
|
FPSeconds(curr_stream_time.time_since_epoch()).count(),
|
||||||
curr_frame_id);
|
curr_frame_id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} // end foreach frame
|
} // end foreach frame
|
||||||
Debug(3, "Skipping %ld frames", event_data->frame_count);
|
Debug(3, "Skipping %d frames", event_data->frame_count);
|
||||||
} else {
|
} else {
|
||||||
Warning("Requested an event time less than the start of the event. event_time %" PRIi64 " < start_time %" PRIi64,
|
Warning("Requested an event time less than the start of the event. event_time %" PRIi64 " < start_time %" PRIi64,
|
||||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(event_time.time_since_epoch()).count()),
|
static_cast<int64>(std::chrono::duration_cast<Seconds>(event_time.time_since_epoch()).count()),
|
||||||
|
@ -90,13 +90,13 @@ bool EventStream::loadInitialEventData(int monitor_id, SystemTimePoint event_tim
|
||||||
|
|
||||||
bool EventStream::loadInitialEventData(
|
bool EventStream::loadInitialEventData(
|
||||||
uint64_t init_event_id,
|
uint64_t init_event_id,
|
||||||
unsigned int init_frame_id
|
int init_frame_id
|
||||||
) {
|
) {
|
||||||
loadEventData(init_event_id);
|
loadEventData(init_event_id);
|
||||||
|
|
||||||
if ( init_frame_id ) {
|
if ( init_frame_id ) {
|
||||||
if ( init_frame_id >= event_data->frame_count ) {
|
if ( init_frame_id >= event_data->frame_count ) {
|
||||||
Error("Invalid frame id specified. %d > %lu", init_frame_id, event_data->frame_count);
|
Error("Invalid frame id specified. %d > %d", init_frame_id, event_data->frame_count);
|
||||||
curr_stream_time = event_data->start_time;
|
curr_stream_time = event_data->start_time;
|
||||||
curr_frame_id = 1;
|
curr_frame_id = 1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -225,20 +225,24 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
||||||
|
|
||||||
sql = stringtf("SELECT `FrameId`, unix_timestamp(`TimeStamp`), `Delta` "
|
sql = stringtf("SELECT `FrameId`, unix_timestamp(`TimeStamp`), `Delta` "
|
||||||
"FROM `Frames` WHERE `EventId` = %" PRIu64 " ORDER BY `FrameId` ASC", event_id);
|
"FROM `Frames` WHERE `EventId` = %" PRIu64 " ORDER BY `FrameId` ASC", event_id);
|
||||||
|
|
||||||
result = zmDbFetch(sql);
|
result = zmDbFetch(sql);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
event_data->n_frames = mysql_num_rows(result);
|
event_data->n_frames = mysql_num_rows(result);
|
||||||
|
|
||||||
event_data->frames = new FrameData[event_data->frame_count];
|
event_data->frames = new FrameData[event_data->frame_count];
|
||||||
|
if (event_data->frame_count < event_data->n_frames) {
|
||||||
|
event_data->frame_count = event_data->n_frames;
|
||||||
|
Warning("Event %" PRId64 " has more frames in the Frames table (%d) than in the Event record (%d)",
|
||||||
|
event_data->event_id, event_data->n_frames, event_data->frame_count);
|
||||||
|
}
|
||||||
|
|
||||||
int last_id = 0;
|
int last_id = 0;
|
||||||
SystemTimePoint last_timestamp = event_data->start_time;
|
SystemTimePoint last_timestamp = event_data->start_time;
|
||||||
Microseconds last_delta = Seconds(0);
|
Microseconds last_delta = Seconds(0);
|
||||||
|
|
||||||
while ( ( dbrow = mysql_fetch_row(result) ) ) {
|
while ((dbrow = mysql_fetch_row(result))) {
|
||||||
int id = atoi(dbrow[0]);
|
int id = atoi(dbrow[0]);
|
||||||
//timestamp = atof(dbrow[1]);
|
//timestamp = atof(dbrow[1]);
|
||||||
Microseconds delta = std::chrono::duration_cast<Microseconds>(FPSeconds(atof(dbrow[2])));
|
Microseconds delta = std::chrono::duration_cast<Microseconds>(FPSeconds(atof(dbrow[2])));
|
||||||
|
@ -280,7 +284,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
||||||
// Incomplete events might not have any frame data
|
// Incomplete events might not have any frame data
|
||||||
event_data->last_frame_id = last_id;
|
event_data->last_frame_id = last_id;
|
||||||
|
|
||||||
if ( mysql_errno(&dbconn) ) {
|
if (mysql_errno(&dbconn)) {
|
||||||
Error("Can't fetch row: %s", mysql_error(&dbconn));
|
Error("Can't fetch row: %s", mysql_error(&dbconn));
|
||||||
exit(mysql_errno(&dbconn));
|
exit(mysql_errno(&dbconn));
|
||||||
}
|
}
|
||||||
|
@ -310,7 +314,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
||||||
else
|
else
|
||||||
curr_stream_time = event_data->frames[event_data->last_frame_id-1].timestamp;
|
curr_stream_time = event_data->frames[event_data->last_frame_id-1].timestamp;
|
||||||
}
|
}
|
||||||
Debug(2, "Event: %" PRIu64 ", Frames: %ld, Last Frame ID (%ld, Duration: %.2f s Frames Duration: %.2f s",
|
Debug(2, "Event: %" PRIu64 ", Frames: %d, Last Frame ID (%d, Duration: %.2f s Frames Duration: %.2f s",
|
||||||
event_data->event_id,
|
event_data->event_id,
|
||||||
event_data->frame_count,
|
event_data->frame_count,
|
||||||
event_data->last_frame_id,
|
event_data->last_frame_id,
|
||||||
|
@ -342,12 +346,12 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
||||||
if (
|
if (
|
||||||
(mode == MODE_SINGLE || mode == MODE_NONE)
|
(mode == MODE_SINGLE || mode == MODE_NONE)
|
||||||
&&
|
&&
|
||||||
((unsigned int)curr_frame_id == event_data->last_frame_id)
|
(curr_frame_id == event_data->last_frame_id)
|
||||||
) {
|
) {
|
||||||
Debug(1, "Was in single or no replay mode, and at last frame, so jumping to 1st frame");
|
Debug(1, "Was in single or no replay mode, and at last frame, so jumping to 1st frame");
|
||||||
curr_frame_id = 1;
|
curr_frame_id = 1;
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "mode is %s, current frame is %ld, frame count is %ld, last frame id is %ld",
|
Debug(1, "mode is %s, current frame is %d, frame count is %d, last frame id is %d",
|
||||||
StreamMode_Strings[(int) mode].c_str(),
|
StreamMode_Strings[(int) mode].c_str(),
|
||||||
curr_frame_id,
|
curr_frame_id,
|
||||||
event_data->frame_count,
|
event_data->frame_count,
|
||||||
|
@ -404,9 +408,9 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
||||||
paused = true;
|
paused = true;
|
||||||
replay_rate = ZM_RATE_BASE;
|
replay_rate = ZM_RATE_BASE;
|
||||||
step = 1;
|
step = 1;
|
||||||
if ( (unsigned int)curr_frame_id < event_data->last_frame_id )
|
if (curr_frame_id < event_data->last_frame_id)
|
||||||
curr_frame_id += 1;
|
curr_frame_id += 1;
|
||||||
Debug(1, "Got SLOWFWD command new frame id %ld", curr_frame_id);
|
Debug(1, "Got SLOWFWD command new frame id %d", curr_frame_id);
|
||||||
break;
|
break;
|
||||||
case CMD_SLOWREV :
|
case CMD_SLOWREV :
|
||||||
paused = true;
|
paused = true;
|
||||||
|
@ -414,7 +418,7 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
||||||
step = -1;
|
step = -1;
|
||||||
curr_frame_id -= 1;
|
curr_frame_id -= 1;
|
||||||
if ( curr_frame_id < 1 ) curr_frame_id = 1;
|
if ( curr_frame_id < 1 ) curr_frame_id = 1;
|
||||||
Debug(1, "Got SLOWREV command new frame id %ld", curr_frame_id);
|
Debug(1, "Got SLOWREV command new frame id %d", curr_frame_id);
|
||||||
break;
|
break;
|
||||||
case CMD_FASTREV :
|
case CMD_FASTREV :
|
||||||
Debug(1, "Got FAST REV command");
|
Debug(1, "Got FAST REV command");
|
||||||
|
@ -538,12 +542,12 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
||||||
|
|
||||||
if ( curr_frame_id < 1 ) {
|
if ( curr_frame_id < 1 ) {
|
||||||
curr_frame_id = 1;
|
curr_frame_id = 1;
|
||||||
} else if ( (unsigned long)curr_frame_id > event_data->last_frame_id ) {
|
} else if (curr_frame_id > event_data->last_frame_id) {
|
||||||
curr_frame_id = event_data->last_frame_id;
|
curr_frame_id = event_data->last_frame_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
curr_stream_time = event_data->frames[curr_frame_id-1].timestamp;
|
curr_stream_time = event_data->frames[curr_frame_id-1].timestamp;
|
||||||
Debug(1, "Got SEEK command, to %f s (new current frame id: %ld offset %f s)",
|
Debug(1, "Got SEEK command, to %f s (new current frame id: %d offset %f s)",
|
||||||
FPSeconds(offset).count(),
|
FPSeconds(offset).count(),
|
||||||
curr_frame_id,
|
curr_frame_id,
|
||||||
FPSeconds(event_data->frames[curr_frame_id - 1].offset).count());
|
FPSeconds(event_data->frames[curr_frame_id - 1].offset).count());
|
||||||
|
@ -615,11 +619,11 @@ bool EventStream::checkEventLoaded() {
|
||||||
sql = stringtf(
|
sql = stringtf(
|
||||||
"SELECT `Id` FROM `Events` WHERE `MonitorId` = %d AND `Id` < %" PRIu64 " ORDER BY `Id` DESC LIMIT 1",
|
"SELECT `Id` FROM `Events` WHERE `MonitorId` = %d AND `Id` < %" PRIu64 " ORDER BY `Id` DESC LIMIT 1",
|
||||||
event_data->monitor_id, event_data->event_id);
|
event_data->monitor_id, event_data->event_id);
|
||||||
} else if ( (unsigned int)curr_frame_id > event_data->last_frame_id ) {
|
} else if (curr_frame_id > event_data->last_frame_id) {
|
||||||
if (event_data->end_time.time_since_epoch() == Seconds(0)) {
|
if (event_data->end_time.time_since_epoch() == Seconds(0)) {
|
||||||
// We are viewing an in-process event, so just reload it.
|
// We are viewing an in-process event, so just reload it.
|
||||||
loadEventData(event_data->event_id);
|
loadEventData(event_data->event_id);
|
||||||
if ( (unsigned int)curr_frame_id > event_data->last_frame_id )
|
if (curr_frame_id > event_data->last_frame_id)
|
||||||
curr_frame_id = event_data->last_frame_id;
|
curr_frame_id = event_data->last_frame_id;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -628,7 +632,7 @@ bool EventStream::checkEventLoaded() {
|
||||||
event_data->monitor_id, event_data->event_id);
|
event_data->monitor_id, event_data->event_id);
|
||||||
} else {
|
} else {
|
||||||
// No event change required
|
// No event change required
|
||||||
Debug(3, "No event change required, as curr frame %ld <=> event frames %lu",
|
Debug(3, "No event change required, as curr frame %d <=> event frames %d",
|
||||||
curr_frame_id, event_data->frame_count);
|
curr_frame_id, event_data->frame_count);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -662,8 +666,8 @@ bool EventStream::checkEventLoaded() {
|
||||||
curr_frame_id = event_data->last_frame_id;
|
curr_frame_id = event_data->last_frame_id;
|
||||||
else
|
else
|
||||||
curr_frame_id = 1;
|
curr_frame_id = 1;
|
||||||
Debug(2, "New frame id = %ld", curr_frame_id);
|
Debug(2, "New frame id = %d", curr_frame_id);
|
||||||
start = std::chrono::system_clock::now();
|
start = std::chrono::steady_clock::now();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
Debug(2, "No next event loaded using %s. Pausing", sql.c_str());
|
Debug(2, "No next event loaded using %s. Pausing", sql.c_str());
|
||||||
|
@ -689,22 +693,20 @@ bool EventStream::checkEventLoaded() {
|
||||||
|
|
||||||
Image * EventStream::getImage( ) {
|
Image * EventStream::getImage( ) {
|
||||||
std::string path = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
std::string path = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
||||||
Debug(2, "EventStream::getImage path(%s) from %s frame(%ld) ", path.c_str(), event_data->path.c_str(), curr_frame_id);
|
Debug(2, "EventStream::getImage path(%s) from %s frame(%d) ", path.c_str(), event_data->path.c_str(), curr_frame_id);
|
||||||
Image *image = new Image(path.c_str());
|
Image *image = new Image(path.c_str());
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventStream::sendFrame(Microseconds delta_us) {
|
bool EventStream::sendFrame(Microseconds delta_us) {
|
||||||
Debug(2, "Sending frame %ld", curr_frame_id);
|
Debug(2, "Sending frame %d", curr_frame_id);
|
||||||
|
|
||||||
std::string filepath;
|
std::string filepath;
|
||||||
struct stat filestat = {};
|
struct stat filestat = {};
|
||||||
|
|
||||||
// This needs to be abstracted. If we are saving jpgs, then load the capture file.
|
// This needs to be abstracted. If we are saving jpgs, then load the capture file.
|
||||||
// If we are only saving analysis frames, then send that.
|
// If we are only saving analysis frames, then send that.
|
||||||
if (event_data->SaveJPEGs & 1) {
|
if ((frame_type == FRAME_ANALYSIS) && (event_data->SaveJPEGs & 2)) {
|
||||||
filepath = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
|
||||||
} else if (event_data->SaveJPEGs & 2) {
|
|
||||||
filepath = stringtf(staticConfig.analyse_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
filepath = stringtf(staticConfig.analyse_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
||||||
if (stat(filepath.c_str(), &filestat) < 0) {
|
if (stat(filepath.c_str(), &filestat) < 0) {
|
||||||
Debug(1, "analyze file %s not found will try to stream from other", filepath.c_str());
|
Debug(1, "analyze file %s not found will try to stream from other", filepath.c_str());
|
||||||
|
@ -714,7 +716,9 @@ bool EventStream::sendFrame(Microseconds delta_us) {
|
||||||
filepath = "";
|
filepath = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ( !ffmpeg_input ) {
|
} else if (event_data->SaveJPEGs & 1) {
|
||||||
|
filepath = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
||||||
|
} 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");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -795,7 +799,13 @@ bool EventStream::sendFrame(Microseconds delta_us) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Image *send_image = prepareImage(image);
|
Image *send_image = prepareImage(image);
|
||||||
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
|
if (temp_img_buffer_size < send_image->Size()) {
|
||||||
|
Debug(1, "Resizing image buffer from %zu to %u",
|
||||||
|
temp_img_buffer_size, send_image->Size());
|
||||||
|
delete[] temp_img_buffer;
|
||||||
|
temp_img_buffer = new uint8_t[send_image->Size()];
|
||||||
|
temp_img_buffer_size = send_image->Size();
|
||||||
|
}
|
||||||
int img_buffer_size = 0;
|
int img_buffer_size = 0;
|
||||||
uint8_t *img_buffer = temp_img_buffer;
|
uint8_t *img_buffer = temp_img_buffer;
|
||||||
|
|
||||||
|
@ -837,12 +847,13 @@ void EventStream::runStream() {
|
||||||
|
|
||||||
//checkInitialised();
|
//checkInitialised();
|
||||||
|
|
||||||
if ( type == STREAM_JPEG )
|
if (type == STREAM_JPEG)
|
||||||
fputs("Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n\r\n", stdout);
|
fputs("Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n\r\n", stdout);
|
||||||
|
|
||||||
if ( !event_data ) {
|
if (!event_data) {
|
||||||
sendTextFrame("No event data found");
|
sendTextFrame("No event data found");
|
||||||
exit(0);
|
zm_terminate = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
double fps = 1.0;
|
double fps = 1.0;
|
||||||
|
@ -851,13 +862,13 @@ void EventStream::runStream() {
|
||||||
}
|
}
|
||||||
updateFrameRate(fps);
|
updateFrameRate(fps);
|
||||||
|
|
||||||
start = std::chrono::system_clock::now();
|
start = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
SystemTimePoint::duration last_frame_offset = Seconds(0);
|
SystemTimePoint::duration last_frame_offset = Seconds(0);
|
||||||
SystemTimePoint::duration time_to_event = Seconds(0);
|
SystemTimePoint::duration time_to_event = Seconds(0);
|
||||||
|
|
||||||
while ( !zm_terminate ) {
|
while ( !zm_terminate ) {
|
||||||
now = std::chrono::system_clock::now();
|
now = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
Microseconds delta = Microseconds(0);
|
Microseconds delta = Microseconds(0);
|
||||||
send_frame = false;
|
send_frame = false;
|
||||||
|
@ -880,7 +891,7 @@ void EventStream::runStream() {
|
||||||
|
|
||||||
if ( !paused ) {
|
if ( !paused ) {
|
||||||
// Figure out if we should send this frame
|
// Figure out if we should send this frame
|
||||||
Debug(3, "not paused at cur_frame_id (%ld-1) mod frame_mod(%d)", curr_frame_id, frame_mod);
|
Debug(3, "not paused at cur_frame_id (%d-1) mod frame_mod(%d)", curr_frame_id, frame_mod);
|
||||||
// If we are streaming and this frame is due to be sent
|
// If we are streaming and this frame is due to be sent
|
||||||
// frame mod defaults to 1 and if we are going faster than max_fps will get multiplied by 2
|
// frame mod defaults to 1 and if we are going faster than max_fps will get multiplied by 2
|
||||||
// so if it is 2, then we send every other frame, if is it 4 then every fourth frame, etc.
|
// so if it is 2, then we send every other frame, if is it 4 then every fourth frame, etc.
|
||||||
|
@ -904,7 +915,7 @@ void EventStream::runStream() {
|
||||||
|
|
||||||
// time_to_event > 0 means that we are not in the event
|
// time_to_event > 0 means that we are not in the event
|
||||||
if (time_to_event > Seconds(0) and mode == MODE_ALL) {
|
if (time_to_event > Seconds(0) and mode == MODE_ALL) {
|
||||||
SystemTimePoint::duration time_since_last_send = now - last_frame_sent;
|
TimePoint::duration time_since_last_send = now - last_frame_sent;
|
||||||
Debug(1, "Time since last send = %.2f s", FPSeconds(time_since_last_send).count());
|
Debug(1, "Time since last send = %.2f s", FPSeconds(time_since_last_send).count());
|
||||||
if (time_since_last_send > Seconds(1)) {
|
if (time_since_last_send > Seconds(1)) {
|
||||||
char frame_text[64];
|
char frame_text[64];
|
||||||
|
@ -976,13 +987,13 @@ void EventStream::runStream() {
|
||||||
// +/- 1? What if we are skipping frames?
|
// +/- 1? What if we are skipping frames?
|
||||||
curr_frame_id += (replay_rate>0) ? frame_mod : -1*frame_mod;
|
curr_frame_id += (replay_rate>0) ? frame_mod : -1*frame_mod;
|
||||||
// sending the frame may have taken some time, so reload now
|
// sending the frame may have taken some time, so reload now
|
||||||
now = std::chrono::system_clock::now();
|
now = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
// we incremented by replay_rate, so might have jumped past frame_count
|
// we incremented by replay_rate, so might have jumped past frame_count
|
||||||
if ( (mode == MODE_SINGLE) && (
|
if ( (mode == MODE_SINGLE) && (
|
||||||
(curr_frame_id < 1 )
|
(curr_frame_id < 1 )
|
||||||
||
|
||
|
||||||
((unsigned int)curr_frame_id >= event_data->frame_count)
|
(curr_frame_id >= event_data->frame_count)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Debug(2, "Have mode==MODE_SINGLE and at end of event, looping back to start");
|
Debug(2, "Have mode==MODE_SINGLE and at end of event, looping back to start");
|
||||||
|
@ -990,45 +1001,51 @@ void EventStream::runStream() {
|
||||||
// Have to reset start to now when replaying
|
// Have to reset start to now when replaying
|
||||||
start = now;
|
start = now;
|
||||||
}
|
}
|
||||||
frame_data = &event_data->frames[curr_frame_id-1];
|
|
||||||
|
|
||||||
// frame_data->delta is the time since last frame as a float in seconds
|
if (curr_frame_id <= event_data->frame_count) {
|
||||||
// but what if we are skipping frames? We need the distance from the last frame sent
|
frame_data = &event_data->frames[curr_frame_id-1];
|
||||||
// Also, what about reverse? needs to be absolute value
|
|
||||||
|
|
||||||
// There are two ways to go about this, not sure which is correct.
|
// frame_data->delta is the time since last frame as a float in seconds
|
||||||
// you can calculate the relationship between now and the start
|
// but what if we are skipping frames? We need the distance from the last frame sent
|
||||||
// or calc the relationship from the last frame. I think from the start is better as it self-corrects
|
// Also, what about reverse? needs to be absolute value
|
||||||
//
|
|
||||||
if (last_frame_offset != Seconds(0)) {
|
|
||||||
// We assume that we are going forward and the next frame is in the future.
|
|
||||||
delta = std::chrono::duration_cast<Microseconds>(frame_data->offset - (now - start));
|
|
||||||
|
|
||||||
Debug(2, "New delta: now - start = %" PRIu64 " us offset %" PRIi64 " us- elapsed = %" PRIu64 " us",
|
// There are two ways to go about this, not sure which is correct.
|
||||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(now - start).count()),
|
// you can calculate the relationship between now and the start
|
||||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(frame_data->offset).count()),
|
// or calc the relationship from the last frame. I think from the start is better as it self-corrects
|
||||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()));
|
//
|
||||||
} else {
|
if (last_frame_offset != Seconds(0)) {
|
||||||
Debug(2, "No last frame_offset, no sleep");
|
// We assume that we are going forward and the next frame is in the future.
|
||||||
delta = Seconds(0);
|
delta = std::chrono::duration_cast<Microseconds>(frame_data->offset - (now - start));
|
||||||
}
|
|
||||||
last_frame_offset = frame_data->offset;
|
|
||||||
|
|
||||||
if (send_frame && type != STREAM_MPEG) {
|
Debug(2, "New delta: now - start = %" PRIu64 " us offset %" PRIi64 " us- elapsed = %" PRIu64 " us",
|
||||||
if (delta != Seconds(0)) {
|
static_cast<int64>(std::chrono::duration_cast<Microseconds>(now - start).count()),
|
||||||
if (delta > MAX_SLEEP) {
|
static_cast<int64>(std::chrono::duration_cast<Microseconds>(frame_data->offset).count()),
|
||||||
Debug(1, "Limiting sleep to %" PRIi64 " ms because calculated sleep is too long: %" PRIi64" us",
|
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()));
|
||||||
|
} else {
|
||||||
|
Debug(2, "No last frame_offset, no sleep");
|
||||||
|
delta = Seconds(0);
|
||||||
|
}
|
||||||
|
last_frame_offset = frame_data->offset;
|
||||||
|
|
||||||
|
if (send_frame && type != STREAM_MPEG) {
|
||||||
|
if (delta != Seconds(0)) {
|
||||||
|
if (delta > MAX_SLEEP) {
|
||||||
|
Debug(1, "Limiting sleep to %" PRIi64 " ms because calculated sleep is too long: %" PRIi64" us",
|
||||||
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(MAX_SLEEP).count()),
|
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(MAX_SLEEP).count()),
|
||||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()));
|
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()));
|
||||||
delta = MAX_SLEEP;
|
delta = MAX_SLEEP;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::this_thread::sleep_for(delta);
|
std::this_thread::sleep_for(delta);
|
||||||
Debug(3, "Done sleeping: %" PRIi64 " us",
|
Debug(3, "Done sleeping: %" PRIi64 " us",
|
||||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()));
|
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()));
|
||||||
}
|
}
|
||||||
}
|
} // end if need to sleep
|
||||||
|
} else {
|
||||||
|
Debug(1, "invalid curr_frame_id %d !< %d", curr_frame_id, event_data->frame_count);
|
||||||
|
} // end if not at end of event
|
||||||
} else {
|
} else {
|
||||||
|
// Paused
|
||||||
delta = std::chrono::duration_cast<Microseconds>(FPSeconds(
|
delta = std::chrono::duration_cast<Microseconds>(FPSeconds(
|
||||||
ZM_RATE_BASE / ((base_fps ? base_fps : 1) * (replay_rate ? abs(replay_rate * 2) : 2))));
|
ZM_RATE_BASE / ((base_fps ? base_fps : 1) * (replay_rate ? abs(replay_rate * 2) : 2))));
|
||||||
|
|
||||||
|
@ -1087,62 +1104,57 @@ void EventStream::runStream() {
|
||||||
} // end void EventStream::runStream()
|
} // end void EventStream::runStream()
|
||||||
|
|
||||||
bool EventStream::send_file(const std::string &filepath) {
|
bool EventStream::send_file(const std::string &filepath) {
|
||||||
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
|
FILE *fdj = fopen(filepath.c_str(), "rb");
|
||||||
|
if (!fdj) {
|
||||||
int img_buffer_size = 0;
|
|
||||||
uint8_t *img_buffer = temp_img_buffer;
|
|
||||||
|
|
||||||
FILE *fdj = nullptr;
|
|
||||||
fdj = fopen(filepath.c_str(), "rb");
|
|
||||||
if ( !fdj ) {
|
|
||||||
Error("Can't open %s: %s", filepath.c_str(), strerror(errno));
|
Error("Can't open %s: %s", filepath.c_str(), strerror(errno));
|
||||||
std::string error_message = stringtf("Can't open %s: %s", filepath.c_str(), strerror(errno));
|
std::string error_message = stringtf("Can't open %s: %s", filepath.c_str(), strerror(errno));
|
||||||
return sendTextFrame(error_message.c_str());
|
return sendTextFrame(error_message.c_str());
|
||||||
}
|
}
|
||||||
#if HAVE_SENDFILE
|
|
||||||
static struct stat filestat;
|
static struct stat filestat;
|
||||||
if ( fstat(fileno(fdj), &filestat) < 0 ) {
|
if (fstat(fileno(fdj), &filestat) < 0) {
|
||||||
fclose(fdj); /* Close the file handle */
|
fclose(fdj); /* Close the file handle */
|
||||||
Error("Failed getting information about file %s: %s", filepath.c_str(), strerror(errno));
|
Error("Failed getting information about file %s: %s", filepath.c_str(), strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ( !filestat.st_size ) {
|
if (!filestat.st_size) {
|
||||||
fclose(fdj); /* Close the file handle */
|
fclose(fdj); /* Close the file handle */
|
||||||
Info("File size is zero. Unable to send raw frame %ld: %s", curr_frame_id, strerror(errno));
|
Info("File size is zero. Unable to send raw frame %d: %s", curr_frame_id, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ( 0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size) ) {
|
if (0 > fprintf(stdout, "Content-Length: %jd\r\n\r\n", filestat.st_size)) {
|
||||||
fclose(fdj); /* Close the file handle */
|
fclose(fdj); /* Close the file handle */
|
||||||
Info("Unable to send raw frame %ld: %s", curr_frame_id, strerror(errno));
|
Info("Unable to send raw frame %d: %s", curr_frame_id, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int rc = zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size);
|
ssize_t remaining = filestat.st_size;
|
||||||
if ( rc == (int)filestat.st_size ) {
|
|
||||||
|
while (remaining > 0) {
|
||||||
|
ssize_t rc = zm_sendfile(fileno(stdout), fileno(fdj), nullptr, remaining);
|
||||||
|
if (rc < 0) break;
|
||||||
|
if (rc > 0) {
|
||||||
|
remaining -= rc;
|
||||||
|
}
|
||||||
|
} // end while remaining
|
||||||
|
|
||||||
|
if (!remaining) {
|
||||||
// Success
|
// Success
|
||||||
fclose(fdj); /* Close the file handle */
|
fclose(fdj); /* Close the file handle */
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Warning("Unable to send raw frame %ld: %s rc %d", curr_frame_id, strerror(errno), rc);
|
Warning("Unable to send raw frame %d: %s %zu remaining",
|
||||||
#endif
|
curr_frame_id, strerror(errno), remaining);
|
||||||
img_buffer_size = fread(img_buffer, 1, sizeof(temp_img_buffer), fdj);
|
return false;
|
||||||
fclose(fdj); /* Close the file handle */
|
} // end bool EventStream::send_file(const std::string &filepath)
|
||||||
if ( !img_buffer_size ) {
|
|
||||||
Info("Unable to read raw frame %ld: %s", curr_frame_id, strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return send_buffer(img_buffer, img_buffer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EventStream::send_buffer(uint8_t* buffer, int size) {
|
bool EventStream::send_buffer(uint8_t* buffer, int size) {
|
||||||
if ( 0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", size) ) {
|
if ( 0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", size) ) {
|
||||||
Info("Unable to send raw frame %ld: %s", curr_frame_id, strerror(errno));
|
Info("Unable to send raw frame %d: %s", curr_frame_id, strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int rc = fwrite(buffer, size, 1, stdout);
|
int rc = fwrite(buffer, size, 1, stdout);
|
||||||
|
|
||||||
if ( 1 != rc ) {
|
if ( 1 != rc ) {
|
||||||
Error("Unable to send raw frame %ld: %s %d", curr_frame_id, strerror(errno), rc);
|
Error("Unable to send raw frame %d: %s %d", curr_frame_id, strerror(errno), rc);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -1150,7 +1162,7 @@ bool EventStream::send_buffer(uint8_t* buffer, int size) {
|
||||||
|
|
||||||
void EventStream::setStreamStart(
|
void EventStream::setStreamStart(
|
||||||
uint64_t init_event_id,
|
uint64_t init_event_id,
|
||||||
unsigned int init_frame_id=0) {
|
int init_frame_id=0) {
|
||||||
loadInitialEventData(init_event_id, init_frame_id);
|
loadInitialEventData(init_event_id, init_frame_id);
|
||||||
} // end void EventStream::setStreamStart(init_event_id,init_frame_id=0)
|
} // end void EventStream::setStreamStart(init_event_id,init_frame_id=0)
|
||||||
|
|
||||||
|
|
|
@ -49,9 +49,9 @@ class EventStream : public StreamBase {
|
||||||
struct EventData {
|
struct EventData {
|
||||||
uint64_t event_id;
|
uint64_t event_id;
|
||||||
unsigned int monitor_id;
|
unsigned int monitor_id;
|
||||||
unsigned long storage_id;
|
unsigned int storage_id;
|
||||||
unsigned long frame_count; // Value of Frames column in Event
|
int frame_count; // Value of Frames column in Event
|
||||||
unsigned long last_frame_id; // Highest frame id known about. Can be < frame_count in incomplete events
|
int last_frame_id; // Highest frame id known about. Can be < frame_count in incomplete events
|
||||||
SystemTimePoint start_time;
|
SystemTimePoint start_time;
|
||||||
SystemTimePoint end_time;
|
SystemTimePoint end_time;
|
||||||
Microseconds duration;
|
Microseconds duration;
|
||||||
|
@ -73,16 +73,16 @@ class EventStream : public StreamBase {
|
||||||
StreamMode mode;
|
StreamMode mode;
|
||||||
bool forceEventChange;
|
bool forceEventChange;
|
||||||
|
|
||||||
long curr_frame_id;
|
int curr_frame_id;
|
||||||
SystemTimePoint curr_stream_time;
|
SystemTimePoint curr_stream_time;
|
||||||
bool send_frame;
|
bool send_frame;
|
||||||
SystemTimePoint start; // clock time when started the event
|
TimePoint start; // clock time when started the event
|
||||||
|
|
||||||
EventData *event_data;
|
EventData *event_data;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool loadEventData(uint64_t event_id);
|
bool loadEventData(uint64_t event_id);
|
||||||
bool loadInitialEventData(uint64_t init_event_id, unsigned int init_frame_id);
|
bool loadInitialEventData(uint64_t init_event_id, int init_frame_id);
|
||||||
bool loadInitialEventData(int monitor_id, SystemTimePoint event_time);
|
bool loadInitialEventData(int monitor_id, SystemTimePoint event_time);
|
||||||
|
|
||||||
bool checkEventLoaded();
|
bool checkEventLoaded();
|
||||||
|
@ -118,7 +118,7 @@ class EventStream : public StreamBase {
|
||||||
ffmpeg_input = nullptr;
|
ffmpeg_input = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void setStreamStart(uint64_t init_event_id, unsigned int init_frame_id);
|
void setStreamStart(uint64_t init_event_id, int init_frame_id);
|
||||||
void setStreamStart(int monitor_id, time_t event_time);
|
void setStreamStart(int monitor_id, time_t event_time);
|
||||||
void setStreamMode(StreamMode p_mode) { mode = p_mode; }
|
void setStreamMode(StreamMode p_mode) { mode = p_mode; }
|
||||||
void runStream() override;
|
void runStream() override;
|
||||||
|
|
|
@ -257,8 +257,8 @@ void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output)
|
||||||
Debug(1, "ids [0x%x]", st->id);
|
Debug(1, "ids [0x%x]", st->id);
|
||||||
if (lang)
|
if (lang)
|
||||||
Debug(1, "language (%s)", lang->value);
|
Debug(1, "language (%s)", lang->value);
|
||||||
Debug(1, "frames:%d, frame_size:%d stream timebase: %d/%d",
|
Debug(1, "frame_size:%d stream timebase: %d/%d",
|
||||||
st->codec_info_nb_frames, codec->frame_size,
|
codec->frame_size,
|
||||||
st->time_base.num, st->time_base.den
|
st->time_base.num, st->time_base.den
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -293,17 +293,16 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
mFormatContext->interrupt_callback.opaque = this;
|
mFormatContext->interrupt_callback.opaque = this;
|
||||||
|
|
||||||
ret = avformat_open_input(&mFormatContext, mPath.c_str(), nullptr, &opts);
|
ret = avformat_open_input(&mFormatContext, mPath.c_str(), nullptr, &opts);
|
||||||
if ( ret != 0 )
|
if (ret != 0) {
|
||||||
{
|
logPrintf(Logger::ERROR + monitor->Importance(),
|
||||||
Error("Unable to open input %s due to: %s", mPath.c_str(),
|
"Unable to open input %s due to: %s", mPath.c_str(),
|
||||||
av_make_error_string(ret).c_str());
|
av_make_error_string(ret).c_str());
|
||||||
|
|
||||||
if ( mFormatContext ) {
|
if (mFormatContext) {
|
||||||
avformat_close_input(&mFormatContext);
|
avformat_close_input(&mFormatContext);
|
||||||
mFormatContext = nullptr;
|
mFormatContext = nullptr;
|
||||||
}
|
}
|
||||||
av_dict_free(&opts);
|
av_dict_free(&opts);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
AVDictionaryEntry *e = nullptr;
|
AVDictionaryEntry *e = nullptr;
|
||||||
|
@ -458,6 +457,17 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
#endif
|
#endif
|
||||||
} // end if hwaccel_name
|
} // end if hwaccel_name
|
||||||
|
|
||||||
|
// set codec to automatically determine how many threads suits best for the decoding job
|
||||||
|
mVideoCodecContext->thread_count = 0;
|
||||||
|
|
||||||
|
if (mVideoCodec->capabilities | AV_CODEC_CAP_FRAME_THREADS) {
|
||||||
|
mVideoCodecContext->thread_type = FF_THREAD_FRAME;
|
||||||
|
} else if (mVideoCodec->capabilities | AV_CODEC_CAP_SLICE_THREADS) {
|
||||||
|
mVideoCodecContext->thread_type = FF_THREAD_SLICE;
|
||||||
|
} else {
|
||||||
|
mVideoCodecContext->thread_count = 1; //don't use multithreading
|
||||||
|
}
|
||||||
|
|
||||||
ret = avcodec_open2(mVideoCodecContext, mVideoCodec, &opts);
|
ret = avcodec_open2(mVideoCodecContext, mVideoCodec, &opts);
|
||||||
|
|
||||||
e = nullptr;
|
e = nullptr;
|
||||||
|
|
|
@ -31,8 +31,7 @@ int FFmpeg_Input::Open(
|
||||||
const AVStream * audio_in_stream,
|
const AVStream * audio_in_stream,
|
||||||
const AVCodecContext * audio_in_ctx
|
const AVCodecContext * audio_in_ctx
|
||||||
) {
|
) {
|
||||||
video_stream_id = video_in_stream->index;
|
int max_stream_index = video_stream_id = video_in_stream->index;
|
||||||
int max_stream_index = video_in_stream->index;
|
|
||||||
|
|
||||||
if ( audio_in_stream ) {
|
if ( audio_in_stream ) {
|
||||||
max_stream_index = video_in_stream->index > audio_in_stream->index ? video_in_stream->index : audio_in_stream->index;
|
max_stream_index = video_in_stream->index > audio_in_stream->index ? video_in_stream->index : audio_in_stream->index;
|
||||||
|
|
|
@ -155,7 +155,7 @@ void FifoStream::runStream() {
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!zm_terminate) {
|
while (!zm_terminate) {
|
||||||
now = std::chrono::system_clock::now();
|
now = std::chrono::steady_clock::now();
|
||||||
checkCommandQueue();
|
checkCommandQueue();
|
||||||
|
|
||||||
if (stream_type == MJPEG) {
|
if (stream_type == MJPEG) {
|
||||||
|
|
|
@ -28,7 +28,6 @@ class FifoStream : public StreamBase {
|
||||||
std::string stream_path;
|
std::string stream_path;
|
||||||
int total_read;
|
int total_read;
|
||||||
int bytes_read;
|
int bytes_read;
|
||||||
unsigned int frame_count;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef enum { UNKNOWN, MJPEG, RAW } StreamType;
|
typedef enum { UNKNOWN, MJPEG, RAW } StreamType;
|
||||||
|
@ -39,9 +38,9 @@ class FifoStream : public StreamBase {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FifoStream() :
|
FifoStream() :
|
||||||
|
StreamBase(),
|
||||||
total_read(0),
|
total_read(0),
|
||||||
bytes_read(0),
|
bytes_read(0),
|
||||||
frame_count(0),
|
|
||||||
stream_type(UNKNOWN)
|
stream_type(UNKNOWN)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
|
@ -46,9 +46,9 @@ FileCamera::FileCamera(
|
||||||
p_hue,
|
p_hue,
|
||||||
p_colour,
|
p_colour,
|
||||||
p_capture,
|
p_capture,
|
||||||
p_record_audio)
|
p_record_audio),
|
||||||
|
path(p_path)
|
||||||
{
|
{
|
||||||
path = std::string(p_path);
|
|
||||||
if (capture) {
|
if (capture) {
|
||||||
Initialise();
|
Initialise();
|
||||||
}
|
}
|
||||||
|
|
116
src/zm_image.cpp
116
src/zm_image.cpp
|
@ -24,6 +24,7 @@
|
||||||
#include "zm_utils.h"
|
#include "zm_utils.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <mutex>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
@ -80,9 +81,14 @@ imgbufcpy_fptr_t fptr_imgbufcpy;
|
||||||
/* Font */
|
/* Font */
|
||||||
static ZmFont font;
|
static ZmFont font;
|
||||||
|
|
||||||
|
std::mutex jpeg_mutex;
|
||||||
|
|
||||||
void Image::update_function_pointers() {
|
void Image::update_function_pointers() {
|
||||||
/* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */
|
/* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements
|
||||||
if ( pixels % 16 || pixels % 12 ) {
|
* previous tests were %16 or %12 but that is incorrect. Should just be %4
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (pixels %4) {
|
||||||
// have to use non-loop unrolled functions
|
// have to use non-loop unrolled functions
|
||||||
delta8_rgb = &std_delta8_rgb;
|
delta8_rgb = &std_delta8_rgb;
|
||||||
delta8_bgr = &std_delta8_bgr;
|
delta8_bgr = &std_delta8_bgr;
|
||||||
|
@ -92,6 +98,7 @@ void Image::update_function_pointers() {
|
||||||
delta8_abgr = &std_delta8_abgr;
|
delta8_abgr = &std_delta8_abgr;
|
||||||
delta8_gray8 = &std_delta8_gray8;
|
delta8_gray8 = &std_delta8_gray8;
|
||||||
blend = &std_blend;
|
blend = &std_blend;
|
||||||
|
Debug(1, "Using slow std functions because pixels %d mod 4=%d", pixels, pixels%4);
|
||||||
} else {
|
} else {
|
||||||
// Use either sse or neon, or loop unrolled version
|
// Use either sse or neon, or loop unrolled version
|
||||||
delta8_rgb = fptr_delta8_rgb;
|
delta8_rgb = fptr_delta8_rgb;
|
||||||
|
@ -114,22 +121,23 @@ Image::Image() :
|
||||||
delta8_argb(&std_delta8_argb),
|
delta8_argb(&std_delta8_argb),
|
||||||
delta8_abgr(&std_delta8_abgr),
|
delta8_abgr(&std_delta8_abgr),
|
||||||
delta8_gray8(&std_delta8_gray8),
|
delta8_gray8(&std_delta8_gray8),
|
||||||
blend(&std_blend)
|
blend(&std_blend),
|
||||||
|
width(0),
|
||||||
|
linesize(0),
|
||||||
|
height(0),
|
||||||
|
pixels(0),
|
||||||
|
colours(0),
|
||||||
|
padding(0),
|
||||||
|
size(0),
|
||||||
|
subpixelorder(0),
|
||||||
|
allocation(0),
|
||||||
|
buffer(nullptr),
|
||||||
|
buffertype(ZM_BUFTYPE_DONTFREE),
|
||||||
|
holdbuffer(0)
|
||||||
{
|
{
|
||||||
if ( !initialised )
|
if (!initialised)
|
||||||
Initialise();
|
Initialise();
|
||||||
width = 0;
|
// Update blend to fast function determined by Initialise, I'm sure this can be improve.
|
||||||
linesize = 0;
|
|
||||||
height = 0;
|
|
||||||
padding = 0;
|
|
||||||
pixels = 0;
|
|
||||||
colours = 0;
|
|
||||||
subpixelorder = 0;
|
|
||||||
size = 0;
|
|
||||||
allocation = 0;
|
|
||||||
buffer = 0;
|
|
||||||
buffertype = ZM_BUFTYPE_DONTFREE;
|
|
||||||
holdbuffer = 0;
|
|
||||||
blend = fptr_blend;
|
blend = fptr_blend;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,15 +166,15 @@ Image::Image(int p_width, int p_height, int p_colours, int p_subpixelorder, uint
|
||||||
colours(p_colours),
|
colours(p_colours),
|
||||||
padding(p_padding),
|
padding(p_padding),
|
||||||
subpixelorder(p_subpixelorder),
|
subpixelorder(p_subpixelorder),
|
||||||
buffer(p_buffer) {
|
buffer(p_buffer),
|
||||||
|
holdbuffer(0)
|
||||||
|
{
|
||||||
|
|
||||||
if (!initialised)
|
if (!initialised)
|
||||||
Initialise();
|
Initialise();
|
||||||
pixels = width * height;
|
pixels = width * height;
|
||||||
linesize = p_width * p_colours;
|
linesize = p_width * p_colours;
|
||||||
size = linesize * height + padding;
|
size = linesize * height + padding;
|
||||||
buffer = nullptr;
|
|
||||||
holdbuffer = 0;
|
|
||||||
if (p_buffer) {
|
if (p_buffer) {
|
||||||
allocation = size;
|
allocation = size;
|
||||||
buffertype = ZM_BUFTYPE_DONTFREE;
|
buffertype = ZM_BUFTYPE_DONTFREE;
|
||||||
|
@ -174,7 +182,7 @@ Image::Image(int p_width, int p_height, int p_colours, int p_subpixelorder, uint
|
||||||
} else {
|
} else {
|
||||||
AllocImgBuffer(size);
|
AllocImgBuffer(size);
|
||||||
}
|
}
|
||||||
if (!subpixelorder and colours>1) {
|
if (!subpixelorder and (colours>1)) {
|
||||||
// Default to RGBA when no subpixelorder is specified.
|
// Default to RGBA when no subpixelorder is specified.
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||||
}
|
}
|
||||||
|
@ -214,25 +222,26 @@ Image::Image(int p_width, int p_linesize, int p_height, int p_colours, int p_sub
|
||||||
update_function_pointers();
|
update_function_pointers();
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::Image(const AVFrame *frame) {
|
Image::Image(const AVFrame *frame) :
|
||||||
|
colours(ZM_COLOUR_RGB32),
|
||||||
|
padding(0),
|
||||||
|
subpixelorder(ZM_SUBPIX_ORDER_RGBA),
|
||||||
|
imagePixFormat(AV_PIX_FMT_RGBA),
|
||||||
|
buffer(0),
|
||||||
|
holdbuffer(0)
|
||||||
|
{
|
||||||
width = frame->width;
|
width = frame->width;
|
||||||
height = frame->height;
|
height = frame->height;
|
||||||
pixels = width*height;
|
pixels = width*height;
|
||||||
|
|
||||||
zm_dump_video_frame(frame, "Image.Assign(frame)");
|
zm_dump_video_frame(frame, "Image.Assign(frame)");
|
||||||
// FIXME
|
// FIXME
|
||||||
colours = ZM_COLOUR_RGB32;
|
//(AVPixelFormat)frame->format;
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
|
||||||
imagePixFormat = AV_PIX_FMT_RGBA;
|
|
||||||
//(AVPixelFormat)frame->format;
|
|
||||||
|
|
||||||
size = av_image_get_buffer_size(AV_PIX_FMT_RGBA, width, height, 32);
|
size = av_image_get_buffer_size(AV_PIX_FMT_RGBA, width, height, 32);
|
||||||
// av_image_get_linesize isn't aligned, so we have to do that.
|
// av_image_get_linesize isn't aligned, so we have to do that.
|
||||||
linesize = FFALIGN(av_image_get_linesize(AV_PIX_FMT_RGBA, width, 0), 32);
|
linesize = FFALIGN(av_image_get_linesize(AV_PIX_FMT_RGBA, width, 0), 32);
|
||||||
padding = 0;
|
|
||||||
|
|
||||||
buffer = nullptr;
|
|
||||||
holdbuffer = 0;
|
|
||||||
AllocImgBuffer(size);
|
AllocImgBuffer(size);
|
||||||
this->Assign(frame);
|
this->Assign(frame);
|
||||||
}
|
}
|
||||||
|
@ -1081,11 +1090,16 @@ bool Image::WriteJpeg(const std::string &filename,
|
||||||
const int &quality_override,
|
const int &quality_override,
|
||||||
SystemTimePoint timestamp,
|
SystemTimePoint timestamp,
|
||||||
bool on_blocking_abort) const {
|
bool on_blocking_abort) const {
|
||||||
|
|
||||||
if (config.colour_jpeg_files && (colours == ZM_COLOUR_GRAY8)) {
|
if (config.colour_jpeg_files && (colours == ZM_COLOUR_GRAY8)) {
|
||||||
Image temp_image(*this);
|
Image temp_image(*this);
|
||||||
temp_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB);
|
temp_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB);
|
||||||
return temp_image.WriteJpeg(filename, quality_override, timestamp, on_blocking_abort);
|
return temp_image.WriteJpeg(filename, quality_override, timestamp, on_blocking_abort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// jpeg libs are not thread safe
|
||||||
|
std::unique_lock<std::mutex> lck(jpeg_mutex);
|
||||||
|
|
||||||
int quality = quality_override ? quality_override : config.jpeg_file_quality;
|
int quality = quality_override ? quality_override : config.jpeg_file_quality;
|
||||||
|
|
||||||
jpeg_compress_struct *cinfo = writejpg_ccinfo[quality];
|
jpeg_compress_struct *cinfo = writejpg_ccinfo[quality];
|
||||||
|
@ -1155,7 +1169,7 @@ bool Image::WriteJpeg(const std::string &filename,
|
||||||
} else if (subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
|
} else if (subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
|
||||||
cinfo->in_color_space = JCS_EXT_XBGR;
|
cinfo->in_color_space = JCS_EXT_XBGR;
|
||||||
} else {
|
} else {
|
||||||
Warning("Unknwon subpixelorder %d", subpixelorder);
|
Warning("Unknown subpixelorder %d", subpixelorder);
|
||||||
/* Assume RGBA */
|
/* Assume RGBA */
|
||||||
cinfo->in_color_space = JCS_EXT_RGBX;
|
cinfo->in_color_space = JCS_EXT_RGBX;
|
||||||
}
|
}
|
||||||
|
@ -1364,6 +1378,8 @@ bool Image::EncodeJpeg(JOCTET *outbuffer, int *outbuffer_size, int quality_overr
|
||||||
return temp_image.EncodeJpeg(outbuffer, outbuffer_size, quality_override);
|
return temp_image.EncodeJpeg(outbuffer, outbuffer_size, quality_override);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lck(jpeg_mutex);
|
||||||
|
|
||||||
int quality = quality_override ? quality_override : config.jpeg_stream_quality;
|
int quality = quality_override ? quality_override : config.jpeg_stream_quality;
|
||||||
|
|
||||||
struct jpeg_compress_struct *cinfo = encodejpg_ccinfo[quality];
|
struct jpeg_compress_struct *cinfo = encodejpg_ccinfo[quality];
|
||||||
|
@ -1677,15 +1693,15 @@ void Image::Overlay( const Image &image ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RGB32 compatible: complete */
|
/* RGB32 compatible: complete */
|
||||||
void Image::Overlay( const Image &image, unsigned int x, unsigned int y ) {
|
void Image::Overlay( const Image &image, const unsigned int lo_x, const unsigned int lo_y ) {
|
||||||
if ( !(width < image.width || height < image.height) ) {
|
if ( !(width < image.width || height < image.height) ) {
|
||||||
Panic("Attempt to overlay image too big for destination, %dx%d > %dx%d",
|
Panic("Attempt to overlay image too big for destination, %dx%d > %dx%d",
|
||||||
image.width, image.height, width, height );
|
image.width, image.height, width, height );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !(width < (x+image.width) || height < (y+image.height)) ) {
|
if ( !(width < (lo_x+image.width) || height < (lo_y+image.height)) ) {
|
||||||
Panic("Attempt to overlay image outside of destination bounds, %dx%d @ %dx%d > %dx%d",
|
Panic("Attempt to overlay image outside of destination bounds, %dx%d @ %dx%d > %dx%d",
|
||||||
image.width, image.height, x, y, width, height );
|
image.width, image.height, lo_x, lo_y, width, height );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !(colours == image.colours) ) {
|
if ( !(colours == image.colours) ) {
|
||||||
|
@ -1693,10 +1709,8 @@ void Image::Overlay( const Image &image, unsigned int x, unsigned int y ) {
|
||||||
colours, image.colours);
|
colours, image.colours);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int lo_x = x;
|
unsigned int hi_x = (lo_x+image.width)-1;
|
||||||
unsigned int lo_y = y;
|
unsigned int hi_y = (lo_y+image.height-1);
|
||||||
unsigned int hi_x = (x+image.width)-1;
|
|
||||||
unsigned int hi_y = (y+image.height-1);
|
|
||||||
if ( colours == ZM_COLOUR_GRAY8 ) {
|
if ( colours == ZM_COLOUR_GRAY8 ) {
|
||||||
const uint8_t *psrc = image.buffer;
|
const uint8_t *psrc = image.buffer;
|
||||||
for ( unsigned int y = lo_y; y <= hi_y; y++ ) {
|
for ( unsigned int y = lo_y; y <= hi_y; y++ ) {
|
||||||
|
@ -1729,7 +1743,6 @@ void Image::Overlay( const Image &image, unsigned int x, unsigned int y ) {
|
||||||
} // end void Image::Overlay( const Image &image, unsigned int x, unsigned int y )
|
} // end void Image::Overlay( const Image &image, unsigned int x, unsigned int y )
|
||||||
|
|
||||||
void Image::Blend( const Image &image, int transparency ) {
|
void Image::Blend( const Image &image, int transparency ) {
|
||||||
uint8_t* new_buffer;
|
|
||||||
|
|
||||||
if ( !(
|
if ( !(
|
||||||
width == image.width && height == image.height
|
width == image.width && height == image.height
|
||||||
|
@ -1743,7 +1756,7 @@ void Image::Blend( const Image &image, int transparency ) {
|
||||||
if ( transparency <= 0 )
|
if ( transparency <= 0 )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
new_buffer = AllocBuffer(size);
|
uint8_t* new_buffer = AllocBuffer(size);
|
||||||
|
|
||||||
#ifdef ZM_IMAGE_PROFILING
|
#ifdef ZM_IMAGE_PROFILING
|
||||||
TimePoint start = std::chrono::steady_clock::now();
|
TimePoint start = std::chrono::steady_clock::now();
|
||||||
|
@ -2732,7 +2745,7 @@ void Image::Flip( bool leftright ) {
|
||||||
AssignDirect(width, height, colours, subpixelorder, flip_buffer, size, ZM_BUFTYPE_ZM);
|
AssignDirect(width, height, colours, subpixelorder, flip_buffer, size, ZM_BUFTYPE_ZM);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::Scale(unsigned int factor) {
|
void Image::Scale(const unsigned int factor) {
|
||||||
if ( !factor ) {
|
if ( !factor ) {
|
||||||
Error("Bogus scale factor %d found", factor);
|
Error("Bogus scale factor %d found", factor);
|
||||||
return;
|
return;
|
||||||
|
@ -2756,15 +2769,13 @@ void Image::Scale(unsigned int factor) {
|
||||||
unsigned int h_count = ZM_SCALE_BASE/2;
|
unsigned int h_count = ZM_SCALE_BASE/2;
|
||||||
unsigned int last_h_index = 0;
|
unsigned int last_h_index = 0;
|
||||||
unsigned int last_w_index = 0;
|
unsigned int last_w_index = 0;
|
||||||
unsigned int h_index;
|
|
||||||
for ( unsigned int y = 0; y < height; y++ ) {
|
for ( unsigned int y = 0; y < height; y++ ) {
|
||||||
unsigned char *ps = &buffer[y*wc];
|
unsigned char *ps = &buffer[y*wc];
|
||||||
unsigned int w_count = ZM_SCALE_BASE/2;
|
unsigned int w_count = ZM_SCALE_BASE/2;
|
||||||
unsigned int w_index;
|
|
||||||
last_w_index = 0;
|
last_w_index = 0;
|
||||||
for ( unsigned int x = 0; x < width; x++ ) {
|
for ( unsigned int x = 0; x < width; x++ ) {
|
||||||
w_count += factor;
|
w_count += factor;
|
||||||
w_index = w_count/ZM_SCALE_BASE;
|
unsigned int w_index = w_count/ZM_SCALE_BASE;
|
||||||
for (unsigned int f = last_w_index; f < w_index; f++ ) {
|
for (unsigned int f = last_w_index; f < w_index; f++ ) {
|
||||||
for ( unsigned int c = 0; c < colours; c++ ) {
|
for ( unsigned int c = 0; c < colours; c++ ) {
|
||||||
*pd++ = *(ps+c);
|
*pd++ = *(ps+c);
|
||||||
|
@ -2774,7 +2785,7 @@ void Image::Scale(unsigned int factor) {
|
||||||
last_w_index = w_index;
|
last_w_index = w_index;
|
||||||
}
|
}
|
||||||
h_count += factor;
|
h_count += factor;
|
||||||
h_index = h_count/ZM_SCALE_BASE;
|
unsigned int h_index = h_count/ZM_SCALE_BASE;
|
||||||
for ( unsigned int f = last_h_index+1; f < h_index; f++ ) {
|
for ( unsigned int f = last_h_index+1; f < h_index; f++ ) {
|
||||||
memcpy(pd, pd-nwc, nwc);
|
memcpy(pd, pd-nwc, nwc);
|
||||||
pd += nwc;
|
pd += nwc;
|
||||||
|
@ -2786,17 +2797,14 @@ void Image::Scale(unsigned int factor) {
|
||||||
} else {
|
} else {
|
||||||
unsigned char *pd = scale_buffer;
|
unsigned char *pd = scale_buffer;
|
||||||
unsigned int wc = width*colours;
|
unsigned int wc = width*colours;
|
||||||
unsigned int xstart = factor/2;
|
unsigned int h_count = factor/2;
|
||||||
unsigned int ystart = factor/2;
|
|
||||||
unsigned int h_count = ystart;
|
|
||||||
unsigned int last_h_index = 0;
|
unsigned int last_h_index = 0;
|
||||||
unsigned int last_w_index = 0;
|
unsigned int last_w_index = 0;
|
||||||
unsigned int h_index;
|
|
||||||
for ( unsigned int y = 0; y < height; y++ ) {
|
for ( unsigned int y = 0; y < height; y++ ) {
|
||||||
h_count += factor;
|
h_count += factor;
|
||||||
h_index = h_count/ZM_SCALE_BASE;
|
unsigned int h_index = h_count/ZM_SCALE_BASE;
|
||||||
if ( h_index > last_h_index ) {
|
if ( h_index > last_h_index ) {
|
||||||
unsigned int w_count = xstart;
|
unsigned int w_count = factor/2;
|
||||||
unsigned int w_index;
|
unsigned int w_index;
|
||||||
last_w_index = 0;
|
last_w_index = 0;
|
||||||
|
|
||||||
|
@ -2825,6 +2833,7 @@ void Image::Scale(unsigned int factor) {
|
||||||
|
|
||||||
void Image::Deinterlace_Discard() {
|
void Image::Deinterlace_Discard() {
|
||||||
/* Simple deinterlacing. Copy the even lines into the odd lines */
|
/* Simple deinterlacing. Copy the even lines into the odd lines */
|
||||||
|
// ICON: These can be drastically improved. But who cares?
|
||||||
|
|
||||||
if ( colours == ZM_COLOUR_GRAY8 ) {
|
if ( colours == ZM_COLOUR_GRAY8 ) {
|
||||||
const uint8_t *psrc;
|
const uint8_t *psrc;
|
||||||
|
@ -3107,9 +3116,9 @@ __attribute__((noinline,__target__("sse2")))
|
||||||
#endif
|
#endif
|
||||||
void sse2_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) {
|
void sse2_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) {
|
||||||
#if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
|
#if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
|
||||||
static uint32_t divider = 0;
|
|
||||||
static uint32_t clearmask = 0;
|
|
||||||
static double current_blendpercent = 0.0;
|
static double current_blendpercent = 0.0;
|
||||||
|
static uint32_t clearmask = 0;
|
||||||
|
static uint32_t divider = 0;
|
||||||
|
|
||||||
if ( current_blendpercent != blendpercent ) {
|
if ( current_blendpercent != blendpercent ) {
|
||||||
/* Attempt to match the blending percent to one of the possible values */
|
/* Attempt to match the blending percent to one of the possible values */
|
||||||
|
@ -3310,10 +3319,10 @@ void neon32_armv7_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* r
|
||||||
|
|
||||||
__attribute__((noinline)) void neon64_armv8_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) {
|
__attribute__((noinline)) void neon64_armv8_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) {
|
||||||
#if (defined(__aarch64__) && !defined(ZM_STRIP_NEON))
|
#if (defined(__aarch64__) && !defined(ZM_STRIP_NEON))
|
||||||
static int8_t divider = 0;
|
|
||||||
static double current_blendpercent = 0.0;
|
static double current_blendpercent = 0.0;
|
||||||
|
static int8_t divider = 0;
|
||||||
|
|
||||||
if(current_blendpercent != blendpercent) {
|
if (current_blendpercent != blendpercent) {
|
||||||
/* Attempt to match the blending percent to one of the possible values */
|
/* Attempt to match the blending percent to one of the possible values */
|
||||||
if(blendpercent < 2.34375) {
|
if(blendpercent < 2.34375) {
|
||||||
// 1.5625% blending
|
// 1.5625% blending
|
||||||
|
@ -3393,6 +3402,7 @@ __attribute__((noinline)) void neon64_armv8_fastblend(const uint8_t* col1, const
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((noinline)) void std_blend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) {
|
__attribute__((noinline)) void std_blend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) {
|
||||||
|
Warning("Using slow std_blend");
|
||||||
double divide = blendpercent / 100.0;
|
double divide = blendpercent / 100.0;
|
||||||
double opacity = 1.0 - divide;
|
double opacity = 1.0 - divide;
|
||||||
const uint8_t* const max_ptr = result + count;
|
const uint8_t* const max_ptr = result + count;
|
||||||
|
|
|
@ -145,11 +145,13 @@ class Image {
|
||||||
explicit Image(const AVFrame *frame);
|
explicit Image(const AVFrame *frame);
|
||||||
|
|
||||||
~Image();
|
~Image();
|
||||||
|
|
||||||
static void Initialise();
|
static void Initialise();
|
||||||
static void Deinitialise();
|
static void Deinitialise();
|
||||||
|
|
||||||
inline void DumpImgBuffer() {
|
inline void DumpImgBuffer() {
|
||||||
DumpBuffer(buffer, buffertype);
|
if (buffertype != ZM_BUFTYPE_DONTFREE)
|
||||||
|
DumpBuffer(buffer, buffertype);
|
||||||
buffertype = ZM_BUFTYPE_DONTFREE;
|
buffertype = ZM_BUFTYPE_DONTFREE;
|
||||||
buffer = nullptr;
|
buffer = nullptr;
|
||||||
allocation = 0;
|
allocation = 0;
|
||||||
|
|
|
@ -23,7 +23,7 @@ void bind_libvnc_symbols() {
|
||||||
|
|
||||||
libvnc_lib = dlopen("libvncclient.so", RTLD_LAZY | RTLD_GLOBAL);
|
libvnc_lib = dlopen("libvncclient.so", RTLD_LAZY | RTLD_GLOBAL);
|
||||||
if (!libvnc_lib) {
|
if (!libvnc_lib) {
|
||||||
Error("Error loading libvncclient: %s", dlerror());
|
Error("Error loading libvncclient.so: %s", dlerror());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,11 +135,6 @@ VncCamera::VncCamera(
|
||||||
}
|
}
|
||||||
|
|
||||||
VncCamera::~VncCamera() {
|
VncCamera::~VncCamera() {
|
||||||
if (capture and mRfb) {
|
|
||||||
if (mRfb->frameBuffer)
|
|
||||||
free(mRfb->frameBuffer);
|
|
||||||
(*rfbClientCleanup_f)(mRfb);
|
|
||||||
}
|
|
||||||
if (libvnc_lib) {
|
if (libvnc_lib) {
|
||||||
dlclose(libvnc_lib);
|
dlclose(libvnc_lib);
|
||||||
libvnc_lib = nullptr;
|
libvnc_lib = nullptr;
|
||||||
|
@ -253,6 +248,12 @@ int VncCamera::PostCapture() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int VncCamera::Close() {
|
int VncCamera::Close() {
|
||||||
|
if (capture and mRfb) {
|
||||||
|
if (mRfb->frameBuffer)
|
||||||
|
free(mRfb->frameBuffer);
|
||||||
|
(*rfbClientCleanup_f)(mRfb);
|
||||||
|
mRfb = nullptr;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
1415
src/zm_monitor.cpp
1415
src/zm_monitor.cpp
File diff suppressed because it is too large
Load Diff
111
src/zm_monitor.h
111
src/zm_monitor.h
|
@ -23,6 +23,7 @@
|
||||||
#include "zm_define.h"
|
#include "zm_define.h"
|
||||||
#include "zm_camera.h"
|
#include "zm_camera.h"
|
||||||
#include "zm_analysis_thread.h"
|
#include "zm_analysis_thread.h"
|
||||||
|
#include "zm_poll_thread.h"
|
||||||
#include "zm_decoder_thread.h"
|
#include "zm_decoder_thread.h"
|
||||||
#include "zm_event.h"
|
#include "zm_event.h"
|
||||||
#include "zm_fifo.h"
|
#include "zm_fifo.h"
|
||||||
|
@ -33,6 +34,13 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
|
#include "soapPullPointSubscriptionBindingProxy.h"
|
||||||
|
#include "plugin/wsseapi.h"
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
class Group;
|
class Group;
|
||||||
|
|
||||||
|
@ -40,6 +48,7 @@ class Group;
|
||||||
#define MOTION_CAUSE "Motion"
|
#define MOTION_CAUSE "Motion"
|
||||||
#define LINKED_CAUSE "Linked"
|
#define LINKED_CAUSE "Linked"
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// This is the main class for monitors. Each monitor is associated
|
// This is the main class for monitors. Each monitor is associated
|
||||||
// with a camera and is effectively a collector for events.
|
// with a camera and is effectively a collector for events.
|
||||||
|
@ -69,7 +78,7 @@ public:
|
||||||
FILE,
|
FILE,
|
||||||
FFMPEG,
|
FFMPEG,
|
||||||
LIBVLC,
|
LIBVLC,
|
||||||
CURL,
|
LIBCURL,
|
||||||
NVSOCKET,
|
NVSOCKET,
|
||||||
VNC,
|
VNC,
|
||||||
} CameraType;
|
} CameraType;
|
||||||
|
@ -247,7 +256,51 @@ protected:
|
||||||
bool hasAlarmed();
|
bool hasAlarmed();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AmcrestAPI {
|
||||||
protected:
|
protected:
|
||||||
|
Monitor *parent;
|
||||||
|
std::string amcrest_response;
|
||||||
|
CURLM *curl_multi = nullptr;
|
||||||
|
CURL *Amcrest_handle = nullptr;
|
||||||
|
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
|
||||||
|
|
||||||
|
public:
|
||||||
|
AmcrestAPI( Monitor *parent_);
|
||||||
|
~AmcrestAPI();
|
||||||
|
int API_Connect();
|
||||||
|
void WaitForMessage();
|
||||||
|
bool Amcrest_Alarmed;
|
||||||
|
int start_Amcrest();
|
||||||
|
};
|
||||||
|
|
||||||
|
class JanusManager {
|
||||||
|
protected:
|
||||||
|
Monitor *parent;
|
||||||
|
CURL *curl = nullptr;
|
||||||
|
//helper class for CURL
|
||||||
|
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
|
||||||
|
bool Janus_Healthy;
|
||||||
|
std::string janus_session;
|
||||||
|
std::string janus_handle;
|
||||||
|
std::string janus_endpoint;
|
||||||
|
std::string stream_key;
|
||||||
|
std::string rtsp_username;
|
||||||
|
std::string rtsp_password;
|
||||||
|
std::string rtsp_path;
|
||||||
|
|
||||||
|
public:
|
||||||
|
JanusManager(Monitor *parent_);
|
||||||
|
~JanusManager();
|
||||||
|
int add_to_janus();
|
||||||
|
int check_janus();
|
||||||
|
int remove_from_janus();
|
||||||
|
int get_janus_session();
|
||||||
|
int get_janus_handle();
|
||||||
|
int get_janus_plugin();
|
||||||
|
std::string get_stream_key();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// These are read from the DB and thereafter remain unchanged
|
// These are read from the DB and thereafter remain unchanged
|
||||||
unsigned int id;
|
unsigned int id;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
@ -257,6 +310,8 @@ protected:
|
||||||
Function function; // What the monitor is doing
|
Function function; // What the monitor is doing
|
||||||
bool enabled; // Whether the monitor is enabled or asleep
|
bool enabled; // Whether the monitor is enabled or asleep
|
||||||
bool decoding_enabled; // Whether the monitor will decode h264/h265 packets
|
bool decoding_enabled; // Whether the monitor will decode h264/h265 packets
|
||||||
|
bool janus_enabled; // Whether we set the h264/h265 stream up on janus
|
||||||
|
bool janus_audio_enabled; // Whether we tell Janus to try to include audio.
|
||||||
|
|
||||||
std::string protocol;
|
std::string protocol;
|
||||||
std::string method;
|
std::string method;
|
||||||
|
@ -268,6 +323,13 @@ protected:
|
||||||
std::string path;
|
std::string path;
|
||||||
std::string second_path;
|
std::string second_path;
|
||||||
|
|
||||||
|
std::string onvif_url;
|
||||||
|
std::string onvif_username;
|
||||||
|
std::string onvif_password;
|
||||||
|
std::string onvif_options;
|
||||||
|
bool onvif_event_listener;
|
||||||
|
bool use_Amcrest_API;
|
||||||
|
|
||||||
std::string device;
|
std::string device;
|
||||||
int palette;
|
int palette;
|
||||||
int channel;
|
int channel;
|
||||||
|
@ -351,7 +413,6 @@ protected:
|
||||||
int first_alarm_count;
|
int first_alarm_count;
|
||||||
int last_alarm_count;
|
int last_alarm_count;
|
||||||
bool last_signal;
|
bool last_signal;
|
||||||
int last_section_mod;
|
|
||||||
int buffer_count;
|
int buffer_count;
|
||||||
State state;
|
State state;
|
||||||
SystemTimePoint start_time;
|
SystemTimePoint start_time;
|
||||||
|
@ -390,6 +451,7 @@ protected:
|
||||||
|
|
||||||
VideoStore *videoStore;
|
VideoStore *videoStore;
|
||||||
PacketQueue packetqueue;
|
PacketQueue packetqueue;
|
||||||
|
std::unique_ptr<PollThread> Poller;
|
||||||
packetqueue_iterator *analysis_it;
|
packetqueue_iterator *analysis_it;
|
||||||
std::unique_ptr<AnalysisThread> analysis_thread;
|
std::unique_ptr<AnalysisThread> analysis_thread;
|
||||||
packetqueue_iterator *decoder_it;
|
packetqueue_iterator *decoder_it;
|
||||||
|
@ -404,6 +466,8 @@ protected:
|
||||||
|
|
||||||
int n_linked_monitors;
|
int n_linked_monitors;
|
||||||
MonitorLink **linked_monitors;
|
MonitorLink **linked_monitors;
|
||||||
|
std::string event_start_command;
|
||||||
|
std::string event_end_command;
|
||||||
|
|
||||||
std::vector<Group *> groups;
|
std::vector<Group *> groups;
|
||||||
|
|
||||||
|
@ -414,6 +478,25 @@ protected:
|
||||||
std::string diag_path_ref;
|
std::string diag_path_ref;
|
||||||
std::string diag_path_delta;
|
std::string diag_path_delta;
|
||||||
|
|
||||||
|
//ONVIF
|
||||||
|
bool Poll_Trigger_State;
|
||||||
|
bool Event_Poller_Healthy;
|
||||||
|
bool Event_Poller_Closes_Event;
|
||||||
|
|
||||||
|
JanusManager *Janus_Manager;
|
||||||
|
AmcrestAPI *Amcrest_Manager;
|
||||||
|
|
||||||
|
#ifdef WITH_GSOAP
|
||||||
|
struct soap *soap = nullptr;
|
||||||
|
_tev__CreatePullPointSubscription request;
|
||||||
|
_tev__CreatePullPointSubscriptionResponse response;
|
||||||
|
_tev__PullMessages tev__PullMessages;
|
||||||
|
_tev__PullMessagesResponse tev__PullMessagesResponse;
|
||||||
|
PullPointSubscriptionBindingProxy proxyEvent;
|
||||||
|
void set_credentials(struct soap *soap);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Used in check signal
|
// Used in check signal
|
||||||
uint8_t red_val;
|
uint8_t red_val;
|
||||||
uint8_t green_val;
|
uint8_t green_val;
|
||||||
|
@ -470,6 +553,19 @@ public:
|
||||||
inline bool DecodingEnabled() const {
|
inline bool DecodingEnabled() const {
|
||||||
return decoding_enabled;
|
return decoding_enabled;
|
||||||
}
|
}
|
||||||
|
bool JanusEnabled() {
|
||||||
|
return janus_enabled;
|
||||||
|
}
|
||||||
|
bool JanusAudioEnabled() {
|
||||||
|
return janus_audio_enabled;
|
||||||
|
}
|
||||||
|
bool OnvifEnabled() {
|
||||||
|
return onvif_event_listener;
|
||||||
|
}
|
||||||
|
int check_janus(); //returns 1 for healthy, 0 for success but missing stream, negative for error.
|
||||||
|
bool EventPollerHealthy() {
|
||||||
|
return Event_Poller_Healthy;
|
||||||
|
}
|
||||||
inline const char *EventPrefix() const { return event_prefix.c_str(); }
|
inline const char *EventPrefix() const { return event_prefix.c_str(); }
|
||||||
inline bool Ready() const {
|
inline bool Ready() const {
|
||||||
if ( image_count >= ready_count ) {
|
if ( image_count >= ready_count ) {
|
||||||
|
@ -533,6 +629,12 @@ public:
|
||||||
std::string GetAudioFifoPath() const { return shared_data ? shared_data->audio_fifo_path : ""; };
|
std::string GetAudioFifoPath() const { return shared_data ? shared_data->audio_fifo_path : ""; };
|
||||||
std::string GetRTSPStreamName() const { return rtsp_streamname; };
|
std::string GetRTSPStreamName() const { return rtsp_streamname; };
|
||||||
|
|
||||||
|
const std::string &getONVIF_URL() const { return onvif_url; };
|
||||||
|
const std::string &getONVIF_Username() const { return onvif_username; };
|
||||||
|
const std::string &getONVIF_Password() const { return onvif_password; };
|
||||||
|
const std::string &getONVIF_Options() const { return onvif_options; };
|
||||||
|
|
||||||
|
Image *GetAlarmImage();
|
||||||
int GetImage(int32_t index=-1, int scale=100);
|
int GetImage(int32_t index=-1, int scale=100);
|
||||||
ZMPacket *getSnapshot( int index=-1 ) const;
|
ZMPacket *getSnapshot( int index=-1 ) const;
|
||||||
SystemTimePoint GetTimestamp(int index = -1) const;
|
SystemTimePoint GetTimestamp(int index = -1) const;
|
||||||
|
@ -589,8 +691,13 @@ public:
|
||||||
bool CheckSignal( const Image *image );
|
bool CheckSignal( const Image *image );
|
||||||
bool Analyse();
|
bool Analyse();
|
||||||
bool Decode();
|
bool Decode();
|
||||||
|
bool Poll();
|
||||||
void DumpImage( Image *dump_image ) const;
|
void DumpImage( Image *dump_image ) const;
|
||||||
void TimestampImage(Image *ts_image, SystemTimePoint ts_time) const;
|
void TimestampImage(Image *ts_image, SystemTimePoint ts_time) const;
|
||||||
|
Event *openEvent(
|
||||||
|
const std::shared_ptr<ZMPacket> &snap,
|
||||||
|
const std::string &cause,
|
||||||
|
const Event::StringSetMap ¬eSetMap);
|
||||||
void closeEvent();
|
void closeEvent();
|
||||||
|
|
||||||
void Reload();
|
void Reload();
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
//
|
||||||
|
// ZoneMinder Monitor::AmcrestAPI Class Implementation, $Date$, $Revision$
|
||||||
|
// Copyright (C) 2022 Jonathan Bennett
|
||||||
|
//
|
||||||
|
// 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_monitor.h"
|
||||||
|
|
||||||
|
|
||||||
|
Monitor::AmcrestAPI::AmcrestAPI(Monitor *parent_) {
|
||||||
|
parent = parent_;
|
||||||
|
curl_multi = curl_multi_init();
|
||||||
|
start_Amcrest();
|
||||||
|
}
|
||||||
|
|
||||||
|
Monitor::AmcrestAPI::~AmcrestAPI() {
|
||||||
|
if (Amcrest_handle != nullptr) { //potentially clean up the old handle
|
||||||
|
curl_multi_remove_handle(curl_multi, Amcrest_handle);
|
||||||
|
curl_easy_cleanup(Amcrest_handle);
|
||||||
|
}
|
||||||
|
if (curl_multi != nullptr) curl_multi_cleanup(curl_multi);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Monitor::AmcrestAPI::start_Amcrest() {
|
||||||
|
//init the transfer and start it in multi-handle
|
||||||
|
int running_handles;
|
||||||
|
long response_code;
|
||||||
|
struct CURLMsg *m;
|
||||||
|
CURLMcode curl_error;
|
||||||
|
if (Amcrest_handle != nullptr) { //potentially clean up the old handle
|
||||||
|
curl_multi_remove_handle(curl_multi, Amcrest_handle);
|
||||||
|
curl_easy_cleanup(Amcrest_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string full_url = parent->onvif_url;
|
||||||
|
if (full_url.back() != '/') full_url += '/';
|
||||||
|
full_url += "eventManager.cgi?action=attach&codes=[VideoMotion]";
|
||||||
|
Amcrest_handle = curl_easy_init();
|
||||||
|
if (!Amcrest_handle){
|
||||||
|
Warning("Handle is null!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
curl_easy_setopt(Amcrest_handle, CURLOPT_URL, full_url.c_str());
|
||||||
|
curl_easy_setopt(Amcrest_handle, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(Amcrest_handle, CURLOPT_WRITEDATA, &amcrest_response);
|
||||||
|
curl_easy_setopt(Amcrest_handle, CURLOPT_USERNAME, parent->onvif_username.c_str());
|
||||||
|
curl_easy_setopt(Amcrest_handle, CURLOPT_PASSWORD, parent->onvif_password.c_str());
|
||||||
|
curl_easy_setopt(Amcrest_handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
|
||||||
|
curl_error = curl_multi_add_handle(curl_multi, Amcrest_handle);
|
||||||
|
if (curl_error != CURLM_OK) {
|
||||||
|
Warning("error of %i", curl_error);
|
||||||
|
}
|
||||||
|
curl_error = curl_multi_perform(curl_multi, &running_handles);
|
||||||
|
if (curl_error == CURLM_OK) {
|
||||||
|
curl_easy_getinfo(Amcrest_handle, CURLINFO_RESPONSE_CODE, &response_code);
|
||||||
|
int msgq = 0;
|
||||||
|
m = curl_multi_info_read(curl_multi, &msgq);
|
||||||
|
if (m && (m->msg == CURLMSG_DONE)) {
|
||||||
|
Warning("Libcurl exited Early: %i", m->data.result);
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_multi_wait(curl_multi, NULL, 0, 300, NULL);
|
||||||
|
curl_error = curl_multi_perform(curl_multi, &running_handles);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((curl_error == CURLM_OK) && (running_handles > 0)) {
|
||||||
|
parent->Event_Poller_Healthy = TRUE;
|
||||||
|
} else {
|
||||||
|
Warning("Response: %s", amcrest_response.c_str());
|
||||||
|
Warning("Seeing %i streams, and error of %i, url: %s", running_handles, curl_error, full_url.c_str());
|
||||||
|
curl_easy_getinfo(Amcrest_handle, CURLINFO_OS_ERRNO, &response_code);
|
||||||
|
Warning("Response code: %lu", response_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Monitor::AmcrestAPI::WaitForMessage() {
|
||||||
|
int open_handles;
|
||||||
|
int transfers;
|
||||||
|
curl_multi_perform(curl_multi, &open_handles);
|
||||||
|
if (open_handles == 0) {
|
||||||
|
start_Amcrest(); //http transfer ended, need to restart.
|
||||||
|
} else {
|
||||||
|
curl_multi_wait(curl_multi, NULL, 0, 5000, &transfers); //wait for max 5 seconds for event.
|
||||||
|
if (transfers > 0) { //have data to deal with
|
||||||
|
curl_multi_perform(curl_multi, &open_handles); //actually grabs the data, populates amcrest_response
|
||||||
|
if (amcrest_response.find("action=Start") != std::string::npos) {
|
||||||
|
//Event Start
|
||||||
|
Debug(1,"Triggered on ONVIF");
|
||||||
|
if (!parent->Poll_Trigger_State) {
|
||||||
|
Debug(1,"Triggered Event");
|
||||||
|
parent->Poll_Trigger_State = TRUE;
|
||||||
|
}
|
||||||
|
} else if (amcrest_response.find("action=Stop") != std::string::npos){
|
||||||
|
Debug(1, "Triggered off ONVIF");
|
||||||
|
parent->Poll_Trigger_State = FALSE;
|
||||||
|
if (!parent->Event_Poller_Closes_Event) { //If we get a close event, then we know to expect them.
|
||||||
|
parent->Event_Poller_Closes_Event = TRUE;
|
||||||
|
Debug(1,"Setting ClosesEvent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
amcrest_response.clear(); //We've dealt with the message, need to clear the queue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Monitor::AmcrestAPI::WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||||
|
{
|
||||||
|
((std::string*)userp)->append((char*)contents, size * nmemb);
|
||||||
|
return size * nmemb;
|
||||||
|
}
|
|
@ -0,0 +1,316 @@
|
||||||
|
//
|
||||||
|
// ZoneMinder Monitor::JanusManager Class Implementation, $Date$, $Revision$
|
||||||
|
// Copyright (C) 2022 Jonathan Bennett
|
||||||
|
//
|
||||||
|
// 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_monitor.h"
|
||||||
|
|
||||||
|
|
||||||
|
Monitor::JanusManager::JanusManager(Monitor *parent_) { //constructor takes care of init and calls add_to
|
||||||
|
std::string response;
|
||||||
|
std::size_t pos;
|
||||||
|
parent = parent_;
|
||||||
|
if ((config.janus_path != nullptr) && (config.janus_path[0] != '\0')) {
|
||||||
|
janus_endpoint = config.janus_path; //TODO: strip trailing /
|
||||||
|
} else {
|
||||||
|
janus_endpoint = "127.0.0.1:8088/janus";
|
||||||
|
}
|
||||||
|
if (janus_endpoint.back() == '/') janus_endpoint.pop_back(); //remove the trailing slash if present
|
||||||
|
std::size_t pos2 = parent->path.find("@", pos);
|
||||||
|
if (pos2 != std::string::npos) { //If we find an @ symbol, we have a username/password. Otherwise, passwordless login.
|
||||||
|
|
||||||
|
std::size_t pos = parent->path.find(":", 7); //Search for the colon, but only after the RTSP:// text.
|
||||||
|
if (pos == std::string::npos) throw std::runtime_error("Cannot Parse URL for Janus."); //Looks like an invalid url
|
||||||
|
rtsp_username = parent->path.substr(7, pos-7);
|
||||||
|
|
||||||
|
rtsp_password = parent->path.substr(pos+1, pos2 - pos - 1);
|
||||||
|
rtsp_path = "RTSP://";
|
||||||
|
rtsp_path += parent->path.substr(pos2 + 1);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
rtsp_username = "";
|
||||||
|
rtsp_password = "";
|
||||||
|
rtsp_path = parent->path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Monitor::JanusManager::~JanusManager() {
|
||||||
|
if (janus_session.empty()) get_janus_session();
|
||||||
|
if (janus_handle.empty()) get_janus_handle();
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
std::string endpoint;
|
||||||
|
|
||||||
|
std::string postData = "{\"janus\" : \"create\", \"transaction\" : \"randomString\"}";
|
||||||
|
//std::size_t pos;
|
||||||
|
CURLcode res;
|
||||||
|
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if(!curl) return;
|
||||||
|
|
||||||
|
endpoint = janus_endpoint;
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += janus_session;
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += janus_handle;
|
||||||
|
|
||||||
|
//Assemble our actual request
|
||||||
|
postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {";
|
||||||
|
postData += "\"request\" : \"destroy\", \"admin_key\" : \"";
|
||||||
|
postData += config.janus_secret;
|
||||||
|
postData += "\", \"id\" : ";
|
||||||
|
postData += std::to_string(parent->id);
|
||||||
|
postData += "}}";
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL,endpoint.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
if (res != CURLE_OK) {
|
||||||
|
Warning("Libcurl attempted %s got %s", endpoint.c_str(), curl_easy_strerror(res));
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug(1, "Removed stream from Janus: %s", response.c_str());
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int Monitor::JanusManager::check_janus() {
|
||||||
|
if (janus_session.empty()) get_janus_session();
|
||||||
|
if (janus_handle.empty()) get_janus_handle();
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
std::string endpoint = janus_endpoint;
|
||||||
|
std::string postData;
|
||||||
|
//std::size_t pos;
|
||||||
|
CURLcode res;
|
||||||
|
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if(!curl) return -1;
|
||||||
|
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += janus_session;
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += janus_handle;
|
||||||
|
|
||||||
|
//Assemble our actual request
|
||||||
|
postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {";
|
||||||
|
postData += "\"request\" : \"info\", \"id\" : ";
|
||||||
|
postData += std::to_string(parent->id);
|
||||||
|
postData += "}}";
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL,endpoint.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
if (res != CURLE_OK) { //may mean an error code thrown by Janus, because of a bad session
|
||||||
|
Warning("Attempted %s got %s", endpoint.c_str(), curl_easy_strerror(res));
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
janus_session = "";
|
||||||
|
janus_handle = "";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
Debug(1, "Queried for stream status: %s", response.c_str());
|
||||||
|
if (response.find("\"janus\": \"error\"") != std::string::npos) {
|
||||||
|
if (response.find("No such session") != std::string::npos) {
|
||||||
|
Warning("Janus Session timed out");
|
||||||
|
janus_session = "";
|
||||||
|
return -2;
|
||||||
|
} else if (response.find("No such handle") != std::string::npos) {
|
||||||
|
Warning("Janus Handle timed out");
|
||||||
|
janus_handle = "";
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
} else if (response.find("No such mountpoint") != std::string::npos) {
|
||||||
|
Warning("Mountpoint Missing");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Monitor::JanusManager::add_to_janus() {
|
||||||
|
if (janus_session.empty()) get_janus_session();
|
||||||
|
if (janus_handle.empty()) get_janus_handle();
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
std::string endpoint = janus_endpoint;
|
||||||
|
|
||||||
|
CURLcode res;
|
||||||
|
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if (!curl) {
|
||||||
|
Error("Failed to init curl");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += janus_session;
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += janus_handle;
|
||||||
|
|
||||||
|
//Assemble our actual request
|
||||||
|
std::string postData = "{\"janus\" : \"message\", \"transaction\" : \"randomString\", \"body\" : {";
|
||||||
|
postData += "\"request\" : \"create\", \"admin_key\" : \"";
|
||||||
|
postData += config.janus_secret;
|
||||||
|
postData += "\", \"type\" : \"rtsp\", ";
|
||||||
|
postData += "\"url\" : \"";
|
||||||
|
postData += rtsp_path;
|
||||||
|
if (rtsp_username != "") {
|
||||||
|
postData += "\", \"rtsp_user\" : \"";
|
||||||
|
postData += rtsp_username;
|
||||||
|
postData += "\", \"rtsp_pwd\" : \"";
|
||||||
|
postData += rtsp_password;
|
||||||
|
}
|
||||||
|
postData += "\", \"id\" : ";
|
||||||
|
postData += std::to_string(parent->id);
|
||||||
|
if (parent->janus_audio_enabled) postData += ", \"audio\" : true";
|
||||||
|
postData += ", \"video\" : true}}";
|
||||||
|
Warning("Sending %s to %s", postData.c_str(), endpoint.c_str());
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL,endpoint.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
if (res != CURLE_OK) {
|
||||||
|
Error("Failed to curl_easy_perform adding rtsp stream");
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (response.find("\"janus\": \"error\"") != std::string::npos) {
|
||||||
|
if (response.find("No such session") != std::string::npos) {
|
||||||
|
Warning("Janus Session timed out");
|
||||||
|
janus_session = "";
|
||||||
|
return -2;
|
||||||
|
} else if (response.find("No such handle") != std::string::npos) {
|
||||||
|
Warning("Janus Handle timed out");
|
||||||
|
janus_handle = "";
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//scan for missing session or handle id "No such session" "no such handle"
|
||||||
|
|
||||||
|
Debug(1,"Added stream to Janus: %s", response.c_str());
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t Monitor::JanusManager::WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||||
|
{
|
||||||
|
((std::string*)userp)->append((char*)contents, size * nmemb);
|
||||||
|
return size * nmemb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void Monitor::JanusManager::generateKey()
|
||||||
|
{
|
||||||
|
const std::string CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
|
|
||||||
|
std::random_device random_device;
|
||||||
|
std::mt19937 generator(random_device());
|
||||||
|
std::uniform_int_distribution<> distribution(0, CHARACTERS.size() - 1);
|
||||||
|
|
||||||
|
std::string random_string;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < 16; ++i)
|
||||||
|
{
|
||||||
|
random_string += CHARACTERS[distribution(generator)];
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_key = random_string;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
int Monitor::JanusManager::get_janus_session() {
|
||||||
|
janus_session = "";
|
||||||
|
std::string endpoint = janus_endpoint;
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
|
||||||
|
std::string postData = "{\"janus\" : \"create\", \"transaction\" : \"randomString\"}";
|
||||||
|
std::size_t pos;
|
||||||
|
CURLcode res;
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if(!curl) return -1;
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
if (res != CURLE_OK) {
|
||||||
|
Warning("Libcurl attempted %s got %s", endpoint.c_str(), curl_easy_strerror(res));
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = response.find("\"id\": ");
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
{
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
janus_session = response.substr(pos + 6, 16);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
} //get_janus_session
|
||||||
|
|
||||||
|
int Monitor::JanusManager::get_janus_handle() {
|
||||||
|
std::string response = "";
|
||||||
|
std::string endpoint = janus_endpoint;
|
||||||
|
std::size_t pos;
|
||||||
|
|
||||||
|
CURLcode res;
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if(!curl) return -1;
|
||||||
|
|
||||||
|
endpoint += "/";
|
||||||
|
endpoint += janus_session;
|
||||||
|
std::string postData = "{\"janus\" : \"attach\", \"plugin\" : \"janus.plugin.streaming\", \"transaction\" : \"randomString\"}";
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL,endpoint.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
if (res != CURLE_OK)
|
||||||
|
{
|
||||||
|
Warning("Libcurl attempted %s got %s", endpoint.c_str(), curl_easy_strerror(res));
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = response.find("\"id\": ");
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
{
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
janus_handle = response.substr(pos + 6, 16);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return 1;
|
||||||
|
} //get_janus_handle
|
|
@ -0,0 +1,199 @@
|
||||||
|
//
|
||||||
|
// ZoneMinder Monitor Class Implementation, $Date$, $Revision$
|
||||||
|
// Copyright (C) 2001-2008 Philip Coombes
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 2
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "zm_monitor.h"
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#if ZM_MEM_MAPPED
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#else // ZM_MEM_MAPPED
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/shm.h>
|
||||||
|
#endif // ZM_MEM_MAPPED
|
||||||
|
|
||||||
|
Monitor::MonitorLink::MonitorLink(unsigned int p_id, const char *p_name) :
|
||||||
|
id(p_id),
|
||||||
|
shared_data(nullptr),
|
||||||
|
trigger_data(nullptr),
|
||||||
|
video_store_data(nullptr)
|
||||||
|
{
|
||||||
|
strncpy(name, p_name, sizeof(name)-1);
|
||||||
|
|
||||||
|
#if ZM_MEM_MAPPED
|
||||||
|
map_fd = -1;
|
||||||
|
mem_file = stringtf("%s/zm.mmap.%u", staticConfig.PATH_MAP.c_str(), id);
|
||||||
|
#else // ZM_MEM_MAPPED
|
||||||
|
shm_id = 0;
|
||||||
|
#endif // ZM_MEM_MAPPED
|
||||||
|
mem_size = 0;
|
||||||
|
mem_ptr = nullptr;
|
||||||
|
|
||||||
|
last_event_id = 0;
|
||||||
|
last_state = IDLE;
|
||||||
|
|
||||||
|
last_connect_time = 0;
|
||||||
|
connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Monitor::MonitorLink::~MonitorLink() {
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Monitor::MonitorLink::connect() {
|
||||||
|
SystemTimePoint now = std::chrono::system_clock::now();
|
||||||
|
if (!last_connect_time || (now - std::chrono::system_clock::from_time_t(last_connect_time)) > Seconds(60)) {
|
||||||
|
last_connect_time = std::chrono::system_clock::to_time_t(now);
|
||||||
|
|
||||||
|
mem_size = sizeof(SharedData) + sizeof(TriggerData);
|
||||||
|
|
||||||
|
Debug(1, "link.mem.size=%jd", static_cast<intmax_t>(mem_size));
|
||||||
|
#if ZM_MEM_MAPPED
|
||||||
|
map_fd = open(mem_file.c_str(), O_RDWR, (mode_t)0600);
|
||||||
|
if (map_fd < 0) {
|
||||||
|
Debug(3, "Can't open linked memory map file %s: %s", mem_file.c_str(), strerror(errno));
|
||||||
|
disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
while (map_fd <= 2) {
|
||||||
|
int new_map_fd = dup(map_fd);
|
||||||
|
Warning("Got one of the stdio fds for our mmap handle. map_fd was %d, new one is %d", map_fd, new_map_fd);
|
||||||
|
close(map_fd);
|
||||||
|
map_fd = new_map_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat map_stat;
|
||||||
|
if (fstat(map_fd, &map_stat) < 0) {
|
||||||
|
Error("Can't stat linked memory map file %s: %s", mem_file.c_str(), strerror(errno));
|
||||||
|
disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map_stat.st_size == 0) {
|
||||||
|
Error("Linked memory map file %s is empty: %s", mem_file.c_str(), strerror(errno));
|
||||||
|
disconnect();
|
||||||
|
return false;
|
||||||
|
} else if (map_stat.st_size < mem_size) {
|
||||||
|
Error("Got unexpected memory map file size %ld, expected %jd", map_stat.st_size, static_cast<intmax_t>(mem_size));
|
||||||
|
disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0);
|
||||||
|
if (mem_ptr == MAP_FAILED) {
|
||||||
|
Error("Can't map file %s (%jd bytes) to memory: %s", mem_file.c_str(), static_cast<intmax_t>(mem_size), strerror(errno));
|
||||||
|
disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else // ZM_MEM_MAPPED
|
||||||
|
shm_id = shmget((config.shm_key&0xffff0000)|id, mem_size, 0700);
|
||||||
|
if (shm_id < 0) {
|
||||||
|
Debug(3, "Can't shmget link memory: %s", strerror(errno));
|
||||||
|
connected = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mem_ptr = (unsigned char *)shmat(shm_id, 0, 0);
|
||||||
|
if ((int)mem_ptr == -1) {
|
||||||
|
Debug(3, "Can't shmat link memory: %s", strerror(errno));
|
||||||
|
connected = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif // ZM_MEM_MAPPED
|
||||||
|
|
||||||
|
shared_data = (SharedData *)mem_ptr;
|
||||||
|
trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData));
|
||||||
|
|
||||||
|
if (!shared_data->valid) {
|
||||||
|
Debug(3, "Linked memory not initialised by capture daemon");
|
||||||
|
disconnect();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_state = shared_data->state;
|
||||||
|
last_event_id = shared_data->last_event_id;
|
||||||
|
connected = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} // end bool Monitor::MonitorLink::connect()
|
||||||
|
|
||||||
|
bool Monitor::MonitorLink::disconnect() {
|
||||||
|
if (connected) {
|
||||||
|
connected = false;
|
||||||
|
|
||||||
|
#if ZM_MEM_MAPPED
|
||||||
|
if (mem_ptr > (void *)0) {
|
||||||
|
msync(mem_ptr, mem_size, MS_ASYNC);
|
||||||
|
munmap(mem_ptr, mem_size);
|
||||||
|
}
|
||||||
|
if (map_fd >= 0)
|
||||||
|
close(map_fd);
|
||||||
|
|
||||||
|
map_fd = -1;
|
||||||
|
#else // ZM_MEM_MAPPED
|
||||||
|
struct shmid_ds shm_data;
|
||||||
|
if (shmctl(shm_id, IPC_STAT, &shm_data) < 0) {
|
||||||
|
Debug(3, "Can't shmctl: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
shm_id = 0;
|
||||||
|
|
||||||
|
if (shm_data.shm_nattch <= 1) {
|
||||||
|
if (shmctl(shm_id, IPC_RMID, 0) < 0) {
|
||||||
|
Debug(3, "Can't shmctl: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shmdt(mem_ptr) < 0) {
|
||||||
|
Debug(3, "Can't shmdt: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif // ZM_MEM_MAPPED
|
||||||
|
mem_size = 0;
|
||||||
|
mem_ptr = nullptr;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Monitor::MonitorLink::isAlarmed() {
|
||||||
|
if (!connected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return( shared_data->state == ALARM );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Monitor::MonitorLink::inAlarm() {
|
||||||
|
if (!connected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return( shared_data->state == ALARM || shared_data->state == ALERT );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Monitor::MonitorLink::hasAlarmed() {
|
||||||
|
if (shared_data->state == ALARM) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
last_event_id = shared_data->last_event_id;
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -134,6 +134,18 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case CMD_MAXFPS :
|
||||||
|
{
|
||||||
|
double int_part = ((unsigned char) msg->msg_data[1] << 24) | ((unsigned char) msg->msg_data[2] << 16)
|
||||||
|
| ((unsigned char) msg->msg_data[3] << 8) | (unsigned char) msg->msg_data[4];
|
||||||
|
double dec_part = ((unsigned char) msg->msg_data[5] << 24) | ((unsigned char) msg->msg_data[6] << 16)
|
||||||
|
| ((unsigned char) msg->msg_data[7] << 8) | (unsigned char) msg->msg_data[8];
|
||||||
|
|
||||||
|
maxfps = (int_part + dec_part / 1000000.0);
|
||||||
|
|
||||||
|
Debug(1, "Got MAXFPS %f", maxfps);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case CMD_SLOWFWD :
|
case CMD_SLOWFWD :
|
||||||
Debug(1, "Got SLOW FWD command");
|
Debug(1, "Got SLOW FWD command");
|
||||||
paused = true;
|
paused = true;
|
||||||
|
@ -231,6 +243,14 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
||||||
Info("User initiated exit - CMD_QUIT");
|
Info("User initiated exit - CMD_QUIT");
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
break;
|
break;
|
||||||
|
case CMD_ANALYZE_ON :
|
||||||
|
frame_type = FRAME_ANALYSIS;
|
||||||
|
Debug(1, "ANALYSIS on");
|
||||||
|
break;
|
||||||
|
case CMD_ANALYZE_OFF :
|
||||||
|
frame_type = FRAME_NORMAL;
|
||||||
|
Debug(1, "ANALYSIS off");
|
||||||
|
break;
|
||||||
case CMD_QUERY :
|
case CMD_QUERY :
|
||||||
Debug(1, "Got QUERY command, sending STATUS");
|
Debug(1, "Got QUERY command, sending STATUS");
|
||||||
break;
|
break;
|
||||||
|
@ -268,7 +288,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
||||||
} else {
|
} else {
|
||||||
FPSeconds elapsed = now - last_fps_update;
|
FPSeconds elapsed = now - last_fps_update;
|
||||||
if (elapsed.count()) {
|
if (elapsed.count()) {
|
||||||
actual_fps = (frame_count - last_frame_count) / elapsed.count();
|
actual_fps = (actual_fps + (frame_count - last_frame_count) / elapsed.count())/2;
|
||||||
last_frame_count = frame_count;
|
last_frame_count = frame_count;
|
||||||
last_fps_update = now;
|
last_fps_update = now;
|
||||||
}
|
}
|
||||||
|
@ -288,9 +308,9 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
||||||
status_data.delayed = delayed;
|
status_data.delayed = delayed;
|
||||||
status_data.paused = paused;
|
status_data.paused = paused;
|
||||||
status_data.rate = replay_rate;
|
status_data.rate = replay_rate;
|
||||||
status_data.delay = FPSeconds(now - last_frame_timestamp).count();
|
status_data.delay = FPSeconds(now - last_frame_sent).count();
|
||||||
status_data.zoom = zoom;
|
status_data.zoom = zoom;
|
||||||
Debug(2, "fps: %.2f capture_fps: %.2f analysis_fps: %.2f Buffer Level:%d, Delayed:%d, Paused:%d, Rate:%d, delay:%.3f, Zoom:%d, Enabled:%d Forced:%d",
|
Debug(2, "viewing fps: %.2f capture_fps: %.2f analysis_fps: %.2f Buffer Level:%d, Delayed:%d, Paused:%d, Rate:%d, delay:%.3f, Zoom:%d, Enabled:%d Forced:%d",
|
||||||
status_data.fps,
|
status_data.fps,
|
||||||
status_data.capture_fps,
|
status_data.capture_fps,
|
||||||
status_data.analysis_fps,
|
status_data.analysis_fps,
|
||||||
|
@ -359,14 +379,15 @@ bool MonitorStream::sendFrame(const std::string &filepath, SystemTimePoint times
|
||||||
fputs("\r\n", stdout);
|
fputs("\r\n", stdout);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
TimePoint send_end_time = std::chrono::steady_clock::now();
|
if (maxfps > 0.0) {
|
||||||
TimePoint::duration frame_send_time = send_end_time - send_start_time;
|
TimePoint send_end_time = std::chrono::steady_clock::now();
|
||||||
|
TimePoint::duration frame_send_time = send_end_time - send_start_time;
|
||||||
|
|
||||||
if (frame_send_time > Milliseconds(lround(Milliseconds::period::den / maxfps))) {
|
if (frame_send_time > Milliseconds(lround(Milliseconds::period::den / maxfps))) {
|
||||||
maxfps /= 2;
|
Info("Frame send time %" PRIi64 " ms too slow, throttling maxfps to %.2f",
|
||||||
Info("Frame send time %" PRIi64 " ms too slow, throttling maxfps to %.2f",
|
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(frame_send_time).count()),
|
||||||
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(frame_send_time).count()),
|
maxfps);
|
||||||
maxfps);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
last_frame_sent = now;
|
last_frame_sent = now;
|
||||||
|
@ -377,12 +398,15 @@ bool MonitorStream::sendFrame(const std::string &filepath, SystemTimePoint times
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
|
bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
|
||||||
Image *send_image = prepareImage(image);
|
|
||||||
if (!config.timestamp_on_capture) {
|
if (!config.timestamp_on_capture) {
|
||||||
monitor->TimestampImage(send_image, timestamp);
|
monitor->TimestampImage(image, timestamp);
|
||||||
}
|
}
|
||||||
|
Image *send_image = prepareImage(image);
|
||||||
|
|
||||||
fputs("--" BOUNDARY "\r\n", stdout);
|
fputs("--" BOUNDARY "\r\n", stdout);
|
||||||
|
// Calculate how long it takes to actually send the frame
|
||||||
|
TimePoint send_start_time = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
if ( type == STREAM_MPEG ) {
|
if ( type == STREAM_MPEG ) {
|
||||||
if ( !vid_stream ) {
|
if ( !vid_stream ) {
|
||||||
vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height());
|
vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height());
|
||||||
|
@ -398,14 +422,17 @@ bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
|
||||||
|
|
||||||
/* double pts = */ vid_stream->EncodeFrame(send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_time.count());
|
/* double pts = */ vid_stream->EncodeFrame(send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_time.count());
|
||||||
} else {
|
} else {
|
||||||
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
|
if (temp_img_buffer_size < send_image->Size()) {
|
||||||
|
Debug(1, "Resizing image buffer from %zu to %u",
|
||||||
|
temp_img_buffer_size, send_image->Size());
|
||||||
|
delete[] temp_img_buffer;
|
||||||
|
temp_img_buffer = new uint8_t[send_image->Size()];
|
||||||
|
temp_img_buffer_size = send_image->Size();
|
||||||
|
}
|
||||||
|
|
||||||
int img_buffer_size = 0;
|
int img_buffer_size = 0;
|
||||||
unsigned char *img_buffer = temp_img_buffer;
|
unsigned char *img_buffer = temp_img_buffer;
|
||||||
|
|
||||||
// Calculate how long it takes to actually send the frame
|
|
||||||
TimePoint send_start_time = std::chrono::steady_clock::now();
|
|
||||||
|
|
||||||
switch ( type ) {
|
switch ( type ) {
|
||||||
case STREAM_JPEG :
|
case STREAM_JPEG :
|
||||||
send_image->EncodeJpeg(img_buffer, &img_buffer_size);
|
send_image->EncodeJpeg(img_buffer, &img_buffer_size);
|
||||||
|
@ -446,19 +473,23 @@ bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
|
||||||
fputs("\r\n", stdout);
|
fputs("\r\n", stdout);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
TimePoint send_end_time = std::chrono::steady_clock::now();
|
|
||||||
TimePoint::duration frame_send_time = send_end_time - send_start_time;
|
|
||||||
|
|
||||||
if (frame_send_time > Milliseconds(lround(Milliseconds::period::den / maxfps))) {
|
|
||||||
maxfps /= 1.5;
|
|
||||||
Warning("Frame send time %" PRIi64 " msec too slow, throttling maxfps to %.2f",
|
|
||||||
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(frame_send_time).count()),
|
|
||||||
maxfps);
|
|
||||||
}
|
|
||||||
} // Not mpeg
|
} // Not mpeg
|
||||||
last_frame_sent = now;
|
|
||||||
|
last_frame_sent = std::chrono::steady_clock::now();
|
||||||
|
if (maxfps > 0.0) {
|
||||||
|
TimePoint::duration frame_send_time = last_frame_sent - send_start_time;
|
||||||
|
TimePoint::duration maxfps_milliseconds = Milliseconds(lround(Milliseconds::period::den / maxfps));
|
||||||
|
|
||||||
|
if (frame_send_time > maxfps_milliseconds) {
|
||||||
|
//maxfps /= 1.5;
|
||||||
|
Warning("Frame send time %" PRIi64 " msec too slow (> %" PRIi64 ", throttling maxfps to %.3f",
|
||||||
|
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(frame_send_time).count()),
|
||||||
|
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(maxfps_milliseconds).count()),
|
||||||
|
maxfps);
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
} // end bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp)
|
||||||
|
|
||||||
void MonitorStream::runStream() {
|
void MonitorStream::runStream() {
|
||||||
if (type == STREAM_SINGLE) {
|
if (type == STREAM_SINGLE) {
|
||||||
|
@ -501,7 +532,8 @@ void MonitorStream::runStream() {
|
||||||
// point to end which is theoretically not a valid value because all indexes are % image_buffer_count
|
// point to end which is theoretically not a valid value because all indexes are % image_buffer_count
|
||||||
int32_t last_read_index = monitor->image_buffer_count;
|
int32_t last_read_index = monitor->image_buffer_count;
|
||||||
|
|
||||||
SystemTimePoint stream_start_time = std::chrono::system_clock::now();
|
TimePoint stream_start_time = std::chrono::steady_clock::now();
|
||||||
|
when_to_send_next_frame = stream_start_time; // initialize it to now so that we spit out a frame immediately
|
||||||
|
|
||||||
frame_count = 0;
|
frame_count = 0;
|
||||||
|
|
||||||
|
@ -570,7 +602,7 @@ void MonitorStream::runStream() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
now = std::chrono::system_clock::now();
|
now = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
bool was_paused = paused;
|
bool was_paused = paused;
|
||||||
bool got_command = false; // commands like zoom should output a frame even if paused
|
bool got_command = false; // commands like zoom should output a frame even if paused
|
||||||
|
@ -617,7 +649,7 @@ void MonitorStream::runStream() {
|
||||||
temp_read_index = MOD_ADD(temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count);
|
temp_read_index = MOD_ADD(temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count);
|
||||||
} else {
|
} else {
|
||||||
FPSeconds expected_delta_time = ((FPSeconds(swap_image->timestamp - last_frame_timestamp)) * ZM_RATE_BASE) / replay_rate;
|
FPSeconds expected_delta_time = ((FPSeconds(swap_image->timestamp - last_frame_timestamp)) * ZM_RATE_BASE) / replay_rate;
|
||||||
SystemTimePoint::duration actual_delta_time = now - last_frame_sent;
|
TimePoint::duration actual_delta_time = now - last_frame_sent;
|
||||||
|
|
||||||
// If the next frame is due
|
// If the next frame is due
|
||||||
if (actual_delta_time > expected_delta_time) {
|
if (actual_delta_time > expected_delta_time) {
|
||||||
|
@ -683,7 +715,8 @@ void MonitorStream::runStream() {
|
||||||
if (last_read_index != monitor->shared_data->last_write_index) {
|
if (last_read_index != monitor->shared_data->last_write_index) {
|
||||||
// have a new image to send
|
// have a new image to send
|
||||||
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
|
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
|
||||||
if ((frame_mod == 1) || ((frame_count%frame_mod) == 0)) {
|
//if ((frame_mod == 1) || ((frame_count%frame_mod) == 0)) {
|
||||||
|
if ( now >= when_to_send_next_frame ) {
|
||||||
if (!paused && !delayed) {
|
if (!paused && !delayed) {
|
||||||
last_read_index = monitor->shared_data->last_write_index;
|
last_read_index = monitor->shared_data->last_write_index;
|
||||||
Debug(2, "Sending frame index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)",
|
Debug(2, "Sending frame index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)",
|
||||||
|
@ -693,9 +726,22 @@ void MonitorStream::runStream() {
|
||||||
// Perhaps we should use NOW instead.
|
// Perhaps we should use NOW instead.
|
||||||
last_frame_timestamp =
|
last_frame_timestamp =
|
||||||
SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index]));
|
SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index]));
|
||||||
Image *image = monitor->image_buffer[index];
|
|
||||||
|
|
||||||
if (!sendFrame(image, last_frame_timestamp)) {
|
Image *send_image = nullptr;
|
||||||
|
if ((frame_type == FRAME_ANALYSIS) &&
|
||||||
|
(monitor->GetFunction() == Monitor::MOCORD || monitor->GetFunction() == Monitor::MODECT)) {
|
||||||
|
Debug(1, "Sending analysis image");
|
||||||
|
send_image = monitor->GetAlarmImage();
|
||||||
|
if (!send_image) {
|
||||||
|
Debug(1, "Falling back");
|
||||||
|
send_image = monitor->image_buffer[index];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Debug(1, "Sending regular image index %d", index);
|
||||||
|
send_image = monitor->image_buffer[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendFrame(send_image, last_frame_timestamp)) {
|
||||||
Debug(2, "sendFrame failed, quiting.");
|
Debug(2, "sendFrame failed, quiting.");
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
break;
|
break;
|
||||||
|
@ -704,7 +750,7 @@ void MonitorStream::runStream() {
|
||||||
if (frame_count == 0) {
|
if (frame_count == 0) {
|
||||||
// Chrome will not display the first frame until it receives another.
|
// Chrome will not display the first frame until it receives another.
|
||||||
// Firefox is fine. So just send the first frame twice.
|
// Firefox is fine. So just send the first frame twice.
|
||||||
if (!sendFrame(image, last_frame_timestamp)) {
|
if (!sendFrame(send_image, last_frame_timestamp)) {
|
||||||
Debug(2, "sendFrame failed, quiting.");
|
Debug(2, "sendFrame failed, quiting.");
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
break;
|
break;
|
||||||
|
@ -726,7 +772,7 @@ void MonitorStream::runStream() {
|
||||||
frame_count++;
|
frame_count++;
|
||||||
frame_count++;
|
frame_count++;
|
||||||
} else {
|
} else {
|
||||||
SystemTimePoint::duration actual_delta_time = now - last_frame_sent;
|
TimePoint::duration actual_delta_time = now - last_frame_sent;
|
||||||
if (actual_delta_time > Seconds(5)) {
|
if (actual_delta_time > Seconds(5)) {
|
||||||
if (paused_image) {
|
if (paused_image) {
|
||||||
// Send keepalive
|
// Send keepalive
|
||||||
|
@ -742,9 +788,9 @@ void MonitorStream::runStream() {
|
||||||
} // end if actual_delta_time > 5
|
} // end if actual_delta_time > 5
|
||||||
} // end if change in zoom
|
} // end if change in zoom
|
||||||
} // end if paused or not
|
} // end if paused or not
|
||||||
} else {
|
//} else {
|
||||||
frame_count++;
|
//frame_count++;
|
||||||
} // end if should send frame
|
} // end if should send frame now > when_to_send_next_frame
|
||||||
|
|
||||||
if (buffered_playback && !paused) {
|
if (buffered_playback && !paused) {
|
||||||
if (monitor->shared_data->valid) {
|
if (monitor->shared_data->valid) {
|
||||||
|
@ -775,17 +821,42 @@ void MonitorStream::runStream() {
|
||||||
}
|
}
|
||||||
} // end if buffered playback
|
} // end if buffered playback
|
||||||
} else {
|
} else {
|
||||||
Debug(3, "Waiting for capture last_write_index=%u", monitor->shared_data->last_write_index);
|
Debug(3, "Waiting for capture last_write_index=%u == last_read_index=%u",
|
||||||
|
monitor->shared_data->last_write_index,
|
||||||
|
last_read_index);
|
||||||
} // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index )
|
} // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index )
|
||||||
|
|
||||||
FPSeconds sleep_time =
|
FPSeconds sleep_time;
|
||||||
FPSeconds(ZM_RATE_BASE / ((base_fps ? base_fps : 1) * (replay_rate ? abs(replay_rate * 2) : 2)));
|
if (now >= when_to_send_next_frame) {
|
||||||
|
// sent a frame, so update
|
||||||
|
|
||||||
|
double capture_fps = monitor->GetFPS();
|
||||||
|
double fps = ((maxfps > 0.0) && (capture_fps > maxfps)) ? maxfps : capture_fps;
|
||||||
|
double sleep_time_seconds = (1 / ((fps ? fps : 1))) // 1 second / fps
|
||||||
|
* (replay_rate ? abs(replay_rate)/ZM_RATE_BASE : 1); // replay_rate is 100 for 1x
|
||||||
|
Debug(3, "Using %f for maxfps. capture_fps: %f maxfps %f * replay_rate: %d = %f", fps, capture_fps, maxfps, replay_rate, sleep_time_seconds);
|
||||||
|
|
||||||
|
sleep_time = FPSeconds(sleep_time_seconds);
|
||||||
|
if (when_to_send_next_frame > now)
|
||||||
|
sleep_time -= when_to_send_next_frame - now;
|
||||||
|
|
||||||
|
when_to_send_next_frame = now + std::chrono::duration_cast<Microseconds>(sleep_time);
|
||||||
|
|
||||||
|
if (last_frame_sent > now) {
|
||||||
|
FPSeconds elapsed = last_frame_sent - now;
|
||||||
|
if (sleep_time > elapsed) {
|
||||||
|
sleep_time -= elapsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sleep_time = when_to_send_next_frame - now;
|
||||||
|
}
|
||||||
|
|
||||||
if (sleep_time > MonitorStream::MAX_SLEEP) {
|
if (sleep_time > MonitorStream::MAX_SLEEP) {
|
||||||
|
Debug(3, "Sleeping for MAX_SLEEP_USEC instead of %" PRIi64 " us",
|
||||||
|
static_cast<int64>(std::chrono::duration_cast<Microseconds>(sleep_time).count()));
|
||||||
// Shouldn't sleep for long because we need to check command queue, etc.
|
// Shouldn't sleep for long because we need to check command queue, etc.
|
||||||
sleep_time = MonitorStream::MAX_SLEEP;
|
sleep_time = MonitorStream::MAX_SLEEP;
|
||||||
Debug(3, "Sleeping for MAX_SLEEP_USEC %" PRIi64 " us",
|
|
||||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(sleep_time).count()));
|
|
||||||
} else {
|
} else {
|
||||||
Debug(3, "Sleeping for %" PRIi64 " us",
|
Debug(3, "Sleeping for %" PRIi64 " us",
|
||||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(sleep_time).count()));
|
static_cast<int64>(std::chrono::duration_cast<Microseconds>(sleep_time).count()));
|
||||||
|
@ -797,13 +868,6 @@ void MonitorStream::runStream() {
|
||||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(ttl).count()));
|
static_cast<int64>(std::chrono::duration_cast<Microseconds>(ttl).count()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last_frame_sent.time_since_epoch() == Seconds(0)) {
|
|
||||||
// If we didn't capture above, because frame_mod was bad? Then last_frame_sent will not have a value.
|
|
||||||
last_frame_sent = now;
|
|
||||||
Warning("no last_frame_sent. Shouldn't happen. frame_mod was (%d) frame_count (%d)",
|
|
||||||
frame_mod, frame_count);
|
|
||||||
}
|
|
||||||
} // end while ! zm_terminate
|
} // end while ! zm_terminate
|
||||||
|
|
||||||
if (buffered_playback) {
|
if (buffered_playback) {
|
||||||
|
@ -854,16 +918,16 @@ void MonitorStream::SingleImage(int scale) {
|
||||||
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
|
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
|
||||||
Debug(1, "write index: %d %d", monitor->shared_data->last_write_index, index);
|
Debug(1, "write index: %d %d", monitor->shared_data->last_write_index, index);
|
||||||
Image *snap_image = monitor->image_buffer[index];
|
Image *snap_image = monitor->image_buffer[index];
|
||||||
|
if (!config.timestamp_on_capture) {
|
||||||
|
monitor->TimestampImage(snap_image,
|
||||||
|
SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index])));
|
||||||
|
}
|
||||||
|
|
||||||
if ( scale != ZM_SCALE_BASE ) {
|
if ( scale != ZM_SCALE_BASE ) {
|
||||||
scaled_image.Assign(*snap_image);
|
scaled_image.Assign(*snap_image);
|
||||||
scaled_image.Scale(scale);
|
scaled_image.Scale(scale);
|
||||||
snap_image = &scaled_image;
|
snap_image = &scaled_image;
|
||||||
}
|
}
|
||||||
if (!config.timestamp_on_capture) {
|
|
||||||
monitor->TimestampImage(snap_image,
|
|
||||||
SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index])));
|
|
||||||
}
|
|
||||||
snap_image->EncodeJpeg(img_buffer, &img_buffer_size);
|
snap_image->EncodeJpeg(img_buffer, &img_buffer_size);
|
||||||
|
|
||||||
fprintf(stdout,
|
fprintf(stdout,
|
||||||
|
|
124
src/zm_mpeg.cpp
124
src/zm_mpeg.cpp
|
@ -42,19 +42,26 @@ void VideoStream::SetupFormat( ) {
|
||||||
ofc = nullptr;
|
ofc = nullptr;
|
||||||
avformat_alloc_output_context2(&ofc, nullptr, format, filename);
|
avformat_alloc_output_context2(&ofc, nullptr, format, filename);
|
||||||
|
|
||||||
if ( !ofc ) {
|
if (!ofc) {
|
||||||
Fatal("avformat_alloc_..._context failed");
|
Fatal("avformat_alloc_..._context failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
of = ofc->oformat;
|
of = ofc->oformat;
|
||||||
Debug(1, "Using output format: %s (%s)", of->name, of->long_name);
|
Debug(1, "Using output format: %s (%s)", of->name, of->long_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate ) {
|
int VideoStream::SetupCodec(
|
||||||
|
int colours,
|
||||||
|
int subpixelorder,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int bitrate,
|
||||||
|
double frame_rate
|
||||||
|
) {
|
||||||
/* ffmpeg format matching */
|
/* ffmpeg format matching */
|
||||||
switch ( colours ) {
|
switch (colours) {
|
||||||
case ZM_COLOUR_RGB24:
|
case ZM_COLOUR_RGB24:
|
||||||
if ( subpixelorder == ZM_SUBPIX_ORDER_BGR ) {
|
if (subpixelorder == ZM_SUBPIX_ORDER_BGR) {
|
||||||
/* BGR subpixel order */
|
/* BGR subpixel order */
|
||||||
pf = AV_PIX_FMT_BGR24;
|
pf = AV_PIX_FMT_BGR24;
|
||||||
} else {
|
} else {
|
||||||
|
@ -63,13 +70,13 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ZM_COLOUR_RGB32:
|
case ZM_COLOUR_RGB32:
|
||||||
if ( subpixelorder == ZM_SUBPIX_ORDER_ARGB ) {
|
if (subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
|
||||||
/* ARGB subpixel order */
|
/* ARGB subpixel order */
|
||||||
pf = AV_PIX_FMT_ARGB;
|
pf = AV_PIX_FMT_ARGB;
|
||||||
} else if ( subpixelorder == ZM_SUBPIX_ORDER_ABGR ) {
|
} else if (subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
|
||||||
/* ABGR subpixel order */
|
/* ABGR subpixel order */
|
||||||
pf = AV_PIX_FMT_ABGR;
|
pf = AV_PIX_FMT_ABGR;
|
||||||
} else if ( subpixelorder == ZM_SUBPIX_ORDER_BGRA ) {
|
} else if (subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
|
||||||
/* BGRA subpixel order */
|
/* BGRA subpixel order */
|
||||||
pf = AV_PIX_FMT_BGRA;
|
pf = AV_PIX_FMT_BGRA;
|
||||||
} else {
|
} else {
|
||||||
|
@ -85,22 +92,22 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( strcmp("rtp", of->name) == 0 ) {
|
if (strcmp("rtp", of->name) == 0) {
|
||||||
// RTP must have a packet_size.
|
// RTP must have a packet_size.
|
||||||
// Not sure what this value should be really...
|
// Not sure what this value should be really...
|
||||||
ofc->packet_size = width*height;
|
ofc->packet_size = width*height;
|
||||||
Debug(1,"Setting packet_size to %d", ofc->packet_size);
|
Debug(1,"Setting packet_size to %d", ofc->packet_size);
|
||||||
|
|
||||||
if ( of->video_codec == AV_CODEC_ID_NONE ) {
|
if (of->video_codec == AV_CODEC_ID_NONE) {
|
||||||
// RTP does not have a default codec in ffmpeg <= 0.8.
|
// RTP does not have a default codec in ffmpeg <= 0.8.
|
||||||
of->video_codec = AV_CODEC_ID_MPEG4;
|
of->video_codec = AV_CODEC_ID_MPEG4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_AVCODECID codec_id = of->video_codec;
|
_AVCODECID codec_id = of->video_codec;
|
||||||
if ( codec_name ) {
|
if (codec_name) {
|
||||||
AVCodec *a = avcodec_find_encoder_by_name(codec_name);
|
AVCodec *a = avcodec_find_encoder_by_name(codec_name);
|
||||||
if ( a ) {
|
if (a) {
|
||||||
codec_id = a->id;
|
codec_id = a->id;
|
||||||
Debug(1, "Using codec \"%s\"", codec_name);
|
Debug(1, "Using codec \"%s\"", codec_name);
|
||||||
} else {
|
} else {
|
||||||
|
@ -111,31 +118,29 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
|
||||||
/* add the video streams using the default format codecs
|
/* add the video streams using the default format codecs
|
||||||
and initialize the codecs */
|
and initialize the codecs */
|
||||||
ost = nullptr;
|
ost = nullptr;
|
||||||
if ( codec_id != AV_CODEC_ID_NONE ) {
|
if (codec_id != AV_CODEC_ID_NONE) {
|
||||||
codec = avcodec_find_encoder(codec_id);
|
codec = avcodec_find_encoder(codec_id);
|
||||||
if ( !codec ) {
|
if (!codec) {
|
||||||
Fatal("Could not find encoder for '%s'", avcodec_get_name(codec_id));
|
Error("Could not find encoder for '%s'", avcodec_get_name(codec_id));
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug(1, "Found encoder for '%s'", avcodec_get_name(codec_id));
|
Debug(1, "Found encoder for '%s'", avcodec_get_name(codec_id));
|
||||||
|
ost = avformat_new_stream(ofc, codec);
|
||||||
ost = avformat_new_stream( ofc, codec );
|
if (!ost) {
|
||||||
|
Error("Could not alloc stream");
|
||||||
if ( !ost ) {
|
return -1;
|
||||||
Fatal( "Could not alloc stream" );
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Debug( 1, "Allocated stream (%d) !=? (%d)", ost->id , ofc->nb_streams - 1 );
|
Debug(1, "Allocated stream (%d) !=? (%d)", ost->id , ofc->nb_streams - 1);
|
||||||
ost->id = ofc->nb_streams - 1;
|
ost->id = ofc->nb_streams - 1;
|
||||||
|
|
||||||
codec_context = avcodec_alloc_context3(nullptr);
|
codec_context = avcodec_alloc_context3(nullptr);
|
||||||
//avcodec_parameters_to_context(codec_context, ost->codecpar);
|
//avcodec_parameters_to_context(codec_context, ost->codecpar);
|
||||||
|
|
||||||
codec_context->codec_id = codec->id;
|
codec_context->codec_id = codec->id;
|
||||||
codec_context->codec_type = codec->type;
|
codec_context->codec_type = codec->type;
|
||||||
|
|
||||||
codec_context->pix_fmt = strcmp("mjpeg", ofc->oformat->name) == 0 ? AV_PIX_FMT_YUVJ422P : AV_PIX_FMT_YUV420P;
|
codec_context->pix_fmt = strcmp("mjpeg", ofc->oformat->name) == 0 ? AV_PIX_FMT_YUVJ422P : AV_PIX_FMT_YUV420P;
|
||||||
if ( bitrate <= 100 ) {
|
if (bitrate <= 100) {
|
||||||
// Quality based bitrate control (VBR). Scale is 1..31 where 1 is best.
|
// Quality based bitrate control (VBR). Scale is 1..31 where 1 is best.
|
||||||
// This gets rid of artifacts in the beginning of the movie; and well, even quality.
|
// This gets rid of artifacts in the beginning of the movie; and well, even quality.
|
||||||
codec_context->flags |= AV_CODEC_FLAG_QSCALE;
|
codec_context->flags |= AV_CODEC_FLAG_QSCALE;
|
||||||
|
@ -155,22 +160,22 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
|
||||||
codec_context->time_base.num = 1;
|
codec_context->time_base.num = 1;
|
||||||
ost->time_base.den = frame_rate;
|
ost->time_base.den = frame_rate;
|
||||||
ost->time_base.num = 1;
|
ost->time_base.num = 1;
|
||||||
|
|
||||||
|
|
||||||
Debug( 1, "Will encode in %d fps. %dx%d", codec_context->time_base.den, width, height );
|
Debug( 1, "Will encode in %d fps. %dx%d", codec_context->time_base.den, width, height );
|
||||||
|
|
||||||
/* emit one intra frame every second */
|
/* emit one intra frame every second */
|
||||||
codec_context->gop_size = frame_rate;
|
codec_context->gop_size = frame_rate;
|
||||||
|
|
||||||
// some formats want stream headers to be separate
|
// some formats want stream headers to be separate
|
||||||
if ( of->flags & AVFMT_GLOBALHEADER )
|
if (of->flags & AVFMT_GLOBALHEADER)
|
||||||
codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
||||||
|
|
||||||
avcodec_parameters_from_context(ost->codecpar, codec_context);
|
avcodec_parameters_from_context(ost->codecpar, codec_context);
|
||||||
zm_dump_codecpar(ost->codecpar);
|
zm_dump_codecpar(ost->codecpar);
|
||||||
} else {
|
} else {
|
||||||
Fatal( "of->video_codec == AV_CODEC_ID_NONE" );
|
Error("of->video_codec == AV_CODEC_ID_NONE");
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoStream::SetParameters( ) {
|
void VideoStream::SetParameters( ) {
|
||||||
|
@ -461,9 +466,8 @@ double VideoStream::EncodeFrame( const uint8_t *buffer, int buffer_size, bool _a
|
||||||
}
|
}
|
||||||
|
|
||||||
double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp, unsigned int timestamp ) {
|
double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp, unsigned int timestamp ) {
|
||||||
|
|
||||||
if ( codec_context->pix_fmt != pf ) {
|
if ( codec_context->pix_fmt != pf ) {
|
||||||
static struct SwsContext *img_convert_ctx = nullptr;
|
static struct SwsContext *img_convert_ctx = nullptr;
|
||||||
memcpy( tmp_opicture->data[0], buffer, buffer_size );
|
memcpy( tmp_opicture->data[0], buffer, buffer_size );
|
||||||
if ( !img_convert_ctx ) {
|
if ( !img_convert_ctx ) {
|
||||||
img_convert_ctx = sws_getCachedContext( nullptr, codec_context->width, codec_context->height, pf, codec_context->width, codec_context->height, codec_context->pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr );
|
img_convert_ctx = sws_getCachedContext( nullptr, codec_context->width, codec_context->height, pf, codec_context->width, codec_context->height, codec_context->pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr );
|
||||||
|
@ -478,34 +482,33 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size,
|
||||||
|
|
||||||
AVPacket *pkt = packet_buffers[packet_index];
|
AVPacket *pkt = packet_buffers[packet_index];
|
||||||
av_init_packet( pkt );
|
av_init_packet( pkt );
|
||||||
int got_packet = 0;
|
int got_packet = 0;
|
||||||
if (codec_context->codec_type == AVMEDIA_TYPE_VIDEO &&
|
if (codec_context->codec_type == AVMEDIA_TYPE_VIDEO &&
|
||||||
codec_context->codec_id == AV_CODEC_ID_RAWVIDEO) {
|
codec_context->codec_id == AV_CODEC_ID_RAWVIDEO) {
|
||||||
pkt->flags |= AV_PKT_FLAG_KEY;
|
pkt->flags |= AV_PKT_FLAG_KEY;
|
||||||
pkt->stream_index = ost->index;
|
pkt->stream_index = ost->index;
|
||||||
pkt->data = (uint8_t *)opicture_ptr;
|
pkt->data = (uint8_t *)opicture_ptr;
|
||||||
pkt->size = sizeof (AVPicture);
|
pkt->size = sizeof (AVPicture);
|
||||||
got_packet = 1;
|
got_packet = 1;
|
||||||
} else {
|
} else {
|
||||||
opicture_ptr->pts = codec_context->frame_number;
|
opicture_ptr->pts = codec_context->frame_number;
|
||||||
opicture_ptr->quality = codec_context->global_quality;
|
opicture_ptr->quality = codec_context->global_quality;
|
||||||
|
|
||||||
avcodec_send_frame(codec_context, opicture_ptr);
|
avcodec_send_frame(codec_context, opicture_ptr);
|
||||||
int ret = avcodec_receive_packet(codec_context, pkt);
|
int ret = avcodec_receive_packet(codec_context, pkt);
|
||||||
if ( ret < 0 ) {
|
if (ret < 0) {
|
||||||
if ( AVERROR_EOF != ret ) {
|
if (AVERROR_EOF != ret) {
|
||||||
Error("ERror encoding video (%d) (%s)", ret,
|
Error("ERror encoding video (%d) (%s)", ret, av_err2str(ret));
|
||||||
av_err2str(ret));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
got_packet = 1;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
got_packet = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if ( got_packet ) {
|
if (got_packet) {
|
||||||
// if ( c->coded_frame->key_frame )
|
// if ( c->coded_frame->key_frame )
|
||||||
// {
|
// {
|
||||||
// pkt->flags |= AV_PKT_FLAG_KEY;
|
// pkt->flags |= AV_PKT_FLAG_KEY;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if ( pkt->pts != (int64_t)AV_NOPTS_VALUE ) {
|
if ( pkt->pts != (int64_t)AV_NOPTS_VALUE ) {
|
||||||
pkt->pts = av_rescale_q( pkt->pts, codec_context->time_base, ost->time_base );
|
pkt->pts = av_rescale_q( pkt->pts, codec_context->time_base, ost->time_base );
|
||||||
|
@ -518,17 +521,16 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ( opicture_ptr->pts);
|
return opicture_ptr->pts;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VideoStream::SendPacket(AVPacket *packet) {
|
int VideoStream::SendPacket(AVPacket *packet) {
|
||||||
|
int ret = av_write_frame(ofc, packet);
|
||||||
int ret = av_write_frame( ofc, packet );
|
if (ret < 0) {
|
||||||
if ( ret != 0 ) {
|
Error("Error %d while writing video frame: %s", ret, av_err2str(errno));
|
||||||
Fatal( "Error %d while writing video frame: %s", ret, av_err2str( errno ) );
|
}
|
||||||
}
|
av_packet_unref(packet);
|
||||||
av_packet_unref( packet );
|
return ret;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *VideoStream::StreamingThreadCallback(void *ctx) {
|
void *VideoStream::StreamingThreadCallback(void *ctx) {
|
||||||
|
|
|
@ -68,7 +68,7 @@ protected:
|
||||||
static void Initialise();
|
static void Initialise();
|
||||||
|
|
||||||
void SetupFormat( );
|
void SetupFormat( );
|
||||||
void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate );
|
int SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate );
|
||||||
void SetParameters();
|
void SetParameters();
|
||||||
void ActuallyOpenStream();
|
void ActuallyOpenStream();
|
||||||
double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
|
||||||
|
|
|
@ -57,6 +57,7 @@ class ZMPacket {
|
||||||
int64_t pts; // pts in the packet can be in another time base. This MUST be in AV_TIME_BASE_Q
|
int64_t pts; // pts in the packet can be in another time base. This MUST be in AV_TIME_BASE_Q
|
||||||
bool decoded;
|
bool decoded;
|
||||||
std::vector<ZoneStats> zone_stats;
|
std::vector<ZoneStats> zone_stats;
|
||||||
|
std::string alarm_cause;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AVPacket *av_packet() { return &packet; }
|
AVPacket *av_packet() { return &packet; }
|
||||||
|
|
|
@ -116,15 +116,16 @@ bool PacketQueue::queuePacket(std::shared_ptr<ZMPacket> add_packet) {
|
||||||
, max_video_packet_count);
|
, max_video_packet_count);
|
||||||
|
|
||||||
for (
|
for (
|
||||||
auto it = ++pktQueue.begin();
|
auto it = ++pktQueue.begin();
|
||||||
it != pktQueue.end() and *it != add_packet;
|
//it != pktQueue.end() and // can't git end because we added our packet
|
||||||
|
*it != add_packet;
|
||||||
|
// iterator is incremented by erase
|
||||||
) {
|
) {
|
||||||
std::shared_ptr <ZMPacket>zm_packet = *it;
|
std::shared_ptr <ZMPacket>zm_packet = *it;
|
||||||
|
|
||||||
ZMLockedPacket *lp = new ZMLockedPacket(zm_packet);
|
ZMLockedPacket lp(zm_packet);
|
||||||
if (!lp->trylock()) {
|
if (!lp.trylock()) {
|
||||||
Debug(1, "Found locked packet when trying to free up video packets. Skipping to next one");
|
Warning("Found locked packet when trying to free up video packets. This basically means that decoding is not keeping up.");
|
||||||
delete lp;
|
|
||||||
++it;
|
++it;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -136,7 +137,7 @@ bool PacketQueue::queuePacket(std::shared_ptr<ZMPacket> add_packet) {
|
||||||
) {
|
) {
|
||||||
auto iterator_it = *iterators_it;
|
auto iterator_it = *iterators_it;
|
||||||
// Have to check each iterator and make sure it doesn't point to the packet we are about to delete
|
// Have to check each iterator and make sure it doesn't point to the packet we are about to delete
|
||||||
if ((*iterator_it!=pktQueue.end()) and (*(*iterator_it) == zm_packet)) {
|
if (*(*iterator_it) == zm_packet) {
|
||||||
Debug(1, "Bumping IT because it is at the front that we are deleting");
|
Debug(1, "Bumping IT because it is at the front that we are deleting");
|
||||||
++(*iterator_it);
|
++(*iterator_it);
|
||||||
}
|
}
|
||||||
|
@ -153,8 +154,6 @@ bool PacketQueue::queuePacket(std::shared_ptr<ZMPacket> add_packet) {
|
||||||
max_video_packet_count,
|
max_video_packet_count,
|
||||||
pktQueue.size());
|
pktQueue.size());
|
||||||
|
|
||||||
delete lp;
|
|
||||||
|
|
||||||
if (zm_packet->packet.stream_index == video_stream_id)
|
if (zm_packet->packet.stream_index == video_stream_id)
|
||||||
break;
|
break;
|
||||||
} // end while
|
} // end while
|
||||||
|
@ -162,7 +161,7 @@ bool PacketQueue::queuePacket(std::shared_ptr<ZMPacket> add_packet) {
|
||||||
} // end lock scope
|
} // end lock scope
|
||||||
// We signal on every packet because someday we may analyze sound
|
// We signal on every packet because someday we may analyze sound
|
||||||
Debug(4, "packetqueue queuepacket, unlocked signalling");
|
Debug(4, "packetqueue queuepacket, unlocked signalling");
|
||||||
condition.notify_all();
|
condition.notify_one();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} // end bool PacketQueue::queuePacket(ZMPacket* zm_packet)
|
} // end bool PacketQueue::queuePacket(ZMPacket* zm_packet)
|
||||||
|
@ -312,7 +311,6 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
|
||||||
pktQueue.size());
|
pktQueue.size());
|
||||||
pktQueue.pop_front();
|
pktQueue.pop_front();
|
||||||
packet_counts[zm_packet->packet.stream_index] -= 1;
|
packet_counts[zm_packet->packet.stream_index] -= 1;
|
||||||
//delete zm_packet;
|
|
||||||
}
|
}
|
||||||
} // end if have at least max_video_packet_count video packets remaining
|
} // end if have at least max_video_packet_count video packets remaining
|
||||||
// We signal on every packet because someday we may analyze sound
|
// We signal on every packet because someday we may analyze sound
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
#include "zm_poll_thread.h"
|
||||||
|
|
||||||
|
#include "zm_monitor.h"
|
||||||
|
#include "zm_signal.h"
|
||||||
|
#include "zm_time.h"
|
||||||
|
|
||||||
|
PollThread::PollThread(Monitor *monitor) :
|
||||||
|
monitor_(monitor), terminate_(false) {
|
||||||
|
thread_ = std::thread(&PollThread::Run, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
PollThread::~PollThread() {
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PollThread::Start() {
|
||||||
|
if (thread_.joinable()) thread_.join();
|
||||||
|
terminate_ = false;
|
||||||
|
Debug(3, "Starting polling thread");
|
||||||
|
thread_ = std::thread(&PollThread::Run, this);
|
||||||
|
}
|
||||||
|
void PollThread::Stop() {
|
||||||
|
terminate_ = true;
|
||||||
|
if (thread_.joinable()) {
|
||||||
|
thread_.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void PollThread::Run() {
|
||||||
|
while (!(terminate_ or zm_terminate)) {
|
||||||
|
monitor_->Poll();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef ZM_POLL_THREAD_H
|
||||||
|
#define ZM_POLL_THREAD_H
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
class Monitor;
|
||||||
|
|
||||||
|
class PollThread {
|
||||||
|
public:
|
||||||
|
explicit PollThread(Monitor *monitor);
|
||||||
|
~PollThread();
|
||||||
|
PollThread(PollThread &rhs) = delete;
|
||||||
|
PollThread(PollThread &&rhs) = delete;
|
||||||
|
|
||||||
|
void Start();
|
||||||
|
void Stop();
|
||||||
|
bool Stopped() const { return terminate_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Run();
|
||||||
|
|
||||||
|
Monitor *monitor_;
|
||||||
|
std::atomic<bool> terminate_;
|
||||||
|
std::thread thread_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -22,6 +22,7 @@
|
||||||
#include "zm_config.h"
|
#include "zm_config.h"
|
||||||
#include "zm_monitor.h"
|
#include "zm_monitor.h"
|
||||||
#include "zm_packet.h"
|
#include "zm_packet.h"
|
||||||
|
#include "zm_signal.h"
|
||||||
|
|
||||||
RemoteCameraRtsp::RemoteCameraRtsp(
|
RemoteCameraRtsp::RemoteCameraRtsp(
|
||||||
const Monitor *monitor,
|
const Monitor *monitor,
|
||||||
|
@ -126,8 +127,8 @@ int RemoteCameraRtsp::Disconnect() {
|
||||||
|
|
||||||
int RemoteCameraRtsp::PrimeCapture() {
|
int RemoteCameraRtsp::PrimeCapture() {
|
||||||
Debug(2, "Waiting for sources");
|
Debug(2, "Waiting for sources");
|
||||||
for (int i = 0; i < 100 && !rtspThread->hasSources(); i++) {
|
for (int i = 100; i && !zm_terminate && !rtspThread->hasSources(); i--) {
|
||||||
std::this_thread::sleep_for(Microseconds(100));
|
std::this_thread::sleep_for(Microseconds(10000));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rtspThread->hasSources()) {
|
if (!rtspThread->hasSources()) {
|
||||||
|
@ -168,8 +169,10 @@ int RemoteCameraRtsp::PrimeCapture() {
|
||||||
}
|
}
|
||||||
} // end foreach stream
|
} // end foreach stream
|
||||||
|
|
||||||
if ( mVideoStreamId == -1 )
|
if ( mVideoStreamId == -1 ) {
|
||||||
Fatal("Unable to locate video stream");
|
Error("Unable to locate video stream");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if ( mAudioStreamId == -1 )
|
if ( mAudioStreamId == -1 )
|
||||||
Debug(3, "Unable to locate audio stream");
|
Debug(3, "Unable to locate audio stream");
|
||||||
|
|
||||||
|
@ -179,17 +182,22 @@ int RemoteCameraRtsp::PrimeCapture() {
|
||||||
|
|
||||||
// Find the decoder for the video stream
|
// Find the decoder for the video stream
|
||||||
AVCodec *codec = avcodec_find_decoder(mVideoCodecContext->codec_id);
|
AVCodec *codec = avcodec_find_decoder(mVideoCodecContext->codec_id);
|
||||||
if ( codec == nullptr )
|
if ( codec == nullptr ) {
|
||||||
Panic("Unable to locate codec %d decoder", mVideoCodecContext->codec_id);
|
Error("Unable to locate codec %d decoder", mVideoCodecContext->codec_id);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// Open codec
|
// Open codec
|
||||||
if ( avcodec_open2(mVideoCodecContext, codec, nullptr) < 0 )
|
if ( avcodec_open2(mVideoCodecContext, codec, nullptr) < 0 ) {
|
||||||
Panic("Can't open codec");
|
Error("Can't open codec");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int pSize = av_image_get_buffer_size(imagePixFormat, width, height, 1);
|
int pSize = av_image_get_buffer_size(imagePixFormat, width, height, 1);
|
||||||
|
|
||||||
if ( (unsigned int)pSize != imagesize ) {
|
if ( (unsigned int)pSize != imagesize ) {
|
||||||
Fatal("Image size mismatch. Required: %d Available: %llu", pSize, imagesize);
|
Error("Image size mismatch. Required: %d Available: %llu", pSize, imagesize);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -208,18 +216,13 @@ int RemoteCameraRtsp::PreCapture() {
|
||||||
int RemoteCameraRtsp::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
int RemoteCameraRtsp::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
||||||
int frameComplete = false;
|
int frameComplete = false;
|
||||||
AVPacket *packet = &zm_packet->packet;
|
AVPacket *packet = &zm_packet->packet;
|
||||||
if ( !zm_packet->image ) {
|
|
||||||
Debug(1, "Allocating image %dx%d %d colours %d", width, height, colours, subpixelorder);
|
|
||||||
zm_packet->image = new Image(width, height, colours, subpixelorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
while (!frameComplete) {
|
while (!frameComplete) {
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
if (!rtspThread || rtspThread->IsStopped())
|
if (!rtspThread || rtspThread->IsStopped() || zm_terminate)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if ( rtspThread->getFrame(buffer) ) {
|
if (rtspThread->getFrame(buffer)) {
|
||||||
Debug(3, "Read frame %d bytes", buffer.size());
|
Debug(3, "Read frame %d bytes", buffer.size());
|
||||||
Hexdump(4, buffer.head(), 16);
|
Hexdump(4, buffer.head(), 16);
|
||||||
|
|
||||||
|
@ -254,36 +257,20 @@ int RemoteCameraRtsp::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
||||||
|
|
||||||
//while ( (!frameComplete) && (buffer.size() > 0) ) {
|
//while ( (!frameComplete) && (buffer.size() > 0) ) {
|
||||||
if ( buffer.size() > 0 ) {
|
if ( buffer.size() > 0 ) {
|
||||||
packet->data = buffer.head();
|
packet->data = (uint8_t*)av_malloc(buffer.size());
|
||||||
|
memcpy(packet->data, buffer.head(), buffer.size());
|
||||||
|
//packet->data = buffer.head();
|
||||||
packet->size = buffer.size();
|
packet->size = buffer.size();
|
||||||
bytes += packet->size;
|
bytes += packet->size;
|
||||||
|
buffer -= packet->size;
|
||||||
|
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
gettimeofday(&now, NULL);
|
gettimeofday(&now, nullptr);
|
||||||
packet->pts = packet->dts = now.tv_sec*1000000+now.tv_usec;
|
packet->pts = packet->dts = now.tv_sec*1000000+now.tv_usec;
|
||||||
|
zm_packet->codec_type = mVideoCodecContext->codec_type;
|
||||||
int bytes_consumed = zm_packet->decode(mVideoCodecContext);
|
zm_packet->stream = mVideoStream;
|
||||||
if ( bytes_consumed < 0 ) {
|
frameComplete = true;
|
||||||
Error("Error while decoding frame %d", frameCount);
|
Debug(2, "Frame: %d - %d/%d", frameCount, packet->size, buffer.size());
|
||||||
//Hexdump(Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size());
|
|
||||||
}
|
|
||||||
buffer -= packet->size;
|
|
||||||
if ( bytes_consumed ) {
|
|
||||||
zm_dump_video_frame(zm_packet->in_frame, "remote_rtsp_decode");
|
|
||||||
if (!mVideoStream->codecpar->width) {
|
|
||||||
zm_dump_codec(mVideoCodecContext);
|
|
||||||
zm_dump_codecpar(mVideoStream->codecpar);
|
|
||||||
mVideoStream->codecpar->width = zm_packet->in_frame->width;
|
|
||||||
mVideoStream->codecpar->height = zm_packet->in_frame->height;
|
|
||||||
zm_dump_codecpar(mVideoStream->codecpar);
|
|
||||||
}
|
|
||||||
zm_packet->codec_type = mVideoCodecContext->codec_type;
|
|
||||||
zm_packet->stream = mVideoStream;
|
|
||||||
frameComplete = true;
|
|
||||||
Debug(2, "Frame: %d - %d/%d", frameCount, bytes_consumed, buffer.size());
|
|
||||||
packet->data = nullptr;
|
|
||||||
packet->size = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} /* getFrame() */
|
} /* getFrame() */
|
||||||
} // end while true
|
} // end while true
|
||||||
|
|
45
src/zm_rgb.h
45
src/zm_rgb.h
|
@ -118,39 +118,32 @@ constexpr Rgb kRGBTransparent = 0x01000000;
|
||||||
|
|
||||||
/* Convert RGB colour value into BGR\ARGB\ABGR */
|
/* Convert RGB colour value into BGR\ARGB\ABGR */
|
||||||
inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) {
|
inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) {
|
||||||
Rgb result;
|
Rgb result = 0;
|
||||||
|
|
||||||
switch(p_subpixorder) {
|
|
||||||
|
|
||||||
|
switch (p_subpixorder) {
|
||||||
case ZM_SUBPIX_ORDER_BGR:
|
case ZM_SUBPIX_ORDER_BGR:
|
||||||
case ZM_SUBPIX_ORDER_BGRA:
|
case ZM_SUBPIX_ORDER_BGRA:
|
||||||
{
|
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
||||||
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
||||||
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
||||||
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
break;
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ZM_SUBPIX_ORDER_ARGB:
|
case ZM_SUBPIX_ORDER_ARGB:
|
||||||
{
|
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
||||||
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
||||||
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
||||||
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
break;
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ZM_SUBPIX_ORDER_ABGR:
|
case ZM_SUBPIX_ORDER_ABGR:
|
||||||
{
|
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
||||||
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
||||||
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
||||||
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
break;
|
||||||
}
|
/* Grayscale */
|
||||||
break;
|
|
||||||
/* Grayscale */
|
|
||||||
case ZM_SUBPIX_ORDER_NONE:
|
case ZM_SUBPIX_ORDER_NONE:
|
||||||
result = p_col & 0xff;
|
result = p_col & 0xff;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return p_col;
|
result = p_col;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -277,7 +277,7 @@ void RtpCtrlThread::Run() {
|
||||||
TimePoint last_receive = std::chrono::steady_clock::now();
|
TimePoint last_receive = std::chrono::steady_clock::now();
|
||||||
bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true.
|
bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true.
|
||||||
|
|
||||||
while (!mTerminate && select.wait() >= 0) {
|
while (!mTerminate && (select.wait() >= 0)) {
|
||||||
TimePoint now = std::chrono::steady_clock::now();
|
TimePoint now = std::chrono::steady_clock::now();
|
||||||
zm::Select::CommsList readable = select.getReadable();
|
zm::Select::CommsList readable = select.getReadable();
|
||||||
if ( readable.size() == 0 ) {
|
if ( readable.size() == 0 ) {
|
||||||
|
|
|
@ -45,8 +45,10 @@ RtpSource::RtpSource(
|
||||||
mFrame(65536),
|
mFrame(65536),
|
||||||
mFrameCount(0),
|
mFrameCount(0),
|
||||||
mFrameGood(true),
|
mFrameGood(true),
|
||||||
|
prevM(false),
|
||||||
mFrameReady(false),
|
mFrameReady(false),
|
||||||
mFrameProcessed(false)
|
mFrameProcessed(false),
|
||||||
|
mTerminate(false)
|
||||||
{
|
{
|
||||||
char hostname[256] = "";
|
char hostname[256] = "";
|
||||||
gethostname(hostname, sizeof(hostname));
|
gethostname(hostname, sizeof(hostname));
|
||||||
|
|
|
@ -91,8 +91,6 @@ private:
|
||||||
bool mFrameGood;
|
bool mFrameGood;
|
||||||
bool prevM;
|
bool prevM;
|
||||||
|
|
||||||
bool mTerminate;
|
|
||||||
|
|
||||||
bool mFrameReady;
|
bool mFrameReady;
|
||||||
std::condition_variable mFrameReadyCv;
|
std::condition_variable mFrameReadyCv;
|
||||||
std::mutex mFrameReadyMutex;
|
std::mutex mFrameReadyMutex;
|
||||||
|
@ -100,6 +98,7 @@ private:
|
||||||
bool mFrameProcessed;
|
bool mFrameProcessed;
|
||||||
std::condition_variable mFrameProcessedCv;
|
std::condition_variable mFrameProcessedCv;
|
||||||
std::mutex mFrameProcessedMutex;
|
std::mutex mFrameProcessedMutex;
|
||||||
|
bool mTerminate;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init(uint16_t seq);
|
void init(uint16_t seq);
|
||||||
|
|
|
@ -298,6 +298,14 @@ int main(int argc, char *argv[]) {
|
||||||
session->GetMediaSessionId(), xop::channel_1, audioFifoPath);
|
session->GetMediaSessionId(), xop::channel_1, audioFifoPath);
|
||||||
audioSource->setFrequency(monitor->GetAudioFrequency());
|
audioSource->setFrequency(monitor->GetAudioFrequency());
|
||||||
audioSource->setChannels(monitor->GetAudioChannels());
|
audioSource->setChannels(monitor->GetAudioChannels());
|
||||||
|
} else if (std::string::npos != audioFifoPath.find("pcm_alaw")) {
|
||||||
|
Debug(1, "Adding G711A source at %dHz %d channels",
|
||||||
|
monitor->GetAudioFrequency(), monitor->GetAudioChannels());
|
||||||
|
session->AddSource(xop::channel_1, xop::G711ASource::CreateNew());
|
||||||
|
audioSource = new ADTS_ZoneMinderFifoSource(rtspServer,
|
||||||
|
session->GetMediaSessionId(), xop::channel_1, audioFifoPath);
|
||||||
|
audioSource->setFrequency(monitor->GetAudioFrequency());
|
||||||
|
audioSource->setChannels(monitor->GetAudioChannels());
|
||||||
} else {
|
} else {
|
||||||
Warning("Unknown format in %s", audioFifoPath.c_str());
|
Warning("Unknown format in %s", audioFifoPath.c_str());
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,7 @@ void ZoneMinderFifoSource::WriteRun() {
|
||||||
fuNal.buffer()[2] = fuNal.buffer()[2]&~0x80; // FU header (no S bit)
|
fuNal.buffer()[2] = fuNal.buffer()[2]&~0x80; // FU header (no S bit)
|
||||||
headerSize = 3;
|
headerSize = 3;
|
||||||
}
|
}
|
||||||
while (nalRemaining) {
|
while (nalRemaining && !stop_) {
|
||||||
if ( nalRemaining < maxNalSize ) {
|
if ( nalRemaining < maxNalSize ) {
|
||||||
// This is the last fragment:
|
// This is the last fragment:
|
||||||
fuNal.buffer()[headerSize-1] |= 0x40; // set the E bit in the FU header
|
fuNal.buffer()[headerSize-1] |= 0x40; // set the E bit in the FU header
|
||||||
|
@ -166,7 +166,7 @@ int ZoneMinderFifoSource::getNextFrame() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug(3, "%s bytes read %d bytes, buffer size %u", m_fifo.c_str(), bytes_read, m_buffer.size());
|
Debug(3, "%s bytes read %d bytes, buffer size %u", m_fifo.c_str(), bytes_read, m_buffer.size());
|
||||||
while (m_buffer.size()) {
|
while (m_buffer.size() and !stop_) {
|
||||||
unsigned int data_size = 0;
|
unsigned int data_size = 0;
|
||||||
int64_t pts;
|
int64_t pts;
|
||||||
unsigned char *header_end = nullptr;
|
unsigned char *header_end = nullptr;
|
||||||
|
@ -224,7 +224,7 @@ int ZoneMinderFifoSource::getNextFrame() {
|
||||||
int bytes_needed = data_size - (m_buffer.size() - header_size);
|
int bytes_needed = data_size - (m_buffer.size() - header_size);
|
||||||
if (bytes_needed > 0) {
|
if (bytes_needed > 0) {
|
||||||
Debug(4, "Need another %d bytes. Trying to read them", bytes_needed);
|
Debug(4, "Need another %d bytes. Trying to read them", bytes_needed);
|
||||||
while (bytes_needed) {
|
while (bytes_needed and !stop_) {
|
||||||
bytes_read = m_buffer.read_into(m_fd, bytes_needed);
|
bytes_read = m_buffer.read_into(m_fd, bytes_needed);
|
||||||
if (bytes_read <= 0) {
|
if (bytes_read <= 0) {
|
||||||
Debug(1, "Failed to read another %d bytes, got %d.", bytes_needed, bytes_read);
|
Debug(1, "Failed to read another %d bytes, got %d.", bytes_needed, bytes_read);
|
||||||
|
@ -252,13 +252,14 @@ int ZoneMinderFifoSource::getNextFrame() {
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lck(mutex_);
|
std::unique_lock<std::mutex> lck(mutex_);
|
||||||
Debug(3, "have lock");
|
Debug(3, "have lock");
|
||||||
while (framesList.size()) {
|
while (!stop_ && framesList.size()) {
|
||||||
std::pair<unsigned char*, size_t> nal = framesList.front();
|
std::pair<unsigned char*, size_t> nal = framesList.front();
|
||||||
framesList.pop_front();
|
framesList.pop_front();
|
||||||
NAL_Frame *Nal = new NAL_Frame(nal.first, nal.second, pts);
|
NAL_Frame *Nal = new NAL_Frame(nal.first, nal.second, pts);
|
||||||
m_nalQueue.push(Nal);
|
m_nalQueue.push(Nal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Debug(3, "notifying");
|
||||||
condition_.notify_all();
|
condition_.notify_all();
|
||||||
} // end while m_buffer.size()
|
} // end while m_buffer.size()
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -21,7 +21,7 @@ class ZoneMinderDeviceSource;
|
||||||
|
|
||||||
class BaseServerMediaSubsession {
|
class BaseServerMediaSubsession {
|
||||||
public:
|
public:
|
||||||
BaseServerMediaSubsession(StreamReplicator* replicator):
|
explicit BaseServerMediaSubsession(StreamReplicator* replicator):
|
||||||
m_replicator(replicator) {};
|
m_replicator(replicator) {};
|
||||||
|
|
||||||
FramedSource* createSource(
|
FramedSource* createSource(
|
||||||
|
|
|
@ -3,34 +3,44 @@
|
||||||
|
|
||||||
#ifdef HAVE_SENDFILE4_SUPPORT
|
#ifdef HAVE_SENDFILE4_SUPPORT
|
||||||
#include <sys/sendfile.h>
|
#include <sys/sendfile.h>
|
||||||
int zm_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) {
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = sendfile(out_fd, in_fd, offset, size);
|
|
||||||
if ( err < 0 )
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
#elif HAVE_SENDFILE7_SUPPORT
|
#elif HAVE_SENDFILE7_SUPPORT
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
int zm_sendfile(int out_fd, int in_fd, off_t *offset, off_t size) {
|
|
||||||
int err;
|
|
||||||
err = sendfile(in_fd, out_fd, *offset, size, nullptr, &size, 0);
|
|
||||||
if (err && errno != EAGAIN)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
if (size) {
|
|
||||||
*offset += size;
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
#error "Your platform does not support sendfile. Sorry."
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Function to send the contents of a file. Will use sendfile or fall back to reading/writing */
|
||||||
|
|
||||||
|
ssize_t zm_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) {
|
||||||
|
#ifdef HAVE_SENDFILE4_SUPPORT
|
||||||
|
ssize_t err = sendfile(out_fd, in_fd, offset, size);
|
||||||
|
if (err < 0) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
|
||||||
|
#elif HAVE_SENDFILE7_SUPPORT
|
||||||
|
ssize_t err = sendfile(in_fd, out_fd, *offset, size, nullptr, &size, 0);
|
||||||
|
if (err && errno != EAGAIN)
|
||||||
|
return -errno;
|
||||||
|
return size;
|
||||||
|
#else
|
||||||
|
uint8_t buffer[size];
|
||||||
|
ssize_t err = read(in_fd, buffer, size);
|
||||||
|
if (err < 0) {
|
||||||
|
Error("Unable to read %zu bytes: %s", size, strerror(errno));
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fwrite(out_fd, buffer, size);
|
||||||
|
if (err < 0) {
|
||||||
|
Error("Unable to write %zu bytes: %s", size, strerror(errno));
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#endif // ZM_SENDFILE_H
|
#endif // ZM_SENDFILE_H
|
||||||
|
|
|
@ -31,10 +31,8 @@ constexpr Seconds StreamBase::MAX_STREAM_DELAY;
|
||||||
constexpr Milliseconds StreamBase::MAX_SLEEP;
|
constexpr Milliseconds StreamBase::MAX_SLEEP;
|
||||||
|
|
||||||
StreamBase::~StreamBase() {
|
StreamBase::~StreamBase() {
|
||||||
if (vid_stream) {
|
delete vid_stream;
|
||||||
delete vid_stream;
|
delete temp_img_buffer;
|
||||||
vid_stream = nullptr;
|
|
||||||
}
|
|
||||||
closeComms();
|
closeComms();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,10 +126,10 @@ bool StreamBase::checkCommandQueue() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if ( connkey ) {
|
} else if ( connkey ) {
|
||||||
Warning("No sd in checkCommandQueue, comms not open?");
|
Warning("No sd in checkCommandQueue, comms not open for connkey %06d?", connkey);
|
||||||
} else {
|
} else {
|
||||||
// Perfectly valid if only getting a snapshot
|
// Perfectly valid if only getting a snapshot
|
||||||
Debug(1, "No sd in checkCommandQueue, comms not open?");
|
Debug(1, "No sd in checkCommandQueue, comms not open.");
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} // end bool StreamBase::checkCommandQueue()
|
} // end bool StreamBase::checkCommandQueue()
|
||||||
|
@ -157,7 +155,6 @@ Image *StreamBase::prepareImage(Image *image) {
|
||||||
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
|
int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE;
|
||||||
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
|
int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE;
|
||||||
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
|
int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag;
|
||||||
int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag;
|
|
||||||
|
|
||||||
Debug(3,
|
Debug(3,
|
||||||
"Scaling by %d, zooming by %d = magnifying by %d(%d)\n"
|
"Scaling by %d, zooming by %d = magnifying by %d(%d)\n"
|
||||||
|
@ -169,8 +166,7 @@ Image *StreamBase::prepareImage(Image *image) {
|
||||||
"Last actual image width = %d, height = %d\n"
|
"Last actual image width = %d, height = %d\n"
|
||||||
"Display image width = %d, height = %d\n"
|
"Display image width = %d, height = %d\n"
|
||||||
"Last display image width = %d, height = %d\n"
|
"Last display image width = %d, height = %d\n"
|
||||||
"Send image width = %d, height = %d\n"
|
"Send image width = %d, height = %d\n",
|
||||||
"Last send image width = %d, height = %d\n",
|
|
||||||
scale, zoom, mag, act_mag,
|
scale, zoom, mag, act_mag,
|
||||||
last_scale, last_zoom, last_mag, last_act_mag,
|
last_scale, last_zoom, last_mag, last_act_mag,
|
||||||
base_image_width, base_image_height,
|
base_image_width, base_image_height,
|
||||||
|
@ -180,8 +176,7 @@ Image *StreamBase::prepareImage(Image *image) {
|
||||||
last_act_image_width, last_act_image_height,
|
last_act_image_width, last_act_image_height,
|
||||||
disp_image_width, disp_image_height,
|
disp_image_width, disp_image_height,
|
||||||
last_disp_image_width, last_disp_image_height,
|
last_disp_image_width, last_disp_image_height,
|
||||||
send_image_width, send_image_height,
|
send_image_width, send_image_height
|
||||||
last_send_image_width, last_send_image_height
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( ( mag != ZM_SCALE_BASE ) && (act_mag != ZM_SCALE_BASE) ) {
|
if ( ( mag != ZM_SCALE_BASE ) && (act_mag != ZM_SCALE_BASE) ) {
|
||||||
|
@ -386,9 +381,9 @@ void StreamBase::openComms() {
|
||||||
strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path));
|
strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path));
|
||||||
rem_addr.sun_family = AF_UNIX;
|
rem_addr.sun_family = AF_UNIX;
|
||||||
|
|
||||||
last_comm_update = std::chrono::system_clock::now();
|
last_comm_update = std::chrono::steady_clock::now();
|
||||||
|
Debug(3, "comms open at %s", loc_sock_path);
|
||||||
} // end if connKey > 0
|
} // end if connKey > 0
|
||||||
Debug(3, "comms open at %s", loc_sock_path);
|
|
||||||
} // end void StreamBase::openComms()
|
} // end void StreamBase::openComms()
|
||||||
|
|
||||||
void StreamBase::closeComms() {
|
void StreamBase::closeComms() {
|
||||||
|
|
|
@ -40,6 +40,7 @@ public:
|
||||||
STREAM_SINGLE,
|
STREAM_SINGLE,
|
||||||
STREAM_MPEG
|
STREAM_MPEG
|
||||||
} StreamType;
|
} StreamType;
|
||||||
|
typedef enum { FRAME_NORMAL, FRAME_ANALYSIS } FrameType;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static constexpr Seconds MAX_STREAM_DELAY = Seconds(5);
|
static constexpr Seconds MAX_STREAM_DELAY = Seconds(5);
|
||||||
|
@ -88,6 +89,9 @@ protected:
|
||||||
CMD_VARPLAY,
|
CMD_VARPLAY,
|
||||||
CMD_GET_IMAGE,
|
CMD_GET_IMAGE,
|
||||||
CMD_QUIT,
|
CMD_QUIT,
|
||||||
|
CMD_MAXFPS,
|
||||||
|
CMD_ANALYZE_ON,
|
||||||
|
CMD_ANALYZE_OFF,
|
||||||
CMD_QUERY=99
|
CMD_QUERY=99
|
||||||
} MsgCommand;
|
} MsgCommand;
|
||||||
|
|
||||||
|
@ -96,6 +100,7 @@ protected:
|
||||||
std::shared_ptr<Monitor> monitor;
|
std::shared_ptr<Monitor> monitor;
|
||||||
|
|
||||||
StreamType type;
|
StreamType type;
|
||||||
|
FrameType frame_type;
|
||||||
const char *format;
|
const char *format;
|
||||||
int replay_rate;
|
int replay_rate;
|
||||||
int scale;
|
int scale;
|
||||||
|
@ -118,26 +123,30 @@ protected:
|
||||||
bool paused;
|
bool paused;
|
||||||
int step;
|
int step;
|
||||||
|
|
||||||
SystemTimePoint now;
|
TimePoint now;
|
||||||
SystemTimePoint last_comm_update;
|
TimePoint last_comm_update;
|
||||||
|
|
||||||
double maxfps;
|
double maxfps;
|
||||||
double base_fps; // Should be capturing fps, hence a rough target
|
double base_fps; // Should be capturing fps, hence a rough target
|
||||||
double effective_fps; // Target fps after taking max_fps into account
|
double effective_fps; // Target fps after taking max_fps into account
|
||||||
double actual_fps; // sliding calculated actual streaming fps achieved
|
double actual_fps; // sliding calculated actual streaming fps achieved
|
||||||
SystemTimePoint last_fps_update;
|
TimePoint last_fps_update;
|
||||||
int frame_count; // Count of frames sent
|
int frame_count; // Count of frames sent
|
||||||
int last_frame_count; // Used in calculating actual_fps from frame_count - last_frame_count
|
int last_frame_count; // Used in calculating actual_fps from frame_count - last_frame_count
|
||||||
|
|
||||||
int frame_mod;
|
int frame_mod;
|
||||||
|
|
||||||
SystemTimePoint last_frame_sent;
|
TimePoint last_frame_sent;
|
||||||
SystemTimePoint last_frame_timestamp;
|
SystemTimePoint last_frame_timestamp;
|
||||||
|
TimePoint when_to_send_next_frame; // When to send next frame so if now < send_next_frame, skip
|
||||||
|
|
||||||
VideoStream *vid_stream;
|
VideoStream *vid_stream;
|
||||||
|
|
||||||
CmdMsg msg;
|
CmdMsg msg;
|
||||||
|
|
||||||
|
unsigned char *temp_img_buffer; // Used when encoding or sending file data
|
||||||
|
size_t temp_img_buffer_size;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool loadMonitor(int monitor_id);
|
bool loadMonitor(int monitor_id);
|
||||||
bool checkInitialised();
|
bool checkInitialised();
|
||||||
|
@ -151,6 +160,7 @@ public:
|
||||||
monitor_id(0),
|
monitor_id(0),
|
||||||
monitor(nullptr),
|
monitor(nullptr),
|
||||||
type(DEFAULT_TYPE),
|
type(DEFAULT_TYPE),
|
||||||
|
frame_type(FRAME_NORMAL),
|
||||||
format(""),
|
format(""),
|
||||||
replay_rate(DEFAULT_RATE),
|
replay_rate(DEFAULT_RATE),
|
||||||
scale(DEFAULT_SCALE),
|
scale(DEFAULT_SCALE),
|
||||||
|
@ -175,7 +185,9 @@ public:
|
||||||
actual_fps(0.0),
|
actual_fps(0.0),
|
||||||
frame_count(0),
|
frame_count(0),
|
||||||
last_frame_count(0),
|
last_frame_count(0),
|
||||||
frame_mod(1)
|
frame_mod(1),
|
||||||
|
temp_img_buffer(nullptr),
|
||||||
|
temp_img_buffer_size(0)
|
||||||
{
|
{
|
||||||
memset(&loc_sock_path, 0, sizeof(loc_sock_path));
|
memset(&loc_sock_path, 0, sizeof(loc_sock_path));
|
||||||
memset(&loc_addr, 0, sizeof(loc_addr));
|
memset(&loc_addr, 0, sizeof(loc_addr));
|
||||||
|
@ -196,7 +208,9 @@ public:
|
||||||
type = STREAM_RAW;
|
type = STREAM_RAW;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
void setStreamFrameType(FrameType p_type) {
|
||||||
|
frame_type = p_type;
|
||||||
}
|
}
|
||||||
void setStreamFormat(const char *p_format) {
|
void setStreamFormat(const char *p_format) {
|
||||||
format = p_format;
|
format = p_format;
|
||||||
|
@ -207,10 +221,11 @@ public:
|
||||||
scale = DEFAULT_SCALE;
|
scale = DEFAULT_SCALE;
|
||||||
}
|
}
|
||||||
void setStreamReplayRate(int p_rate) {
|
void setStreamReplayRate(int p_rate) {
|
||||||
Debug(2, "Setting replay_rate %d", p_rate);
|
Debug(1, "Setting replay_rate %d", p_rate);
|
||||||
replay_rate = p_rate;
|
replay_rate = p_rate;
|
||||||
}
|
}
|
||||||
void setStreamMaxFPS(double p_maxfps) {
|
void setStreamMaxFPS(double p_maxfps) {
|
||||||
|
Debug(1, "Setting max fps to %f", p_maxfps);
|
||||||
maxfps = p_maxfps;
|
maxfps = p_maxfps;
|
||||||
}
|
}
|
||||||
void setStreamBitrate(int p_bitrate) {
|
void setStreamBitrate(int p_bitrate) {
|
||||||
|
|
|
@ -22,7 +22,14 @@
|
||||||
#include "zm_image.h"
|
#include "zm_image.h"
|
||||||
#include "zm_logger.h"
|
#include "zm_logger.h"
|
||||||
|
|
||||||
SWScale::SWScale() : gotdefaults(false), swscale_ctx(nullptr), input_avframe(nullptr), output_avframe(nullptr) {
|
SWScale::SWScale() :
|
||||||
|
gotdefaults(false),
|
||||||
|
swscale_ctx(nullptr),
|
||||||
|
input_avframe(nullptr),
|
||||||
|
output_avframe(nullptr),
|
||||||
|
default_width(0),
|
||||||
|
default_height(0)
|
||||||
|
{
|
||||||
Debug(4, "SWScale object created");
|
Debug(4, "SWScale object created");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue