Merge branch 'master' into multistream
This commit is contained in:
commit
6bd04de5f6
|
@ -4,7 +4,7 @@
|
|||
web/api/lib
|
||||
web/includes/csrf/
|
||||
web/js/videojs.zoomrotate.js
|
||||
web/skins/classic/js/bootstrap.js
|
||||
web/skins/classic/js/bootstrap-4.5.0.js
|
||||
web/skins/classic/js/chosen
|
||||
web/skins/classic/js/dateTimePicker
|
||||
web/skins/classic/js/jquery-*.js
|
||||
|
|
|
@ -167,6 +167,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
|
||||
|
@ -407,21 +409,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)
|
||||
|
@ -540,6 +545,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)
|
||||
|
|
|
@ -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)
|
|
@ -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');
|
||||
*/
|
|
@ -283,6 +283,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 +413,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,6 +449,8 @@ 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',
|
||||
`Capturing` enum('None','Ondemand', 'Always') NOT NULL default 'Always',
|
||||
|
@ -457,6 +461,8 @@ CREATE TABLE `Monitors` (
|
|||
`DecodingEnabled` tinyint(3) unsigned NOT NULL default '1',
|
||||
`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 '',
|
||||
|
@ -976,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
|
||||
|
@ -1120,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
|
||||
--
|
||||
|
|
|
@ -28,8 +28,8 @@ SET @s = (SELECT IF(
|
|||
AND table_name = 'Monitors'
|
||||
AND column_name = 'TotalEvents'
|
||||
) > 0,
|
||||
"SELECT 'Column TotalEvents is already removed from Monitors'",
|
||||
"ALTER TABLE `Monitors` DROP `TotalEvents`"
|
||||
"ALTER TABLE `Monitors` DROP `TotalEvents`",
|
||||
"SELECT 'Column TotalEvents is already removed from Monitors'"
|
||||
));
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
||||
|
@ -50,8 +50,8 @@ SET @s = (SELECT IF(
|
|||
AND table_name = 'Monitors'
|
||||
AND column_name = 'TotalEventDiskSpace'
|
||||
) > 0,
|
||||
"SELECT 'Column TotalEventDiskSpace is already removed from Monitors'",
|
||||
"ALTER TABLE `Monitors` DROP `TotalEventDiskSpace`"
|
||||
"ALTER TABLE `Monitors` DROP `TotalEventDiskSpace`",
|
||||
"SELECT 'Column TotalEventDiskSpace is already removed from Monitors'"
|
||||
));
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
||||
|
|
|
@ -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;
|
|
@ -1,60 +1,74 @@
|
|||
|
||||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Monitors'
|
||||
AND column_name = 'Capturing'
|
||||
AND column_name = 'ManufacturerId'
|
||||
) > 0,
|
||||
"SELECT 'Column Capturing already exists in Monitors'",
|
||||
"ALTER TABLE `Monitors` ADD `Capturing` enum('None','Ondemand', 'Always') NOT NULL default 'Always' AFTER `Function`"
|
||||
"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 = 'Analysing'
|
||||
AND column_name = 'ModelId'
|
||||
) > 0,
|
||||
"SELECT 'Column Analysing already exists in Monitors'",
|
||||
"ALTER TABLE `Monitors` ADD `Analysing` enum('None','Always') NOT NULL default 'Always'"
|
||||
"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.COLUMNS WHERE table_schema = DATABASE()
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Monitors'
|
||||
AND column_name = 'AnalysisSource'
|
||||
AND column_name = 'ModelId'
|
||||
) > 0,
|
||||
"SELECT 'Column AnalysisSource already exists in Monitors'",
|
||||
"ALTER TABLE `Monitors` ADD `AnalysisSource` enum('Primary','Secondary') NOT NULL DEFAULT 'Primary' AFTER `Analysing`"
|
||||
"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.COLUMNS WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Monitors'
|
||||
AND column_name = 'Recording'
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE table_schema = DATABASE()
|
||||
AND table_name = 'MonitorPresets'
|
||||
AND column_name = 'ModelId'
|
||||
) > 0,
|
||||
"SELECT 'Column Recording already exists in Monitors'",
|
||||
"ALTER TABLE `Monitors` ADD `Recording` enum('None', 'OnMotion', 'Always') NOT NULL default 'Always'"
|
||||
"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;
|
||||
|
||||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Monitors'
|
||||
AND column_name = 'RecordingSource'
|
||||
) > 0,
|
||||
"SELECT 'Column RecordingSource already exists in Monitors'",
|
||||
"ALTER TABLE `Monitors` ADD `RecordingSource` enum('Primary','Secondary','Both') NOT NULL DEFAULT 'Primary' AFTER `RecordAudio`"
|
||||
));
|
||||
|
||||
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,59 @@
|
|||
SET @s = (SELECT IF(
|
||||
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||
AND table_name = 'Monitors'
|
||||
AND column_name = 'Capturing'
|
||||
) > 0,
|
||||
"SELECT 'Column Capturing already exists in Monitors'",
|
||||
"ALTER TABLE `Monitors` ADD `Capturing` enum('None','Ondemand', 'Always') NOT NULL default 'Always' AFTER `Function`"
|
||||
));
|
||||
|
||||
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 = 'Analysing'
|
||||
) > 0,
|
||||
"SELECT 'Column Analysing already exists in Monitors'",
|
||||
"ALTER TABLE `Monitors` ADD `Analysing` enum('None','Always') NOT NULL default 'Always'"
|
||||
));
|
||||
|
||||
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 = 'AnalysisSource'
|
||||
) > 0,
|
||||
"SELECT 'Column AnalysisSource already exists in Monitors'",
|
||||
"ALTER TABLE `Monitors` ADD `AnalysisSource` enum('Primary','Secondary') NOT NULL DEFAULT 'Primary' AFTER `Analysing`"
|
||||
));
|
||||
|
||||
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 = 'Recording'
|
||||
) > 0,
|
||||
"SELECT 'Column Recording already exists in Monitors'",
|
||||
"ALTER TABLE `Monitors` ADD `Recording` enum('None', 'OnMotion', 'Always') NOT NULL default 'Always'"
|
||||
));
|
||||
|
||||
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 = 'RecordingSource'
|
||||
) > 0,
|
||||
"SELECT 'Column RecordingSource already exists in Monitors'",
|
||||
"ALTER TABLE `Monitors` ADD `RecordingSource` enum('Primary','Secondary','Both') NOT NULL DEFAULT 'Primary' AFTER `RecordAudio`"
|
||||
));
|
||||
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
|
@ -36,7 +36,7 @@
|
|||
%global _hardened_build 1
|
||||
|
||||
Name: zoneminder
|
||||
Version: 1.37.1
|
||||
Version: 1.37.6
|
||||
Release: 1%{?dist}
|
||||
Summary: A camera monitoring and analysis tool
|
||||
Group: System Environment/Daemons
|
||||
|
|
|
@ -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
|
||||
|
@ -70,7 +69,6 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
|
|||
,policykit-1
|
||||
,rsyslog | system-log-daemon
|
||||
,zip
|
||||
,libpcre3
|
||||
,libcrypt-eksblowfish-perl
|
||||
,libdata-entropy-perl
|
||||
,libvncclient1|libvncclient0
|
||||
|
|
|
@ -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,6 +5,12 @@ set +e
|
|||
create_db () {
|
||||
echo "Checking for db"
|
||||
mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Cannot talk to database. You will have to create the db manually with something like:";
|
||||
echo "cat /usr/share/zoneminder/db/zm_create.sql | mysql -u root";
|
||||
return;
|
||||
fi
|
||||
|
||||
# test if database if already present...
|
||||
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
|
||||
echo "Creating zm db"
|
||||
|
|
|
@ -225,7 +225,7 @@ change the 3 to a 1
|
|||
|
||||
I can't see more than 6 monitors in montage on my browser
|
||||
---------------------------------------------------------
|
||||
Browsers such a Chrome and Safari only support upto 6 streams from the same domain. To work around that, take a look at the multi-port configuration discussed in the ``MIN_STREAMING_PORT`` configuration in :doc:`/userguide/options/options_network`
|
||||
Browsers such a Chrome and Safari only support up to 6 streams from the same domain. To work around that, take a look at the multi-port configuration discussed in the ``MIN_STREAMING_PORT`` configuration in :doc:`/userguide/options/options_network`
|
||||
|
||||
Why is ZoneMinder using so much CPU?
|
||||
---------------------------------------
|
||||
|
|
|
@ -3,6 +3,50 @@ Debian
|
|||
|
||||
.. contents::
|
||||
|
||||
Easy Way: Debian 11 (Bullseye)
|
||||
------------------------------
|
||||
|
||||
This procedure will guide you through the installation of ZoneMinder on Debian 11 (Bullseye).
|
||||
|
||||
**Step 1:** Setup Sudo (optional but recommended)
|
||||
|
||||
By default Debian does not come with sudo, so you have to install it and configure it manually.
|
||||
This step is optional but recommended and the following instructions assume that you have setup sudo.
|
||||
If you prefer to setup ZoneMinder as root, do it at your own risk and adapt the following instructions accordingly.
|
||||
|
||||
::
|
||||
|
||||
apt install sudo
|
||||
usermod -a -G sudo <username>
|
||||
exit
|
||||
|
||||
Now your terminal session is back under your normal user. You can check that
|
||||
you are now part of the sudo group with the command ``groups``, "sudo" should
|
||||
appear in the list. If not, run ``newgrp sudo`` and check again with ``groups``.
|
||||
|
||||
**Step 2:** Update system and install zoneminder
|
||||
|
||||
Run the following commands.
|
||||
|
||||
::
|
||||
|
||||
sudo apt update
|
||||
sudo apt upgrade
|
||||
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``.
|
||||
|
||||
**Step 3:** Setup permissions for zm.conf
|
||||
|
||||
To make sure zoneminder can read the configuration file, run the following command.
|
||||
|
||||
::
|
||||
|
||||
sudo chgrp -c www-data /etc/zm/zm.conf
|
||||
|
||||
Congratulations! You should now be able to access zoneminder at ``http://yourhostname/zm``
|
||||
|
||||
Easy Way: Debian Buster
|
||||
------------------------
|
||||
|
||||
|
@ -60,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
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ An Easy To Use Docker Image
|
|||
===========================
|
||||
If you are interested in trying out ZoneMinder quickly, user Dan Landon maintains an easy to use docker image for ZoneMinder. With a few simple configuration changes, it also provides complete Event Notification Server and Machine Learning hook support. Please follow instructions in his repostory. He maintains two repositories:
|
||||
|
||||
* If you want to run the latest stable release, please use his `zoneminder repository <https://github.com/dlandon/zoneminder>`__.
|
||||
* If you want to run the latest stable release, please use his `zoneminder machine learning repository <https://github.com/dlandon/zoneminder.machine.learning>`__.
|
||||
* If you want to run the latest zoneminder master, please use his `zoneminder master repository <https://github.com/dlandon/zoneminder.master-docker>`__.
|
||||
|
||||
In both cases, instructions are provided in the repo README files.
|
||||
|
|
|
@ -86,7 +86,7 @@ Source Path
|
|||
Use this field to enter the full URL of the stream or file your camera supports. This is usually an RTSP url. There are several methods to learn this:
|
||||
|
||||
* Check the documentation that came with your camera
|
||||
* Look for your camera in the hardware compatibilty list in the `hardware compatibility wiki <https://wiki.zoneminder.com/Hardware_Compatibility_List>`__
|
||||
* Look for your camera in the hardware compatibility list in the `hardware compatibility wiki <https://wiki.zoneminder.com/Hardware_Compatibility_List>`__
|
||||
* Try ZoneMinder's new ONVIF probe feature
|
||||
* Download and install the `ONVIF Device Manager <https://sourceforge.net/projects/onvifdm/>`__ onto a Windows machine
|
||||
* Use Google to find third party sites, such as ispy, which document this information
|
||||
|
@ -179,12 +179,12 @@ Storage Tab
|
|||
The storage section allows for each monitor to configure if and how video and audio are recorded.
|
||||
|
||||
Save JPEGs
|
||||
Records video in individual JPEG frames. Storing JPEG frames requires more storage space than h264 but it allows to view an event anytime while it is being recorded.
|
||||
Records video in individual JPEG frames. Storing JPEG frames requires more storage space than h264 but it allows one to view an event anytime while it is being recorded.
|
||||
|
||||
* Disabled – video is not recorded as JPEG frames. If this setting is selected, then "Video Writer" should be enabled otherwise there is no video recording at all.
|
||||
* Frames only – video is recorded in individual JPEG frames.
|
||||
* Analysis images only (if available) – video is recorded in invidual JPEG frames with an overlay of the motion detection analysis information. Note that this overlay remains permanently visible in the frames.
|
||||
* Frames + Analysis images (if available) – video is recorded twice, once as normal individual JPEG frames and once in invidual JPEG frames with analysis information overlaid.
|
||||
* Analysis images only (if available) – video is recorded in individual JPEG frames with an overlay of the motion detection analysis information. Note that this overlay remains permanently visible in the frames.
|
||||
* Frames + Analysis images (if available) – video is recorded twice, once as normal individual JPEG frames and once in individual JPEG frames with analysis information overlaid.
|
||||
|
||||
Video Writer
|
||||
Records video in real video format. It provides much better compression results than saving JPEGs, thus longer video history can be stored.
|
||||
|
|
|
@ -34,7 +34,7 @@ Here is what the filter window looks like
|
|||
* Update used disk space: calculates how much disk space is currently taken by the event and updates the db record.
|
||||
* Create video for all matches: creates a video file of all the events that match
|
||||
* Create video for all matches: ffmpeg will be used to create a video file (mp4) out of all the stored jpgs if using jpeg storage.
|
||||
* Execute command on all matches: Allows you to execute any arbitrary command on the matched events. You can use replacement tokens as subsequent arguents to the command, the last argument will be the absolute path to the event, preceeded by replacement arguents. eg: /usr/bin/script.sh %MN% will excecute as /usr/bin/script.sh MonitorName /path/to/event. Please note that urls may contain characters like & that need quoting. So you may need to put quotes around them like /usr/bin/scrupt.sh "%MN%".
|
||||
* Execute command on all matches: Allows you to execute any arbitrary command on the matched events. You can use replacement tokens as subsequent arguents to the command, the last argument will be the absolute path to the event, preceded by replacement arguents. eg: /usr/bin/script.sh %MN% will execute as /usr/bin/script.sh MonitorName /path/to/event. Please note that urls may contain characters like & that need quoting. So you may need to put quotes around them like /usr/bin/scrupt.sh "%MN%".
|
||||
* Delete all matches: Deletes all the matched events.
|
||||
* Email details of all matches: Sends an email to the configured address with details about the event.
|
||||
* Copy all matches: copies the event files to another location, specified in the Copy To dropdown. The other location must be setup in the Storage Tab under options.
|
||||
|
|
|
@ -53,7 +53,7 @@ This screen is called the "console" screen in ZoneMinder and shows a summary of
|
|||
* **B**: This brings up a color coded log window that shows various system and component level logs. This window is useful if you are trying to diagnose issues. Refer to :doc:`logging`.
|
||||
* **C**: ZoneMinder allows you to group monitors for logical separation. This option lets you create new groups, associate monitors to them and edit/delete existing groups.
|
||||
* **D**: Filters are a powerful mechanism to perform actions when certain conditions are met. ZoneMinder comes with some preset filters that keep a tab of disk space and others. Many users create their own filters for more advanced actions like sending emails when certain events occur and more. Refer to :doc:`filterevents`.
|
||||
* **E**: The Cycle option allows you to rotate between live views of each cofigured monitor.
|
||||
* **E**: The Cycle option allows you to rotate between live views of each configured monitor.
|
||||
* **F**: The Montage option shows a collage of your monitors. You can customize them including moving them around.
|
||||
* **G**: Montage Review allows you to simultaneously view past events for different monitors. Note that this is a very resource intensive page and its performance will vary based on your system capabilities.
|
||||
* **H**: Audit Events Report is more of a power user feature. This option looks for recording gaps in events and recording issues in mp4 files.
|
||||
|
|
|
@ -30,7 +30,7 @@ This screen allows you to configure various permissions on a per user basis. The
|
|||
.. note:: if you are using zmNinja, users are required to have 'View' access to system because multi-server information is only available as part of this permission
|
||||
|
||||
- Bandwidth
|
||||
- Specifies the maximum bandwith that this user can configure (Low, Medium or High)
|
||||
- Specifies the maximum bandwidth that this user can configure (Low, Medium or High)
|
||||
|
||||
- API enabled
|
||||
- Specifies if the ZoneMinder API is enabled for this user (needs to be on, if you are using a mobile app such as zmNinja)
|
||||
|
@ -42,4 +42,4 @@ Here is an example of a restricted user, for example:
|
|||
|
||||
.. image:: images/Options_Users_Example.png
|
||||
|
||||
This user "home" is enabled, can view live streams and events, but only from "DoorBell" and "DeckCamera". This user also cannot control PTZ.
|
||||
This user "home" is enabled, can view live streams and events, but only from "DoorBell" and "DeckCamera". This user also cannot control PTZ.
|
||||
|
|
|
@ -147,7 +147,7 @@ This attribute is of type L<SOAP::WSDL::XSD::Typelib::Builtin::integer|SOAP::WSD
|
|||
|
||||
=item * Cells
|
||||
|
||||
A “1” denotes a cell where motion is detected and a “0” an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
A "1" denotes a cell where motion is detected and a "0" an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -2709,7 +2709,7 @@ Returns a L<ONVIF::Device::Elements::GetNetworkInterfacesResponse|ONVIF::Device:
|
|||
|
||||
=head3 SetNetworkInterfaces
|
||||
|
||||
For interoperability with a client unaware of the IEEE 802.11 extension a device shall retain its IEEE 802.11 configuration if the IEEE 802.11 configuration element isn’t present in the request.
|
||||
For interoperability with a client unaware of the IEEE 802.11 extension a device shall retain its IEEE 802.11 configuration if the IEEE 802.11 configuration element isn't present in the request.
|
||||
|
||||
Returns a L<ONVIF::Device::Elements::SetNetworkInterfacesResponse|ONVIF::Device::Elements::SetNetworkInterfacesResponse> object.
|
||||
|
||||
|
@ -3093,7 +3093,7 @@ Returns a L<ONVIF::Device::Elements::SetRelayOutputStateResponse|ONVIF::Device::
|
|||
|
||||
=head3 SendAuxiliaryCommand
|
||||
|
||||
tt:IRLamp|Auto – Request to configure an IR illuminator attached to the unit so that it automatically turns ON and OFF. A device that indicates auxiliary service capability shall support this command.
|
||||
tt:IRLamp|Auto - Request to configure an IR illuminator attached to the unit so that it automatically turns ON and OFF. A device that indicates auxiliary service capability shall support this command.
|
||||
|
||||
Returns a L<ONVIF::Device::Elements::SendAuxiliaryCommandResponse|ONVIF::Device::Elements::SendAuxiliaryCommandResponse> object.
|
||||
|
||||
|
@ -3288,7 +3288,7 @@ Returns a L<ONVIF::Device::Elements::GetSystemUrisResponse|ONVIF::Device::Elemen
|
|||
|
||||
=head3 StartFirmwareUpgrade
|
||||
|
||||
The value of the Content-Type header in the HTTP POST request shall be “application/octetstream”.
|
||||
The value of the Content-Type header in the HTTP POST request shall be "application/octetstream".
|
||||
|
||||
Returns a L<ONVIF::Device::Elements::StartFirmwareUpgradeResponse|ONVIF::Device::Elements::StartFirmwareUpgradeResponse> object.
|
||||
|
||||
|
@ -3298,7 +3298,7 @@ Returns a L<ONVIF::Device::Elements::StartFirmwareUpgradeResponse|ONVIF::Device:
|
|||
|
||||
=head3 StartSystemRestore
|
||||
|
||||
The value of the Content-Type header in the HTTP POST request shall be “application/octetstream”.
|
||||
The value of the Content-Type header in the HTTP POST request shall be "application/octetstream".
|
||||
|
||||
Returns a L<ONVIF::Device::Elements::StartSystemRestoreResponse|ONVIF::Device::Elements::StartSystemRestoreResponse> object.
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ This attribute is of type L<SOAP::WSDL::XSD::Typelib::Builtin::integer|SOAP::WSD
|
|||
|
||||
=item * Cells
|
||||
|
||||
A “1” denotes a cell where motion is detected and a “0” an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
A "1" denotes a cell where motion is detected and a "0" an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -2147,7 +2147,7 @@ Returns a L<ONVIF::Media::Elements::GetAudioOutputsResponse|ONVIF::Media::Elemen
|
|||
|
||||
=head3 CreateProfile
|
||||
|
||||
This operation creates a new empty media profile. The media profile shall be created in the device and shall be persistent (remain after reboot). A created profile shall be deletable and a device shall set the “fixed” attribute to false in the returned Profile.
|
||||
This operation creates a new empty media profile. The media profile shall be created in the device and shall be persistent (remain after reboot). A created profile shall be deletable and a device shall set the "fixed" attribute to false in the returned Profile.
|
||||
|
||||
Returns a L<ONVIF::Media::Elements::CreateProfileResponse|ONVIF::Media::Elements::CreateProfileResponse> object.
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ This attribute is of type L<SOAP::WSDL::XSD::Typelib::Builtin::integer|SOAP::WSD
|
|||
|
||||
=item * Cells
|
||||
|
||||
A “1” denotes a cell where motion is detected and a “0” an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
A "1" denotes a cell where motion is detected and a "0" an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -987,7 +987,7 @@ Returns a L<ONVIF::PTZ::Elements::GotoHomePositionResponse|ONVIF::PTZ::Elements:
|
|||
|
||||
=head3 SetHomePosition
|
||||
|
||||
Operation to save current position as the home position. The SetHomePosition command returns with a failure if the “home” position is fixed and cannot be overwritten. If the SetHomePosition is successful, it is possible to recall the Home Position with the GotoHomePosition command.
|
||||
Operation to save current position as the home position. The SetHomePosition command returns with a failure if the "home" position is fixed and cannot be overwritten. If the SetHomePosition is successful, it is possible to recall the Home Position with the GotoHomePosition command.
|
||||
|
||||
Returns a L<ONVIF::PTZ::Elements::SetHomePositionResponse|ONVIF::PTZ::Elements::SetHomePositionResponse> object.
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ This attribute is of type L<SOAP::WSDL::XSD::Typelib::Builtin::integer|SOAP::WSD
|
|||
|
||||
=item * Cells
|
||||
|
||||
A “1” denotes a cell where motion is detected and a “0” an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
A "1" denotes a cell where motion is detected and a "0" an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ of the corresponding class can be passed instead of the marked hash ref.
|
|||
You may pass any combination of objects, hash and list refs to these
|
||||
methods, as long as you meet the structure.
|
||||
|
||||
List items (i.e. multiple occurences) are not displayed in the synopsis.
|
||||
List items (i.e. multiple occurrences) are not displayed in the synopsis.
|
||||
You may generally pass a list ref of hash refs (or objects) instead of a hash
|
||||
ref - this may result in invalid XML if used improperly, though. Note that
|
||||
SOAP::WSDL always expects list references at maximum depth position.
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1064,7 +1064,7 @@ our @options = (
|
|||
},
|
||||
{
|
||||
name => 'ZM_FFMPEG_FORMATS',
|
||||
default => 'mpg mpeg wmv asf avi* mov swf 3gp**',
|
||||
default => 'mp4* mpg mpeg wmv asf avi mov swf 3gp**',
|
||||
description => 'Formats to allow for ffmpeg video generation',
|
||||
help => q`
|
||||
Ffmpeg can generate video in many different formats. This
|
||||
|
|
|
@ -29,6 +29,7 @@ use strict;
|
|||
use warnings;
|
||||
|
||||
require ZoneMinder::Base;
|
||||
require ZoneMinder::Object;
|
||||
require ZoneMinder::Monitor;
|
||||
|
||||
our $VERSION = $ZoneMinder::Base::VERSION;
|
||||
|
@ -42,24 +43,116 @@ our $VERSION = $ZoneMinder::Base::VERSION;
|
|||
use ZoneMinder::Logger qw(:all);
|
||||
use ZoneMinder::Database qw(:all);
|
||||
|
||||
use parent qw(ZoneMinder::Object);
|
||||
|
||||
use vars qw/ $table $primary_key %fields $serial %defaults $debug/;
|
||||
$table = 'Controls';
|
||||
$serial = $primary_key = 'Id';
|
||||
%fields = map { $_ => $_ } qw(
|
||||
Id
|
||||
Name
|
||||
Type
|
||||
Protocol
|
||||
CanWake
|
||||
CanSleep
|
||||
CanReset
|
||||
CanReboot
|
||||
CanZoom
|
||||
CanAutoZoom
|
||||
CanZoomAbs
|
||||
CanZoomRel
|
||||
CanZoomCon
|
||||
MinZoomRange
|
||||
MaxZoomRange
|
||||
MinZoomStep
|
||||
MaxZoomStep
|
||||
HasZoomSpeed
|
||||
MinZoomSpeed
|
||||
MaxZoomSpeed
|
||||
CanFocus
|
||||
CanAutoFocus
|
||||
CanFocusAbs
|
||||
CanFocusRel
|
||||
CanFocusCon
|
||||
MinFocusRange
|
||||
MaxFocusRange
|
||||
MinFocusStep
|
||||
MaxFocusStep
|
||||
HasFocusSpeed
|
||||
MinFocusSpeed
|
||||
MaxFocusSpeed
|
||||
CanIris
|
||||
CanAutoIris
|
||||
CanIrisAbs
|
||||
CanIrisRel
|
||||
CanIrisCon
|
||||
MinIrisRange
|
||||
MaxIrisRange
|
||||
MinIrisStep
|
||||
MaxIrisStep
|
||||
HasIrisSpeed
|
||||
MinIrisSpeed
|
||||
MaxIrisSpeed
|
||||
CanGain
|
||||
CanAutoGain
|
||||
CanGainAbs
|
||||
CanGainRel
|
||||
CanGainCon
|
||||
MinGainRange
|
||||
MaxGainRange
|
||||
MinGainStep
|
||||
MaxGainStep
|
||||
HasGainSpeed
|
||||
MinGainSpeed
|
||||
MaxGainSpeed
|
||||
CanWhite
|
||||
CanAutoWhite
|
||||
CanWhiteAbs
|
||||
CanWhiteRel
|
||||
CanWhiteCon
|
||||
MinWhiteRange
|
||||
MaxWhiteRange
|
||||
MinWhiteStep
|
||||
MaxWhiteStep
|
||||
HasWhiteSpeed
|
||||
MinWhiteSpeed
|
||||
MaxWhiteSpeed
|
||||
HasPresets
|
||||
NumPresets
|
||||
HasHomePreset
|
||||
CanSetPresets
|
||||
CanMove
|
||||
CanMoveDiag
|
||||
CanMoveMap
|
||||
CanMoveAbs
|
||||
CanMoveRel
|
||||
CanMoveCon
|
||||
CanPan
|
||||
MinPanRange
|
||||
MaxPanRange
|
||||
MinPanStep
|
||||
MaxPanStep
|
||||
HasPanSpeed
|
||||
MinPanSpeed
|
||||
MaxPanSpeed
|
||||
HasTurboPan
|
||||
TurboPanSpeed
|
||||
CanTilt
|
||||
MinTiltRange
|
||||
MaxTiltRange
|
||||
MinTiltStep
|
||||
MaxTiltStep
|
||||
HasTiltSpeed
|
||||
MinTiltSpeed
|
||||
MaxTiltSpeed
|
||||
HasTurboTilt
|
||||
TurboTiltSpeed
|
||||
CanAutoScan
|
||||
NumScanPaths
|
||||
);
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
if ( !defined($id) ) {
|
||||
Fatal('No monitor defined when invoking protocol '.$class);
|
||||
}
|
||||
my $self = {};
|
||||
$self->{name} = $class;
|
||||
$self->{id} = $id;
|
||||
bless($self, $class);
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub DESTROY {
|
||||
}
|
||||
|
||||
sub AUTOLOAD {
|
||||
my $self = shift;
|
||||
my $class = ref($self);
|
||||
|
@ -79,24 +172,24 @@ sub AUTOLOAD {
|
|||
|
||||
sub getKey {
|
||||
my $self = shift;
|
||||
return $self->{id};
|
||||
return $self->{Id};
|
||||
}
|
||||
|
||||
sub open {
|
||||
my $self = shift;
|
||||
Fatal('No open method defined for protocol '.$self->{name});
|
||||
Fatal('No open method defined for protocol '.$self->{Protocol});
|
||||
}
|
||||
|
||||
sub close {
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
Debug('No close method defined for protocol '.$self->{name});
|
||||
Debug('No close method defined for protocol '.$self->{Protocol});
|
||||
}
|
||||
|
||||
sub loadMonitor {
|
||||
my $self = shift;
|
||||
if ( !$self->{Monitor} ) {
|
||||
if ( !($self->{Monitor} = ZoneMinder::Monitor->find_one(Id=>$self->{id})) ) {
|
||||
if ( !($self->{Monitor} = ZoneMinder::Monitor->find_one(Id=>$self->{MonitorId})) ) {
|
||||
Fatal('Monitor id '.$self->{id}.' not found');
|
||||
}
|
||||
if ( defined($self->{Monitor}->{AutoStopTimeout}) ) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -41,120 +41,133 @@ our @ISA = qw(ZoneMinder::Control);
|
|||
|
||||
use ZoneMinder::Logger qw(:all);
|
||||
use ZoneMinder::Config qw(:all);
|
||||
use ZoneMinder::General qw(:all);
|
||||
|
||||
use Time::HiRes qw( usleep );
|
||||
use URI::Encode qw(uri_encode);
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
our $REALM = '';
|
||||
our $PROTOCOL = 'http://';
|
||||
our $USERNAME = 'admin';
|
||||
our $PASSWORD = '';
|
||||
our $ADDRESS = '';
|
||||
our $BASE_URL = '';
|
||||
|
||||
$self->loadMonitor();
|
||||
Debug( "Camera open" );
|
||||
use LWP::UserAgent;
|
||||
$self->{ua} = LWP::UserAgent->new;
|
||||
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
|
||||
sub open {
|
||||
my $self = shift;
|
||||
$self->loadMonitor();
|
||||
|
||||
$self->{state} = 'open';
|
||||
if (($self->{Monitor}->{ControlAddress} =~ /^(?<PROTOCOL>https?:\/\/)?(?<USERNAME>[^:@]+)?:?(?<PASSWORD>[^\/@]+)?@?(?<ADDRESS>.*)$/)) {
|
||||
$PROTOCOL = $+{PROTOCOL} if $+{PROTOCOL};
|
||||
$USERNAME = $+{USERNAME} if $+{USERNAME};
|
||||
$PASSWORD = $+{PASSWORD} if $+{PASSWORD};
|
||||
$ADDRESS = $+{ADDRESS} if $+{ADDRESS};
|
||||
} else {
|
||||
Error('Failed to parse auth from address ' . $self->{Monitor}->{ControlAddress});
|
||||
$ADDRESS = $self->{Monitor}->{ControlAddress};
|
||||
}
|
||||
if ( !($ADDRESS =~ /:/) ) {
|
||||
Error('You generally need to also specify the port. I will append :80');
|
||||
$ADDRESS .= ':80';
|
||||
}
|
||||
$BASE_URL = $PROTOCOL.($USERNAME?$USERNAME.':'.$PASSWORD.'@':'').$ADDRESS;
|
||||
|
||||
use LWP::UserAgent;
|
||||
$self->{ua} = LWP::UserAgent->new;
|
||||
$self->{ua}->agent( 'ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION );
|
||||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
sub close {
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $msg = shift;
|
||||
my $msg_len = length($msg);
|
||||
sub sendCmd {
|
||||
my ($self, $cmd, $speedcmd) = @_;
|
||||
|
||||
Debug( $msg."[".$msg_len."]" );
|
||||
$self->printMsg( $speedcmd, 'Tx' );
|
||||
$self->printMsg( $cmd, 'Tx' );
|
||||
|
||||
my $req = HTTP::Request->new( GET => $BASE_URL."/cgi-bin/camctrl/eCamCtrl.cgi?stream=0&$speedcmd&$cmd");
|
||||
my $res = $self->{ua}->request($req);
|
||||
|
||||
if (!$res->is_success) {
|
||||
Error('Request failed: '.$res->status_line().' (URI: '.$req->as_string().')');
|
||||
}
|
||||
return $res->is_success;
|
||||
}
|
||||
|
||||
sub sendCmd
|
||||
{
|
||||
my ($self, $cmd, $speedcmd) = @_;
|
||||
|
||||
my $result = undef;
|
||||
|
||||
printMsg( $speedcmd, "Tx" );
|
||||
printMsg( $cmd, "Tx" );
|
||||
|
||||
my $req = HTTP::Request->new( GET => "http://" . $self->{Monitor}->{ControlAddress} . "/cgi-bin/camctrl/eCamCtrl.cgi?stream=0&$speedcmd&$cmd" );
|
||||
my $res = $self->{ua}->request($req);
|
||||
|
||||
if ( $res->is_success )
|
||||
{
|
||||
$result = !undef;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Request failed: '" . $res->status_line() . "' (URI: '" . $req->as_string() . "')" );
|
||||
}
|
||||
|
||||
return( $result );
|
||||
sub moveConUp {
|
||||
my ($self, $params) = @_;
|
||||
my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6);
|
||||
$self->sendCmd( 'move=up', $speed );
|
||||
}
|
||||
|
||||
sub moveConUp
|
||||
{
|
||||
my ($self, $params) = @_;
|
||||
my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6);
|
||||
Debug( "Move Up" );
|
||||
$self->sendCmd( 'move=up', $speed );
|
||||
sub moveConDown {
|
||||
my ($self, $params) = @_;
|
||||
my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6);
|
||||
$self->sendCmd( 'move=down', $speed );
|
||||
}
|
||||
|
||||
sub moveConDown
|
||||
{
|
||||
my ($self, $params) = @_;
|
||||
my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6);
|
||||
Debug( "Move Down" );
|
||||
$self->sendCmd( 'move=down', $speed );
|
||||
sub moveConLeft {
|
||||
my ($self, $params) = @_;
|
||||
my $speed = 'speedpan=-' . $params->{panspeed};
|
||||
$self->sendCmd( 'move=left', $speed );
|
||||
}
|
||||
|
||||
sub moveConLeft
|
||||
{
|
||||
my ($self, $params) = @_;
|
||||
my $speed = 'speedpan=-' . $params->{panspeed};
|
||||
Debug( "Move Left" );
|
||||
$self->sendCmd( 'move=left', $speed );
|
||||
sub moveConRight {
|
||||
my ($self, $params) = @_;
|
||||
my $speed = 'speedpan=' . ($params->{panspeed} - 6);
|
||||
$self->sendCmd( 'move=right', $speed );
|
||||
}
|
||||
|
||||
sub moveConRight
|
||||
{
|
||||
my ($self, $params) = @_;
|
||||
my $speed = 'speedpan=' . ($params->{panspeed} - 6);
|
||||
Debug( "Move Right" );
|
||||
$self->sendCmd( 'move=right', $speed );
|
||||
sub moveStop {
|
||||
my $self = shift;
|
||||
Debug( "Move Stop: not implemented" );
|
||||
# not implemented
|
||||
}
|
||||
|
||||
sub moveStop
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Move Stop" );
|
||||
# not implemented
|
||||
sub zoomConTele {
|
||||
my ($self, $params) = @_;
|
||||
my $speed = 'speedzoom=' . ($params->{speed} - 6);
|
||||
$self->sendCmd( 'zoom=tele', $speed );
|
||||
}
|
||||
|
||||
sub zoomConTele
|
||||
{
|
||||
my ($self, $params) = @_;
|
||||
my $speed = 'speedzoom=' . ($params->{speed} - 6);
|
||||
Debug( "Zoom In" );
|
||||
$self->sendCmd( 'zoom=tele', $speed );
|
||||
sub zoomConWide {
|
||||
my ($self, $params) = @_;
|
||||
my $speed = 'speedzoom=' . ($params->{speed} - 6);
|
||||
$self->sendCmd( 'zoom=wide', $speed );
|
||||
}
|
||||
|
||||
sub zoomConWide
|
||||
{
|
||||
my ($self, $params) = @_;
|
||||
my $speed = 'speedzoom=' . ($params->{speed} - 6);
|
||||
Debug( "Zoom Out" );
|
||||
$self->sendCmd( 'zoom=wide', $speed );
|
||||
sub reset {
|
||||
my $self = shift;
|
||||
$self->sendCmd( 'move=home' );
|
||||
}
|
||||
|
||||
sub reset
|
||||
{
|
||||
my $self = shift;
|
||||
Debug( "Camera Reset" );
|
||||
$self->sendCmd( 'move=home' );
|
||||
sub get_config {
|
||||
my $self = shift;
|
||||
|
||||
my $url = $BASE_URL.'/cgi-bin/admin/lsctrl.cgi?cmd=queryStatus&retType=javascript';
|
||||
my $req = new HTTP::Request(GET => $url);
|
||||
my $response = $self->{ua}->request($req);
|
||||
if ( $response->is_success() ) {
|
||||
my $resp = $response->decoded_content;
|
||||
return ZoneMinder::General::parseNameEqualsValueToHash($resp);
|
||||
}
|
||||
Warn("Failed to get config from $url: " . $response->status_line());
|
||||
return;
|
||||
} # end sub get_config
|
||||
|
||||
sub set_config {
|
||||
my $self = shift;
|
||||
my $diff = shift;
|
||||
|
||||
my $url = $BASE_URL.'/cgi-bin/'.$USERNAME.'/setparam.cgi?'.
|
||||
join('&', map { $_.'='.uri_encode($$diff{$_}) } keys %$diff);
|
||||
my $response = $self->{ua}->get($url);
|
||||
Debug($response->content);
|
||||
return $response->is_success();
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -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);
|
||||
|
@ -584,6 +585,7 @@ sub DiskSpace {
|
|||
return $_[0]{DiskSpace};
|
||||
}
|
||||
|
||||
# Icon: I removed the locking from this. So we now have an assumption that the Event object is up to date.
|
||||
sub CopyTo {
|
||||
my ( $self, $NewStorage ) = @_;
|
||||
|
||||
|
@ -600,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;
|
||||
|
@ -614,16 +616,12 @@ sub CopyTo {
|
|||
Debug("$NewPath is good");
|
||||
}
|
||||
|
||||
$ZoneMinder::Database::dbh->begin_work();
|
||||
$self->lock_and_load();
|
||||
# data is reloaded, so need to check that the move hasn't already happened.
|
||||
if ( $$self{StorageId} == $$NewStorage{Id} ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
return 'Event has already been moved by someone else.';
|
||||
}
|
||||
|
||||
if ( $$OldStorage{Id} != $$self{StorageId} ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
return 'Old Storage path changed, Event has moved somewhere else.';
|
||||
}
|
||||
|
||||
|
@ -661,39 +659,21 @@ sub CopyTo {
|
|||
}
|
||||
|
||||
my $event_path = $subpath.$self->RelativePath();
|
||||
if ( 0 ) { # Not neccessary
|
||||
Debug("Making directory $event_path/");
|
||||
if ( !$bucket->add_key($event_path.'/', '') ) {
|
||||
Warning("Unable to add key for $event_path/ :". $s3->err . ': '. $s3->errstr());
|
||||
}
|
||||
}
|
||||
|
||||
my @files = glob("$OldPath/*");
|
||||
Debug("Files to move @files");
|
||||
foreach my $file ( @files ) {
|
||||
foreach my $file (@files) {
|
||||
next if $file =~ /^\./;
|
||||
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
|
||||
($file) = ($file =~ /^(.*)$/); # De-taint
|
||||
my $starttime = [gettimeofday];
|
||||
Debug("Moving file $file to $NewPath");
|
||||
my $size = -s $file;
|
||||
if ( ! $size ) {
|
||||
if (!$size) {
|
||||
Info('Not moving file with 0 size');
|
||||
}
|
||||
if ( 0 ) {
|
||||
my $file_contents = File::Slurp::read_file($file);
|
||||
if ( ! $file_contents ) {
|
||||
die 'Loaded empty file, but it had a size. Giving up';
|
||||
}
|
||||
|
||||
my $filename = $event_path.'/'.File::Basename::basename($file);
|
||||
if ( ! $bucket->add_key($filename, $file_contents) ) {
|
||||
die "Unable to add key for $filename : ".$s3->err . ': ' . $s3->errstr;
|
||||
}
|
||||
} else {
|
||||
my $filename = $event_path.'/'.File::Basename::basename($file);
|
||||
if ( ! $bucket->add_key_filename($filename, $file) ) {
|
||||
die "Unable to add key for $filename " . $s3->err . ': '. $s3->errstr;
|
||||
}
|
||||
my $filename = $event_path.'/'.File::Basename::basename($file);
|
||||
if (!$bucket->add_key_filename($filename, $file)) {
|
||||
die "Unable to add key for $filename " . $s3->err . ': '. $s3->errstr;
|
||||
}
|
||||
|
||||
my $duration = tv_interval($starttime);
|
||||
|
@ -704,16 +684,15 @@ sub CopyTo {
|
|||
};
|
||||
Error($@) if $@;
|
||||
} else {
|
||||
Error("Unable to parse S3 Url into it's component parts.");
|
||||
Error('Unable to parse S3 Url into it\'s component parts.');
|
||||
}
|
||||
#die $@ if $@;
|
||||
} # end if Url
|
||||
} # end if s3
|
||||
|
||||
my $error = '';
|
||||
if ( !$moved ) {
|
||||
if (!$moved) {
|
||||
File::Path::make_path($NewPath, {error => \my $err});
|
||||
if ( @$err ) {
|
||||
if (@$err) {
|
||||
for my $diag (@$err) {
|
||||
my ($file, $message) = %$diag;
|
||||
next if $message eq 'File exists';
|
||||
|
@ -724,23 +703,16 @@ sub CopyTo {
|
|||
}
|
||||
}
|
||||
}
|
||||
if ( $error ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
return $error;
|
||||
}
|
||||
return $error if $error;
|
||||
my @files = glob("$OldPath/*");
|
||||
if ( ! @files ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
return 'No files to move.';
|
||||
}
|
||||
return 'No files to move.' if !@files;
|
||||
|
||||
for my $file (@files) {
|
||||
next if $file =~ /^\./;
|
||||
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
|
||||
($file) = ($file =~ /^(.*)$/); # De-taint
|
||||
my $starttime = [gettimeofday];
|
||||
Debug("Moving file $file to $NewPath");
|
||||
my $size = -s $file;
|
||||
if ( ! File::Copy::copy( $file, $NewPath ) ) {
|
||||
if (!File::Copy::copy($file, $NewPath)) {
|
||||
$error .= "Copy failed: for $file to $NewPath: $!";
|
||||
last;
|
||||
}
|
||||
|
@ -749,34 +721,38 @@ sub CopyTo {
|
|||
} # end foreach file.
|
||||
} # end if ! moved
|
||||
|
||||
if ( $error ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
return $error;
|
||||
}
|
||||
return $error;
|
||||
} # end sub CopyTo
|
||||
|
||||
sub MoveTo {
|
||||
my ( $self, $NewStorage ) = @_;
|
||||
my ($self, $NewStorage) = @_;
|
||||
|
||||
if ( !$self->canEdit() ) {
|
||||
if (!$self->canEdit()) {
|
||||
Warning('No permission to move event.');
|
||||
return 'No permission to move event.';
|
||||
}
|
||||
|
||||
my $OldStorage = $self->Storage(undef);
|
||||
my $was_in_transaction = !$ZoneMinder::Database::dbh->{AutoCommit};
|
||||
$ZoneMinder::Database::dbh->begin_work() if !$was_in_transaction;
|
||||
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);
|
||||
if (!$error) {
|
||||
# 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
|
||||
}
|
||||
$ZoneMinder::Database::dbh->commit() if !$was_in_transaction;
|
||||
return $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();
|
||||
if ( $error ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
return $error;
|
||||
}
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
$self->delete_files($OldStorage);
|
||||
return $error;
|
||||
} # end sub MoveTo
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
# ==========================================================================
|
||||
#
|
||||
# ZoneMinder Event_Summary Module
|
||||
# Copyright (C) 2020 ZoneMinder
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# ==========================================================================
|
||||
#
|
||||
# This module contains the common definitions and functions used by the rest
|
||||
# of the ZoneMinder scripts
|
||||
#
|
||||
package ZoneMinder::Event_Summary;
|
||||
|
||||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
require ZoneMinder::Base;
|
||||
require ZoneMinder::Object;
|
||||
|
||||
#our @ISA = qw(Exporter ZoneMinder::Base);
|
||||
use parent qw(ZoneMinder::Object);
|
||||
|
||||
use vars qw/ $table $primary_key %fields $serial %defaults $debug/;
|
||||
$table = 'Event_Summaries';
|
||||
$serial = $primary_key = 'MonitorId';
|
||||
%fields = map { $_ => $_ } qw(
|
||||
MonitorId
|
||||
TotalEvents
|
||||
TotalEventDiskSpace
|
||||
HourEvents
|
||||
HourEventDiskSpace
|
||||
DayEvents
|
||||
DayEventDiskSpace
|
||||
WeekEvents
|
||||
WeekEventDiskSpace
|
||||
MonthEvents
|
||||
MonthEventDiskSpace
|
||||
ArchivedEvents
|
||||
ArchivedEventDiskSpace
|
||||
);
|
||||
|
||||
%defaults = (
|
||||
TotalEvents => undef,
|
||||
TotalEventDiskSpace => undef,
|
||||
HourEvents => undef,
|
||||
HourEventDiskSpace => undef,
|
||||
DayEvents => undef,
|
||||
DayEventDiskSpace => undef,
|
||||
WeekEvents => undef,
|
||||
WeekEventDiskSpace => undef,
|
||||
MonthEvents => undef,
|
||||
MonthEventDiskSpace => undef,
|
||||
ArchivedEvents => undef,
|
||||
ArchivedEventDiskSpace => undef,
|
||||
);
|
||||
|
||||
sub Monitor {
|
||||
return new ZoneMinder::Monitor( $_[0]{MonitorId} );
|
||||
} # end sub Monitor
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
ZoneMinder::Event_Summary - Perl Class for Event Summaries
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use ZoneMinder::Event_Summary;
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Isaac Connor, E<lt>isaac@zoneminder.comE<gt>
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (C) 2001-2017 ZoneMinder LLC
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the same terms as Perl itself, either Perl version 5.8.3 or,
|
||||
at your option, any later version of Perl 5 you may have available.
|
||||
|
||||
|
||||
=cut
|
|
@ -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 ? 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'
|
||||
|
@ -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};
|
||||
|
|
|
@ -31,6 +31,8 @@ our %EXPORT_TAGS = (
|
|||
systemStatus
|
||||
packageControl
|
||||
daemonControl
|
||||
parseNameEqualsValueToHash
|
||||
hash_diff
|
||||
) ]
|
||||
);
|
||||
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
|
||||
|
@ -534,6 +536,42 @@ sub jsonDecode {
|
|||
return $result;
|
||||
}
|
||||
|
||||
sub parseNameEqualsValueToHash {
|
||||
my %settings;
|
||||
foreach my $line ( split ( /\r?\n/, $_[0] ) ) {
|
||||
next if ! $line;
|
||||
next if ! ( $line =~ /=/ );
|
||||
my ($name, $value ) = split('=', $line);
|
||||
$value =~ s/^'//;
|
||||
$value =~ s/'$//;
|
||||
$settings{$name} = defined $value ? $value : '';
|
||||
}
|
||||
return %settings;
|
||||
}
|
||||
|
||||
sub hash_diff {
|
||||
# assumes keys of second hash are all in the first hash
|
||||
my ( $settings, $defaults ) = @_;
|
||||
my %updates;
|
||||
|
||||
foreach my $setting ( keys %{$settings} ) {
|
||||
next if ! exists $$defaults{$setting};
|
||||
if (
|
||||
($$settings{$setting} and ! $$defaults{$setting})
|
||||
or
|
||||
(!$$settings{$setting} and $$defaults{$setting})
|
||||
or
|
||||
(
|
||||
($$settings{$setting} and $$defaults{$setting} and (
|
||||
$$settings{$setting} ne $$defaults{$setting}))
|
||||
)
|
||||
) {
|
||||
$updates{$setting} = $$defaults{$setting};
|
||||
}
|
||||
} # end foreach setting
|
||||
return %updates;
|
||||
}
|
||||
|
||||
sub packageControl {
|
||||
my $command = shift;
|
||||
my $string = $Config{ZM_PATH_BIN}.'/zmpkg.pl '.$command;
|
||||
|
@ -598,6 +636,8 @@ of the ZoneMinder scripts
|
|||
packageControl
|
||||
daemonControl
|
||||
systemStatus
|
||||
parseNameEqualsValueToHash
|
||||
hash_diff
|
||||
) ]
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -35,7 +35,9 @@ require ZoneMinder::Storage;
|
|||
require ZoneMinder::Server;
|
||||
require ZoneMinder::Memory;
|
||||
require ZoneMinder::Monitor_Status;
|
||||
require ZoneMinder::Event_Summary;
|
||||
require ZoneMinder::Zone;
|
||||
use ZoneMinder::Logger qw(:all);
|
||||
|
||||
#our @ISA = qw(Exporter ZoneMinder::Base);
|
||||
use parent qw(ZoneMinder::Object);
|
||||
|
@ -239,20 +241,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
|
||||
|
@ -266,6 +274,15 @@ sub Status {
|
|||
return $$self{Status};
|
||||
}
|
||||
|
||||
sub Event_Summary {
|
||||
my $self = shift;
|
||||
$$self{Event_Summary} = shift if @_;
|
||||
if ( ! $$self{Event_Summary} ) {
|
||||
$$self{Event_Summary} = ZoneMinder::Event_Summary->find_one(MonitorId=>$$self{Id});
|
||||
}
|
||||
return $$self{Event_Summary};
|
||||
}
|
||||
|
||||
sub connect {
|
||||
my $self = shift;
|
||||
return ZoneMinder::Memory::zmMemVerify($self);
|
||||
|
@ -313,6 +330,37 @@ sub resumeMotionDetection {
|
|||
return 1;
|
||||
}
|
||||
|
||||
sub Control {
|
||||
my $self = shift;
|
||||
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}");
|
||||
}
|
||||
} else {
|
||||
Info("No ControlId set in monitor $$self{Id}")
|
||||
}
|
||||
}
|
||||
return $$self{Control};
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
|
|
|
@ -43,18 +43,6 @@ $serial = $primary_key = 'MonitorId';
|
|||
CaptureFPS
|
||||
AnalysisFPS
|
||||
CaptureBandwidth
|
||||
TotalEvents
|
||||
TotalEventDiskSpace
|
||||
HourEvents
|
||||
HourEventDiskSpace
|
||||
DayEvents
|
||||
DayEventDiskSpace
|
||||
WeekEvents
|
||||
WeekEventDiskSpace
|
||||
MonthEvents
|
||||
MonthEventDiskSpace
|
||||
ArchivedEvents
|
||||
ArchivedEventDiskSpace
|
||||
);
|
||||
|
||||
%defaults = (
|
||||
|
@ -62,18 +50,6 @@ $serial = $primary_key = 'MonitorId';
|
|||
CaptureFPS => undef,
|
||||
AnalysisFPS => undef,
|
||||
CaptureBandwidth => undef,
|
||||
TotalEvents => undef,
|
||||
TotalEventDiskSpace => undef,
|
||||
HourEvents => undef,
|
||||
HourEventDiskSpace => undef,
|
||||
DayEvents => undef,
|
||||
DayEventDiskSpace => undef,
|
||||
WeekEvents => undef,
|
||||
WeekEventDiskSpace => undef,
|
||||
MonthEvents => undef,
|
||||
MonthEventDiskSpace => undef,
|
||||
ArchivedEvents => undef,
|
||||
ArchivedEventDiskSpace => undef,
|
||||
);
|
||||
|
||||
sub Monitor {
|
||||
|
|
|
@ -218,7 +218,7 @@ sub save {
|
|||
my $serial = eval '$'.$type.'::serial';
|
||||
my @identified_by = eval '@'.$type.'::identified_by';
|
||||
|
||||
my $ac = ZoneMinder::Database::start_transaction( $local_dbh );
|
||||
my $ac = ZoneMinder::Database::start_transaction( $local_dbh ) if $local_dbh->{AutoCommit};
|
||||
if ( ! $serial ) {
|
||||
my $insert = $force_insert;
|
||||
my %serial = eval '%'.$type.'::serial';
|
||||
|
@ -234,8 +234,8 @@ $log->debug("No serial") if $debug;
|
|||
if ( ! ( ( $_ = $local_dbh->prepare("DELETE FROM `$table` WHERE $where") ) and $_->execute( @$self{@identified_by} ) ) ) {
|
||||
$where =~ s/\?/\%s/g;
|
||||
$log->error("Error deleting: DELETE FROM $table WHERE " . sprintf($where, map { defined $_ ? $_ : 'undef' } ( @$self{@identified_by}) ).'):' . $local_dbh->errstr);
|
||||
$local_dbh->rollback();
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
||||
$local_dbh->rollback() if $ac;
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||
return $local_dbh->errstr;
|
||||
} elsif ( $debug ) {
|
||||
$log->debug("SQL succesful DELETE FROM $table WHERE $where");
|
||||
|
@ -267,8 +267,8 @@ $log->debug("No serial") if $debug;
|
|||
my $error = $local_dbh->errstr;
|
||||
$command =~ s/\?/\%s/g;
|
||||
$log->error('SQL statement execution failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $local_dbh->errstr);
|
||||
$local_dbh->rollback();
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
||||
$local_dbh->rollback() if $ac;
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||
return $error;
|
||||
} # end if
|
||||
if ( $debug or DEBUG_ALL ) {
|
||||
|
@ -282,8 +282,8 @@ $log->debug("No serial") if $debug;
|
|||
my $error = $local_dbh->errstr;
|
||||
$command =~ s/\?/\%s/g;
|
||||
$log->error('SQL failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys, @$fields{@identified_by}}) ).'):' . $local_dbh->errstr);
|
||||
$local_dbh->rollback();
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
||||
$local_dbh->rollback() if $ac;
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||
return $error;
|
||||
} # end if
|
||||
if ( $debug or DEBUG_ALL ) {
|
||||
|
@ -321,8 +321,8 @@ $log->debug("No serial") if $debug;
|
|||
$command =~ s/\?/\%s/g;
|
||||
my $error = $local_dbh->errstr;
|
||||
$log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $error);
|
||||
$local_dbh->rollback();
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
||||
$local_dbh->rollback() if $ac;
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||
return $error;
|
||||
} # end if
|
||||
if ( $debug or DEBUG_ALL ) {
|
||||
|
@ -340,8 +340,8 @@ $log->debug("No serial") if $debug;
|
|||
my $error = $local_dbh->errstr;
|
||||
$command =~ s/\?/\%s/g;
|
||||
$log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}, @sql{@$fields{@identified_by}} ) ).'):' . $error) if $log;
|
||||
$local_dbh->rollback();
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
||||
$local_dbh->rollback() if $ac;
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||
return $error;
|
||||
} # end if
|
||||
if ( $debug or DEBUG_ALL ) {
|
||||
|
@ -350,7 +350,7 @@ $log->debug("No serial") if $debug;
|
|||
} # end if
|
||||
} # end if
|
||||
} # end if
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||
#$self->load();
|
||||
#if ( $$fields{id} ) {
|
||||
#if ( ! $ZoneMinder::Object::cache{$type}{$$self{id}} ) {
|
||||
|
|
|
@ -30,7 +30,6 @@ use autouse 'Pod::Usage'=>qw(pod2usage);
|
|||
use POSIX qw/strftime EPIPE EINTR/;
|
||||
use Socket;
|
||||
use Data::Dumper;
|
||||
use Module::Load::Conditional qw{can_load};
|
||||
|
||||
use constant MAX_CONNECT_DELAY => 15;
|
||||
use constant MAX_COMMAND_WAIT => 1800;
|
||||
|
@ -102,40 +101,21 @@ if ($options{command}) {
|
|||
}
|
||||
} else {
|
||||
# The server isn't there
|
||||
my $monitor = zmDbGetMonitorAndControl($id);
|
||||
require ZoneMinder::Monitor;
|
||||
|
||||
my $monitor = ZoneMinder::Monitor->find_one(Id=>$id);
|
||||
Fatal("Unable to load control data for monitor $id") if !$monitor;
|
||||
|
||||
my $protocol = $monitor->{Protocol};
|
||||
my $control = $monitor->Control();
|
||||
|
||||
my $protocol = $control->{Protocol};
|
||||
if (!$protocol) {
|
||||
Fatal('No protocol is set in monitor. Please edit the monitor, edit control type, select the control capability and fill in the Protocol field');
|
||||
}
|
||||
|
||||
if (-x $protocol) {
|
||||
# Protocol is actually a script!
|
||||
# Holdover from previous versions
|
||||
my $command .= $protocol.' '.$arg_string;
|
||||
Debug($command);
|
||||
|
||||
my $output = qx($command);
|
||||
my $status = $? >> 8;
|
||||
if ($status || logDebugging()) {
|
||||
chomp($output);
|
||||
Debug("Output: $output");
|
||||
}
|
||||
if ($status) {
|
||||
Error("Command '$command' exited with status: $status");
|
||||
exit($status);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
Info("Starting control server $id/$protocol");
|
||||
close(CLIENT);
|
||||
|
||||
if (!can_load(modules => {'ZoneMinder::Control::'.$protocol => undef})) {
|
||||
Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR");
|
||||
}
|
||||
|
||||
my $zm_terminate = 0;
|
||||
sub TermHandler {
|
||||
Info('Received TERM, exiting');
|
||||
|
@ -150,7 +130,6 @@ if ($options{command}) {
|
|||
|
||||
$0 = $0.' --id '.$id;
|
||||
|
||||
my $control = ('ZoneMinder::Control::'.$protocol)->new($id);
|
||||
my $control_key = $control->getKey();
|
||||
$control->loadMonitor();
|
||||
|
||||
|
|
|
@ -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});
|
||||
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);
|
||||
|
||||
|
@ -665,10 +648,10 @@ sub substituteTags {
|
|||
# We have a filter and an event, do we need any more
|
||||
# monitor information?
|
||||
my $need_monitor = $text =~ /%(?:MN|MET|MEH|MED|MEW|MEN|MEA)%/;
|
||||
my $need_status = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/;
|
||||
my $need_summary = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/;
|
||||
|
||||
my $Monitor = $Event->Monitor() if $need_monitor;
|
||||
my $Status = $Monitor->Status() if $need_status;
|
||||
my $Summary = $Monitor->Event_Summary() if $need_summary;
|
||||
|
||||
# Do we need the image information too?
|
||||
my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA|EIMOD|EIMODG)%/;
|
||||
|
@ -692,19 +675,19 @@ sub substituteTags {
|
|||
}
|
||||
$rows ++;
|
||||
}
|
||||
Debug("Frames: rows: $rows first alarm frame: $first_alarm_frame max_alaarm_frame: $max_alarm_frame, score: $max_alarm_score");
|
||||
Debug("Frames: rows: $rows first alarm frame: $first_alarm_frame max_alarm_frame: $max_alarm_frame, score: $max_alarm_score");
|
||||
$sth->finish();
|
||||
}
|
||||
|
||||
my $url = $Config{ZM_URL};
|
||||
$text =~ s/%ZP%/$url/g;
|
||||
$text =~ s/%MN%/$Monitor->{Name}/g;
|
||||
$text =~ s/%MET%/$Status->{TotalEvents}/g;
|
||||
$text =~ s/%MEH%/$Status->{HourEvents}/g;
|
||||
$text =~ s/%MED%/$Status->{DayEvents}/g;
|
||||
$text =~ s/%MEW%/$Status->{WeekEvents}/g;
|
||||
$text =~ s/%MEM%/$Status->{MonthEvents}/g;
|
||||
$text =~ s/%MEA%/$Status->{ArchivedEvents}/g;
|
||||
$text =~ s/%MET%/$Summary->{TotalEvents}/g;
|
||||
$text =~ s/%MEH%/$Summary->{HourEvents}/g;
|
||||
$text =~ s/%MED%/$Summary->{DayEvents}/g;
|
||||
$text =~ s/%MEW%/$Summary->{WeekEvents}/g;
|
||||
$text =~ s/%MEM%/$Summary->{MonthEvents}/g;
|
||||
$text =~ s/%MEA%/$Summary->{ArchivedEvents}/g;
|
||||
$text =~ s/%MP%/$url?view=watch&mid=$Event->{MonitorId}/g;
|
||||
$text =~ s/%MPS%/$url?view=watch&mid=$Event->{MonitorId}&mode=stream/g;
|
||||
$text =~ s/%MPI%/$url?view=watch&mid=$Event->{MonitorId}&mode=still/g;
|
||||
|
@ -1051,9 +1034,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 +1044,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({});
|
||||
|
|
|
@ -166,13 +166,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 +195,32 @@ 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 ($$monitor{Function} eq 'None') {
|
||||
$monitor_reload_time = 0;
|
||||
next;
|
||||
}
|
||||
|
||||
if ( ! zmMemVerify($monitor) ) {
|
||||
if (!zmMemVerify($monitor)) {
|
||||
# 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.
|
||||
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})
|
||||
|
|
|
@ -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);
|
||||
|
@ -80,9 +81,6 @@ 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())) {
|
||||
|
@ -91,82 +89,67 @@ while (!$zm_terminate) {
|
|||
}
|
||||
}
|
||||
|
||||
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';
|
||||
next if $monitor->{Capturing} eq 'Ondemand';
|
||||
my $now = time();
|
||||
my $restart = 0;
|
||||
if (zmMemVerify($monitor)) {
|
||||
next if $monitor->{Capturing} eq 'Ondemand';
|
||||
|
||||
# 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}) {
|
||||
Warning(
|
||||
"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) {
|
||||
Warning('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)
|
||||
|
@ -178,25 +161,14 @@ while (!$zm_terminate) {
|
|||
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;
|
||||
." 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});
|
||||
|
|
|
@ -60,6 +60,7 @@ 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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -251,6 +251,13 @@ void zmDbQueue::process() {
|
|||
mCondition.wait(lock);
|
||||
}
|
||||
while (!mQueue.empty()) {
|
||||
if (mQueue.size() > 20) {
|
||||
Logger *log = Logger::fetch();
|
||||
Logger::Level db_level = log->databaseLevel();
|
||||
log->databaseLevel(Logger::NOLOG);
|
||||
Warning("db queue size has grown larger %zu than 20 entries", mQueue.size());
|
||||
log->databaseLevel(db_level);
|
||||
}
|
||||
std::string sql = mQueue.front();
|
||||
mQueue.pop();
|
||||
// My idea for leaving the locking around each sql statement is to allow
|
||||
|
@ -264,8 +271,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();
|
||||
}
|
||||
|
||||
|
|
102
src/zm_event.cpp
102
src/zm_event.cpp
|
@ -60,8 +60,8 @@ Event::Event(
|
|||
//snapshit_file(),
|
||||
//alarm_file(""),
|
||||
videoStore(nullptr),
|
||||
//video_name(""),
|
||||
//video_file(""),
|
||||
//video_path(""),
|
||||
last_db_frame(0),
|
||||
have_video_keyframe(false),
|
||||
//scheme
|
||||
|
@ -103,7 +103,14 @@ Event::Event(
|
|||
|
||||
// Copy it in case opening the mp4 doesn't work we can set it to another value
|
||||
save_jpegs = monitor->GetOptSaveJPEGs();
|
||||
Storage * storage = monitor->getStorage();
|
||||
Storage *storage = monitor->getStorage();
|
||||
if (monitor->GetOptVideoWriter() != 0) {
|
||||
container = monitor->OutputContainer();
|
||||
if ( container == "auto" || container == "" ) {
|
||||
container = "mp4";
|
||||
}
|
||||
video_incomplete_file = "incomplete."+container;
|
||||
}
|
||||
|
||||
std::string sql = stringtf(
|
||||
"INSERT INTO `Events` "
|
||||
|
@ -120,28 +127,27 @@ Event::Event(
|
|||
state_id,
|
||||
monitor->getOrientation(),
|
||||
0,
|
||||
"",
|
||||
video_incomplete_file.c_str(),
|
||||
save_jpegs,
|
||||
storage->SchemeString().c_str()
|
||||
);
|
||||
id = zmDbDoInsert(sql);
|
||||
|
||||
if ( !SetPath(storage) ) {
|
||||
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() )
|
||||
if (monitor->ServerId())
|
||||
sql += stringtf(" AND ServerId=%u", monitor->ServerId());
|
||||
|
||||
Debug(1, "%s", sql.c_str());
|
||||
storage = nullptr;
|
||||
|
||||
MYSQL_RES *result = zmDbFetch(sql);
|
||||
if ( result ) {
|
||||
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
|
||||
if (result) {
|
||||
for (int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++) {
|
||||
storage = new Storage(atoi(dbrow[0]));
|
||||
if ( SetPath(storage) )
|
||||
if (SetPath(storage))
|
||||
break;
|
||||
delete storage;
|
||||
storage = nullptr;
|
||||
|
@ -149,18 +155,18 @@ Event::Event(
|
|||
mysql_free_result(result);
|
||||
result = nullptr;
|
||||
}
|
||||
if ( !storage ) {
|
||||
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() )
|
||||
if (monitor->ServerId())
|
||||
sql += stringtf(" OR ServerId != %u", monitor->ServerId());
|
||||
|
||||
result = zmDbFetch(sql);
|
||||
if ( result ) {
|
||||
if (result) {
|
||||
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
|
||||
storage = new Storage(atoi(dbrow[0]));
|
||||
if ( SetPath(storage) )
|
||||
if (SetPath(storage))
|
||||
break;
|
||||
delete storage;
|
||||
storage = nullptr;
|
||||
|
@ -169,7 +175,7 @@ Event::Event(
|
|||
result = nullptr;
|
||||
}
|
||||
}
|
||||
if ( !storage ) {
|
||||
if (!storage) {
|
||||
storage = new Storage();
|
||||
Warning("Failed to find a storage area to save events.");
|
||||
}
|
||||
|
@ -178,24 +184,16 @@ Event::Event(
|
|||
} // end if ! setPath(Storage)
|
||||
Debug(1, "Using storage area at %s", path.c_str());
|
||||
|
||||
video_name = "";
|
||||
|
||||
snapshot_file = path + "/snapshot.jpg";
|
||||
alarm_file = path + "/alarm.jpg";
|
||||
|
||||
/* Save as video */
|
||||
video_incomplete_path = path + "/" + video_incomplete_file;
|
||||
|
||||
if ( monitor->GetOptVideoWriter() != 0 ) {
|
||||
std::string container = monitor->OutputContainer();
|
||||
if ( container == "auto" || container == "" ) {
|
||||
container = "mp4";
|
||||
}
|
||||
if (monitor->GetOptVideoWriter() != 0) {
|
||||
/* Save as video */
|
||||
|
||||
video_name = stringtf("%" PRIu64 "-%s.%s", id, "video", container.c_str());
|
||||
video_file = path + "/" + video_name;
|
||||
Debug(1, "Writing video file to %s", video_file.c_str());
|
||||
videoStore = new VideoStore(
|
||||
video_file.c_str(),
|
||||
video_incomplete_path.c_str(),
|
||||
container.c_str(),
|
||||
monitor->GetVideoStream(),
|
||||
monitor->GetVideoCodecContext(),
|
||||
|
@ -213,20 +211,32 @@ Event::Event(
|
|||
zmDbDo(sql);
|
||||
}
|
||||
} else {
|
||||
sql = stringtf("UPDATE Events SET Videoed=1, DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name.c_str(), id);
|
||||
zmDbDo(sql);
|
||||
std::string codec = videoStore->get_codec();
|
||||
video_file = stringtf("%" PRIu64 "-%s.%s.%s", id, "video", codec.c_str(), container.c_str());
|
||||
video_path = path + "/" + video_file;
|
||||
Debug(1, "Video file is %s", video_file.c_str());
|
||||
}
|
||||
} // end if GetOptVideoWriter
|
||||
if (storage != monitor->getStorage())
|
||||
delete storage;
|
||||
}
|
||||
|
||||
Event::~Event() {
|
||||
// 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.
|
||||
|
||||
/* Close the video file */
|
||||
if ( videoStore != nullptr ) {
|
||||
if (videoStore != nullptr) {
|
||||
Debug(4, "Deleting video store");
|
||||
delete videoStore;
|
||||
videoStore = nullptr;
|
||||
int result = rename(video_incomplete_path.c_str(), video_path.c_str());
|
||||
if (result == 0) {
|
||||
Debug(1, "File successfully renamed");
|
||||
} else {
|
||||
Error("Failed renaming %s to %s", video_incomplete_path.c_str(), video_path.c_str());
|
||||
// So that we don't update the event record
|
||||
video_file = video_incomplete_file;
|
||||
}
|
||||
}
|
||||
|
||||
// endtime is set in AddFrame, so SHOULD be set to the value of the last frame timestamp.
|
||||
|
@ -245,21 +255,23 @@ Event::~Event() {
|
|||
}
|
||||
|
||||
std::string sql = stringtf(
|
||||
"UPDATE Events SET Name='%s%" PRIu64 "', EndDateTime = from_unixtime(%ld), Length = %.2f, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64 " AND Name='New Event'",
|
||||
"UPDATE Events SET Name='%s%" PRIu64 "', EndDateTime = from_unixtime(%ld), Length = %.2f, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo='%s' WHERE Id = %" PRIu64 " AND Name='New Event'",
|
||||
monitor->EventPrefix(), id, std::chrono::system_clock::to_time_t(end_time),
|
||||
delta_time.count(),
|
||||
frames, alarm_frames,
|
||||
tot_score, static_cast<uint32>(alarm_frames ? (tot_score / alarm_frames) : 0), max_score,
|
||||
video_file.c_str(), // defaults to ""
|
||||
id);
|
||||
|
||||
if (!zmDbDoUpdate(sql)) {
|
||||
// Name might have been changed during recording, so just do the update without changing the name.
|
||||
sql = stringtf(
|
||||
"UPDATE Events SET EndDateTime = from_unixtime(%ld), Length = %.2f, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64,
|
||||
"UPDATE Events SET EndDateTime = from_unixtime(%ld), Length = %.2f, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo='%s' WHERE Id = %" PRIu64,
|
||||
std::chrono::system_clock::to_time_t(end_time),
|
||||
delta_time.count(),
|
||||
frames, alarm_frames,
|
||||
tot_score, static_cast<uint32>(alarm_frames ? (tot_score / alarm_frames) : 0), max_score,
|
||||
video_file.c_str(), // defaults to ""
|
||||
id);
|
||||
zmDbDoUpdate(sql);
|
||||
} // end if no changed rows due to Name change during recording
|
||||
|
@ -312,32 +324,32 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) {
|
|||
bool update = false;
|
||||
|
||||
//Info( "Checking notes, %d <> %d", noteSetMap.size(), newNoteSetMap.size() );
|
||||
if ( newNoteSetMap.size() > 0 ) {
|
||||
if ( noteSetMap.size() == 0 ) {
|
||||
if (newNoteSetMap.size() > 0) {
|
||||
if (noteSetMap.size() == 0) {
|
||||
noteSetMap = newNoteSetMap;
|
||||
update = true;
|
||||
} else {
|
||||
for ( StringSetMap::const_iterator newNoteSetMapIter = newNoteSetMap.begin();
|
||||
for (StringSetMap::const_iterator newNoteSetMapIter = newNoteSetMap.begin();
|
||||
newNoteSetMapIter != newNoteSetMap.end();
|
||||
++newNoteSetMapIter ) {
|
||||
++newNoteSetMapIter) {
|
||||
const std::string &newNoteGroup = newNoteSetMapIter->first;
|
||||
const StringSet &newNoteSet = newNoteSetMapIter->second;
|
||||
//Info( "Got %d new strings", newNoteSet.size() );
|
||||
if ( newNoteSet.size() > 0 ) {
|
||||
if (newNoteSet.size() > 0) {
|
||||
StringSetMap::iterator noteSetMapIter = noteSetMap.find(newNoteGroup);
|
||||
if ( noteSetMapIter == noteSetMap.end() ) {
|
||||
//Info( "Can't find note group %s, copying %d strings", newNoteGroup.c_str(), newNoteSet.size() );
|
||||
if (noteSetMapIter == noteSetMap.end()) {
|
||||
//Debug(3, "Can't find note group %s, copying %d strings", newNoteGroup.c_str(), newNoteSet.size());
|
||||
noteSetMap.insert(StringSetMap::value_type(newNoteGroup, newNoteSet));
|
||||
update = true;
|
||||
} else {
|
||||
StringSet ¬eSet = noteSetMapIter->second;
|
||||
//Info( "Found note group %s, got %d strings", newNoteGroup.c_str(), newNoteSet.size() );
|
||||
for ( StringSet::const_iterator newNoteSetIter = newNoteSet.begin();
|
||||
//Debug(3, "Found note group %s, got %d strings", newNoteGroup.c_str(), newNoteSet.size());
|
||||
for (StringSet::const_iterator newNoteSetIter = newNoteSet.begin();
|
||||
newNoteSetIter != newNoteSet.end();
|
||||
++newNoteSetIter ) {
|
||||
++newNoteSetIter) {
|
||||
const std::string &newNote = *newNoteSetIter;
|
||||
StringSet::iterator noteSetIter = noteSet.find(newNote);
|
||||
if ( noteSetIter == noteSet.end() ) {
|
||||
if (noteSetIter == noteSet.end()) {
|
||||
noteSet.insert(newNote);
|
||||
update = true;
|
||||
}
|
||||
|
@ -479,7 +491,7 @@ void Event::AddFrame(Image *image,
|
|||
Debug(1, "Writing snapshot");
|
||||
WriteFrameImage(image, timestamp, snapshot_file.c_str());
|
||||
} else {
|
||||
Debug(1, "Not Writing snapshot");
|
||||
Debug(1, "Not Writing snapshot because score %d > max %d", score, max_score);
|
||||
}
|
||||
|
||||
// We are writing an Alarm frame
|
||||
|
@ -491,7 +503,7 @@ void Event::AddFrame(Image *image,
|
|||
Debug(1, "Writing alarm image");
|
||||
WriteFrameImage(image, timestamp, alarm_file.c_str());
|
||||
} else {
|
||||
Debug(1, "Not Writing alarm image");
|
||||
Debug(3, "Not Writing alarm image because alarm frame already written");
|
||||
}
|
||||
|
||||
if (alarm_image and (save_jpegs & 2)) {
|
||||
|
@ -534,7 +546,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();
|
||||
|
|
|
@ -84,8 +84,13 @@ class Event {
|
|||
std::string alarm_file;
|
||||
VideoStore *videoStore;
|
||||
|
||||
std::string video_name;
|
||||
std::string container;
|
||||
std::string codec;
|
||||
std::string video_file;
|
||||
std::string video_path;
|
||||
std::string video_incomplete_file;
|
||||
std::string video_incomplete_path;
|
||||
|
||||
int last_db_frame;
|
||||
bool have_video_keyframe; // a flag to tell us if we have had a video keyframe when writing an mp4. The first frame SHOULD be a video keyframe.
|
||||
Storage::Schemes scheme;
|
||||
|
|
|
@ -141,7 +141,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
event_data->storage_id = dbrow[1] ? atoi(dbrow[1]) : 0;
|
||||
event_data->frame_count = dbrow[2] == nullptr ? 0 : atoi(dbrow[2]);
|
||||
event_data->start_time = SystemTimePoint(Seconds(atoi(dbrow[3])));
|
||||
event_data->end_time = dbrow[4] ? SystemTimePoint(Seconds(atoi(dbrow[4]))) : SystemTimePoint();
|
||||
event_data->end_time = dbrow[4] ? SystemTimePoint(Seconds(atoi(dbrow[4]))) : std::chrono::system_clock::now();
|
||||
event_data->duration = std::chrono::duration_cast<Microseconds>(event_data->end_time - event_data->start_time);
|
||||
event_data->frames_duration =
|
||||
std::chrono::duration_cast<Microseconds>(dbrow[5] ? FPSeconds(atof(dbrow[5])) : FPSeconds(0.0));
|
||||
|
@ -663,6 +663,7 @@ bool EventStream::checkEventLoaded() {
|
|||
else
|
||||
curr_frame_id = 1;
|
||||
Debug(2, "New frame id = %ld", curr_frame_id);
|
||||
start = std::chrono::steady_clock::now();
|
||||
return true;
|
||||
} else {
|
||||
Debug(2, "No next event loaded using %s. Pausing", sql.c_str());
|
||||
|
@ -810,7 +811,7 @@ bool EventStream::sendFrame(Microseconds delta_us) {
|
|||
fputs("Content-Type: image/x-rgbz\r\n", stdout);
|
||||
break;
|
||||
case STREAM_RAW :
|
||||
img_buffer = (uint8_t*)(send_image->Buffer());
|
||||
img_buffer = send_image->Buffer();
|
||||
img_buffer_size = send_image->Size();
|
||||
fputs("Content-Type: image/x-rgb\r\n", stdout);
|
||||
break;
|
||||
|
@ -836,12 +837,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;
|
||||
|
@ -850,13 +852,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;
|
||||
|
@ -903,7 +905,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];
|
||||
|
@ -957,23 +959,25 @@ void EventStream::runStream() {
|
|||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()));
|
||||
|
||||
// if effective > base we should speed up frame delivery
|
||||
delta = std::chrono::duration_cast<Microseconds>((delta * base_fps) / effective_fps);
|
||||
Debug(3, "delta %" PRIi64 " us = base_fps (%f) / effective_fps (%f)",
|
||||
if (base_fps < effective_fps) {
|
||||
delta = std::chrono::duration_cast<Microseconds>((delta * base_fps) / effective_fps);
|
||||
Debug(3, "delta %" PRIi64 " us = base_fps (%f) / effective_fps (%f)",
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()),
|
||||
base_fps,
|
||||
effective_fps);
|
||||
|
||||
// but must not exceed maxfps
|
||||
delta = std::max(delta, Microseconds(lround(Microseconds::period::den / maxfps)));
|
||||
Debug(3, "delta %" PRIi64 " us = base_fps (%f) /effective_fps (%f) from 30fps",
|
||||
// but must not exceed maxfps
|
||||
delta = std::max(delta, Microseconds(lround(Microseconds::period::den / maxfps)));
|
||||
Debug(3, "delta %" PRIi64 " us = base_fps (%f) / effective_fps (%f) from 30fps",
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()),
|
||||
base_fps,
|
||||
effective_fps);
|
||||
}
|
||||
|
||||
// +/- 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) && (
|
||||
|
|
|
@ -76,7 +76,7 @@ class EventStream : public StreamBase {
|
|||
long 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;
|
||||
|
||||
|
|
|
@ -458,6 +458,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;
|
||||
|
|
|
@ -143,8 +143,8 @@ bool Fifo::writePacket(std::string filename, const ZMPacket &packet) {
|
|||
bool Fifo::write(uint8_t *data, size_t bytes, int64_t pts) {
|
||||
if (!(outfile or open())) return false;
|
||||
// Going to write a brief header
|
||||
Debug(1, "Writing header ZM %lu %" PRId64, bytes, pts);
|
||||
if ( fprintf(outfile, "ZM %lu %" PRId64 "\n", bytes, pts) < 0 ) {
|
||||
Debug(1, "Writing header ZM %zu %" PRId64, bytes, pts);
|
||||
if (fprintf(outfile, "ZM %zu %" PRId64 "\n", bytes, pts) < 0) {
|
||||
if (errno != EAGAIN) {
|
||||
Error("Problem during writing: %s", strerror(errno));
|
||||
} else {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -270,7 +270,6 @@ int Image::PopulateFrame(AVFrame *frame) {
|
|||
frame->width = width;
|
||||
frame->height = height;
|
||||
frame->format = imagePixFormat;
|
||||
Debug(1, "PopulateFrame: width %d height %d linesize %d colours %d imagesize %d", width, height, linesize, colours, size);
|
||||
zm_dump_video_frame(frame, "Image.Populate(frame)");
|
||||
return 1;
|
||||
} // int Image::PopulateFrame(AVFrame *frame)
|
||||
|
|
|
@ -179,9 +179,11 @@ class Image {
|
|||
}
|
||||
}
|
||||
|
||||
/* Internal buffer should not be modified from functions outside of this class */
|
||||
inline uint8_t* Buffer() { return buffer; }
|
||||
inline const uint8_t* Buffer() const { return buffer; }
|
||||
inline uint8_t* Buffer(unsigned int x, unsigned int y=0) { return &buffer[(y*linesize) + x*colours]; }
|
||||
inline const uint8_t* Buffer(unsigned int x, unsigned int y=0) const { return &buffer[(y*linesize) + x*colours]; }
|
||||
|
||||
/* Request writeable buffer */
|
||||
uint8_t* WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder);
|
||||
// Is only acceptable on a pre-allocated buffer
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -43,11 +43,11 @@ Logger::IntMap Logger::smSyslogPriorities;
|
|||
|
||||
void Logger::usrHandler(int sig) {
|
||||
Logger *logger = fetch();
|
||||
if ( sig == SIGUSR1 )
|
||||
if (sig == SIGUSR1)
|
||||
logger->level(logger->level()+1);
|
||||
else if ( sig == SIGUSR2 )
|
||||
else if (sig == SIGUSR2)
|
||||
logger->level(logger->level()-1);
|
||||
Info("Logger - Level changed to %d", logger->level());
|
||||
Info("Logger - Level changed to %d %s", logger->level(), smCodes[logger->level()].c_str());
|
||||
}
|
||||
|
||||
Logger::Logger() :
|
||||
|
@ -296,23 +296,23 @@ const std::string &Logger::id(const std::string &id) {
|
|||
}
|
||||
|
||||
Logger::Level Logger::level(Logger::Level level) {
|
||||
if ( level > NOOPT ) {
|
||||
if (level > NOOPT) {
|
||||
mLevel = limit(level);
|
||||
|
||||
mEffectiveLevel = NOLOG;
|
||||
if ( mTerminalLevel > mEffectiveLevel )
|
||||
if (mTerminalLevel > mEffectiveLevel)
|
||||
mEffectiveLevel = mTerminalLevel;
|
||||
if ( mDatabaseLevel > mEffectiveLevel )
|
||||
if (mDatabaseLevel > mEffectiveLevel)
|
||||
mEffectiveLevel = mDatabaseLevel;
|
||||
if ( mFileLevel > mEffectiveLevel )
|
||||
if (mFileLevel > mEffectiveLevel)
|
||||
mEffectiveLevel = mFileLevel;
|
||||
if ( mSyslogLevel > mEffectiveLevel )
|
||||
if (mSyslogLevel > mEffectiveLevel)
|
||||
mEffectiveLevel = mSyslogLevel;
|
||||
if ( mEffectiveLevel > mLevel)
|
||||
if (mEffectiveLevel > mLevel)
|
||||
mEffectiveLevel = mLevel;
|
||||
|
||||
// DEBUG levels should flush
|
||||
if ( mLevel > INFO )
|
||||
if (mLevel > INFO)
|
||||
mFlush = true;
|
||||
}
|
||||
return mLevel;
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
// It will be used whereever a Monitor dbrow is needed. WHERE conditions can be appended
|
||||
std::string load_monitor_sql =
|
||||
"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Capturing`+0, `Analysing`+0, `AnalysisSource`, `Recording`+0, `RecordingSource`, `Enabled`, `DecodingEnabled`, "
|
||||
"`LinkedMonitors`, `AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`,"
|
||||
"`LinkedMonitors`, `EventStartCommand`, `EventEndCommand`, `AnalysisFPSLimit`, `AnalysisUpdateDelay`, `MaxFPS`, `AlarmMaxFPS`,"
|
||||
"`Device`, `Channel`, `Format`, `V4LMultiBuffer`, `V4LCapturesPerFrame`, " // V4L Settings
|
||||
"`Protocol`, `Method`, `Options`, `User`, `Pass`, `Host`, `Port`, `Path`, `SecondPath`, `Width`, `Height`, `Colours`, `Palette`, `Orientation`+0, `Deinterlacing`, "
|
||||
"`DecoderHWAccelName`, `DecoderHWAccelDevice`, `RTSPDescribe`, "
|
||||
|
@ -86,6 +86,7 @@ std::string load_monitor_sql =
|
|||
"`SignalCheckPoints`, `SignalCheckColour`, `Importance`-1 FROM `Monitors`";
|
||||
|
||||
std::string CameraType_Strings[] = {
|
||||
"Unknown",
|
||||
"Local",
|
||||
"Remote",
|
||||
"File",
|
||||
|
@ -93,10 +94,21 @@ std::string CameraType_Strings[] = {
|
|||
"LibVLC",
|
||||
"NVSOCKET",
|
||||
"CURL",
|
||||
"VNC",
|
||||
"VNC"
|
||||
};
|
||||
|
||||
std::string Function_Strings[] = {
|
||||
"Unknown",
|
||||
"None",
|
||||
"Monitor",
|
||||
"Modect",
|
||||
"Record",
|
||||
"Mocord",
|
||||
"Nodect"
|
||||
};
|
||||
|
||||
std::string State_Strings[] = {
|
||||
"Unknown",
|
||||
"IDLE",
|
||||
"PREALARM",
|
||||
"ALARM",
|
||||
|
@ -143,7 +155,7 @@ bool Monitor::MonitorLink::connect() {
|
|||
|
||||
mem_size = sizeof(SharedData) + sizeof(TriggerData);
|
||||
|
||||
Debug(1, "link.mem.size=%jd", mem_size);
|
||||
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) {
|
||||
|
@ -170,14 +182,14 @@ bool Monitor::MonitorLink::connect() {
|
|||
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, 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(), mem_size, strerror(errno));
|
||||
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;
|
||||
}
|
||||
|
@ -424,7 +436,8 @@ Monitor::Monitor()
|
|||
/*
|
||||
std::string load_monitor_sql =
|
||||
"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Capturing`+0, `Analysing`+0, `AnalysisSource`, `Recording`+0, `RecordingSource`, `Enabled`, `DecodingEnabled`, "
|
||||
, LinkedMonitors, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS,"
|
||||
"LinkedMonitors, `EventStartCommand`, `EventEndCommand`, "
|
||||
"AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS,"
|
||||
"Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, " // V4L Settings
|
||||
"Protocol, Method, Options, User, Pass, Host, Port, Path, SecondPath, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, "
|
||||
"SaveJPEGs, VideoWriter, EncoderParameters,
|
||||
|
@ -480,18 +493,11 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
|||
|
||||
enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++;
|
||||
decoding_enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++;
|
||||
decoding_enabled = !(
|
||||
( function == RECORD or function == NODECT )
|
||||
and
|
||||
( savejpegs == 0 )
|
||||
and
|
||||
( videowriter == PASSTHROUGH )
|
||||
and
|
||||
!decoding_enabled
|
||||
);
|
||||
Debug(1, "Decoding enabled: %d", decoding_enabled);
|
||||
// See below after save_jpegs for a recalculation of decoding_enabled
|
||||
|
||||
ReloadLinkedMonitors(dbrow[col]); col++;
|
||||
event_start_command = dbrow[col] ? dbrow[col] : ""; col++;
|
||||
event_end_command = dbrow[col] ? dbrow[col] : ""; col++;
|
||||
|
||||
/* "AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS," */
|
||||
analysis_fps_limit = dbrow[col] ? strtod(dbrow[col], nullptr) : 0.0; col++;
|
||||
|
@ -558,6 +564,17 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
|||
videowriter = (VideoWriter)atoi(dbrow[col]); col++;
|
||||
encoderparams = dbrow[col] ? dbrow[col] : ""; col++;
|
||||
|
||||
decoding_enabled = !(
|
||||
( function == RECORD or function == NODECT )
|
||||
and
|
||||
( savejpegs == 0 )
|
||||
and
|
||||
( videowriter == PASSTHROUGH )
|
||||
and
|
||||
!decoding_enabled
|
||||
);
|
||||
Debug(3, "Decoding enabled: %d function %d %s savejpegs %d videowriter %d", decoding_enabled, function, Function_Strings[function].c_str(), savejpegs, videowriter);
|
||||
|
||||
/*"`OutputCodec`, `Encoder`, `OutputContainer`, " */
|
||||
output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
|
||||
encoder = dbrow[col] ? dbrow[col] : ""; col++;
|
||||
|
@ -939,7 +956,7 @@ bool Monitor::connect() {
|
|||
map_fd = -1;
|
||||
return false;
|
||||
} else {
|
||||
Error("Got unexpected memory map file size %ld, expected %jd", 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));
|
||||
close(map_fd);
|
||||
map_fd = -1;
|
||||
return false;
|
||||
|
@ -951,18 +968,18 @@ bool Monitor::connect() {
|
|||
mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, map_fd, 0);
|
||||
if (mem_ptr == MAP_FAILED) {
|
||||
if (errno == EAGAIN) {
|
||||
Debug(1, "Unable to map file %s (%jd bytes) to locked memory, trying unlocked", mem_file.c_str(), mem_size);
|
||||
Debug(1, "Unable to map file %s (%jd bytes) to locked memory, trying unlocked", mem_file.c_str(), static_cast<intmax_t>(mem_size));
|
||||
#endif
|
||||
mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0);
|
||||
Debug(1, "Mapped file %s (%jd bytes) to unlocked memory", mem_file.c_str(), mem_size);
|
||||
Debug(1, "Mapped file %s (%jd bytes) to unlocked memory", mem_file.c_str(), static_cast<intmax_t>(mem_size));
|
||||
#ifdef MAP_LOCKED
|
||||
} else {
|
||||
Error("Unable to map file %s (%jd bytes) to locked memory (%s)", mem_file.c_str(), mem_size, strerror(errno));
|
||||
Error("Unable to map file %s (%jd bytes) to locked memory (%s)", mem_file.c_str(), static_cast<intmax_t>(mem_size), strerror(errno));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if ((mem_ptr == MAP_FAILED) or (mem_ptr == nullptr)) {
|
||||
Error("Can't map file %s (%jd bytes) to memory: %s(%d)", mem_file.c_str(), mem_size, strerror(errno), errno);
|
||||
Error("Can't map file %s (%jd bytes) to memory: %s(%d)", mem_file.c_str(), static_cast<intmax_t>(mem_size), strerror(errno), errno);
|
||||
close(map_fd);
|
||||
map_fd = -1;
|
||||
mem_ptr = nullptr;
|
||||
|
@ -1648,7 +1665,7 @@ void Monitor::CheckAction() {
|
|||
}
|
||||
}
|
||||
|
||||
void Monitor::UpdateCaptureFPS() {
|
||||
void Monitor::UpdateFPS() {
|
||||
if ( fps_report_interval and
|
||||
(
|
||||
!(image_count%fps_report_interval)
|
||||
|
@ -1667,82 +1684,35 @@ void Monitor::UpdateCaptureFPS() {
|
|||
uint32 new_camera_bytes = camera->Bytes();
|
||||
uint32 new_capture_bandwidth =
|
||||
static_cast<uint32>((new_camera_bytes - last_camera_bytes) / elapsed.count());
|
||||
last_camera_bytes = new_camera_bytes;
|
||||
double new_analysis_fps = (motion_frame_count - last_motion_frame_count) / elapsed.count();
|
||||
|
||||
Debug(4, "%s: %d - last %d = %d now:%lf, last %lf, elapsed %lf = %lffps",
|
||||
"Capturing",
|
||||
Debug(4, "FPS: capture count %d - last capture count %d = %d now:%lf, last %lf, elapsed %lf = capture: %lf fps analysis: %lf fps",
|
||||
image_count,
|
||||
last_capture_image_count,
|
||||
image_count - last_capture_image_count,
|
||||
FPSeconds(now.time_since_epoch()).count(),
|
||||
FPSeconds(last_analysis_fps_time.time_since_epoch()).count(),
|
||||
FPSeconds(last_fps_time.time_since_epoch()).count(),
|
||||
elapsed.count(),
|
||||
new_capture_fps);
|
||||
new_capture_fps,
|
||||
new_analysis_fps);
|
||||
|
||||
Info("%s: %d - Capturing at %.2lf fps, capturing bandwidth %ubytes/sec",
|
||||
name.c_str(), image_count, new_capture_fps, new_capture_bandwidth);
|
||||
Info("%s: %d - Capturing at %.2lf fps, capturing bandwidth %ubytes/sec Analysing at %.2lf fps",
|
||||
name.c_str(), image_count, new_capture_fps, new_capture_bandwidth, new_analysis_fps);
|
||||
|
||||
shared_data->capture_fps = new_capture_fps;
|
||||
last_fps_time = now;
|
||||
last_capture_image_count = image_count;
|
||||
shared_data->analysis_fps = new_analysis_fps;
|
||||
last_motion_frame_count = motion_frame_count;
|
||||
last_camera_bytes = new_camera_bytes;
|
||||
|
||||
std::string sql = stringtf(
|
||||
"UPDATE LOW_PRIORITY Monitor_Status SET CaptureFPS = %.2lf, CaptureBandwidth=%u WHERE MonitorId=%u",
|
||||
new_capture_fps, new_capture_bandwidth, id);
|
||||
"UPDATE LOW_PRIORITY Monitor_Status SET CaptureFPS = %.2lf, CaptureBandwidth=%u, AnalysisFPS = %.2lf WHERE MonitorId=%u",
|
||||
new_capture_fps, new_capture_bandwidth, new_analysis_fps, id);
|
||||
dbQueue.push(std::move(sql));
|
||||
} // now != last_fps_time
|
||||
} // end if report fps
|
||||
} // void Monitor::UpdateCaptureFPS()
|
||||
|
||||
void Monitor::UpdateAnalysisFPS() {
|
||||
Debug(1, "analysis_image_count(%d) motion_count(%d) fps_report_interval(%d) mod%d",
|
||||
analysis_image_count, motion_frame_count, fps_report_interval,
|
||||
((analysis_image_count && fps_report_interval) ? !(analysis_image_count%fps_report_interval) : -1 ) );
|
||||
|
||||
if (
|
||||
( analysis_image_count and fps_report_interval and !(analysis_image_count%fps_report_interval) )
|
||||
or
|
||||
// In startup do faster updates
|
||||
( (analysis_image_count < fps_report_interval) and !(analysis_image_count%10) )
|
||||
) {
|
||||
SystemTimePoint now = std::chrono::system_clock::now();
|
||||
|
||||
FPSeconds elapsed = now - last_analysis_fps_time;
|
||||
Debug(4, "%s: %d - now: %.2f, last %lf, diff %lf",
|
||||
name.c_str(),
|
||||
analysis_image_count,
|
||||
FPSeconds(now.time_since_epoch()).count(),
|
||||
FPSeconds(last_analysis_fps_time.time_since_epoch()).count(),
|
||||
elapsed.count());
|
||||
|
||||
if (elapsed > Seconds(1)) {
|
||||
double new_analysis_fps = (motion_frame_count - last_motion_frame_count) / elapsed.count();
|
||||
Info("%s: %d - Analysing at %.2lf fps from %d - %d=%d / %lf - %lf = %lf",
|
||||
name.c_str(),
|
||||
analysis_image_count,
|
||||
new_analysis_fps,
|
||||
motion_frame_count,
|
||||
last_motion_frame_count,
|
||||
(motion_frame_count - last_motion_frame_count),
|
||||
FPSeconds(now.time_since_epoch()).count(),
|
||||
FPSeconds(last_analysis_fps_time.time_since_epoch()).count(),
|
||||
elapsed.count());
|
||||
|
||||
if (new_analysis_fps != shared_data->analysis_fps) {
|
||||
shared_data->analysis_fps = new_analysis_fps;
|
||||
|
||||
std::string sql = stringtf("UPDATE LOW_PRIORITY Monitor_Status SET AnalysisFPS = %.2lf WHERE MonitorId=%u",
|
||||
new_analysis_fps, id);
|
||||
dbQueue.push(std::move(sql));
|
||||
last_analysis_fps_time = now;
|
||||
last_motion_frame_count = motion_frame_count;
|
||||
} else {
|
||||
Debug(4, "No change in fps");
|
||||
} // end if change in fps
|
||||
} // end if at least 1 second has passed since last update
|
||||
|
||||
} // end if time to do an update
|
||||
} // end void Monitor::UpdateAnalysisFPS
|
||||
} // void Monitor::UpdateFPS()
|
||||
|
||||
// Would be nice if this JUST did analysis
|
||||
// This idea is that we should be analysing as close to the capture frame as possible.
|
||||
|
@ -1901,8 +1871,6 @@ bool Monitor::Analyse() {
|
|||
Debug(3, "signal and active and modect");
|
||||
Event::StringSet zoneSet;
|
||||
|
||||
int motion_score = last_motion_score;
|
||||
|
||||
if (analysis_fps_limit) {
|
||||
double capture_fps = get_capture_fps();
|
||||
motion_frame_skip = capture_fps / analysis_fps_limit;
|
||||
|
@ -1914,38 +1882,45 @@ bool Monitor::Analyse() {
|
|||
if (snap->image) {
|
||||
// decoder may not have been able to provide an image
|
||||
if (!ref_image.Buffer()) {
|
||||
Debug(1, "Assigning instead of Dectecting");
|
||||
Debug(1, "Assigning instead of Detecting");
|
||||
ref_image.Assign(*(snap->image));
|
||||
} else {
|
||||
Debug(1, "Detecting motion on image %d, image %p", snap->image_index, snap->image);
|
||||
// Get new score.
|
||||
motion_score = DetectMotion(*(snap->image), zoneSet);
|
||||
int motion_score = DetectMotion(*(snap->image), zoneSet);
|
||||
|
||||
// lets construct alarm cause. It will contain cause + names of zones alarmed
|
||||
std::string alarm_cause;
|
||||
snap->zone_stats.reserve(zones.size());
|
||||
for (const Zone &zone : zones) {
|
||||
const ZoneStats &stats = zone.GetStats();
|
||||
stats.DumpToLog("After detect motion");
|
||||
snap->zone_stats.push_back(stats);
|
||||
if (zone.Alarmed()) {
|
||||
if (!alarm_cause.empty()) alarm_cause += ",";
|
||||
alarm_cause += std::string(zone.Label());
|
||||
}
|
||||
}
|
||||
if (!alarm_cause.empty())
|
||||
cause = cause+" "+alarm_cause;
|
||||
|
||||
Debug(3, "After motion detection, score:%d last_motion_score(%d), new motion score(%d)",
|
||||
score, last_motion_score, motion_score);
|
||||
motion_frame_count += 1;
|
||||
// Why are we updating the last_motion_score too?
|
||||
last_motion_score = motion_score;
|
||||
if (motion_score) {
|
||||
if (cause.length()) cause += ", ";
|
||||
cause += MOTION_CAUSE;
|
||||
noteSetMap[MOTION_CAUSE] = zoneSet;
|
||||
} // end if motion_score
|
||||
}
|
||||
} else {
|
||||
Debug(1, "no image so skipping motion detection");
|
||||
} // end if has image
|
||||
} else {
|
||||
Debug(1, "Skipped motion detection last motion score was %d", motion_score);
|
||||
Debug(1, "Skipped motion detection last motion score was %d", last_motion_score);
|
||||
}
|
||||
if (motion_score) {
|
||||
score += motion_score;
|
||||
if (cause.length()) cause += ", ";
|
||||
cause += MOTION_CAUSE;
|
||||
noteSetMap[MOTION_CAUSE] = zoneSet;
|
||||
} // end if motion_score
|
||||
score += last_motion_score;
|
||||
} else {
|
||||
Debug(1, "Not Active(%d) enabled %d active %d doing motion detection: %d",
|
||||
Active(), enabled, shared_data->active,
|
||||
|
@ -1953,12 +1928,13 @@ bool Monitor::Analyse() {
|
|||
);
|
||||
} // end if active and doing motion detection
|
||||
|
||||
|
||||
if (function == RECORD or function == MOCORD) {
|
||||
// If doing record, check to see if we need to close the event or not.
|
||||
if (event) {
|
||||
Debug(2, "Have event %" PRIu64 " in record", event->Id());
|
||||
|
||||
if (section_length != Seconds(0) && (timestamp - GetVideoWriterStartTime() >= section_length)
|
||||
if (section_length != Seconds(0) && (timestamp - event->StartTime() >= section_length)
|
||||
&& ((function == MOCORD && event_close_mode != CLOSE_TIME)
|
||||
|| (function == RECORD && event_close_mode == CLOSE_TIME)
|
||||
|| std::chrono::duration_cast<Seconds>(timestamp.time_since_epoch()) % section_length == Seconds(0))) {
|
||||
|
@ -1967,77 +1943,17 @@ bool Monitor::Analyse() {
|
|||
image_count,
|
||||
event->Id(),
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp.time_since_epoch()).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(GetVideoWriterStartTime().time_since_epoch()).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - GetVideoWriterStartTime()).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(event->StartTime().time_since_epoch()).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - event->StartTime()).count()),
|
||||
static_cast<int64>(Seconds(section_length).count()));
|
||||
closeEvent();
|
||||
} // end if section_length
|
||||
} // end if event
|
||||
|
||||
if (!event) {
|
||||
Debug(2, "Creating continuous event");
|
||||
if (!snap->keyframe and (videowriter == PASSTHROUGH)) {
|
||||
// Must start on a keyframe so rewind. Only for passthrough though I guess.
|
||||
// FIXME this iterator is not protected from invalidation
|
||||
packetqueue_iterator *start_it = packetqueue.get_event_start_packet_it(
|
||||
*analysis_it, 0 /* pre_event_count */
|
||||
);
|
||||
event = openEvent(snap, cause.empty() ? "Continuous" : cause, noteSetMap);
|
||||
|
||||
// This gets a lock on the starting packet
|
||||
|
||||
ZMLockedPacket *starting_packet_lock = nullptr;
|
||||
std::shared_ptr<ZMPacket> starting_packet = nullptr;
|
||||
if (*start_it != *analysis_it) {
|
||||
starting_packet_lock = packetqueue.get_packet(start_it);
|
||||
if (!starting_packet_lock) {
|
||||
Warning("Unable to get starting packet lock");
|
||||
delete packet_lock;
|
||||
return false;
|
||||
}
|
||||
starting_packet = starting_packet_lock->packet_;
|
||||
} else {
|
||||
starting_packet = snap;
|
||||
}
|
||||
|
||||
event = new Event(this, starting_packet->timestamp, "Continuous", noteSetMap);
|
||||
// Write out starting packets, do not modify packetqueue it will garbage collect itself
|
||||
while (starting_packet and ((*start_it) != *analysis_it)) {
|
||||
event->AddPacket(starting_packet);
|
||||
// Have added the packet, don't want to unlock it until we have locked the next
|
||||
|
||||
packetqueue.increment_it(start_it);
|
||||
if ((*start_it) == *analysis_it) {
|
||||
if (starting_packet_lock) delete starting_packet_lock;
|
||||
break;
|
||||
}
|
||||
ZMLockedPacket *lp = packetqueue.get_packet(start_it);
|
||||
delete starting_packet_lock;
|
||||
if (!lp) return false;
|
||||
starting_packet_lock = lp;
|
||||
starting_packet = lp->packet_;
|
||||
}
|
||||
packetqueue.free_it(start_it);
|
||||
delete start_it;
|
||||
start_it = nullptr;
|
||||
} else {
|
||||
// Create event from current snap
|
||||
event = new Event(this, timestamp, "Continuous", noteSetMap);
|
||||
}
|
||||
shared_data->last_event_id = event->Id();
|
||||
|
||||
// lets construct alarm cause. It will contain cause + names of zones alarmed
|
||||
std::string alarm_cause;
|
||||
for (const Zone &zone : zones) {
|
||||
if (zone.Alarmed()) {
|
||||
if (!alarm_cause.empty()) alarm_cause += ",";
|
||||
alarm_cause += std::string(zone.Label());
|
||||
}
|
||||
}
|
||||
alarm_cause = cause+" Continuous "+alarm_cause;
|
||||
strncpy(shared_data->alarm_cause, alarm_cause.c_str(), sizeof(shared_data->alarm_cause)-1);
|
||||
SetVideoWriterStartTime(event->StartTime());
|
||||
|
||||
Info("%s: %03d - Opened new event %" PRIu64 ", section start",
|
||||
Info("%s: %03d - Opened new event %" PRIu64 ", continuous section start",
|
||||
name.c_str(), analysis_image_count, event->Id());
|
||||
/* To prevent cancelling out an existing alert\prealarm\alarm state */
|
||||
if (state == IDLE) {
|
||||
|
@ -2046,86 +1962,35 @@ bool Monitor::Analyse() {
|
|||
} // end if ! event
|
||||
} // end if RECORDING
|
||||
|
||||
if (score) {
|
||||
|
||||
if (score and (function != MONITOR)) {
|
||||
if ((state == IDLE) || (state == TAPE) || (state == PREALARM)) {
|
||||
// If we should end then previous continuous event and start a new non-continuous event
|
||||
if (event && event->Frames()
|
||||
&& !event->AlarmFrames()
|
||||
&& event_close_mode == CLOSE_ALARM
|
||||
&& timestamp - GetVideoWriterStartTime() >= min_section_length
|
||||
&& (!pre_event_count || Event::PreAlarmCount() >= alarm_frame_count - 1)) {
|
||||
&& (event_close_mode == CLOSE_ALARM)
|
||||
&& ((timestamp - event->StartTime()) >= min_section_length)
|
||||
&& ((!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count - 1))) {
|
||||
Info("%s: %03d - Closing event %" PRIu64 ", continuous end, alarm begins",
|
||||
name.c_str(), image_count, event->Id());
|
||||
closeEvent();
|
||||
} else if (event) {
|
||||
// This is so if we need more than 1 alarm frame before going into alarm, so it is basically if we have enough alarm frames
|
||||
Debug(3,
|
||||
"pre_alarm_count in event %d, event frames %d, alarm frames %d event length %" PRIi64 " >=? %" PRIi64 " min",
|
||||
Event::PreAlarmCount(),
|
||||
"pre_alarm_count in event %d of %d, event frames %d, alarm frames %d event length %" PRIi64 " >=? %" PRIi64 " min close mode is ALARM? %d",
|
||||
Event::PreAlarmCount(), pre_event_count,
|
||||
event->Frames(),
|
||||
event->AlarmFrames(),
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - GetVideoWriterStartTime()).count()),
|
||||
static_cast<int64>(Seconds(min_section_length).count()));
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - event->StartTime()).count()),
|
||||
static_cast<int64>(Seconds(min_section_length).count()),
|
||||
(event_close_mode == CLOSE_ALARM));
|
||||
}
|
||||
if ((!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1)) {
|
||||
// lets construct alarm cause. It will contain cause + names of zones alarmed
|
||||
std::string alarm_cause = "";
|
||||
for (const Zone &zone : zones) {
|
||||
if (zone.Alarmed()) {
|
||||
alarm_cause = alarm_cause + "," + std::string(zone.Label());
|
||||
}
|
||||
}
|
||||
if (!alarm_cause.empty()) alarm_cause[0] = ' ';
|
||||
alarm_cause = cause + alarm_cause;
|
||||
strncpy(shared_data->alarm_cause, alarm_cause.c_str(), sizeof(shared_data->alarm_cause)-1);
|
||||
Info("%s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u Cause:%s",
|
||||
name.c_str(), image_count, Event::PreAlarmCount(), alarm_frame_count, shared_data->alarm_cause);
|
||||
name.c_str(), image_count, Event::PreAlarmCount(), alarm_frame_count, cause.c_str());
|
||||
|
||||
if (!event) {
|
||||
packetqueue_iterator *start_it = packetqueue.get_event_start_packet_it(
|
||||
*analysis_it,
|
||||
(pre_event_count > alarm_frame_count ? pre_event_count : alarm_frame_count)
|
||||
);
|
||||
ZMLockedPacket *starting_packet_lock = nullptr;
|
||||
std::shared_ptr<ZMPacket> starting_packet = nullptr;
|
||||
if (*start_it != *analysis_it) {
|
||||
starting_packet_lock = packetqueue.get_packet(start_it);
|
||||
if (!starting_packet_lock) return false;
|
||||
starting_packet = starting_packet_lock->packet_;
|
||||
} else {
|
||||
starting_packet = snap;
|
||||
}
|
||||
|
||||
event = new Event(this, starting_packet->timestamp, cause, noteSetMap);
|
||||
shared_data->last_event_id = event->Id();
|
||||
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
|
||||
SetVideoWriterStartTime(event->StartTime());
|
||||
event = openEvent(snap, cause, noteSetMap);
|
||||
shared_data->state = state = ALARM;
|
||||
|
||||
// Write out starting packets, do not modify packetqueue it will garbage collect itself
|
||||
while (*start_it != *analysis_it) {
|
||||
event->AddPacket(starting_packet);
|
||||
|
||||
packetqueue.increment_it(start_it);
|
||||
if ( (*start_it) == (*analysis_it) ) {
|
||||
if (starting_packet_lock) delete starting_packet_lock;
|
||||
break;
|
||||
}
|
||||
ZMLockedPacket *lp = packetqueue.get_packet(start_it);
|
||||
delete starting_packet_lock;
|
||||
if (!lp) {
|
||||
// Shutting down event will be closed by ~Monitor()
|
||||
// Perhaps we shouldn't do this.
|
||||
return false;
|
||||
}
|
||||
starting_packet_lock = lp;
|
||||
starting_packet = lp->packet_;
|
||||
}
|
||||
packetqueue.free_it(start_it);
|
||||
delete start_it;
|
||||
start_it = nullptr;
|
||||
|
||||
Info("%s: %03d - Opening new event %" PRIu64 ", alarm start", name.c_str(), analysis_image_count, event->Id());
|
||||
} else {
|
||||
shared_data->state = state = ALARM;
|
||||
|
@ -2163,8 +2028,10 @@ bool Monitor::Analyse() {
|
|||
Info("%s: %03d - Gone into alert state", name.c_str(), analysis_image_count);
|
||||
shared_data->state = state = ALERT;
|
||||
} else if (state == ALERT) {
|
||||
if (analysis_image_count - last_alarm_count > post_event_count
|
||||
&& timestamp - GetVideoWriterStartTime() >= min_section_length) {
|
||||
if (
|
||||
((analysis_image_count - last_alarm_count) > post_event_count)
|
||||
&&
|
||||
((timestamp - event->StartTime()) >= min_section_length)) {
|
||||
Info("%s: %03d - Left alarm state (%" PRIu64 ") - %d(%d) images",
|
||||
name.c_str(), analysis_image_count, event->Id(), event->Frames(), event->AlarmFrames());
|
||||
//if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE )
|
||||
|
@ -2182,7 +2049,8 @@ bool Monitor::Analyse() {
|
|||
shared_data->state = state = ((function != MOCORD) ? IDLE : TAPE);
|
||||
} else {
|
||||
Debug(1,
|
||||
"State %s because image_count(%d)-last_alarm_count(%d) > post_event_count(%d) and timestamp.tv_sec(%" PRIi64 ") - recording.tv_src(%" PRIi64 ") >= min_section_length(%" PRIi64 ")",
|
||||
"State %d %s because analysis_image_count(%d)-last_alarm_count(%d) > post_event_count(%d) and timestamp.tv_sec(%" PRIi64 ") - recording.tv_src(%" PRIi64 ") >= min_section_length(%" PRIi64 ")",
|
||||
state,
|
||||
State_Strings[state].c_str(),
|
||||
analysis_image_count,
|
||||
last_alarm_count,
|
||||
|
@ -2201,18 +2069,15 @@ bool Monitor::Analyse() {
|
|||
// Generate analysis images if necessary
|
||||
if ((savejpegs > 1) and snap->image) {
|
||||
for (const Zone &zone : zones) {
|
||||
if (zone.Alarmed()) {
|
||||
if (zone.AlarmImage()) {
|
||||
if (zone.Alarmed() and zone.AlarmImage()) {
|
||||
if (!snap->analysis_image)
|
||||
snap->analysis_image = new Image(*(snap->image));
|
||||
snap->analysis_image->Overlay(*(zone.AlarmImage()));
|
||||
}
|
||||
} // end if zone is alarmed
|
||||
} // end foreach zone
|
||||
} // end if savejpegs
|
||||
|
||||
// incremement pre alarm image count
|
||||
//have_pre_alarmed_frames ++;
|
||||
Event::AddPreAlarmFrame(snap->image, timestamp, score, nullptr);
|
||||
} else if (state == ALARM) {
|
||||
for (const Zone &zone : zones) {
|
||||
|
@ -2227,7 +2092,7 @@ bool Monitor::Analyse() {
|
|||
if (event) {
|
||||
if (noteSetMap.size() > 0)
|
||||
event->updateNotes(noteSetMap);
|
||||
if (section_length != Seconds(0) && (timestamp - GetVideoWriterStartTime() >= section_length)) {
|
||||
if (section_length != Seconds(0) && (timestamp - event->StartTime() >= section_length)) {
|
||||
Warning("%s: %03d - event %" PRIu64 ", has exceeded desired section length. %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %" PRIi64,
|
||||
name.c_str(), analysis_image_count, event->Id(),
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp.time_since_epoch()).count()),
|
||||
|
@ -2235,11 +2100,7 @@ bool Monitor::Analyse() {
|
|||
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - GetVideoWriterStartTime()).count()),
|
||||
static_cast<int64>(Seconds(section_length).count()));
|
||||
closeEvent();
|
||||
event = new Event(this, timestamp, cause, noteSetMap);
|
||||
shared_data->last_event_id = event->Id();
|
||||
//set up video store data
|
||||
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
|
||||
SetVideoWriterStartTime(event->StartTime());
|
||||
event = openEvent(snap, cause, noteSetMap);
|
||||
}
|
||||
} else {
|
||||
Error("ALARM but no event");
|
||||
|
@ -2292,8 +2153,6 @@ bool Monitor::Analyse() {
|
|||
// Only do these if it's a video packet.
|
||||
shared_data->last_read_index = snap->image_index;
|
||||
analysis_image_count++;
|
||||
if (function == MODECT or function == MOCORD)
|
||||
UpdateAnalysisFPS();
|
||||
}
|
||||
packetqueue.increment_it(analysis_it);
|
||||
packetqueue.unlock(packet_lock);
|
||||
|
@ -2356,7 +2215,7 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
|
|||
while ( 1 ) {
|
||||
dest_ptr = link_id_str;
|
||||
while ( *src_ptr >= '0' && *src_ptr <= '9' ) {
|
||||
if ( (dest_ptr-link_id_str) < (unsigned int)(sizeof(link_id_str)-1) ) {
|
||||
if ( (unsigned int)(dest_ptr-link_id_str) < (unsigned int)(sizeof(link_id_str)-1) ) {
|
||||
*dest_ptr++ = *src_ptr++;
|
||||
} else {
|
||||
break;
|
||||
|
@ -2574,7 +2433,6 @@ int Monitor::Capture() {
|
|||
|
||||
// Will only be queued if there are iterators allocated in the queue.
|
||||
packetqueue.queuePacket(packet);
|
||||
UpdateCaptureFPS();
|
||||
} else { // result == 0
|
||||
// Question is, do we update last_write_index etc?
|
||||
return 0;
|
||||
|
@ -2614,7 +2472,7 @@ bool Monitor::Decode() {
|
|||
//
|
||||
//capture_image = packet->image = new Image(width, height, camera->Colours(), camera->SubpixelOrder());
|
||||
int ret = packet->decode(camera->getVideoCodecContext());
|
||||
if (ret > 0) {
|
||||
if (ret > 0 and !zm_terminate) {
|
||||
if (packet->in_frame and !packet->image) {
|
||||
packet->image = new Image(camera_width, camera_height, camera->Colours(), camera->SubpixelOrder());
|
||||
AVFrame *input_frame = packet->in_frame;
|
||||
|
@ -2784,7 +2642,7 @@ void Monitor::TimestampImage(Image *ts_image, SystemTimePoint ts_time) const {
|
|||
const char *s_ptr = label_time_text;
|
||||
char *d_ptr = label_text;
|
||||
|
||||
while (*s_ptr && ((d_ptr - label_text) < (unsigned int) sizeof(label_text))) {
|
||||
while (*s_ptr && ((unsigned int)(d_ptr - label_text) < (unsigned int) sizeof(label_text))) {
|
||||
if ( *s_ptr == config.timestamp_code_char[0] ) {
|
||||
bool found_macro = false;
|
||||
switch ( *(s_ptr+1) ) {
|
||||
|
@ -2800,7 +2658,7 @@ void Monitor::TimestampImage(Image *ts_image, SystemTimePoint ts_time) const {
|
|||
typedef std::chrono::duration<int64, std::centi> Centiseconds;
|
||||
Centiseconds centi_sec = std::chrono::duration_cast<Centiseconds>(
|
||||
ts_time.time_since_epoch() - std::chrono::duration_cast<Seconds>(ts_time.time_since_epoch()));
|
||||
d_ptr += snprintf(d_ptr, sizeof(label_text) - (d_ptr - label_text), "%02ld", centi_sec.count());
|
||||
d_ptr += snprintf(d_ptr, sizeof(label_text) - (d_ptr - label_text), "%02lld", static_cast<long long int>(centi_sec.count()));
|
||||
found_macro = true;
|
||||
break;
|
||||
}
|
||||
|
@ -2817,6 +2675,67 @@ void Monitor::TimestampImage(Image *ts_image, SystemTimePoint ts_time) const {
|
|||
Debug(2, "done annotating %s", label_text);
|
||||
} // end void Monitor::TimestampImage
|
||||
|
||||
Event * Monitor::openEvent(
|
||||
const std::shared_ptr<ZMPacket> &snap,
|
||||
const std::string &cause,
|
||||
const Event::StringSetMap noteSetMap) {
|
||||
|
||||
// FIXME this iterator is not protected from invalidation
|
||||
packetqueue_iterator *start_it = packetqueue.get_event_start_packet_it(
|
||||
*analysis_it,
|
||||
(cause == "Continuous" ? 0 : (pre_event_count > alarm_frame_count ? pre_event_count : alarm_frame_count))
|
||||
);
|
||||
|
||||
// This gets a lock on the starting packet
|
||||
|
||||
ZMLockedPacket *starting_packet_lock = nullptr;
|
||||
std::shared_ptr<ZMPacket> starting_packet = nullptr;
|
||||
if (*start_it != *analysis_it) {
|
||||
starting_packet_lock = packetqueue.get_packet(start_it);
|
||||
if (!starting_packet_lock) {
|
||||
Warning("Unable to get starting packet lock");
|
||||
return nullptr;
|
||||
}
|
||||
starting_packet = starting_packet_lock->packet_;
|
||||
} else {
|
||||
starting_packet = snap;
|
||||
}
|
||||
|
||||
event = new Event(this, starting_packet->timestamp, cause, noteSetMap);
|
||||
|
||||
shared_data->last_event_id = event->Id();
|
||||
strncpy(shared_data->alarm_cause, cause.c_str(), sizeof(shared_data->alarm_cause)-1);
|
||||
|
||||
if (!event_start_command.empty()) {
|
||||
if (fork() == 0) {
|
||||
execlp(event_start_command.c_str(), event_start_command.c_str(), std::to_string(event->Id()).c_str(), nullptr);
|
||||
Error("Error execing %s", event_start_command.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Write out starting packets, do not modify packetqueue it will garbage collect itself
|
||||
while (starting_packet and ((*start_it) != *analysis_it)) {
|
||||
event->AddPacket(starting_packet);
|
||||
// Have added the packet, don't want to unlock it until we have locked the next
|
||||
|
||||
packetqueue.increment_it(start_it);
|
||||
if ((*start_it) == *analysis_it) {
|
||||
if (starting_packet_lock) delete starting_packet_lock;
|
||||
break;
|
||||
}
|
||||
ZMLockedPacket *lp = packetqueue.get_packet(start_it);
|
||||
delete starting_packet_lock;
|
||||
if (!lp) return nullptr; // only on terminate FIXME
|
||||
starting_packet_lock = lp;
|
||||
starting_packet = lp->packet_;
|
||||
}
|
||||
packetqueue.free_it(start_it);
|
||||
delete start_it;
|
||||
start_it = nullptr;
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
void Monitor::closeEvent() {
|
||||
if (!event) return;
|
||||
|
||||
|
@ -2827,7 +2746,18 @@ void Monitor::closeEvent() {
|
|||
Debug(1, "close event thread is not joinable");
|
||||
}
|
||||
Debug(1, "Starting thread to close event");
|
||||
close_event_thread = std::thread([](Event *e){ delete e; }, event);
|
||||
close_event_thread = std::thread([](Event *e, const std::string &command){
|
||||
int64_t event_id = e->Id();
|
||||
delete e;
|
||||
|
||||
if (!command.empty()) {
|
||||
if (fork() == 0) {
|
||||
execlp(command.c_str(), command.c_str(), std::to_string(event_id).c_str(), nullptr);
|
||||
Error("Error execing %s", command.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
}, event, event_end_command);
|
||||
Debug(1, "Nulling event");
|
||||
event = nullptr;
|
||||
if (shared_data) video_store_data->recording = {};
|
||||
|
@ -3120,9 +3050,6 @@ int Monitor::PrimeCapture() {
|
|||
int Monitor::PreCapture() const { return camera->PreCapture(); }
|
||||
int Monitor::PostCapture() const { return camera->PostCapture(); }
|
||||
int Monitor::Close() {
|
||||
if (close_event_thread.joinable()) {
|
||||
close_event_thread.join();
|
||||
}
|
||||
// Because the stream indexes may change we have to clear out the packetqueue
|
||||
if (decoder) {
|
||||
decoder->Stop();
|
||||
|
@ -3140,10 +3067,14 @@ int Monitor::Close() {
|
|||
video_fifo = nullptr;
|
||||
}
|
||||
|
||||
if (close_event_thread.joinable()) {
|
||||
close_event_thread.join();
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(event_mutex);
|
||||
if (event) {
|
||||
Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name.c_str(), image_count, event->Id());
|
||||
closeEvent();
|
||||
close_event_thread.join();
|
||||
}
|
||||
if (camera) camera->Close();
|
||||
return 1;
|
||||
|
|
|
@ -128,7 +128,7 @@ public:
|
|||
} Deinterlace;
|
||||
|
||||
typedef enum {
|
||||
UNKNOWN=-1,
|
||||
UNKNOWN,
|
||||
IDLE,
|
||||
PREALARM,
|
||||
ALARM,
|
||||
|
@ -443,6 +443,8 @@ protected:
|
|||
|
||||
int n_linked_monitors;
|
||||
MonitorLink **linked_monitors;
|
||||
std::string event_start_command;
|
||||
std::string event_end_command;
|
||||
|
||||
std::vector<Group *> groups;
|
||||
|
||||
|
@ -612,8 +614,7 @@ public:
|
|||
unsigned int GetLastWriteIndex() const;
|
||||
uint64_t GetLastEventId() const;
|
||||
double GetFPS() const;
|
||||
void UpdateAnalysisFPS();
|
||||
void UpdateCaptureFPS();
|
||||
void UpdateFPS();
|
||||
void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" );
|
||||
void ForceAlarmOff();
|
||||
void CancelForced();
|
||||
|
@ -658,6 +659,10 @@ public:
|
|||
bool Decode();
|
||||
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 noteSetMap);
|
||||
void closeEvent();
|
||||
|
||||
void Reload();
|
||||
|
|
|
@ -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;
|
||||
|
@ -229,6 +241,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
break;
|
||||
case CMD_QUIT :
|
||||
Info("User initiated exit - CMD_QUIT");
|
||||
zm_terminate = true;
|
||||
break;
|
||||
case CMD_QUERY :
|
||||
Debug(1, "Got QUERY command, sending STATUS");
|
||||
|
@ -267,7 +280,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;
|
||||
}
|
||||
|
@ -287,9 +300,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,
|
||||
|
@ -311,13 +324,6 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
Error("Can't sendto on sd %d: %s", sd, strerror(errno));
|
||||
}
|
||||
Debug(2, "Number of bytes sent to (%s): (%d)", rem_addr.sun_path, nbytes);
|
||||
|
||||
// quit after sending a status, if this was a quit request
|
||||
if ((MsgCommand)msg->msg_data[0] == CMD_QUIT) {
|
||||
zm_terminate = true;
|
||||
Debug(2, "Quitting");
|
||||
return;
|
||||
}
|
||||
} // end void MonitorStream::processCommand(const CmdMsg *msg)
|
||||
|
||||
bool MonitorStream::sendFrame(const std::string &filepath, SystemTimePoint timestamp) {
|
||||
|
@ -365,7 +371,6 @@ bool MonitorStream::sendFrame(const std::string &filepath, SystemTimePoint times
|
|||
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);
|
||||
|
@ -379,12 +384,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());
|
||||
|
@ -404,8 +412,6 @@ bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
|
|||
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 :
|
||||
|
@ -414,7 +420,7 @@ bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
|
|||
break;
|
||||
case STREAM_RAW :
|
||||
fputs("Content-Type: image/x-rgb\r\n", stdout);
|
||||
img_buffer = (uint8_t*)send_image->Buffer();
|
||||
img_buffer = send_image->Buffer();
|
||||
img_buffer_size = send_image->Size();
|
||||
break;
|
||||
case STREAM_ZIP :
|
||||
|
@ -447,19 +453,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) {
|
||||
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 +511,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 +581,7 @@ void MonitorStream::runStream() {
|
|||
break;
|
||||
}
|
||||
|
||||
now = std::chrono::system_clock::now();
|
||||
now = std::chrono::steady_clock::now();
|
||||
monitor->setLastViewed(now);
|
||||
|
||||
bool was_paused = paused;
|
||||
|
@ -618,7 +629,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) {
|
||||
|
@ -684,7 +695,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)",
|
||||
|
@ -743,9 +755,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) {
|
||||
|
@ -776,17 +788,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 && (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()));
|
||||
|
@ -798,13 +835,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) {
|
||||
|
@ -855,16 +885,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,
|
||||
|
|
|
@ -116,14 +116,15 @@ 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 *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");
|
||||
Warning("Found locked packet when trying to free up video packets. This basically means that decoding is not keeping up.");
|
||||
delete lp;
|
||||
++it;
|
||||
continue;
|
||||
|
@ -209,7 +210,7 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
|
|||
--it;
|
||||
}
|
||||
}
|
||||
Debug(1, "Tail count is %d, queue size is %lu", tail_count, pktQueue.size());
|
||||
Debug(1, "Tail count is %d, queue size is %zu", tail_count, pktQueue.size());
|
||||
|
||||
if (!keep_keyframes) {
|
||||
// If not doing passthrough, we don't care about starting with a keyframe so logic is simpler
|
||||
|
@ -260,6 +261,7 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
|
|||
zm_packet = *it;
|
||||
lp = new ZMLockedPacket(zm_packet);
|
||||
if (!lp->trylock()) {
|
||||
Debug(3, "Failed locking packet %d", zm_packet->image_index);
|
||||
delete lp;
|
||||
break;
|
||||
}
|
||||
|
@ -280,7 +282,7 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
|
|||
next_front = it;
|
||||
}
|
||||
++video_packets_to_delete;
|
||||
Debug(4, "Counted %d video packets. Which would leave %d in packetqueue tail count is %d",
|
||||
Debug(3, "Counted %d video packets. Which would leave %d in packetqueue tail count is %d",
|
||||
video_packets_to_delete, packet_counts[video_stream_id]-video_packets_to_delete, tail_count);
|
||||
if (packet_counts[video_stream_id] - video_packets_to_delete <= pre_event_video_packet_count + tail_count) {
|
||||
break;
|
||||
|
@ -289,7 +291,7 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
|
|||
++it;
|
||||
} // end while
|
||||
} // end if first packet not locked
|
||||
Debug(1, "Resulting pointing at latest packet? %d, next front points to begin? %d",
|
||||
Debug(1, "Resulting it pointing at latest packet? %d, next front points to begin? %d",
|
||||
( *it == add_packet ),
|
||||
( next_front == pktQueue.begin() )
|
||||
);
|
||||
|
@ -311,7 +313,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
|
||||
|
|
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ RETSIGTYPE zm_die_handler(int signal, siginfo_t * info, void *context)
|
|||
RETSIGTYPE zm_die_handler(int signal)
|
||||
#endif
|
||||
{
|
||||
zm_terminate = true;
|
||||
Error("Got signal %d (%s), crashing", signal, strsignal(signal));
|
||||
#if (defined(__i386__) || defined(__x86_64__))
|
||||
// Get more information if available
|
||||
|
|
|
@ -128,10 +128,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()
|
||||
|
@ -386,9 +386,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() {
|
||||
|
|
|
@ -88,6 +88,7 @@ protected:
|
|||
CMD_VARPLAY,
|
||||
CMD_GET_IMAGE,
|
||||
CMD_QUIT,
|
||||
CMD_MAXFPS,
|
||||
CMD_QUERY=99
|
||||
} MsgCommand;
|
||||
|
||||
|
@ -118,21 +119,22 @@ 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;
|
||||
|
||||
|
@ -207,10 +209,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) {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// ZoneMinder Time Functions & Definitions, $Date$, $Revision$
|
||||
// Copyright (C) 2001-2008 Philip Coombes
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
|
||||
#include "zm_time.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
std::string SystemTimePointToString(SystemTimePoint tp) {
|
||||
time_t tp_sec = std::chrono::system_clock::to_time_t(tp);
|
||||
Microseconds now_frac = std::chrono::duration_cast<Microseconds>(
|
||||
tp.time_since_epoch() - std::chrono::duration_cast<Seconds>(tp.time_since_epoch()));
|
||||
|
||||
std::string timeString;
|
||||
timeString.reserve(64);
|
||||
char *timePtr = &*(timeString.begin());
|
||||
tm tp_tm = {};
|
||||
timePtr += strftime(timePtr, timeString.capacity(), "%x %H:%M:%S", localtime_r(&tp_sec, &tp_tm));
|
||||
snprintf(timePtr, timeString.capacity() - (timePtr - timeString.data()), ".%06" PRIi64, static_cast<int64_t>(now_frac.count()));
|
||||
return timeString;
|
||||
}
|
||||
|
||||
std::string TimePointToString(TimePoint tp) {
|
||||
time_t tp_sec = std::chrono::system_clock::to_time_t(
|
||||
std::chrono::system_clock::now() + (tp - std::chrono::steady_clock::now()));
|
||||
|
||||
Microseconds now_frac = std::chrono::duration_cast<Microseconds>(
|
||||
tp.time_since_epoch() - std::chrono::duration_cast<Seconds>(tp.time_since_epoch()));
|
||||
|
||||
std::string timeString;
|
||||
timeString.reserve(64);
|
||||
char *timePtr = &*(timeString.begin());
|
||||
tm tp_tm = {};
|
||||
timePtr += strftime(timePtr, timeString.capacity(), "%x %H:%M:%S", localtime_r(&tp_sec, &tp_tm));
|
||||
snprintf(timePtr, timeString.capacity() - (timePtr - timeString.data()), ".%06" PRIi64, static_cast<int64_t>(now_frac.count()));
|
||||
return timeString;
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
#define ZM_TIME_H
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <sys/time.h>
|
||||
|
||||
typedef std::chrono::microseconds Microseconds;
|
||||
|
@ -120,4 +121,7 @@ class TimeSegmentAdder {
|
|||
bool finished_;
|
||||
};
|
||||
|
||||
std::string SystemTimePointToString(SystemTimePoint tp);
|
||||
std::string TimePointToString(TimePoint tp);
|
||||
|
||||
#endif // ZM_TIME_H
|
||||
|
|
|
@ -252,8 +252,15 @@ void HwCapsDetect() {
|
|||
#elif defined(__arm__)
|
||||
// ARM processor in 32bit mode
|
||||
// To see if it supports NEON, we need to get that information from the kernel
|
||||
#ifdef __linux__
|
||||
unsigned long auxval = getauxval(AT_HWCAP);
|
||||
if (auxval & HWCAP_ARM_NEON) {
|
||||
#elif defined(__FreeBSD__)
|
||||
unsigned long auxval = 0;
|
||||
elf_aux_info(AT_HWCAP, &auxval, sizeof(auxval));
|
||||
if (auxval & HWCAP_NEON) {
|
||||
#error Unsupported OS.
|
||||
#endif
|
||||
Debug(1,"Detected ARM (AArch32) processor with Neon");
|
||||
neonversion = 1;
|
||||
} else {
|
||||
|
|
|
@ -151,6 +151,7 @@ bool VideoStore::open() {
|
|||
Debug(3, "Encoder Option %s=%s", e->key, e->value);
|
||||
}
|
||||
}
|
||||
av_dict_free(&opts);
|
||||
|
||||
if (video_in_stream) {
|
||||
zm_dump_codecpar(video_in_stream->codecpar);
|
||||
|
@ -184,6 +185,7 @@ bool VideoStore::open() {
|
|||
}
|
||||
} // end if orientation
|
||||
|
||||
av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0);
|
||||
if (av_dict_get(opts, "new_extradata", nullptr, AV_DICT_MATCH_CASE)) {
|
||||
av_dict_set(&opts, "new_extradata", nullptr, 0);
|
||||
// Special flag to tell us to open a codec to get new extraflags to fix weird h265
|
||||
|
@ -230,8 +232,8 @@ bool VideoStore::open() {
|
|||
if (ret < 0) {
|
||||
Error("Could not initialize stream parameteres");
|
||||
}
|
||||
av_dict_free(&opts);
|
||||
} // end if extradata_entry
|
||||
av_dict_free(&opts);
|
||||
} else if (monitor->GetOptVideoWriter() == Monitor::ENCODE) {
|
||||
int wanted_codec = monitor->OutputCodec();
|
||||
if (!wanted_codec) {
|
||||
|
@ -485,6 +487,7 @@ bool VideoStore::open() {
|
|||
zm_dump_stream_format(oc, 0, 0, 1);
|
||||
if (audio_out_stream) zm_dump_stream_format(oc, 1, 0, 1);
|
||||
|
||||
av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0);
|
||||
const AVDictionaryEntry *movflags_entry = av_dict_get(opts, "movflags", nullptr, AV_DICT_MATCH_CASE);
|
||||
if (!movflags_entry) {
|
||||
Debug(1, "setting movflags to frag_keyframe+empty_moov");
|
||||
|
@ -616,7 +619,8 @@ VideoStore::~VideoStore() {
|
|||
|
||||
Debug(1, "Writing trailer");
|
||||
/* Write the trailer before close */
|
||||
if (int rc = av_write_trailer(oc)) {
|
||||
int rc;
|
||||
if ((rc = av_write_trailer(oc)) < 0) {
|
||||
Error("Error writing trailer %s", av_err2str(rc));
|
||||
} else {
|
||||
Debug(3, "Success Writing trailer");
|
||||
|
@ -626,7 +630,7 @@ VideoStore::~VideoStore() {
|
|||
if (!(out_format->flags & AVFMT_NOFILE)) {
|
||||
/* Close the out file. */
|
||||
Debug(4, "Closing");
|
||||
if (int rc = avio_close(oc->pb)) {
|
||||
if ((rc = avio_close(oc->pb)) < 0) {
|
||||
Error("Error closing avio %s", av_err2str(rc));
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -111,6 +111,13 @@ class VideoStore {
|
|||
int writePacket(const std::shared_ptr<ZMPacket> &pkt);
|
||||
int write_packets(PacketQueue &queue);
|
||||
void flush_codecs();
|
||||
const char *get_codec() {
|
||||
if (chosen_codec_data)
|
||||
return chosen_codec_data->codec_codec;
|
||||
if (video_out_stream)
|
||||
return avcodec_get_name(video_out_stream->codecpar->codec_id);
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ZM_VIDEOSTORE_H
|
||||
|
|
|
@ -206,7 +206,7 @@ bool Zone::CheckAlarms(const Image *delta_image) {
|
|||
// Get the difference image
|
||||
Image *diff_image = image = new Image(*delta_image);
|
||||
int diff_width = diff_image->Width();
|
||||
uint8_t* diff_buff = (uint8_t*)diff_image->Buffer();
|
||||
uint8_t* diff_buff = diff_image->Buffer();
|
||||
uint8_t* pdiff;
|
||||
|
||||
unsigned int pixel_diff_count = 0;
|
||||
|
@ -283,7 +283,7 @@ bool Zone::CheckAlarms(const Image *delta_image) {
|
|||
int lo_x = ranges[y].lo_x;
|
||||
int hi_x = ranges[y].hi_x;
|
||||
|
||||
pdiff = (uint8_t*)diff_image->Buffer(lo_x, y);
|
||||
pdiff = diff_image->Buffer(lo_x, y);
|
||||
|
||||
for (int x = lo_x; x <= hi_x; x++, pdiff++) {
|
||||
if (*pdiff == kWhite) {
|
||||
|
@ -366,7 +366,7 @@ bool Zone::CheckAlarms(const Image *delta_image) {
|
|||
int lo_x = ranges[y].lo_x;
|
||||
int hi_x = ranges[y].hi_x;
|
||||
|
||||
pdiff = (uint8_t*)diff_image->Buffer(lo_x, y);
|
||||
pdiff = diff_image->Buffer(lo_x, y);
|
||||
for (int x = lo_x; x <= hi_x; x++, pdiff++) {
|
||||
if (*pdiff == kWhite) {
|
||||
Debug(9, "Got white pixel at %d,%d (%p)", x, y, pdiff);
|
||||
|
@ -878,16 +878,23 @@ std::vector<Zone> Zone::Load(Monitor *monitor) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (polygon.Extent().Lo().x_ < 0 || polygon.Extent().Hi().x_ > static_cast<int32>(monitor->Width())
|
||||
|| polygon.Extent().Lo().y_ < 0 || polygon.Extent().Hi().y_ > static_cast<int32>(monitor->Height())) {
|
||||
Error("Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d), fixing",
|
||||
if (polygon.Extent().Lo().x_ < 0
|
||||
||
|
||||
polygon.Extent().Hi().x_ > static_cast<int32>(monitor->Width())
|
||||
||
|
||||
polygon.Extent().Lo().y_ < 0
|
||||
||
|
||||
polygon.Extent().Hi().y_ > static_cast<int32>(monitor->Height())) {
|
||||
Error("Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d) != (%d,%d), fixing",
|
||||
Id,
|
||||
Name,
|
||||
monitor->Name(),
|
||||
polygon.Extent().Lo().x_,
|
||||
polygon.Extent().Lo().y_,
|
||||
polygon.Extent().Hi().x_,
|
||||
polygon.Extent().Hi().y_);
|
||||
polygon.Extent().Hi().y_,
|
||||
monitor->Width(),
|
||||
monitor->Height());
|
||||
|
||||
polygon.Clip(Box(
|
||||
{0, 0},
|
||||
|
@ -980,7 +987,7 @@ void Zone::std_alarmedpixels(
|
|||
unsigned int hi_x = ranges[y].hi_x;
|
||||
|
||||
Debug(7, "Checking line %d from %d -> %d", y, lo_x, hi_x);
|
||||
uint8_t *pdiff = (uint8_t*)pdiff_image->Buffer(lo_x, y);
|
||||
uint8_t *pdiff = pdiff_image->Buffer(lo_x, y);
|
||||
const uint8_t *ppoly = ppoly_image->Buffer(lo_x, y);
|
||||
|
||||
for ( unsigned int x = lo_x; x <= hi_x; x++, pdiff++, ppoly++ ) {
|
||||
|
|
|
@ -140,7 +140,7 @@ std::shared_ptr<Image> GenerateRandomImage(
|
|||
Image *image = new Image(width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE);
|
||||
|
||||
// Set it to black initially.
|
||||
memset((void *) image->Buffer(0, 0), 0, (size_t) image->LineSize() * (size_t) image->Height());
|
||||
memset(image->Buffer(0, 0), 0, (size_t) image->LineSize() * (size_t) image->Height());
|
||||
|
||||
// Now randomize the pixels inside a box.
|
||||
const int box_width = (width * change_box_percent) / 100;
|
||||
|
@ -149,7 +149,7 @@ std::shared_ptr<Image> GenerateRandomImage(
|
|||
const int box_y = (int) ((uint64_t) mt_rand() * (height - box_height) / RAND_MAX);
|
||||
|
||||
for (int y = 0 ; y < box_height ; y++) {
|
||||
uint8_t *row = (uint8_t *) image->Buffer(box_x, box_y + y);
|
||||
uint8_t *row = image->Buffer(box_x, box_y + y);
|
||||
for (int x = 0 ; x < box_width ; x++) {
|
||||
row[x] = (uint8_t) mt_rand();
|
||||
}
|
||||
|
|
17
src/zmc.cpp
17
src/zmc.cpp
|
@ -220,12 +220,13 @@ int main(int argc, char *argv[]) {
|
|||
zmSetDefaultTermHandler();
|
||||
zmSetDefaultDieHandler();
|
||||
|
||||
sigset_t block_set;
|
||||
sigemptyset(&block_set);
|
||||
|
||||
sigaddset(&block_set, SIGHUP);
|
||||
sigaddset(&block_set, SIGUSR1);
|
||||
sigaddset(&block_set, SIGUSR2);
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = SIG_IGN; //handle signal by ignoring
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
if (sigaction(SIGCHLD, &sa, 0) == -1) {
|
||||
Error("Unable to set SIGCHLD to ignore. There may be zombies.");
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
int prime_capture_log_count = 0;
|
||||
|
@ -273,7 +274,7 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
std::this_thread::sleep_for(sleep_time);
|
||||
}
|
||||
if (zm_terminate){
|
||||
if (zm_terminate) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -317,6 +318,7 @@ int main(int argc, char *argv[]) {
|
|||
result = -1;
|
||||
break;
|
||||
}
|
||||
monitors[i]->UpdateFPS();
|
||||
|
||||
// capture_delay is the amount of time we should sleep in useconds to achieve the desired framerate.
|
||||
Microseconds delay = (monitors[i]->GetState() == Monitor::ALARM) ? monitors[i]->GetAlarmCaptureDelay()
|
||||
|
@ -373,6 +375,7 @@ int main(int argc, char *argv[]) {
|
|||
monitor->Id());
|
||||
zmDbDo(sql);
|
||||
}
|
||||
monitors.clear();
|
||||
|
||||
Image::Deinitialise();
|
||||
Debug(1, "terminating");
|
||||
|
|
12
src/zmu.cpp
12
src/zmu.cpp
|
@ -482,7 +482,7 @@ int main(int argc, char *argv[]) {
|
|||
exit_zmu(-1);
|
||||
}
|
||||
if ( !ValidateAccess(user, mon_id, function) ) {
|
||||
Error("Insufficient privileges for requested action");
|
||||
Error("Insufficient privileges for user %s for requested function %x", username, function);
|
||||
exit_zmu(-1);
|
||||
}
|
||||
} // end if auth
|
||||
|
@ -497,6 +497,16 @@ int main(int argc, char *argv[]) {
|
|||
if ( verbose ) {
|
||||
printf("Monitor %u(%s)\n", monitor->Id(), monitor->Name());
|
||||
}
|
||||
|
||||
if (monitor->GetFunction() == Monitor::NONE) {
|
||||
if (verbose) {
|
||||
printf("Current state: None\n");
|
||||
} else {
|
||||
printf("%d", Monitor::UNKNOWN);
|
||||
}
|
||||
exit_zmu(-1);
|
||||
}
|
||||
|
||||
if ( !monitor->connect() ) {
|
||||
Error("Can't connect to capture daemon: %d %s", monitor->Id(), monitor->Name());
|
||||
exit_zmu(-1);
|
||||
|
|
|
@ -87,11 +87,7 @@ else
|
|||
fi;
|
||||
|
||||
if [ "$DISTROS" == "" ]; then
|
||||
if [ "$RELEASE" != "" ]; then
|
||||
DISTROS="bionic,focal,hirsute,impish"
|
||||
else
|
||||
DISTROS=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`;
|
||||
fi;
|
||||
DISTROS=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`;
|
||||
echo "Defaulting to $DISTROS for distribution";
|
||||
else
|
||||
echo "Building for $DISTROS";
|
||||
|
@ -116,52 +112,6 @@ else
|
|||
echo "Defaulting to ZoneMinder upstream git"
|
||||
GITHUB_FORK="ZoneMinder"
|
||||
fi;
|
||||
if [ "$SNAPSHOT" == "stable" ]; then
|
||||
if [ "$BRANCH" == "" ]; then
|
||||
#REV=$(git rev-list --tags --max-count=1)
|
||||
BRANCH=`git describe --tags $(git rev-list --tags --max-count=1)`;
|
||||
if [ -z "$BRANCH" ]; then
|
||||
# This should only happen in CI environments where tag info isn't available
|
||||
BRANCH=`cat version`
|
||||
echo "Building branch $BRANCH"
|
||||
fi
|
||||
if [ "$BRANCH" == "" ]; then
|
||||
echo "Unable to determine latest stable branch!"
|
||||
exit 0;
|
||||
fi
|
||||
echo "Latest stable branch is $BRANCH";
|
||||
fi;
|
||||
else
|
||||
if [ "$BRANCH" == "" ]; then
|
||||
echo "Defaulting to master branch";
|
||||
BRANCH="master";
|
||||
fi;
|
||||
if [ "$SNAPSHOT" == "NOW" ]; then
|
||||
SNAPSHOT=`date +%Y%m%d%H%M%S`;
|
||||
else
|
||||
if [ "$SNAPSHOT" == "CURRENT" ]; then
|
||||
SNAPSHOT="`date +%Y%m%d.`$(git rev-list ${versionhash}..HEAD --count)"
|
||||
fi;
|
||||
fi;
|
||||
fi;
|
||||
fi
|
||||
|
||||
IFS='.' read -r -a VERSION_PARTS <<< "$RELEASE"
|
||||
if [ "$PPA" == "" ]; then
|
||||
if [ "$RELEASE" != "" ]; then
|
||||
# We need to use our official tarball for the original source, so grab it and overwrite our generated one.
|
||||
if [ "${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" == "1.30" ]; then
|
||||
PPA="ppa:iconnor/zoneminder-stable"
|
||||
else
|
||||
PPA="ppa:iconnor/zoneminder-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}"
|
||||
fi;
|
||||
else
|
||||
if [ "$BRANCH" == "" ]; then
|
||||
PPA="ppa:iconnor/zoneminder-master";
|
||||
else
|
||||
PPA="ppa:iconnor/zoneminder-$BRANCH";
|
||||
fi;
|
||||
fi;
|
||||
fi;
|
||||
|
||||
# Instead of cloning from github each time, if we have a fork lying around, update it and pull from there instead.
|
||||
|
@ -171,15 +121,8 @@ if [ ! -d "${GITHUB_FORK}_zoneminder_release" ]; then
|
|||
cd "${GITHUB_FORK}_ZoneMinder.git"
|
||||
echo "git fetch..."
|
||||
git fetch
|
||||
echo "git checkout $BRANCH"
|
||||
git checkout $BRANCH
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to switch to branch."
|
||||
exit 1;
|
||||
fi;
|
||||
echo "git pull..."
|
||||
git pull
|
||||
cd ../
|
||||
|
||||
echo "git clone ${GITHUB_FORK}_ZoneMinder.git ${GITHUB_FORK}_zoneminder_release"
|
||||
git clone "${GITHUB_FORK}_ZoneMinder.git" "${GITHUB_FORK}_zoneminder_release"
|
||||
else
|
||||
|
@ -192,14 +135,59 @@ else
|
|||
fi;
|
||||
|
||||
cd "${GITHUB_FORK}_zoneminder_release"
|
||||
git checkout $BRANCH
|
||||
cd ../
|
||||
|
||||
VERSION=`cat ${GITHUB_FORK}_zoneminder_release/version`
|
||||
if [ "$SNAPSHOT" == "stable" ]; then
|
||||
if [ "$BRANCH" == "" ]; then
|
||||
#REV=$(git rev-list --tags --max-count=1)
|
||||
BRANCH=`git describe --tags $(git rev-list --tags --max-count=1)`;
|
||||
if [ -z "$BRANCH" ]; then
|
||||
# This should only happen in CI environments where tag info isn't available
|
||||
BRANCH=`cat version`
|
||||
echo "Building branch $BRANCH"
|
||||
fi
|
||||
if [ "$BRANCH" == "" ]; then
|
||||
echo "Unable to determine latest stable branch!"
|
||||
exit 0;
|
||||
fi
|
||||
echo "Latest stable branch is $BRANCH";
|
||||
fi;
|
||||
else
|
||||
if [ "$BRANCH" == "" ]; then
|
||||
echo "Defaulting to master branch";
|
||||
BRANCH="master";
|
||||
fi;
|
||||
if [ "$SNAPSHOT" == "NOW" ]; then
|
||||
SNAPSHOT=`date +%Y%m%d%H%M%S`;
|
||||
else
|
||||
if [ "$SNAPSHOT" == "CURRENT" ]; then
|
||||
# git the latest (short) commit hash of the version file
|
||||
versionhash=$(git log -n1 --pretty=format:%h version)
|
||||
|
||||
# Number of commits since the version file was last changed
|
||||
numcommits=$(git rev-list ${versionhash}..HEAD --count)
|
||||
SNAPSHOT="`date +%Y%m%d.`$(git rev-list ${versionhash}..HEAD --count)"
|
||||
fi;
|
||||
fi;
|
||||
fi;
|
||||
|
||||
|
||||
echo "git checkout $BRANCH"
|
||||
git checkout $BRANCH
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to switch to branch."
|
||||
exit 1;
|
||||
fi;
|
||||
echo "git pull..."
|
||||
git pull
|
||||
# Grab the ZoneMinder version from the contents of the version file
|
||||
VERSION=$(cat version)
|
||||
if [ -z "$VERSION" ]; then
|
||||
exit 1;
|
||||
fi;
|
||||
IFS='.' read -r -a VERSION_PARTS <<< "$VERSION"
|
||||
|
||||
cd ../
|
||||
|
||||
if [ "$SNAPSHOT" != "stable" ] && [ "$SNAPSHOT" != "" ]; then
|
||||
VERSION="$VERSION~$SNAPSHOT";
|
||||
fi;
|
||||
|
@ -230,12 +218,11 @@ rm .gitignore
|
|||
cd ../
|
||||
|
||||
|
||||
if [ !-e "$DIRECTORY.orig.tar.gz" ]; then
|
||||
read -p "$DIRECTORY.orig.tar.gz does not exist, create it? [Y/n]"
|
||||
if [[ $REPLY == [yY] ]]; then
|
||||
|
||||
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
|
||||
fi;
|
||||
if [ -e "$DIRECTORY.orig.tar.gz" ]; then
|
||||
read -p "$DIRECTORY.orig.tar.gz exists, overwrite it? [Y/n]"
|
||||
if [[ "$REPLY" == "" || "$REPLY" == [yY] ]]; then
|
||||
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
|
||||
fi;
|
||||
fi;
|
||||
|
||||
IFS=',' ;for DISTRO in `echo "$DISTROS"`; do
|
||||
|
@ -358,6 +345,22 @@ EOF
|
|||
fi;
|
||||
else
|
||||
SC="zoneminder_${VERSION}-${DISTRO}${PACKAGE_VERSION}_source.changes";
|
||||
if [ "$PPA" == "" ]; then
|
||||
if [ "$RELEASE" != "" ]; then
|
||||
# We need to use our official tarball for the original source, so grab it and overwrite our generated one.
|
||||
if [ "${VERSION_PARTS[0]}.${VERSION_PARTS[1]}" == "1.30" ]; then
|
||||
PPA="ppa:iconnor/zoneminder-stable"
|
||||
else
|
||||
PPA="ppa:iconnor/zoneminder-${VERSION_PARTS[0]}.${VERSION_PARTS[1]}"
|
||||
fi;
|
||||
else
|
||||
if [ "$BRANCH" == "" ]; then
|
||||
PPA="ppa:iconnor/zoneminder-master";
|
||||
else
|
||||
PPA="ppa:iconnor/zoneminder-$BRANCH";
|
||||
fi;
|
||||
fi;
|
||||
fi;
|
||||
|
||||
dput="Y";
|
||||
if [ "$INTERACTIVE" != "no" ]; then
|
||||
|
|
|
@ -93,7 +93,7 @@ if ( canView('Events') or canView('Snapshots') ) {
|
|||
$exportFormat,
|
||||
$exportCompress,
|
||||
$exportStructure,
|
||||
(!empty($_REQUEST['exportFile'])?$_REQUEST['exportFile']:'zmExport'),
|
||||
(!empty($_REQUEST['exportFile'])?$_REQUEST['exportFile']:'zmExport')
|
||||
)) {
|
||||
ajaxResponse(array('exportFile'=>$exportFile));
|
||||
} else {
|
||||
|
|
|
@ -6,28 +6,30 @@ $data = array();
|
|||
// INITIALIZE AND CHECK SANITY
|
||||
//
|
||||
|
||||
if ( !canView('Events') ) $message = 'Insufficient permissions for user '.$user['Username'];
|
||||
if (!canView('Events'))
|
||||
$message = 'Insufficient permissions for user '.$user['Username'].'<br/>';
|
||||
|
||||
if ( empty($_REQUEST['task']) ) {
|
||||
$message = 'Must specify a task';
|
||||
if (empty($_REQUEST['task'])) {
|
||||
$message = 'Must specify a task<br/>';
|
||||
} else {
|
||||
$task = $_REQUEST['task'];
|
||||
}
|
||||
|
||||
if ( empty($_REQUEST['eids']) ) {
|
||||
if ( isset($_REQUEST['task']) && $_REQUEST['task'] != 'query' ) $message = 'No event id(s) supplied';
|
||||
if (empty($_REQUEST['eids'])) {
|
||||
if (isset($_REQUEST['task']) && $_REQUEST['task'] != 'query')
|
||||
$message = 'No event id(s) supplied<br/>';
|
||||
} else {
|
||||
$eids = $_REQUEST['eids'];
|
||||
}
|
||||
|
||||
if ( $message ) {
|
||||
if ($message) {
|
||||
ajaxError($message);
|
||||
return;
|
||||
}
|
||||
|
||||
require_once('includes/Filter.php');
|
||||
$filter = isset($_REQUEST['filter']) ? ZM\Filter::parse($_REQUEST['filter']) : new ZM\Filter();
|
||||
if ( $user['MonitorIds'] ) {
|
||||
if ($user['MonitorIds']) {
|
||||
$filter = $filter->addTerm(array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'IN', 'val'=>$user['MonitorIds']));
|
||||
}
|
||||
|
||||
|
@ -39,10 +41,19 @@ $search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
|
|||
$advsearch = isset($_REQUEST['advsearch']) ? json_decode($_REQUEST['advsearch'], JSON_OBJECT_AS_ARRAY) : array();
|
||||
|
||||
// Order specifies the sort direction, either asc or desc
|
||||
$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC';
|
||||
$order = $filter->sort_asc() ? 'ASC' : 'DESC';
|
||||
if (isset($_REQUEST['order'])) {
|
||||
if (strtolower($_REQUEST['order']) == 'asc') {
|
||||
$order = 'ASC';
|
||||
} else if (strtolower($_REQUEST['order']) == 'desc') {
|
||||
$order = 'DESC';
|
||||
} else {
|
||||
Warning('Invalid value for order ' . $_REQUEST['order']);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort specifies the name of the column to sort on
|
||||
$sort = 'StartDateTime';
|
||||
$sort = $filter->sort_field();
|
||||
if (isset($_REQUEST['sort'])) {
|
||||
$sort = $_REQUEST['sort'];
|
||||
if ($sort == 'EndDateTime') {
|
||||
|
@ -56,20 +67,19 @@ if (isset($_REQUEST['sort'])) {
|
|||
|
||||
// Offset specifies the starting row to return, used for pagination
|
||||
$offset = 0;
|
||||
if ( isset($_REQUEST['offset']) ) {
|
||||
if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) {
|
||||
if (isset($_REQUEST['offset'])) {
|
||||
if ((!is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']))) {
|
||||
ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']);
|
||||
} else {
|
||||
$offset = $_REQUEST['offset'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Limit specifies the number of rows to return
|
||||
// Set the default to 0 for events view, to prevent an issue with ALL pagination
|
||||
$limit = 0;
|
||||
if ( isset($_REQUEST['limit']) ) {
|
||||
if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
|
||||
if (isset($_REQUEST['limit'])) {
|
||||
if ((!is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']))) {
|
||||
ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']);
|
||||
} else {
|
||||
$limit = $_REQUEST['limit'];
|
||||
|
@ -80,25 +90,24 @@ if ( isset($_REQUEST['limit']) ) {
|
|||
// MAIN LOOP
|
||||
//
|
||||
|
||||
switch ( $task ) {
|
||||
switch ($task) {
|
||||
case 'archive' :
|
||||
foreach ( $eids as $eid ) archiveRequest($task, $eid);
|
||||
foreach ($eids as $eid) archiveRequest($task, $eid);
|
||||
break;
|
||||
case 'unarchive' :
|
||||
# The idea is that anyone can archive, but only people with Event Edit permission can unarchive..
|
||||
if ( !canEdit('Events') ) {
|
||||
if (!canEdit('Events')) {
|
||||
ajaxError('Insufficient permissions for user '.$user['Username']);
|
||||
return;
|
||||
}
|
||||
foreach ( $eids as $eid ) archiveRequest($task, $eid);
|
||||
foreach ($eids as $eid) archiveRequest($task, $eid);
|
||||
break;
|
||||
case 'delete' :
|
||||
if ( !canEdit('Events') ) {
|
||||
if (!canEdit('Events')) {
|
||||
ajaxError('Insufficient permissions for user '.$user['Username']);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $eids as $eid ) $data[] = deleteRequest($eid);
|
||||
foreach ($eids as $eid) $data[] = deleteRequest($eid);
|
||||
break;
|
||||
case 'query' :
|
||||
$data = queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit);
|
||||
|
@ -128,6 +137,8 @@ function deleteRequest($eid) {
|
|||
$message[] = array($eid=>'Event not found.');
|
||||
} else if ( $event->Archived() ) {
|
||||
$message[] = array($eid=>'Event is archived, cannot delete it.');
|
||||
} else if (!$event->canEdit()) {
|
||||
$message[] = array($eid=>'You do not have permission to delete event '.$event->Id());
|
||||
} else {
|
||||
$event->delete();
|
||||
}
|
||||
|
@ -136,7 +147,6 @@ function deleteRequest($eid) {
|
|||
}
|
||||
|
||||
function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit) {
|
||||
|
||||
$data = array(
|
||||
'total' => 0,
|
||||
'totalNotFiltered' => 0,
|
||||
|
@ -145,7 +155,7 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
|||
);
|
||||
|
||||
$failed = !$filter->test_pre_sql_conditions();
|
||||
if ( $failed ) {
|
||||
if ($failed) {
|
||||
ZM\Debug('Pre conditions failed, not doing sql');
|
||||
return $data;
|
||||
}
|
||||
|
@ -160,22 +170,27 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
|||
// The names of columns shown in the event view that are NOT dB columns in the database
|
||||
$col_alt = array('Monitor', 'Storage');
|
||||
|
||||
if ( !in_array($sort, array_merge($columns, $col_alt)) ) {
|
||||
ZM\Error('Invalid sort field: ' . $sort);
|
||||
$sort = 'Id';
|
||||
if ( $sort != '' ) {
|
||||
if (!in_array($sort, array_merge($columns, $col_alt))) {
|
||||
ZM\Error('Invalid sort field: ' . $sort);
|
||||
$sort = '';
|
||||
} else if ( $sort == 'Monitor' ) {
|
||||
$sort = 'M.Name';
|
||||
} else {
|
||||
$sort = 'E.'.$sort;
|
||||
}
|
||||
}
|
||||
|
||||
$values = array();
|
||||
$likes = array();
|
||||
$where = $filter->sql()?' WHERE ('.$filter->sql().')' : '';
|
||||
|
||||
$sort = $sort == 'Monitor' ? 'M.Name' : 'E.'.$sort;
|
||||
$col_str = 'E.*, M.Name AS Monitor';
|
||||
$sql = 'SELECT ' .$col_str. ' FROM `Events` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.' ORDER BY '.$sort.' '.$order;
|
||||
$sql = 'SELECT ' .$col_str. ' FROM `Events` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id'.$where.($sort?' ORDER BY '.$sort.' '.$order:'');
|
||||
|
||||
$storage_areas = ZM\Storage::find();
|
||||
$StorageById = array();
|
||||
foreach ( $storage_areas as $S ) {
|
||||
foreach ($storage_areas as $S) {
|
||||
$StorageById[$S->Id()] = $S;
|
||||
}
|
||||
|
||||
|
@ -184,41 +199,43 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
|||
|
||||
ZM\Debug('Calling the following sql query: ' .$sql);
|
||||
$query = dbQuery($sql, $values);
|
||||
if ( $query ) {
|
||||
while ( $row = dbFetchNext($query) ) {
|
||||
$event = new ZM\Event($row);
|
||||
$event->remove_from_cache();
|
||||
if ( !$filter->test_post_sql_conditions($event) ) {
|
||||
continue;
|
||||
}
|
||||
$event_ids[] = $event->Id();
|
||||
$unfiltered_rows[] = $row;
|
||||
} # end foreach row
|
||||
if (!$query) {
|
||||
ajaxError(dbError($sql));
|
||||
return;
|
||||
}
|
||||
while ($row = dbFetchNext($query)) {
|
||||
$event = new ZM\Event($row);
|
||||
$event->remove_from_cache();
|
||||
if (!$filter->test_post_sql_conditions($event)) {
|
||||
continue;
|
||||
}
|
||||
$event_ids[] = $event->Id();
|
||||
$unfiltered_rows[] = $row;
|
||||
} # end foreach row
|
||||
|
||||
ZM\Debug('Have ' . count($unfiltered_rows) . ' events matching base filter.');
|
||||
|
||||
$filtered_rows = null;
|
||||
|
||||
if ( count($advsearch) or $search != '' ) {
|
||||
if (count($advsearch) or $search != '') {
|
||||
$search_filter = new ZM\Filter();
|
||||
$search_filter = $search_filter->addTerm(array('cnj'=>'and', 'attr'=>'Id', 'op'=>'IN', 'val'=>$event_ids));
|
||||
|
||||
// There are two search bars in the log view, normal and advanced
|
||||
// Making an exuctive decision to ignore the normal search, when advanced search is in use
|
||||
// Alternatively we could try to do both
|
||||
if ( count($advsearch) ) {
|
||||
if (count($advsearch)) {
|
||||
$terms = array();
|
||||
foreach ( $advsearch as $col=>$text ) {
|
||||
foreach ($advsearch as $col=>$text) {
|
||||
$terms[] = array('cnj'=>'and', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$text);
|
||||
} # end foreach col in advsearch
|
||||
$terms[0]['obr'] = 1;
|
||||
$terms[count($terms)-1]['cbr'] = 1;
|
||||
$search_filter->addTerms($terms);
|
||||
} else if ( $search != '' ) {
|
||||
} else if ($search != '') {
|
||||
$search = '%' .$search. '%';
|
||||
$terms = array();
|
||||
foreach ( $columns as $col ) {
|
||||
foreach ($columns as $col) {
|
||||
$terms[] = array('cnj'=>'or', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$search);
|
||||
}
|
||||
$terms[0]['obr'] = 1;
|
||||
|
@ -228,15 +245,17 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
|||
} # end if search
|
||||
|
||||
$sql = 'SELECT ' .$col_str. ' FROM `Events` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE '.$search_filter->sql().' ORDER BY ' .$sort. ' ' .$order;
|
||||
ZM\Debug('Calling the following sql query: ' .$sql);
|
||||
$filtered_rows = dbFetchAll($sql);
|
||||
ZM\Debug('Have ' . count($filtered_rows) . ' events matching search filter.');
|
||||
ZM\Debug('Have ' . count($filtered_rows) . ' events matching search filter: '.$sql);
|
||||
} else {
|
||||
$filtered_rows = $unfiltered_rows;
|
||||
} # end if search_filter->terms() > 1
|
||||
|
||||
if ($limit)
|
||||
$filtered_rows = array_slice($filtered_rows, $offset, $limit);
|
||||
|
||||
$returned_rows = array();
|
||||
foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) {
|
||||
foreach ($filtered_rows as $row) {
|
||||
$event = new ZM\Event($row);
|
||||
|
||||
$scale = intval(5*100*ZM_WEB_LIST_THUMB_WIDTH / $event->Width());
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
</div>
|
||||
<div class="modal-body">
|
||||
<?php
|
||||
//require_once('includes/Filter.php');
|
||||
require_once('includes/Filter.php');
|
||||
$fid = validInt($_REQUEST['fid']);
|
||||
if ( !$fid ) {
|
||||
if (!$fid) {
|
||||
echo '<div class="error">No filter id specified.</div>';
|
||||
} else {
|
||||
$filter = new ZM\Filter($_REQUEST['fid']);
|
||||
if ( ! $filter->Id() ) {
|
||||
if (!$filter->Id()) {
|
||||
echo '<div class="error">Filter not found for id '.$_REQUEST['fid'].'</div>';
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,16 @@
|
|||
// We have to manually insert the csrf key into the form when using a modal generated via ajax call
|
||||
echo getCSRFinputHTML();
|
||||
?>
|
||||
<p><label>SQL</label><?php echo $filter->sql() ?></p>
|
||||
<p><label>SQL</label>
|
||||
<?php
|
||||
$sql = 'SELECT E.*,M.Name AS MonitorName,M.DefaultScale<br/>FROM Monitors AS M INNER JOIN Events AS E ON (M.Id = E.MonitorId)<br/>WHERE<br/>';
|
||||
$sql .= $filter->sql();
|
||||
$sql .= $filter->sort_field() ? ' ORDER BY '.$filter->sort_field(). ' ' .($filter->sort_asc() ? 'ASC' : 'DESC') : '';
|
||||
$sql .= $filter->limit() ? ' LIMIT '.$filter->limit() : '';
|
||||
$sql .= $filter->skip_locked() ? ' SKIP LOCKED' : '';
|
||||
|
||||
echo $sql;
|
||||
?></p>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php echo translate('Cancel')?> </button>
|
||||
</div>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue