Merge branch 'master' into multistream

This commit is contained in:
Isaac Connor 2021-12-31 17:53:36 -05:00
commit 6bd04de5f6
174 changed files with 4080 additions and 1851 deletions

View File

@ -4,7 +4,7 @@
web/api/lib web/api/lib
web/includes/csrf/ web/includes/csrf/
web/js/videojs.zoomrotate.js 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/chosen
web/skins/classic/js/dateTimePicker web/skins/classic/js/dateTimePicker
web/skins/classic/js/jquery-*.js web/skins/classic/js/jquery-*.js

View File

@ -167,6 +167,8 @@ set(ZM_NO_X10 "OFF" CACHE BOOL
set(ZM_ONVIF "ON" CACHE BOOL set(ZM_ONVIF "ON" CACHE BOOL
"Set to ON to enable basic ONVIF support. This is EXPERIMENTAL and may not "Set to ON to enable basic ONVIF support. This is EXPERIMENTAL and may not
work with all cameras claiming to be ONVIF compliant. default: ON") work with all cameras claiming to be ONVIF compliant. default: ON")
set(ZM_NO_PCRE "OFF" CACHE BOOL
"Set to ON to skip libpcre3 checks and force building ZM without libpcre3. default: OFF")
set(ZM_NO_RTSPSERVER "OFF" CACHE BOOL set(ZM_NO_RTSPSERVER "OFF" CACHE BOOL
"Set to ON to skip building ZM with rtsp server support. default: OFF") "Set to ON to skip building ZM with rtsp server support. default: OFF")
set(ZM_PERL_MM_PARMS INSTALLDIRS=vendor NO_PACKLIST=1 NO_PERLLOCAL=1 CACHE STRING set(ZM_PERL_MM_PARMS INSTALLDIRS=vendor NO_PACKLIST=1 NO_PERLLOCAL=1 CACHE STRING
@ -407,21 +409,24 @@ else()
message(FATAL_ERROR "ZoneMinder requires pthread but it was not found on your system") message(FATAL_ERROR "ZoneMinder requires pthread but it was not found on your system")
endif() endif()
# pcre (using find_library and find_path) # Do not check for cURL if ZM_NO_CURL is on
find_library(PCRE_LIBRARIES pcre) if(NOT ZM_NO_PRCE)
if(PCRE_LIBRARIES) # pcre (using find_library and find_path)
set(HAVE_LIBPCRE 1) find_library(PCRE_LIBRARIES pcre)
list(APPEND ZM_BIN_LIBS "${PCRE_LIBRARIES}") if(PCRE_LIBRARIES)
find_path(PCRE_INCLUDE_DIR pcre.h) set(HAVE_LIBPCRE 1)
if(PCRE_INCLUDE_DIR) list(APPEND ZM_BIN_LIBS "${PCRE_LIBRARIES}")
include_directories("${PCRE_INCLUDE_DIR}") find_path(PCRE_INCLUDE_DIR pcre.h)
set(CMAKE_REQUIRED_INCLUDES "${PCRE_INCLUDE_DIR}") if(PCRE_INCLUDE_DIR)
include_directories("${PCRE_INCLUDE_DIR}")
set(CMAKE_REQUIRED_INCLUDES "${PCRE_INCLUDE_DIR}")
endif()
mark_as_advanced(FORCE PCRE_LIBRARIES PCRE_INCLUDE_DIR)
check_include_file("pcre.h" HAVE_PCRE_H)
set(optlibsfound "${optlibsfound} PCRE")
else()
set(optlibsnotfound "${optlibsnotfound} PCRE")
endif() endif()
mark_as_advanced(FORCE PCRE_LIBRARIES PCRE_INCLUDE_DIR)
check_include_file("pcre.h" HAVE_PCRE_H)
set(optlibsfound "${optlibsfound} PCRE")
else()
set(optlibsnotfound "${optlibsnotfound} PCRE")
endif() endif()
# mysqlclient (using find_library and find_path) # mysqlclient (using find_library and find_path)
@ -540,6 +545,7 @@ set(ZM_PCRE 0)
if(HAVE_LIBPCRE AND HAVE_PCRE_H) if(HAVE_LIBPCRE AND HAVE_PCRE_H)
set(ZM_PCRE 1) set(ZM_PCRE 1)
endif() endif()
# Check for mmap and enable in all components # Check for mmap and enable in all components
set(ZM_MEM_MAPPED 0) set(ZM_MEM_MAPPED 0)
set(ENABLE_MMAP no) set(ENABLE_MMAP no)

100
cmake/Modules/FindFmt.cmake Normal file
View File

@ -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)

View File

@ -4,6 +4,7 @@
configure_file(zm_create.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" @ONLY) configure_file(zm_create.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" @ONLY)
configure_file(zm_update-1.31.30.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.31.30.sql" @ONLY) configure_file(zm_update-1.31.30.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.31.30.sql" @ONLY)
configure_file(zm_update-1.35.24.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.35.24.sql" @ONLY) configure_file(zm_update-1.35.24.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.35.24.sql" @ONLY)
configure_file(zm_update-1.37.4.sql.in "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.37.4.sql" @ONLY)
# Glob all database upgrade scripts # Glob all database upgrade scripts
file(GLOB dbfileslist RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "zm_update-*.sql") file(GLOB dbfileslist RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "zm_update-*.sql")
@ -15,6 +16,8 @@ install(FILES ${dbfileslist} DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.31.30.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.31.30.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
# install zm_update-1.35.24.sql # install zm_update-1.35.24.sql
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.35.24.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.35.24.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
# install zm_update-1.37.4.sql
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.37.4.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
# install zm_create.sql # install zm_create.sql
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
@ -22,3 +25,8 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" DESTINATION "${CMAKE_I
# install triggers.sql # install triggers.sql
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/triggers.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/triggers.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
# install manufacturers.sql
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/manufacturers.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
# install models.sql
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/models.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")

24
db/manufacturers.sql Normal file
View File

@ -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');

56
db/models.sql Normal file
View File

@ -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');
*/

View File

@ -283,6 +283,7 @@ CREATE TABLE `Filters` (
`Id` int(10) unsigned NOT NULL auto_increment, `Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '', `Name` varchar(64) NOT NULL default '',
`UserId` int(10) unsigned, `UserId` int(10) unsigned,
`ExecuteInterval` int(10) unsigned NOT NULL default '60',
`Query_json` text NOT NULL, `Query_json` text NOT NULL,
`AutoArchive` tinyint(3) unsigned NOT NULL default '0', `AutoArchive` tinyint(3) unsigned NOT NULL default '0',
`AutoUnarchive` tinyint(3) unsigned NOT NULL default '0', `AutoUnarchive` tinyint(3) unsigned NOT NULL default '0',
@ -412,6 +413,7 @@ CREATE TABLE `Models` (
DROP TABLE IF EXISTS `MonitorPresets`; DROP TABLE IF EXISTS `MonitorPresets`;
CREATE TABLE `MonitorPresets` ( CREATE TABLE `MonitorPresets` (
`Id` int(10) unsigned NOT NULL auto_increment, `Id` int(10) unsigned NOT NULL auto_increment,
`ModelId` int unsigned, FOREIGN KEY (`ModelId`) REFERENCES `Models` (Id),
`Name` varchar(64) NOT NULL default '', `Name` varchar(64) NOT NULL default '',
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local', `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local',
`Device` tinytext, `Device` tinytext,
@ -447,6 +449,8 @@ CREATE TABLE `Monitors` (
`Notes` TEXT, `Notes` TEXT,
`ServerId` int(10) unsigned, `ServerId` int(10) unsigned,
`StorageId` smallint(5) unsigned default 0, `StorageId` smallint(5) unsigned default 0,
`ManufacturerId` int unsigned, FOREIGN KEY (`ManufacturerId`) REFERENCES `Manufacturers` (Id),
`ModelId` int unsigned, FOREIGN KEY (`ModelId`) REFERENCES `Models` (Id),
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local', `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite','NVSocket','VNC') NOT NULL default 'Local',
`Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor', `Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor',
`Capturing` enum('None','Ondemand', 'Always') NOT NULL default 'Always', `Capturing` enum('None','Ondemand', 'Always') NOT NULL default 'Always',
@ -457,6 +461,8 @@ CREATE TABLE `Monitors` (
`DecodingEnabled` tinyint(3) unsigned NOT NULL default '1', `DecodingEnabled` tinyint(3) unsigned NOT NULL default '1',
`LinkedMonitors` varchar(255), `LinkedMonitors` varchar(255),
`Triggers` set('X10') NOT NULL default '', `Triggers` set('X10') NOT NULL default '',
`EventStartCommand` VARCHAR(255) NOT NULL DEFAULT '',
`EventEndCommand` VARCHAR(255) NOT NULL DEFAULT '',
`ONVIF_URL` VARCHAR(255) NOT NULL DEFAULT '', `ONVIF_URL` VARCHAR(255) NOT NULL DEFAULT '',
`ONVIF_Username` VARCHAR(64) NOT NULL DEFAULT '', `ONVIF_Username` VARCHAR(64) NOT NULL DEFAULT '',
`ONVIF_Password` VARCHAR(64) NOT NULL DEFAULT '', `ONVIF_Password` VARCHAR(64) NOT NULL DEFAULT '',
@ -976,81 +982,81 @@ INSERT INTO `Controls` VALUES (NULL,'Amcrest HTTP API','Ffmpeg','Amcrest_HTTP',0
-- Add some monitor preset values -- Add some monitor preset values
-- --
INSERT into MonitorPresets VALUES (NULL,'Amcrest, IP8M-T2499EW 640x480, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp','NULL',554,'rtsp://<username>:<password>@<ip-address>/cam/realmonitor?channel=1&subtype=1',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,NULL,'Amcrest, IP8M-T2499EW 640x480, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp','NULL',554,'rtsp://<username>:<password>@<ip-address>/cam/realmonitor?channel=1&subtype=1',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'Amcrest, IP8M-T2499EW 3840x2160, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp','NULL',554,'rtsp://<username>:<password>@<ip-address>/cam/realmonitor?channel=1&subtype=0',NULL,3840,2160,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,NULL,'Amcrest, IP8M-T2499EW 3840x2160, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp','NULL',554,'rtsp://<username>:<password>@<ip-address>/cam/realmonitor?channel=1&subtype=0',NULL,3840,2160,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 320x240, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 640x480, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 320x240, mpjpeg, B&W','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&color=0',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 320x240, mpjpeg, B&W','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&color=0',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP, 640x480, mpjpeg, B&W','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&color=0',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP, 640x480, mpjpeg, B&W','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&color=0',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 320x240, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=320x240&req_fps=5',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,1,4,NULL,'<ip-address>:<port>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=320x240',NULL,320,240,3,5.0,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 640x480, mpjpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=5',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,NULL,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,1,4,NULL,'<ip-address>:<port>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/axis-cgi/jpg/image.cgi?resolution=640x480',NULL,640,480,3,5.0,1,4,NULL,'<ip-address>:<port>',100,100);
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, unicast','Remote','rtsp',0,255,'rtsp','rtpUni','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,NULL,'Axis IP, mpeg4, unicast','Remote','rtsp',0,255,'rtsp','rtpUni','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, multicast','Remote','rtsp',0,255,'rtsp','rtpMulti','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,NULL,'Axis IP, mpeg4, multicast','Remote','rtsp',0,255,'rtsp','rtpMulti','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP','Remote','rtsp',0,255,'rtsp','rtpRtsp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,NULL,'Axis IP, mpeg4, RTP/RTSP','Remote','rtsp',0,255,'rtsp','rtpRtsp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP/HTTP','Remote',NULL,NULL,NULL,'rtsp','rtpRtspHttp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,NULL,'Axis IP, mpeg4, RTP/RTSP/HTTP','Remote',NULL,NULL,NULL,'rtsp','rtpRtspHttp','<ip-address>',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'D-link DCS-930L, 640x480, mjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/mjpeg.cgi',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'D-link DCS-930L, 640x480, mjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/mjpeg.cgi',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'D-Link DCS-5020L, 640x480, mjpeg','Remote','http',0,0,'http','simple','<username>:<pwd>@<ip-address>','80','/video.cgi',NULL,640,480,0,NULL,1,'34',NULL,'<username>:<pwd>@<ip-address>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'D-Link DCS-5020L, 640x480, mjpeg','Remote','http',0,0,'http','simple','<username>:<pwd>@<ip-address>','80','/video.cgi',NULL,640,480,0,NULL,1,'34',NULL,'<username>:<pwd>@<ip-address>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP PTZ, 320x240, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP PTZ, 320x240, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,1,5,NULL,'<ip-address>:<port>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP PTZ, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP PTZ, 640x480, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/nphMotionJpeg?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP PTZ, 640x480, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,NULL,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,1,5,NULL,'<ip-address>:<port>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Panasonic IP PTZ, 640x480, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/SnapshotJPEG?Resolution=640x480&Quality=Standard',NULL,640,480,3,5.0,1,5,NULL,'<ip-address>:<port>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Gadspot IP, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Gadspot IP, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/GetData.cgi',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/GetData.cgi',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Gadspot IP, mpjpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'IP Webcam by Pavel Khlebovich 1920x1080','Remote','/dev/video<?>','0',255,'http','simple','<ip-address>','8080','/video','',1920,1080,0,NULL,0,'0','','',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'IP Webcam by Pavel Khlebovich 1920x1080','Remote','/dev/video<?>','0',255,'http','simple','<ip-address>','8080','/video','',1920,1080,0,NULL,0,'0','','',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'VEO Observer, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'VEO Observer, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/Jpeg/CamImg.jpg',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Blue Net Video Server, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/cgi-bin/image.cgi?control=0&id=admin&passwd=admin',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Blue Net Video Server, jpeg','Remote','http',0,0,'http','simple','<ip-address>',80,'/cgi-bin/image.cgi?control=0&id=admin&passwd=admin',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT into MonitorPresets VALUES (NULL,'ACTi IP, mpeg4, unicast','Remote',NULL,NULL,NULL,'rtsp','rtpUni','<ip-address>',7070,'','/track',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,NULL,'ACTi IP, mpeg4, unicast','Remote',NULL,NULL,NULL,'rtsp','rtpUni','<ip-address>',7070,'','/track',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://<host/address>/axis-media/media.amp?videocodec=h264',NULL,NULL,NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://<host/address>/axis-media/media.amp?videocodec=h264',NULL,NULL,NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Vivotek FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://<host/address>:554/live.sdp',NULL,NULL,NULL,352,240,NULL,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Vivotek FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://<host/address>:554/live.sdp',NULL,NULL,NULL,352,240,NULL,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Axis FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://<host/address>/axis-media/media.amp',NULL,NULL,NULL,640,480,NULL,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Axis FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://<host/address>/axis-media/media.amp',NULL,NULL,NULL,640,480,NULL,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'ACTi TCM FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://admin:123456@<host/address>:7070',NULL,NULL,NULL,320,240,NULL,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'ACTi TCM FFMPEG','Ffmpeg',NULL,NULL,NULL,NULL,NULL,'rtsp://admin:123456@<host/address>:7070',NULL,NULL,NULL,320,240,NULL,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 320x240','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), PAL, 320x240','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 320x240, max 5 FPS','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), PAL, 320x240, max 5 FPS','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 640x480','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), PAL, 640x480','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), PAL, 640x480, max 5 FPS','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), PAL, 640x480, max 5 FPS','Local','/dev/video<?>',0,255,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 320x240','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), NTSC, 320x240','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 320x240, max 5 FPS','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), NTSC, 320x240, max 5 FPS','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,320,240,1345466932,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 640x480','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), NTSC, 640x480','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L2), NTSC, 640x480, max 5 FPS','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L2), NTSC, 640x480, max 5 FPS','Local','/dev/video<?>',0,45056,NULL,'v4l2',NULL,NULL,NULL,NULL,640,480,1345466932,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 320x240','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), PAL, 320x240','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 320x240, max 5 FPS','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), PAL, 320x240, max 5 FPS','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 640x480','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), PAL, 640x480','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), PAL, 640x480, max 5 FPS','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), PAL, 640x480, max 5 FPS','Local','/dev/video<?>',0,0,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 320x240','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), NTSC, 320x240','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 320x240, max 5 FPS','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), NTSC, 320x240, max 5 FPS','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,320,240,13,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 640x480','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), NTSC, 640x480','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'BTTV Video (V4L1), NTSC, 640x480, max 5 FPS','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'BTTV Video (V4L1), NTSC, 640x480, max 5 FPS','Local','/dev/video<?>',0,1,NULL,'v4l1',NULL,NULL,NULL,NULL,640,480,13,5.0,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Remote ZoneMinder','Remote',NULL,NULL,NULL,'http','simple','<ip-address>',80,'/cgi-bin/nph-zms?mode=jpeg&monitor=<monitor-id>&scale=100&maxfps=5&buffer=0',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Remote ZoneMinder','Remote',NULL,NULL,NULL,'http','simple','<ip-address>',80,'/cgi-bin/nph-zms?mode=jpeg&monitor=<monitor-id>&scale=100&maxfps=5&buffer=0',NULL,NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Foscam FI8620 FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://<username>:<pwd>@<ip-address>:554/11',NULL,704,576,0,NULL,1,'10','<admin_pwd>','<ip-address>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Foscam FI8620 FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://<username>:<pwd>@<ip-address>:554/11',NULL,704,576,0,NULL,1,'10','<admin_pwd>','<ip-address>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Foscam FI8608W FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://<username>:<pwd>@<ip-address>:554/11',NULL,640,480,0,NULL,1,'11','<admin_pwd>','<ip-address>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Foscam FI8608W FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://<username>:<pwd>@<ip-address>:554/11',NULL,640,480,0,NULL,1,'11','<admin_pwd>','<ip-address>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Foscam FI9821W FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://<username>:<pwd>@<ip-address>:88/videoMain',NULL,1280,720,0,NULL,1,'12','<admin_pwd>','<ip-address>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Foscam FI9821W FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://<username>:<pwd>@<ip-address>:88/videoMain',NULL,1280,720,0,NULL,1,'12','<admin_pwd>','<ip-address>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Loftek Sentinel PTZ, 640x480, mjpeg','Remote','http',0,0,NULL,NULL,'<ip-address>','80','/videostream.cgi?user=<username>&pwd=<password>&resolution=32&rate=11',NULL,640,480,4,NULL,1,'13','','<username>:<pwd>@<ip-address>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Loftek Sentinel PTZ, 640x480, mjpeg','Remote','http',0,0,NULL,NULL,'<ip-address>','80','/videostream.cgi?user=<username>&pwd=<password>&resolution=32&rate=11',NULL,640,480,4,NULL,1,'13','','<username>:<pwd>@<ip-address>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Airlink 777W PTZ, 640x480, mjpeg','Remote','http',0,0,NULL,NULL,'<username>:<password>@<ip-address>','80','/cgi/mjpg/mjpg.cgi',NULL,640,480,4,NULL,1,'7','','<username>:<pwd>@<ip-address>',100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Airlink 777W PTZ, 640x480, mjpeg','Remote','http',0,0,NULL,NULL,'<username>:<password>@<ip-address>','80','/cgi/mjpg/mjpg.cgi',NULL,640,480,4,NULL,1,'7','','<username>:<pwd>@<ip-address>',100,100);
INSERT INTO MonitorPresets VALUES (NULL,'SunEyes SP-P1802SWPTZ','Libvlc','/dev/video<?>','0',255,'','rtpMulti','','80','rtsp://<ip-address>:554/11','',1920,1080,0,0.00,1,'16','-speed=64','<ip-address>:<port>',100,33); INSERT INTO MonitorPresets VALUES (NULL,NULL,'SunEyes SP-P1802SWPTZ','Libvlc','/dev/video<?>','0',255,'','rtpMulti','','80','rtsp://<ip-address>:554/11','',1920,1080,0,0.00,1,'16','-speed=64','<ip-address>:<port>',100,33);
INSERT INTO MonitorPresets VALUES (NULL,'Qihan IP, 1280x720, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp',NULL,554,'rtsp://<ip-address>/tcp_live/ch0_0',NULL,1280,720,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Qihan IP, 1280x720, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp',NULL,554,'rtsp://<ip-address>/tcp_live/ch0_0',NULL,1280,720,3,NULL,0,NULL,NULL,NULL,100,100);
INSERT INTO MonitorPresets VALUES (NULL,'Qihan IP, 1920x1080, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp',NULL,554,'rtsp://<ip-address>/tcp_live/ch0_0',NULL,1920,1080,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,NULL,'Qihan IP, 1920x1080, RTP/RTSP','Ffmpeg','rtsp',0,255,'rtsp','rtpRtsp',NULL,554,'rtsp://<ip-address>/tcp_live/ch0_0',NULL,1920,1080,3,NULL,0,NULL,NULL,NULL,100,100);
-- --
-- Add some zone preset values -- Add some zone preset values
@ -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. -- We generally don't alter triggers, we drop and re-create them, so let's keep them in a separate file that we can just source in update scripts.
source @PKGDATADIR@/db/triggers.sql source @PKGDATADIR@/db/triggers.sql
source @PKGDATADIR@/db/manufacturers.sql
source @PKGDATADIR@/db/models.sql
-- --
-- Apply the initial configuration -- Apply the initial configuration
-- --

View File

@ -28,8 +28,8 @@ SET @s = (SELECT IF(
AND table_name = 'Monitors' AND table_name = 'Monitors'
AND column_name = 'TotalEvents' AND column_name = 'TotalEvents'
) > 0, ) > 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; PREPARE stmt FROM @s;
EXECUTE stmt; EXECUTE stmt;
@ -50,8 +50,8 @@ SET @s = (SELECT IF(
AND table_name = 'Monitors' AND table_name = 'Monitors'
AND column_name = 'TotalEventDiskSpace' AND column_name = 'TotalEventDiskSpace'
) > 0, ) > 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; PREPARE stmt FROM @s;
EXECUTE stmt; EXECUTE stmt;

View File

@ -56,7 +56,7 @@ EXECUTE stmt;
SET @s = (SELECT IF( SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Monitor_Status' AND table_name = 'Monitor_Status'
AND column_name = 'DayEvents' AND column_name = 'DayEventDiskSpace'
) > 0, ) > 0,
"ALTER TABLE `Monitor_Status` DROP `DayEventDiskSpace`", "ALTER TABLE `Monitor_Status` DROP `DayEventDiskSpace`",
"SELECT 'Column DayEventDiskSpace already removed from Monitor_Status'" "SELECT 'Column DayEventDiskSpace already removed from Monitor_Status'"

47
db/zm_update-1.35.29.sql Normal file
View File

@ -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;

View File

@ -1,60 +1,74 @@
SET @s = (SELECT IF( SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Monitors' AND table_name = 'Monitors'
AND column_name = 'Capturing' AND column_name = 'ManufacturerId'
) > 0, ) > 0,
"SELECT 'Column Capturing already exists in Monitors'", "SELECT 'Column ManufacturerId already exists in Monitors'",
"ALTER TABLE `Monitors` ADD `Capturing` enum('None','Ondemand', 'Always') NOT NULL default 'Always' AFTER `Function`" "ALTER TABLE `Monitors` ADD `ManufacturerId` int(10) unsigned AFTER `StorageId`"
)); ));
PREPARE stmt FROM @s; PREPARE stmt FROM @s;
EXECUTE stmt; EXECUTE stmt;
SET @s = (SELECT IF( 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() (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Monitors' AND table_name = 'Monitors'
AND column_name = 'Analysing' AND column_name = 'ModelId'
) > 0, ) > 0,
"SELECT 'Column Analysing already exists in Monitors'", "SELECT 'Column ModelId already exists in Monitors'",
"ALTER TABLE `Monitors` ADD `Analysing` enum('None','Always') NOT NULL default 'Always'" "ALTER TABLE `Monitors` ADD `ModelId` int(10) unsigned AFTER `ManufacturerId`"
)); ));
PREPARE stmt FROM @s; PREPARE stmt FROM @s;
EXECUTE stmt; EXECUTE stmt;
SET @s = (SELECT IF( SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() (SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE table_schema = DATABASE()
AND table_name = 'Monitors' AND table_name = 'Monitors'
AND column_name = 'AnalysisSource' AND column_name = 'ModelId'
) > 0, ) > 0,
"SELECT 'Column AnalysisSource already exists in Monitors'", "SELECT 'FOREIGN KEY for ModelId already exists in Monitors'",
"ALTER TABLE `Monitors` ADD `AnalysisSource` enum('Primary','Secondary') NOT NULL DEFAULT 'Primary' AFTER `Analysing`" "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; PREPARE stmt FROM @s;
EXECUTE stmt; EXECUTE stmt;
SET @s = (SELECT IF( SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() (SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE table_schema = DATABASE()
AND table_name = 'Monitors' AND table_name = 'MonitorPresets'
AND column_name = 'Recording' AND column_name = 'ModelId'
) > 0, ) > 0,
"SELECT 'Column Recording already exists in Monitors'", "SELECT 'FOREIGN KEY for ModelId already exists in MonitorPresets'",
"ALTER TABLE `Monitors` ADD `Recording` enum('None', 'OnMotion', 'Always') NOT NULL default 'Always'" "ALTER TABLE `MonitorPresets` ADD FOREIGN KEY (`ModelId`) REFERENCES `Models` (Id)"
)); ));
PREPARE stmt FROM @s; PREPARE stmt FROM @s;
EXECUTE stmt; EXECUTE stmt;
SET @s = (SELECT IF( UPDATE `MonitorPresets` SET `ModelId`=(SELECT `Id` FROM `Models` WHERE `Name`='IP8M-T2499EW') WHERE `Name` like 'Amcrest, IP8M-T2499EW
(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;

View File

@ -0,0 +1,2 @@
source @PKGDATADIR@/db/manufacturers.sql
source @PKGDATADIR@/db/models.sql

31
db/zm_update-1.37.5.sql Normal file
View File

@ -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;

18
db/zm_update-1.37.6.sql Normal file
View File

@ -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;

View File

@ -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;

View File

@ -36,7 +36,7 @@
%global _hardened_build 1 %global _hardened_build 1
Name: zoneminder Name: zoneminder
Version: 1.37.1 Version: 1.37.6
Release: 1%{?dist} Release: 1%{?dist}
Summary: A camera monitoring and analysis tool Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons Group: System Environment/Daemons

View File

@ -16,7 +16,6 @@ Build-Depends: debhelper (>= 11), sphinx-doc, python3-sphinx, dh-linktree, dh-ap
,libjpeg-turbo8-dev | libjpeg62-turbo-dev | libjpeg8-dev | libjpeg9-dev ,libjpeg-turbo8-dev | libjpeg62-turbo-dev | libjpeg8-dev | libjpeg9-dev
,libturbojpeg0-dev ,libturbojpeg0-dev
,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat ,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat
,libpcre3-dev
,libpolkit-gobject-1-dev ,libpolkit-gobject-1-dev
,libv4l-dev [!hurd-any] ,libv4l-dev [!hurd-any]
,libvlc-dev ,libvlc-dev
@ -70,7 +69,6 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,policykit-1 ,policykit-1
,rsyslog | system-log-daemon ,rsyslog | system-log-daemon
,zip ,zip
,libpcre3
,libcrypt-eksblowfish-perl ,libcrypt-eksblowfish-perl
,libdata-entropy-perl ,libdata-entropy-perl
,libvncclient1|libvncclient0 ,libvncclient1|libvncclient0

View File

@ -19,6 +19,7 @@ override_dh_auto_configure:
-DCMAKE_VERBOSE_MAKEFILE=ON \ -DCMAKE_VERBOSE_MAKEFILE=ON \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DBUILD_MAN=0 \ -DBUILD_MAN=0 \
-DZM_NO_PCRE=ON \
-DZM_CONFIG_DIR="/etc/zm" \ -DZM_CONFIG_DIR="/etc/zm" \
-DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \ -DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \
-DZM_RUNDIR="/run/zm" \ -DZM_RUNDIR="/run/zm" \

View File

@ -5,6 +5,12 @@ set +e
create_db () { create_db () {
echo "Checking for db" echo "Checking for db"
mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload 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... # test if database if already present...
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
echo "Creating zm db" echo "Creating zm db"

View File

@ -225,7 +225,7 @@ change the 3 to a 1
I can't see more than 6 monitors in montage on my browser 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? Why is ZoneMinder using so much CPU?
--------------------------------------- ---------------------------------------

View File

@ -3,6 +3,50 @@ Debian
.. contents:: .. 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 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: You can do this using:
.. code-block:: ::
echo "deb https://zmrepo.zoneminder.com/debian/release-1.36 buster/" | sudo tee /etc/apt/sources.list.d/zoneminder.list echo "deb https://zmrepo.zoneminder.com/debian/release-1.36 buster/" | sudo tee /etc/apt/sources.list.d/zoneminder.list

View File

@ -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 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>`__. * 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. In both cases, instructions are provided in the repo README files.

View File

@ -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: 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 * 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 * Try ZoneMinder's new ONVIF probe feature
* Download and install the `ONVIF Device Manager <https://sourceforge.net/projects/onvifdm/>`__ onto a Windows machine * 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 * 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. The storage section allows for each monitor to configure if and how video and audio are recorded.
Save JPEGs 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. * 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. * 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. * 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 invidual JPEG frames with analysis information overlaid. * 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 Video Writer
Records video in real video format. It provides much better compression results than saving JPEGs, thus longer video history can be stored. Records video in real video format. It provides much better compression results than saving JPEGs, thus longer video history can be stored.

View File

@ -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. * 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: 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. * 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. * 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. * 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. * 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.

View File

@ -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`. * **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. * **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`. * **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. * **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. * **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. * **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.

View File

@ -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 .. 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 - 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 - 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) - 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 .. 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.

View File

@ -147,7 +147,7 @@ This attribute is of type L<SOAP::WSDL::XSD::Typelib::Builtin::integer|SOAP::WSD
=item * Cells =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).

View File

@ -2709,7 +2709,7 @@ Returns a L<ONVIF::Device::Elements::GetNetworkInterfacesResponse|ONVIF::Device:
=head3 SetNetworkInterfaces =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 isnt 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. 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 =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. 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 =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. 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 =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. Returns a L<ONVIF::Device::Elements::StartSystemRestoreResponse|ONVIF::Device::Elements::StartSystemRestoreResponse> object.

View File

@ -147,7 +147,7 @@ This attribute is of type L<SOAP::WSDL::XSD::Typelib::Builtin::integer|SOAP::WSD
=item * Cells =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).

View File

@ -2147,7 +2147,7 @@ Returns a L<ONVIF::Media::Elements::GetAudioOutputsResponse|ONVIF::Media::Elemen
=head3 CreateProfile =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. Returns a L<ONVIF::Media::Elements::CreateProfileResponse|ONVIF::Media::Elements::CreateProfileResponse> object.

View File

@ -44,7 +44,7 @@ not checked yet.
The current implementation of union resorts to inheriting from the base type, 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> 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."

View File

@ -147,7 +147,7 @@ This attribute is of type L<SOAP::WSDL::XSD::Typelib::Builtin::integer|SOAP::WSD
=item * Cells =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).

View File

@ -44,7 +44,7 @@ not checked yet.
The current implementation of union resorts to inheriting from the base type, 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> 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."

View File

@ -987,7 +987,7 @@ Returns a L<ONVIF::PTZ::Elements::GotoHomePositionResponse|ONVIF::PTZ::Elements:
=head3 SetHomePosition =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. Returns a L<ONVIF::PTZ::Elements::SetHomePositionResponse|ONVIF::PTZ::Elements::SetHomePositionResponse> object.

View File

@ -44,7 +44,7 @@ not checked yet.
The current implementation of union resorts to inheriting from the base type, 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> 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."

View File

@ -147,7 +147,7 @@ This attribute is of type L<SOAP::WSDL::XSD::Typelib::Builtin::integer|SOAP::WSD
=item * Cells =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).

View File

@ -44,7 +44,7 @@ not checked yet.
The current implementation of union resorts to inheriting from the base type, 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> 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."

View File

@ -44,7 +44,7 @@ not checked yet.
The current implementation of union resorts to inheriting from the base type, 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> 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."

View File

@ -44,7 +44,7 @@ not checked yet.
The current implementation of union resorts to inheriting from the base type, 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> 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."

View File

@ -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 You may pass any combination of objects, hash and list refs to these
methods, as long as you meet the structure. 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 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 ref - this may result in invalid XML if used improperly, though. Note that
SOAP::WSDL always expects list references at maximum depth position. SOAP::WSDL always expects list references at maximum depth position.

View File

@ -44,7 +44,7 @@ not checked yet.
The current implementation of union resorts to inheriting from the base type, 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> 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."

View File

@ -44,7 +44,7 @@ not checked yet.
The current implementation of union resorts to inheriting from the base type, 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> 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."

View File

@ -44,7 +44,7 @@ not checked yet.
The current implementation of union resorts to inheriting from the base type, 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> 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."

View File

@ -44,7 +44,7 @@ not checked yet.
The current implementation of union resorts to inheriting from the base type, 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> 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."

View File

@ -44,7 +44,7 @@ not checked yet.
The current implementation of union resorts to inheriting from the base type, 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> 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."

View File

@ -44,7 +44,7 @@ not checked yet.
The current implementation of union resorts to inheriting from the base type, 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> 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."

View File

@ -1064,7 +1064,7 @@ our @options = (
}, },
{ {
name => 'ZM_FFMPEG_FORMATS', 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', description => 'Formats to allow for ffmpeg video generation',
help => q` help => q`
Ffmpeg can generate video in many different formats. This Ffmpeg can generate video in many different formats. This

View File

@ -29,6 +29,7 @@ use strict;
use warnings; use warnings;
require ZoneMinder::Base; require ZoneMinder::Base;
require ZoneMinder::Object;
require ZoneMinder::Monitor; require ZoneMinder::Monitor;
our $VERSION = $ZoneMinder::Base::VERSION; our $VERSION = $ZoneMinder::Base::VERSION;
@ -42,24 +43,116 @@ our $VERSION = $ZoneMinder::Base::VERSION;
use ZoneMinder::Logger qw(:all); use ZoneMinder::Logger qw(:all);
use ZoneMinder::Database 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; 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 { sub AUTOLOAD {
my $self = shift; my $self = shift;
my $class = ref($self); my $class = ref($self);
@ -79,24 +172,24 @@ sub AUTOLOAD {
sub getKey { sub getKey {
my $self = shift; my $self = shift;
return $self->{id}; return $self->{Id};
} }
sub open { sub open {
my $self = shift; my $self = shift;
Fatal('No open method defined for protocol '.$self->{name}); Fatal('No open method defined for protocol '.$self->{Protocol});
} }
sub close { sub close {
my $self = shift; my $self = shift;
$self->{state} = 'closed'; $self->{state} = 'closed';
Debug('No close method defined for protocol '.$self->{name}); Debug('No close method defined for protocol '.$self->{Protocol});
} }
sub loadMonitor { sub loadMonitor {
my $self = shift; my $self = shift;
if ( !$self->{Monitor} ) { 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'); Fatal('Monitor id '.$self->{id}.' not found');
} }
if ( defined($self->{Monitor}->{AutoStopTimeout}) ) { if ( defined($self->{Monitor}->{AutoStopTimeout}) ) {

View File

@ -51,11 +51,21 @@ sub open {
my $self = shift; my $self = shift;
$self->loadMonitor(); $self->loadMonitor();
if ( $self->{Monitor}->{ControlAddress} !~ /^\w+:\/\// ) {
# Has no scheme at the beginning, so won't parse as a URI if ($self->{Monitor}->{ControlAddress} and ($self->{Monitor}->{ControlAddress} ne 'user:pass@ip')) {
$self->{Monitor}->{ControlAddress} = 'http://'.$self->{Monitor}->{ControlAddress}; Debug("Getting connection details from Control Address " . $self->{Monitor}->{ControlAddress});
} if ( $self->{Monitor}->{ControlAddress} !~ /^\w+:\/\// ) {
$uri = URI->new($self->{Monitor}->{ControlAddress}); # Has no scheme at the beginning, so won't parse as a URI
$self->{Monitor}->{ControlAddress} = 'http://'.$self->{Monitor}->{ControlAddress};
}
$uri = URI->new($self->{Monitor}->{ControlAddress});
} elsif ($self->{Monitor}->{Path}) {
Debug("Getting connection details from Path " . $self->{Monitor}->{Path});
$uri = URI->new($self->{Monitor}->{Path});
$uri->scheme('http');
$uri->port(80);
$uri->path('');
}
use LWP::UserAgent; use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new; $self->{ua} = LWP::UserAgent->new;
@ -64,6 +74,7 @@ sub open {
$self->{state} = 'closed'; $self->{state} = 'closed';
my ( $username, $password, $host ) = ( $uri->authority() =~ /^([^:]+):([^@]*)@(.+)$/ ); my ( $username, $password, $host ) = ( $uri->authority() =~ /^([^:]+):([^@]*)@(.+)$/ );
Debug("Have username: $username password: $password host: $host from authority:" . $uri->authority());
$uri->userinfo(undef); $uri->userinfo(undef);
@ -75,40 +86,47 @@ sub open {
# test auth # test auth
my $res = $self->{ua}->get($uri->canonical().$url); my $res = $self->{ua}->get($uri->canonical().$url);
if ( $res->is_success ) { if ($res->is_success) {
if ( $res->content() ne "Properties.PTZ.PTZ=yes\n" ) { if ($res->content() ne "Properties.PTZ.PTZ=yes\n") {
Warning('Response suggests that camera doesn\'t support PTZ. Content:('.$res->content().')'); Warning('Response suggests that camera doesn\'t support PTZ. Content:('.$res->content().')');
} }
$self->{state} = 'open'; $self->{state} = 'open';
return; return;
} }
if ($res->status_line() eq '404 Not Found') {
#older style
$url = 'axis-cgi/com/ptz.cgi';
$res = $self->{ua}->get($uri->canonical().$url);
Debug("Result from getting ".$uri->canonical().$url . ':' . $res->status_line());
}
if ( $res->status_line() eq '401 Unauthorized' ) { if ($res->status_line() eq '401 Unauthorized') {
my $headers = $res->headers(); my $headers = $res->headers();
foreach my $k ( keys %$headers ) { foreach my $k ( keys %$headers ) {
Debug("Initial Header $k => $$headers{$k}"); Debug("Initial Header $k => $$headers{$k}");
} }
if ( $$headers{'www-authenticate'} ) { if ( $$headers{'www-authenticate'} ) {
my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/; foreach my $auth_header ( ref $$headers{'www-authenticate'} eq 'ARRAY' ? @{$$headers{'www-authenticate'}} : ($$headers{'www-authenticate'})) {
if ( $tokens =~ /\w+="([^"]+)"/i ) { my ( $auth, $tokens ) = $auth_header =~ /^(\w+)\s+(.*)$/;
if ( $realm ne $1 ) { if ( $tokens =~ /\w+="([^"]+)"/i ) {
$realm = $1; if ( $realm ne $1 ) {
$self->{ua}->credentials($uri->host_port(), $realm, $username, $password); $realm = $1;
$res = $self->{ua}->get($uri->canonical().$url); $self->{ua}->credentials($uri->host_port(), $realm, $username, $password);
if ( $res->is_success() ) { $res = $self->{ua}->get($uri->canonical().$url);
Info("Auth succeeded after setting realm to $realm. You can set this value in the Control Device field to speed up connections and remove these log entries."); if ( $res->is_success() ) {
$self->{state} = 'open'; Info("Auth succeeded after setting realm to $realm. You can set this value in the Control Device field to speed up connections and remove these log entries.");
return; $self->{state} = 'open';
return;
}
Error('Authentication still failed after updating REALM status: '.$res->status_line);
} else {
Error('Authentication failed, not a REALM problem');
} }
Error('Authentication still failed after updating REALM status: '.$res->status_line);
} else { } else {
Error('Authentication failed, not a REALM problem'); Error('Failed to match realm in tokens');
} } # end if
} else { } # end foreach auth header
Error('Failed to match realm in tokens');
} # end if
} else { } else {
Debug('No headers line'); Debug('No headers line');
} # end if headers } # end if headers

View File

@ -41,120 +41,133 @@ our @ISA = qw(ZoneMinder::Control);
use ZoneMinder::Logger qw(:all); use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all); use ZoneMinder::Config qw(:all);
use ZoneMinder::General qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
use URI::Encode qw(uri_encode);
sub open our $REALM = '';
{ our $PROTOCOL = 'http://';
my $self = shift; our $USERNAME = 'admin';
our $PASSWORD = '';
our $ADDRESS = '';
our $BASE_URL = '';
$self->loadMonitor(); sub open {
Debug( "Camera open" ); my $self = shift;
use LWP::UserAgent; $self->loadMonitor();
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
$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 sub close {
{ my $self = shift;
my $self = shift; $self->{state} = 'closed';
$self->{state} = 'closed';
} }
sub printMsg sub sendCmd {
{ my ($self, $cmd, $speedcmd) = @_;
my $msg = shift;
my $msg_len = length($msg);
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 sub moveConUp {
{ my ($self, $params) = @_;
my ($self, $cmd, $speedcmd) = @_; my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6);
$self->sendCmd( 'move=up', $speed );
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 sub moveConDown {
{ my ($self, $params) = @_;
my ($self, $params) = @_; my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6);
my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6); $self->sendCmd( 'move=down', $speed );
Debug( "Move Up" );
$self->sendCmd( 'move=up', $speed );
} }
sub moveConDown sub moveConLeft {
{ my ($self, $params) = @_;
my ($self, $params) = @_; my $speed = 'speedpan=-' . $params->{panspeed};
my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6); $self->sendCmd( 'move=left', $speed );
Debug( "Move Down" );
$self->sendCmd( 'move=down', $speed );
} }
sub moveConLeft sub moveConRight {
{ my ($self, $params) = @_;
my ($self, $params) = @_; my $speed = 'speedpan=' . ($params->{panspeed} - 6);
my $speed = 'speedpan=-' . $params->{panspeed}; $self->sendCmd( 'move=right', $speed );
Debug( "Move Left" );
$self->sendCmd( 'move=left', $speed );
} }
sub moveConRight sub moveStop {
{ my $self = shift;
my ($self, $params) = @_; Debug( "Move Stop: not implemented" );
my $speed = 'speedpan=' . ($params->{panspeed} - 6); # not implemented
Debug( "Move Right" );
$self->sendCmd( 'move=right', $speed );
} }
sub moveStop sub zoomConTele {
{ my ($self, $params) = @_;
my $self = shift; my $speed = 'speedzoom=' . ($params->{speed} - 6);
Debug( "Move Stop" ); $self->sendCmd( 'zoom=tele', $speed );
# not implemented
} }
sub zoomConTele sub zoomConWide {
{ my ($self, $params) = @_;
my ($self, $params) = @_; my $speed = 'speedzoom=' . ($params->{speed} - 6);
my $speed = 'speedzoom=' . ($params->{speed} - 6); $self->sendCmd( 'zoom=wide', $speed );
Debug( "Zoom In" );
$self->sendCmd( 'zoom=tele', $speed );
} }
sub zoomConWide sub reset {
{ my $self = shift;
my ($self, $params) = @_; $self->sendCmd( 'move=home' );
my $speed = 'speedzoom=' . ($params->{speed} - 6);
Debug( "Zoom Out" );
$self->sendCmd( 'zoom=wide', $speed );
} }
sub reset sub get_config {
{ my $self = shift;
my $self = shift;
Debug( "Camera Reset" ); my $url = $BASE_URL.'/cgi-bin/admin/lsctrl.cgi?cmd=queryStatus&retType=javascript';
$self->sendCmd( 'move=home' ); 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; 1;

View File

@ -43,6 +43,7 @@ require Date::Parse;
require POSIX; require POSIX;
use Date::Format qw(time2str); use Date::Format qw(time2str);
use Time::HiRes qw(gettimeofday tv_interval stat); use Time::HiRes qw(gettimeofday tv_interval stat);
use Scalar::Util qw(looks_like_number);
#our @ISA = qw(ZoneMinder::Object); #our @ISA = qw(ZoneMinder::Object);
use parent qw(ZoneMinder::Object); use parent qw(ZoneMinder::Object);
@ -584,6 +585,7 @@ sub DiskSpace {
return $_[0]{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 { sub CopyTo {
my ( $self, $NewStorage ) = @_; my ( $self, $NewStorage ) = @_;
@ -600,7 +602,7 @@ sub CopyTo {
# First determine if we can move it to the dest. # First determine if we can move it to the dest.
# We do this before bothering to lock the event # We do this before bothering to lock the event
my ( $NewPath ) = ( $NewStorage->Path() =~ /^(.*)$/ ); # De-taint my ( $NewPath ) = ( $NewStorage->Path() =~ /^(.*)$/ ); # De-taint
if ( ! $$NewStorage{Id} ) { if ( ! looks_like_number($$NewStorage{Id}) ) {
return 'New storage does not have an id. Moving will not happen.'; return 'New storage does not have an id. Moving will not happen.';
} elsif ( $$NewStorage{Id} == $$self{StorageId} ) { } elsif ( $$NewStorage{Id} == $$self{StorageId} ) {
return 'Event is already located at ' . $NewPath; return 'Event is already located at ' . $NewPath;
@ -614,16 +616,12 @@ sub CopyTo {
Debug("$NewPath is good"); 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. # data is reloaded, so need to check that the move hasn't already happened.
if ( $$self{StorageId} == $$NewStorage{Id} ) { if ( $$self{StorageId} == $$NewStorage{Id} ) {
$ZoneMinder::Database::dbh->commit();
return 'Event has already been moved by someone else.'; return 'Event has already been moved by someone else.';
} }
if ( $$OldStorage{Id} != $$self{StorageId} ) { if ( $$OldStorage{Id} != $$self{StorageId} ) {
$ZoneMinder::Database::dbh->commit();
return 'Old Storage path changed, Event has moved somewhere else.'; return 'Old Storage path changed, Event has moved somewhere else.';
} }
@ -661,39 +659,21 @@ sub CopyTo {
} }
my $event_path = $subpath.$self->RelativePath(); 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/*"); my @files = glob("$OldPath/*");
Debug("Files to move @files"); Debug("Files to move @files");
foreach my $file ( @files ) { foreach my $file (@files) {
next if $file =~ /^\./; next if $file =~ /^\./;
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint ($file) = ($file =~ /^(.*)$/); # De-taint
my $starttime = [gettimeofday]; my $starttime = [gettimeofday];
Debug("Moving file $file to $NewPath"); Debug("Moving file $file to $NewPath");
my $size = -s $file; my $size = -s $file;
if ( ! $size ) { if (!$size) {
Info('Not moving file with 0 size'); Info('Not moving file with 0 size');
} }
if ( 0 ) { my $filename = $event_path.'/'.File::Basename::basename($file);
my $file_contents = File::Slurp::read_file($file); if (!$bucket->add_key_filename($filename, $file)) {
if ( ! $file_contents ) { die "Unable to add key for $filename " . $s3->err . ': '. $s3->errstr;
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 $duration = tv_interval($starttime); my $duration = tv_interval($starttime);
@ -704,16 +684,15 @@ sub CopyTo {
}; };
Error($@) if $@; Error($@) if $@;
} else { } 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 Url
} # end if s3 } # end if s3
my $error = ''; my $error = '';
if ( !$moved ) { if (!$moved) {
File::Path::make_path($NewPath, {error => \my $err}); File::Path::make_path($NewPath, {error => \my $err});
if ( @$err ) { if (@$err) {
for my $diag (@$err) { for my $diag (@$err) {
my ($file, $message) = %$diag; my ($file, $message) = %$diag;
next if $message eq 'File exists'; next if $message eq 'File exists';
@ -724,23 +703,16 @@ sub CopyTo {
} }
} }
} }
if ( $error ) { return $error if $error;
$ZoneMinder::Database::dbh->commit();
return $error;
}
my @files = glob("$OldPath/*"); my @files = glob("$OldPath/*");
if ( ! @files ) { return 'No files to move.' if !@files;
$ZoneMinder::Database::dbh->commit();
return 'No files to move.';
}
for my $file (@files) { for my $file (@files) {
next if $file =~ /^\./; next if $file =~ /^\./;
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint ($file) = ($file =~ /^(.*)$/); # De-taint
my $starttime = [gettimeofday]; my $starttime = [gettimeofday];
Debug("Moving file $file to $NewPath");
my $size = -s $file; my $size = -s $file;
if ( ! File::Copy::copy( $file, $NewPath ) ) { if (!File::Copy::copy($file, $NewPath)) {
$error .= "Copy failed: for $file to $NewPath: $!"; $error .= "Copy failed: for $file to $NewPath: $!";
last; last;
} }
@ -749,34 +721,38 @@ sub CopyTo {
} # end foreach file. } # end foreach file.
} # end if ! moved } # end if ! moved
if ( $error ) { return $error;
$ZoneMinder::Database::dbh->commit();
return $error;
}
} # end sub CopyTo } # end sub CopyTo
sub MoveTo { sub MoveTo {
my ( $self, $NewStorage ) = @_; my ($self, $NewStorage) = @_;
if ( !$self->canEdit() ) { if (!$self->canEdit()) {
Warning('No permission to move event.'); Warning('No permission to move event.');
return '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); 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; 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); $self->delete_files($OldStorage);
return $error; return $error;
} # end sub MoveTo } # end sub MoveTo

View File

@ -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

View File

@ -56,6 +56,7 @@ $primary_key = 'Id';
%fields = map { $_ => $_ } qw( %fields = map { $_ => $_ } qw(
Id Id
Name Name
ExecuteInterval
Query_json Query_json
AutoArchive AutoArchive
AutoUnarchive AutoUnarchive
@ -106,7 +107,6 @@ sub Execute {
$sql =~ s/zmSystemLoad/$load/g; $sql =~ s/zmSystemLoad/$load/g;
} }
$sql .= ' FOR UPDATE' if $$self{LockRows};
Debug("Filter::Execute SQL ($sql)"); Debug("Filter::Execute SQL ($sql)");
my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql) my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql)
@ -230,8 +230,8 @@ sub Sql {
# PostCondition, so no further SQL # PostCondition, so no further SQL
} else { } else {
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/; ( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) { # Empty value will result in () from split
foreach my $temp_value ( $stripped_value ? split( /["'\s]*?,["'\s]*?/, $stripped_value ) : $stripped_value ) {
if ( $term->{attr} eq 'AlarmedZoneId' ) { if ( $term->{attr} eq 'AlarmedZoneId' ) {
$value = '(SELECT * FROM Stats WHERE EventId=E.Id AND Score > 0 AND ZoneId='.$value.')'; $value = '(SELECT * FROM Stats WHERE EventId=E.Id AND Score > 0 AND ZoneId='.$value.')';
} elsif ( $term->{attr} =~ /^MonitorName/ ) { } elsif ( $term->{attr} =~ /^MonitorName/ ) {
@ -250,7 +250,8 @@ sub Sql {
$$self{Server} = new ZoneMinder::Server($temp_value); $$self{Server} = new ZoneMinder::Server($temp_value);
} }
} elsif ( $term->{attr} eq 'StorageId' ) { } elsif ( $term->{attr} eq 'StorageId' ) {
$value = "'$temp_value'"; # Empty means NULL, otherwise must be an integer
$value = $temp_value ne '' ? int($temp_value) : 'NULL';
$$self{Storage} = new ZoneMinder::Storage($temp_value); $$self{Storage} = new ZoneMinder::Storage($temp_value);
} elsif ( $term->{attr} eq 'Name' } elsif ( $term->{attr} eq 'Name'
|| $term->{attr} eq 'Cause' || $term->{attr} eq 'Cause'
@ -370,10 +371,7 @@ sub Sql {
if ( @auto_terms ) { if ( @auto_terms ) {
$sql .= ' AND ( '.join(' or ', @auto_terms).' )'; $sql .= ' AND ( '.join(' or ', @auto_terms).' )';
} }
if ( !$filter_expr->{sort_field} ) {
$filter_expr->{sort_field} = 'StartDateTime';
$filter_expr->{sort_asc} = 0;
}
my $sort_column = ''; my $sort_column = '';
if ( $filter_expr->{sort_field} eq 'Id' ) { if ( $filter_expr->{sort_field} eq 'Id' ) {
$sort_column = 'E.Id'; $sort_column = 'E.Id';
@ -405,14 +403,21 @@ sub Sql {
$sort_column = 'E.MaxScore'; $sort_column = 'E.MaxScore';
} elsif ( $filter_expr->{sort_field} eq 'DiskSpace' ) { } elsif ( $filter_expr->{sort_field} eq 'DiskSpace' ) {
$sort_column = 'E.DiskSpace'; $sort_column = 'E.DiskSpace';
} else { } elsif ( $filter_expr->{sort_field} ne '' ) {
$sort_column = 'E.StartDateTime'; $sort_column = 'E.'.$filter_expr->{sort_field};
} }
my $sort_order = $filter_expr->{sort_asc} ? 'ASC' : 'DESC'; if ( $sort_column ne '' ) {
$sql .= ' ORDER BY '.$sort_column.' '.$sort_order; $sql .= ' ORDER BY '.$sort_column.' '.($filter_expr->{sort_asc} ? 'ASC' : 'DESC');
if ( $filter_expr->{limit} ) { }
if ($filter_expr->{limit}) {
$sql .= ' LIMIT 0,'.$filter_expr->{limit}; $sql .= ' LIMIT 0,'.$filter_expr->{limit};
} }
if ($$self{LockRows}) {
$sql .= ' FOR UPDATE';
if ($filter_expr->{skip_locked}) {
$sql .= ' SKIP LOCKED';
}
}
$self->{Sql} = $sql; $self->{Sql} = $sql;
} # end if has Sql } # end if has Sql
return $self->{Sql}; return $self->{Sql};

View File

@ -31,6 +31,8 @@ our %EXPORT_TAGS = (
systemStatus systemStatus
packageControl packageControl
daemonControl daemonControl
parseNameEqualsValueToHash
hash_diff
) ] ) ]
); );
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
@ -534,6 +536,42 @@ sub jsonDecode {
return $result; 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 { sub packageControl {
my $command = shift; my $command = shift;
my $string = $Config{ZM_PATH_BIN}.'/zmpkg.pl '.$command; my $string = $Config{ZM_PATH_BIN}.'/zmpkg.pl '.$command;
@ -598,6 +636,8 @@ of the ZoneMinder scripts
packageControl packageControl
daemonControl daemonControl
systemStatus systemStatus
parseNameEqualsValueToHash
hash_diff
) ] ) ]

View File

@ -42,6 +42,7 @@ our @ISA = qw(Exporter ZoneMinder::Base);
# will save memory. # will save memory.
our %EXPORT_TAGS = ( our %EXPORT_TAGS = (
constants => [ qw( constants => [ qw(
STATE_UNKNOWN
STATE_IDLE STATE_IDLE
STATE_PREALARM STATE_PREALARM
STATE_ALARM STATE_ALARM
@ -98,11 +99,12 @@ our $VERSION = $ZoneMinder::Base::VERSION;
use ZoneMinder::Config qw(:all); use ZoneMinder::Config qw(:all);
use ZoneMinder::Logger qw(:all); use ZoneMinder::Logger qw(:all);
use constant STATE_IDLE => 0; use constant STATE_UNKNOWN => 0;
use constant STATE_PREALARM => 1; use constant STATE_IDLE => 1;
use constant STATE_ALARM => 2; use constant STATE_PREALARM => 2;
use constant STATE_ALERT => 3; use constant STATE_ALARM => 3;
use constant STATE_TAPE => 4; use constant STATE_ALERT => 4;
use constant STATE_TAPE => 5;
use constant ACTION_GET => 1; use constant ACTION_GET => 1;
use constant ACTION_SET => 2; use constant ACTION_SET => 2;

View File

@ -35,7 +35,9 @@ require ZoneMinder::Storage;
require ZoneMinder::Server; require ZoneMinder::Server;
require ZoneMinder::Memory; require ZoneMinder::Memory;
require ZoneMinder::Monitor_Status; require ZoneMinder::Monitor_Status;
require ZoneMinder::Event_Summary;
require ZoneMinder::Zone; require ZoneMinder::Zone;
use ZoneMinder::Logger qw(:all);
#our @ISA = qw(Exporter ZoneMinder::Base); #our @ISA = qw(Exporter ZoneMinder::Base);
use parent qw(ZoneMinder::Object); use parent qw(ZoneMinder::Object);
@ -239,20 +241,26 @@ sub control {
my $command = shift; my $command = shift;
my $process = shift; my $process = shift;
if ( $command eq 'stop' or $command eq 'restart' ) { if ($command eq 'stop' or $command eq 'restart') {
if ( $process ) { if ($process) {
`/usr/bin/zmdc.pl stop $process -m $$monitor{Id}`; ZoneMinder::General::runCommand("zmdc.pl stop $process -m $$monitor{Id}");
} else { } else {
`/usr/bin/zmdc.pl stop zma -m $$monitor{Id}`; if ($monitor->{Type} eq 'Local') {
`/usr/bin/zmdc.pl stop zmc -m $$monitor{Id}`; ZoneMinder::General::runCommand('zmdc.pl stop zmc -d '.$monitor->{Device});
} else {
ZoneMinder::General::runCommand('zmdc.pl stop zmc -m '.$monitor->{Id});
}
} }
} }
if ( $command eq 'start' or $command eq 'restart' ) { if ( $command eq 'start' or $command eq 'restart' ) {
if ( $process ) { if ( $process ) {
`/usr/bin/zmdc.pl start $process -m $$monitor{Id}`; ZoneMinder::General::runCommand("zmdc.pl start $process -m $$monitor{Id}");
} else { } else {
`/usr/bin/zmdc.pl start zmc -m $$monitor{Id}`; if ($monitor->{Type} eq 'Local') {
`/usr/bin/zmdc.pl start zma -m $$monitor{Id}`; ZoneMinder::General::runCommand('zmdc.pl start zmc -d '.$monitor->{Device});
} else {
ZoneMinder::General::runCommand('zmdc.pl start zmc -m '.$monitor->{Id});
}
} # end if } # end if
} }
} # end sub control } # end sub control
@ -266,6 +274,15 @@ sub Status {
return $$self{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 { sub connect {
my $self = shift; my $self = shift;
return ZoneMinder::Memory::zmMemVerify($self); return ZoneMinder::Memory::zmMemVerify($self);
@ -313,6 +330,37 @@ sub resumeMotionDetection {
return 1; 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; 1;
__END__ __END__

View File

@ -43,18 +43,6 @@ $serial = $primary_key = 'MonitorId';
CaptureFPS CaptureFPS
AnalysisFPS AnalysisFPS
CaptureBandwidth CaptureBandwidth
TotalEvents
TotalEventDiskSpace
HourEvents
HourEventDiskSpace
DayEvents
DayEventDiskSpace
WeekEvents
WeekEventDiskSpace
MonthEvents
MonthEventDiskSpace
ArchivedEvents
ArchivedEventDiskSpace
); );
%defaults = ( %defaults = (
@ -62,18 +50,6 @@ $serial = $primary_key = 'MonitorId';
CaptureFPS => undef, CaptureFPS => undef,
AnalysisFPS => undef, AnalysisFPS => undef,
CaptureBandwidth => 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 { sub Monitor {

View File

@ -218,7 +218,7 @@ sub save {
my $serial = eval '$'.$type.'::serial'; my $serial = eval '$'.$type.'::serial';
my @identified_by = eval '@'.$type.'::identified_by'; 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 ) { if ( ! $serial ) {
my $insert = $force_insert; my $insert = $force_insert;
my %serial = eval '%'.$type.'::serial'; 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} ) ) ) { if ( ! ( ( $_ = $local_dbh->prepare("DELETE FROM `$table` WHERE $where") ) and $_->execute( @$self{@identified_by} ) ) ) {
$where =~ s/\?/\%s/g; $where =~ s/\?/\%s/g;
$log->error("Error deleting: DELETE FROM $table WHERE " . sprintf($where, map { defined $_ ? $_ : 'undef' } ( @$self{@identified_by}) ).'):' . $local_dbh->errstr); $log->error("Error deleting: DELETE FROM $table WHERE " . sprintf($where, map { defined $_ ? $_ : 'undef' } ( @$self{@identified_by}) ).'):' . $local_dbh->errstr);
$local_dbh->rollback(); $local_dbh->rollback() if $ac;
ZoneMinder::Database::end_transaction( $local_dbh, $ac ); ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
return $local_dbh->errstr; return $local_dbh->errstr;
} elsif ( $debug ) { } elsif ( $debug ) {
$log->debug("SQL succesful DELETE FROM $table WHERE $where"); $log->debug("SQL succesful DELETE FROM $table WHERE $where");
@ -267,8 +267,8 @@ $log->debug("No serial") if $debug;
my $error = $local_dbh->errstr; my $error = $local_dbh->errstr;
$command =~ s/\?/\%s/g; $command =~ s/\?/\%s/g;
$log->error('SQL statement execution failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $local_dbh->errstr); $log->error('SQL statement execution failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $local_dbh->errstr);
$local_dbh->rollback(); $local_dbh->rollback() if $ac;
ZoneMinder::Database::end_transaction( $local_dbh, $ac ); ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
return $error; return $error;
} # end if } # end if
if ( $debug or DEBUG_ALL ) { if ( $debug or DEBUG_ALL ) {
@ -282,8 +282,8 @@ $log->debug("No serial") if $debug;
my $error = $local_dbh->errstr; my $error = $local_dbh->errstr;
$command =~ s/\?/\%s/g; $command =~ s/\?/\%s/g;
$log->error('SQL failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys, @$fields{@identified_by}}) ).'):' . $local_dbh->errstr); $log->error('SQL failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys, @$fields{@identified_by}}) ).'):' . $local_dbh->errstr);
$local_dbh->rollback(); $local_dbh->rollback() if $ac;
ZoneMinder::Database::end_transaction( $local_dbh, $ac ); ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
return $error; return $error;
} # end if } # end if
if ( $debug or DEBUG_ALL ) { if ( $debug or DEBUG_ALL ) {
@ -321,8 +321,8 @@ $log->debug("No serial") if $debug;
$command =~ s/\?/\%s/g; $command =~ s/\?/\%s/g;
my $error = $local_dbh->errstr; my $error = $local_dbh->errstr;
$log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $error); $log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $error);
$local_dbh->rollback(); $local_dbh->rollback() if $ac;
ZoneMinder::Database::end_transaction( $local_dbh, $ac ); ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
return $error; return $error;
} # end if } # end if
if ( $debug or DEBUG_ALL ) { if ( $debug or DEBUG_ALL ) {
@ -340,8 +340,8 @@ $log->debug("No serial") if $debug;
my $error = $local_dbh->errstr; my $error = $local_dbh->errstr;
$command =~ s/\?/\%s/g; $command =~ s/\?/\%s/g;
$log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}, @sql{@$fields{@identified_by}} ) ).'):' . $error) if $log; $log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}, @sql{@$fields{@identified_by}} ) ).'):' . $error) if $log;
$local_dbh->rollback(); $local_dbh->rollback() if $ac;
ZoneMinder::Database::end_transaction( $local_dbh, $ac ); ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
return $error; return $error;
} # end if } # end if
if ( $debug or DEBUG_ALL ) { if ( $debug or DEBUG_ALL ) {
@ -350,7 +350,7 @@ $log->debug("No serial") if $debug;
} # end if } # end if
} # end if } # end if
} # end if } # end if
ZoneMinder::Database::end_transaction( $local_dbh, $ac ); ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
#$self->load(); #$self->load();
#if ( $$fields{id} ) { #if ( $$fields{id} ) {
#if ( ! $ZoneMinder::Object::cache{$type}{$$self{id}} ) { #if ( ! $ZoneMinder::Object::cache{$type}{$$self{id}} ) {

View File

@ -30,7 +30,6 @@ use autouse 'Pod::Usage'=>qw(pod2usage);
use POSIX qw/strftime EPIPE EINTR/; use POSIX qw/strftime EPIPE EINTR/;
use Socket; use Socket;
use Data::Dumper; use Data::Dumper;
use Module::Load::Conditional qw{can_load};
use constant MAX_CONNECT_DELAY => 15; use constant MAX_CONNECT_DELAY => 15;
use constant MAX_COMMAND_WAIT => 1800; use constant MAX_COMMAND_WAIT => 1800;
@ -102,40 +101,21 @@ if ($options{command}) {
} }
} else { } else {
# The server isn't there # 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; 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) { 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'); 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"); Info("Starting control server $id/$protocol");
close(CLIENT); 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; my $zm_terminate = 0;
sub TermHandler { sub TermHandler {
Info('Received TERM, exiting'); Info('Received TERM, exiting');
@ -150,7 +130,6 @@ if ($options{command}) {
$0 = $0.' --id '.$id; $0 = $0.' --id '.$id;
my $control = ('ZoneMinder::Control::'.$protocol)->new($id);
my $control_key = $control->getKey(); my $control_key = $control->getKey();
$control->loadMonitor(); $control->loadMonitor();

View File

@ -429,10 +429,20 @@ sub start {
# It's not running, or at least it's not been started by us # It's not running, or at least it's not been started by us
$process = { daemon=>$daemon, args=>\@args, command=>$command, keepalive=>!undef }; $process = { daemon=>$daemon, args=>\@args, command=>$command, keepalive=>!undef };
} elsif ( $process->{pid} && $pid_hash{$process->{pid}} ) { } elsif ( $process->{pid} && $pid_hash{$process->{pid}} ) {
dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' already running at " if ($process->{term_sent_at}) {
dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' was told to term at "
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{term_sent_at}))
.", pid = $process->{pid}\n"
);
$process->{keepalive} = !undef;
$process->{delay} = 0;
delete $terminating_processes{$command};
} else {
dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' already running at "
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{started})) .strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
.", pid = $process->{pid}\n" .", pid = $process->{pid}\n"
); );
}
return; return;
} }
@ -523,7 +533,7 @@ sub send_stop {
."\n" ."\n"
); );
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n"; sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
return(); return ();
} }
my $pid = $process->{pid}; my $pid = $process->{pid};
@ -586,7 +596,7 @@ sub check_for_processes_to_kill {
sub stop { sub stop {
my ( $daemon, @args ) = @_; my ( $daemon, @args ) = @_;
my $command = join(' ', $daemon, @args ); my $command = join(' ', $daemon, @args);
my $process = $cmd_hash{$command}; my $process = $cmd_hash{$command};
if ( !$process ) { if ( !$process ) {
dPrint(ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'"); dPrint(ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'");

View File

@ -21,28 +21,6 @@
# #
# ========================================================================== # ==========================================================================
=head1 NAME
zmfilter.pl - ZoneMinder tool to filter events
=head1 SYNOPSIS
zmfilter.pl [-f <filter name>,--filter=<filter name>] [--filter_id=<filter id>] | -v, --version
=head1 DESCRIPTION
This script continuously monitors the recorded events for the given
monitor and applies any filters which would delete and/or upload
matching events.
=head1 OPTIONS
-f{filter name}, --filter={filter name} - The name of a specific filter to run
--filter_id={filter id} - The id of a specific filter to run
-v, --version - Print ZoneMinder version
=cut
use strict; use strict;
use bytes; use bytes;
@ -160,10 +138,9 @@ $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL};
my $event_id = 0; my $event_id = 0;
if ( !EVENT_PATH ) { if (!EVENT_PATH) {
Error('No event path defined. Config was '.$Config{ZM_DIR_EVENTS}); Error('No event path defined. Config was '.$Config{ZM_DIR_EVENTS});
die; die;
} }
@ -195,26 +172,37 @@ if ( ! ( $filter_name or $filter_id ) ) {
my @filters; my @filters;
my $last_action = 0; my $last_action = 0;
while( !$zm_terminate ) { while (!$zm_terminate) {
my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL};
my $now = time; my $now = time;
if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) { if (($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY}) {
Debug('Reloading filters'); Debug('Reloading filters');
$last_action = $now; $last_action = $now;
@filters = getFilters({ Name=>$filter_name, Id=>$filter_id }); @filters = getFilters({ Name=>$filter_name, Id=>$filter_id });
} }
foreach my $filter ( @filters ) { foreach my $filter (@filters) {
last if $zm_terminate; last if $zm_terminate;
if ( $$filter{Concurrent} and ! ( $filter_id or $filter_name ) ) {
my $elapsed = ($now - $$filter{last_ran});
if ($$filter{last_ran} and ($elapsed < $$filter{ExecuteInterval})) {
my $filter_delay = $$filter{ExecuteInterval} - ($now - $$filter{last_ran});
$delay = $filter_delay if $filter_delay < $delay;
Debug("Setting delay to $delay because ExecuteInterval=$$filter{ExecuteInterval} and $elapsed have elapsed");
next;
}
if ($$filter{Concurrent} and !($filter_id or $filter_name)) {
my ( $proc ) = $0 =~ /(\S+)/; my ( $proc ) = $0 =~ /(\S+)/;
my ( $id ) = $$filter{Id} =~ /(\d+)/; my ( $id ) = $$filter{Id} =~ /(\d+)/;
Debug("Running concurrent filter process $proc --filter_id $$filter{Id} => $id for $$filter{Name}"); Debug("Running concurrent filter process $proc --filter_id $$filter{Id} => $id for $$filter{Name}");
system(qq`$proc --filter "$$filter{Name}" &`); system(qq`$proc --filter_id $id &`);
} else { } else {
checkFilter($filter); checkFilter($filter);
$$filter{last_ran} = $now;
} }
} } # end foreach filter
last if (!$daemon and ($filter_name or $filter_id)) or $zm_terminate; last if (!$daemon and ($filter_name or $filter_id)) or $zm_terminate;
@ -384,11 +372,6 @@ sub checkFilter {
} # end if AutoCopy } # end if AutoCopy
if ( $filter->{UpdateDiskSpace} ) { if ( $filter->{UpdateDiskSpace} ) {
if ( $$filter{LockRows} ) {
$ZoneMinder::Database::dbh->begin_work();
$Event->lock_and_load();
}
my $old_diskspace = $$Event{DiskSpace}; my $old_diskspace = $$Event{DiskSpace};
my $new_diskspace = $Event->DiskSpace(undef); my $new_diskspace = $Event->DiskSpace(undef);
@ -665,10 +648,10 @@ sub substituteTags {
# We have a filter and an event, do we need any more # We have a filter and an event, do we need any more
# monitor information? # monitor information?
my $need_monitor = $text =~ /%(?:MN|MET|MEH|MED|MEW|MEN|MEA)%/; 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 $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? # Do we need the image information too?
my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA|EIMOD|EIMODG)%/; my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA|EIMOD|EIMODG)%/;
@ -692,19 +675,19 @@ sub substituteTags {
} }
$rows ++; $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(); $sth->finish();
} }
my $url = $Config{ZM_URL}; my $url = $Config{ZM_URL};
$text =~ s/%ZP%/$url/g; $text =~ s/%ZP%/$url/g;
$text =~ s/%MN%/$Monitor->{Name}/g; $text =~ s/%MN%/$Monitor->{Name}/g;
$text =~ s/%MET%/$Status->{TotalEvents}/g; $text =~ s/%MET%/$Summary->{TotalEvents}/g;
$text =~ s/%MEH%/$Status->{HourEvents}/g; $text =~ s/%MEH%/$Summary->{HourEvents}/g;
$text =~ s/%MED%/$Status->{DayEvents}/g; $text =~ s/%MED%/$Summary->{DayEvents}/g;
$text =~ s/%MEW%/$Status->{WeekEvents}/g; $text =~ s/%MEW%/$Summary->{WeekEvents}/g;
$text =~ s/%MEM%/$Status->{MonthEvents}/g; $text =~ s/%MEM%/$Summary->{MonthEvents}/g;
$text =~ s/%MEA%/$Status->{ArchivedEvents}/g; $text =~ s/%MEA%/$Summary->{ArchivedEvents}/g;
$text =~ s/%MP%/$url?view=watch&mid=$Event->{MonitorId}/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/%MPS%/$url?view=watch&mid=$Event->{MonitorId}&mode=stream/g;
$text =~ s/%MPI%/$url?view=watch&mid=$Event->{MonitorId}&mode=still/g; $text =~ s/%MPI%/$url?view=watch&mid=$Event->{MonitorId}&mode=still/g;
@ -1051,9 +1034,7 @@ sub executeCommand {
my $filter = shift; my $filter = shift;
my $Event = shift; my $Event = shift;
my $event_path = $Event->Path(); my $command = $filter->{AutoExecuteCmd}.' '.$Event->Path();
my $command = $filter->{AutoExecuteCmd}.' '.$event_path;
$command = substituteTags($command, $filter, $Event); $command = substituteTags($command, $filter, $Event);
Info("Executing '$command'"); Info("Executing '$command'");
@ -1063,15 +1044,37 @@ sub executeCommand {
chomp($output); chomp($output);
Debug("Output: $output"); Debug("Output: $output");
} }
if ( $status ) { if ($status) {
Error("Command '$command' exited with status: $status"); Error("Command '$command' exited with status: $status");
return 0; return 0;
} else { } else {
my $sql = 'UPDATE `Events` SET `Executed` = 1 WHERE `Id` = ?'; ZoneMinder::Database::zmSQLExecute('UPDATE `Events` SET `Executed` = 1 WHERE `Id` = ?', $Event->{Id});
my $sth = $dbh->prepare_cached($sql)
or Fatal("Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute( $Event->{Id} )
or Fatal("Unable to execute '$sql': ".$dbh->errstr());
} }
return( 1 ); return 1;
} }
1;
__END__
=head1 NAME
zmfilter.pl - ZoneMinder tool to select events and perform actions on them
=head1 SYNOPSIS
zmfilter.pl [-f <filter name>,--filter=<filter name>] [--filter_id=<filter id>] [--daemon] | -v, --version
=head1 DESCRIPTION
This script performs a specified database query to select recorded events and performs specified actions on them
such as email reporting, deleting, moving, etc. If the --daemon option is given it will remain resident, repeating
the query and applying actions. This is normally managed by zmdc.pl however it can be used manually as well.
=head1 OPTIONS
-f{filter name}, --filter={filter name} - The name of a specific filter to run
--filter_id={filter id} - The id of a specific filter to run
--daemon - Causes zmfilter.pl to stay running endlessly repeating the filter(s).
-v, --version - Print ZoneMinder version
=cut

View File

@ -263,7 +263,10 @@ sub countQuery {
sub getMonitorRef { sub getMonitorRef {
my $dbh = shift; my $dbh = shift;
my $sql = 'SELECT `Id`,`Name`,`Type`,`Function`,`Width`,`Height`,`Colours`,`MaxFPS`,`AlarmMaxFPS` FROM `Monitors`'; my $sql = 'SELECT `Id`,`Name`,`Type`,`Function`,`Width`,`Height`,`Colours`,`MaxFPS`,`AlarmMaxFPS`,
(SELECT Name FROM Manufacturers WHERE Manufacturers.Id = ManufacturerId),
(SELECT Name FROM Models WHERE Models.Id = ModelId)
FROM `Monitors`';
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
my $arrayref = $sth->fetchall_arrayref({}); my $arrayref = $sth->fetchall_arrayref({});

View File

@ -166,13 +166,9 @@ while (!$zm_terminate) {
foreach my $connection ( values(%spawned_connections) ) { foreach my $connection ( values(%spawned_connections) ) {
if ( vec($rout, $connection->fileno(), 1) ) { if ( vec($rout, $connection->fileno(), 1) ) {
Debug('Got input from spawned connection ' Debug('Got input from spawned connection '
.$connection->name() .$connection->name().' ('.$connection->fileno().')');
.' ('
.$connection->fileno()
.')'
);
my $messages = $connection->getMessages(); my $messages = $connection->getMessages();
if ( defined($messages) ) { if (defined($messages)) {
foreach my $message ( @$messages ) { foreach my $message ( @$messages ) {
handleMessage($connection, $message); handleMessage($connection, $message);
} }
@ -199,34 +195,32 @@ while (!$zm_terminate) {
# Check polled connections # Check polled connections
foreach my $connection ( @in_poll_connections ) { foreach my $connection ( @in_poll_connections ) {
my $messages = $connection->getMessages(); my $messages = $connection->getMessages();
if ( defined($messages) ) { if (defined($messages)) {
foreach my $message ( @$messages ) { foreach my $message (@$messages) { handleMessage($connection, $message) };
handleMessage($connection, $message);
}
} }
} }
# Check for alarms that might have happened # Check for alarms that might have happened
my @out_messages; my @out_messages;
foreach my $monitor ( values %monitors ) { foreach my $monitor ( values %monitors ) {
if ($$monitor{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. # Our attempt to verify the memory handle failed. We should reload the monitors.
# Don't need to zmMemInvalidate because the monitor reload will do it. # Don't need to zmMemInvalidate because the monitor reload will do it.
push @needsReload, $monitor; push @needsReload, $monitor;
next; next;
} }
my ( $state, $last_event ) = zmMemRead( $monitor, my ($state, $last_event) = zmMemRead($monitor, [
[
'shared_data:state', 'shared_data:state',
'shared_data:last_event' 'shared_data:last_event'
] ]);
);
#print( "$monitor->{Id}: S:$state, LE:$last_event" ); if ($state == STATE_ALARM or $state == STATE_ALERT) {
#print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}" );
if ( $state == STATE_ALARM or $state == STATE_ALERT ) {
# In alarm state # In alarm state
if ( !defined($monitor->{LastEvent}) if ( !defined($monitor->{LastEvent})
or ($last_event != $monitor->{LastEvent}) or ($last_event != $monitor->{LastEvent})

View File

@ -56,6 +56,7 @@ use constant START_DELAY => 30; # To give everything else time to start
@EXTRA_PERL_LIB@ @EXTRA_PERL_LIB@
use ZoneMinder; use ZoneMinder;
use ZoneMinder::Storage; use ZoneMinder::Storage;
use ZoneMinder::Monitor;
use POSIX; use POSIX;
use DBI; use DBI;
use autouse 'Data::Dumper'=>qw(Dumper); use autouse 'Data::Dumper'=>qw(Dumper);
@ -80,9 +81,6 @@ Info('Watchdog starting, pausing for '.START_DELAY.' seconds');
sleep(START_DELAY); sleep(START_DELAY);
my $dbh = zmDbConnect(); my $dbh = zmDbConnect();
my $sql = $Config{ZM_SERVER_ID} ? 'SELECT * FROM Monitors WHERE ServerId=?' : 'SELECT * FROM Monitors';
my $sth = $dbh->prepare_cached($sql)
or Fatal("Can't prepare '$sql': ".$dbh->errstr());
while (!$zm_terminate) { while (!$zm_terminate) {
while (!($dbh and $dbh->ping())) { while (!($dbh and $dbh->ping())) {
@ -91,82 +89,67 @@ while (!$zm_terminate) {
} }
} }
my $res = $sth->execute($Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : ()) foreach my $monitor (ZoneMinder::Monitor->find($Config{ZM_SERVER_ID} ? (ServerId=>$Config{ZM_SERVER_ID}) : ())) {
or Fatal('Can\'t execute: '.$sth->errstr());
while (my $monitor = $sth->fetchrow_hashref()) {
next if $monitor->{Function} eq 'None'; next if $monitor->{Function} eq 'None';
next if $monitor->{Type} eq 'WebSite'; next if $monitor->{Type} eq 'WebSite';
next if $monitor->{Capturing} eq 'Ondemand';
my $now = time(); my $now = time();
my $restart = 0; my $restart = 0;
if (zmMemVerify($monitor)) { zmMemInvalidate($monitor);
next if $monitor->{Capturing} eq 'Ondemand'; if (!zmMemVerify($monitor)) {
# Check we have got an image recently
my $capture_time = zmGetLastWriteTime($monitor);
if (!defined($capture_time)) {
# Can't read from shared data
Debug('LastWriteTime is not defined.');
zmMemInvalidate($monitor);
next;
}
Debug("Monitor $$monitor{Id} LastWriteTime is $capture_time.");
if (!$capture_time) {
my $startup_time = zmGetStartupTime($monitor);
if (($now - $startup_time) > $Config{ZM_WATCH_MAX_DELAY}) {
Warning(
"Restarting capture daemon for $$monitor{Name}, no image since startup. ".
"Startup time was $startup_time - now $now > $Config{ZM_WATCH_MAX_DELAY}"
);
$restart = 1;
} else {
# We can't get the last capture time so can't be sure it's died, it might just be starting up.
zmMemInvalidate($monitor);
next;
}
}
if (!$restart) {
my $max_image_delay = (
$monitor->{MaxFPS}
&&($monitor->{MaxFPS}>0)
&&($monitor->{MaxFPS}<1)
) ? (3/$monitor->{MaxFPS})
: $Config{ZM_WATCH_MAX_DELAY}
;
my $image_delay = $now - $capture_time;
Debug("Monitor $monitor->{Id} last captured $image_delay seconds ago, max is $max_image_delay");
if ( $image_delay > $max_image_delay ) {
Warning("Restarting capture daemon for "
.$monitor->{Name}.", time since last capture $image_delay seconds ($now-$capture_time)"
);
$restart = 1;
}
} # end if ! restart
} else {
Info("Restarting capture daemon for $monitor->{Name}, shared data not valid"); Info("Restarting capture daemon for $monitor->{Name}, shared data not valid");
$restart = 1; $monitor->control('restart');
next;
} }
if ($restart) { # Check we have got an image recently
my $command; my $capture_time = zmGetLastWriteTime($monitor);
if ($monitor->{Type} eq 'Local') { if (!defined($capture_time)) {
$command = 'zmdc.pl restart zmc -d '.$monitor->{Device}; # Can't read from shared data
} else { Warning('LastWriteTime is not defined.');
$command = 'zmdc.pl restart zmc -m '.$monitor->{Id}; next;
}
Debug("Monitor $$monitor{Id} LastWriteTime is $capture_time.");
if (!$capture_time) {
# We can't get the last capture time so can't be sure it's died, it might just be starting up.
my $startup_time = zmGetStartupTime($monitor);
if (($now - $startup_time) > $Config{ZM_WATCH_MAX_DELAY}) {
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); next;
} elsif ($monitor->{Function} ne 'Monitor') { }
# Now check analysis daemon
$restart = 0; my $max_image_delay = (
$monitor->{MaxFPS}
&&($monitor->{MaxFPS}>0)
&&($monitor->{MaxFPS}<1)
) ? (3/$monitor->{MaxFPS})
: $Config{ZM_WATCH_MAX_DELAY}
;
my $image_delay = $now - $capture_time;
Debug("Monitor $monitor->{Id} last captured $image_delay seconds ago, max is $max_image_delay");
if ($image_delay > $max_image_delay) {
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 # Check we have got an image recently
my $image_time = zmGetLastReadTime($monitor); my $image_time = zmGetLastReadTime($monitor);
if (!defined($image_time)) { if (!defined($image_time)) {
# Can't read from shared data # Can't read from shared data
$restart = 1;
Error("Error reading shared data for $$monitor{Id} $$monitor{Name}"); Error("Error reading shared data for $$monitor{Id} $$monitor{Name}");
$monitor->control('restart');
next;
} elsif (!$image_time) { } elsif (!$image_time) {
# We can't get the last capture time so can't be sure it's died. Debug("Last analyse time for $$monitor{Id} $$monitor{Name} was zero.");
#$restart = 1;
Error("Last analyse time for $$monitor{Id} $$monitor{Name} was zero.");
} else { } else {
my $max_image_delay = ( $monitor->{MaxFPS} my $max_image_delay = ( $monitor->{MaxFPS}
&&($monitor->{MaxFPS}>0) &&($monitor->{MaxFPS}>0)
@ -178,25 +161,14 @@ while (!$zm_terminate) {
Debug("Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay"); Debug("Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay");
if ($image_delay > $max_image_delay) { if ($image_delay > $max_image_delay) {
Warning("Analysis daemon for $$monitor{Id} $$monitor{Name} needs restarting," Warning("Analysis daemon for $$monitor{Id} $$monitor{Name} needs restarting,"
." time since last analysis $image_delay seconds ($now-$image_time)" ." time since last analysis $image_delay seconds ($now-$image_time)");
); $monitor->control('restart');
$restart = 1; next;
} }
} }
if ($restart) {
Info("Restarting analysis daemon for $$monitor{Id} $$monitor{Name}");
my $command;
if ( $monitor->{Type} eq 'Local' ) {
$command = 'zmdc.pl restart zmc -d '.$monitor->{Device};
} else {
$command = 'zmdc.pl restart zmc -m '.$monitor->{Id};
}
runCommand($command);
} # end if restart
} # end if check analysis daemon } # end if check analysis daemon
# Prevent open handles building up if we have connect to shared memory
zmMemInvalidate($monitor); # Close our file handle to the zmc process we are about to end
} # end foreach monitor } # end foreach monitor
sleep($Config{ZM_WATCH_CHECK_INTERVAL}); sleep($Config{ZM_WATCH_CHECK_INTERVAL});

View File

@ -60,6 +60,7 @@ set(ZM_BIN_SRC_FILES
zm_signal.cpp zm_signal.cpp
zm_stream.cpp zm_stream.cpp
zm_swscale.cpp zm_swscale.cpp
zm_time.cpp
zm_user.cpp zm_user.cpp
zm_utils.cpp zm_utils.cpp
zm_videostore.cpp zm_videostore.cpp

View File

@ -65,7 +65,7 @@ unsigned int Buffer::expand(unsigned int count) {
int Buffer::read_into(int sd, unsigned int bytes) { int Buffer::read_into(int sd, unsigned int bytes) {
// Make sure there is enough space // Make sure there is enough space
this->expand(bytes); this->expand(bytes);
Debug(3, "Reading %u btes", bytes); Debug(3, "Reading %u bytes", bytes);
int bytes_read = ::read(sd, mTail, bytes); int bytes_read = ::read(sd, mTail, bytes);
if (bytes_read > 0) { if (bytes_read > 0) {
mTail += bytes_read; mTail += bytes_read;

View File

@ -251,6 +251,13 @@ void zmDbQueue::process() {
mCondition.wait(lock); mCondition.wait(lock);
} }
while (!mQueue.empty()) { 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(); std::string sql = mQueue.front();
mQueue.pop(); mQueue.pop();
// My idea for leaving the locking around each sql statement is to allow // 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) { void zmDbQueue::push(std::string &&sql) {
if (mTerminate) return; if (mTerminate) return;
std::unique_lock<std::mutex> lock(mMutex); {
mQueue.push(std::move(sql)); std::unique_lock<std::mutex> lock(mMutex);
mQueue.push(std::move(sql));
}
mCondition.notify_all(); mCondition.notify_all();
} }

View File

@ -60,8 +60,8 @@ Event::Event(
//snapshit_file(), //snapshit_file(),
//alarm_file(""), //alarm_file(""),
videoStore(nullptr), videoStore(nullptr),
//video_name(""),
//video_file(""), //video_file(""),
//video_path(""),
last_db_frame(0), last_db_frame(0),
have_video_keyframe(false), have_video_keyframe(false),
//scheme //scheme
@ -103,7 +103,14 @@ Event::Event(
// Copy it in case opening the mp4 doesn't work we can set it to another value // Copy it in case opening the mp4 doesn't work we can set it to another value
save_jpegs = monitor->GetOptSaveJPEGs(); 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( std::string sql = stringtf(
"INSERT INTO `Events` " "INSERT INTO `Events` "
@ -120,28 +127,27 @@ Event::Event(
state_id, state_id,
monitor->getOrientation(), monitor->getOrientation(),
0, 0,
"", video_incomplete_file.c_str(),
save_jpegs, save_jpegs,
storage->SchemeString().c_str() storage->SchemeString().c_str()
); );
id = zmDbDoInsert(sql); id = zmDbDoInsert(sql);
if ( !SetPath(storage) ) { if (!SetPath(storage)) {
// Try another // Try another
Warning("Failed creating event dir at %s", storage->Path()); Warning("Failed creating event dir at %s", storage->Path());
sql = stringtf("SELECT `Id` FROM `Storage` WHERE `Id` != %u", storage->Id()); sql = stringtf("SELECT `Id` FROM `Storage` WHERE `Id` != %u", storage->Id());
if ( monitor->ServerId() ) if (monitor->ServerId())
sql += stringtf(" AND ServerId=%u", monitor->ServerId()); sql += stringtf(" AND ServerId=%u", monitor->ServerId());
Debug(1, "%s", sql.c_str());
storage = nullptr; storage = nullptr;
MYSQL_RES *result = zmDbFetch(sql); MYSQL_RES *result = zmDbFetch(sql);
if ( result ) { if (result) {
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) { for (int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++) {
storage = new Storage(atoi(dbrow[0])); storage = new Storage(atoi(dbrow[0]));
if ( SetPath(storage) ) if (SetPath(storage))
break; break;
delete storage; delete storage;
storage = nullptr; storage = nullptr;
@ -149,18 +155,18 @@ Event::Event(
mysql_free_result(result); mysql_free_result(result);
result = nullptr; result = nullptr;
} }
if ( !storage ) { if (!storage) {
Info("No valid local storage area found. Trying all other areas."); Info("No valid local storage area found. Trying all other areas.");
// Try remote // Try remote
sql = "SELECT `Id` FROM `Storage` WHERE ServerId IS NULL"; sql = "SELECT `Id` FROM `Storage` WHERE ServerId IS NULL";
if ( monitor->ServerId() ) if (monitor->ServerId())
sql += stringtf(" OR ServerId != %u", monitor->ServerId()); sql += stringtf(" OR ServerId != %u", monitor->ServerId());
result = zmDbFetch(sql); result = zmDbFetch(sql);
if ( result ) { if (result) {
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) { for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
storage = new Storage(atoi(dbrow[0])); storage = new Storage(atoi(dbrow[0]));
if ( SetPath(storage) ) if (SetPath(storage))
break; break;
delete storage; delete storage;
storage = nullptr; storage = nullptr;
@ -169,7 +175,7 @@ Event::Event(
result = nullptr; result = nullptr;
} }
} }
if ( !storage ) { if (!storage) {
storage = new Storage(); storage = new Storage();
Warning("Failed to find a storage area to save events."); Warning("Failed to find a storage area to save events.");
} }
@ -178,24 +184,16 @@ Event::Event(
} // end if ! setPath(Storage) } // end if ! setPath(Storage)
Debug(1, "Using storage area at %s", path.c_str()); Debug(1, "Using storage area at %s", path.c_str());
video_name = "";
snapshot_file = path + "/snapshot.jpg"; snapshot_file = path + "/snapshot.jpg";
alarm_file = path + "/alarm.jpg"; alarm_file = path + "/alarm.jpg";
/* Save as video */ video_incomplete_path = path + "/" + video_incomplete_file;
if ( monitor->GetOptVideoWriter() != 0 ) { if (monitor->GetOptVideoWriter() != 0) {
std::string container = monitor->OutputContainer(); /* Save as video */
if ( container == "auto" || container == "" ) {
container = "mp4";
}
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( videoStore = new VideoStore(
video_file.c_str(), video_incomplete_path.c_str(),
container.c_str(), container.c_str(),
monitor->GetVideoStream(), monitor->GetVideoStream(),
monitor->GetVideoCodecContext(), monitor->GetVideoCodecContext(),
@ -213,20 +211,32 @@ Event::Event(
zmDbDo(sql); zmDbDo(sql);
} }
} else { } else {
sql = stringtf("UPDATE Events SET Videoed=1, DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name.c_str(), id); std::string codec = videoStore->get_codec();
zmDbDo(sql); 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 } // end if GetOptVideoWriter
if (storage != monitor->getStorage())
delete storage;
} }
Event::~Event() { Event::~Event() {
// We close the videowriter first, because if we finish the event, we might try to view the file, but we aren't done writing it yet. // 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 */ /* Close the video file */
if ( videoStore != nullptr ) { if (videoStore != nullptr) {
Debug(4, "Deleting video store"); Debug(4, "Deleting video store");
delete videoStore; delete videoStore;
videoStore = nullptr; 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. // 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( 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), monitor->EventPrefix(), id, std::chrono::system_clock::to_time_t(end_time),
delta_time.count(), delta_time.count(),
frames, alarm_frames, frames, alarm_frames,
tot_score, static_cast<uint32>(alarm_frames ? (tot_score / alarm_frames) : 0), max_score, tot_score, static_cast<uint32>(alarm_frames ? (tot_score / alarm_frames) : 0), max_score,
video_file.c_str(), // defaults to ""
id); id);
if (!zmDbDoUpdate(sql)) { if (!zmDbDoUpdate(sql)) {
// Name might have been changed during recording, so just do the update without changing the name. // Name might have been changed during recording, so just do the update without changing the name.
sql = stringtf( 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), std::chrono::system_clock::to_time_t(end_time),
delta_time.count(), delta_time.count(),
frames, alarm_frames, frames, alarm_frames,
tot_score, static_cast<uint32>(alarm_frames ? (tot_score / alarm_frames) : 0), max_score, tot_score, static_cast<uint32>(alarm_frames ? (tot_score / alarm_frames) : 0), max_score,
video_file.c_str(), // defaults to ""
id); id);
zmDbDoUpdate(sql); zmDbDoUpdate(sql);
} // end if no changed rows due to Name change during recording } // end if no changed rows due to Name change during recording
@ -312,32 +324,32 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) {
bool update = false; bool update = false;
//Info( "Checking notes, %d <> %d", noteSetMap.size(), newNoteSetMap.size() ); //Info( "Checking notes, %d <> %d", noteSetMap.size(), newNoteSetMap.size() );
if ( newNoteSetMap.size() > 0 ) { if (newNoteSetMap.size() > 0) {
if ( noteSetMap.size() == 0 ) { if (noteSetMap.size() == 0) {
noteSetMap = newNoteSetMap; noteSetMap = newNoteSetMap;
update = true; update = true;
} else { } else {
for ( StringSetMap::const_iterator newNoteSetMapIter = newNoteSetMap.begin(); for (StringSetMap::const_iterator newNoteSetMapIter = newNoteSetMap.begin();
newNoteSetMapIter != newNoteSetMap.end(); newNoteSetMapIter != newNoteSetMap.end();
++newNoteSetMapIter ) { ++newNoteSetMapIter) {
const std::string &newNoteGroup = newNoteSetMapIter->first; const std::string &newNoteGroup = newNoteSetMapIter->first;
const StringSet &newNoteSet = newNoteSetMapIter->second; const StringSet &newNoteSet = newNoteSetMapIter->second;
//Info( "Got %d new strings", newNoteSet.size() ); //Info( "Got %d new strings", newNoteSet.size() );
if ( newNoteSet.size() > 0 ) { if (newNoteSet.size() > 0) {
StringSetMap::iterator noteSetMapIter = noteSetMap.find(newNoteGroup); StringSetMap::iterator noteSetMapIter = noteSetMap.find(newNoteGroup);
if ( noteSetMapIter == noteSetMap.end() ) { if (noteSetMapIter == noteSetMap.end()) {
//Info( "Can't find note group %s, copying %d strings", newNoteGroup.c_str(), newNoteSet.size() ); //Debug(3, "Can't find note group %s, copying %d strings", newNoteGroup.c_str(), newNoteSet.size());
noteSetMap.insert(StringSetMap::value_type(newNoteGroup, newNoteSet)); noteSetMap.insert(StringSetMap::value_type(newNoteGroup, newNoteSet));
update = true; update = true;
} else { } else {
StringSet &noteSet = noteSetMapIter->second; StringSet &noteSet = noteSetMapIter->second;
//Info( "Found note group %s, got %d strings", newNoteGroup.c_str(), newNoteSet.size() ); //Debug(3, "Found note group %s, got %d strings", newNoteGroup.c_str(), newNoteSet.size());
for ( StringSet::const_iterator newNoteSetIter = newNoteSet.begin(); for (StringSet::const_iterator newNoteSetIter = newNoteSet.begin();
newNoteSetIter != newNoteSet.end(); newNoteSetIter != newNoteSet.end();
++newNoteSetIter ) { ++newNoteSetIter) {
const std::string &newNote = *newNoteSetIter; const std::string &newNote = *newNoteSetIter;
StringSet::iterator noteSetIter = noteSet.find(newNote); StringSet::iterator noteSetIter = noteSet.find(newNote);
if ( noteSetIter == noteSet.end() ) { if (noteSetIter == noteSet.end()) {
noteSet.insert(newNote); noteSet.insert(newNote);
update = true; update = true;
} }
@ -479,7 +491,7 @@ void Event::AddFrame(Image *image,
Debug(1, "Writing snapshot"); Debug(1, "Writing snapshot");
WriteFrameImage(image, timestamp, snapshot_file.c_str()); WriteFrameImage(image, timestamp, snapshot_file.c_str());
} else { } 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 // We are writing an Alarm frame
@ -491,7 +503,7 @@ void Event::AddFrame(Image *image,
Debug(1, "Writing alarm image"); Debug(1, "Writing alarm image");
WriteFrameImage(image, timestamp, alarm_file.c_str()); WriteFrameImage(image, timestamp, alarm_file.c_str());
} else { } 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)) { if (alarm_image and (save_jpegs & 2)) {
@ -534,7 +546,7 @@ void Event::AddFrame(Image *image,
or or
(frame_type == BULK) (frame_type == BULK)
or or
(fps and (frame_data.size() > fps))) { (fps and (frame_data.size() > 5*fps))) {
Debug(1, "Adding %zu frames to DB because write_to_db:%d or frames > analysis fps %f or BULK(%d)", Debug(1, "Adding %zu frames to DB because write_to_db:%d or frames > analysis fps %f or BULK(%d)",
frame_data.size(), write_to_db, fps, (frame_type == BULK)); frame_data.size(), write_to_db, fps, (frame_type == BULK));
WriteDbFrames(); WriteDbFrames();

View File

@ -84,8 +84,13 @@ class Event {
std::string alarm_file; std::string alarm_file;
VideoStore *videoStore; VideoStore *videoStore;
std::string video_name; std::string container;
std::string codec;
std::string video_file; std::string video_file;
std::string video_path;
std::string video_incomplete_file;
std::string video_incomplete_path;
int last_db_frame; 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. 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; Storage::Schemes scheme;

View File

@ -141,7 +141,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
event_data->storage_id = dbrow[1] ? atoi(dbrow[1]) : 0; event_data->storage_id = dbrow[1] ? atoi(dbrow[1]) : 0;
event_data->frame_count = dbrow[2] == nullptr ? 0 : atoi(dbrow[2]); event_data->frame_count = dbrow[2] == nullptr ? 0 : atoi(dbrow[2]);
event_data->start_time = SystemTimePoint(Seconds(atoi(dbrow[3]))); 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->duration = std::chrono::duration_cast<Microseconds>(event_data->end_time - event_data->start_time);
event_data->frames_duration = event_data->frames_duration =
std::chrono::duration_cast<Microseconds>(dbrow[5] ? FPSeconds(atof(dbrow[5])) : FPSeconds(0.0)); std::chrono::duration_cast<Microseconds>(dbrow[5] ? FPSeconds(atof(dbrow[5])) : FPSeconds(0.0));
@ -663,6 +663,7 @@ bool EventStream::checkEventLoaded() {
else else
curr_frame_id = 1; curr_frame_id = 1;
Debug(2, "New frame id = %ld", curr_frame_id); Debug(2, "New frame id = %ld", curr_frame_id);
start = std::chrono::steady_clock::now();
return true; return true;
} else { } else {
Debug(2, "No next event loaded using %s. Pausing", sql.c_str()); Debug(2, "No next event loaded using %s. Pausing", sql.c_str());
@ -810,7 +811,7 @@ bool EventStream::sendFrame(Microseconds delta_us) {
fputs("Content-Type: image/x-rgbz\r\n", stdout); fputs("Content-Type: image/x-rgbz\r\n", stdout);
break; break;
case STREAM_RAW : case STREAM_RAW :
img_buffer = (uint8_t*)(send_image->Buffer()); img_buffer = send_image->Buffer();
img_buffer_size = send_image->Size(); img_buffer_size = send_image->Size();
fputs("Content-Type: image/x-rgb\r\n", stdout); fputs("Content-Type: image/x-rgb\r\n", stdout);
break; break;
@ -836,12 +837,13 @@ void EventStream::runStream() {
//checkInitialised(); //checkInitialised();
if ( type == STREAM_JPEG ) if (type == STREAM_JPEG)
fputs("Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n\r\n", stdout); fputs("Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n\r\n", stdout);
if ( !event_data ) { if (!event_data) {
sendTextFrame("No event data found"); sendTextFrame("No event data found");
exit(0); zm_terminate = true;
return;
} }
double fps = 1.0; double fps = 1.0;
@ -850,13 +852,13 @@ void EventStream::runStream() {
} }
updateFrameRate(fps); updateFrameRate(fps);
start = std::chrono::system_clock::now(); start = std::chrono::steady_clock::now();
SystemTimePoint::duration last_frame_offset = Seconds(0); SystemTimePoint::duration last_frame_offset = Seconds(0);
SystemTimePoint::duration time_to_event = Seconds(0); SystemTimePoint::duration time_to_event = Seconds(0);
while ( !zm_terminate ) { while ( !zm_terminate ) {
now = std::chrono::system_clock::now(); now = std::chrono::steady_clock::now();
Microseconds delta = Microseconds(0); Microseconds delta = Microseconds(0);
send_frame = false; send_frame = false;
@ -903,7 +905,7 @@ void EventStream::runStream() {
// time_to_event > 0 means that we are not in the event // time_to_event > 0 means that we are not in the event
if (time_to_event > Seconds(0) and mode == MODE_ALL) { if (time_to_event > Seconds(0) and mode == MODE_ALL) {
SystemTimePoint::duration time_since_last_send = now - last_frame_sent; TimePoint::duration time_since_last_send = now - last_frame_sent;
Debug(1, "Time since last send = %.2f s", FPSeconds(time_since_last_send).count()); Debug(1, "Time since last send = %.2f s", FPSeconds(time_since_last_send).count());
if (time_since_last_send > Seconds(1)) { if (time_since_last_send > Seconds(1)) {
char frame_text[64]; char frame_text[64];
@ -957,23 +959,25 @@ void EventStream::runStream() {
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count())); static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()));
// if effective > base we should speed up frame delivery // if effective > base we should speed up frame delivery
delta = std::chrono::duration_cast<Microseconds>((delta * base_fps) / effective_fps); if (base_fps < effective_fps) {
Debug(3, "delta %" PRIi64 " us = base_fps (%f) / effective_fps (%f)", 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()), static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()),
base_fps, base_fps,
effective_fps); effective_fps);
// but must not exceed maxfps // but must not exceed maxfps
delta = std::max(delta, Microseconds(lround(Microseconds::period::den / maxfps))); delta = std::max(delta, Microseconds(lround(Microseconds::period::den / maxfps)));
Debug(3, "delta %" PRIi64 " us = base_fps (%f) /effective_fps (%f) from 30fps", Debug(3, "delta %" PRIi64 " us = base_fps (%f) / effective_fps (%f) from 30fps",
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()), static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()),
base_fps, base_fps,
effective_fps); effective_fps);
}
// +/- 1? What if we are skipping frames? // +/- 1? What if we are skipping frames?
curr_frame_id += (replay_rate>0) ? frame_mod : -1*frame_mod; curr_frame_id += (replay_rate>0) ? frame_mod : -1*frame_mod;
// sending the frame may have taken some time, so reload now // sending the frame may have taken some time, so reload now
now = std::chrono::system_clock::now(); now = std::chrono::steady_clock::now();
// we incremented by replay_rate, so might have jumped past frame_count // we incremented by replay_rate, so might have jumped past frame_count
if ( (mode == MODE_SINGLE) && ( if ( (mode == MODE_SINGLE) && (

View File

@ -76,7 +76,7 @@ class EventStream : public StreamBase {
long curr_frame_id; long curr_frame_id;
SystemTimePoint curr_stream_time; SystemTimePoint curr_stream_time;
bool send_frame; bool send_frame;
SystemTimePoint start; // clock time when started the event TimePoint start; // clock time when started the event
EventData *event_data; EventData *event_data;

View File

@ -458,6 +458,17 @@ int FfmpegCamera::OpenFfmpeg() {
#endif #endif
} // end if hwaccel_name } // end if hwaccel_name
// set codec to automatically determine how many threads suits best for the decoding job
mVideoCodecContext->thread_count = 0;
if (mVideoCodec->capabilities | AV_CODEC_CAP_FRAME_THREADS) {
mVideoCodecContext->thread_type = FF_THREAD_FRAME;
} else if (mVideoCodec->capabilities | AV_CODEC_CAP_SLICE_THREADS) {
mVideoCodecContext->thread_type = FF_THREAD_SLICE;
} else {
mVideoCodecContext->thread_count = 1; //don't use multithreading
}
ret = avcodec_open2(mVideoCodecContext, mVideoCodec, &opts); ret = avcodec_open2(mVideoCodecContext, mVideoCodec, &opts);
e = nullptr; e = nullptr;

View File

@ -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) { bool Fifo::write(uint8_t *data, size_t bytes, int64_t pts) {
if (!(outfile or open())) return false; if (!(outfile or open())) return false;
// Going to write a brief header // Going to write a brief header
Debug(1, "Writing header ZM %lu %" PRId64, bytes, pts); Debug(1, "Writing header ZM %zu %" PRId64, bytes, pts);
if ( fprintf(outfile, "ZM %lu %" PRId64 "\n", bytes, pts) < 0 ) { if (fprintf(outfile, "ZM %zu %" PRId64 "\n", bytes, pts) < 0) {
if (errno != EAGAIN) { if (errno != EAGAIN) {
Error("Problem during writing: %s", strerror(errno)); Error("Problem during writing: %s", strerror(errno));
} else { } else {

View File

@ -155,7 +155,7 @@ void FifoStream::runStream() {
} }
while (!zm_terminate) { while (!zm_terminate) {
now = std::chrono::system_clock::now(); now = std::chrono::steady_clock::now();
checkCommandQueue(); checkCommandQueue();
if (stream_type == MJPEG) { if (stream_type == MJPEG) {

View File

@ -270,7 +270,6 @@ int Image::PopulateFrame(AVFrame *frame) {
frame->width = width; frame->width = width;
frame->height = height; frame->height = height;
frame->format = imagePixFormat; 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)"); zm_dump_video_frame(frame, "Image.Populate(frame)");
return 1; return 1;
} // int Image::PopulateFrame(AVFrame *frame) } // int Image::PopulateFrame(AVFrame *frame)

View File

@ -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 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]; } inline const uint8_t* Buffer(unsigned int x, unsigned int y=0) const { return &buffer[(y*linesize) + x*colours]; }
/* Request writeable buffer */ /* 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); 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 // Is only acceptable on a pre-allocated buffer

View File

@ -23,7 +23,7 @@ void bind_libvnc_symbols() {
libvnc_lib = dlopen("libvncclient.so", RTLD_LAZY | RTLD_GLOBAL); libvnc_lib = dlopen("libvncclient.so", RTLD_LAZY | RTLD_GLOBAL);
if (!libvnc_lib) { if (!libvnc_lib) {
Error("Error loading libvncclient: %s", dlerror()); Error("Error loading libvncclient.so: %s", dlerror());
return; return;
} }
@ -135,11 +135,6 @@ VncCamera::VncCamera(
} }
VncCamera::~VncCamera() { VncCamera::~VncCamera() {
if (capture and mRfb) {
if (mRfb->frameBuffer)
free(mRfb->frameBuffer);
(*rfbClientCleanup_f)(mRfb);
}
if (libvnc_lib) { if (libvnc_lib) {
dlclose(libvnc_lib); dlclose(libvnc_lib);
libvnc_lib = nullptr; libvnc_lib = nullptr;
@ -253,6 +248,12 @@ int VncCamera::PostCapture() {
} }
int VncCamera::Close() { int VncCamera::Close() {
if (capture and mRfb) {
if (mRfb->frameBuffer)
free(mRfb->frameBuffer);
(*rfbClientCleanup_f)(mRfb);
mRfb = nullptr;
}
return 1; return 1;
} }
#endif #endif

View File

@ -43,11 +43,11 @@ Logger::IntMap Logger::smSyslogPriorities;
void Logger::usrHandler(int sig) { void Logger::usrHandler(int sig) {
Logger *logger = fetch(); Logger *logger = fetch();
if ( sig == SIGUSR1 ) if (sig == SIGUSR1)
logger->level(logger->level()+1); logger->level(logger->level()+1);
else if ( sig == SIGUSR2 ) else if (sig == SIGUSR2)
logger->level(logger->level()-1); 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() : Logger::Logger() :
@ -296,23 +296,23 @@ const std::string &Logger::id(const std::string &id) {
} }
Logger::Level Logger::level(Logger::Level level) { Logger::Level Logger::level(Logger::Level level) {
if ( level > NOOPT ) { if (level > NOOPT) {
mLevel = limit(level); mLevel = limit(level);
mEffectiveLevel = NOLOG; mEffectiveLevel = NOLOG;
if ( mTerminalLevel > mEffectiveLevel ) if (mTerminalLevel > mEffectiveLevel)
mEffectiveLevel = mTerminalLevel; mEffectiveLevel = mTerminalLevel;
if ( mDatabaseLevel > mEffectiveLevel ) if (mDatabaseLevel > mEffectiveLevel)
mEffectiveLevel = mDatabaseLevel; mEffectiveLevel = mDatabaseLevel;
if ( mFileLevel > mEffectiveLevel ) if (mFileLevel > mEffectiveLevel)
mEffectiveLevel = mFileLevel; mEffectiveLevel = mFileLevel;
if ( mSyslogLevel > mEffectiveLevel ) if (mSyslogLevel > mEffectiveLevel)
mEffectiveLevel = mSyslogLevel; mEffectiveLevel = mSyslogLevel;
if ( mEffectiveLevel > mLevel) if (mEffectiveLevel > mLevel)
mEffectiveLevel = mLevel; mEffectiveLevel = mLevel;
// DEBUG levels should flush // DEBUG levels should flush
if ( mLevel > INFO ) if (mLevel > INFO)
mFlush = true; mFlush = true;
} }
return mLevel; return mLevel;

View File

@ -70,7 +70,7 @@
// It will be used whereever a Monitor dbrow is needed. WHERE conditions can be appended // It will be used whereever a Monitor dbrow is needed. WHERE conditions can be appended
std::string load_monitor_sql = std::string load_monitor_sql =
"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Capturing`+0, `Analysing`+0, `AnalysisSource`, `Recording`+0, `RecordingSource`, `Enabled`, `DecodingEnabled`, " "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 "`Device`, `Channel`, `Format`, `V4LMultiBuffer`, `V4LCapturesPerFrame`, " // V4L Settings
"`Protocol`, `Method`, `Options`, `User`, `Pass`, `Host`, `Port`, `Path`, `SecondPath`, `Width`, `Height`, `Colours`, `Palette`, `Orientation`+0, `Deinterlacing`, " "`Protocol`, `Method`, `Options`, `User`, `Pass`, `Host`, `Port`, `Path`, `SecondPath`, `Width`, `Height`, `Colours`, `Palette`, `Orientation`+0, `Deinterlacing`, "
"`DecoderHWAccelName`, `DecoderHWAccelDevice`, `RTSPDescribe`, " "`DecoderHWAccelName`, `DecoderHWAccelDevice`, `RTSPDescribe`, "
@ -86,6 +86,7 @@ std::string load_monitor_sql =
"`SignalCheckPoints`, `SignalCheckColour`, `Importance`-1 FROM `Monitors`"; "`SignalCheckPoints`, `SignalCheckColour`, `Importance`-1 FROM `Monitors`";
std::string CameraType_Strings[] = { std::string CameraType_Strings[] = {
"Unknown",
"Local", "Local",
"Remote", "Remote",
"File", "File",
@ -93,10 +94,21 @@ std::string CameraType_Strings[] = {
"LibVLC", "LibVLC",
"NVSOCKET", "NVSOCKET",
"CURL", "CURL",
"VNC", "VNC"
};
std::string Function_Strings[] = {
"Unknown",
"None",
"Monitor",
"Modect",
"Record",
"Mocord",
"Nodect"
}; };
std::string State_Strings[] = { std::string State_Strings[] = {
"Unknown",
"IDLE", "IDLE",
"PREALARM", "PREALARM",
"ALARM", "ALARM",
@ -143,7 +155,7 @@ bool Monitor::MonitorLink::connect() {
mem_size = sizeof(SharedData) + sizeof(TriggerData); 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 #if ZM_MEM_MAPPED
map_fd = open(mem_file.c_str(), O_RDWR, (mode_t)0600); map_fd = open(mem_file.c_str(), O_RDWR, (mode_t)0600);
if (map_fd < 0) { if (map_fd < 0) {
@ -170,14 +182,14 @@ bool Monitor::MonitorLink::connect() {
disconnect(); disconnect();
return false; return false;
} else if (map_stat.st_size < mem_size) { } 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(); disconnect();
return false; return false;
} }
mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0); mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0);
if (mem_ptr == MAP_FAILED) { 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(); disconnect();
return false; return false;
} }
@ -424,7 +436,8 @@ Monitor::Monitor()
/* /*
std::string load_monitor_sql = std::string load_monitor_sql =
"SELECT `Id`, `Name`, `ServerId`, `StorageId`, `Type`, `Function`+0, `Capturing`+0, `Analysing`+0, `AnalysisSource`, `Recording`+0, `RecordingSource`, `Enabled`, `DecodingEnabled`, " "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 "Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, " // V4L Settings
"Protocol, Method, Options, User, Pass, Host, Port, Path, SecondPath, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, " "Protocol, Method, Options, User, Pass, Host, Port, Path, SecondPath, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, "
"SaveJPEGs, VideoWriter, EncoderParameters, "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++; enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++;
decoding_enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++; decoding_enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++;
decoding_enabled = !( // See below after save_jpegs for a recalculation of decoding_enabled
( function == RECORD or function == NODECT )
and
( savejpegs == 0 )
and
( videowriter == PASSTHROUGH )
and
!decoding_enabled
);
Debug(1, "Decoding enabled: %d", decoding_enabled);
ReloadLinkedMonitors(dbrow[col]); col++; ReloadLinkedMonitors(dbrow[col]); col++;
event_start_command = dbrow[col] ? dbrow[col] : ""; col++;
event_end_command = dbrow[col] ? dbrow[col] : ""; col++;
/* "AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS," */ /* "AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS," */
analysis_fps_limit = dbrow[col] ? strtod(dbrow[col], nullptr) : 0.0; col++; 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++; videowriter = (VideoWriter)atoi(dbrow[col]); col++;
encoderparams = dbrow[col] ? 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`, " */ /*"`OutputCodec`, `Encoder`, `OutputContainer`, " */
output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++; output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
encoder = dbrow[col] ? dbrow[col] : ""; col++; encoder = dbrow[col] ? dbrow[col] : ""; col++;
@ -939,7 +956,7 @@ bool Monitor::connect() {
map_fd = -1; map_fd = -1;
return false; return false;
} else { } 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); close(map_fd);
map_fd = -1; map_fd = -1;
return false; 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); 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 (mem_ptr == MAP_FAILED) {
if (errno == EAGAIN) { 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 #endif
mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0); 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 #ifdef MAP_LOCKED
} else { } 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 #endif
if ((mem_ptr == MAP_FAILED) or (mem_ptr == nullptr)) { 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); close(map_fd);
map_fd = -1; map_fd = -1;
mem_ptr = nullptr; mem_ptr = nullptr;
@ -1648,7 +1665,7 @@ void Monitor::CheckAction() {
} }
} }
void Monitor::UpdateCaptureFPS() { void Monitor::UpdateFPS() {
if ( fps_report_interval and if ( fps_report_interval and
( (
!(image_count%fps_report_interval) !(image_count%fps_report_interval)
@ -1667,82 +1684,35 @@ void Monitor::UpdateCaptureFPS() {
uint32 new_camera_bytes = camera->Bytes(); uint32 new_camera_bytes = camera->Bytes();
uint32 new_capture_bandwidth = uint32 new_capture_bandwidth =
static_cast<uint32>((new_camera_bytes - last_camera_bytes) / elapsed.count()); 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", Debug(4, "FPS: capture count %d - last capture count %d = %d now:%lf, last %lf, elapsed %lf = capture: %lf fps analysis: %lf fps",
"Capturing",
image_count, image_count,
last_capture_image_count, last_capture_image_count,
image_count - last_capture_image_count, image_count - last_capture_image_count,
FPSeconds(now.time_since_epoch()).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(), elapsed.count(),
new_capture_fps); new_capture_fps,
new_analysis_fps);
Info("%s: %d - Capturing at %.2lf fps, capturing bandwidth %ubytes/sec", 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); name.c_str(), image_count, new_capture_fps, new_capture_bandwidth, new_analysis_fps);
shared_data->capture_fps = new_capture_fps; shared_data->capture_fps = new_capture_fps;
last_fps_time = now; last_fps_time = now;
last_capture_image_count = image_count; 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( std::string sql = stringtf(
"UPDATE LOW_PRIORITY Monitor_Status SET CaptureFPS = %.2lf, CaptureBandwidth=%u WHERE MonitorId=%u", "UPDATE LOW_PRIORITY Monitor_Status SET CaptureFPS = %.2lf, CaptureBandwidth=%u, AnalysisFPS = %.2lf WHERE MonitorId=%u",
new_capture_fps, new_capture_bandwidth, id); new_capture_fps, new_capture_bandwidth, new_analysis_fps, id);
dbQueue.push(std::move(sql)); dbQueue.push(std::move(sql));
} // now != last_fps_time } // now != last_fps_time
} // end if report fps } // end if report fps
} // void Monitor::UpdateCaptureFPS() } // void Monitor::UpdateFPS()
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
// Would be nice if this JUST did analysis // Would be nice if this JUST did analysis
// This idea is that we should be analysing as close to the capture frame as possible. // 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"); Debug(3, "signal and active and modect");
Event::StringSet zoneSet; Event::StringSet zoneSet;
int motion_score = last_motion_score;
if (analysis_fps_limit) { if (analysis_fps_limit) {
double capture_fps = get_capture_fps(); double capture_fps = get_capture_fps();
motion_frame_skip = capture_fps / analysis_fps_limit; motion_frame_skip = capture_fps / analysis_fps_limit;
@ -1914,38 +1882,45 @@ bool Monitor::Analyse() {
if (snap->image) { if (snap->image) {
// decoder may not have been able to provide an image // decoder may not have been able to provide an image
if (!ref_image.Buffer()) { if (!ref_image.Buffer()) {
Debug(1, "Assigning instead of Dectecting"); Debug(1, "Assigning instead of Detecting");
ref_image.Assign(*(snap->image)); ref_image.Assign(*(snap->image));
} else { } else {
Debug(1, "Detecting motion on image %d, image %p", snap->image_index, snap->image); Debug(1, "Detecting motion on image %d, image %p", snap->image_index, snap->image);
// Get new score. // 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()); snap->zone_stats.reserve(zones.size());
for (const Zone &zone : zones) { for (const Zone &zone : zones) {
const ZoneStats &stats = zone.GetStats(); const ZoneStats &stats = zone.GetStats();
stats.DumpToLog("After detect motion"); stats.DumpToLog("After detect motion");
snap->zone_stats.push_back(stats); 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)", Debug(3, "After motion detection, score:%d last_motion_score(%d), new motion score(%d)",
score, last_motion_score, motion_score); score, last_motion_score, motion_score);
motion_frame_count += 1; motion_frame_count += 1;
// Why are we updating the last_motion_score too?
last_motion_score = motion_score; last_motion_score = motion_score;
if (motion_score) {
if (cause.length()) cause += ", ";
cause += MOTION_CAUSE;
noteSetMap[MOTION_CAUSE] = zoneSet;
} // end if motion_score
} }
} else { } else {
Debug(1, "no image so skipping motion detection"); Debug(1, "no image so skipping motion detection");
} // end if has image } // end if has image
} else { } 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 += last_motion_score;
score += motion_score;
if (cause.length()) cause += ", ";
cause += MOTION_CAUSE;
noteSetMap[MOTION_CAUSE] = zoneSet;
} // end if motion_score
} else { } else {
Debug(1, "Not Active(%d) enabled %d active %d doing motion detection: %d", Debug(1, "Not Active(%d) enabled %d active %d doing motion detection: %d",
Active(), enabled, shared_data->active, Active(), enabled, shared_data->active,
@ -1953,12 +1928,13 @@ bool Monitor::Analyse() {
); );
} // end if active and doing motion detection } // end if active and doing motion detection
if (function == RECORD or function == MOCORD) { if (function == RECORD or function == MOCORD) {
// If doing record, check to see if we need to close the event or not. // If doing record, check to see if we need to close the event or not.
if (event) { if (event) {
Debug(2, "Have event %" PRIu64 " in record", event->Id()); 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 == MOCORD && event_close_mode != CLOSE_TIME)
|| (function == RECORD && 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))) { || std::chrono::duration_cast<Seconds>(timestamp.time_since_epoch()) % section_length == Seconds(0))) {
@ -1967,77 +1943,17 @@ bool Monitor::Analyse() {
image_count, image_count,
event->Id(), event->Id(),
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp.time_since_epoch()).count()), 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>(event->StartTime().time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - GetVideoWriterStartTime()).count()), static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - event->StartTime()).count()),
static_cast<int64>(Seconds(section_length).count())); static_cast<int64>(Seconds(section_length).count()));
closeEvent(); closeEvent();
} // end if section_length } // end if section_length
} // end if event } // end if event
if (!event) { if (!event) {
Debug(2, "Creating continuous event"); event = openEvent(snap, cause.empty() ? "Continuous" : cause, noteSetMap);
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 */
);
// This gets a lock on the starting packet Info("%s: %03d - Opened new event %" PRIu64 ", continuous section start",
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",
name.c_str(), analysis_image_count, event->Id()); name.c_str(), analysis_image_count, event->Id());
/* To prevent cancelling out an existing alert\prealarm\alarm state */ /* To prevent cancelling out an existing alert\prealarm\alarm state */
if (state == IDLE) { if (state == IDLE) {
@ -2046,86 +1962,35 @@ bool Monitor::Analyse() {
} // end if ! event } // end if ! event
} // end if RECORDING } // end if RECORDING
if (score) { if (score and (function != MONITOR)) {
if ((state == IDLE) || (state == TAPE) || (state == PREALARM)) { if ((state == IDLE) || (state == TAPE) || (state == PREALARM)) {
// If we should end then previous continuous event and start a new non-continuous event // If we should end then previous continuous event and start a new non-continuous event
if (event && event->Frames() if (event && event->Frames()
&& !event->AlarmFrames() && !event->AlarmFrames()
&& event_close_mode == CLOSE_ALARM && (event_close_mode == CLOSE_ALARM)
&& timestamp - GetVideoWriterStartTime() >= min_section_length && ((timestamp - event->StartTime()) >= min_section_length)
&& (!pre_event_count || Event::PreAlarmCount() >= alarm_frame_count - 1)) { && ((!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count - 1))) {
Info("%s: %03d - Closing event %" PRIu64 ", continuous end, alarm begins", Info("%s: %03d - Closing event %" PRIu64 ", continuous end, alarm begins",
name.c_str(), image_count, event->Id()); name.c_str(), image_count, event->Id());
closeEvent(); closeEvent();
} else if (event) { } 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 // 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, Debug(3,
"pre_alarm_count in event %d, event frames %d, alarm frames %d event length %" PRIi64 " >=? %" PRIi64 " min", "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(), Event::PreAlarmCount(), pre_event_count,
event->Frames(), event->Frames(),
event->AlarmFrames(), event->AlarmFrames(),
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - GetVideoWriterStartTime()).count()), static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - event->StartTime()).count()),
static_cast<int64>(Seconds(min_section_length).count())); static_cast<int64>(Seconds(min_section_length).count()),
(event_close_mode == CLOSE_ALARM));
} }
if ((!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1)) { 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", 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) { if (!event) {
packetqueue_iterator *start_it = packetqueue.get_event_start_packet_it( event = openEvent(snap, cause, noteSetMap);
*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());
shared_data->state = state = ALARM; 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()); Info("%s: %03d - Opening new event %" PRIu64 ", alarm start", name.c_str(), analysis_image_count, event->Id());
} else { } else {
shared_data->state = state = ALARM; 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); Info("%s: %03d - Gone into alert state", name.c_str(), analysis_image_count);
shared_data->state = state = ALERT; shared_data->state = state = ALERT;
} else if (state == ALERT) { } else if (state == ALERT) {
if (analysis_image_count - last_alarm_count > post_event_count if (
&& timestamp - GetVideoWriterStartTime() >= min_section_length) { ((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", Info("%s: %03d - Left alarm state (%" PRIu64 ") - %d(%d) images",
name.c_str(), analysis_image_count, event->Id(), event->Frames(), event->AlarmFrames()); name.c_str(), analysis_image_count, event->Id(), event->Frames(), event->AlarmFrames());
//if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE ) //if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE )
@ -2182,7 +2049,8 @@ bool Monitor::Analyse() {
shared_data->state = state = ((function != MOCORD) ? IDLE : TAPE); shared_data->state = state = ((function != MOCORD) ? IDLE : TAPE);
} else { } else {
Debug(1, 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(), State_Strings[state].c_str(),
analysis_image_count, analysis_image_count,
last_alarm_count, last_alarm_count,
@ -2201,18 +2069,15 @@ bool Monitor::Analyse() {
// Generate analysis images if necessary // Generate analysis images if necessary
if ((savejpegs > 1) and snap->image) { if ((savejpegs > 1) and snap->image) {
for (const Zone &zone : zones) { for (const Zone &zone : zones) {
if (zone.Alarmed()) { if (zone.Alarmed() and zone.AlarmImage()) {
if (zone.AlarmImage()) {
if (!snap->analysis_image) if (!snap->analysis_image)
snap->analysis_image = new Image(*(snap->image)); snap->analysis_image = new Image(*(snap->image));
snap->analysis_image->Overlay(*(zone.AlarmImage())); snap->analysis_image->Overlay(*(zone.AlarmImage()));
}
} // end if zone is alarmed } // end if zone is alarmed
} // end foreach zone } // end foreach zone
} // end if savejpegs } // end if savejpegs
// incremement pre alarm image count // incremement pre alarm image count
//have_pre_alarmed_frames ++;
Event::AddPreAlarmFrame(snap->image, timestamp, score, nullptr); Event::AddPreAlarmFrame(snap->image, timestamp, score, nullptr);
} else if (state == ALARM) { } else if (state == ALARM) {
for (const Zone &zone : zones) { for (const Zone &zone : zones) {
@ -2227,7 +2092,7 @@ bool Monitor::Analyse() {
if (event) { if (event) {
if (noteSetMap.size() > 0) if (noteSetMap.size() > 0)
event->updateNotes(noteSetMap); 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, Warning("%s: %03d - event %" PRIu64 ", has exceeded desired section length. %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %" PRIi64,
name.c_str(), analysis_image_count, event->Id(), name.c_str(), analysis_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>(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>(std::chrono::duration_cast<Seconds>(timestamp - GetVideoWriterStartTime()).count()),
static_cast<int64>(Seconds(section_length).count())); static_cast<int64>(Seconds(section_length).count()));
closeEvent(); closeEvent();
event = new Event(this, timestamp, cause, noteSetMap); event = openEvent(snap, 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());
} }
} else { } else {
Error("ALARM but no event"); Error("ALARM but no event");
@ -2292,8 +2153,6 @@ bool Monitor::Analyse() {
// Only do these if it's a video packet. // Only do these if it's a video packet.
shared_data->last_read_index = snap->image_index; shared_data->last_read_index = snap->image_index;
analysis_image_count++; analysis_image_count++;
if (function == MODECT or function == MOCORD)
UpdateAnalysisFPS();
} }
packetqueue.increment_it(analysis_it); packetqueue.increment_it(analysis_it);
packetqueue.unlock(packet_lock); packetqueue.unlock(packet_lock);
@ -2356,7 +2215,7 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
while ( 1 ) { while ( 1 ) {
dest_ptr = link_id_str; dest_ptr = link_id_str;
while ( *src_ptr >= '0' && *src_ptr <= '9' ) { 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++; *dest_ptr++ = *src_ptr++;
} else { } else {
break; break;
@ -2574,7 +2433,6 @@ int Monitor::Capture() {
// Will only be queued if there are iterators allocated in the queue. // Will only be queued if there are iterators allocated in the queue.
packetqueue.queuePacket(packet); packetqueue.queuePacket(packet);
UpdateCaptureFPS();
} else { // result == 0 } else { // result == 0
// Question is, do we update last_write_index etc? // Question is, do we update last_write_index etc?
return 0; return 0;
@ -2614,7 +2472,7 @@ bool Monitor::Decode() {
// //
//capture_image = packet->image = new Image(width, height, camera->Colours(), camera->SubpixelOrder()); //capture_image = packet->image = new Image(width, height, camera->Colours(), camera->SubpixelOrder());
int ret = packet->decode(camera->getVideoCodecContext()); int ret = packet->decode(camera->getVideoCodecContext());
if (ret > 0) { if (ret > 0 and !zm_terminate) {
if (packet->in_frame and !packet->image) { if (packet->in_frame and !packet->image) {
packet->image = new Image(camera_width, camera_height, camera->Colours(), camera->SubpixelOrder()); packet->image = new Image(camera_width, camera_height, camera->Colours(), camera->SubpixelOrder());
AVFrame *input_frame = packet->in_frame; 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; const char *s_ptr = label_time_text;
char *d_ptr = label_text; char *d_ptr = label_text;
while (*s_ptr && ((d_ptr - label_text) < (unsigned int) sizeof(label_text))) { while (*s_ptr && ((unsigned int)(d_ptr - label_text) < (unsigned int) sizeof(label_text))) {
if ( *s_ptr == config.timestamp_code_char[0] ) { if ( *s_ptr == config.timestamp_code_char[0] ) {
bool found_macro = false; bool found_macro = false;
switch ( *(s_ptr+1) ) { 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; typedef std::chrono::duration<int64, std::centi> Centiseconds;
Centiseconds centi_sec = std::chrono::duration_cast<Centiseconds>( Centiseconds centi_sec = std::chrono::duration_cast<Centiseconds>(
ts_time.time_since_epoch() - std::chrono::duration_cast<Seconds>(ts_time.time_since_epoch())); 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; found_macro = true;
break; break;
} }
@ -2817,6 +2675,67 @@ void Monitor::TimestampImage(Image *ts_image, SystemTimePoint ts_time) const {
Debug(2, "done annotating %s", label_text); Debug(2, "done annotating %s", label_text);
} // end void Monitor::TimestampImage } // 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() { void Monitor::closeEvent() {
if (!event) return; if (!event) return;
@ -2827,7 +2746,18 @@ void Monitor::closeEvent() {
Debug(1, "close event thread is not joinable"); Debug(1, "close event thread is not joinable");
} }
Debug(1, "Starting thread to close event"); 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"); Debug(1, "Nulling event");
event = nullptr; event = nullptr;
if (shared_data) video_store_data->recording = {}; if (shared_data) video_store_data->recording = {};
@ -3120,9 +3050,6 @@ int Monitor::PrimeCapture() {
int Monitor::PreCapture() const { return camera->PreCapture(); } int Monitor::PreCapture() const { return camera->PreCapture(); }
int Monitor::PostCapture() const { return camera->PostCapture(); } int Monitor::PostCapture() const { return camera->PostCapture(); }
int Monitor::Close() { 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 // Because the stream indexes may change we have to clear out the packetqueue
if (decoder) { if (decoder) {
decoder->Stop(); decoder->Stop();
@ -3140,10 +3067,14 @@ int Monitor::Close() {
video_fifo = nullptr; video_fifo = nullptr;
} }
if (close_event_thread.joinable()) {
close_event_thread.join();
}
std::lock_guard<std::mutex> lck(event_mutex); std::lock_guard<std::mutex> lck(event_mutex);
if (event) { if (event) {
Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name.c_str(), image_count, event->Id()); Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name.c_str(), image_count, event->Id());
closeEvent(); closeEvent();
close_event_thread.join();
} }
if (camera) camera->Close(); if (camera) camera->Close();
return 1; return 1;

View File

@ -128,7 +128,7 @@ public:
} Deinterlace; } Deinterlace;
typedef enum { typedef enum {
UNKNOWN=-1, UNKNOWN,
IDLE, IDLE,
PREALARM, PREALARM,
ALARM, ALARM,
@ -443,6 +443,8 @@ protected:
int n_linked_monitors; int n_linked_monitors;
MonitorLink **linked_monitors; MonitorLink **linked_monitors;
std::string event_start_command;
std::string event_end_command;
std::vector<Group *> groups; std::vector<Group *> groups;
@ -612,8 +614,7 @@ public:
unsigned int GetLastWriteIndex() const; unsigned int GetLastWriteIndex() const;
uint64_t GetLastEventId() const; uint64_t GetLastEventId() const;
double GetFPS() const; double GetFPS() const;
void UpdateAnalysisFPS(); void UpdateFPS();
void UpdateCaptureFPS();
void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" ); void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" );
void ForceAlarmOff(); void ForceAlarmOff();
void CancelForced(); void CancelForced();
@ -658,6 +659,10 @@ public:
bool Decode(); bool Decode();
void DumpImage( Image *dump_image ) const; void DumpImage( Image *dump_image ) const;
void TimestampImage(Image *ts_image, SystemTimePoint ts_time) const; void TimestampImage(Image *ts_image, SystemTimePoint ts_time) const;
Event *openEvent(
const std::shared_ptr<ZMPacket> &snap,
const std::string &cause,
const Event::StringSetMap noteSetMap);
void closeEvent(); void closeEvent();
void Reload(); void Reload();

View File

@ -134,6 +134,18 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
break; break;
} }
break; break;
case CMD_MAXFPS :
{
double int_part = ((unsigned char) msg->msg_data[1] << 24) | ((unsigned char) msg->msg_data[2] << 16)
| ((unsigned char) msg->msg_data[3] << 8) | (unsigned char) msg->msg_data[4];
double dec_part = ((unsigned char) msg->msg_data[5] << 24) | ((unsigned char) msg->msg_data[6] << 16)
| ((unsigned char) msg->msg_data[7] << 8) | (unsigned char) msg->msg_data[8];
maxfps = (int_part + dec_part / 1000000.0);
Debug(1, "Got MAXFPS %f", maxfps);
break;
}
case CMD_SLOWFWD : case CMD_SLOWFWD :
Debug(1, "Got SLOW FWD command"); Debug(1, "Got SLOW FWD command");
paused = true; paused = true;
@ -229,6 +241,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
break; break;
case CMD_QUIT : case CMD_QUIT :
Info("User initiated exit - CMD_QUIT"); Info("User initiated exit - CMD_QUIT");
zm_terminate = true;
break; break;
case CMD_QUERY : case CMD_QUERY :
Debug(1, "Got QUERY command, sending STATUS"); Debug(1, "Got QUERY command, sending STATUS");
@ -267,7 +280,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
} else { } else {
FPSeconds elapsed = now - last_fps_update; FPSeconds elapsed = now - last_fps_update;
if (elapsed.count()) { if (elapsed.count()) {
actual_fps = (frame_count - last_frame_count) / elapsed.count(); actual_fps = (actual_fps + (frame_count - last_frame_count) / elapsed.count())/2;
last_frame_count = frame_count; last_frame_count = frame_count;
last_fps_update = now; last_fps_update = now;
} }
@ -287,9 +300,9 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
status_data.delayed = delayed; status_data.delayed = delayed;
status_data.paused = paused; status_data.paused = paused;
status_data.rate = replay_rate; status_data.rate = replay_rate;
status_data.delay = FPSeconds(now - last_frame_timestamp).count(); status_data.delay = FPSeconds(now - last_frame_sent).count();
status_data.zoom = zoom; status_data.zoom = zoom;
Debug(2, "fps: %.2f capture_fps: %.2f analysis_fps: %.2f Buffer Level:%d, Delayed:%d, Paused:%d, Rate:%d, delay:%.3f, Zoom:%d, Enabled:%d Forced:%d", Debug(2, "viewing fps: %.2f capture_fps: %.2f analysis_fps: %.2f Buffer Level:%d, Delayed:%d, Paused:%d, Rate:%d, delay:%.3f, Zoom:%d, Enabled:%d Forced:%d",
status_data.fps, status_data.fps,
status_data.capture_fps, status_data.capture_fps,
status_data.analysis_fps, status_data.analysis_fps,
@ -311,13 +324,6 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
Error("Can't sendto on sd %d: %s", sd, strerror(errno)); 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); 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) } // end void MonitorStream::processCommand(const CmdMsg *msg)
bool MonitorStream::sendFrame(const std::string &filepath, SystemTimePoint timestamp) { 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; TimePoint::duration frame_send_time = send_end_time - send_start_time;
if (frame_send_time > Milliseconds(lround(Milliseconds::period::den / maxfps))) { if (frame_send_time > Milliseconds(lround(Milliseconds::period::den / maxfps))) {
maxfps /= 2;
Info("Frame send time %" PRIi64 " ms too slow, throttling maxfps to %.2f", Info("Frame send time %" PRIi64 " ms too slow, throttling maxfps to %.2f",
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(frame_send_time).count()), static_cast<int64>(std::chrono::duration_cast<Milliseconds>(frame_send_time).count()),
maxfps); maxfps);
@ -379,12 +384,15 @@ bool MonitorStream::sendFrame(const std::string &filepath, SystemTimePoint times
} }
bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) { bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
Image *send_image = prepareImage(image);
if (!config.timestamp_on_capture) { if (!config.timestamp_on_capture) {
monitor->TimestampImage(send_image, timestamp); monitor->TimestampImage(image, timestamp);
} }
Image *send_image = prepareImage(image);
fputs("--" BOUNDARY "\r\n", stdout); fputs("--" BOUNDARY "\r\n", stdout);
// Calculate how long it takes to actually send the frame
TimePoint send_start_time = std::chrono::steady_clock::now();
if (type == STREAM_MPEG) { if (type == STREAM_MPEG) {
if (!vid_stream) { if (!vid_stream) {
vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height()); vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height());
@ -404,8 +412,6 @@ bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
int img_buffer_size = 0; int img_buffer_size = 0;
unsigned char *img_buffer = temp_img_buffer; unsigned char *img_buffer = temp_img_buffer;
// Calculate how long it takes to actually send the frame
TimePoint send_start_time = std::chrono::steady_clock::now();
switch (type) { switch (type) {
case STREAM_JPEG : case STREAM_JPEG :
@ -414,7 +420,7 @@ bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
break; break;
case STREAM_RAW : case STREAM_RAW :
fputs("Content-Type: image/x-rgb\r\n", stdout); 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(); img_buffer_size = send_image->Size();
break; break;
case STREAM_ZIP : case STREAM_ZIP :
@ -447,19 +453,23 @@ bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
fputs("\r\n", stdout); fputs("\r\n", stdout);
fflush(stdout); fflush(stdout);
TimePoint send_end_time = std::chrono::steady_clock::now();
TimePoint::duration frame_send_time = send_end_time - send_start_time;
if (frame_send_time > Milliseconds(lround(Milliseconds::period::den / maxfps))) {
maxfps /= 1.5;
Warning("Frame send time %" PRIi64 " msec too slow, throttling maxfps to %.2f",
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(frame_send_time).count()),
maxfps);
}
} // Not mpeg } // Not mpeg
last_frame_sent = now;
last_frame_sent = std::chrono::steady_clock::now();
if (maxfps) {
TimePoint::duration frame_send_time = last_frame_sent - send_start_time;
TimePoint::duration maxfps_milliseconds = Milliseconds(lround(Milliseconds::period::den / maxfps));
if (frame_send_time > maxfps_milliseconds) {
//maxfps /= 1.5;
Warning("Frame send time %" PRIi64 " msec too slow (> %" PRIi64 ", throttling maxfps to %.3f",
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(frame_send_time).count()),
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(maxfps_milliseconds).count()),
maxfps);
}
}
return true; return true;
} } // end bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp)
void MonitorStream::runStream() { void MonitorStream::runStream() {
if (type == STREAM_SINGLE) { if (type == STREAM_SINGLE) {
@ -501,7 +511,8 @@ void MonitorStream::runStream() {
// point to end which is theoretically not a valid value because all indexes are % image_buffer_count // point to end which is theoretically not a valid value because all indexes are % image_buffer_count
int32_t last_read_index = monitor->image_buffer_count; int32_t last_read_index = monitor->image_buffer_count;
SystemTimePoint stream_start_time = std::chrono::system_clock::now(); TimePoint stream_start_time = std::chrono::steady_clock::now();
when_to_send_next_frame = stream_start_time; // initialize it to now so that we spit out a frame immediately
frame_count = 0; frame_count = 0;
@ -570,7 +581,7 @@ void MonitorStream::runStream() {
break; break;
} }
now = std::chrono::system_clock::now(); now = std::chrono::steady_clock::now();
monitor->setLastViewed(now); monitor->setLastViewed(now);
bool was_paused = paused; 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); temp_read_index = MOD_ADD(temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count);
} else { } else {
FPSeconds expected_delta_time = ((FPSeconds(swap_image->timestamp - last_frame_timestamp)) * ZM_RATE_BASE) / replay_rate; FPSeconds expected_delta_time = ((FPSeconds(swap_image->timestamp - last_frame_timestamp)) * ZM_RATE_BASE) / replay_rate;
SystemTimePoint::duration actual_delta_time = now - last_frame_sent; TimePoint::duration actual_delta_time = now - last_frame_sent;
// If the next frame is due // If the next frame is due
if (actual_delta_time > expected_delta_time) { if (actual_delta_time > expected_delta_time) {
@ -684,7 +695,8 @@ void MonitorStream::runStream() {
if (last_read_index != monitor->shared_data->last_write_index) { if (last_read_index != monitor->shared_data->last_write_index) {
// have a new image to send // have a new image to send
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
if ((frame_mod == 1) || ((frame_count%frame_mod) == 0)) { //if ((frame_mod == 1) || ((frame_count%frame_mod) == 0)) {
if ( now >= when_to_send_next_frame ) {
if (!paused && !delayed) { if (!paused && !delayed) {
last_read_index = monitor->shared_data->last_write_index; last_read_index = monitor->shared_data->last_write_index;
Debug(2, "Sending frame index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)", Debug(2, "Sending frame index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)",
@ -743,9 +755,9 @@ void MonitorStream::runStream() {
} // end if actual_delta_time > 5 } // end if actual_delta_time > 5
} // end if change in zoom } // end if change in zoom
} // end if paused or not } // end if paused or not
} else { //} else {
frame_count++; //frame_count++;
} // end if should send frame } // end if should send frame now > when_to_send_next_frame
if (buffered_playback && !paused) { if (buffered_playback && !paused) {
if (monitor->shared_data->valid) { if (monitor->shared_data->valid) {
@ -776,17 +788,42 @@ void MonitorStream::runStream() {
} }
} // end if buffered playback } // end if buffered playback
} else { } else {
Debug(3, "Waiting for capture last_write_index=%u", monitor->shared_data->last_write_index); Debug(3, "Waiting for capture last_write_index=%u == last_read_index=%u",
monitor->shared_data->last_write_index,
last_read_index);
} // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) } // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index )
FPSeconds sleep_time = FPSeconds sleep_time;
FPSeconds(ZM_RATE_BASE / ((base_fps ? base_fps : 1) * (replay_rate ? abs(replay_rate * 2) : 2))); if (now >= when_to_send_next_frame) {
// sent a frame, so update
double capture_fps = monitor->GetFPS();
double fps = (maxfps && (capture_fps > maxfps)) ? maxfps : capture_fps;
double sleep_time_seconds = (1 / ((fps ? fps : 1))) // 1 second / fps
* (replay_rate ? abs(replay_rate)/ZM_RATE_BASE : 1); // replay_rate is 100 for 1x
Debug(3, "Using %f for maxfps. capture_fps: %f maxfps %f * replay_rate: %d = %f", fps, capture_fps, maxfps, replay_rate, sleep_time_seconds);
sleep_time = FPSeconds(sleep_time_seconds);
if (when_to_send_next_frame > now)
sleep_time -= when_to_send_next_frame - now;
when_to_send_next_frame = now + std::chrono::duration_cast<Microseconds>(sleep_time);
if (last_frame_sent > now) {
FPSeconds elapsed = last_frame_sent - now;
if (sleep_time > elapsed) {
sleep_time -= elapsed;
}
}
} else {
sleep_time = when_to_send_next_frame - now;
}
if (sleep_time > MonitorStream::MAX_SLEEP) { if (sleep_time > MonitorStream::MAX_SLEEP) {
Debug(3, "Sleeping for MAX_SLEEP_USEC instead of %" PRIi64 " us",
static_cast<int64>(std::chrono::duration_cast<Microseconds>(sleep_time).count()));
// Shouldn't sleep for long because we need to check command queue, etc. // Shouldn't sleep for long because we need to check command queue, etc.
sleep_time = MonitorStream::MAX_SLEEP; sleep_time = MonitorStream::MAX_SLEEP;
Debug(3, "Sleeping for MAX_SLEEP_USEC %" PRIi64 " us",
static_cast<int64>(std::chrono::duration_cast<Microseconds>(sleep_time).count()));
} else { } else {
Debug(3, "Sleeping for %" PRIi64 " us", Debug(3, "Sleeping for %" PRIi64 " us",
static_cast<int64>(std::chrono::duration_cast<Microseconds>(sleep_time).count())); static_cast<int64>(std::chrono::duration_cast<Microseconds>(sleep_time).count()));
@ -798,13 +835,6 @@ void MonitorStream::runStream() {
static_cast<int64>(std::chrono::duration_cast<Microseconds>(ttl).count())); static_cast<int64>(std::chrono::duration_cast<Microseconds>(ttl).count()));
break; break;
} }
if (last_frame_sent.time_since_epoch() == Seconds(0)) {
// If we didn't capture above, because frame_mod was bad? Then last_frame_sent will not have a value.
last_frame_sent = now;
Warning("no last_frame_sent. Shouldn't happen. frame_mod was (%d) frame_count (%d)",
frame_mod, frame_count);
}
} // end while ! zm_terminate } // end while ! zm_terminate
if (buffered_playback) { if (buffered_playback) {
@ -855,16 +885,16 @@ void MonitorStream::SingleImage(int scale) {
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
Debug(1, "write index: %d %d", monitor->shared_data->last_write_index, index); Debug(1, "write index: %d %d", monitor->shared_data->last_write_index, index);
Image *snap_image = monitor->image_buffer[index]; Image *snap_image = monitor->image_buffer[index];
if (!config.timestamp_on_capture) {
monitor->TimestampImage(snap_image,
SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index])));
}
if ( scale != ZM_SCALE_BASE ) { if ( scale != ZM_SCALE_BASE ) {
scaled_image.Assign(*snap_image); scaled_image.Assign(*snap_image);
scaled_image.Scale(scale); scaled_image.Scale(scale);
snap_image = &scaled_image; snap_image = &scaled_image;
} }
if (!config.timestamp_on_capture) {
monitor->TimestampImage(snap_image,
SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index])));
}
snap_image->EncodeJpeg(img_buffer, &img_buffer_size); snap_image->EncodeJpeg(img_buffer, &img_buffer_size);
fprintf(stdout, fprintf(stdout,

View File

@ -116,14 +116,15 @@ bool PacketQueue::queuePacket(std::shared_ptr<ZMPacket> add_packet) {
, max_video_packet_count); , max_video_packet_count);
for ( for (
auto it = ++pktQueue.begin(); auto it = ++pktQueue.begin();
it != pktQueue.end() and *it != add_packet; it != pktQueue.end() and *it != add_packet;
// iterator is incremented by erase
) { ) {
std::shared_ptr <ZMPacket>zm_packet = *it; std::shared_ptr <ZMPacket>zm_packet = *it;
ZMLockedPacket *lp = new ZMLockedPacket(zm_packet); ZMLockedPacket *lp = new ZMLockedPacket(zm_packet);
if (!lp->trylock()) { if (!lp->trylock()) {
Debug(1, "Found locked packet when trying to free up video packets. Skipping to next one"); Warning("Found locked packet when trying to free up video packets. This basically means that decoding is not keeping up.");
delete lp; delete lp;
++it; ++it;
continue; continue;
@ -209,7 +210,7 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
--it; --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 (!keep_keyframes) {
// If not doing passthrough, we don't care about starting with a keyframe so logic is simpler // 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; zm_packet = *it;
lp = new ZMLockedPacket(zm_packet); lp = new ZMLockedPacket(zm_packet);
if (!lp->trylock()) { if (!lp->trylock()) {
Debug(3, "Failed locking packet %d", zm_packet->image_index);
delete lp; delete lp;
break; break;
} }
@ -280,7 +282,7 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
next_front = it; next_front = it;
} }
++video_packets_to_delete; ++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); 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) { if (packet_counts[video_stream_id] - video_packets_to_delete <= pre_event_video_packet_count + tail_count) {
break; break;
@ -289,7 +291,7 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
++it; ++it;
} // end while } // end while
} // end if first packet not locked } // 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 ), ( *it == add_packet ),
( next_front == pktQueue.begin() ) ( next_front == pktQueue.begin() )
); );
@ -311,7 +313,6 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
pktQueue.size()); pktQueue.size());
pktQueue.pop_front(); pktQueue.pop_front();
packet_counts[zm_packet->packet.stream_index] -= 1; packet_counts[zm_packet->packet.stream_index] -= 1;
//delete zm_packet;
} }
} // end if have at least max_video_packet_count video packets remaining } // end if have at least max_video_packet_count video packets remaining
// We signal on every packet because someday we may analyze sound // We signal on every packet because someday we may analyze sound

View File

@ -118,41 +118,34 @@ constexpr Rgb kRGBTransparent = 0x01000000;
/* Convert RGB colour value into BGR\ARGB\ABGR */ /* Convert RGB colour value into BGR\ARGB\ABGR */
inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) { inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) {
Rgb result; Rgb result = 0;
switch(p_subpixorder) { switch (p_subpixorder) {
case ZM_SUBPIX_ORDER_BGR: case ZM_SUBPIX_ORDER_BGR:
case ZM_SUBPIX_ORDER_BGRA: case ZM_SUBPIX_ORDER_BGRA:
{ BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col); GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col); RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col); break;
}
break;
case ZM_SUBPIX_ORDER_ARGB: case ZM_SUBPIX_ORDER_ARGB:
{ BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col); GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col); RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col); break;
}
break;
case ZM_SUBPIX_ORDER_ABGR: case ZM_SUBPIX_ORDER_ABGR:
{ BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col); GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col); RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col); break;
} /* Grayscale */
break;
/* Grayscale */
case ZM_SUBPIX_ORDER_NONE: case ZM_SUBPIX_ORDER_NONE:
result = p_col & 0xff; result = p_col & 0xff;
break; break;
default: default:
return p_col; result = p_col;
break; break;
} }
return result; return result;
} }

View File

@ -46,6 +46,7 @@ RETSIGTYPE zm_die_handler(int signal, siginfo_t * info, void *context)
RETSIGTYPE zm_die_handler(int signal) RETSIGTYPE zm_die_handler(int signal)
#endif #endif
{ {
zm_terminate = true;
Error("Got signal %d (%s), crashing", signal, strsignal(signal)); Error("Got signal %d (%s), crashing", signal, strsignal(signal));
#if (defined(__i386__) || defined(__x86_64__)) #if (defined(__i386__) || defined(__x86_64__))
// Get more information if available // Get more information if available

View File

@ -128,10 +128,10 @@ bool StreamBase::checkCommandQueue() {
return true; return true;
} }
} else if ( connkey ) { } else if ( connkey ) {
Warning("No sd in checkCommandQueue, comms not open?"); Warning("No sd in checkCommandQueue, comms not open for connkey %06d?", connkey);
} else { } else {
// Perfectly valid if only getting a snapshot // Perfectly valid if only getting a snapshot
Debug(1, "No sd in checkCommandQueue, comms not open?"); Debug(1, "No sd in checkCommandQueue, comms not open.");
} }
return false; return false;
} // end bool StreamBase::checkCommandQueue() } // end bool StreamBase::checkCommandQueue()
@ -386,9 +386,9 @@ void StreamBase::openComms() {
strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path)); strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path));
rem_addr.sun_family = AF_UNIX; rem_addr.sun_family = AF_UNIX;
last_comm_update = std::chrono::system_clock::now(); last_comm_update = std::chrono::steady_clock::now();
Debug(3, "comms open at %s", loc_sock_path);
} // end if connKey > 0 } // end if connKey > 0
Debug(3, "comms open at %s", loc_sock_path);
} // end void StreamBase::openComms() } // end void StreamBase::openComms()
void StreamBase::closeComms() { void StreamBase::closeComms() {

View File

@ -88,6 +88,7 @@ protected:
CMD_VARPLAY, CMD_VARPLAY,
CMD_GET_IMAGE, CMD_GET_IMAGE,
CMD_QUIT, CMD_QUIT,
CMD_MAXFPS,
CMD_QUERY=99 CMD_QUERY=99
} MsgCommand; } MsgCommand;
@ -118,21 +119,22 @@ protected:
bool paused; bool paused;
int step; int step;
SystemTimePoint now; TimePoint now;
SystemTimePoint last_comm_update; TimePoint last_comm_update;
double maxfps; double maxfps;
double base_fps; // Should be capturing fps, hence a rough target double base_fps; // Should be capturing fps, hence a rough target
double effective_fps; // Target fps after taking max_fps into account double effective_fps; // Target fps after taking max_fps into account
double actual_fps; // sliding calculated actual streaming fps achieved double actual_fps; // sliding calculated actual streaming fps achieved
SystemTimePoint last_fps_update; TimePoint last_fps_update;
int frame_count; // Count of frames sent int frame_count; // Count of frames sent
int last_frame_count; // Used in calculating actual_fps from frame_count - last_frame_count int last_frame_count; // Used in calculating actual_fps from frame_count - last_frame_count
int frame_mod; int frame_mod;
SystemTimePoint last_frame_sent; TimePoint last_frame_sent;
SystemTimePoint last_frame_timestamp; SystemTimePoint last_frame_timestamp;
TimePoint when_to_send_next_frame; // When to send next frame so if now < send_next_frame, skip
VideoStream *vid_stream; VideoStream *vid_stream;
@ -207,10 +209,11 @@ public:
scale = DEFAULT_SCALE; scale = DEFAULT_SCALE;
} }
void setStreamReplayRate(int p_rate) { void setStreamReplayRate(int p_rate) {
Debug(2, "Setting replay_rate %d", p_rate); Debug(1, "Setting replay_rate %d", p_rate);
replay_rate = p_rate; replay_rate = p_rate;
} }
void setStreamMaxFPS(double p_maxfps) { void setStreamMaxFPS(double p_maxfps) {
Debug(1, "Setting max fps to %f", p_maxfps);
maxfps = p_maxfps; maxfps = p_maxfps;
} }
void setStreamBitrate(int p_bitrate) { void setStreamBitrate(int p_bitrate) {

52
src/zm_time.cpp Normal file
View File

@ -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;
}

View File

@ -21,6 +21,7 @@
#define ZM_TIME_H #define ZM_TIME_H
#include <chrono> #include <chrono>
#include <string>
#include <sys/time.h> #include <sys/time.h>
typedef std::chrono::microseconds Microseconds; typedef std::chrono::microseconds Microseconds;
@ -120,4 +121,7 @@ class TimeSegmentAdder {
bool finished_; bool finished_;
}; };
std::string SystemTimePointToString(SystemTimePoint tp);
std::string TimePointToString(TimePoint tp);
#endif // ZM_TIME_H #endif // ZM_TIME_H

View File

@ -252,8 +252,15 @@ void HwCapsDetect() {
#elif defined(__arm__) #elif defined(__arm__)
// ARM processor in 32bit mode // ARM processor in 32bit mode
// To see if it supports NEON, we need to get that information from the kernel // To see if it supports NEON, we need to get that information from the kernel
#ifdef __linux__
unsigned long auxval = getauxval(AT_HWCAP); unsigned long auxval = getauxval(AT_HWCAP);
if (auxval & HWCAP_ARM_NEON) { 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"); Debug(1,"Detected ARM (AArch32) processor with Neon");
neonversion = 1; neonversion = 1;
} else { } else {

View File

@ -151,6 +151,7 @@ bool VideoStore::open() {
Debug(3, "Encoder Option %s=%s", e->key, e->value); Debug(3, "Encoder Option %s=%s", e->key, e->value);
} }
} }
av_dict_free(&opts);
if (video_in_stream) { if (video_in_stream) {
zm_dump_codecpar(video_in_stream->codecpar); zm_dump_codecpar(video_in_stream->codecpar);
@ -184,6 +185,7 @@ bool VideoStore::open() {
} }
} // end if orientation } // 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)) { if (av_dict_get(opts, "new_extradata", nullptr, AV_DICT_MATCH_CASE)) {
av_dict_set(&opts, "new_extradata", nullptr, 0); 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 // 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) { if (ret < 0) {
Error("Could not initialize stream parameteres"); Error("Could not initialize stream parameteres");
} }
av_dict_free(&opts);
} // end if extradata_entry } // end if extradata_entry
av_dict_free(&opts);
} else if (monitor->GetOptVideoWriter() == Monitor::ENCODE) { } else if (monitor->GetOptVideoWriter() == Monitor::ENCODE) {
int wanted_codec = monitor->OutputCodec(); int wanted_codec = monitor->OutputCodec();
if (!wanted_codec) { if (!wanted_codec) {
@ -485,6 +487,7 @@ bool VideoStore::open() {
zm_dump_stream_format(oc, 0, 0, 1); zm_dump_stream_format(oc, 0, 0, 1);
if (audio_out_stream) zm_dump_stream_format(oc, 1, 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); const AVDictionaryEntry *movflags_entry = av_dict_get(opts, "movflags", nullptr, AV_DICT_MATCH_CASE);
if (!movflags_entry) { if (!movflags_entry) {
Debug(1, "setting movflags to frag_keyframe+empty_moov"); Debug(1, "setting movflags to frag_keyframe+empty_moov");
@ -616,7 +619,8 @@ VideoStore::~VideoStore() {
Debug(1, "Writing trailer"); Debug(1, "Writing trailer");
/* Write the trailer before close */ /* 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)); Error("Error writing trailer %s", av_err2str(rc));
} else { } else {
Debug(3, "Success Writing trailer"); Debug(3, "Success Writing trailer");
@ -626,7 +630,7 @@ VideoStore::~VideoStore() {
if (!(out_format->flags & AVFMT_NOFILE)) { if (!(out_format->flags & AVFMT_NOFILE)) {
/* Close the out file. */ /* Close the out file. */
Debug(4, "Closing"); 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)); Error("Error closing avio %s", av_err2str(rc));
} }
} else { } else {

View File

@ -111,6 +111,13 @@ class VideoStore {
int writePacket(const std::shared_ptr<ZMPacket> &pkt); int writePacket(const std::shared_ptr<ZMPacket> &pkt);
int write_packets(PacketQueue &queue); int write_packets(PacketQueue &queue);
void flush_codecs(); 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 #endif // ZM_VIDEOSTORE_H

View File

@ -206,7 +206,7 @@ bool Zone::CheckAlarms(const Image *delta_image) {
// Get the difference image // Get the difference image
Image *diff_image = image = new Image(*delta_image); Image *diff_image = image = new Image(*delta_image);
int diff_width = diff_image->Width(); 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; uint8_t* pdiff;
unsigned int pixel_diff_count = 0; 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 lo_x = ranges[y].lo_x;
int hi_x = ranges[y].hi_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++) { for (int x = lo_x; x <= hi_x; x++, pdiff++) {
if (*pdiff == kWhite) { if (*pdiff == kWhite) {
@ -366,7 +366,7 @@ bool Zone::CheckAlarms(const Image *delta_image) {
int lo_x = ranges[y].lo_x; int lo_x = ranges[y].lo_x;
int hi_x = ranges[y].hi_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++) { for (int x = lo_x; x <= hi_x; x++, pdiff++) {
if (*pdiff == kWhite) { if (*pdiff == kWhite) {
Debug(9, "Got white pixel at %d,%d (%p)", x, y, pdiff); Debug(9, "Got white pixel at %d,%d (%p)", x, y, pdiff);
@ -878,16 +878,23 @@ std::vector<Zone> Zone::Load(Monitor *monitor) {
continue; continue;
} }
if (polygon.Extent().Lo().x_ < 0 || polygon.Extent().Hi().x_ > static_cast<int32>(monitor->Width()) if (polygon.Extent().Lo().x_ < 0
|| 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", 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, Id,
Name, Name,
monitor->Name(), monitor->Name(),
polygon.Extent().Lo().x_, polygon.Extent().Lo().x_,
polygon.Extent().Lo().y_, polygon.Extent().Lo().y_,
polygon.Extent().Hi().x_, polygon.Extent().Hi().x_,
polygon.Extent().Hi().y_); polygon.Extent().Hi().y_,
monitor->Width(),
monitor->Height());
polygon.Clip(Box( polygon.Clip(Box(
{0, 0}, {0, 0},
@ -980,7 +987,7 @@ void Zone::std_alarmedpixels(
unsigned int hi_x = ranges[y].hi_x; unsigned int hi_x = ranges[y].hi_x;
Debug(7, "Checking line %d from %d -> %d", y, lo_x, 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); const uint8_t *ppoly = ppoly_image->Buffer(lo_x, y);
for ( unsigned int x = lo_x; x <= hi_x; x++, pdiff++, ppoly++ ) { for ( unsigned int x = lo_x; x <= hi_x; x++, pdiff++, ppoly++ ) {

View File

@ -140,7 +140,7 @@ std::shared_ptr<Image> GenerateRandomImage(
Image *image = new Image(width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE); Image *image = new Image(width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE);
// Set it to black initially. // 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. // Now randomize the pixels inside a box.
const int box_width = (width * change_box_percent) / 100; 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); const int box_y = (int) ((uint64_t) mt_rand() * (height - box_height) / RAND_MAX);
for (int y = 0 ; y < box_height ; y++) { 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++) { for (int x = 0 ; x < box_width ; x++) {
row[x] = (uint8_t) mt_rand(); row[x] = (uint8_t) mt_rand();
} }

View File

@ -220,12 +220,13 @@ int main(int argc, char *argv[]) {
zmSetDefaultTermHandler(); zmSetDefaultTermHandler();
zmSetDefaultDieHandler(); zmSetDefaultDieHandler();
sigset_t block_set; struct sigaction sa;
sigemptyset(&block_set); sa.sa_handler = SIG_IGN; //handle signal by ignoring
sigemptyset(&sa.sa_mask);
sigaddset(&block_set, SIGHUP); sa.sa_flags = 0;
sigaddset(&block_set, SIGUSR1); if (sigaction(SIGCHLD, &sa, 0) == -1) {
sigaddset(&block_set, SIGUSR2); Error("Unable to set SIGCHLD to ignore. There may be zombies.");
}
int result = 0; int result = 0;
int prime_capture_log_count = 0; int prime_capture_log_count = 0;
@ -273,7 +274,7 @@ int main(int argc, char *argv[]) {
std::this_thread::sleep_for(sleep_time); std::this_thread::sleep_for(sleep_time);
} }
if (zm_terminate){ if (zm_terminate) {
break; break;
} }
@ -317,6 +318,7 @@ int main(int argc, char *argv[]) {
result = -1; result = -1;
break; break;
} }
monitors[i]->UpdateFPS();
// capture_delay is the amount of time we should sleep in useconds to achieve the desired framerate. // 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() Microseconds delay = (monitors[i]->GetState() == Monitor::ALARM) ? monitors[i]->GetAlarmCaptureDelay()
@ -373,6 +375,7 @@ int main(int argc, char *argv[]) {
monitor->Id()); monitor->Id());
zmDbDo(sql); zmDbDo(sql);
} }
monitors.clear();
Image::Deinitialise(); Image::Deinitialise();
Debug(1, "terminating"); Debug(1, "terminating");

View File

@ -482,7 +482,7 @@ int main(int argc, char *argv[]) {
exit_zmu(-1); exit_zmu(-1);
} }
if ( !ValidateAccess(user, mon_id, function) ) { 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); exit_zmu(-1);
} }
} // end if auth } // end if auth
@ -497,6 +497,16 @@ int main(int argc, char *argv[]) {
if ( verbose ) { if ( verbose ) {
printf("Monitor %u(%s)\n", monitor->Id(), monitor->Name()); 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() ) { if ( !monitor->connect() ) {
Error("Can't connect to capture daemon: %d %s", monitor->Id(), monitor->Name()); Error("Can't connect to capture daemon: %d %s", monitor->Id(), monitor->Name());
exit_zmu(-1); exit_zmu(-1);

View File

@ -87,11 +87,7 @@ else
fi; fi;
if [ "$DISTROS" == "" ]; then if [ "$DISTROS" == "" ]; then
if [ "$RELEASE" != "" ]; then DISTROS=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`;
DISTROS="bionic,focal,hirsute,impish"
else
DISTROS=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`;
fi;
echo "Defaulting to $DISTROS for distribution"; echo "Defaulting to $DISTROS for distribution";
else else
echo "Building for $DISTROS"; echo "Building for $DISTROS";
@ -116,52 +112,6 @@ else
echo "Defaulting to ZoneMinder upstream git" echo "Defaulting to ZoneMinder upstream git"
GITHUB_FORK="ZoneMinder" GITHUB_FORK="ZoneMinder"
fi; 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; fi;
# Instead of cloning from github each time, if we have a fork lying around, update it and pull from there instead. # 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" cd "${GITHUB_FORK}_ZoneMinder.git"
echo "git fetch..." echo "git fetch..."
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 ../ cd ../
echo "git clone ${GITHUB_FORK}_ZoneMinder.git ${GITHUB_FORK}_zoneminder_release" echo "git clone ${GITHUB_FORK}_ZoneMinder.git ${GITHUB_FORK}_zoneminder_release"
git clone "${GITHUB_FORK}_ZoneMinder.git" "${GITHUB_FORK}_zoneminder_release" git clone "${GITHUB_FORK}_ZoneMinder.git" "${GITHUB_FORK}_zoneminder_release"
else else
@ -192,14 +135,59 @@ else
fi; fi;
cd "${GITHUB_FORK}_zoneminder_release" 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 if [ -z "$VERSION" ]; then
exit 1; exit 1;
fi; fi;
IFS='.' read -r -a VERSION_PARTS <<< "$VERSION"
cd ../
if [ "$SNAPSHOT" != "stable" ] && [ "$SNAPSHOT" != "" ]; then if [ "$SNAPSHOT" != "stable" ] && [ "$SNAPSHOT" != "" ]; then
VERSION="$VERSION~$SNAPSHOT"; VERSION="$VERSION~$SNAPSHOT";
fi; fi;
@ -230,12 +218,11 @@ rm .gitignore
cd ../ cd ../
if [ !-e "$DIRECTORY.orig.tar.gz" ]; then if [ -e "$DIRECTORY.orig.tar.gz" ]; then
read -p "$DIRECTORY.orig.tar.gz does not exist, create it? [Y/n]" read -p "$DIRECTORY.orig.tar.gz exists, overwrite it? [Y/n]"
if [[ $REPLY == [yY] ]]; then if [[ "$REPLY" == "" || "$REPLY" == [yY] ]]; then
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig fi;
fi;
fi; fi;
IFS=',' ;for DISTRO in `echo "$DISTROS"`; do IFS=',' ;for DISTRO in `echo "$DISTROS"`; do
@ -358,6 +345,22 @@ EOF
fi; fi;
else else
SC="zoneminder_${VERSION}-${DISTRO}${PACKAGE_VERSION}_source.changes"; 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"; dput="Y";
if [ "$INTERACTIVE" != "no" ]; then if [ "$INTERACTIVE" != "no" ]; then

View File

@ -1 +1 @@
1.37.1 1.37.6

View File

@ -93,7 +93,7 @@ if ( canView('Events') or canView('Snapshots') ) {
$exportFormat, $exportFormat,
$exportCompress, $exportCompress,
$exportStructure, $exportStructure,
(!empty($_REQUEST['exportFile'])?$_REQUEST['exportFile']:'zmExport'), (!empty($_REQUEST['exportFile'])?$_REQUEST['exportFile']:'zmExport')
)) { )) {
ajaxResponse(array('exportFile'=>$exportFile)); ajaxResponse(array('exportFile'=>$exportFile));
} else { } else {

View File

@ -6,28 +6,30 @@ $data = array();
// INITIALIZE AND CHECK SANITY // 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']) ) { if (empty($_REQUEST['task'])) {
$message = 'Must specify a task'; $message = 'Must specify a task<br/>';
} else { } else {
$task = $_REQUEST['task']; $task = $_REQUEST['task'];
} }
if ( empty($_REQUEST['eids']) ) { if (empty($_REQUEST['eids'])) {
if ( isset($_REQUEST['task']) && $_REQUEST['task'] != 'query' ) $message = 'No event id(s) supplied'; if (isset($_REQUEST['task']) && $_REQUEST['task'] != 'query')
$message = 'No event id(s) supplied<br/>';
} else { } else {
$eids = $_REQUEST['eids']; $eids = $_REQUEST['eids'];
} }
if ( $message ) { if ($message) {
ajaxError($message); ajaxError($message);
return; return;
} }
require_once('includes/Filter.php'); require_once('includes/Filter.php');
$filter = isset($_REQUEST['filter']) ? ZM\Filter::parse($_REQUEST['filter']) : new ZM\Filter(); $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'])); $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(); $advsearch = isset($_REQUEST['advsearch']) ? json_decode($_REQUEST['advsearch'], JSON_OBJECT_AS_ARRAY) : array();
// Order specifies the sort direction, either asc or desc // 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 specifies the name of the column to sort on
$sort = 'StartDateTime'; $sort = $filter->sort_field();
if (isset($_REQUEST['sort'])) { if (isset($_REQUEST['sort'])) {
$sort = $_REQUEST['sort']; $sort = $_REQUEST['sort'];
if ($sort == 'EndDateTime') { if ($sort == 'EndDateTime') {
@ -56,20 +67,19 @@ if (isset($_REQUEST['sort'])) {
// Offset specifies the starting row to return, used for pagination // Offset specifies the starting row to return, used for pagination
$offset = 0; $offset = 0;
if ( isset($_REQUEST['offset']) ) { if (isset($_REQUEST['offset'])) {
if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) { if ((!is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']))) {
ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']); ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']);
} else { } else {
$offset = $_REQUEST['offset']; $offset = $_REQUEST['offset'];
} }
} }
// Limit specifies the number of rows to return // Limit specifies the number of rows to return
// Set the default to 0 for events view, to prevent an issue with ALL pagination // Set the default to 0 for events view, to prevent an issue with ALL pagination
$limit = 0; $limit = 0;
if ( isset($_REQUEST['limit']) ) { if (isset($_REQUEST['limit'])) {
if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) { if ((!is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']))) {
ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']); ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']);
} else { } else {
$limit = $_REQUEST['limit']; $limit = $_REQUEST['limit'];
@ -80,25 +90,24 @@ if ( isset($_REQUEST['limit']) ) {
// MAIN LOOP // MAIN LOOP
// //
switch ( $task ) { switch ($task) {
case 'archive' : case 'archive' :
foreach ( $eids as $eid ) archiveRequest($task, $eid); foreach ($eids as $eid) archiveRequest($task, $eid);
break; break;
case 'unarchive' : case 'unarchive' :
# The idea is that anyone can archive, but only people with Event Edit permission can 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']); ajaxError('Insufficient permissions for user '.$user['Username']);
return; return;
} }
foreach ( $eids as $eid ) archiveRequest($task, $eid); foreach ($eids as $eid) archiveRequest($task, $eid);
break; break;
case 'delete' : case 'delete' :
if ( !canEdit('Events') ) { if (!canEdit('Events')) {
ajaxError('Insufficient permissions for user '.$user['Username']); ajaxError('Insufficient permissions for user '.$user['Username']);
return; return;
} }
foreach ($eids as $eid) $data[] = deleteRequest($eid);
foreach ( $eids as $eid ) $data[] = deleteRequest($eid);
break; break;
case 'query' : case 'query' :
$data = queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit); $data = queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit);
@ -128,6 +137,8 @@ function deleteRequest($eid) {
$message[] = array($eid=>'Event not found.'); $message[] = array($eid=>'Event not found.');
} else if ( $event->Archived() ) { } else if ( $event->Archived() ) {
$message[] = array($eid=>'Event is archived, cannot delete it.'); $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 { } else {
$event->delete(); $event->delete();
} }
@ -136,7 +147,6 @@ function deleteRequest($eid) {
} }
function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit) { function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit) {
$data = array( $data = array(
'total' => 0, 'total' => 0,
'totalNotFiltered' => 0, 'totalNotFiltered' => 0,
@ -145,7 +155,7 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
); );
$failed = !$filter->test_pre_sql_conditions(); $failed = !$filter->test_pre_sql_conditions();
if ( $failed ) { if ($failed) {
ZM\Debug('Pre conditions failed, not doing sql'); ZM\Debug('Pre conditions failed, not doing sql');
return $data; 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 // The names of columns shown in the event view that are NOT dB columns in the database
$col_alt = array('Monitor', 'Storage'); $col_alt = array('Monitor', 'Storage');
if ( !in_array($sort, array_merge($columns, $col_alt)) ) { if ( $sort != '' ) {
ZM\Error('Invalid sort field: ' . $sort); if (!in_array($sort, array_merge($columns, $col_alt))) {
$sort = 'Id'; ZM\Error('Invalid sort field: ' . $sort);
$sort = '';
} else if ( $sort == 'Monitor' ) {
$sort = 'M.Name';
} else {
$sort = 'E.'.$sort;
}
} }
$values = array(); $values = array();
$likes = array(); $likes = array();
$where = $filter->sql()?' WHERE ('.$filter->sql().')' : ''; $where = $filter->sql()?' WHERE ('.$filter->sql().')' : '';
$sort = $sort == 'Monitor' ? 'M.Name' : 'E.'.$sort;
$col_str = 'E.*, M.Name AS Monitor'; $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(); $storage_areas = ZM\Storage::find();
$StorageById = array(); $StorageById = array();
foreach ( $storage_areas as $S ) { foreach ($storage_areas as $S) {
$StorageById[$S->Id()] = $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); ZM\Debug('Calling the following sql query: ' .$sql);
$query = dbQuery($sql, $values); $query = dbQuery($sql, $values);
if ( $query ) { if (!$query) {
while ( $row = dbFetchNext($query) ) { ajaxError(dbError($sql));
$event = new ZM\Event($row); return;
$event->remove_from_cache();
if ( !$filter->test_post_sql_conditions($event) ) {
continue;
}
$event_ids[] = $event->Id();
$unfiltered_rows[] = $row;
} # end foreach row
} }
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.'); ZM\Debug('Have ' . count($unfiltered_rows) . ' events matching base filter.');
$filtered_rows = null; $filtered_rows = null;
if ( count($advsearch) or $search != '' ) { if (count($advsearch) or $search != '') {
$search_filter = new ZM\Filter(); $search_filter = new ZM\Filter();
$search_filter = $search_filter->addTerm(array('cnj'=>'and', 'attr'=>'Id', 'op'=>'IN', 'val'=>$event_ids)); $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 // 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 // Making an exuctive decision to ignore the normal search, when advanced search is in use
// Alternatively we could try to do both // Alternatively we could try to do both
if ( count($advsearch) ) { if (count($advsearch)) {
$terms = array(); $terms = array();
foreach ( $advsearch as $col=>$text ) { foreach ($advsearch as $col=>$text) {
$terms[] = array('cnj'=>'and', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$text); $terms[] = array('cnj'=>'and', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$text);
} # end foreach col in advsearch } # end foreach col in advsearch
$terms[0]['obr'] = 1; $terms[0]['obr'] = 1;
$terms[count($terms)-1]['cbr'] = 1; $terms[count($terms)-1]['cbr'] = 1;
$search_filter->addTerms($terms); $search_filter->addTerms($terms);
} else if ( $search != '' ) { } else if ($search != '') {
$search = '%' .$search. '%'; $search = '%' .$search. '%';
$terms = array(); $terms = array();
foreach ( $columns as $col ) { foreach ($columns as $col) {
$terms[] = array('cnj'=>'or', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$search); $terms[] = array('cnj'=>'or', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$search);
} }
$terms[0]['obr'] = 1; $terms[0]['obr'] = 1;
@ -228,15 +245,17 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
} # end if search } # 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; $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); $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 { } else {
$filtered_rows = $unfiltered_rows; $filtered_rows = $unfiltered_rows;
} # end if search_filter->terms() > 1 } # end if search_filter->terms() > 1
if ($limit)
$filtered_rows = array_slice($filtered_rows, $offset, $limit);
$returned_rows = array(); $returned_rows = array();
foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) { foreach ($filtered_rows as $row) {
$event = new ZM\Event($row); $event = new ZM\Event($row);
$scale = intval(5*100*ZM_WEB_LIST_THUMB_WIDTH / $event->Width()); $scale = intval(5*100*ZM_WEB_LIST_THUMB_WIDTH / $event->Width());

View File

@ -9,13 +9,13 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<?php <?php
//require_once('includes/Filter.php'); require_once('includes/Filter.php');
$fid = validInt($_REQUEST['fid']); $fid = validInt($_REQUEST['fid']);
if ( !$fid ) { if (!$fid) {
echo '<div class="error">No filter id specified.</div>'; echo '<div class="error">No filter id specified.</div>';
} else { } else {
$filter = new ZM\Filter($_REQUEST['fid']); $filter = new ZM\Filter($_REQUEST['fid']);
if ( ! $filter->Id() ) { if (!$filter->Id()) {
echo '<div class="error">Filter not found for id '.$_REQUEST['fid'].'</div>'; 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 // We have to manually insert the csrf key into the form when using a modal generated via ajax call
echo getCSRFinputHTML(); 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"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php echo translate('Cancel')?> </button> <button type="button" class="btn btn-secondary" data-dismiss="modal"><?php echo translate('Cancel')?> </button>
</div> </div>

Some files were not shown because too many files have changed in this diff Show More