Merge branch 'master' into fmt
This commit is contained in:
commit
cad57df0bb
|
@ -5,6 +5,7 @@ web/api/lib
|
|||
web/includes/csrf/
|
||||
web/js/videojs.zoomrotate.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/dateTimePicker
|
||||
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/video.js
|
||||
web/tools/mootools
|
||||
web/js/janus.js
|
||||
web/js/ajaxQueue.js
|
||||
|
||||
# Cannot be parsed as JS
|
||||
web/skins/classic/includes/export_functions.php
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module.exports = {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"es2017": true,
|
||||
},
|
||||
"extends": ["google"],
|
||||
"overrides": [{
|
||||
|
|
|
@ -19,7 +19,7 @@ jobs:
|
|||
- crypto_backend: gnutls
|
||||
jwt_backend: libjwt
|
||||
runs-on: ubuntu-latest
|
||||
container: centos:8
|
||||
container: rockylinux:8
|
||||
|
||||
steps:
|
||||
- name: Enable RPMFusion, EPEL and PowerTools
|
||||
|
|
|
@ -16,6 +16,6 @@ jobs:
|
|||
with:
|
||||
submodules: recursive
|
||||
- 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
|
||||
run: npx eslint --ext .php,.js .
|
||||
|
|
|
@ -2,8 +2,7 @@ name: Create packages
|
|||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
|
@ -21,6 +20,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: '0'
|
||||
submodules: recursive
|
||||
- name: Run packpack
|
||||
env:
|
||||
|
@ -29,3 +29,13 @@ jobs:
|
|||
DIST: ${{ matrix.os_dist.dist }}
|
||||
DOCKER_REPO: iconzm/packpack
|
||||
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_PATH_MAP
|
||||
ZM_PATH_ARP
|
||||
ZM_PATH_ARP_SCAN
|
||||
ZM_CONFIG_DIR
|
||||
ZM_CONFIG_SUBDIR
|
||||
ZM_SYSTEMD
|
||||
|
@ -145,6 +146,8 @@ set(ZM_PATH_MAP "/dev/shm" CACHE PATH
|
|||
"Location to save mapped memory files, default: /dev/shm")
|
||||
set(ZM_PATH_ARP "" CACHE PATH
|
||||
"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
|
||||
"Location of ZoneMinder configuration, default system config directory")
|
||||
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 to ON to enable basic ONVIF support. This is EXPERIMENTAL and may not
|
||||
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 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
|
||||
|
@ -316,10 +321,10 @@ endif()
|
|||
# Do not check for cURL if ZM_NO_CURL is on
|
||||
if(NOT ZM_NO_CURL)
|
||||
# cURL
|
||||
find_package(CURL)
|
||||
find_package(CURL REQUIRED)
|
||||
if(CURL_FOUND)
|
||||
set(HAVE_LIBCURL 1)
|
||||
#list(APPEND ZM_BIN_LIBS ${CURL_LIBRARIES})
|
||||
list(APPEND ZM_BIN_LIBS ${CURL_LIBRARIES})
|
||||
include_directories(${CURL_INCLUDE_DIRS})
|
||||
set(CMAKE_REQUIRED_INCLUDES ${CURL_INCLUDE_DIRS})
|
||||
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")
|
||||
endif()
|
||||
|
||||
# pcre (using find_library and find_path)
|
||||
find_library(PCRE_LIBRARIES pcre)
|
||||
if(PCRE_LIBRARIES)
|
||||
set(HAVE_LIBPCRE 1)
|
||||
list(APPEND ZM_BIN_LIBS "${PCRE_LIBRARIES}")
|
||||
find_path(PCRE_INCLUDE_DIR pcre.h)
|
||||
if(PCRE_INCLUDE_DIR)
|
||||
include_directories("${PCRE_INCLUDE_DIR}")
|
||||
set(CMAKE_REQUIRED_INCLUDES "${PCRE_INCLUDE_DIR}")
|
||||
# Do not check for cURL if ZM_NO_CURL is on
|
||||
if(NOT ZM_NO_PRCE)
|
||||
# pcre (using find_library and find_path)
|
||||
find_library(PCRE_LIBRARIES pcre)
|
||||
if(PCRE_LIBRARIES)
|
||||
set(HAVE_LIBPCRE 1)
|
||||
list(APPEND ZM_BIN_LIBS "${PCRE_LIBRARIES}")
|
||||
find_path(PCRE_INCLUDE_DIR pcre.h)
|
||||
if(PCRE_INCLUDE_DIR)
|
||||
include_directories("${PCRE_INCLUDE_DIR}")
|
||||
set(CMAKE_REQUIRED_INCLUDES "${PCRE_INCLUDE_DIR}")
|
||||
endif()
|
||||
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()
|
||||
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()
|
||||
|
||||
# mysqlclient (using find_library and find_path)
|
||||
|
@ -513,6 +521,15 @@ endif()
|
|||
#list(APPEND ZM_BIN_LIBS "${Boost_LIBRARIES}")
|
||||
#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)
|
||||
set(HAVE_RTSP_SERVER 1)
|
||||
else()
|
||||
|
@ -542,6 +559,7 @@ set(ZM_PCRE 0)
|
|||
if(HAVE_LIBPCRE AND HAVE_PCRE_H)
|
||||
set(ZM_PCRE 1)
|
||||
endif()
|
||||
|
||||
# Check for mmap and enable in all components
|
||||
set(ZM_MEM_MAPPED 0)
|
||||
set(ENABLE_MMAP no)
|
||||
|
@ -628,6 +646,18 @@ if(ZM_PATH_ARP STREQUAL "")
|
|||
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
|
||||
set(ZM_PID "${ZM_RUNDIR}/zm.pid")
|
||||
set(ZM_CONFIG "${ZM_CONFIG_DIR}/zm.conf")
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
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)
|
||||
[![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
|
||||
|
||||
|
@ -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:
|
||||
|
||||
- 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)
|
||||
- RHEL/CentOS and clones 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
|
||||
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
|
||||
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_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.37.4.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.37.4.sql" @ONLY)
|
||||
|
||||
# Glob all database upgrade scripts
|
||||
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 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 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(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(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,
|
||||
`Category` varchar(32) NOT NULL default '',
|
||||
`Readonly` tinyint(3) unsigned NOT NULL default '0',
|
||||
`Private` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
`Requires` text,
|
||||
PRIMARY KEY (`Name`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
@ -188,7 +189,7 @@ CREATE TABLE `Events` (
|
|||
`StorageId` smallint(5) unsigned default 0,
|
||||
`SecondaryStorageId` smallint(5) unsigned default 0,
|
||||
`Name` varchar(64) NOT NULL default '',
|
||||
`Cause` varchar(32) NOT NULL default '',
|
||||
`Cause` TEXT,
|
||||
`StartDateTime` datetime default NULL,
|
||||
`EndDateTime` datetime default NULL,
|
||||
`Width` smallint(5) unsigned NOT NULL default '0',
|
||||
|
@ -283,6 +284,7 @@ CREATE TABLE `Filters` (
|
|||
`Id` int(10) unsigned NOT NULL auto_increment,
|
||||
`Name` varchar(64) NOT NULL default '',
|
||||
`UserId` int(10) unsigned,
|
||||
`ExecuteInterval` int(10) unsigned NOT NULL default '60',
|
||||
`Query_json` text NOT NULL,
|
||||
`AutoArchive` 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`;
|
||||
CREATE TABLE `MonitorPresets` (
|
||||
`Id` int(10) unsigned NOT NULL auto_increment,
|
||||
`ModelId` int unsigned, FOREIGN KEY (`ModelId`) REFERENCES `Models` (Id),
|
||||
`Name` varchar(64) NOT NULL default '',
|
||||
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local',
|
||||
`Device` tinytext,
|
||||
|
@ -447,16 +450,24 @@ CREATE TABLE `Monitors` (
|
|||
`Notes` TEXT,
|
||||
`ServerId` int(10) unsigned,
|
||||
`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',
|
||||
`Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor',
|
||||
`Enabled` 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),
|
||||
`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_Username` VARCHAR(64) NOT NULL DEFAULT '',
|
||||
`ONVIF_Password` 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 '',
|
||||
`Channel` tinyint(3) 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
|
||||
--
|
||||
|
||||
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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'Axis IP, mpeg4, RTP/RTSP/HTTP','Remote',NULL,NULL,NULL,'rtsp','rtpRtspHttp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||
INSERT INTO MonitorPresets VALUES (NULL,'D-link DCS-930L, 640x480, mjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/mjpeg.cgi',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||
INSERT INTO MonitorPresets VALUES (NULL,'D-Link DCS-5020L, 640x480, mjpeg','Remote','http',0,0,'http','simple','<username>:<pwd>@<ip-address>','80','/video.cgi',NULL,640,480,0,NULL,1,'34',NULL,'<username>:<pwd>@<ip-address>',100,100);
|
||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
|
||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100);
|
||||
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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,'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 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,'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, 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, 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, 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, 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, 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, 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, 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, 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, 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 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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/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,'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-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,'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, 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, 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, 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, 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, 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 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, 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, 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, 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, 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, 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,'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, 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, 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,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,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,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,'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,'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,'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,'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,'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,'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,'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, 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, 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, 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), 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, 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, 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, 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 (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, 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, 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, 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), 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, 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, 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, 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,'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,'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 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 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,'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,'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,'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,'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, 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
|
||||
|
@ -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.
|
||||
source @PKGDATADIR@/db/triggers.sql
|
||||
|
||||
source @PKGDATADIR@/db/manufacturers.sql
|
||||
source @PKGDATADIR@/db/models.sql
|
||||
--
|
||||
-- Apply the initial configuration
|
||||
--
|
||||
|
|
|
@ -56,7 +56,7 @@ EXECUTE stmt;
|
|||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Monitor_Status'
|
||||
AND column_name = 'DayEvents'
|
||||
AND column_name = 'DayEventDiskSpace'
|
||||
) > 0,
|
||||
"ALTER TABLE `Monitor_Status` DROP `DayEventDiskSpace`",
|
||||
"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
|
||||
|
||||
# RtspServer is configured as a git submodule
|
||||
%global rtspserver_commit cd7fd49becad6010a1b8466bfebbd93999a39878
|
||||
%global rtspserver_commit eab32851421ffe54fec0229c3efc44c642bc8d46
|
||||
|
||||
%global sslcert %{_sysconfdir}/pki/tls/certs/localhost.crt
|
||||
%global sslkey %{_sysconfdir}/pki/tls/private/localhost.key
|
||||
|
@ -36,7 +36,7 @@
|
|||
%global _hardened_build 1
|
||||
|
||||
Name: zoneminder
|
||||
Version: 1.37.1
|
||||
Version: 1.37.11
|
||||
Release: 1%{?dist}
|
||||
Summary: A camera monitoring and analysis tool
|
||||
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
|
||||
|
||||
%{_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}/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
|
||||
,libturbojpeg0-dev
|
||||
,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat
|
||||
,libpcre3-dev
|
||||
,libpolkit-gobject-1-dev
|
||||
,libv4l-dev [!hurd-any]
|
||||
,libvlc-dev
|
||||
|
@ -32,6 +31,7 @@ Build-Depends: debhelper (>= 11), sphinx-doc, python3-sphinx, dh-linktree, dh-ap
|
|||
,libvncserver-dev
|
||||
,libfmt-dev
|
||||
,libjwt-gnutls-dev|libjwt-dev
|
||||
,libgsoap-dev
|
||||
|
||||
Standards-Version: 4.5.0
|
||||
Homepage: https://www.zoneminder.com/
|
||||
|
@ -44,7 +44,8 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
|||
,libswscale5|libswscale4
|
||||
,libswresample3|libswresample2
|
||||
,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
|
||||
,libphp-serialization-perl
|
||||
,libmodule-load-conditional-perl
|
||||
|
@ -72,12 +73,12 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
|||
,policykit-1
|
||||
,rsyslog | system-log-daemon
|
||||
,zip
|
||||
,libpcre3
|
||||
,libcrypt-eksblowfish-perl
|
||||
,libdata-entropy-perl
|
||||
,libvncclient1|libvncclient0
|
||||
,libfmt
|
||||
,libjwt-gnutls0|libjwt0
|
||||
,libgsoap-2.8.104|libgsoap-2.8.91|libgsoap-2.8.75|libgsoap-2.8.60|libgsoap10
|
||||
|
||||
Recommends: ${misc:Recommends}
|
||||
,libapache2-mod-php | php-fpm
|
||||
|
|
|
@ -19,6 +19,7 @@ override_dh_auto_configure:
|
|||
-DCMAKE_VERBOSE_MAKEFILE=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_MAN=0 \
|
||||
-DZM_NO_PCRE=ON \
|
||||
-DZM_CONFIG_DIR="/etc/zm" \
|
||||
-DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \
|
||||
-DZM_RUNDIR="/run/zm" \
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
Description=ZoneMinder CCTV recording and surveillance system
|
||||
After=network.target mysql.service
|
||||
# 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]
|
||||
#User=www-data
|
||||
|
|
|
@ -4,7 +4,7 @@ Debian
|
|||
.. contents::
|
||||
|
||||
Easy Way: 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 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
|
||||
|
||||
|
@ -104,7 +104,7 @@ Add the following to the /etc/apt/sources.list.d/zoneminder.list file
|
|||
|
||||
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
|
||||
|
||||
|
@ -337,3 +337,6 @@ Reload Apache to enable your changes and then 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.
|
||||
|
||||
.. _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(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.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-tmpfiles.conf.in "${CMAKE_CURRENT_BINARY_DIR}/zoneminder-tmpfiles.conf" @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)
|
||||
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.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()
|
||||
|
||||
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]
|
||||
Description=ZoneMinder CCTV recording and security system
|
||||
After=network.target mysqld.service httpd.service
|
||||
Requires=mysqld.service httpd.service
|
||||
After=network.target mysqld.service httpd.service janus.service
|
||||
Requires=mysqld.service httpd.service janus.service
|
||||
|
||||
[Service]
|
||||
User=@WEB_USER@
|
||||
|
|
|
@ -156,7 +156,7 @@ sub loadConfigFromDB {
|
|||
print("Error: unable to load options from database: $DBI::errstr\n");
|
||||
return(0);
|
||||
}
|
||||
my $sql = "select * from Config";
|
||||
my $sql = 'SELECT * FROM Config';
|
||||
my $sth = $dbh->prepare_cached( $sql )
|
||||
or croak( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||
my $res = $sth->execute()
|
||||
|
@ -203,7 +203,7 @@ sub saveConfigToDB {
|
|||
my $res = $dbh->do( $sql )
|
||||
or croak( "Can't do '$sql': ".$dbh->errstr() );
|
||||
|
||||
$sql = "replace into Config set Id = ?, Name = ?, Value = ?, Type = ?, DefaultValue = ?, Hint = ?, Pattern = ?, Format = ?, Prompt = ?, Help = ?, Category = ?, Readonly = ?, Requires = ?";
|
||||
$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 )
|
||||
or croak( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||
foreach my $option ( @options ) {
|
||||
|
@ -240,6 +240,7 @@ sub saveConfigToDB {
|
|||
$option->{help},
|
||||
$option->{category},
|
||||
$option->{readonly} ? 1 : 0,
|
||||
$option->{private} ? 1 : 0,
|
||||
$option->{db_requires}
|
||||
) or croak("Can't execute when updating config entry $$option{name}: ".$sth->errstr() );
|
||||
} # end foreach option
|
||||
|
|
|
@ -304,6 +304,7 @@ our @options = (
|
|||
{ name=>'ZM_AUTH_RELAY', value=>'hashed' }
|
||||
],
|
||||
type => $types{string},
|
||||
private => 1,
|
||||
category => 'system',
|
||||
},
|
||||
{
|
||||
|
@ -370,6 +371,28 @@ our @options = (
|
|||
type => $types{boolean},
|
||||
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',
|
||||
default => 'yes',
|
||||
|
@ -462,6 +485,7 @@ our @options = (
|
|||
{name=>'ZM_OPT_USE_GOOG_RECAPTCHA', value=>'yes'}
|
||||
],
|
||||
type => $types{string},
|
||||
private => 1,
|
||||
category => 'system',
|
||||
},
|
||||
{
|
||||
|
@ -477,6 +501,7 @@ our @options = (
|
|||
{name=>'ZM_OPT_USE_GOOG_RECAPTCHA', value=>'yes'}
|
||||
],
|
||||
type => $types{string},
|
||||
private => 1,
|
||||
category => 'system',
|
||||
},
|
||||
{
|
||||
|
|
|
@ -51,11 +51,21 @@ sub open {
|
|||
my $self = shift;
|
||||
|
||||
$self->loadMonitor();
|
||||
if ( $self->{Monitor}->{ControlAddress} !~ /^\w+:\/\// ) {
|
||||
# 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});
|
||||
|
||||
if ($self->{Monitor}->{ControlAddress} and ($self->{Monitor}->{ControlAddress} ne 'user:pass@ip')) {
|
||||
Debug("Getting connection details from Control Address " . $self->{Monitor}->{ControlAddress});
|
||||
if ( $self->{Monitor}->{ControlAddress} !~ /^\w+:\/\// ) {
|
||||
# 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;
|
||||
$self->{ua} = LWP::UserAgent->new;
|
||||
|
@ -64,6 +74,7 @@ sub open {
|
|||
$self->{state} = 'closed';
|
||||
|
||||
my ( $username, $password, $host ) = ( $uri->authority() =~ /^([^:]+):([^@]*)@(.+)$/ );
|
||||
Debug("Have username: $username password: $password host: $host from authority:" . $uri->authority());
|
||||
|
||||
$uri->userinfo(undef);
|
||||
|
||||
|
@ -75,40 +86,47 @@ sub open {
|
|||
# test auth
|
||||
my $res = $self->{ua}->get($uri->canonical().$url);
|
||||
|
||||
if ( $res->is_success ) {
|
||||
if ( $res->content() ne "Properties.PTZ.PTZ=yes\n" ) {
|
||||
if ($res->is_success) {
|
||||
if ($res->content() ne "Properties.PTZ.PTZ=yes\n") {
|
||||
Warning('Response suggests that camera doesn\'t support PTZ. Content:('.$res->content().')');
|
||||
}
|
||||
$self->{state} = 'open';
|
||||
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();
|
||||
foreach my $k ( keys %$headers ) {
|
||||
Debug("Initial Header $k => $$headers{$k}");
|
||||
}
|
||||
|
||||
if ( $$headers{'www-authenticate'} ) {
|
||||
my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
|
||||
if ( $tokens =~ /\w+="([^"]+)"/i ) {
|
||||
if ( $realm ne $1 ) {
|
||||
$realm = $1;
|
||||
$self->{ua}->credentials($uri->host_port(), $realm, $username, $password);
|
||||
$res = $self->{ua}->get($uri->canonical().$url);
|
||||
if ( $res->is_success() ) {
|
||||
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.");
|
||||
$self->{state} = 'open';
|
||||
return;
|
||||
foreach my $auth_header ( ref $$headers{'www-authenticate'} eq 'ARRAY' ? @{$$headers{'www-authenticate'}} : ($$headers{'www-authenticate'})) {
|
||||
my ( $auth, $tokens ) = $auth_header =~ /^(\w+)\s+(.*)$/;
|
||||
if ( $tokens =~ /\w+="([^"]+)"/i ) {
|
||||
if ( $realm ne $1 ) {
|
||||
$realm = $1;
|
||||
$self->{ua}->credentials($uri->host_port(), $realm, $username, $password);
|
||||
$res = $self->{ua}->get($uri->canonical().$url);
|
||||
if ( $res->is_success() ) {
|
||||
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.");
|
||||
$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 {
|
||||
Error('Authentication failed, not a REALM problem');
|
||||
}
|
||||
} else {
|
||||
Error('Failed to match realm in tokens');
|
||||
} # end if
|
||||
Error('Failed to match realm in tokens');
|
||||
} # end if
|
||||
} # end foreach auth header
|
||||
} else {
|
||||
Debug('No headers line');
|
||||
} # end if headers
|
||||
|
|
|
@ -194,7 +194,6 @@ sub getCamParams {
|
|||
}
|
||||
}
|
||||
|
||||
#autoStop
|
||||
#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
|
||||
sub autoStop {
|
||||
my $self = shift;
|
||||
|
@ -202,13 +201,19 @@ sub autoStop {
|
|||
|
||||
if ( $autostop ) {
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
} # end sub autoStop
|
||||
|
||||
# Reset the Camera
|
||||
sub reset {
|
||||
|
|
|
@ -374,6 +374,25 @@ sub reset {
|
|||
$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;
|
||||
__END__
|
||||
|
||||
|
|
|
@ -273,6 +273,9 @@ sub zmDbDo {
|
|||
if ( ! defined $rows ) {
|
||||
$sql =~ s/\?/'%s'/;
|
||||
Error(sprintf("Failed $sql :", @_).$dbh->errstr());
|
||||
} elsif ( ZoneMinder::Logger::logLevel() > INFO ) {
|
||||
$sql =~ s/\?/'%s'/;
|
||||
Debug(sprintf("Succeeded $sql : $rows rows affected", @_));
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ require Date::Parse;
|
|||
require POSIX;
|
||||
use Date::Format qw(time2str);
|
||||
use Time::HiRes qw(gettimeofday tv_interval stat);
|
||||
use Scalar::Util qw(looks_like_number);
|
||||
|
||||
#our @ISA = qw(ZoneMinder::Object);
|
||||
use parent qw(ZoneMinder::Object);
|
||||
|
@ -601,7 +602,7 @@ sub CopyTo {
|
|||
# First determine if we can move it to the dest.
|
||||
# We do this before bothering to lock the event
|
||||
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.';
|
||||
} elsif ( $$NewStorage{Id} == $$self{StorageId} ) {
|
||||
return 'Event is already located at ' . $NewPath;
|
||||
|
@ -733,19 +734,22 @@ sub MoveTo {
|
|||
|
||||
my $was_in_transaction = !$ZoneMinder::Database::dbh->{AutoCommit};
|
||||
$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 $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.
|
||||
$$self{StorageId} = $$NewStorage{Id};
|
||||
$self->Storage($NewStorage);
|
||||
$error .= $self->save();
|
||||
|
||||
# Going to leave it to upper layer as to whether we rollback or not
|
||||
# Going to leave it to upper layer as to whether we rollback or not
|
||||
}
|
||||
$ZoneMinder::Database::dbh->commit() if !$was_in_transaction;
|
||||
return $error if $error;
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ $primary_key = 'Id';
|
|||
%fields = map { $_ => $_ } qw(
|
||||
Id
|
||||
Name
|
||||
ExecuteInterval
|
||||
Query_json
|
||||
AutoArchive
|
||||
AutoUnarchive
|
||||
|
@ -106,7 +107,6 @@ sub Execute {
|
|||
$sql =~ s/zmSystemLoad/$load/g;
|
||||
}
|
||||
|
||||
$sql .= ' FOR UPDATE' if $$self{LockRows};
|
||||
|
||||
Debug("Filter::Execute SQL ($sql)");
|
||||
my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql)
|
||||
|
@ -230,8 +230,8 @@ sub Sql {
|
|||
# PostCondition, so no further SQL
|
||||
} else {
|
||||
( 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' ) {
|
||||
$value = '(SELECT * FROM Stats WHERE EventId=E.Id AND Score > 0 AND ZoneId='.$value.')';
|
||||
} elsif ( $term->{attr} =~ /^MonitorName/ ) {
|
||||
|
@ -250,7 +250,8 @@ sub Sql {
|
|||
$$self{Server} = new ZoneMinder::Server($temp_value);
|
||||
}
|
||||
} 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);
|
||||
} elsif ( $term->{attr} eq 'Name'
|
||||
|| $term->{attr} eq 'Cause'
|
||||
|
@ -322,7 +323,7 @@ sub Sql {
|
|||
$self->{Sql} .= ' IS NOT '.$value;
|
||||
} elsif ( $term->{op} eq '=[]' or $term->{op} eq 'IN' ) {
|
||||
$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).')';
|
||||
} elsif ( $term->{op} eq 'LIKE' ) {
|
||||
$self->{Sql} .= ' LIKE '.$value;
|
||||
|
@ -370,10 +371,7 @@ sub Sql {
|
|||
if ( @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 = '';
|
||||
if ( $filter_expr->{sort_field} eq 'Id' ) {
|
||||
$sort_column = 'E.Id';
|
||||
|
@ -405,14 +403,21 @@ sub Sql {
|
|||
$sort_column = 'E.MaxScore';
|
||||
} elsif ( $filter_expr->{sort_field} eq 'DiskSpace' ) {
|
||||
$sort_column = 'E.DiskSpace';
|
||||
} else {
|
||||
$sort_column = 'E.StartDateTime';
|
||||
} elsif ( $filter_expr->{sort_field} ne '' ) {
|
||||
$sort_column = 'E.'.$filter_expr->{sort_field};
|
||||
}
|
||||
my $sort_order = $filter_expr->{sort_asc} ? 'ASC' : 'DESC';
|
||||
$sql .= ' ORDER BY '.$sort_column.' '.$sort_order;
|
||||
if ( $filter_expr->{limit} ) {
|
||||
if ( $sort_column ne '' ) {
|
||||
$sql .= ' ORDER BY '.$sort_column.' '.($filter_expr->{sort_asc} ? 'ASC' : 'DESC');
|
||||
}
|
||||
if ($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;
|
||||
} # end if has Sql
|
||||
return $self->{Sql};
|
||||
|
|
|
@ -28,6 +28,7 @@ our %EXPORT_TAGS = (
|
|||
makePath
|
||||
jsonEncode
|
||||
jsonDecode
|
||||
jsonLoad
|
||||
systemStatus
|
||||
packageControl
|
||||
daemonControl
|
||||
|
@ -536,6 +537,23 @@ sub jsonDecode {
|
|||
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 {
|
||||
my %settings;
|
||||
foreach my $line ( split ( /\r?\n/, $_[0] ) ) {
|
||||
|
|
|
@ -638,6 +638,7 @@ sub logInit( ;@ ) {
|
|||
$logger = ZoneMinder::Logger->new() if !$logger;
|
||||
$logger->initialise(%options);
|
||||
logSetSignal();
|
||||
return $logger;
|
||||
}
|
||||
|
||||
sub logReinit {
|
||||
|
|
|
@ -42,6 +42,7 @@ our @ISA = qw(Exporter ZoneMinder::Base);
|
|||
# will save memory.
|
||||
our %EXPORT_TAGS = (
|
||||
constants => [ qw(
|
||||
STATE_UNKNOWN
|
||||
STATE_IDLE
|
||||
STATE_PREALARM
|
||||
STATE_ALARM
|
||||
|
@ -98,11 +99,12 @@ our $VERSION = $ZoneMinder::Base::VERSION;
|
|||
use ZoneMinder::Config qw(:all);
|
||||
use ZoneMinder::Logger qw(:all);
|
||||
|
||||
use constant STATE_IDLE => 0;
|
||||
use constant STATE_PREALARM => 1;
|
||||
use constant STATE_ALARM => 2;
|
||||
use constant STATE_ALERT => 3;
|
||||
use constant STATE_TAPE => 4;
|
||||
use constant STATE_UNKNOWN => 0;
|
||||
use constant STATE_IDLE => 1;
|
||||
use constant STATE_PREALARM => 2;
|
||||
use constant STATE_ALARM => 3;
|
||||
use constant STATE_ALERT => 4;
|
||||
use constant STATE_TAPE => 5;
|
||||
|
||||
use constant ACTION_GET => 1;
|
||||
use constant ACTION_SET => 2;
|
||||
|
|
|
@ -56,6 +56,14 @@ $serial = $primary_key = 'Id';
|
|||
Enabled
|
||||
LinkedMonitors
|
||||
Triggers
|
||||
EventStartCommand
|
||||
EventEndCommand
|
||||
ONVIF_URL
|
||||
ONVIF_Username
|
||||
ONVIF_Password
|
||||
ONVIF_Options
|
||||
ONVIF_Event_Listener
|
||||
use_Amcrest_API
|
||||
Device
|
||||
Channel
|
||||
Format
|
||||
|
@ -133,6 +141,9 @@ $serial = $primary_key = 'Id';
|
|||
DefaultCodec
|
||||
Latitude
|
||||
Longitude
|
||||
RTSPServer
|
||||
RTSPStreamName
|
||||
Importance
|
||||
);
|
||||
|
||||
%defaults = (
|
||||
|
@ -241,20 +252,26 @@ sub control {
|
|||
my $command = shift;
|
||||
my $process = shift;
|
||||
|
||||
if ( $command eq 'stop' or $command eq 'restart' ) {
|
||||
if ( $process ) {
|
||||
`/usr/bin/zmdc.pl stop $process -m $$monitor{Id}`;
|
||||
if ($command eq 'stop' or $command eq 'restart') {
|
||||
if ($process) {
|
||||
ZoneMinder::General::runCommand("zmdc.pl stop $process -m $$monitor{Id}");
|
||||
} else {
|
||||
`/usr/bin/zmdc.pl stop zma -m $$monitor{Id}`;
|
||||
`/usr/bin/zmdc.pl stop zmc -m $$monitor{Id}`;
|
||||
if ($monitor->{Type} eq 'Local') {
|
||||
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 ( $process ) {
|
||||
`/usr/bin/zmdc.pl start $process -m $$monitor{Id}`;
|
||||
ZoneMinder::General::runCommand("zmdc.pl start $process -m $$monitor{Id}");
|
||||
} else {
|
||||
`/usr/bin/zmdc.pl start zmc -m $$monitor{Id}`;
|
||||
`/usr/bin/zmdc.pl start zma -m $$monitor{Id}`;
|
||||
if ($monitor->{Type} eq 'Local') {
|
||||
ZoneMinder::General::runCommand('zmdc.pl start zmc -d '.$monitor->{Device});
|
||||
} else {
|
||||
ZoneMinder::General::runCommand('zmdc.pl start zmc -m '.$monitor->{Id});
|
||||
}
|
||||
} # end if
|
||||
}
|
||||
} # end sub control
|
||||
|
@ -326,18 +343,30 @@ sub resumeMotionDetection {
|
|||
|
||||
sub Control {
|
||||
my $self = shift;
|
||||
if ( ! exists $$self{Control}) {
|
||||
require ZoneMinder::Control;
|
||||
my $Control = ZoneMinder::Control->find_one(Id=>$$self{ControlId});
|
||||
if ($Control) {
|
||||
require Module::Load::Conditional;
|
||||
if (!Module::Load::Conditional::can_load(modules => {'ZoneMinder::Control::'.$$Control{Protocol} => undef})) {
|
||||
Error("Can't load ZoneMinder::Control::$$Control{Protocol}\n$Module::Load::Conditional::ERROR");
|
||||
return undef;
|
||||
if (!exists $$self{Control}) {
|
||||
if ($$self{ControlId}) {
|
||||
require ZoneMinder::Control;
|
||||
my $Control = ZoneMinder::Control->find_one(Id=>$$self{ControlId});
|
||||
if ($Control) {
|
||||
my $Protocol = $$Control{Protocol};
|
||||
|
||||
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};
|
||||
$$Control{MonitorId} = $$self{Id};
|
||||
$$self{Control} = $Control;
|
||||
} else {
|
||||
Info("No ControlId set in monitor $$self{Id}")
|
||||
}
|
||||
}
|
||||
return $$self{Control};
|
||||
|
|
|
@ -639,9 +639,9 @@ $log->debug("Have array for $k $$search{$k}") if DEBUG_ALL;
|
|||
|
||||
if ( ! ( $db_field =~ /\?/ ) ) {
|
||||
if ( @{$$search{$k}} != 1 ) {
|
||||
push @where, $db_field .' IN ('.join(',', map {'?'} @{$$search{$k}} ) . ')';
|
||||
push @where, '`'.$db_field .'` IN ('.join(',', map {'?'} @{$$search{$k}} ) . ')';
|
||||
} else {
|
||||
push @where, $db_field.'=?';
|
||||
push @where, '`'.$db_field.'`=?';
|
||||
} # end if
|
||||
} else {
|
||||
$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}} ) {
|
||||
my $v = $$search{$k}{$p_k};
|
||||
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};
|
||||
} else {
|
||||
push @where, $db_field.'=?';
|
||||
push @where, '`'.$db_field.'`=?';
|
||||
push @values, $p_k, $v;
|
||||
} # end if
|
||||
} # 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';
|
||||
} else {
|
||||
if ( ! ( $db_field =~ /\?/ ) ) {
|
||||
push @where, $db_field .'=?';
|
||||
push @where, '`'.$db_field .'`=?';
|
||||
} else {
|
||||
push @where, $db_field;
|
||||
}
|
||||
|
|
|
@ -429,10 +429,20 @@ sub start {
|
|||
# It's not running, or at least it's not been started by us
|
||||
$process = { daemon=>$daemon, args=>\@args, command=>$command, keepalive=>!undef };
|
||||
} elsif ( $process->{pid} && $pid_hash{$process->{pid}} ) {
|
||||
dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' already running at "
|
||||
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}))
|
||||
.", pid = $process->{pid}\n"
|
||||
);
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -523,7 +533,7 @@ sub send_stop {
|
|||
."\n"
|
||||
);
|
||||
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
|
||||
return();
|
||||
return ();
|
||||
}
|
||||
|
||||
my $pid = $process->{pid};
|
||||
|
@ -586,7 +596,7 @@ sub check_for_processes_to_kill {
|
|||
|
||||
sub stop {
|
||||
my ( $daemon, @args ) = @_;
|
||||
my $command = join(' ', $daemon, @args );
|
||||
my $command = join(' ', $daemon, @args);
|
||||
my $process = $cmd_hash{$command};
|
||||
if ( !$process ) {
|
||||
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 bytes;
|
||||
|
||||
|
@ -160,10 +138,9 @@ $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
|
|||
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
|
||||
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
||||
|
||||
my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL};
|
||||
my $event_id = 0;
|
||||
|
||||
if ( !EVENT_PATH ) {
|
||||
if (!EVENT_PATH) {
|
||||
Error('No event path defined. Config was '.$Config{ZM_DIR_EVENTS});
|
||||
die;
|
||||
}
|
||||
|
@ -195,26 +172,37 @@ if ( ! ( $filter_name or $filter_id ) ) {
|
|||
my @filters;
|
||||
my $last_action = 0;
|
||||
|
||||
while( !$zm_terminate ) {
|
||||
while (!$zm_terminate) {
|
||||
my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL};
|
||||
my $now = time;
|
||||
if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) {
|
||||
if (($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY}) {
|
||||
Debug('Reloading filters');
|
||||
$last_action = $now;
|
||||
@filters = getFilters({ Name=>$filter_name, Id=>$filter_id });
|
||||
}
|
||||
|
||||
foreach my $filter ( @filters ) {
|
||||
foreach my $filter (@filters) {
|
||||
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 ( $id ) = $$filter{Id} =~ /(\d+)/;
|
||||
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 {
|
||||
checkFilter($filter);
|
||||
$$filter{last_ran} = $now;
|
||||
}
|
||||
}
|
||||
} # end foreach filter
|
||||
|
||||
last if (!$daemon and ($filter_name or $filter_id)) or $zm_terminate;
|
||||
|
||||
|
@ -384,11 +372,6 @@ sub checkFilter {
|
|||
} # end if AutoCopy
|
||||
|
||||
if ( $filter->{UpdateDiskSpace} ) {
|
||||
if ( $$filter{LockRows} ) {
|
||||
$ZoneMinder::Database::dbh->begin_work();
|
||||
$Event->lock_and_load();
|
||||
}
|
||||
|
||||
my $old_diskspace = $$Event{DiskSpace};
|
||||
my $new_diskspace = $Event->DiskSpace(undef);
|
||||
|
||||
|
@ -481,15 +464,18 @@ sub generateImage {
|
|||
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 $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 = '';
|
||||
|
||||
# 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 ) {
|
||||
Debug("Using analysis and jpeg exists $analyse_image_path");
|
||||
$image_path = $analyse_image_path;
|
||||
} elsif ( -r $capture_image_path ) {
|
||||
Debug("Using captures and jpeg exists $capture_image_path");
|
||||
$image_path = $capture_image_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'";
|
||||
#$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);
|
||||
|
@ -503,6 +489,8 @@ sub generateImage {
|
|||
} else {
|
||||
$image_path = $capture_image_path;
|
||||
}
|
||||
} else {
|
||||
Debug("No files found at $analyse_image_path, $capture_image_path or $video_path");
|
||||
}
|
||||
return $image_path;
|
||||
}
|
||||
|
@ -740,7 +728,7 @@ sub substituteTags {
|
|||
if ( -e $path ) {
|
||||
push @$attachments_ref, { type=>'image/jpeg', path=>$path };
|
||||
} 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 $Event = shift;
|
||||
|
||||
my $event_path = $Event->Path();
|
||||
|
||||
my $command = $filter->{AutoExecuteCmd}.' '.$event_path;
|
||||
my $command = $filter->{AutoExecuteCmd}.' '.$Event->Path();
|
||||
$command = substituteTags($command, $filter, $Event);
|
||||
|
||||
Info("Executing '$command'");
|
||||
|
@ -1063,15 +1049,37 @@ sub executeCommand {
|
|||
chomp($output);
|
||||
Debug("Output: $output");
|
||||
}
|
||||
if ( $status ) {
|
||||
if ($status) {
|
||||
Error("Command '$command' exited with status: $status");
|
||||
return 0;
|
||||
} else {
|
||||
my $sql = 'UPDATE `Events` SET `Executed` = 1 WHERE `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());
|
||||
ZoneMinder::Database::zmSQLExecute('UPDATE `Events` SET `Executed` = 1 WHERE `Id` = ?', $Event->{Id});
|
||||
}
|
||||
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 {
|
||||
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 $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
|
||||
my $arrayref = $sth->fetchall_arrayref({});
|
||||
|
|
|
@ -42,6 +42,7 @@ use constant SELECT_TIMEOUT => 0.25;
|
|||
|
||||
@EXTRA_PERL_LIB@
|
||||
use ZoneMinder;
|
||||
use ZoneMinder::Monitor;
|
||||
use ZoneMinder::Trigger::Channel::Inet;
|
||||
use ZoneMinder::Trigger::Channel::Unix;
|
||||
use ZoneMinder::Trigger::Channel::Serial;
|
||||
|
@ -166,13 +167,9 @@ while (!$zm_terminate) {
|
|||
foreach my $connection ( values(%spawned_connections) ) {
|
||||
if ( vec($rout, $connection->fileno(), 1) ) {
|
||||
Debug('Got input from spawned connection '
|
||||
.$connection->name()
|
||||
.' ('
|
||||
.$connection->fileno()
|
||||
.')'
|
||||
);
|
||||
.$connection->name().' ('.$connection->fileno().')');
|
||||
my $messages = $connection->getMessages();
|
||||
if ( defined($messages) ) {
|
||||
if (defined($messages)) {
|
||||
foreach my $message ( @$messages ) {
|
||||
handleMessage($connection, $message);
|
||||
}
|
||||
|
@ -199,34 +196,28 @@ while (!$zm_terminate) {
|
|||
# Check polled connections
|
||||
foreach my $connection ( @in_poll_connections ) {
|
||||
my $messages = $connection->getMessages();
|
||||
if ( defined($messages) ) {
|
||||
foreach my $message ( @$messages ) {
|
||||
handleMessage($connection, $message);
|
||||
}
|
||||
if (defined($messages)) {
|
||||
foreach my $message (@$messages) { handleMessage($connection, $message) };
|
||||
}
|
||||
}
|
||||
|
||||
# Check for alarms that might have happened
|
||||
my @out_messages;
|
||||
foreach my $monitor ( values %monitors ) {
|
||||
|
||||
if ( ! zmMemVerify($monitor) ) {
|
||||
if (!$monitor->connect()) {
|
||||
# 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.
|
||||
Debug("Failed connect, putting on reloads");
|
||||
push @needsReload, $monitor;
|
||||
next;
|
||||
}
|
||||
|
||||
my ( $state, $last_event ) = zmMemRead( $monitor,
|
||||
[
|
||||
my ($state, $last_event) = zmMemRead($monitor, [
|
||||
'shared_data:state',
|
||||
'shared_data:last_event'
|
||||
]
|
||||
);
|
||||
]);
|
||||
|
||||
#print( "$monitor->{Id}: S:$state, LE:$last_event" );
|
||||
#print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}" );
|
||||
if ( $state == STATE_ALARM or $state == STATE_ALERT ) {
|
||||
if ($state == STATE_ALARM or $state == STATE_ALERT) {
|
||||
# In alarm state
|
||||
if ( !defined($monitor->{LastEvent})
|
||||
or ($last_event != $monitor->{LastEvent})
|
||||
|
@ -255,6 +246,7 @@ while (!$zm_terminate) {
|
|||
}
|
||||
$monitor->{LastState} = $state;
|
||||
$monitor->{LastEvent} = $last_event;
|
||||
$monitor->disconnect();
|
||||
} # end foreach monitor
|
||||
|
||||
foreach my $connection ( @out_connections ) {
|
||||
|
@ -304,15 +296,22 @@ while (!$zm_terminate) {
|
|||
|
||||
# Reload all monitors from the dB every 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();
|
||||
@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
|
||||
} elsif ( @needsReload ) {
|
||||
foreach my $monitor ( @needsReload ) {
|
||||
loadMonitor($monitor);
|
||||
} elsif (@needsReload) {
|
||||
foreach my $monitor (@needsReload) {
|
||||
$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 = ();
|
||||
}
|
||||
|
@ -323,40 +322,21 @@ while (!$zm_terminate) {
|
|||
Info('Trigger daemon exiting');
|
||||
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 {
|
||||
$monitor_reload_time = time();
|
||||
|
||||
my %new_monitors = ();
|
||||
%monitors = ();
|
||||
|
||||
my $sql = 'SELECT * FROM `Monitors`
|
||||
WHERE find_in_set( `Function`, \'Modect,Mocord,Nodect,Record\' )'.
|
||||
( $Config{ZM_SERVER_ID} ? ' AND `ServerId`=?' : '' )
|
||||
;
|
||||
my $sth = $dbh->prepare_cached( $sql )
|
||||
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||
my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () )
|
||||
or Fatal( "Can't execute: ".$sth->errstr() );
|
||||
|
||||
while ( my $monitor = $sth->fetchrow_hashref() ) {
|
||||
if ( zmMemVerify($monitor) ) { # This will re-init shared memory
|
||||
foreach my $monitor ( ZoneMinder::Monitor->find(
|
||||
Function=>['Modect','Mocord','Nodect','Record'],
|
||||
($Config{ZM_SERVER_ID} ? (ServerId=>$Config{ZM_SERVER_ID}) : ()),
|
||||
)) {
|
||||
if ($monitor->connect()) { # This will re-init shared memory
|
||||
$monitor->{LastState} = zmGetMonitorState($monitor);
|
||||
$monitor->{LastEvent} = zmGetLastEvent($monitor);
|
||||
}
|
||||
$new_monitors{$monitor->{Id}} = $monitor;
|
||||
$monitors{$monitor->{Id}} = $monitor;
|
||||
} # end while fetchrow
|
||||
%monitors = %new_monitors;
|
||||
} # end sub loadMonitors
|
||||
|
||||
sub handleMessage {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#
|
||||
# ==========================================================================
|
||||
#
|
||||
# ZoneMinder Update Script, $Date$, $Revision$
|
||||
# ZoneMinder Update Script
|
||||
# Copyright (C) 2001-2008 Philip Coombes
|
||||
#
|
||||
# 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
|
||||
|
||||
This script just checks what the most recent release of ZoneMinder is
|
||||
at the the moment. It will eventually be responsible for applying and
|
||||
configuring upgrades etc, including on the fly upgrades.
|
||||
This script checks what the most recent release of ZoneMinder is
|
||||
at the the moment by downloading https://update.zoneminder.com/version.txt.
|
||||
It can also apply and configure upgrades etc, including on the fly upgrades.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
-c, --check - Check for updated versions of ZoneMinder
|
||||
-f, --freshen - Freshen the configuration in the database. Equivalent of old zmconfig.pl -noi
|
||||
--migrate-events - Update database structures as per USE_DEEP_STORAGE setting.
|
||||
-v <version>, --version=<version> - Force upgrade to the current version from <version>
|
||||
-u <dbuser>, --user=<dbuser> - Alternate DB user with privileges to alter DB
|
||||
-p <dbpass>, --pass=<dbpass> - Password of alternate DB user with privileges to alter DB
|
||||
-s, --super - Use system maintenance account on debian based systems instead of unprivileged account
|
||||
-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
|
||||
-c, --check - Check for updated versions of ZoneMinder.
|
||||
If not interactive zmupdate.pl will stay running, checking every hour.
|
||||
If interactive will try once, print out result and quit.
|
||||
-f, --freshen - Freshen the configuration in the database. Equivalent of old zmconfig.pl -noi
|
||||
--migrate-events - Update database structures as per USE_DEEP_STORAGE setting.
|
||||
-v <version>, --version=<version> - Force upgrade to the current version from <version>
|
||||
-u <dbuser>, --user=<dbuser> - Alternate DB user with privileges to alter DB
|
||||
-p <dbpass>, --pass=<dbpass> - Password of alternate DB user with privileges to alter DB
|
||||
-s, --super - Use system maintenance account on debian based systems instead of unprivileged account
|
||||
-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
|
||||
use strict;
|
||||
use warnings;
|
||||
use bytes;
|
||||
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 );
|
||||
logSetSignal();
|
||||
|
||||
my $interactive = 1;
|
||||
my $interactive = -t STDERR; # interactive if we have IO
|
||||
my $check = 0;
|
||||
my $freshen = 0;
|
||||
my $rename = 0;
|
||||
|
@ -122,9 +123,8 @@ GetOptions(
|
|||
) or pod2usage(-exitstatus => -1);
|
||||
|
||||
my $dbh = zmDbConnect(undef, { mysql_multi_statements=>1 } );
|
||||
if ( !$dbh ) {
|
||||
die "Unable to connect to db\n";
|
||||
}
|
||||
die "Unable to connect to db\n" if !$dbh;
|
||||
|
||||
$Config{ZM_DB_USER} = $dbUser;
|
||||
$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.
|
||||
|
@ -144,8 +144,10 @@ if ( ($check + $freshen + $rename + $zoneFix + $migrateEvents + ($version?1:0))
|
|||
pod2usage(-exitstatus => -1);
|
||||
}
|
||||
|
||||
if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) {
|
||||
print('Update agent starting at '.strftime('%y/%m/%d %H:%M:%S', localtime() )."\n");
|
||||
if ($check) {
|
||||
if (!$interactive) {
|
||||
Info('Update agent starting at '.strftime('%y/%m/%d %H:%M:%S', localtime() )."\n");
|
||||
}
|
||||
|
||||
my $currVersion = $Config{ZM_DYN_CURR_VERSION};
|
||||
my $lastVersion = $Config{ZM_DYN_LAST_VERSION};
|
||||
|
@ -153,16 +155,14 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) {
|
|||
|
||||
if ( !$currVersion ) {
|
||||
$currVersion = $Config{ZM_VERSION};
|
||||
|
||||
my $sql = "update Config set Value = ? where Name = 'ZM_DYN_CURR_VERSION'";
|
||||
my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr());
|
||||
my $res = $sth->execute($currVersion) or die("Can't execute: ".$sth->errstr());
|
||||
$sth->finish();
|
||||
zmDbDo("UPDATE `Config` SET `Value` = ? WHERE `Name` = 'ZM_DYN_CURR_VERSION'", $currVersion);
|
||||
}
|
||||
|
||||
while ( 1 ) {
|
||||
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');
|
||||
|
||||
use LWP::UserAgent;
|
||||
|
@ -175,21 +175,18 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) {
|
|||
my $res = $ua->request($req);
|
||||
|
||||
if ( $res->is_success ) {
|
||||
$lastVersion = $res->content;
|
||||
chomp($lastVersion);
|
||||
my $latestVersion = $res->content;
|
||||
chomp($latestVersion);
|
||||
$lastCheck = $now;
|
||||
|
||||
Info('Got version: '.$lastVersion);
|
||||
Info('Got version: '.$latestVersion);
|
||||
|
||||
my $lv_sql = 'UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_VERSION\'';
|
||||
my $lv_sth = $dbh->prepare_cached($lv_sql) or die("Can't prepare '$lv_sql': ".$dbh->errstr());
|
||||
my $lv_res = $lv_sth->execute($lastVersion) or die("Can't execute: ".$lv_sth->errstr());
|
||||
$lv_sth->finish();
|
||||
|
||||
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();
|
||||
zmDbDo('UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_VERSION\'', $latestVersion);
|
||||
zmDbDo('UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_CHECK\'', $lastCheck);
|
||||
if ($interactive) {
|
||||
print("Last version $lastVersion, Latest version $latestVersion, our version " . ZM_VERSION."\n");
|
||||
exit(0);
|
||||
}
|
||||
} else {
|
||||
Error('Error check failed: \''.$res->status_line().'\'');
|
||||
}
|
||||
|
@ -447,11 +444,6 @@ if ( $version ) {
|
|||
|
||||
print( "\nUpgrading database to version ".ZM_VERSION."\n" );
|
||||
|
||||
# Update config first of all
|
||||
migratePaths();
|
||||
ZoneMinder::Config::loadConfigFromDB();
|
||||
ZoneMinder::Config::saveConfigToDB();
|
||||
|
||||
my $cascade = undef;
|
||||
if ( $cascade || $version eq "1.19.0" ) {
|
||||
# Patch the database
|
||||
|
@ -1044,14 +1036,16 @@ sub patchDB {
|
|||
} # end sub patchDB
|
||||
|
||||
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 $sth = $dbh->prepare_cached($sql) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||
my $res = $sth->execute() or die("Can't execute: ".$sth->errstr());
|
||||
while( my $user = $sth->fetchrow_hashref() ) {
|
||||
while ( my $user = $sth->fetchrow_hashref() ) {
|
||||
my $scheme = substr($user->{Password}, 0, 1);
|
||||
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 $settings = '$2a$10$'.$salt;
|
||||
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@
|
||||
use ZoneMinder;
|
||||
use ZoneMinder::Storage;
|
||||
use ZoneMinder::Monitor;
|
||||
use POSIX;
|
||||
use DBI;
|
||||
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};
|
||||
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
||||
|
||||
logInit();
|
||||
my $log = logInit();
|
||||
logSetSignal();
|
||||
my $zm_terminate = 0;
|
||||
sub TermHandler {
|
||||
|
@ -80,91 +81,76 @@ Info('Watchdog starting, pausing for '.START_DELAY.' seconds');
|
|||
sleep(START_DELAY);
|
||||
|
||||
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 ( ! ( $dbh and $dbh->ping() ) ) {
|
||||
if ( ! ( $dbh = zmDbConnect() ) ) {
|
||||
while (!($dbh and $dbh->ping())) {
|
||||
if (!($dbh = zmDbConnect())) {
|
||||
sleep($Config{ZM_WATCH_CHECK_INTERVAL});
|
||||
}
|
||||
}
|
||||
|
||||
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() ) {
|
||||
foreach my $monitor (ZoneMinder::Monitor->find($Config{ZM_SERVER_ID} ? (ServerId=>$Config{ZM_SERVER_ID}) : ())) {
|
||||
next if $monitor->{Function} eq 'None';
|
||||
next if $monitor->{Type} eq 'WebSite';
|
||||
my $now = time();
|
||||
my $restart = 0;
|
||||
if (zmMemVerify($monitor)) {
|
||||
# Check we have got an image recently
|
||||
my $capture_time = zmGetLastWriteTime($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 {
|
||||
|
||||
zmMemInvalidate($monitor);
|
||||
if (!zmMemVerify($monitor)) {
|
||||
Info("Restarting capture daemon for $monitor->{Name}, shared data not valid");
|
||||
$restart = 1;
|
||||
$monitor->control('restart');
|
||||
next;
|
||||
}
|
||||
|
||||
if ($restart) {
|
||||
my $command;
|
||||
if ($monitor->{Type} eq 'Local') {
|
||||
$command = 'zmdc.pl restart zmc -d '.$monitor->{Device};
|
||||
} else {
|
||||
$command = 'zmdc.pl restart zmc -m '.$monitor->{Id};
|
||||
# Check we have got an image recently
|
||||
my $capture_time = zmGetLastWriteTime($monitor);
|
||||
if (!defined($capture_time)) {
|
||||
# Can't read from shared data
|
||||
Warning('LastWriteTime is not defined.');
|
||||
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);
|
||||
} elsif ($monitor->{Function} ne 'Monitor') {
|
||||
# Now check analysis daemon
|
||||
$restart = 0;
|
||||
next;
|
||||
}
|
||||
|
||||
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
|
||||
my $image_time = zmGetLastReadTime($monitor);
|
||||
if (!defined($image_time)) {
|
||||
# Can't read from shared data
|
||||
$restart = 1;
|
||||
# Can't read from shared data
|
||||
Error("Error reading shared data for $$monitor{Id} $$monitor{Name}");
|
||||
$monitor->control('restart');
|
||||
next;
|
||||
} elsif (!$image_time) {
|
||||
# We can't get the last capture time so can't be sure it's died.
|
||||
#$restart = 1;
|
||||
Error("Last analyse time for $$monitor{Id} $$monitor{Name} was zero.");
|
||||
Debug("Last analyse time for $$monitor{Id} $$monitor{Name} was zero.");
|
||||
} else {
|
||||
my $max_image_delay = ( $monitor->{MaxFPS}
|
||||
&&($monitor->{MaxFPS}>0)
|
||||
|
@ -175,26 +161,16 @@ while (!$zm_terminate) {
|
|||
my $image_delay = $now-$image_time;
|
||||
Debug("Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay");
|
||||
if ($image_delay > $max_image_delay) {
|
||||
Warning("Analysis daemon for $$monitor{Id} $$monitor{Name} needs restarting,"
|
||||
." time since last analysis $image_delay seconds ($now-$image_time)"
|
||||
);
|
||||
$restart = 1;
|
||||
$log->logPrint(ZoneMinder::Logger::WARNING+$monitor->Importance(),
|
||||
"daemon for $$monitor{Id} $$monitor{Name} needs restarting,"
|
||||
." time since last analysis $image_delay seconds ($now-$image_time)");
|
||||
$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
|
||||
# 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
|
||||
|
||||
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)
|
||||
set(ZM_BIN_SRC_FILES
|
||||
zm_analysis_thread.cpp
|
||||
zm_poll_thread.cpp
|
||||
zm_buffer.cpp
|
||||
zm_camera.cpp
|
||||
zm_comms.cpp
|
||||
|
@ -32,6 +33,9 @@ set(ZM_BIN_SRC_FILES
|
|||
zm_libvnc_camera.cpp
|
||||
zm_local_camera.cpp
|
||||
zm_monitor.cpp
|
||||
zm_monitor_monitorlink.cpp
|
||||
zm_monitor_janus.cpp
|
||||
zm_monitor_amcrest.cpp
|
||||
zm_monitorstream.cpp
|
||||
zm_ffmpeg.cpp
|
||||
zm_ffmpeg_camera.cpp
|
||||
|
@ -60,12 +64,64 @@ set(ZM_BIN_SRC_FILES
|
|||
zm_signal.cpp
|
||||
zm_stream.cpp
|
||||
zm_swscale.cpp
|
||||
zm_time.cpp
|
||||
zm_user.cpp
|
||||
zm_utils.cpp
|
||||
zm_videostore.cpp
|
||||
zm_zone.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.
|
||||
add_library(zm STATIC ${ZM_BIN_SRC_FILES})
|
||||
|
||||
|
@ -74,6 +130,15 @@ target_include_directories(zm
|
|||
${CMAKE_BINARY_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
|
||||
PUBLIC
|
||||
FFMPEG::avcodec
|
||||
|
@ -88,6 +153,15 @@ target_link_libraries(zm
|
|||
PRIVATE
|
||||
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")
|
||||
target_link_libraries(zm
|
||||
PUBLIC
|
||||
|
@ -109,6 +183,11 @@ add_executable(zms zms.cpp)
|
|||
add_executable(zmu zmu.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
|
||||
PRIVATE
|
||||
zm-core-interface
|
||||
|
|
|
@ -23,6 +23,14 @@ void AnalysisThread::Start() {
|
|||
|
||||
void AnalysisThread::Run() {
|
||||
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) {
|
||||
// Make sure there is enough space
|
||||
this->expand(bytes);
|
||||
Debug(3, "Reading %u btes", bytes);
|
||||
Debug(3, "Reading %u bytes", bytes);
|
||||
int bytes_read = ::read(sd, mTail, bytes);
|
||||
if (bytes_read > 0) {
|
||||
mTail += bytes_read;
|
||||
|
|
|
@ -245,6 +245,7 @@ class Socket : public CommsBase {
|
|||
}
|
||||
|
||||
virtual ssize_t recv(std::string &msg) const {
|
||||
msg.reserve(ZM_NETWORK_BUFSIZ);
|
||||
std::vector<char> buffer(msg.capacity());
|
||||
ssize_t nBytes;
|
||||
if ((nBytes = ::recv(mSd, buffer.data(), buffer.size(), 0)) < 0) {
|
||||
|
|
|
@ -108,8 +108,8 @@ bool zmDbConnect() {
|
|||
}
|
||||
|
||||
void zmDbClose() {
|
||||
std::lock_guard<std::mutex> lck(db_mutex);
|
||||
if (zmDbConnected) {
|
||||
std::lock_guard<std::mutex> lck(db_mutex);
|
||||
mysql_close(&dbconn);
|
||||
// mysql_init() call implicitly mysql_library_init() but
|
||||
// mysql_close() does not call mysql_library_end()
|
||||
|
@ -238,8 +238,13 @@ zmDbQueue::~zmDbQueue() {
|
|||
}
|
||||
|
||||
void zmDbQueue::stop() {
|
||||
mTerminate = true;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mMutex);
|
||||
|
||||
mTerminate = true;
|
||||
}
|
||||
mCondition.notify_all();
|
||||
|
||||
if (mThread.joinable()) mThread.join();
|
||||
}
|
||||
|
||||
|
@ -251,11 +256,11 @@ void zmDbQueue::process() {
|
|||
mCondition.wait(lock);
|
||||
}
|
||||
while (!mQueue.empty()) {
|
||||
if (mQueue.size() > 10) {
|
||||
if (mQueue.size() > 30) {
|
||||
Logger *log = Logger::fetch();
|
||||
Logger::Level db_level = log->databaseLevel();
|
||||
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);
|
||||
}
|
||||
std::string sql = mQueue.front();
|
||||
|
@ -271,8 +276,10 @@ void zmDbQueue::process() {
|
|||
|
||||
void zmDbQueue::push(std::string &&sql) {
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
256
src/zm_event.cpp
256
src/zm_event.cpp
|
@ -55,7 +55,7 @@ Event::Event(
|
|||
alarm_frames(0),
|
||||
alarm_frame_written(false),
|
||||
tot_score(0),
|
||||
max_score(0),
|
||||
max_score(-1),
|
||||
//path(""),
|
||||
//snapshit_file(),
|
||||
//alarm_file(""),
|
||||
|
@ -65,7 +65,8 @@ Event::Event(
|
|||
last_db_frame(0),
|
||||
have_video_keyframe(false),
|
||||
//scheme
|
||||
save_jpegs(0)
|
||||
save_jpegs(0),
|
||||
terminate_(false)
|
||||
{
|
||||
std::string notes;
|
||||
createNotes(notes);
|
||||
|
@ -133,98 +134,22 @@ Event::Event(
|
|||
);
|
||||
id = zmDbDoInsert(sql);
|
||||
|
||||
if (!SetPath(storage)) {
|
||||
// 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;
|
||||
thread_ = std::thread(&Event::Run, this);
|
||||
}
|
||||
|
||||
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 */
|
||||
// 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) {
|
||||
Debug(4, "Deleting video store");
|
||||
delete videoStore;
|
||||
|
@ -291,6 +216,10 @@ 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 {
|
||||
int thisquality =
|
||||
(alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality)) ?
|
||||
|
@ -372,7 +301,13 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) {
|
|||
} // end if update
|
||||
} // 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 ||
|
||||
( ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) &&
|
||||
( 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);
|
||||
// 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.
|
||||
Debug(1, "Writing snapshot");
|
||||
Debug(1, "Writing snapshot to %s", snapshot_file.c_str());
|
||||
WriteFrameImage(image, timestamp, snapshot_file.c_str());
|
||||
} 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
|
||||
|
@ -500,17 +435,19 @@ void Event::AddFrame(Image *image,
|
|||
if (!alarm_frame_written) {
|
||||
write_to_db = true; // OD processing will need it, so the db needs to know about it
|
||||
alarm_frame_written = true;
|
||||
Debug(1, "Writing alarm image");
|
||||
WriteFrameImage(image, timestamp, alarm_file.c_str());
|
||||
Debug(1, "Writing alarm image to %s", 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 {
|
||||
Debug(3, "Not Writing alarm image because alarm frame already written");
|
||||
}
|
||||
|
||||
if (alarm_image and (save_jpegs & 2)) {
|
||||
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)) {
|
||||
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
|
||||
|
@ -523,11 +460,15 @@ void Event::AddFrame(Image *image,
|
|||
bool db_frame = ( frame_type == BULK )
|
||||
or ( frame_type == ALARM )
|
||||
or ( frames == 1 )
|
||||
or ( score > (int)max_score )
|
||||
or ( score > max_score )
|
||||
or ( monitor_state == Monitor::ALERT )
|
||||
or ( monitor_state == Monitor::ALARM )
|
||||
or ( monitor_state == Monitor::PREALARM );
|
||||
|
||||
if (score > max_score) {
|
||||
max_score = score;
|
||||
}
|
||||
|
||||
if (db_frame) {
|
||||
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",
|
||||
|
@ -546,7 +487,7 @@ void Event::AddFrame(Image *image,
|
|||
or
|
||||
(frame_type == BULK)
|
||||
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)",
|
||||
frame_data.size(), write_to_db, fps, (frame_type == BULK));
|
||||
WriteDbFrames();
|
||||
|
@ -568,9 +509,6 @@ void Event::AddFrame(Image *image,
|
|||
} // end if frame_type == BULK
|
||||
} // end if db_frame
|
||||
|
||||
if (score > (int) max_score) {
|
||||
max_score = score;
|
||||
}
|
||||
end_time = timestamp;
|
||||
}
|
||||
|
||||
|
@ -650,3 +588,117 @@ bool Event::SetPath(Storage *storage) {
|
|||
} // deep storage or not
|
||||
return true;
|
||||
} // 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_define.h"
|
||||
#include "zm_packet.h"
|
||||
#include "zm_storage.h"
|
||||
#include "zm_time.h"
|
||||
#include "zm_utils.h"
|
||||
#include "zm_zone.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
|
||||
|
||||
class EventStream;
|
||||
class Frame;
|
||||
|
@ -77,8 +84,8 @@ class Event {
|
|||
int frames;
|
||||
int alarm_frames;
|
||||
bool alarm_frame_written;
|
||||
unsigned int tot_score;
|
||||
unsigned int max_score;
|
||||
int tot_score;
|
||||
int max_score;
|
||||
std::string path;
|
||||
std::string snapshot_file;
|
||||
std::string alarm_file;
|
||||
|
@ -98,6 +105,15 @@ class Event {
|
|||
|
||||
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:
|
||||
static bool OpenFrameSocket(int);
|
||||
static bool ValidateFrameSocket(int);
|
||||
|
@ -110,41 +126,50 @@ class Event {
|
|||
|
||||
uint64_t Id() const { return id; }
|
||||
const std::string &Cause() const { return cause; }
|
||||
void addNote(const char *cause, const std::string ¬e);
|
||||
int Frames() const { return frames; }
|
||||
int AlarmFrames() const { return alarm_frames; }
|
||||
|
||||
SystemTimePoint StartTime() const { return start_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 SendFrameImage(const Image *image, bool alarm_frame=false);
|
||||
bool WriteFrameImage(Image *image, SystemTimePoint timestamp, const char *event_file, bool alarm_frame = false) const;
|
||||
|
||||
void updateNotes(const StringSetMap &stringSetMap);
|
||||
|
||||
void AddFrame(Image *image,
|
||||
void AddFrame(Image *image,
|
||||
SystemTimePoint timestamp,
|
||||
const std::vector<ZoneStats> &stats,
|
||||
int score = 0,
|
||||
Image *alarm_image = nullptr);
|
||||
|
||||
void Stop() {
|
||||
terminate_ = true;
|
||||
packet_queue_condition.notify_all();
|
||||
}
|
||||
bool Stopped() const { return terminate_; }
|
||||
|
||||
private:
|
||||
void WriteDbFrames();
|
||||
bool SetPath(Storage *storage);
|
||||
|
||||
public:
|
||||
static std::string getSubPath(tm time) {
|
||||
std::string subpath = stringtf("%02d/%02d/%02d/%02d/%02d/%02d",
|
||||
time.tm_year - 100, time.tm_mon + 1, time.tm_mday,
|
||||
time.tm_hour, time.tm_min, time.tm_sec);
|
||||
return subpath;
|
||||
}
|
||||
static std::string getSubPath(time_t *time) {
|
||||
tm time_tm = {};
|
||||
localtime_r(time, &time_tm);
|
||||
return Event::getSubPath(time_tm);
|
||||
}
|
||||
static std::string getSubPath(tm time) {
|
||||
std::string subpath = stringtf("%02d/%02d/%02d/%02d/%02d/%02d",
|
||||
time.tm_year - 100, time.tm_mon + 1, time.tm_mday,
|
||||
time.tm_hour, time.tm_min, time.tm_sec);
|
||||
return subpath;
|
||||
}
|
||||
static std::string getSubPath(time_t *time) {
|
||||
tm time_tm = {};
|
||||
localtime_r(time, &time_tm);
|
||||
return Event::getSubPath(time_tm);
|
||||
}
|
||||
|
||||
const char* getEventFile() const {
|
||||
return video_file.c_str();
|
||||
|
@ -154,18 +179,6 @@ class Event {
|
|||
return pre_alarm_count;
|
||||
}
|
||||
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;
|
||||
}
|
||||
static void AddPreAlarmFrame(
|
||||
|
@ -174,28 +187,11 @@ class Event {
|
|||
int score=0,
|
||||
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++;
|
||||
}
|
||||
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();
|
||||
}
|
||||
int MonitorId();
|
||||
};
|
||||
|
||||
#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
|
||||
if (event_time >= event_data->start_time) {
|
||||
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 );
|
||||
if (event_data->frames[i].timestamp >= event_time) {
|
||||
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(),
|
||||
curr_frame_id);
|
||||
break;
|
||||
}
|
||||
} // end foreach frame
|
||||
Debug(3, "Skipping %ld frames", event_data->frame_count);
|
||||
Debug(3, "Skipping %d frames", event_data->frame_count);
|
||||
} else {
|
||||
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()),
|
||||
|
@ -90,13 +90,13 @@ bool EventStream::loadInitialEventData(int monitor_id, SystemTimePoint event_tim
|
|||
|
||||
bool EventStream::loadInitialEventData(
|
||||
uint64_t init_event_id,
|
||||
unsigned int init_frame_id
|
||||
int init_frame_id
|
||||
) {
|
||||
loadEventData(init_event_id);
|
||||
|
||||
if ( init_frame_id ) {
|
||||
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_frame_id = 1;
|
||||
} else {
|
||||
|
@ -225,20 +225,24 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
|
||||
sql = stringtf("SELECT `FrameId`, unix_timestamp(`TimeStamp`), `Delta` "
|
||||
"FROM `Frames` WHERE `EventId` = %" PRIu64 " ORDER BY `FrameId` ASC", event_id);
|
||||
|
||||
result = zmDbFetch(sql);
|
||||
if (!result) {
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
event_data->n_frames = mysql_num_rows(result);
|
||||
|
||||
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;
|
||||
SystemTimePoint last_timestamp = event_data->start_time;
|
||||
Microseconds last_delta = Seconds(0);
|
||||
|
||||
while ( ( dbrow = mysql_fetch_row(result) ) ) {
|
||||
while ((dbrow = mysql_fetch_row(result))) {
|
||||
int id = atoi(dbrow[0]);
|
||||
//timestamp = atof(dbrow[1]);
|
||||
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
|
||||
event_data->last_frame_id = last_id;
|
||||
|
||||
if ( mysql_errno(&dbconn) ) {
|
||||
if (mysql_errno(&dbconn)) {
|
||||
Error("Can't fetch row: %s", mysql_error(&dbconn));
|
||||
exit(mysql_errno(&dbconn));
|
||||
}
|
||||
|
@ -310,7 +314,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
else
|
||||
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->frame_count,
|
||||
event_data->last_frame_id,
|
||||
|
@ -342,12 +346,12 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
|||
if (
|
||||
(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");
|
||||
curr_frame_id = 1;
|
||||
} 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(),
|
||||
curr_frame_id,
|
||||
event_data->frame_count,
|
||||
|
@ -404,9 +408,9 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
|||
paused = true;
|
||||
replay_rate = ZM_RATE_BASE;
|
||||
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;
|
||||
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;
|
||||
case CMD_SLOWREV :
|
||||
paused = true;
|
||||
|
@ -414,7 +418,7 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
|||
step = -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;
|
||||
case CMD_FASTREV :
|
||||
Debug(1, "Got FAST REV command");
|
||||
|
@ -538,12 +542,12 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
|||
|
||||
if ( 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_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(),
|
||||
curr_frame_id,
|
||||
FPSeconds(event_data->frames[curr_frame_id - 1].offset).count());
|
||||
|
@ -615,11 +619,11 @@ bool EventStream::checkEventLoaded() {
|
|||
sql = stringtf(
|
||||
"SELECT `Id` FROM `Events` WHERE `MonitorId` = %d AND `Id` < %" PRIu64 " ORDER BY `Id` DESC LIMIT 1",
|
||||
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)) {
|
||||
// We are viewing an in-process event, so just reload it.
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
|
@ -628,7 +632,7 @@ bool EventStream::checkEventLoaded() {
|
|||
event_data->monitor_id, event_data->event_id);
|
||||
} else {
|
||||
// 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);
|
||||
return false;
|
||||
}
|
||||
|
@ -662,8 +666,8 @@ bool EventStream::checkEventLoaded() {
|
|||
curr_frame_id = event_data->last_frame_id;
|
||||
else
|
||||
curr_frame_id = 1;
|
||||
Debug(2, "New frame id = %ld", curr_frame_id);
|
||||
start = std::chrono::system_clock::now();
|
||||
Debug(2, "New frame id = %d", curr_frame_id);
|
||||
start = std::chrono::steady_clock::now();
|
||||
return true;
|
||||
} else {
|
||||
Debug(2, "No next event loaded using %s. Pausing", sql.c_str());
|
||||
|
@ -689,22 +693,20 @@ bool EventStream::checkEventLoaded() {
|
|||
|
||||
Image * EventStream::getImage( ) {
|
||||
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());
|
||||
return image;
|
||||
}
|
||||
|
||||
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;
|
||||
struct stat filestat = {};
|
||||
|
||||
// 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 (event_data->SaveJPEGs & 1) {
|
||||
filepath = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
||||
} else if (event_data->SaveJPEGs & 2) {
|
||||
if ((frame_type == FRAME_ANALYSIS) && (event_data->SaveJPEGs & 2)) {
|
||||
filepath = stringtf(staticConfig.analyse_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
||||
if (stat(filepath.c_str(), &filestat) < 0) {
|
||||
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 = "";
|
||||
}
|
||||
}
|
||||
} 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");
|
||||
return false;
|
||||
}
|
||||
|
@ -795,7 +799,13 @@ bool EventStream::sendFrame(Microseconds delta_us) {
|
|||
}
|
||||
|
||||
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;
|
||||
uint8_t *img_buffer = temp_img_buffer;
|
||||
|
||||
|
@ -837,12 +847,13 @@ void EventStream::runStream() {
|
|||
|
||||
//checkInitialised();
|
||||
|
||||
if ( type == STREAM_JPEG )
|
||||
if (type == STREAM_JPEG)
|
||||
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");
|
||||
exit(0);
|
||||
zm_terminate = true;
|
||||
return;
|
||||
}
|
||||
|
||||
double fps = 1.0;
|
||||
|
@ -851,13 +862,13 @@ void EventStream::runStream() {
|
|||
}
|
||||
updateFrameRate(fps);
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
start = std::chrono::steady_clock::now();
|
||||
|
||||
SystemTimePoint::duration last_frame_offset = Seconds(0);
|
||||
SystemTimePoint::duration time_to_event = Seconds(0);
|
||||
|
||||
while ( !zm_terminate ) {
|
||||
now = std::chrono::system_clock::now();
|
||||
now = std::chrono::steady_clock::now();
|
||||
|
||||
Microseconds delta = Microseconds(0);
|
||||
send_frame = false;
|
||||
|
@ -880,7 +891,7 @@ void EventStream::runStream() {
|
|||
|
||||
if ( !paused ) {
|
||||
// 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
|
||||
// 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.
|
||||
|
@ -904,7 +915,7 @@ void EventStream::runStream() {
|
|||
|
||||
// time_to_event > 0 means that we are not in the event
|
||||
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());
|
||||
if (time_since_last_send > Seconds(1)) {
|
||||
char frame_text[64];
|
||||
|
@ -976,13 +987,13 @@ void EventStream::runStream() {
|
|||
// +/- 1? What if we are skipping frames?
|
||||
curr_frame_id += (replay_rate>0) ? frame_mod : -1*frame_mod;
|
||||
// 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
|
||||
if ( (mode == MODE_SINGLE) && (
|
||||
(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");
|
||||
|
@ -990,45 +1001,51 @@ void EventStream::runStream() {
|
|||
// Have to reset start to now when replaying
|
||||
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
|
||||
// but what if we are skipping frames? We need the distance from the last frame sent
|
||||
// Also, what about reverse? needs to be absolute value
|
||||
if (curr_frame_id <= event_data->frame_count) {
|
||||
frame_data = &event_data->frames[curr_frame_id-1];
|
||||
|
||||
// There are two ways to go about this, not sure which is correct.
|
||||
// you can calculate the relationship between now and the start
|
||||
// or calc the relationship from the last frame. I think from the start is better as it self-corrects
|
||||
//
|
||||
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));
|
||||
// frame_data->delta is the time since last frame as a float in seconds
|
||||
// but what if we are skipping frames? We need the distance from the last frame sent
|
||||
// Also, what about reverse? needs to be absolute value
|
||||
|
||||
Debug(2, "New delta: now - start = %" PRIu64 " us offset %" PRIi64 " us- elapsed = %" PRIu64 " us",
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(now - start).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(frame_data->offset).count()),
|
||||
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;
|
||||
// There are two ways to go about this, not sure which is correct.
|
||||
// you can calculate the relationship between now and the start
|
||||
// or calc the relationship from the last frame. I think from the start is better as it self-corrects
|
||||
//
|
||||
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));
|
||||
|
||||
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",
|
||||
Debug(2, "New delta: now - start = %" PRIu64 " us offset %" PRIi64 " us- elapsed = %" PRIu64 " us",
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(now - start).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(frame_data->offset).count()),
|
||||
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<Microseconds>(delta).count()));
|
||||
delta = MAX_SLEEP;
|
||||
}
|
||||
delta = MAX_SLEEP;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(delta);
|
||||
Debug(3, "Done sleeping: %" PRIi64 " us",
|
||||
std::this_thread::sleep_for(delta);
|
||||
Debug(3, "Done sleeping: %" PRIi64 " us",
|
||||
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 {
|
||||
// Paused
|
||||
delta = std::chrono::duration_cast<Microseconds>(FPSeconds(
|
||||
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()
|
||||
|
||||
bool EventStream::send_file(const std::string &filepath) {
|
||||
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
|
||||
|
||||
int img_buffer_size = 0;
|
||||
uint8_t *img_buffer = temp_img_buffer;
|
||||
|
||||
FILE *fdj = nullptr;
|
||||
fdj = fopen(filepath.c_str(), "rb");
|
||||
if ( !fdj ) {
|
||||
FILE *fdj = fopen(filepath.c_str(), "rb");
|
||||
if (!fdj) {
|
||||
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));
|
||||
return sendTextFrame(error_message.c_str());
|
||||
}
|
||||
#if HAVE_SENDFILE
|
||||
static struct stat filestat;
|
||||
if ( fstat(fileno(fdj), &filestat) < 0 ) {
|
||||
if (fstat(fileno(fdj), &filestat) < 0) {
|
||||
fclose(fdj); /* Close the file handle */
|
||||
Error("Failed getting information about file %s: %s", filepath.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if ( !filestat.st_size ) {
|
||||
if (!filestat.st_size) {
|
||||
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;
|
||||
}
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
int rc = zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size);
|
||||
if ( rc == (int)filestat.st_size ) {
|
||||
ssize_t remaining = 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
|
||||
fclose(fdj); /* Close the file handle */
|
||||
return true;
|
||||
}
|
||||
Warning("Unable to send raw frame %ld: %s rc %d", curr_frame_id, strerror(errno), rc);
|
||||
#endif
|
||||
img_buffer_size = fread(img_buffer, 1, sizeof(temp_img_buffer), fdj);
|
||||
fclose(fdj); /* Close the file handle */
|
||||
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);
|
||||
}
|
||||
Warning("Unable to send raw frame %d: %s %zu remaining",
|
||||
curr_frame_id, strerror(errno), remaining);
|
||||
return false;
|
||||
} // end bool EventStream::send_file(const std::string &filepath)
|
||||
|
||||
bool EventStream::send_buffer(uint8_t* buffer, int 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;
|
||||
}
|
||||
int rc = fwrite(buffer, size, 1, stdout);
|
||||
|
||||
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 true;
|
||||
|
@ -1150,7 +1162,7 @@ bool EventStream::send_buffer(uint8_t* buffer, int size) {
|
|||
|
||||
void EventStream::setStreamStart(
|
||||
uint64_t init_event_id,
|
||||
unsigned int init_frame_id=0) {
|
||||
int init_frame_id=0) {
|
||||
loadInitialEventData(init_event_id, init_frame_id);
|
||||
} // end void EventStream::setStreamStart(init_event_id,init_frame_id=0)
|
||||
|
||||
|
|
|
@ -49,9 +49,9 @@ class EventStream : public StreamBase {
|
|||
struct EventData {
|
||||
uint64_t event_id;
|
||||
unsigned int monitor_id;
|
||||
unsigned long storage_id;
|
||||
unsigned long 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
|
||||
unsigned int storage_id;
|
||||
int frame_count; // Value of Frames column in Event
|
||||
int last_frame_id; // Highest frame id known about. Can be < frame_count in incomplete events
|
||||
SystemTimePoint start_time;
|
||||
SystemTimePoint end_time;
|
||||
Microseconds duration;
|
||||
|
@ -73,16 +73,16 @@ class EventStream : public StreamBase {
|
|||
StreamMode mode;
|
||||
bool forceEventChange;
|
||||
|
||||
long curr_frame_id;
|
||||
int curr_frame_id;
|
||||
SystemTimePoint curr_stream_time;
|
||||
bool send_frame;
|
||||
SystemTimePoint start; // clock time when started the event
|
||||
TimePoint start; // clock time when started the event
|
||||
|
||||
EventData *event_data;
|
||||
|
||||
protected:
|
||||
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 checkEventLoaded();
|
||||
|
@ -118,7 +118,7 @@ class EventStream : public StreamBase {
|
|||
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 setStreamMode(StreamMode p_mode) { mode = p_mode; }
|
||||
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);
|
||||
if (lang)
|
||||
Debug(1, "language (%s)", lang->value);
|
||||
Debug(1, "frames:%d, frame_size:%d stream timebase: %d/%d",
|
||||
st->codec_info_nb_frames, codec->frame_size,
|
||||
Debug(1, "frame_size:%d stream timebase: %d/%d",
|
||||
codec->frame_size,
|
||||
st->time_base.num, st->time_base.den
|
||||
);
|
||||
|
||||
|
|
|
@ -293,17 +293,16 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
mFormatContext->interrupt_callback.opaque = this;
|
||||
|
||||
ret = avformat_open_input(&mFormatContext, mPath.c_str(), nullptr, &opts);
|
||||
if ( ret != 0 )
|
||||
{
|
||||
Error("Unable to open input %s due to: %s", mPath.c_str(),
|
||||
if (ret != 0) {
|
||||
logPrintf(Logger::ERROR + monitor->Importance(),
|
||||
"Unable to open input %s due to: %s", mPath.c_str(),
|
||||
av_make_error_string(ret).c_str());
|
||||
|
||||
if ( mFormatContext ) {
|
||||
if (mFormatContext) {
|
||||
avformat_close_input(&mFormatContext);
|
||||
mFormatContext = nullptr;
|
||||
}
|
||||
av_dict_free(&opts);
|
||||
|
||||
return -1;
|
||||
}
|
||||
AVDictionaryEntry *e = nullptr;
|
||||
|
@ -458,6 +457,17 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
#endif
|
||||
} // 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);
|
||||
|
||||
e = nullptr;
|
||||
|
|
|
@ -31,8 +31,7 @@ int FFmpeg_Input::Open(
|
|||
const AVStream * audio_in_stream,
|
||||
const AVCodecContext * audio_in_ctx
|
||||
) {
|
||||
video_stream_id = video_in_stream->index;
|
||||
int max_stream_index = video_in_stream->index;
|
||||
int max_stream_index = video_stream_id = video_in_stream->index;
|
||||
|
||||
if ( audio_in_stream ) {
|
||||
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) {
|
||||
now = std::chrono::system_clock::now();
|
||||
now = std::chrono::steady_clock::now();
|
||||
checkCommandQueue();
|
||||
|
||||
if (stream_type == MJPEG) {
|
||||
|
|
|
@ -28,7 +28,6 @@ class FifoStream : public StreamBase {
|
|||
std::string stream_path;
|
||||
int total_read;
|
||||
int bytes_read;
|
||||
unsigned int frame_count;
|
||||
|
||||
protected:
|
||||
typedef enum { UNKNOWN, MJPEG, RAW } StreamType;
|
||||
|
@ -39,9 +38,9 @@ class FifoStream : public StreamBase {
|
|||
|
||||
public:
|
||||
FifoStream() :
|
||||
StreamBase(),
|
||||
total_read(0),
|
||||
bytes_read(0),
|
||||
frame_count(0),
|
||||
stream_type(UNKNOWN)
|
||||
{}
|
||||
|
||||
|
|
|
@ -46,9 +46,9 @@ FileCamera::FileCamera(
|
|||
p_hue,
|
||||
p_colour,
|
||||
p_capture,
|
||||
p_record_audio)
|
||||
p_record_audio),
|
||||
path(p_path)
|
||||
{
|
||||
path = std::string(p_path);
|
||||
if (capture) {
|
||||
Initialise();
|
||||
}
|
||||
|
|
116
src/zm_image.cpp
116
src/zm_image.cpp
|
@ -24,6 +24,7 @@
|
|||
#include "zm_utils.h"
|
||||
#include <algorithm>
|
||||
#include <fcntl.h>
|
||||
#include <mutex>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
@ -80,9 +81,14 @@ imgbufcpy_fptr_t fptr_imgbufcpy;
|
|||
/* Font */
|
||||
static ZmFont font;
|
||||
|
||||
std::mutex jpeg_mutex;
|
||||
|
||||
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 */
|
||||
if ( pixels % 16 || pixels % 12 ) {
|
||||
/* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements
|
||||
* previous tests were %16 or %12 but that is incorrect. Should just be %4
|
||||
*/
|
||||
|
||||
if (pixels %4) {
|
||||
// have to use non-loop unrolled functions
|
||||
delta8_rgb = &std_delta8_rgb;
|
||||
delta8_bgr = &std_delta8_bgr;
|
||||
|
@ -92,6 +98,7 @@ void Image::update_function_pointers() {
|
|||
delta8_abgr = &std_delta8_abgr;
|
||||
delta8_gray8 = &std_delta8_gray8;
|
||||
blend = &std_blend;
|
||||
Debug(1, "Using slow std functions because pixels %d mod 4=%d", pixels, pixels%4);
|
||||
} else {
|
||||
// Use either sse or neon, or loop unrolled version
|
||||
delta8_rgb = fptr_delta8_rgb;
|
||||
|
@ -114,22 +121,23 @@ Image::Image() :
|
|||
delta8_argb(&std_delta8_argb),
|
||||
delta8_abgr(&std_delta8_abgr),
|
||||
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();
|
||||
width = 0;
|
||||
linesize = 0;
|
||||
height = 0;
|
||||
padding = 0;
|
||||
pixels = 0;
|
||||
colours = 0;
|
||||
subpixelorder = 0;
|
||||
size = 0;
|
||||
allocation = 0;
|
||||
buffer = 0;
|
||||
buffertype = ZM_BUFTYPE_DONTFREE;
|
||||
holdbuffer = 0;
|
||||
// Update blend to fast function determined by Initialise, I'm sure this can be improve.
|
||||
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),
|
||||
padding(p_padding),
|
||||
subpixelorder(p_subpixelorder),
|
||||
buffer(p_buffer) {
|
||||
buffer(p_buffer),
|
||||
holdbuffer(0)
|
||||
{
|
||||
|
||||
if (!initialised)
|
||||
Initialise();
|
||||
pixels = width * height;
|
||||
linesize = p_width * p_colours;
|
||||
size = linesize * height + padding;
|
||||
buffer = nullptr;
|
||||
holdbuffer = 0;
|
||||
if (p_buffer) {
|
||||
allocation = size;
|
||||
buffertype = ZM_BUFTYPE_DONTFREE;
|
||||
|
@ -174,7 +182,7 @@ Image::Image(int p_width, int p_height, int p_colours, int p_subpixelorder, uint
|
|||
} else {
|
||||
AllocImgBuffer(size);
|
||||
}
|
||||
if (!subpixelorder and colours>1) {
|
||||
if (!subpixelorder and (colours>1)) {
|
||||
// Default to RGBA when no subpixelorder is specified.
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
height = frame->height;
|
||||
pixels = width*height;
|
||||
|
||||
zm_dump_video_frame(frame, "Image.Assign(frame)");
|
||||
// FIXME
|
||||
colours = ZM_COLOUR_RGB32;
|
||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||
imagePixFormat = AV_PIX_FMT_RGBA;
|
||||
//(AVPixelFormat)frame->format;
|
||||
//(AVPixelFormat)frame->format;
|
||||
|
||||
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.
|
||||
linesize = FFALIGN(av_image_get_linesize(AV_PIX_FMT_RGBA, width, 0), 32);
|
||||
padding = 0;
|
||||
|
||||
buffer = nullptr;
|
||||
holdbuffer = 0;
|
||||
AllocImgBuffer(size);
|
||||
this->Assign(frame);
|
||||
}
|
||||
|
@ -1081,11 +1090,16 @@ bool Image::WriteJpeg(const std::string &filename,
|
|||
const int &quality_override,
|
||||
SystemTimePoint timestamp,
|
||||
bool on_blocking_abort) const {
|
||||
|
||||
if (config.colour_jpeg_files && (colours == ZM_COLOUR_GRAY8)) {
|
||||
Image temp_image(*this);
|
||||
temp_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB);
|
||||
return temp_image.WriteJpeg(filename, quality_override, 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;
|
||||
|
||||
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) {
|
||||
cinfo->in_color_space = JCS_EXT_XBGR;
|
||||
} else {
|
||||
Warning("Unknwon subpixelorder %d", subpixelorder);
|
||||
Warning("Unknown subpixelorder %d", subpixelorder);
|
||||
/* Assume RGBA */
|
||||
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);
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lck(jpeg_mutex);
|
||||
|
||||
int quality = quality_override ? quality_override : config.jpeg_stream_quality;
|
||||
|
||||
struct jpeg_compress_struct *cinfo = encodejpg_ccinfo[quality];
|
||||
|
@ -1677,15 +1693,15 @@ void Image::Overlay( const Image &image ) {
|
|||
}
|
||||
|
||||
/* 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) ) {
|
||||
Panic("Attempt to overlay image too big for destination, %dx%d > %dx%d",
|
||||
image.width, image.height, width, height );
|
||||
}
|
||||
|
||||
if ( !(width < (x+image.width) || height < (y+image.height)) ) {
|
||||
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",
|
||||
image.width, image.height, x, y, width, height );
|
||||
image.width, image.height, lo_x, lo_y, width, height );
|
||||
}
|
||||
|
||||
if ( !(colours == image.colours) ) {
|
||||
|
@ -1693,10 +1709,8 @@ void Image::Overlay( const Image &image, unsigned int x, unsigned int y ) {
|
|||
colours, image.colours);
|
||||
}
|
||||
|
||||
unsigned int lo_x = x;
|
||||
unsigned int lo_y = y;
|
||||
unsigned int hi_x = (x+image.width)-1;
|
||||
unsigned int hi_y = (y+image.height-1);
|
||||
unsigned int hi_x = (lo_x+image.width)-1;
|
||||
unsigned int hi_y = (lo_y+image.height-1);
|
||||
if ( colours == ZM_COLOUR_GRAY8 ) {
|
||||
const uint8_t *psrc = image.buffer;
|
||||
for ( unsigned int y = lo_y; y <= hi_y; y++ ) {
|
||||
|
@ -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 )
|
||||
|
||||
void Image::Blend( const Image &image, int transparency ) {
|
||||
uint8_t* new_buffer;
|
||||
|
||||
if ( !(
|
||||
width == image.width && height == image.height
|
||||
|
@ -1743,7 +1756,7 @@ void Image::Blend( const Image &image, int transparency ) {
|
|||
if ( transparency <= 0 )
|
||||
return;
|
||||
|
||||
new_buffer = AllocBuffer(size);
|
||||
uint8_t* new_buffer = AllocBuffer(size);
|
||||
|
||||
#ifdef ZM_IMAGE_PROFILING
|
||||
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);
|
||||
}
|
||||
|
||||
void Image::Scale(unsigned int factor) {
|
||||
void Image::Scale(const unsigned int factor) {
|
||||
if ( !factor ) {
|
||||
Error("Bogus scale factor %d found", factor);
|
||||
return;
|
||||
|
@ -2756,15 +2769,13 @@ void Image::Scale(unsigned int factor) {
|
|||
unsigned int h_count = ZM_SCALE_BASE/2;
|
||||
unsigned int last_h_index = 0;
|
||||
unsigned int last_w_index = 0;
|
||||
unsigned int h_index;
|
||||
for ( unsigned int y = 0; y < height; y++ ) {
|
||||
unsigned char *ps = &buffer[y*wc];
|
||||
unsigned int w_count = ZM_SCALE_BASE/2;
|
||||
unsigned int w_index;
|
||||
last_w_index = 0;
|
||||
for ( unsigned int x = 0; x < width; x++ ) {
|
||||
w_count += factor;
|
||||
w_index = w_count/ZM_SCALE_BASE;
|
||||
unsigned int w_index = w_count/ZM_SCALE_BASE;
|
||||
for (unsigned int f = last_w_index; f < w_index; f++ ) {
|
||||
for ( unsigned int c = 0; c < colours; c++ ) {
|
||||
*pd++ = *(ps+c);
|
||||
|
@ -2774,7 +2785,7 @@ void Image::Scale(unsigned int factor) {
|
|||
last_w_index = w_index;
|
||||
}
|
||||
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++ ) {
|
||||
memcpy(pd, pd-nwc, nwc);
|
||||
pd += nwc;
|
||||
|
@ -2786,17 +2797,14 @@ void Image::Scale(unsigned int factor) {
|
|||
} else {
|
||||
unsigned char *pd = scale_buffer;
|
||||
unsigned int wc = width*colours;
|
||||
unsigned int xstart = factor/2;
|
||||
unsigned int ystart = factor/2;
|
||||
unsigned int h_count = ystart;
|
||||
unsigned int h_count = factor/2;
|
||||
unsigned int last_h_index = 0;
|
||||
unsigned int last_w_index = 0;
|
||||
unsigned int h_index;
|
||||
for ( unsigned int y = 0; y < height; y++ ) {
|
||||
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 ) {
|
||||
unsigned int w_count = xstart;
|
||||
unsigned int w_count = factor/2;
|
||||
unsigned int w_index;
|
||||
last_w_index = 0;
|
||||
|
||||
|
@ -2825,6 +2833,7 @@ void Image::Scale(unsigned int factor) {
|
|||
|
||||
void Image::Deinterlace_Discard() {
|
||||
/* Simple deinterlacing. Copy the even lines into the odd lines */
|
||||
// ICON: These can be drastically improved. But who cares?
|
||||
|
||||
if ( colours == ZM_COLOUR_GRAY8 ) {
|
||||
const uint8_t *psrc;
|
||||
|
@ -3107,9 +3116,9 @@ __attribute__((noinline,__target__("sse2")))
|
|||
#endif
|
||||
void sse2_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) {
|
||||
#if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
|
||||
static uint32_t divider = 0;
|
||||
static uint32_t clearmask = 0;
|
||||
static double current_blendpercent = 0.0;
|
||||
static uint32_t clearmask = 0;
|
||||
static uint32_t divider = 0;
|
||||
|
||||
if ( current_blendpercent != blendpercent ) {
|
||||
/* 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) {
|
||||
#if (defined(__aarch64__) && !defined(ZM_STRIP_NEON))
|
||||
static int8_t divider = 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 */
|
||||
if(blendpercent < 2.34375) {
|
||||
// 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) {
|
||||
Warning("Using slow std_blend");
|
||||
double divide = blendpercent / 100.0;
|
||||
double opacity = 1.0 - divide;
|
||||
const uint8_t* const max_ptr = result + count;
|
||||
|
|
|
@ -145,11 +145,13 @@ class Image {
|
|||
explicit Image(const AVFrame *frame);
|
||||
|
||||
~Image();
|
||||
|
||||
static void Initialise();
|
||||
static void Deinitialise();
|
||||
|
||||
inline void DumpImgBuffer() {
|
||||
DumpBuffer(buffer, buffertype);
|
||||
if (buffertype != ZM_BUFTYPE_DONTFREE)
|
||||
DumpBuffer(buffer, buffertype);
|
||||
buffertype = ZM_BUFTYPE_DONTFREE;
|
||||
buffer = nullptr;
|
||||
allocation = 0;
|
||||
|
|
|
@ -23,7 +23,7 @@ void bind_libvnc_symbols() {
|
|||
|
||||
libvnc_lib = dlopen("libvncclient.so", RTLD_LAZY | RTLD_GLOBAL);
|
||||
if (!libvnc_lib) {
|
||||
Error("Error loading libvncclient: %s", dlerror());
|
||||
Error("Error loading libvncclient.so: %s", dlerror());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -135,11 +135,6 @@ VncCamera::VncCamera(
|
|||
}
|
||||
|
||||
VncCamera::~VncCamera() {
|
||||
if (capture and mRfb) {
|
||||
if (mRfb->frameBuffer)
|
||||
free(mRfb->frameBuffer);
|
||||
(*rfbClientCleanup_f)(mRfb);
|
||||
}
|
||||
if (libvnc_lib) {
|
||||
dlclose(libvnc_lib);
|
||||
libvnc_lib = nullptr;
|
||||
|
@ -253,6 +248,12 @@ int VncCamera::PostCapture() {
|
|||
}
|
||||
|
||||
int VncCamera::Close() {
|
||||
if (capture and mRfb) {
|
||||
if (mRfb->frameBuffer)
|
||||
free(mRfb->frameBuffer);
|
||||
(*rfbClientCleanup_f)(mRfb);
|
||||
mRfb = nullptr;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
|
1419
src/zm_monitor.cpp
1419
src/zm_monitor.cpp
File diff suppressed because it is too large
Load Diff
121
src/zm_monitor.h
121
src/zm_monitor.h
|
@ -23,6 +23,7 @@
|
|||
#include "zm_define.h"
|
||||
#include "zm_camera.h"
|
||||
#include "zm_analysis_thread.h"
|
||||
#include "zm_poll_thread.h"
|
||||
#include "zm_decoder_thread.h"
|
||||
#include "zm_event.h"
|
||||
#include "zm_fifo.h"
|
||||
|
@ -33,6 +34,13 @@
|
|||
#include <memory>
|
||||
#include <sys/time.h>
|
||||
#include <vector>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#ifdef WITH_GSOAP
|
||||
#include "soapPullPointSubscriptionBindingProxy.h"
|
||||
#include "plugin/wsseapi.h"
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
|
||||
class Group;
|
||||
|
||||
|
@ -40,6 +48,7 @@ class Group;
|
|||
#define MOTION_CAUSE "Motion"
|
||||
#define LINKED_CAUSE "Linked"
|
||||
|
||||
|
||||
//
|
||||
// This is the main class for monitors. Each monitor is associated
|
||||
// with a camera and is effectively a collector for events.
|
||||
|
@ -69,7 +78,7 @@ public:
|
|||
FILE,
|
||||
FFMPEG,
|
||||
LIBVLC,
|
||||
CURL,
|
||||
LIBCURL,
|
||||
NVSOCKET,
|
||||
VNC,
|
||||
} CameraType;
|
||||
|
@ -84,7 +93,7 @@ public:
|
|||
} Orientation;
|
||||
|
||||
typedef enum {
|
||||
DEINTERLACE_DISABLED = 0x00000000,
|
||||
DEINTERLACE_DISABLED = 0x00000000,
|
||||
DEINTERLACE_FOUR_FIELD_SOFT = 0x00001E04,
|
||||
DEINTERLACE_FOUR_FIELD_MEDIUM = 0x00001404,
|
||||
DEINTERLACE_FOUR_FIELD_HARD = 0x00000A04,
|
||||
|
@ -145,12 +154,12 @@ protected:
|
|||
uint32_t last_frame_score; /* +60 */
|
||||
uint32_t audio_frequency; /* +64 */
|
||||
uint32_t audio_channels; /* +68 */
|
||||
/*
|
||||
/*
|
||||
** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038.
|
||||
** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16.
|
||||
** Because startup_time is 64bit it may be aligned to a 64bit boundary. So it's offset SHOULD be a multiple
|
||||
** Because startup_time is 64bit it may be aligned to a 64bit boundary. So it's offset SHOULD be a multiple
|
||||
** of 8. Add or delete epadding's to achieve this.
|
||||
*/
|
||||
*/
|
||||
union { /* +72 */
|
||||
time_t startup_time; /* When the zmc process started. zmwatch uses this to see how long the process has been running without getting any images */
|
||||
uint64_t extrapad1;
|
||||
|
@ -247,7 +256,51 @@ protected:
|
|||
bool hasAlarmed();
|
||||
};
|
||||
|
||||
class AmcrestAPI {
|
||||
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
|
||||
unsigned int id;
|
||||
std::string name;
|
||||
|
@ -257,6 +310,8 @@ protected:
|
|||
Function function; // What the monitor is doing
|
||||
bool enabled; // Whether the monitor is enabled or asleep
|
||||
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 method;
|
||||
|
@ -268,6 +323,13 @@ protected:
|
|||
std::string 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;
|
||||
int palette;
|
||||
int channel;
|
||||
|
@ -351,7 +413,6 @@ protected:
|
|||
int first_alarm_count;
|
||||
int last_alarm_count;
|
||||
bool last_signal;
|
||||
int last_section_mod;
|
||||
int buffer_count;
|
||||
State state;
|
||||
SystemTimePoint start_time;
|
||||
|
@ -390,6 +451,7 @@ protected:
|
|||
|
||||
VideoStore *videoStore;
|
||||
PacketQueue packetqueue;
|
||||
std::unique_ptr<PollThread> Poller;
|
||||
packetqueue_iterator *analysis_it;
|
||||
std::unique_ptr<AnalysisThread> analysis_thread;
|
||||
packetqueue_iterator *decoder_it;
|
||||
|
@ -404,6 +466,8 @@ protected:
|
|||
|
||||
int n_linked_monitors;
|
||||
MonitorLink **linked_monitors;
|
||||
std::string event_start_command;
|
||||
std::string event_end_command;
|
||||
|
||||
std::vector<Group *> groups;
|
||||
|
||||
|
@ -414,6 +478,25 @@ protected:
|
|||
std::string diag_path_ref;
|
||||
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
|
||||
uint8_t red_val;
|
||||
uint8_t green_val;
|
||||
|
@ -470,6 +553,19 @@ public:
|
|||
inline bool DecodingEnabled() const {
|
||||
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 bool Ready() const {
|
||||
if ( image_count >= ready_count ) {
|
||||
|
@ -518,7 +614,7 @@ public:
|
|||
void SetVideoWriterStartTime(SystemTimePoint t) {
|
||||
video_store_data->recording = zm::chrono::duration_cast<timeval>(t.time_since_epoch());
|
||||
}
|
||||
|
||||
|
||||
unsigned int GetPreEventCount() const { return pre_event_count; };
|
||||
int32_t GetImageBufferCount() const { return image_buffer_count; };
|
||||
State GetState() const { return (State)shared_data->state; }
|
||||
|
@ -533,6 +629,12 @@ public:
|
|||
std::string GetAudioFifoPath() const { return shared_data ? shared_data->audio_fifo_path : ""; };
|
||||
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);
|
||||
ZMPacket *getSnapshot( int index=-1 ) const;
|
||||
SystemTimePoint GetTimestamp(int index = -1) const;
|
||||
|
@ -589,8 +691,13 @@ public:
|
|||
bool CheckSignal( const Image *image );
|
||||
bool Analyse();
|
||||
bool Decode();
|
||||
bool Poll();
|
||||
void DumpImage( Image *dump_image ) 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 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;
|
||||
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 :
|
||||
Debug(1, "Got SLOW FWD command");
|
||||
paused = true;
|
||||
|
@ -231,6 +243,14 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
Info("User initiated exit - CMD_QUIT");
|
||||
zm_terminate = true;
|
||||
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 :
|
||||
Debug(1, "Got QUERY command, sending STATUS");
|
||||
break;
|
||||
|
@ -268,7 +288,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
} else {
|
||||
FPSeconds elapsed = now - last_fps_update;
|
||||
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_fps_update = now;
|
||||
}
|
||||
|
@ -288,9 +308,9 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
status_data.delayed = delayed;
|
||||
status_data.paused = paused;
|
||||
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;
|
||||
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.capture_fps,
|
||||
status_data.analysis_fps,
|
||||
|
@ -359,14 +379,15 @@ bool MonitorStream::sendFrame(const std::string &filepath, SystemTimePoint times
|
|||
fputs("\r\n", stdout);
|
||||
fflush(stdout);
|
||||
|
||||
TimePoint send_end_time = std::chrono::steady_clock::now();
|
||||
TimePoint::duration frame_send_time = send_end_time - send_start_time;
|
||||
if (maxfps > 0.0) {
|
||||
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 /= 2;
|
||||
Info("Frame send time %" PRIi64 " ms too slow, throttling maxfps to %.2f",
|
||||
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(frame_send_time).count()),
|
||||
maxfps);
|
||||
if (frame_send_time > Milliseconds(lround(Milliseconds::period::den / maxfps))) {
|
||||
Info("Frame send time %" PRIi64 " ms too slow, throttling maxfps to %.2f",
|
||||
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(frame_send_time).count()),
|
||||
maxfps);
|
||||
}
|
||||
}
|
||||
|
||||
last_frame_sent = now;
|
||||
|
@ -377,12 +398,15 @@ bool MonitorStream::sendFrame(const std::string &filepath, SystemTimePoint times
|
|||
}
|
||||
|
||||
bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
|
||||
Image *send_image = prepareImage(image);
|
||||
if (!config.timestamp_on_capture) {
|
||||
monitor->TimestampImage(send_image, timestamp);
|
||||
monitor->TimestampImage(image, timestamp);
|
||||
}
|
||||
Image *send_image = prepareImage(image);
|
||||
|
||||
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 ( !vid_stream ) {
|
||||
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());
|
||||
} 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;
|
||||
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 ) {
|
||||
case STREAM_JPEG :
|
||||
send_image->EncodeJpeg(img_buffer, &img_buffer_size);
|
||||
|
@ -446,19 +473,23 @@ bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
|
|||
fputs("\r\n", 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
|
||||
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;
|
||||
}
|
||||
} // end bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp)
|
||||
|
||||
void MonitorStream::runStream() {
|
||||
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
|
||||
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;
|
||||
|
||||
|
@ -570,7 +602,7 @@ void MonitorStream::runStream() {
|
|||
break;
|
||||
}
|
||||
|
||||
now = std::chrono::system_clock::now();
|
||||
now = std::chrono::steady_clock::now();
|
||||
|
||||
bool was_paused = 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);
|
||||
} else {
|
||||
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 (actual_delta_time > expected_delta_time) {
|
||||
|
@ -683,7 +715,8 @@ void MonitorStream::runStream() {
|
|||
if (last_read_index != monitor->shared_data->last_write_index) {
|
||||
// have a new image to send
|
||||
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) {
|
||||
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)",
|
||||
|
@ -693,9 +726,22 @@ void MonitorStream::runStream() {
|
|||
// Perhaps we should use NOW instead.
|
||||
last_frame_timestamp =
|
||||
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.");
|
||||
zm_terminate = true;
|
||||
break;
|
||||
|
@ -704,7 +750,7 @@ void MonitorStream::runStream() {
|
|||
if (frame_count == 0) {
|
||||
// Chrome will not display the first frame until it receives another.
|
||||
// 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.");
|
||||
zm_terminate = true;
|
||||
break;
|
||||
|
@ -726,7 +772,7 @@ void MonitorStream::runStream() {
|
|||
frame_count++;
|
||||
frame_count++;
|
||||
} 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 (paused_image) {
|
||||
// Send keepalive
|
||||
|
@ -742,9 +788,9 @@ void MonitorStream::runStream() {
|
|||
} // end if actual_delta_time > 5
|
||||
} // end if change in zoom
|
||||
} // end if paused or not
|
||||
} else {
|
||||
frame_count++;
|
||||
} // end if should send frame
|
||||
//} else {
|
||||
//frame_count++;
|
||||
} // end if should send frame now > when_to_send_next_frame
|
||||
|
||||
if (buffered_playback && !paused) {
|
||||
if (monitor->shared_data->valid) {
|
||||
|
@ -775,17 +821,42 @@ void MonitorStream::runStream() {
|
|||
}
|
||||
} // end if buffered playback
|
||||
} 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 )
|
||||
|
||||
FPSeconds sleep_time =
|
||||
FPSeconds(ZM_RATE_BASE / ((base_fps ? base_fps : 1) * (replay_rate ? abs(replay_rate * 2) : 2)));
|
||||
FPSeconds sleep_time;
|
||||
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) {
|
||||
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.
|
||||
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 {
|
||||
Debug(3, "Sleeping for %" PRIi64 " us",
|
||||
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()));
|
||||
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
|
||||
|
||||
if (buffered_playback) {
|
||||
|
@ -854,16 +918,16 @@ void MonitorStream::SingleImage(int scale) {
|
|||
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);
|
||||
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 ) {
|
||||
scaled_image.Assign(*snap_image);
|
||||
scaled_image.Scale(scale);
|
||||
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);
|
||||
|
||||
fprintf(stdout,
|
||||
|
|
182
src/zm_mpeg.cpp
182
src/zm_mpeg.cpp
|
@ -1,17 +1,17 @@
|
|||
/*
|
||||
* ZoneMinder MPEG class implementation, $Date$, $Revision$
|
||||
* Copyright (C) 2001-2008 Philip Coombes
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
@ -42,19 +42,26 @@ void VideoStream::SetupFormat( ) {
|
|||
ofc = nullptr;
|
||||
avformat_alloc_output_context2(&ofc, nullptr, format, filename);
|
||||
|
||||
if ( !ofc ) {
|
||||
if (!ofc) {
|
||||
Fatal("avformat_alloc_..._context failed");
|
||||
}
|
||||
|
||||
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 */
|
||||
switch ( colours ) {
|
||||
switch (colours) {
|
||||
case ZM_COLOUR_RGB24:
|
||||
if ( subpixelorder == ZM_SUBPIX_ORDER_BGR ) {
|
||||
if (subpixelorder == ZM_SUBPIX_ORDER_BGR) {
|
||||
/* BGR subpixel order */
|
||||
pf = AV_PIX_FMT_BGR24;
|
||||
} else {
|
||||
|
@ -63,13 +70,13 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
|
|||
}
|
||||
break;
|
||||
case ZM_COLOUR_RGB32:
|
||||
if ( subpixelorder == ZM_SUBPIX_ORDER_ARGB ) {
|
||||
if (subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
|
||||
/* ARGB subpixel order */
|
||||
pf = AV_PIX_FMT_ARGB;
|
||||
} else if ( subpixelorder == ZM_SUBPIX_ORDER_ABGR ) {
|
||||
} else if (subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
|
||||
/* ABGR subpixel order */
|
||||
pf = AV_PIX_FMT_ABGR;
|
||||
} else if ( subpixelorder == ZM_SUBPIX_ORDER_BGRA ) {
|
||||
} else if (subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
|
||||
/* BGRA subpixel order */
|
||||
pf = AV_PIX_FMT_BGRA;
|
||||
} else {
|
||||
|
@ -85,22 +92,22 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
|
|||
break;
|
||||
}
|
||||
|
||||
if ( strcmp("rtp", of->name) == 0 ) {
|
||||
if (strcmp("rtp", of->name) == 0) {
|
||||
// RTP must have a packet_size.
|
||||
// Not sure what this value should be really...
|
||||
ofc->packet_size = width*height;
|
||||
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.
|
||||
of->video_codec = AV_CODEC_ID_MPEG4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_AVCODECID codec_id = of->video_codec;
|
||||
if ( codec_name ) {
|
||||
if (codec_name) {
|
||||
AVCodec *a = avcodec_find_encoder_by_name(codec_name);
|
||||
if ( a ) {
|
||||
if (a) {
|
||||
codec_id = a->id;
|
||||
Debug(1, "Using codec \"%s\"", codec_name);
|
||||
} 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
|
||||
and initialize the codecs */
|
||||
ost = nullptr;
|
||||
if ( codec_id != AV_CODEC_ID_NONE ) {
|
||||
if (codec_id != AV_CODEC_ID_NONE) {
|
||||
codec = avcodec_find_encoder(codec_id);
|
||||
if ( !codec ) {
|
||||
Fatal("Could not find encoder for '%s'", avcodec_get_name(codec_id));
|
||||
if (!codec) {
|
||||
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));
|
||||
|
||||
ost = avformat_new_stream( ofc, codec );
|
||||
|
||||
if ( !ost ) {
|
||||
Fatal( "Could not alloc stream" );
|
||||
return;
|
||||
ost = avformat_new_stream(ofc, codec);
|
||||
if (!ost) {
|
||||
Error("Could not alloc stream");
|
||||
return -1;
|
||||
}
|
||||
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;
|
||||
|
||||
codec_context = avcodec_alloc_context3(nullptr);
|
||||
//avcodec_parameters_to_context(codec_context, ost->codecpar);
|
||||
|
||||
codec_context->codec_id = codec->id;
|
||||
codec_context->codec_type = codec->type;
|
||||
|
||||
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.
|
||||
// This gets rid of artifacts in the beginning of the movie; and well, even quality.
|
||||
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;
|
||||
ost->time_base.den = frame_rate;
|
||||
ost->time_base.num = 1;
|
||||
|
||||
|
||||
Debug( 1, "Will encode in %d fps. %dx%d", codec_context->time_base.den, width, height );
|
||||
|
||||
|
||||
/* emit one intra frame every second */
|
||||
codec_context->gop_size = frame_rate;
|
||||
|
||||
// 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;
|
||||
|
||||
avcodec_parameters_from_context(ost->codecpar, codec_context);
|
||||
zm_dump_codecpar(ost->codecpar);
|
||||
} else {
|
||||
Fatal( "of->video_codec == AV_CODEC_ID_NONE" );
|
||||
}
|
||||
Error("of->video_codec == AV_CODEC_ID_NONE");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VideoStream::SetParameters( ) {
|
||||
|
@ -198,11 +203,11 @@ const char *VideoStream::MimeType() const {
|
|||
bool VideoStream::OpenStream( ) {
|
||||
int ret;
|
||||
|
||||
/* now that all the parameters are set, we can open the
|
||||
/* now that all the parameters are set, we can open the
|
||||
video codecs and allocate the necessary encode buffers */
|
||||
if ( ost ) {
|
||||
Debug(1,"Opening codec");
|
||||
|
||||
|
||||
/* open the codec */
|
||||
|
||||
if ((ret = avcodec_open2(codec_context, codec, nullptr)) < 0) {
|
||||
|
@ -319,7 +324,7 @@ VideoStream::VideoStream( const char *in_filename, const char *in_format, int bi
|
|||
if ( !initialised ) {
|
||||
Initialise( );
|
||||
}
|
||||
|
||||
|
||||
if ( format ) {
|
||||
int length = strlen(format);
|
||||
codec_and_format = new char[length+1];;
|
||||
|
@ -337,13 +342,13 @@ VideoStream::VideoStream( const char *in_filename, const char *in_format, int bi
|
|||
SetupFormat( );
|
||||
SetupCodec( colours, subpixelorder, width, height, bitrate, frame_rate );
|
||||
SetParameters( );
|
||||
|
||||
|
||||
// Allocate buffered packets.
|
||||
packet_buffers = new AVPacket*[2];
|
||||
packet_buffers[0] = new AVPacket();
|
||||
packet_buffers[1] = new AVPacket();
|
||||
packet_index = 0;
|
||||
|
||||
|
||||
// Initialize mutex used by streaming thread.
|
||||
if ( pthread_mutex_init( buffer_copy_lock, nullptr ) != 0 ) {
|
||||
Fatal("pthread_mutex_init failed");
|
||||
|
@ -353,35 +358,35 @@ VideoStream::VideoStream( const char *in_filename, const char *in_format, int bi
|
|||
|
||||
VideoStream::~VideoStream( ) {
|
||||
Debug( 1, "VideoStream destructor." );
|
||||
|
||||
|
||||
// Stop streaming thread.
|
||||
if ( streaming_thread ) {
|
||||
do_streaming = false;
|
||||
void* thread_exit_code;
|
||||
|
||||
|
||||
Debug( 1, "Asking streaming thread to exit." );
|
||||
|
||||
|
||||
// Wait for thread to exit.
|
||||
pthread_join(streaming_thread, &thread_exit_code);
|
||||
}
|
||||
|
||||
|
||||
if ( buffer_copy != nullptr ) {
|
||||
av_free( buffer_copy );
|
||||
}
|
||||
|
||||
|
||||
if ( buffer_copy_lock ) {
|
||||
if ( pthread_mutex_destroy( buffer_copy_lock ) != 0 ) {
|
||||
Error( "pthread_mutex_destroy failed" );
|
||||
}
|
||||
delete buffer_copy_lock;
|
||||
}
|
||||
|
||||
|
||||
if (packet_buffers) {
|
||||
delete packet_buffers[0];
|
||||
delete packet_buffers[1];
|
||||
delete[] packet_buffers;
|
||||
}
|
||||
|
||||
|
||||
/* close each codec */
|
||||
if ( ost ) {
|
||||
avcodec_close( codec_context );
|
||||
|
@ -409,7 +414,7 @@ VideoStream::~VideoStream( ) {
|
|||
|
||||
/* free the stream */
|
||||
av_free( ofc );
|
||||
|
||||
|
||||
/* free format and codec_name data. */
|
||||
if ( codec_and_format ) {
|
||||
delete codec_and_format;
|
||||
|
@ -420,12 +425,12 @@ double VideoStream::EncodeFrame( const uint8_t *buffer, int buffer_size, bool _a
|
|||
if ( pthread_mutex_lock(buffer_copy_lock) != 0 ) {
|
||||
Fatal( "EncodeFrame: pthread_mutex_lock failed." );
|
||||
}
|
||||
|
||||
|
||||
if (buffer_copy_size < buffer_size) {
|
||||
if ( buffer_copy ) {
|
||||
av_free(buffer_copy);
|
||||
}
|
||||
|
||||
|
||||
// Allocate a buffer to store source images for the streaming thread to encode.
|
||||
buffer_copy = (uint8_t *)av_malloc(buffer_size);
|
||||
if ( !buffer_copy ) {
|
||||
|
@ -435,35 +440,34 @@ double VideoStream::EncodeFrame( const uint8_t *buffer, int buffer_size, bool _a
|
|||
}
|
||||
buffer_copy_size = buffer_size;
|
||||
}
|
||||
|
||||
|
||||
add_timestamp = _add_timestamp;
|
||||
timestamp = _timestamp;
|
||||
buffer_copy_used = buffer_size;
|
||||
memcpy(buffer_copy, buffer, buffer_size);
|
||||
|
||||
|
||||
if ( pthread_mutex_unlock(buffer_copy_lock) != 0 ) {
|
||||
Fatal( "EncodeFrame: pthread_mutex_unlock failed." );
|
||||
}
|
||||
|
||||
|
||||
if ( streaming_thread == 0 ) {
|
||||
Debug( 1, "Starting streaming thread" );
|
||||
|
||||
|
||||
// Start a thread for streaming encoded video.
|
||||
if (pthread_create( &streaming_thread, nullptr, StreamingThreadCallback, (void*) this) != 0){
|
||||
// Log a fatal error and exit the process.
|
||||
Fatal( "VideoStream failed to create streaming thread." );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//return ActuallyEncodeFrame( buffer, buffer_size, add_timestamp, timestamp);
|
||||
|
||||
|
||||
return _timestamp;
|
||||
}
|
||||
|
||||
double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp, unsigned int timestamp ) {
|
||||
|
||||
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 );
|
||||
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 );
|
||||
|
@ -475,37 +479,36 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size,
|
|||
memcpy( opicture->data[0], buffer, buffer_size );
|
||||
}
|
||||
AVFrame *opicture_ptr = opicture;
|
||||
|
||||
|
||||
AVPacket *pkt = packet_buffers[packet_index];
|
||||
av_init_packet( pkt );
|
||||
int got_packet = 0;
|
||||
if (codec_context->codec_type == AVMEDIA_TYPE_VIDEO &&
|
||||
codec_context->codec_id == AV_CODEC_ID_RAWVIDEO) {
|
||||
pkt->flags |= AV_PKT_FLAG_KEY;
|
||||
pkt->stream_index = ost->index;
|
||||
pkt->data = (uint8_t *)opicture_ptr;
|
||||
pkt->size = sizeof (AVPicture);
|
||||
got_packet = 1;
|
||||
int got_packet = 0;
|
||||
if (codec_context->codec_type == AVMEDIA_TYPE_VIDEO &&
|
||||
codec_context->codec_id == AV_CODEC_ID_RAWVIDEO) {
|
||||
pkt->flags |= AV_PKT_FLAG_KEY;
|
||||
pkt->stream_index = ost->index;
|
||||
pkt->data = (uint8_t *)opicture_ptr;
|
||||
pkt->size = sizeof (AVPicture);
|
||||
got_packet = 1;
|
||||
} else {
|
||||
opicture_ptr->pts = codec_context->frame_number;
|
||||
opicture_ptr->quality = codec_context->global_quality;
|
||||
|
||||
avcodec_send_frame(codec_context, opicture_ptr);
|
||||
int ret = avcodec_receive_packet(codec_context, pkt);
|
||||
if ( ret < 0 ) {
|
||||
if ( AVERROR_EOF != ret ) {
|
||||
Error("ERror encoding video (%d) (%s)", ret,
|
||||
av_err2str(ret));
|
||||
}
|
||||
} else {
|
||||
got_packet = 1;
|
||||
avcodec_send_frame(codec_context, opicture_ptr);
|
||||
int ret = avcodec_receive_packet(codec_context, pkt);
|
||||
if (ret < 0) {
|
||||
if (AVERROR_EOF != ret) {
|
||||
Error("ERror encoding video (%d) (%s)", ret, av_err2str(ret));
|
||||
}
|
||||
} else {
|
||||
got_packet = 1;
|
||||
}
|
||||
|
||||
if ( got_packet ) {
|
||||
// if ( c->coded_frame->key_frame )
|
||||
// {
|
||||
// pkt->flags |= AV_PKT_FLAG_KEY;
|
||||
// }
|
||||
if (got_packet) {
|
||||
// if ( c->coded_frame->key_frame )
|
||||
// {
|
||||
// pkt->flags |= AV_PKT_FLAG_KEY;
|
||||
// }
|
||||
|
||||
if ( pkt->pts != (int64_t)AV_NOPTS_VALUE ) {
|
||||
pkt->pts = av_rescale_q( pkt->pts, codec_context->time_base, ost->time_base );
|
||||
|
@ -517,18 +520,17 @@ double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size,
|
|||
pkt->stream_index = ost->index;
|
||||
}
|
||||
}
|
||||
|
||||
return ( opicture_ptr->pts);
|
||||
|
||||
return opicture_ptr->pts;
|
||||
}
|
||||
|
||||
int VideoStream::SendPacket(AVPacket *packet) {
|
||||
|
||||
int ret = av_write_frame( ofc, packet );
|
||||
if ( ret != 0 ) {
|
||||
Fatal( "Error %d while writing video frame: %s", ret, av_err2str( errno ) );
|
||||
}
|
||||
av_packet_unref( packet );
|
||||
return ret;
|
||||
int ret = av_write_frame(ofc, packet);
|
||||
if (ret < 0) {
|
||||
Error("Error %d while writing video frame: %s", ret, av_err2str(errno));
|
||||
}
|
||||
av_packet_unref(packet);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *VideoStream::StreamingThreadCallback(void *ctx) {
|
||||
|
|
|
@ -68,7 +68,7 @@ protected:
|
|||
static void Initialise();
|
||||
|
||||
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 ActuallyOpenStream();
|
||||
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
|
||||
bool decoded;
|
||||
std::vector<ZoneStats> zone_stats;
|
||||
std::string alarm_cause;
|
||||
|
||||
public:
|
||||
AVPacket *av_packet() { return &packet; }
|
||||
|
|
|
@ -116,15 +116,16 @@ bool PacketQueue::queuePacket(std::shared_ptr<ZMPacket> add_packet) {
|
|||
, max_video_packet_count);
|
||||
|
||||
for (
|
||||
auto it = ++pktQueue.begin();
|
||||
it != pktQueue.end() and *it != add_packet;
|
||||
auto it = ++pktQueue.begin();
|
||||
//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;
|
||||
|
||||
ZMLockedPacket *lp = new ZMLockedPacket(zm_packet);
|
||||
if (!lp->trylock()) {
|
||||
Debug(1, "Found locked packet when trying to free up video packets. Skipping to next one");
|
||||
delete lp;
|
||||
ZMLockedPacket lp(zm_packet);
|
||||
if (!lp.trylock()) {
|
||||
Warning("Found locked packet when trying to free up video packets. This basically means that decoding is not keeping up.");
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
@ -136,7 +137,7 @@ bool PacketQueue::queuePacket(std::shared_ptr<ZMPacket> add_packet) {
|
|||
) {
|
||||
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
|
||||
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");
|
||||
++(*iterator_it);
|
||||
}
|
||||
|
@ -153,8 +154,6 @@ bool PacketQueue::queuePacket(std::shared_ptr<ZMPacket> add_packet) {
|
|||
max_video_packet_count,
|
||||
pktQueue.size());
|
||||
|
||||
delete lp;
|
||||
|
||||
if (zm_packet->packet.stream_index == video_stream_id)
|
||||
break;
|
||||
} // end while
|
||||
|
@ -162,7 +161,7 @@ bool PacketQueue::queuePacket(std::shared_ptr<ZMPacket> add_packet) {
|
|||
} // end lock scope
|
||||
// We signal on every packet because someday we may analyze sound
|
||||
Debug(4, "packetqueue queuepacket, unlocked signalling");
|
||||
condition.notify_all();
|
||||
condition.notify_one();
|
||||
|
||||
return true;
|
||||
} // end bool PacketQueue::queuePacket(ZMPacket* zm_packet)
|
||||
|
@ -312,7 +311,6 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
|
|||
pktQueue.size());
|
||||
pktQueue.pop_front();
|
||||
packet_counts[zm_packet->packet.stream_index] -= 1;
|
||||
//delete zm_packet;
|
||||
}
|
||||
} // end if have at least max_video_packet_count video packets remaining
|
||||
// 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_monitor.h"
|
||||
#include "zm_packet.h"
|
||||
#include "zm_signal.h"
|
||||
|
||||
RemoteCameraRtsp::RemoteCameraRtsp(
|
||||
const Monitor *monitor,
|
||||
|
@ -126,8 +127,8 @@ int RemoteCameraRtsp::Disconnect() {
|
|||
|
||||
int RemoteCameraRtsp::PrimeCapture() {
|
||||
Debug(2, "Waiting for sources");
|
||||
for (int i = 0; i < 100 && !rtspThread->hasSources(); i++) {
|
||||
std::this_thread::sleep_for(Microseconds(100));
|
||||
for (int i = 100; i && !zm_terminate && !rtspThread->hasSources(); i--) {
|
||||
std::this_thread::sleep_for(Microseconds(10000));
|
||||
}
|
||||
|
||||
if (!rtspThread->hasSources()) {
|
||||
|
@ -168,8 +169,10 @@ int RemoteCameraRtsp::PrimeCapture() {
|
|||
}
|
||||
} // end foreach stream
|
||||
|
||||
if ( mVideoStreamId == -1 )
|
||||
Fatal("Unable to locate video stream");
|
||||
if ( mVideoStreamId == -1 ) {
|
||||
Error("Unable to locate video stream");
|
||||
return -1;
|
||||
}
|
||||
if ( mAudioStreamId == -1 )
|
||||
Debug(3, "Unable to locate audio stream");
|
||||
|
||||
|
@ -179,17 +182,22 @@ int RemoteCameraRtsp::PrimeCapture() {
|
|||
|
||||
// Find the decoder for the video stream
|
||||
AVCodec *codec = avcodec_find_decoder(mVideoCodecContext->codec_id);
|
||||
if ( codec == nullptr )
|
||||
Panic("Unable to locate codec %d decoder", mVideoCodecContext->codec_id);
|
||||
if ( codec == nullptr ) {
|
||||
Error("Unable to locate codec %d decoder", mVideoCodecContext->codec_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Open codec
|
||||
if ( avcodec_open2(mVideoCodecContext, codec, nullptr) < 0 )
|
||||
Panic("Can't open codec");
|
||||
if ( avcodec_open2(mVideoCodecContext, codec, nullptr) < 0 ) {
|
||||
Error("Can't open codec");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pSize = av_image_get_buffer_size(imagePixFormat, width, height, 1);
|
||||
|
||||
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;
|
||||
|
@ -208,18 +216,13 @@ int RemoteCameraRtsp::PreCapture() {
|
|||
int RemoteCameraRtsp::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
||||
int frameComplete = false;
|
||||
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) {
|
||||
buffer.clear();
|
||||
if (!rtspThread || rtspThread->IsStopped())
|
||||
if (!rtspThread || rtspThread->IsStopped() || zm_terminate)
|
||||
return -1;
|
||||
|
||||
if ( rtspThread->getFrame(buffer) ) {
|
||||
if (rtspThread->getFrame(buffer)) {
|
||||
Debug(3, "Read frame %d bytes", buffer.size());
|
||||
Hexdump(4, buffer.head(), 16);
|
||||
|
||||
|
@ -254,36 +257,20 @@ int RemoteCameraRtsp::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
|||
|
||||
//while ( (!frameComplete) && (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();
|
||||
bytes += packet->size;
|
||||
buffer -= packet->size;
|
||||
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
gettimeofday(&now, nullptr);
|
||||
packet->pts = packet->dts = now.tv_sec*1000000+now.tv_usec;
|
||||
|
||||
int bytes_consumed = zm_packet->decode(mVideoCodecContext);
|
||||
if ( bytes_consumed < 0 ) {
|
||||
Error("Error while decoding frame %d", frameCount);
|
||||
//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;
|
||||
}
|
||||
zm_packet->codec_type = mVideoCodecContext->codec_type;
|
||||
zm_packet->stream = mVideoStream;
|
||||
frameComplete = true;
|
||||
Debug(2, "Frame: %d - %d/%d", frameCount, packet->size, buffer.size());
|
||||
}
|
||||
} /* getFrame() */
|
||||
} // end while true
|
||||
|
|
47
src/zm_rgb.h
47
src/zm_rgb.h
|
@ -118,41 +118,34 @@ constexpr Rgb kRGBTransparent = 0x01000000;
|
|||
|
||||
/* Convert RGB colour value into BGR\ARGB\ABGR */
|
||||
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_BGRA:
|
||||
{
|
||||
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ARGB:
|
||||
{
|
||||
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ABGR:
|
||||
{
|
||||
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
/* Grayscale */
|
||||
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
||||
break;
|
||||
/* Grayscale */
|
||||
case ZM_SUBPIX_ORDER_NONE:
|
||||
result = p_col & 0xff;
|
||||
break;
|
||||
result = p_col & 0xff;
|
||||
break;
|
||||
default:
|
||||
return p_col;
|
||||
break;
|
||||
result = p_col;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -277,7 +277,7 @@ void RtpCtrlThread::Run() {
|
|||
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.
|
||||
|
||||
while (!mTerminate && select.wait() >= 0) {
|
||||
while (!mTerminate && (select.wait() >= 0)) {
|
||||
TimePoint now = std::chrono::steady_clock::now();
|
||||
zm::Select::CommsList readable = select.getReadable();
|
||||
if ( readable.size() == 0 ) {
|
||||
|
|
|
@ -45,8 +45,10 @@ RtpSource::RtpSource(
|
|||
mFrame(65536),
|
||||
mFrameCount(0),
|
||||
mFrameGood(true),
|
||||
prevM(false),
|
||||
mFrameReady(false),
|
||||
mFrameProcessed(false)
|
||||
mFrameProcessed(false),
|
||||
mTerminate(false)
|
||||
{
|
||||
char hostname[256] = "";
|
||||
gethostname(hostname, sizeof(hostname));
|
||||
|
|
|
@ -91,8 +91,6 @@ private:
|
|||
bool mFrameGood;
|
||||
bool prevM;
|
||||
|
||||
bool mTerminate;
|
||||
|
||||
bool mFrameReady;
|
||||
std::condition_variable mFrameReadyCv;
|
||||
std::mutex mFrameReadyMutex;
|
||||
|
@ -100,6 +98,7 @@ private:
|
|||
bool mFrameProcessed;
|
||||
std::condition_variable mFrameProcessedCv;
|
||||
std::mutex mFrameProcessedMutex;
|
||||
bool mTerminate;
|
||||
|
||||
private:
|
||||
void init(uint16_t seq);
|
||||
|
|
|
@ -298,6 +298,14 @@ int main(int argc, char *argv[]) {
|
|||
session->GetMediaSessionId(), xop::channel_1, audioFifoPath);
|
||||
audioSource->setFrequency(monitor->GetAudioFrequency());
|
||||
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 {
|
||||
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)
|
||||
headerSize = 3;
|
||||
}
|
||||
while (nalRemaining) {
|
||||
while (nalRemaining && !stop_) {
|
||||
if ( nalRemaining < maxNalSize ) {
|
||||
// This is the last fragment:
|
||||
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());
|
||||
while (m_buffer.size()) {
|
||||
while (m_buffer.size() and !stop_) {
|
||||
unsigned int data_size = 0;
|
||||
int64_t pts;
|
||||
unsigned char *header_end = nullptr;
|
||||
|
@ -224,7 +224,7 @@ int ZoneMinderFifoSource::getNextFrame() {
|
|||
int bytes_needed = data_size - (m_buffer.size() - header_size);
|
||||
if (bytes_needed > 0) {
|
||||
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);
|
||||
if (bytes_read <= 0) {
|
||||
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_);
|
||||
Debug(3, "have lock");
|
||||
while (framesList.size()) {
|
||||
while (!stop_ && framesList.size()) {
|
||||
std::pair<unsigned char*, size_t> nal = framesList.front();
|
||||
framesList.pop_front();
|
||||
NAL_Frame *Nal = new NAL_Frame(nal.first, nal.second, pts);
|
||||
m_nalQueue.push(Nal);
|
||||
}
|
||||
}
|
||||
Debug(3, "notifying");
|
||||
condition_.notify_all();
|
||||
} // end while m_buffer.size()
|
||||
return 1;
|
||||
|
|
|
@ -21,7 +21,7 @@ class ZoneMinderDeviceSource;
|
|||
|
||||
class BaseServerMediaSubsession {
|
||||
public:
|
||||
BaseServerMediaSubsession(StreamReplicator* replicator):
|
||||
explicit BaseServerMediaSubsession(StreamReplicator* replicator):
|
||||
m_replicator(replicator) {};
|
||||
|
||||
FramedSource* createSource(
|
||||
|
|
|
@ -3,34 +3,44 @@
|
|||
|
||||
#ifdef HAVE_SENDFILE4_SUPPORT
|
||||
#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
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.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
|
||||
#error "Your platform does not support sendfile. Sorry."
|
||||
#include <unistd.h>
|
||||
#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
|
||||
|
|
|
@ -31,10 +31,8 @@ constexpr Seconds StreamBase::MAX_STREAM_DELAY;
|
|||
constexpr Milliseconds StreamBase::MAX_SLEEP;
|
||||
|
||||
StreamBase::~StreamBase() {
|
||||
if (vid_stream) {
|
||||
delete vid_stream;
|
||||
vid_stream = nullptr;
|
||||
}
|
||||
delete vid_stream;
|
||||
delete temp_img_buffer;
|
||||
closeComms();
|
||||
}
|
||||
|
||||
|
@ -128,10 +126,10 @@ bool StreamBase::checkCommandQueue() {
|
|||
return true;
|
||||
}
|
||||
} else if ( connkey ) {
|
||||
Warning("No sd in checkCommandQueue, comms not open?");
|
||||
Warning("No sd in checkCommandQueue, comms not open for connkey %06d?", connkey);
|
||||
} else {
|
||||
// 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;
|
||||
} // 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 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 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,
|
||||
"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"
|
||||
"Display image width = %d, height = %d\n"
|
||||
"Last display image width = %d, height = %d\n"
|
||||
"Send image width = %d, height = %d\n"
|
||||
"Last send image width = %d, height = %d\n",
|
||||
"Send image width = %d, height = %d\n",
|
||||
scale, zoom, mag, act_mag,
|
||||
last_scale, last_zoom, last_mag, last_act_mag,
|
||||
base_image_width, base_image_height,
|
||||
|
@ -180,8 +176,7 @@ Image *StreamBase::prepareImage(Image *image) {
|
|||
last_act_image_width, last_act_image_height,
|
||||
disp_image_width, disp_image_height,
|
||||
last_disp_image_width, last_disp_image_height,
|
||||
send_image_width, send_image_height,
|
||||
last_send_image_width, last_send_image_height
|
||||
send_image_width, send_image_height
|
||||
);
|
||||
|
||||
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));
|
||||
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
|
||||
Debug(3, "comms open at %s", loc_sock_path);
|
||||
} // end void StreamBase::openComms()
|
||||
|
||||
void StreamBase::closeComms() {
|
||||
|
|
|
@ -40,6 +40,7 @@ public:
|
|||
STREAM_SINGLE,
|
||||
STREAM_MPEG
|
||||
} StreamType;
|
||||
typedef enum { FRAME_NORMAL, FRAME_ANALYSIS } FrameType;
|
||||
|
||||
protected:
|
||||
static constexpr Seconds MAX_STREAM_DELAY = Seconds(5);
|
||||
|
@ -88,6 +89,9 @@ protected:
|
|||
CMD_VARPLAY,
|
||||
CMD_GET_IMAGE,
|
||||
CMD_QUIT,
|
||||
CMD_MAXFPS,
|
||||
CMD_ANALYZE_ON,
|
||||
CMD_ANALYZE_OFF,
|
||||
CMD_QUERY=99
|
||||
} MsgCommand;
|
||||
|
||||
|
@ -96,6 +100,7 @@ protected:
|
|||
std::shared_ptr<Monitor> monitor;
|
||||
|
||||
StreamType type;
|
||||
FrameType frame_type;
|
||||
const char *format;
|
||||
int replay_rate;
|
||||
int scale;
|
||||
|
@ -118,26 +123,30 @@ protected:
|
|||
bool paused;
|
||||
int step;
|
||||
|
||||
SystemTimePoint now;
|
||||
SystemTimePoint last_comm_update;
|
||||
TimePoint now;
|
||||
TimePoint last_comm_update;
|
||||
|
||||
double maxfps;
|
||||
double base_fps; // Should be capturing fps, hence a rough target
|
||||
double effective_fps; // Target fps after taking max_fps into account
|
||||
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 last_frame_count; // Used in calculating actual_fps from frame_count - last_frame_count
|
||||
|
||||
int frame_mod;
|
||||
|
||||
SystemTimePoint last_frame_sent;
|
||||
TimePoint last_frame_sent;
|
||||
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;
|
||||
|
||||
CmdMsg msg;
|
||||
|
||||
unsigned char *temp_img_buffer; // Used when encoding or sending file data
|
||||
size_t temp_img_buffer_size;
|
||||
|
||||
protected:
|
||||
bool loadMonitor(int monitor_id);
|
||||
bool checkInitialised();
|
||||
|
@ -151,6 +160,7 @@ public:
|
|||
monitor_id(0),
|
||||
monitor(nullptr),
|
||||
type(DEFAULT_TYPE),
|
||||
frame_type(FRAME_NORMAL),
|
||||
format(""),
|
||||
replay_rate(DEFAULT_RATE),
|
||||
scale(DEFAULT_SCALE),
|
||||
|
@ -175,7 +185,9 @@ public:
|
|||
actual_fps(0.0),
|
||||
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_addr, 0, sizeof(loc_addr));
|
||||
|
@ -196,7 +208,9 @@ public:
|
|||
type = STREAM_RAW;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
void setStreamFrameType(FrameType p_type) {
|
||||
frame_type = p_type;
|
||||
}
|
||||
void setStreamFormat(const char *p_format) {
|
||||
format = p_format;
|
||||
|
@ -207,10 +221,11 @@ public:
|
|||
scale = DEFAULT_SCALE;
|
||||
}
|
||||
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;
|
||||
}
|
||||
void setStreamMaxFPS(double p_maxfps) {
|
||||
Debug(1, "Setting max fps to %f", p_maxfps);
|
||||
maxfps = p_maxfps;
|
||||
}
|
||||
void setStreamBitrate(int p_bitrate) {
|
||||
|
|
|
@ -22,7 +22,14 @@
|
|||
#include "zm_image.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");
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue