Merge branch 'storageareas' of github.com:ConnorTechnology/ZoneMinder into storageareas

pull/2090/head^2
Isaac Connor 2018-04-25 04:29:35 -04:00
commit 184596ed4b
977 changed files with 20692 additions and 14655 deletions

4
.gitmodules vendored
View File

@ -1,7 +1,7 @@
[submodule "web/api/app/Plugin/Crud"] [submodule "web/api/app/Plugin/Crud"]
path = web/api/app/Plugin/Crud path = web/api/app/Plugin/Crud
url = https://github.com/FriendsOfCake/crud.git url = https://github.com/ZoneMinder/crud.git
branch = 3.0 branch = 3.0
[submodule "web/api/app/Plugin/CakePHP-Enum-Behavior"] [submodule "web/api/app/Plugin/CakePHP-Enum-Behavior"]
path = web/api/app/Plugin/CakePHP-Enum-Behavior path = web/api/app/Plugin/CakePHP-Enum-Behavior
url = https://github.com/connortechnology/CakePHP-Enum-Behavior.git url = https://github.com/ZoneMinder/CakePHP-Enum-Behavior.git

View File

@ -26,17 +26,15 @@ addons:
- binfmt-support - binfmt-support
- qemu - qemu
- qemu-user-static - qemu-user-static
- dnsutils
- traceroute
install: install:
- update-binfmts --enable qemu-arm - update-binfmts --enable qemu-arm
env: env:
global: global:
- DEB_BUILD_OPTIONS="parallel=4" - SMPFLAGS=-j4
- DEBUILD_LINTIAN="no"
- SMPFLAGS="-j4"
matrix: matrix:
- OS=el DIST=6
- OS=el DIST=6 ARCH=i386 DOCKER_REPO=knnniggett/packpack
- OS=el DIST=7 - OS=el DIST=7
- OS=fedora DIST=26 DOCKER_REPO=knnniggett/packpack - OS=fedora DIST=26 DOCKER_REPO=knnniggett/packpack
- OS=fedora DIST=27 DOCKER_REPO=knnniggett/packpack - OS=fedora DIST=27 DOCKER_REPO=knnniggett/packpack

View File

@ -58,6 +58,8 @@ if(NOT HOST_OS)
"ZoneMinder was unable to deterimine the host OS. Please report this. Value of CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}") "ZoneMinder was unable to deterimine the host OS. Please report this. Value of CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
endif(NOT HOST_OS) endif(NOT HOST_OS)
set (CMAKE_CXX_STANDARD 11)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# Default CLFAGS and CXXFLAGS: # Default CLFAGS and CXXFLAGS:
set(CMAKE_C_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2") set(CMAKE_C_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2")
set(CMAKE_CXX_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2") set(CMAKE_CXX_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2")
@ -139,6 +141,8 @@ set(ZM_WEBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/zoneminder/www"
"Location of the web files, default: <prefix>/${CMAKE_INSTALL_DATADIR}/zoneminder/www") "Location of the web files, default: <prefix>/${CMAKE_INSTALL_DATADIR}/zoneminder/www")
set(ZM_CGIDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH set(ZM_CGIDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH
"Location of the cgi-bin files, default: <prefix>/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin") "Location of the cgi-bin files, default: <prefix>/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin")
set(ZM_CACHEDIR "/var/cache/zoneminder" CACHE PATH
"Location of the web server cache busting files, default: /var/cache/zoneminder")
set(ZM_CONTENTDIR "/var/lib/zoneminder" CACHE PATH set(ZM_CONTENTDIR "/var/lib/zoneminder" CACHE PATH
"Location of dynamic content (events and images), default: /var/lib/zoneminder") "Location of dynamic content (events and images), default: /var/lib/zoneminder")
set(ZM_DB_HOST "localhost" CACHE STRING set(ZM_DB_HOST "localhost" CACHE STRING
@ -203,7 +207,7 @@ set(ZM_PERL_SEARCH_PATH "" CACHE PATH
where ZM_PERL_MM_PARMS has been modified such that ZoneMinder's Perl modules are where ZM_PERL_MM_PARMS has been modified such that ZoneMinder's Perl modules are
installed outside Perl's default search path.") installed outside Perl's default search path.")
set(ZM_TARGET_DISTRO "" CACHE STRING set(ZM_TARGET_DISTRO "" CACHE STRING
"Build ZoneMinder for a specific distribution. Currently, valid names are: fc24, fc25, el6, el7, OS13, FreeBSD") "Build ZoneMinder for a specific distribution. Currently, valid names are: fc27, fc26, el7, OS13, FreeBSD")
set(ZM_SYSTEMD "OFF" CACHE BOOL set(ZM_SYSTEMD "OFF" CACHE BOOL
"Set to ON to force building ZM with systemd support. default: OFF") "Set to ON to force building ZM with systemd support. default: OFF")
@ -482,24 +486,24 @@ if(MP4V2_LIBRARIES)
if(MP4V2_INCLUDE_DIR) if(MP4V2_INCLUDE_DIR)
include_directories("${MP4V2_INCLUDE_DIR}") include_directories("${MP4V2_INCLUDE_DIR}")
set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}")
check_include_file("mp4v2/mp4v2.h" HAVE_MP4V2_MP4V2_H)
endif(MP4V2_INCLUDE_DIR) endif(MP4V2_INCLUDE_DIR)
check_include_file("mp4v2/mp4v2.h" HAVE_MP4V2_MP4V2_H)
# mp4v2.h # mp4v2.h
find_path(MP4V2_INCLUDE_DIR mp4v2.h) find_path(MP4V2_INCLUDE_DIR mp4v2.h)
if(MP4V2_INCLUDE_DIR) if(MP4V2_INCLUDE_DIR)
include_directories("${MP4V2_INCLUDE_DIR}") include_directories("${MP4V2_INCLUDE_DIR}")
set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}")
check_include_file("mp4v2.h" HAVE_MP4V2_H)
endif(MP4V2_INCLUDE_DIR) endif(MP4V2_INCLUDE_DIR)
check_include_file("mp4v2.h" HAVE_MP4V2_H)
# mp4.h # mp4.h
find_path(MP4V2_INCLUDE_DIR mp4.h) find_path(MP4V2_INCLUDE_DIR mp4.h)
if(MP4V2_INCLUDE_DIR) if(MP4V2_INCLUDE_DIR)
include_directories("${MP4V2_INCLUDE_DIR}") include_directories("${MP4V2_INCLUDE_DIR}")
set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}")
check_include_file("mp4.h" HAVE_MP4_H)
endif(MP4V2_INCLUDE_DIR) endif(MP4V2_INCLUDE_DIR)
check_include_file("mp4.h" HAVE_MP4_H)
mark_as_advanced(FORCE MP4V2_LIBRARIES MP4V2_INCLUDE_DIR) mark_as_advanced(FORCE MP4V2_LIBRARIES MP4V2_INCLUDE_DIR)
set(optlibsfound "${optlibsfound} mp4v2") set(optlibsfound "${optlibsfound} mp4v2")

View File

@ -1,7 +1,7 @@
ZoneMinder ZoneMinder
========== ==========
[![Build Status](https://travis-ci.org/ZoneMinder/ZoneMinder.png)](https://travis-ci.org/ZoneMinder/ZoneMinder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received) [![Build Status](https://travis-ci.org/ZoneMinder/zoneminder.png)](https://travis-ci.org/ZoneMinder/zoneminder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org

View File

@ -2,6 +2,7 @@
# Create files from the .in files # Create files from the .in files
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)
# 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")
@ -9,8 +10,12 @@ file(GLOB dbfileslist RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "zm_update-*.sql")
# Install the database upgrade scripts # Install the database upgrade scripts
install(FILES ${dbfileslist} DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") install(FILES ${dbfileslist} DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
# install zm_update-1.31.30.sql
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_update-1.31.30.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")
# install triggers.sql # install triggers.sql
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/triggers.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db") install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/triggers.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")

View File

@ -60,7 +60,6 @@ FOR EACH ROW
// //
delimiter //
DROP TRIGGER IF EXISTS Events_Week_delete_trigger// DROP TRIGGER IF EXISTS Events_Week_delete_trigger//
CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week
FOR EACH ROW BEGIN FOR EACH ROW BEGIN
@ -89,9 +88,6 @@ FOR EACH ROW
END; END;
// //
DELIMITER ;
delimiter //
DROP TRIGGER IF EXISTS Events_Month_delete_trigger// DROP TRIGGER IF EXISTS Events_Month_delete_trigger//
CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month
FOR EACH ROW BEGIN FOR EACH ROW BEGIN
@ -120,39 +116,26 @@ FOR EACH ROW
END; END;
// //
drop procedure if exists update_storage_stats; drop procedure if exists update_storage_stats//
create procedure update_storage_stats(IN StorageId smallint(5), IN space BIGINT)
sql security invoker
deterministic
begin
update Storage set DiskSpace = COALESCE(DiskSpace,0) + COALESCE(space,0) where Id = StorageId;
end;
//
drop trigger if exists event_update_trigger// drop trigger if exists event_update_trigger//
CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events
FOR EACH ROW FOR EACH ROW
BEGIN BEGIN
declare diff BIGINT default 0; declare diff BIGINT default 0;
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0); set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( NEW.StorageId = OLD.StorageID ) THEN IF ( NEW.StorageId = OLD.StorageID ) THEN
IF ( diff ) THEN IF ( diff ) THEN
call update_storage_stats(OLD.StorageId, diff); UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + diff WHERE Id = OLD.StorageId;
END IF; END IF;
ELSE ELSE
IF ( NEW.DiskSpace ) THEN IF ( NEW.DiskSpace ) THEN
call update_storage_stats(NEW.StorageId, NEW.DiskSpace); UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Id = NEW.StorageId;
END IF; END IF;
IF ( OLD.DiskSpace ) THEN IF ( OLD.DiskSpace ) THEN
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace); UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) - OLD.DiskSpace WHERE Id = OLD.StorageId;
END IF; END IF;
END IF; END IF;
@ -220,7 +203,7 @@ CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events
FOR EACH ROW FOR EACH ROW
BEGIN BEGIN
IF ( OLD.DiskSpace ) THEN IF ( OLD.DiskSpace ) THEN
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace); UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) - CAST(OLD.DiskSpace AS SIGNED) WHERE Id = OLD.StorageId;
END IF; END IF;
DELETE FROM Events_Hour WHERE EventId=OLD.Id; DELETE FROM Events_Hour WHERE EventId=OLD.Id;
DELETE FROM Events_Day WHERE EventId=OLD.Id; DELETE FROM Events_Day WHERE EventId=OLD.Id;

View File

@ -182,7 +182,7 @@ CREATE TABLE `Devices` (
DROP TABLE IF EXISTS `Events`; DROP TABLE IF EXISTS `Events`;
CREATE TABLE `Events` ( CREATE TABLE `Events` (
`Id` int(10) unsigned NOT NULL auto_increment, `Id` bigint unsigned NOT NULL auto_increment,
`MonitorId` int(10) unsigned NOT NULL default '0', `MonitorId` int(10) unsigned NOT NULL default '0',
`StorageId` smallint(5) unsigned default 0, `StorageId` smallint(5) unsigned default 0,
`Name` varchar(64) NOT NULL default '', `Name` varchar(64) NOT NULL default '',
@ -210,6 +210,7 @@ CREATE TABLE `Events` (
`Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0', `Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0',
`DiskSpace` bigint unsigned default NULL, `DiskSpace` bigint unsigned default NULL,
`Scheme` enum('Deep','Medium','Shallow') NOT NULL default 'Medium', `Scheme` enum('Deep','Medium','Shallow') NOT NULL default 'Medium',
`Locked` BOOLEAN NOT NULL DEFAULT False,
PRIMARY KEY (`Id`), PRIMARY KEY (`Id`),
KEY `Events_MonitorId_idx` (`MonitorId`), KEY `Events_MonitorId_idx` (`MonitorId`),
KEY `Events_StorageId_idx` (`StorageId`), KEY `Events_StorageId_idx` (`StorageId`),
@ -219,7 +220,7 @@ CREATE TABLE `Events` (
DROP TABLE IF EXISTS `Events_Hour`; DROP TABLE IF EXISTS `Events_Hour`;
CREATE TABLE `Events_Hour` ( CREATE TABLE `Events_Hour` (
`EventId` int(10) unsigned NOT NULL, `EventId` BIGINT unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL, `MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL, `StartTime` datetime default NULL,
`DiskSpace` bigint unsigned default NULL, `DiskSpace` bigint unsigned default NULL,
@ -230,7 +231,7 @@ CREATE TABLE `Events_Hour` (
DROP TABLE IF EXISTS `Events_Day`; DROP TABLE IF EXISTS `Events_Day`;
CREATE TABLE `Events_Day` ( CREATE TABLE `Events_Day` (
`EventId` int(10) unsigned NOT NULL, `EventId` BIGINT unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL, `MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL, `StartTime` datetime default NULL,
`DiskSpace` bigint unsigned default NULL, `DiskSpace` bigint unsigned default NULL,
@ -241,7 +242,7 @@ CREATE TABLE `Events_Day` (
DROP TABLE IF EXISTS `Events_Week`; DROP TABLE IF EXISTS `Events_Week`;
CREATE TABLE `Events_Week` ( CREATE TABLE `Events_Week` (
`EventId` int(10) unsigned NOT NULL, `EventId` BIGINT unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL, `MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL, `StartTime` datetime default NULL,
`DiskSpace` bigint unsigned default NULL, `DiskSpace` bigint unsigned default NULL,
@ -252,7 +253,7 @@ CREATE TABLE `Events_Week` (
DROP TABLE IF EXISTS `Events_Month`; DROP TABLE IF EXISTS `Events_Month`;
CREATE TABLE `Events_Month` ( CREATE TABLE `Events_Month` (
`EventId` int(10) unsigned NOT NULL, `EventId` BIGINT unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL, `MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL, `StartTime` datetime default NULL,
`DiskSpace` bigint unsigned default NULL, `DiskSpace` bigint unsigned default NULL,
@ -264,7 +265,7 @@ CREATE TABLE `Events_Month` (
DROP TABLE IF EXISTS `Events_Archived`; DROP TABLE IF EXISTS `Events_Archived`;
CREATE TABLE `Events_Archived` ( CREATE TABLE `Events_Archived` (
`EventId` int(10) unsigned NOT NULL, `EventId` BIGINT unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL, `MonitorId` int(10) unsigned NOT NULL,
`DiskSpace` bigint unsigned default NULL, `DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`EventId`), PRIMARY KEY (`EventId`),
@ -303,8 +304,8 @@ CREATE TABLE `Filters` (
DROP TABLE IF EXISTS `Frames`; DROP TABLE IF EXISTS `Frames`;
CREATE TABLE `Frames` ( CREATE TABLE `Frames` (
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT, `Id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`EventId` int(10) unsigned NOT NULL default '0', `EventId` BIGINT UNSIGNED NOT NULL default '0',
`FrameId` int(10) unsigned NOT NULL default '0', `FrameId` int(10) unsigned NOT NULL default '0',
`Type` enum('Normal','Bulk','Alarm') NOT NULL default 'Normal', `Type` enum('Normal','Bulk','Alarm') NOT NULL default 'Normal',
`TimeStamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, `TimeStamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
@ -498,6 +499,7 @@ CREATE TABLE `Monitors` (
`DefaultView` enum('Events','Control') NOT NULL default 'Events', `DefaultView` enum('Events','Control') NOT NULL default 'Events',
`DefaultRate` smallint(5) unsigned NOT NULL default '100', `DefaultRate` smallint(5) unsigned NOT NULL default '100',
`DefaultScale` smallint(5) unsigned NOT NULL default '100', `DefaultScale` smallint(5) unsigned NOT NULL default '100',
`SignalCheckPoints` INT UNSIGNED NOT NULL default '0',
`SignalCheckColour` varchar(32) NOT NULL default '#0000BE', `SignalCheckColour` varchar(32) NOT NULL default '#0000BE',
`WebColour` varchar(32) NOT NULL default 'red', `WebColour` varchar(32) NOT NULL default 'red',
`Exif` tinyint(1) unsigned NOT NULL default '0', `Exif` tinyint(1) unsigned NOT NULL default '0',
@ -526,6 +528,7 @@ CREATE TABLE `Monitor_Status` (
`Status` enum('Unknown','NotRunning','Running','Connected','Signal') NOT NULL default 'Unknown', `Status` enum('Unknown','NotRunning','Running','Connected','Signal') NOT NULL default 'Unknown',
`CaptureFPS` DECIMAL(10,2) NOT NULL default 0, `CaptureFPS` DECIMAL(10,2) NOT NULL default 0,
`AnalysisFPS` DECIMAL(5,2) NOT NULL default 0, `AnalysisFPS` DECIMAL(5,2) NOT NULL default 0,
`CaptureBandwidth` INT NOT NULL default 0,
PRIMARY KEY (`MonitorId`) PRIMARY KEY (`MonitorId`)
) ENGINE=MEMORY; ) ENGINE=MEMORY;
-- --
@ -701,16 +704,18 @@ CREATE TABLE `Storage` (
`Path` varchar(64) NOT NULL default '', `Path` varchar(64) NOT NULL default '',
`Name` varchar(64) NOT NULL default '', `Name` varchar(64) NOT NULL default '',
`Type` enum('local','s3fs') NOT NULL default 'local', `Type` enum('local','s3fs') NOT NULL default 'local',
`Url` varchar(255) default NULL,
`DiskSpace` bigint default NULL, `DiskSpace` bigint default NULL,
`Scheme` enum('Deep','Medium','Shallow') NOT NULL default 'Medium', `Scheme` enum('Deep','Medium','Shallow') NOT NULL default 'Medium',
`ServerId` int(10) unsigned, `ServerId` int(10) unsigned,
`DoDelete` BOOLEAN NOT NULL DEFAULT true,
PRIMARY KEY (`Id`) PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
-- --
-- Create a default storage location -- Create a default storage location
-- --
insert into Storage VALUES (NULL, '/var/cache/zoneminder/events', 'Default', 'local', NULL, 'Medium', 0 ); insert into Storage VALUES (NULL, '@ZM_DIR_EVENTS@', 'Default', 'local', NULL, NULL, 'Medium', 0, true );
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
@ -771,6 +776,7 @@ INSERT INTO `Controls` VALUES (NULL,'Floureon 1080P','Ffmpeg','Floureon',0,0,0,1
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-423','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,64,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-423','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,64,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
-- --
-- Add some monitor preset values -- Add some monitor preset values

View File

@ -0,0 +1,20 @@
DROP TABLE IF EXISTS `Monitor_Status`;
CREATE TABLE `Monitor_Status` (
`MonitorId` int(10) unsigned NOT NULL,
`Status` enum('Unknown','NotRunning','Running','Connected','Signal') NOT NULL default 'Unknown',
`CaptureFPS` DECIMAL(10,2) NOT NULL default 0,
`AnalysisFPS` DECIMAL(5,2) NOT NULL default 0,
PRIMARY KEY (`MonitorId`)
) ENGINE=MEMORY;
SET SESSION sql_mode='NO_AUTO_VALUE_ON_ZERO';
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM Storage WHERE Name = 'Default' AND Id=0 AND Path='@ZM_DIR_EVENTS@'
) > 0,
"SELECT 'Default Storage Area already exists.'",
"INSERT INTO Storage (Id,Name,Path,Scheme,ServerId) VALUES (0,'Default','@ZM_DIR_EVENTS@','Medium',NULL)"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

12
db/zm_update-1.31.40.sql Normal file
View File

@ -0,0 +1,12 @@
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Storage'
AND column_name = 'Url'
) > 0,
"SELECT 'Column Url already exists in Storage'",
"ALTER TABLE `Storage` ADD `Url` VARCHAR(255) default NULL AFTER `Type`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

25
db/zm_update-1.31.41.sql Normal file
View File

@ -0,0 +1,25 @@
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Storage'
AND column_name = 'DoDelete'
) > 0,
"SELECT 'Column DoDelete already exists in Storage'",
"ALTER TABLE `Storage` ADD `DoDelete` BOOLEAN NOT NULL default true AFTER `ServerId`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Events'
AND column_name = 'Locked'
) > 0,
"SELECT 'Column Locked already exists in Events'",
"ALTER TABLE `Events` ADD `Locked` BOOLEAN NOT NULL default false AFTER `Scheme`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

12
db/zm_update-1.31.42.sql Normal file
View File

@ -0,0 +1,12 @@
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Monitors'
AND column_name = 'SignalCheckPoints'
) > 0,
"SELECT 'Column SignalCheckPoints already exists in Storage'",
"ALTER TABLE `Monitors` ADD `SignalCheckPoints` INT UNSIGNED NOT NULL default '0' AFTER `DefaultScale`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

12
db/zm_update-1.31.44.sql Normal file
View File

@ -0,0 +1,12 @@
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Monitor_Status'
AND column_name = 'CaptureBandwidth'
) > 0,
"SELECT 'Column CaptureBandwidth already exists in Monitor_Status'",
"ALTER TABLE `Monitor_Status` ADD `CaptureBandwidth` INT NOT NULL default 0 AFTER `AnalysisFPS`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

@ -14,24 +14,18 @@ if((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx"))
endif((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx")) endif((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx"))
# Configure the zoneminder service files # Configure the zoneminder service files
if(ZM_TARGET_DISTRO STREQUAL "el6") configure_file(systemd/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY)
configure_file(sysvinit/zoneminder.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.sysvinit @ONLY) if(ZM_WEB_USER STREQUAL "nginx")
configure_file(sysvinit/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY) configure_file(nginx/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
configure_file(nginx/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
configure_file(nginx/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
configure_file(nginx/zoneminder.php-fpm.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.php-fpm.conf @ONLY)
configure_file(nginx/README.Fedora ${CMAKE_CURRENT_SOURCE_DIR}/readme/README.Fedora COPYONLY)
else(ZM_WEB_USER STREQUAL "nginx")
configure_file(systemd/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY) configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
else(ZM_TARGET_DISTRO STREQUAL "el6") configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
configure_file(systemd/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY) endif(ZM_WEB_USER STREQUAL "nginx")
if(ZM_WEB_USER STREQUAL "nginx")
configure_file(nginx/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
configure_file(nginx/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
configure_file(nginx/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
configure_file(nginx/zoneminder.php-fpm.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.php-fpm.conf @ONLY)
configure_file(nginx/README.Fedora ${CMAKE_CURRENT_SOURCE_DIR}/readme/README.Fedora COPYONLY)
else(ZM_WEB_USER STREQUAL "nginx")
configure_file(systemd/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY)
configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY)
configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY)
endif(ZM_WEB_USER STREQUAL "nginx")
endif(ZM_TARGET_DISTRO STREQUAL "el6")
# Unpack jscalendar & move files into position # Unpack jscalendar & move files into position
message(STATUS "Unpacking and Installing jscalendar...") message(STATUS "Unpacking and Installing jscalendar...")
@ -52,6 +46,7 @@ file(MAKE_DIRECTORY sock swap zoneminder zoneminder-upload events images temp)
install(DIRECTORY sock swap DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY sock swap DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY zoneminder DESTINATION /var/log DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder DESTINATION /var/log DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY zoneminder DESTINATION /var/run DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder DESTINATION /var/run DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY zoneminder DESTINATION /var/cache DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY zoneminder-upload DESTINATION /var/spool DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder-upload DESTINATION /var/spool DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY events images temp DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY events images temp DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
@ -61,23 +56,18 @@ install(CODE "execute_process(COMMAND ln -sf ../../../../../../var/lib/zoneminde
# Link to Cambozola # Link to Cambozola
install(CODE "execute_process(COMMAND ln -sf ../../java/cambozola.jar \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/cambozola.jar\")") install(CODE "execute_process(COMMAND ln -sf ../../java/cambozola.jar \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/cambozola.jar\")")
# Install auxiliary files required to run zoneminder on CentOS # Install auxiliary files
install(FILES misc/redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(FILES misc/redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY jscalendar-1.0/ DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/tools/jscalendar) install(DIRECTORY jscalendar-1.0/ DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/tools/jscalendar)
# Install zoneminder service files
install(FILES zoneminder.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) install(FILES zoneminder.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
install(FILES zoneminder.conf DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
if(ZM_WEB_USER STREQUAL "nginx") if(ZM_WEB_USER STREQUAL "nginx")
install(FILES zoneminder.conf DESTINATION /etc/nginx/default.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
install(FILES zoneminder.php-fpm.conf DESTINATION /etc/php-fpm.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ RENAME zoneminder.conf) install(FILES zoneminder.php-fpm.conf DESTINATION /etc/php-fpm.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ RENAME zoneminder.conf)
else(ZM_WEB_USER STREQUAL "nginx")
install(FILES zoneminder.conf DESTINATION /etc/httpd/conf.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
endif(ZM_WEB_USER STREQUAL "nginx") endif(ZM_WEB_USER STREQUAL "nginx")
if(ZM_TARGET_DISTRO STREQUAL "el6") install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
install(FILES zoneminder.sysvinit DESTINATION /etc/rc.d/init.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) install(FILES zoneminder.tmpfiles DESTINATION /usr/lib/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
else(ZM_TARGET_DISTRO STREQUAL "el6")
install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
install(FILES zoneminder.tmpfiles DESTINATION /usr/lib/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ)
endif(ZM_TARGET_DISTRO STREQUAL "el6")

View File

@ -9,6 +9,23 @@ RewriteEngine On
RewriteCond %{HTTPS} !=on RewriteCond %{HTTPS} !=on
RewriteRule ^/?(zm)(.*) https://%{SERVER_NAME}/$1$2 [R,L] RewriteRule ^/?(zm)(.*) https://%{SERVER_NAME}/$1$2 [R,L]
# Order matters. This alias must come first.
Alias /zm/cache "@ZM_CACHEDIR@"
<Directory "@ZM_CACHEDIR@">
SSLRequireSSL
Options -Indexes +MultiViews +FollowSymLinks
AllowOverride None
<IfModule mod_authz_core.c>
# Apache 2.4
Require all granted
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order deny,allow
Allow from all
</IfModule>
</Directory>
Alias /zm "@ZM_WEBDIR@" Alias /zm "@ZM_WEBDIR@"
<Directory "@ZM_WEBDIR@"> <Directory "@ZM_WEBDIR@">
# explicitly set index.php as the only directoryindex # explicitly set index.php as the only directoryindex

View File

@ -1,37 +1,19 @@
What's New What's New
========== ==========
1. ZoneMinder now uses a conf.d subfolder to process custom changes to 1. See the ZoneMinder release notes for a list of new features:
variables found in zm.conf. Changes to zm.conf will be overwritten https://github.com/ZoneMinder/zoneminder/releases
during an upgrade. Instead, create a file with a ".conf" extension under
the conf.d folder and make your changes there.
2. ZoneMinder now supports recording directly to video container! This feature
is new and should be treated as experimental. Refer to the documentation
regarding how to use this feature.
3. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to
"/cgi-bin-zm/zms". This has been to done to avoid this bug:
https://bugzilla.redhat.com/show_bug.cgi?id=973067
IMPORTANT: You must manually inspect the value for PATH_ZMS under Options
and verify it is set to "/cgi-bin-zm/nph-zms". Failure to do so will result
in a broken system. You have been warned.
4. This package uses the HTTPS protocol by default to access the web portal.
Requests using HTTP will auto-redirect to HTTPS. See README.https for
more information.
New installs New installs
============ ============
1. Unless you are already using MariaDB server, you need to ensure that the 1. Unless you are already using MariaDB server, you need to ensure that the
server is configured to start during boot and properly secured by running: server is configured to start during boot and properly secured by running:
sudo dnf install mariadb-server sudo dnf install mariadb-server
sudo systemctl enable mariadb sudo systemctl enable mariadb
sudo systemctl start mariadb.service sudo systemctl start mariadb.service
mysql_secure_installation mysql_secure_installation
2. Assuming the database is local and using the password for the root account 2. Assuming the database is local and using the password for the root account
set during the previous step, you will need to create the ZoneMinder set during the previous step, you will need to create the ZoneMinder
@ -50,13 +32,13 @@ New installs
/etc/zm/conf.d and set your credentials there. For example, create the file /etc/zm/conf.d and set your credentials there. For example, create the file
/etc/zm/conf.d/zm-db-user.conf and add the following content to it: /etc/zm/conf.d/zm-db-user.conf and add the following content to it:
ZM_DB_USER = {username of the sql account you want to use} ZM_DB_USER = {username of the sql account you want to use}
ZM_DB_PASS = {password of the sql account you want to use} ZM_DB_PASS = {password of the sql account you want to use}
Once the file has been saved, set proper file & ownership permissions on it: Once the file has been saved, set proper file & ownership permissions on it:
sudo chown root:apache *.conf sudo chown root:apache *.conf
sudo chmod 640 *.conf sudo chmod 640 *.conf
4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local 4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
timezone. PHP will complain loudly if this is not set, or if it is set timezone. PHP will complain loudly if this is not set, or if it is set
@ -82,34 +64,62 @@ New installs
SELINUX line from "enforcing" to "disabled". This change will take SELINUX line from "enforcing" to "disabled". This change will take
effect after a reboot. effect after a reboot.
6. Install mod_ssl or configure /etc/httpd/conf.d/zoneminder.conf to meet your 6. Configure the web server
needs. This package comes preconfigured for HTTPS using the default self
signed certificate on your system. The recommended way to complete this step
is to simply install mod_ssl:
sudo dnf install mod_ssl This package uses the HTTPS protocol by default to access the web portal,
using rhe default self signed certificate on your system. Requests using
HTTP will auto-redirect to HTTPS.
If this does not meet your needs, then read README.https to Inspect the web server configuration file and verify it meets your needs:
learn about alternatives. When in doubt, install mod_ssl.
/etc/zm/www/zoneminder.conf
If you are running other web enabled services then you may need to edit
this file to suite. See README.https to learn about other alternatives.
When in doubt, proceed with the default:
sudo ln -s /etc/zm/www/zoneminder.conf /etc/httpd/conf.d/
sudo dnf install mod_ssl
7. Now start the web server: 7. Now start the web server:
sudo systemctl enable httpd sudo systemctl enable httpd
sudo systemctl start httpd sudo systemctl start httpd
8. Now start zoneminder: 8. Now start zoneminder:
sudo systemctl enable zoneminder sudo systemctl enable zoneminder
sudo systemctl start zoneminder sudo systemctl start zoneminder
9. The Fedora repos have a ZoneMinder package available, but it does not 9. Optionally configure the firewall
support ffmpeg or libvlc, which many modern IP cameras require. Most users
will want to prevent the ZoneMinder package in the Fedora repos from All Redhat distros ship with the firewall enabled. That means you will not
overwriting the ZoneMinder package in zmrepo, during a future dnf update. To be able to access the ZoneMinder web console from a remote machine until
prevent that from happening you must edit /etc/yum.repos.d/fedora.repo changes are made to the firewall.
and /etc/yum.repos.d/fedora-updates.repo. Add the line "exclude=zoneminder*"
without the quotes under the [fedora] and [fedora-updates] blocks, What follows are a set of minimal commands to allow remote access to the
respectively. ZoneMinder web console and also allow ZoneMinder's ONVIF discovery to
work. The following commands do not put any restrictions on which remote
machine(s) have access to the listed ports or services.
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --permanent --zone=public --add-port=3702/udp
sudo firewall-cmd --reload
Additional changes to the firewall may be required, depending on your
security requirements and how you use the system. It is up to you to verify
these commands are sufficient.
10. Access the ZoneMinder web console
You may now access the ZoneMinder web console from your web browser using
an appropriate url. Here are some examples:
http://localhost/zm (works from the local machine only)
http://{machine name}/zm (works only if dns is configured for your network)
http://{ip address}/zm
Upgrades Upgrades
======== ========
@ -131,7 +141,7 @@ Upgrades
See step 2 of the Installation section to add missing permissions. See step 2 of the Installation section to add missing permissions.
3. Verify the ZoneMinder Apache configuration file in the folder 3. Verify the ZoneMinder Apache configuration file in the folder
/etc/httpd/conf.d. You will have a file called "zoneminder.conf" and there /etc/zm/www. You will have a file called "zoneminder.conf" and there
may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file
exists, inspect it and merge anything new in that file with zoneminder.conf. exists, inspect it and merge anything new in that file with zoneminder.conf.
Verify the SSL REquirements meet your needs. Read README.https if necessary. Verify the SSL REquirements meet your needs. Read README.https if necessary.

View File

@ -1,161 +0,0 @@
What's New
==========
1. ***EOL NOTICE***
It has become increasingly difficult to maintain the ZoneMinder project such
that it remains compatible with EL6 distros. The version of php shipped with
EL6 distros and the version of ffmpeg which will build against EL6 are too
old. It is with regret that I must announce our plans to stop supporting
ZoneMinder on EL6 distros soon. Your best option is to upgrade to an EL7
distro or another distro with newer php & ffmpeg packages. Please note that
replacing core packages, such as php, will not be supported by us. You are
on your own should you choose to go down that path.
2. ZoneMinder now uses a conf.d subfolder to process custom changes to
variables found in zm.conf. Changes to zm.conf will be overwritten
during an upgrade. Instead, create a file with a ".conf" extension under
th2 conf.d folder and make your changes there.
3. ZoneMinder now supports recording directly to video container! This feature
is new and should be treated as experimental. Refer to the documentation
regarding how to use this feature.
4. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to
"/cgi-bin-zm/zms". This has been to done match the configuration of
CentOS7/Fedora and simplify the build process.
IMPORTANT: You must manually verify the value of PATH_ZMS under Options.
Make sure it is set to "/cgi-bin-zm/nph-zms". Failure to do so will result
in a broken system. You have been warned.
5. This package uses the HTTPS protocol by default to access the web portal.
Requests using HTTP will auto-redirect to HTTPS. See README.https for
more information.
6. The php package that ships with CentOS 6 does not support the new ZoneMinder
API. If you require API functionality (such as using a mobile app) then you
should consider an upgrade to CentOS 7 or use Fedora.
New installs
============
1. Unless you are already using MySQL server, you need to ensure that
the server is confired to start during boot and properly secured
by running:
sudo yum install mysql-server
sudo service mysqld start
/usr/bin/mysql_secure_installation
sudo chkconfig mysqld on
2. Using the password for the root account set during the previous step, you
will need to create the ZoneMinder database and configure a database
account for ZoneMinder to use:
mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql
mysql -uroot -p -e "grant all on zm.* to \
'zmuser'@localhost identified by 'zmpass';"
mysqladmin -uroot -p reload
The database account credentials, zmuser/zmpass, are arbitrary. Set them to
anything that suits your environment.
3. If you have chosen to change the zoneminder database account credentials to
something other than zmuser/zmpass, you must now create a config file under
/etc/zm/conf.d and set your credentials there. For example, create the file
/etc/zm/conf.d/zm-db-user.conf and add the following content to it:
ZM_DB_USER = {username of the sql account you want to use}
ZM_DB_PASS = {password of the sql account you want to use}
Once the file has been saved, set proper file & ownership permissions on it:
sudo chown root:apache *.conf
sudo chmod 640 *.conf
4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
timezone. PHP will complain loudly if this is not set, or if it is set
incorrectly, and these complaints will show up in the zoneminder logging
system as errors
If you are not sure of the proper timezone specification to use, look at
http://php.net/date.timezone
5. Install mod_ssl or configure /etc/httpd/conf.d/zoneminder.conf to meet your
needs. This package comes preconfigured for HTTPS using the default self
signed certificate on your system. The recommended way to complete this step
is to simply install mod_ssl:
sudo yum install mod_ssl
If this does not meet your needs, then read README.https to
learn about alternatives. When in doubt, install mod_ssl.
6. Configure the web server to start automatically:
sudo chkconfig httpd on
sudo service httpd start
7. This package will automatically configure and install an SELinux policy
called local_zoneminder. A copy of this policy is in the documentation
folder.
It is still possible to run into SELinux issues, however. If this is case,
you can disable SELinux permanently by editing the following:
/etc/selinux/conf
Change SELINUX line from "enforcing" to "disabled". This change will not
take effect until a reboot, however. To avoid a reboot, execute the
following from the commandline:
sudo setenforce 0
8. Finally, you may start the ZoneMinder service:
sudo service zoneminder start
Then point your web browser to http://<machine name or ip>/zm
Upgrades
========
1. Conf.d folder support has been added to ZoneMinder 1.31.0. Any custom
changes previously made to zm.conf must now be made in one or more custom
config files, created under the conf.d folder. Do this now. See
/etc/zm/conf.d/README for details. Once you recreate any custom config changes
under the conf.d folder, they will remain in place indefinitely.
2. Verify permissions of the zmuser account.
Over time, the database account permissions required for normal operation
have increased. Verify the zmuser database account has been granted all
permission to the ZoneMinder database:
mysql -uroot -p -e "show grants for zmuser@localhost;"
See step 2 of the Installation section to add missing permissions.
3. Verify the ZoneMinder Apache configuration file in the folder
/etc/httpd/conf.d. You will have a file called "zoneminder.conf" and there
may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file
exists, inspect it and merge anything new in that file with zoneminder.conf.
Verify the SSL REquirements meet your needs. Read README.https if necessary.
4. Upgrade the database before starting ZoneMinder.
Most upgrades can be performed by executing the following command:
sudo zmupdate.pl
Recent versions of ZoneMinder don't require any parameters added to the
zmupdate command. However, if ZoneMinder complains, you may need to call
zmupdate in the following manner:
sudo zmupdate.pl --user=root --pass=<mysql_root_pwd> --version=<from version>
5. Now restart the web server then start zoneminder:
sudo service httpd restart
sudo service zoneminder start

View File

@ -1,26 +1,8 @@
What's New What's New
========== ==========
1. ZoneMinder now uses a conf.d subfolder to process custom changes to 1. See the ZoneMinder release notes for a list of new features:
variables found in zm.conf. Changes to zm.conf will be overwritten https://github.com/ZoneMinder/zoneminder/releases
during an upgrade. Instead, create a file with a ".conf" extension under
the conf.d folder and make your changes there.
2. ZoneMinder now supports recording directly to video container! This feature
is new and should be treated as experimental. Refer to the documentation
regarding how to use this feature.
3. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to
"/cgi-bin-zm/zms". This has been to done to avoid this bug:
https://bugzilla.redhat.com/show_bug.cgi?id=973067
IMPORTANT: You must manually inspect the value for PATH_ZMS under Options
and verify it is set to "/cgi-bin-zm/nph-zms". Failure to do so will result
in a broken system. You have been warned.
4. This package uses the HTTPS protocol by default to access the web portal.
Requests using HTTP will auto-redirect to HTTPS. See README.https for
more information.
New installs New installs
============ ============
@ -28,10 +10,10 @@ New installs
1. Unless you are already using MariaDB server, you need to ensure that the 1. Unless you are already using MariaDB server, you need to ensure that the
server is configured to start during boot and properly secured by running: server is configured to start during boot and properly secured by running:
sudo yum install mariadb-server sudo yum install mariadb-server
sudo systemctl enable mariadb sudo systemctl enable mariadb
sudo systemctl start mariadb.service sudo systemctl start mariadb.service
mysql_secure_installation mysql_secure_installation
2. Using the password for the root account set during the previous step, you 2. Using the password for the root account set during the previous step, you
will need to create the ZoneMinder database and configure a database will need to create the ZoneMinder database and configure a database
@ -50,13 +32,13 @@ New installs
/etc/zm/conf.d and set your credentials there. For example, create the file /etc/zm/conf.d and set your credentials there. For example, create the file
/etc/zm/conf.d/zm-db-user.conf and add the following content to it: /etc/zm/conf.d/zm-db-user.conf and add the following content to it:
ZM_DB_USER = {username of the sql account you want to use} ZM_DB_USER = {username of the sql account you want to use}
ZM_DB_PASS = {password of the sql account you want to use} ZM_DB_PASS = {password of the sql account you want to use}
Once the file has been saved, set proper file & ownership permissions on it: Once the file has been saved, set proper file & ownership permissions on it:
sudo chown root:apache *.conf sudo chown root:apache *.conf
sudo chmod 640 *.conf sudo chmod 640 *.conf
4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local 4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local
timezone. PHP will complain loudly if this is not set, or if it is set timezone. PHP will complain loudly if this is not set, or if it is set
@ -82,25 +64,62 @@ New installs
SELINUX line from "enforcing" to "disabled". This change will take SELINUX line from "enforcing" to "disabled". This change will take
effect after a reboot. effect after a reboot.
6. Install mod_ssl or configure /etc/httpd/conf.d/zoneminder.conf to meet your 6. Configure the web server
needs. This package comes preconfigured for HTTPS using the default self
signed certificate on your system. The recommended way to complete this step
is to simply install mod_ssl:
sudo yum install mod_ssl This package uses the HTTPS protocol by default to access the web portal,
using rhe default self signed certificate on your system. Requests using
HTTP will auto-redirect to HTTPS.
If this does not meet your needs, then read README.https to Inspect the web server configuration file and verify it meets your needs:
learn about alternatives. When in doubt, install mod_ssl.
/etc/zm/www/zoneminder.conf
If you are running other web enabled services then you may need to edit
this file to suite. See README.https to learn about other alternatives.
When in doubt, proceed with the default:
sudo ln -s /etc/zm/www/zoneminder.conf /etc/httpd/conf.d/
sudo dnf install mod_ssl
7. Now start the web server: 7. Now start the web server:
sudo systemctl enable httpd sudo systemctl enable httpd
sudo systemctl start httpd sudo systemctl start httpd
8. Now start zoneminder: 8. Now start zoneminder:
sudo systemctl enable zoneminder sudo systemctl enable zoneminder
sudo systemctl start zoneminder sudo systemctl start zoneminder
9. Optionally configure the firewall
All Redhat distros ship with the firewall enabled. That means you will not
be able to access the ZoneMinder web console from a remote machine until
changes are made to the firewall.
What follows are a set of minimal commands to allow remote access to the
ZoneMinder web console and also allow ZoneMinder's ONVIF discovery to
work. The following commands do not put any restrictions on which remote
machine(s) have access to the listed ports or services.
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --permanent --zone=public --add-port=3702/udp
sudo firewall-cmd --reload
Additional changes to the firewall may be required, depending on your
security requirements and how you use the system. It is up to you to verify
these commands are sufficient.
10. Access the ZoneMinder web console
You may now access the ZoneMinder web console from your web browser using
an appropriate url. Here are some examples:
http://localhost/zm (works from the local machine only)
http://{machine name}/zm (works only if dns is configured for your network)
http://{ip address}/zm
Upgrades Upgrades
======== ========
@ -122,7 +141,7 @@ Upgrades
See step 2 of the Installation section to add missing permissions. See step 2 of the Installation section to add missing permissions.
3. Verify the ZoneMinder Apache configuration file in the folder 3. Verify the ZoneMinder Apache configuration file in the folder
/etc/httpd/conf.d. You will have a file called "zoneminder.conf" and there /etc/zm/www. You will have a file called "zoneminder.conf" and there
may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file
exists, inspect it and merge anything new in that file with zoneminder.conf. exists, inspect it and merge anything new in that file with zoneminder.conf.
Verify the SSL REquirements meet your needs. Read README.https if necessary. Verify the SSL REquirements meet your needs. Read README.https if necessary.

View File

@ -1,29 +1,26 @@
HTTPS is now a requirement HTTPS is now the default
========================== ========================
This package now depends on Apache's mod_ssl package. This will automatically By default, ZoneMinder will use the certifciate created when the mod_ssl
be installed along with ZoneMinder. Upon installation, the mod_ssl package pacakge was installed on your system.
will create a default, self-signed certificate. This is the certificate that
ZoneMinder will use out of the box.
Since the certificate is self-signed, you will get a warning from your browser Since the certificate is self-signed, you will get a warning from your browser
the first time you access the web portal. This is normal. the first time you access the web portal. This is normal.
This is not intended to be an all encompasing solution for everyone. ZoneMinder This is not intended to be an all encompasing solution for everyone. ZoneMinder
will work just fine over HTTPS the way it is currently configured. However, will work just fine over HTTPS the way it is currently configured. However,
here are a couple of considerations you may want to take. here are a couple of considerations you may want to take to improve your
experience.
1. Create your own certificate. The CentOS wiki has a guide that describes how 1. Install a fully signed certificate from letsencrypt, using certbot. See the
certbot site for more information. This free service is very easy to set up.
https://certbot.eff.org/all-instructions/
2. Create your own certificate. The CentOS wiki has a guide that describes how
to do this: https://wiki.centos.org/HowTos/Https . Additionally, Googling to do this: https://wiki.centos.org/HowTos/Https . Additionally, Googling
"centos certificate" reveals many articles on the subject. Note that some "centos certificate" reveals many articles on the subject.
third party applications, such as zmNinja, will require you to create a
certificate different than the default certificate on your machine.
2. You can turn off HTTPS entirely by simply commenting out the SSLRequireSSL 3. You can turn off HTTPS entirely by simply commenting out the SSLRequireSSL
directives found in /etc/httpd/conf.d/zoneminder.conf. You should also directives found in /etc/httpd/conf.d/zoneminder.conf. You should also
comment out the HTTP -> HTTPS Rewrite rule. comment out the HTTP -> HTTPS Rewrite rule.
3. Install a fully signed certificate from letsencrypt. See the Letsencrypt
site for more information. https://letsencrypt.org/
This service is totally free!

View File

@ -1,2 +1,5 @@
D @ZM_TMPDIR@ 0755 @WEB_USER@ @WEB_GROUP@ D @ZM_TMPDIR@ 0755 @WEB_USER@ @WEB_GROUP@
D @ZM_SOCKDIR@ 0755 @WEB_USER@ @WEB_GROUP@ D @ZM_SOCKDIR@ 0755 @WEB_USER@ @WEB_GROUP@
D @ZM_CACHEDIR@ 0755 @WEB_USER@ @WEB_GROUP@
D @ZM_DIR_EVENTS@ 0755 @WEB_USER@ @WEB_GROUP@
D @ZM_DIR_IMAGES@ 0755 @WEB_USER@ @WEB_GROUP@

View File

@ -1,121 +0,0 @@
#!/bin/sh
# description: ZoneMinder is the top Linux video camera security and surveillance solution. ZoneMinder is intended for use in single or multi-camera video security applications.Copyright: Philip Coombes, Corey DeLasaux 2003-2008
# chkconfig: - 99 00
# processname: zmpkg.pl
# Source function library.
. /etc/rc.d/init.d/functions
prog=ZoneMinder
ZM_CONFIG="@ZM_CONFIG@"
pidfile="@ZM_RUNDIR@"
LOCKFILE=/var/lock/subsys/zm
loadconf()
{
if [ -f $ZM_CONFIG ]; then
. $ZM_CONFIG
else
echo "ERROR: $ZM_CONFIG not found."
return 1
fi
}
loadconf
command="$ZM_PATH_BIN/zmpkg.pl"
start()
{
# Commenting out as it is not needed. Leaving as a placeholder for future use.
# zmupdate || return $?
loadconf || return $?
#Make sure the directory for our PID folder exists or create one.
[ ! -d $pidfile ] \
&& mkdir -m 774 $pidfile \
&& chown $ZM_WEB_USER:$ZM_WEB_GROUP $pidfile
#Make sure the folder for the socks file exists or create one
GetPath="select Value from Config where Name='ZM_PATH_SOCKS'"
dbHost=`echo $ZM_DB_HOST | cut -d: -f1`
dbPort=`echo $ZM_DB_HOST | cut -d: -s -f2`
if [ "$dbPort" = "" ]
then
ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$ZM_DB_HOST -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'`
else
ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$dbHost -P$dbPort -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'`
fi
[ ! -d $ZM_PATH_SOCK ] \
&& mkdir -m 774 $ZM_PATH_SOCK \
&& chown $ZM_WEB_USER:$ZM_WEB_GROUP $ZM_PATH_SOCK
echo -n $"Starting $prog: "
$command start
RETVAL=$?
[ $RETVAL = 0 ] && success || failure
echo
[ $RETVAL = 0 ] && touch $LOCKFILE
return $RETVAL
}
stop()
{
loadconf
echo -n $"Stopping $prog: "
$command stop
RETVAL=$?
[ $RETVAL = 0 ] && success || failure
echo
[ $RETVAL = 0 ] && rm -f $LOCKFILE
}
zmstatus()
{
loadconf
result=`$command status`
if [ "$result" = "running" ]; then
echo "ZoneMinder is running"
$ZM_PATH_BIN/zmu -l
RETVAL=0
else
echo "ZoneMinder is stopped"
RETVAL=1
fi
}
zmupdate()
{
if [ -x $ZM_PATH_BIN/zmupdate.pl ]; then
$ZM_PATH_BIN/zmupdate.pl -f
fi
}
case "$1" in
'start')
start
;;
'stop')
stop
;;
'restart')
stop
start
;;
'condrestart')
loadconf
result=`$ZM_PATH_BIN/zmdc.pl check`
if [ "$result" = "running" ]; then
$ZM_PATH_BIN/zmdc.pl shutdown > /dev/null
rm -f $LOCKFILE
start
fi
;;
'status')
status httpd
status mysqld
zmstatus
;;
*)
echo "Usage: $0 { start | stop | restart | condrestart | status }"
RETVAL=1
;;
esac
exit $RETVAL

View File

@ -1,7 +0,0 @@
@ZM_LOGDIR@/*log
{
weekly
notifempty
missingok
create 660 @WEB_USER@ @WEB_GROUP@
}

View File

@ -2,13 +2,13 @@
%global zmgid_final apache %global zmgid_final apache
# Crud is configured as a git submodule # Crud is configured as a git submodule
%global crud_version 3.0.10 %global crud_version 3.1.0-zm
# CakePHP-Enum-Behavior is configured as a git submodule
%global ceb_version 1.0-zm
%if "%{zmuid_final}" == "nginx" %if "%{zmuid_final}" == "nginx"
%global with_nginx 1 %global with_nginx 1
%global wwwconfdir %{_sysconfdir}/nginx/default.d
%else
%global wwwconfdir %{_sysconfdir}/httpd/conf.d
%endif %endif
%global sslcert %{_sysconfdir}/pki/tls/certs/localhost.crt %global sslcert %{_sysconfdir}/pki/tls/certs/localhost.crt
@ -22,18 +22,11 @@
%global with_apcu_bc 1 %global with_apcu_bc 1
%endif %endif
# Include files for SysV init or systemd
%if 0%{?fedora} >= 15 || 0%{?rhel} >= 7
%global with_init_systemd 1
%else
%global with_init_sysv 1
%endif
%global readme_suffix %{?rhel:Redhat%{?rhel}}%{!?rhel:Fedora} %global readme_suffix %{?rhel:Redhat%{?rhel}}%{!?rhel:Fedora}
%global _hardened_build 1 %global _hardened_build 1
Name: zoneminder Name: zoneminder
Version: 1.31.1 Version: 1.31.42
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
@ -41,18 +34,18 @@ Group: System Environment/Daemons
# Mootools is inder the MIT license: http://mootools.net/ # Mootools is inder the MIT license: http://mootools.net/
# CakePHP is under the MIT license: https://github.com/cakephp/cakephp # CakePHP is under the MIT license: https://github.com/cakephp/cakephp
# Crud is under the MIT license: https://github.com/FriendsOfCake/crud # Crud is under the MIT license: https://github.com/FriendsOfCake/crud
# CakePHP-Enum-Behavior is under the MIT license: https://github.com/asper/CakePHP-Enum-Behavior
License: GPLv2+ and LGPLv2+ and MIT License: GPLv2+ and LGPLv2+ and MIT
URL: http://www.zoneminder.com/ URL: http://www.zoneminder.com/
Source0: https://github.com/ZoneMinder/ZoneMinder/archive/%{version}.tar.gz#/zoneminder-%{version}.tar.gz Source0: https://github.com/ZoneMinder/ZoneMinder/archive/%{version}.tar.gz#/zoneminder-%{version}.tar.gz
Source1: https://github.com/FriendsOfCake/crud/archive/v%{crud_version}.tar.gz#/crud-%{crud_version}.tar.gz Source1: https://github.com/ZoneMinder/crud/archive/v%{crud_version}.tar.gz#/crud-%{crud_version}.tar.gz
Source2: https://github.com/ZoneMinder/CakePHP-Enum-Behavior/archive/%{ceb_version}.tar.gz#/cakephp-enum-behavior-%{ceb_version}.tar.gz
%{?with_init_systemd:BuildRequires: systemd-devel} BuildRequires: systemd-devel
%{?with_init_systemd:BuildRequires: mariadb-devel} BuildRequires: mariadb-devel
%{?with_init_systemd:BuildRequires: perl-podlators} BuildRequires: perl-podlators
%{?with_init_systemd:BuildRequires: polkit-devel} BuildRequires: polkit-devel
%{?with_init_sysv:BuildRequires: mysql-devel}
%{?el6:BuildRequires: epel-rpm-macros}
BuildRequires: cmake >= 2.8.7 BuildRequires: cmake >= 2.8.7
BuildRequires: gnutls-devel BuildRequires: gnutls-devel
BuildRequires: bzip2-devel BuildRequires: bzip2-devel
@ -82,6 +75,11 @@ BuildRequires: vlc-devel
BuildRequires: libcurl-devel BuildRequires: libcurl-devel
BuildRequires: libv4l-devel BuildRequires: libv4l-devel
BuildRequires: ffmpeg-devel BuildRequires: ffmpeg-devel
BuildRequires: desktop-file-utils
# Required for mp4 container support
BuildRequires: libmp4v2-devel
BuildRequires: x264-devel
%{?with_nginx:Requires: nginx} %{?with_nginx:Requires: nginx}
%{?with_nginx:Requires: fcgiwrap} %{?with_nginx:Requires: fcgiwrap}
@ -112,19 +110,10 @@ Requires: perl(LWP::Protocol::https)
Requires: ca-certificates Requires: ca-certificates
Requires: zip Requires: zip
%{?with_init_systemd:Requires(post): systemd} Requires(post): systemd
%{?with_init_systemd:Requires(post): systemd-sysv} Requires(post): systemd-sysv
%{?with_init_systemd:Requires(preun): systemd} Requires(preun): systemd
%{?with_init_systemd:Requires(postun): systemd} Requires(postun): systemd
%{?with_init_sysv:Requires(post): /sbin/chkconfig}
%{?with_init_sysv:Requires(post): %{_bindir}/checkmodule}
%{?with_init_sysv:Requires(post): %{_bindir}/semodule_package}
%{?with_init_sysv:Requires(post): %{_sbindir}/semodule}
%{?with_init_sysv:Requires(preun): /sbin/chkconfig}
%{?with_init_sysv:Requires(preun): /sbin/service}
%{?with_init_sysv:Requires(preun): %{_sbindir}/semodule}
%{?with_init_sysv:Requires(postun): /sbin/service}
Requires(post): %{_bindir}/gpasswd Requires(post): %{_bindir}/gpasswd
Requires(post): %{_bindir}/less Requires(post): %{_bindir}/less
@ -143,6 +132,11 @@ too much degradation of performance.
%{__rm} -rf ./web/api/app/Plugin/Crud %{__rm} -rf ./web/api/app/Plugin/Crud
%{__mv} -f crud-%{crud_version} ./web/api/app/Plugin/Crud %{__mv} -f crud-%{crud_version} ./web/api/app/Plugin/Crud
# The all powerful autosetup macro does not work after the second source tarball
%{__gzip} -dc %{_sourcedir}/cakephp-enum-behavior-%{ceb_version}.tar.gz | tar -xvvf -
%{__rm} -rf ./web/api/app/Plugin/CakePHP-Enum-Behavior
%{__mv} -f CakePHP-Enum-Behavior-%{ceb_version} ./web/api/app/Plugin/CakePHP-Enum-Behavior
# Change the following default values # Change the following default values
./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes ./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes
./utils/zmeditconfigdata.sh ZM_UPLOAD_FTP_LOC_DIR %{_localstatedir}/spool/zoneminder-upload ./utils/zmeditconfigdata.sh ZM_UPLOAD_FTP_LOC_DIR %{_localstatedir}/spool/zoneminder-upload
@ -163,6 +157,12 @@ too much degradation of performance.
%install %install
%make_install %make_install
desktop-file-install \
--dir %{buildroot}%{_datadir}/applications \
--delete-original \
--mode 644 \
%{buildroot}%{_datadir}/applications/zoneminder.desktop
# Remove unwanted files and folders # Remove unwanted files and folders
find %{buildroot} \( -name .htaccess -or -name .editorconfig -or -name .packlist -or -name .git -or -name .gitignore -or -name .gitattributes -or -name .travis.yml \) -type f -delete > /dev/null 2>&1 || : find %{buildroot} \( -name .htaccess -or -name .editorconfig -or -name .packlist -or -name .git -or -name .gitignore -or -name .gitattributes -or -name .travis.yml \) -type f -delete > /dev/null 2>&1 || :
@ -174,24 +174,10 @@ find %{buildroot}%{_datadir}/zoneminder/www/api \( -name cake -or -name cake.php
%{__ln_s} ../../../../../../../..%{_sysconfdir}/pki/tls/certs/ca-bundle.crt %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem %{__ln_s} ../../../../../../../..%{_sysconfdir}/pki/tls/certs/ca-bundle.crt %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem
%post %post
%if 0%{?with_init_sysv}
/sbin/chkconfig --add zoneminder
/sbin/chkconfig zoneminder on
# Create and load zoneminder selinux policy module
echo -e "\nCreating and installing a ZoneMinder SELinux policy module. Please wait.\n"
%{_bindir}/checkmodule -M -m -o %{_docdir}/%{name}-%{version}/local_zoneminder.mod %{_docdir}/%{name}-%{version}/local_zoneminder.te > /dev/null 2>&1 || :
%{_bindir}/semodule_package -o %{_docdir}/%{name}-%{version}/local_zoneminder.pp -m %{_docdir}/%{name}-%{version}/local_zoneminder.mod > /dev/null 2>&1 || :
%{_sbindir}/semodule -i %{_docdir}/%{name}-%{version}/local_zoneminder.pp > /dev/null 2>&1 || :
%endif
%if 0%{?with_init_systemd}
# Initial installation # Initial installation
if [ $1 -eq 1 ] ; then if [ $1 -eq 1 ] ; then
%systemd_post %{name}.service %systemd_post %{name}.service
fi fi
%endif
# Upgrade from a previous version of zoneminder # Upgrade from a previous version of zoneminder
if [ $1 -eq 2 ] ; then if [ $1 -eq 2 ] ; then
@ -245,34 +231,11 @@ EOF
%endif %endif
%preun %preun
%if 0%{?with_init_sysv}
if [ $1 -eq 0 ]; then
/sbin/service zoneminder stop > /dev/null 2>&1 || :
/sbin/chkconfig --del zoneminder
echo -e "\nRemoving ZoneMinder SELinux policy module. Please wait.\n"
%{_sbindir}/semodule -r local_zoneminder.pp
fi
%endif
%if 0%{?with_init_systemd}
%systemd_preun %{name}.service %systemd_preun %{name}.service
%endif
%postun %postun
%if 0%{?with_init_sysv}
if [ $1 -ge 1 ]; then
/sbin/service zoneminder condrestart > /dev/null 2>&1 || :
fi
# Remove the doc folder.
rm -rf %{_docdir}/%{name}-%{version}
%endif
%if 0%{?with_init_systemd}
%systemd_postun_with_restart %{name}.service %systemd_postun_with_restart %{name}.service
%endif
%if 0%{?with_init_systemd}
%triggerun -- zoneminder < 1.25.0-4 %triggerun -- zoneminder < 1.25.0-4
# Save the current service runlevel info # Save the current service runlevel info
# User must manually run systemd-sysv-convert --apply zoneminder # User must manually run systemd-sysv-convert --apply zoneminder
@ -282,7 +245,6 @@ rm -rf %{_docdir}/%{name}-%{version}
# Run these because the SysV package being removed won't do them # Run these because the SysV package being removed won't do them
/sbin/chkconfig --del zoneminder >/dev/null 2>&1 || : /sbin/chkconfig --del zoneminder >/dev/null 2>&1 || :
/bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || : /bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || :
%endif
%files %files
%license COPYING %license COPYING
@ -300,25 +262,18 @@ rm -rf %{_docdir}/%{name}-%{version}
%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/*.conf %config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/*.conf
%ghost %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/zmcustom.conf %ghost %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/zmcustom.conf
%config(noreplace) %attr(644,root,root) %{wwwconfdir}/zoneminder.conf %config(noreplace) %attr(644,root,root) /etc/zm/www/zoneminder.conf
%config(noreplace) %{_sysconfdir}/logrotate.d/zoneminder %config(noreplace) %{_sysconfdir}/logrotate.d/zoneminder
%if 0%{?with_nginx} %if 0%{?with_nginx}
%config(noreplace) %{_sysconfdir}/php-fpm.d/zoneminder.conf %config(noreplace) %{_sysconfdir}/php-fpm.d/zoneminder.conf
%endif %endif
%if 0%{?with_init_systemd}
%{_tmpfilesdir}/zoneminder.conf %{_tmpfilesdir}/zoneminder.conf
%{_unitdir}/zoneminder.service %{_unitdir}/zoneminder.service
%{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy %{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy
%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules %{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules
%{_bindir}/zmsystemctl.pl %{_bindir}/zmsystemctl.pl
%endif
%if 0%{?with_init_sysv}
%doc distros/redhat/misc/local_zoneminder.te
%attr(755,root,root) %{_initrddir}/zoneminder
%endif
%{_bindir}/zma %{_bindir}/zma
%{_bindir}/zmaudit.pl %{_bindir}/zmaudit.pl
@ -337,6 +292,7 @@ rm -rf %{_docdir}/%{name}-%{version}
%{_bindir}/zmtelemetry.pl %{_bindir}/zmtelemetry.pl
%{_bindir}/zmx10.pl %{_bindir}/zmx10.pl
%{_bindir}/zmonvif-probe.pl %{_bindir}/zmonvif-probe.pl
%{_bindir}/zmstats.pl
%{perl_vendorlib}/ZoneMinder* %{perl_vendorlib}/ZoneMinder*
%{perl_vendorlib}/ONVIF* %{perl_vendorlib}/ONVIF*
@ -347,6 +303,7 @@ rm -rf %{_docdir}/%{name}-%{version}
%{_libexecdir}/zoneminder/ %{_libexecdir}/zoneminder/
%{_datadir}/zoneminder/ %{_datadir}/zoneminder/
%{_datadir}/applications/*%{name}.desktop
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/events %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/events
@ -354,11 +311,18 @@ rm -rf %{_docdir}/%{name}-%{version}
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/sock %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/sock
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/swap %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/swap
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/temp %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/temp
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/cache/zoneminder
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/log/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/log/zoneminder
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/spool/zoneminder-upload %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/spool/zoneminder-upload
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/run/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/run/zoneminder
%changelog %changelog
* Sun Apr 22 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.31.42-1
- Remove support for sysvinit a.k.a. el6
- use desktop-file-install for new zoneminder.desktop file
- add new web cache folder
- 1.31.42 development snapshot
* Tue May 09 2017 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.30.4-1 * Tue May 09 2017 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.30.4-1
- modify autosetup macro parameters - modify autosetup macro parameters
- modify requirements for php-pecl-acpu-bc package - modify requirements for php-pecl-acpu-bc package

View File

@ -6,6 +6,12 @@ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin"
Require all granted Require all granted
</Directory> </Directory>
# Order matters. This Alias must come first
Alias /zm/cache /var/cache/zoneminder/cache
<Directory /var/cache/zoneminder/cache>
Options -Indexes +FollowSymLinks
</Directory>
Alias /zm /usr/share/zoneminder/www Alias /zm /usr/share/zoneminder/www
<Directory /usr/share/zoneminder/www> <Directory /usr/share/zoneminder/www>
php_flag register_globals off php_flag register_globals off
@ -15,6 +21,28 @@ Alias /zm /usr/share/zoneminder/www
</IfModule> </IfModule>
</Directory> </Directory>
<Directory /usr/share/zoneminder/www/api> # For better visibility, the following directives have been migrated from the
AllowOverride All # default .htaccess files included with the CakePHP project.
# Parameters not set here are inherited from the parent directive above.
<Directory "/usr/share/zoneminder/www/api">
RewriteEngine on
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
RewriteBase /zm/api
</Directory> </Directory>
<Directory "/usr/share/zoneminder/www/api/app">
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "/usr/share/zoneminder/www/api/app/webroot">
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
RewriteBase /zm/api
</Directory>

View File

@ -54,6 +54,8 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,liburi-encode-perl ,liburi-encode-perl
,libwww-perl ,libwww-perl
,libdata-uuid-perl ,libdata-uuid-perl
,libnumber-bytes-human
,libfile-slurp-perl
,mysql-client | virtual-mysql-client ,mysql-client | virtual-mysql-client
,perl-modules ,perl-modules
,php5-mysql, php5-gd, php5-apcu, php-apc ,php5-mysql, php5-gd, php5-apcu, php-apc

View File

@ -25,6 +25,7 @@ override_dh_auto_configure:
-DZM_SOCKDIR="/var/run/zm" \ -DZM_SOCKDIR="/var/run/zm" \
-DZM_TMPDIR="/tmp/zm" \ -DZM_TMPDIR="/tmp/zm" \
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
-DZM_CACHEDIR="/var/cache/zoneminder/cache" \
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
-DZM_DIR_IMAGES="/var/cache/zoneminder/images" \ -DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" \ -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" \

View File

@ -3,6 +3,7 @@ var/lib/zm
var/cache/zoneminder/events var/cache/zoneminder/events
var/cache/zoneminder/images var/cache/zoneminder/images
var/cache/zoneminder/temp var/cache/zoneminder/temp
var/cache/zoneminder/cache
usr/share/zoneminder/db usr/share/zoneminder/db
etc/zm etc/zm
etc/zm/conf.d etc/zm/conf.d

View File

@ -23,8 +23,7 @@ configuration file:
Upgrading database Upgrading database
------------------ ------------------
Prior to 1.28.1 database upgrade was performed automatically. The database is updated automatically on installation. You should not need to take this step.
"zoneminder" service will refuse to start with outdated database.
Assuming that database is on "localhost" then the following command can be Assuming that database is on "localhost" then the following command can be
used to upgrade "zm" database: used to upgrade "zm" database:
@ -45,17 +44,11 @@ The following command prints the current version of zoneminder database:
Enabling service Enabling service
---------------- ----------------
By default Zoneminder service is not starting automatically and need to be By default Zoneminder service is not automatically started and needs to be
manually activated once database is configured: manually enabled once database is configured:
On systemd:
sudo systemctl enable zoneminder.service sudo systemctl enable zoneminder.service
On SysV:
sudo update-rc.d zoneminder enable
Web server set-up Web server set-up
----------------- -----------------
@ -82,10 +75,10 @@ Common configuration steps for Apache2:
## nginx / fcgiwrap ## nginx / fcgiwrap
Nginx needs "php5-fpm" package to support PHP and "fcgiwrap" package Nginx needs "php-fpm" package to support PHP and "fcgiwrap" package
for binary "cgi-bin" applications: for binary "cgi-bin" applications:
sudo apt-get install php5-fpm fcgiwrap sudo apt-get install php-fpm fcgiwrap
To enable a URL alias that makes Zoneminder available from To enable a URL alias that makes Zoneminder available from
@ -119,32 +112,9 @@ site configuration.
Changing the location for images and events Changing the location for images and events
------------------------------------------- -------------------------------------------
Zoneminder, in its upstream form, stores data in /usr/share/zoneminder/. This ZoneMinder is now able to be configured to use an alternative location for storing
package modifies that by changing /usr/share/zoneminder/images and events and images at compile time. This package makes use of that, so symlinks in
/usr/share/zoneminder/events to symlinks to directories under /usr/share/zoneminder/www are no longer necessary.
/var/cache/zoneminder.
There are numerous places these could be put and ways to do it. But, at the
moment, if you change this, an upgrade will fail with a warning about these
locations having changed (the reason for this was that previously, an upgrade
would silently revert the changes and cause event loss - refer
bug #608793).
If you do want to change the location, here are a couple of suggestions.
(thanks to vagrant@freegeek.org):
These lines in fstab could allow you to bind-mount an alternate location
/dev/sdX1 /otherdrive ext3 defaults 0 2
/otherdrive/zoneminder/images /var/cache/zoneminder/images bind defaults 0 2
/otherdrive/zoneminder/events /var/cache/zoneminder/events bind defaults 0 2
or if you have a separate partition for each:
/dev/sdX1 /var/cache/zoneminder/images ext3 defaults 0 2
/dev/sdX2 /var/cache/zoneminder/events ext3 defaults 0 2
-- Peter Howard <pjh@northern-ridge.com.au>, Sun, 16 Jan 2010 01:35:51 +1100
Access to /dev/video* Access to /dev/video*
--------------------- ---------------------

View File

@ -1,7 +1,3 @@
zoneminder (1.31.4-vivid1) vivid; urgency=medium zoneminder (1.31.39~20180223.27-stretch-1) unstable; urgency=low
*
* Release 1.31.4 -- Isaac Connor <iconnor@connortechnology.com> Fri, 23 Feb 2018 14:15:59 -0500
-- Isaac Connor <iconnor@tesla.com> Thu, 21 Sep 2017 09:55:31 -0700

View File

@ -6,6 +6,12 @@ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin"
Require all granted Require all granted
</Directory> </Directory>
# Order matters. This alias must come first.
Alias /zm/cache /var/cache/zoneminder/cache
<Directory /var/cache/zoneminder/cache>
Options -Indexes +FollowSymLinks
</Directory>
Alias /zm /usr/share/zoneminder/www Alias /zm /usr/share/zoneminder/www
<Directory /usr/share/zoneminder/www> <Directory /usr/share/zoneminder/www>
Options -Indexes +FollowSymLinks Options -Indexes +FollowSymLinks
@ -14,6 +20,27 @@ Alias /zm /usr/share/zoneminder/www
</IfModule> </IfModule>
</Directory> </Directory>
<Directory /usr/share/zoneminder/www/api> # For better visibility, the following directives have been migrated from the
AllowOverride All # default .htaccess files included with the CakePHP project.
# Parameters not set here are inherited from the parent directive above.
<Directory "/usr/share/zoneminder/www/api">
RewriteEngine on
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "/usr/share/zoneminder/www/api/app">
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "/usr/share/zoneminder/www/api/app/webroot">
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
RewriteBase /zm/api
</Directory> </Directory>

View File

@ -16,7 +16,7 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa
,libcurl4-gnutls-dev ,libcurl4-gnutls-dev
,libgnutls-openssl-dev ,libgnutls-openssl-dev
,libjpeg8-dev | libjpeg9-dev | libjpeg62-turbo-dev ,libjpeg8-dev | libjpeg9-dev | libjpeg62-turbo-dev
,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev ,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat
,libpcre3-dev ,libpcre3-dev
,libpolkit-gobject-1-dev ,libpolkit-gobject-1-dev
,libv4l-dev (>= 0.8.3) [!hurd-any] ,libv4l-dev (>= 0.8.3) [!hurd-any]
@ -39,7 +39,7 @@ Package: zoneminder
Architecture: any Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,javascript-common ,javascript-common
,libmp4v2-2, libx264-142|libx264-148, libswscale-ffmpeg3|libswscale4|libswscale3 ,libmp4v2-2, libx264-142|libx264-148|libx264-152, libswscale-ffmpeg3|libswscale4|libswscale3
,ffmpeg | libav-tools ,ffmpeg | libav-tools
,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl ,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl
,libdbd-mysql-perl ,libdbd-mysql-perl
@ -59,9 +59,11 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libsoap-wsdl-perl ,libsoap-wsdl-perl
,libio-socket-multicast-perl ,libio-socket-multicast-perl
,libdigest-sha-perl ,libdigest-sha-perl
,libsys-cpu-perl, libsys-cpuload-perl, libsys-meminfo-perl ,libsys-cpu-perl, libsys-meminfo-perl
,libdata-uuid-perl ,libdata-uuid-perl
,mysql-client | virtual-mysql-client ,libnumber-bytes-human-perl
,libfile-slurp-perl
,mysql-client | mariadb-client | virtual-mysql-client
,perl-modules ,perl-modules
,php5-mysql | php-mysql, php5-gd | php-gd , php5-apcu | php-apcu , php-apc | php-apcu-bc ,php5-mysql | php-mysql, php5-gd | php-gd , php5-apcu | php-apcu , php-apc | php-apcu-bc
,policykit-1 ,policykit-1
@ -70,7 +72,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libpcre3 ,libpcre3
Recommends: ${misc:Recommends} Recommends: ${misc:Recommends}
,libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm ,libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm
,mysql-server | virtual-mysql-server ,mysql-server | mariadb-server | virtual-mysql-server
,zoneminder-doc (>= ${source:Version}) ,zoneminder-doc (>= ${source:Version})
,ffmpeg ,ffmpeg
Suggests: fcgiwrap, logrotate Suggests: fcgiwrap, logrotate

View File

@ -25,6 +25,7 @@ override_dh_auto_configure:
-DZM_SOCKDIR="/var/run/zm" \ -DZM_SOCKDIR="/var/run/zm" \
-DZM_TMPDIR="/tmp/zm" \ -DZM_TMPDIR="/tmp/zm" \
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
-DZM_CACHEDIR="/var/cache/zoneminder/cache" \
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
-DZM_DIR_IMAGES="/var/cache/zoneminder/images" \ -DZM_DIR_IMAGES="/var/cache/zoneminder/images" \
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms"
@ -66,9 +67,6 @@ override_dh_fixperms:
chown root:www-data $(CURDIR)/debian/zoneminder/etc/zm/zm.conf chown root:www-data $(CURDIR)/debian/zoneminder/etc/zm/zm.conf
chmod 640 $(CURDIR)/debian/zoneminder/etc/zm/zm.conf chmod 640 $(CURDIR)/debian/zoneminder/etc/zm/zm.conf
override_dh_installinit:
dh_installinit --no-start
override_dh_systemd_start: override_dh_systemd_start:
dh_systemd_start --no-start dh_systemd_start --no-start

View File

@ -3,7 +3,7 @@ var/lib/zm
var/cache/zoneminder/events var/cache/zoneminder/events
var/cache/zoneminder/images var/cache/zoneminder/images
var/cache/zoneminder/temp var/cache/zoneminder/temp
var/cache/zoneminder/cache
usr/share/zoneminder/db usr/share/zoneminder/db
usr/share/zoneminder/www/cache
etc/zm/ etc/zm/
etc/zm/conf.d etc/zm/conf.d

View File

@ -1,91 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: zoneminder
# Required-Start: $network $remote_fs $syslog
# Required-Stop: $network $remote_fs $syslog
# Should-Start: mysql
# Should-Stop: mysql
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Control ZoneMinder as a Service
# Description: ZoneMinder CCTV recording and surveillance system
### END INIT INFO
# chkconfig: 2345 20 20
# Source function library.
. /lib/lsb/init-functions
prog=ZoneMinder
ZM_PATH_BIN="/usr/bin"
RUNDIR="/var/run/zm"
TMPDIR="/tmp/zm"
command="$ZM_PATH_BIN/zmpkg.pl"
start() {
echo -n "Starting $prog: "
export TZ=:/etc/localtime
mkdir -p "$RUNDIR" && chown www-data:www-data "$RUNDIR"
mkdir -p "$TMPDIR" && chown www-data:www-data "$TMPDIR"
$command start
RETVAL=$?
[ $RETVAL = 0 ] && echo success
[ $RETVAL != 0 ] && echo failure
echo
[ $RETVAL = 0 ] && touch /var/lock/zm
return $RETVAL
}
stop() {
echo -n "Stopping $prog: "
#
# Why is this status check being done?
# as $command stop returns 1 if zoneminder
# is stopped, which will result in
# this returning 1, which will stuff
# dpkg when it tries to stop zoneminder before
# uninstalling . . .
#
result=`$command status`
if [ ! "$result" = "running" ]; then
echo "Zoneminder already stopped"
echo
RETVAL=0
else
$command stop
RETVAL=$?
[ $RETVAL = 0 ] && echo success
[ $RETVAL != 0 ] && echo failure
echo
[ $RETVAL = 0 ] && rm -f /var/lock/zm
fi
}
status() {
result=`$command status`
if [ "$result" = "running" ]; then
echo "ZoneMinder is running"
RETVAL=0
else
echo "ZoneMinder is stopped"
RETVAL=1
fi
}
case "$1" in
'start')
start
;;
'stop')
stop
;;
'restart' | 'force-reload')
stop
start
;;
'status')
status
;;
*)
echo "Usage: $0 { start | stop | restart | status }"
RETVAL=1
;;
esac
exit $RETVAL

View File

@ -1,43 +1,65 @@
#! /bin/sh #! /bin/sh
set -e set +e
if [ "$1" = "configure" ]; then if [ "$1" = "configure" ]; then
. /etc/zm/zm.conf . /etc/zm/zm.conf
for i in /etc/zm/conf.d/*.conf; do for CONFFILE in /etc/zm/conf.d/*.conf; do
. $i . "$CONFFILE"
done; done
# The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group # The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group
chown www-data:root /var/log/zm chown www-data:root /var/log/zm
chown www-data:www-data /var/lib/zm chown www-data:www-data /var/lib/zm
if [ -z "$2" ]; then if [ -z "$2" ]; then
chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/* /usr/share/zoneminder/www/cache chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/*
fi fi
if [ ! -e "/etc/apache2/mods-enabled/cgi.load" ]; then if [ ! -e "/etc/apache2/mods-enabled/cgi.load" ] && [ "$(command -v a2enmod)" != "" ]; then
echo "The cgi module is not enabled in apache2. I am enabling it using a2enmod cgi." echo "The cgi module is not enabled in apache2. I am enabling it using a2enmod cgi."
a2enmod cgi a2enmod cgi
fi fi
if [ "$ZM_DB_HOST" = "localhost" ]; then if [ "$ZM_DB_HOST" = "localhost" ]; then
if [ -e "/etc/init.d/mysql" ]; then if [ -e "/lib/systemd/system/mysql.service" ] || [ -e "/lib/systemd/system/mariadb.service" ]; then
# Do this every time the package is installed or upgraded
# Ensure zoneminder is stopped # Ensure zoneminder is stopped
deb-systemd-invoke stop zoneminder.service || exit $? deb-systemd-invoke stop zoneminder.service || exit $?
# #
# Get mysql started if it isn't # Get mysql started if it isn't running
# #
if $(/etc/init.d/mysql status >/dev/null 2>&1); then
if [ -e "/lib/systemd/system/mariadb.service" ]; then
DBSERVICE="mariadb.service"
else
DBSERVICE="mysql.service"
fi
echo "Detected db service is $DBSERVICE"
if systemctl is-failed --quiet $DBSERVICE; then
echo "$DBSERVICE is in a failed state; it will not be started."
echo "If you have already resolved the problem preventing $DBSERVICE from running,"
echo "run sudo systemctl restart $DBSERVICE then run sudo dpkg-reconfigure zoneminder."
exit 1
fi
if ! systemctl is-active --quiet mysql.service mariadb.service; then
# Due to /etc/init.d service autogeneration, mysql.service always returns the status of mariadb.service
# However, mariadb.service will not return the status of mysql.service.
deb-systemd-invoke start $DBSERVICE
fi
# Make sure systemctl status exit code is 0; i.e. the DB is running
if systemctl is-active --quiet "$DBSERVICE"; then
mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload
# 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"
cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf
# This creates the user. # This creates the user.
echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
else else
echo "Updating permissions"
echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql echo "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
fi fi
@ -46,19 +68,18 @@ if [ "$1" = "configure" ]; then
# Add any new PTZ control configurations to the database (will not overwrite) # Add any new PTZ control configurations to the database (will not overwrite)
zmcamtool.pl --import >/dev/null 2>&1 zmcamtool.pl --import >/dev/null 2>&1
else else
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.'
fi fi
else else
echo 'mysql not found, assuming remote server.' echo 'MySQL/MariaDB not found; assuming remote server.'
fi fi
else else
echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)" echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)."
fi fi
echo "Done Updating, starting ZoneMinder" echo "Done Updating; starting ZoneMinder."
deb-systemd-invoke restart zoneminder.service || exit $? deb-systemd-invoke restart zoneminder.service
fi fi

View File

@ -1,4 +1,4 @@
d /var/run/zm 0755 www-data www-data d /var/run/zm 0755 www-data www-data
d /tmp/zm 0755 www-data www-data d /tmp/zm 0755 www-data www-data
d /var/tmp/zm 0755 www-data www-data d /var/tmp/zm 0755 www-data www-data
d /usr/share/zoneminder/www/cache 0755 www-data www-data d /var/cache/zoneminder/cache 0755 www-data www-data

View File

@ -98,15 +98,15 @@ This command will add a new http monitor.
:: ::
curl -XPOST http://server/zm/api/monitors.json -d "Monitor[Name]=Cliff-Burton \ curl -XPOST http://server/zm/api/monitors.json -d "Monitor[Name]=Cliff-Burton\
&Monitor[Function]=Modect \ &Monitor[Function]=Modect\
&Monitor[Protocol]=http \ &Monitor[Protocol]=http\
&Monitor[Method]=simple \ &Monitor[Method]=simple\
&Monitor[Host]=usr:pass@192.168.11.20 \ &Monitor[Host]=usr:pass@192.168.11.20\
&Monitor[Port]=80 \ &Monitor[Port]=80\
&Monitor[Path]=/mjpg/video.mjpg \ &Monitor[Path]=/mjpg/video.mjpg\
&Monitor[Width]=704 \ &Monitor[Width]=704\
&Monitor[Height]=480 \ &Monitor[Height]=480\
&Monitor[Colours]=4" &Monitor[Colours]=4"
Edit monitor 1 Edit monitor 1
@ -304,26 +304,26 @@ Create a Zone
:: ::
curl -XPOST http://server/zm/api/zones.json -d "Zone[Name]=Jason-Newsted \ curl -XPOST http://server/zm/api/zones.json -d "Zone[Name]=Jason-Newsted\
&Zone[MonitorId]=3 \ &Zone[MonitorId]=3\
&Zone[Type]=Active \ &Zone[Type]=Active\
&Zone[Units]=Percent \ &Zone[Units]=Percent\
&Zone[NumCoords]=4 \ &Zone[NumCoords]=4\
&Zone[Coords]=0,0 639,0 639,479 0,479 \ &Zone[Coords]=0,0 639,0 639,479 0,479\
&Zone[AlarmRGB]=16711680 \ &Zone[AlarmRGB]=16711680\
&Zone[CheckMethod]=Blobs \ &Zone[CheckMethod]=Blobs\
&Zone[MinPixelThreshold]=25 \ &Zone[MinPixelThreshold]=25\
&Zone[MaxPixelThreshold]= \ &Zone[MaxPixelThreshold]=\
&Zone[MinAlarmPixels]=9216 \ &Zone[MinAlarmPixels]=9216\
&Zone[MaxAlarmPixels]= \ &Zone[MaxAlarmPixels]=\
&Zone[FilterX]=3 \ &Zone[FilterX]=3\
&Zone[FilterY]=3 \ &Zone[FilterY]=3\
&Zone[MinFilterPixels]=9216 \ &Zone[MinFilterPixels]=9216\
&Zone[MaxFilterPixels]=230400 \ &Zone[MaxFilterPixels]=230400\
&Zone[MinBlobPixels]=6144 \ &Zone[MinBlobPixels]=6144\
&Zone[MaxBlobPixels]= \ &Zone[MaxBlobPixels]=\
&Zone[MinBlobs]=1 \ &Zone[MinBlobs]=1\
&Zone[MaxBlobs]= \ &Zone[MaxBlobs]=\
&Zone[OverloadFrames]=0" &Zone[OverloadFrames]=0"
PTZ Control APIs PTZ Control APIs

View File

@ -71,22 +71,22 @@ The 1.2 at the start is basically adding 20% on top of the calculation to accoun
The math breakdown for 4 cameras running at 1280x960 capture, 50 frame buffer, 24 bit color space: The math breakdown for 4 cameras running at 1280x960 capture, 50 frame buffer, 24 bit color space:
:: ::
1280*960 = 1,228,800 (bits) 1280*960 = 1,228,800 (bytes)
1,228,800 * 24 = 2,359,296,000 (bits) 1,228,800 * (3 bytes for 24 bit) = 3,686,400 (bytes)
2,359,296,000 * 50 = 5,898,240,000 (bits) 3,686,400 * 50 = 184,320,000 (bytes)
5,898,240,000 * 4 = 7,077,888,000 (bits) 184,320,000 * 4 = 737,280,000 (bytes)
7,077,888,000 / 8 = 884,736,000 (bytes) 737,280,000 / 1024 = 720,000 (Kilobytes)
884,736,000 / 1000 = 884,736 (Kilobytes) 720,000 / 1024 = 703.125 (Megabytes)
884,736 / 1000 = 864 (Megabytes) 703.125 / 1024 = 0.686 (Gigabytes)
864 / 1000 = 0.9 (Gigabyte)
Around 900MB of memory. Around 700MB of memory.
So if you have 2GB of memory, you should be all set. Right? **Not, really**: So if you have 2GB of memory, you should be all set. Right? **Not, really**:
* This is just the base memory required to capture the streams. Remember ZM is always capturing streams irrespective of whether you are actually recording or not - to make sure its image ring buffer is there with pre images when an alarm kicks in. * This is just the base memory required to capture the streams. Remember ZM is always capturing streams irrespective of whether you are actually recording or not - to make sure its image ring buffer is there with pre images when an alarm kicks in.
* You also need to account for other processes not related to ZM running in your box * You also need to account for other processes not related to ZM running in your box
* You also need to account for other ZM processes - for example, I noticed the audit daemon takes up a good amount of memory when it runs, DB updates also take up memory * You also need to account for other ZM processes - for example, I noticed the audit daemon takes up a good amount of memory when it runs, DB updates also take up memory
* If you are using H264 encoding, that buffers a lot of frames in memory as well.
So a good rule of thumb is to make sure you have twice the memory as the calculation above (and if you are using the ZM server for other purposes, please factor in those memory requirements as well) So a good rule of thumb is to make sure you have twice the memory as the calculation above (and if you are using the ZM server for other purposes, please factor in those memory requirements as well)
@ -128,15 +128,14 @@ So, for example:
:: ::
384x288 capture resolution, that makes: 110 592 pixels 384x288 capture resolution, that makes: 110 592 pixels
in 24 bit color that's x24 = 2 654 208 bits per frame in 24 bit color that's x 3 = 331,776 bytes per frame
by 80 frames ring buffer x80 = 212 336 640 bits per camera by 80 frames ring buffer x80 = 26,542,080 bytes per camera
by 4 cameras x4 = 849 346 560 bits. by 4 cameras x4 = 106,168,320 bytes.
Plus 10% overhead = 934 281 216 bits Plus 10% overhead = 116,785,152 bytes
That's 116 785 152 bytes, and Thats 114,048 kB, respectively 111.38 MB.
= 114 048 kB, respectively 111.38 MB. If my shared memory is set to 134,217,728, which is exactly 128MB,
If my shared memory is set to 134 217 728, which is exactly 128MB,
that means I shouldn't have any problem. that means I shouldn't have any problem.
(Note that 1 byte = 8 bits and 1kbyte = 1024bytes, 1MB = 1024 kB) (Note that 1kbyte = 1024bytes, 1MB = 1024 kB)
If for instance you were using 24bit 640x480 then this would come to about 92Mb if you are using the default buffer size of 100. If this is too large then you can either reduce the image or buffer sizes or increase the maximum amount of shared memory available. If you are using RedHat then you can get details on how to change these settings `here <http://www.redhat.com/docs/manuals/database/RHDB-2.1-Manual/admin_user/kernel-resources.html>`__ If for instance you were using 24bit 640x480 then this would come to about 92Mb if you are using the default buffer size of 100. If this is too large then you can either reduce the image or buffer sizes or increase the maximum amount of shared memory available. If you are using RedHat then you can get details on how to change these settings `here <http://www.redhat.com/docs/manuals/database/RHDB-2.1-Manual/admin_user/kernel-resources.html>`__

View File

@ -0,0 +1,39 @@
Dedicated Drive, Partition, or Network Share
============================================
One of the first steps the end user must perform after installing ZoneMinder is to dedicate an entire partition, drive, or network share for ZoneMinder's event storage.
The reason being, ZoneMinder will, by design, fill up your hard disk, and you don't want to do that to your root volume!
The following steps apply to ZoneMinder 1.31 or newer, running on a typical Linux system, which uses systemd.
If you are using an older version of ZoneMinder, please follow the legacy steps in the `ZoneMinder Wiki <https://wiki.zoneminder.com/Using_a_dedicated_Hard_Drive>`_.
**Step 1:** Stop ZoneMinder
::
sudo systemctl stop zoneminder
**Step 2:** Mount your dedicated drive, partition, or network share to the local filesystem in any folder of your choosing.
We recommend you use systemd to manage the mount points.
Instructions on how to accomplish this can be found `here <https://zoneminder.blogspot.com/p/blog-page.html>`_ and `here <https://wiki.zoneminder.com/Common_Issues_with_Zoneminder_Installation_on_Ubuntu#Use_Systemd_to_Mount_Internal_Drive_or_NAS>`_.
Note that bind mounting ZoneMinder's images folder is optional. Newer version of ZoneMinder write very little, if anything, to the images folder.
Verify the dedicated drive, partition, or network share is successfully mounted before proceeding to the next step.
**Step 3:** Set the owner and group to that of the web server user account. Debian based distros typically use "www-data" as the web server user account while many rpm based distros use "apache".
::
sudo chown -R apache:apache /path/to/your/zoneminder/events/folder
sudo chown -R apache:apache /path/to/your/zoneminder/images/folder
Recall from the previous step, the images folder is optional.
**Step 4:** Create a config file under /etc/zm/conf.d using your favorite text editor. Name the file anything you want just as long as it ends in ".conf".
Add the following content to the file and save your changes:
::
ZM_DIR_EVENTS=/full/path/to/the/events/folder
ZM_DIR_IMAGES=/full/path/to/the/images/folder
**Step 5:** Start ZoneMinder and inspect the ZoneMinder log files for errors.
::
sudo systemctl start zoneminder

View File

@ -11,3 +11,4 @@ Contents:
debian debian
redhat redhat
multiserver multiserver
dedicateddrive

View File

@ -1,4 +1,4 @@
All Distros - A Simpler Way to Build ZoneMinder All Distros - A Docker Way to Build ZoneMinder
=============================================== ===============================================
.. contents:: .. contents::
@ -27,6 +27,8 @@ Procedure
- If the desired distro does not appear in either list, then unfortuantely you cannot use the procedure described here. - If the desired distro does not appear in either list, then unfortuantely you cannot use the procedure described here.
- If the desired distro architecture is arm, refer to `Appendix A - Enable Qemu On the Host`_ to enable qemu emulation on your amd64 host machine.
**Step 2:** Install Docker. **Step 2:** Install Docker.
You need to have a working installation of Docker so head over to the `Docker site <https://docs.docker.com/engine/installation/>`_ and get it working. Before continuing to the next step, verify you can run the Docker "Hello World" container as a normal user. To run a Docker container as a normal user, issue the following: You need to have a working installation of Docker so head over to the `Docker site <https://docs.docker.com/engine/installation/>`_ and get it working. Before continuing to the next step, verify you can run the Docker "Hello World" container as a normal user. To run a Docker container as a normal user, issue the following:
@ -99,7 +101,27 @@ For advanced users who really want to go out into uncharted waters, it is theore
Building arm packages in this manner has not been tested by us, however. Building arm packages in this manner has not been tested by us, however.
Appendix A - Enable Qemu On the Host
------------------------------------
If you intend to build ZoneMinder packages for arm on an amd64 host, then Debian users can following these steps to enable transparent Qemu emulation:
::
sudo apt-get install binfmt-support qemu qemu-user-static
Verify arm emulation is enabled by issuing:
::
sudo update-binfmts --enable qemu-arm
You may get a message stating emulation for this processor is already enabled.
More testing needs to be done for Redhat distros but it appears Fedora users can just run:
::
sudo systemctl start systemd-binfmt
TO-DO: Verify the details behind enabling qemu emulation on redhat distros. Pull requests are welcome.

View File

@ -45,8 +45,6 @@ The following notes are based on real problems which have occurred by those who
How to Install ZoneMinder How to Install ZoneMinder
------------------------- -------------------------
These instructions apply to all redhat distros and compatible clones, except for RHEL/CentOS 6.
ZoneMinder releases are now being hosted at RPM Fusion. New users should navigate the `RPM Fusion site <https://rpmfusion.org>`_ then follow the instructions to enable that repo. RHEL/CentOS users must also navaigate to the `EPEL Site <https://fedoraproject.org/wiki/EPEL>`_ and enable that repo as well. Once enabled, install ZoneMinder from the commandline: ZoneMinder releases are now being hosted at RPM Fusion. New users should navigate the `RPM Fusion site <https://rpmfusion.org>`_ then follow the instructions to enable that repo. RHEL/CentOS users must also navaigate to the `EPEL Site <https://fedoraproject.org/wiki/EPEL>`_ and enable that repo as well. Once enabled, install ZoneMinder from the commandline:
:: ::
@ -57,13 +55,6 @@ Note that RHEL/CentOS 7 users should use yum instead of dnf.
Once ZoneMinder has been installed, it is critically important that you read the README file under /usr/share/doc/zoneminder. ZoneMinder will not run without completing the steps outlined in the README. Once ZoneMinder has been installed, it is critically important that you read the README file under /usr/share/doc/zoneminder. ZoneMinder will not run without completing the steps outlined in the README.
How to Install ZoneMinder on RHEL/CentOS 6
------------------------------------------
We continue to encounter build problems, caused by the age of this distro. It is unforuntate, but we can see the writing on the wall. We do not have a date set, but the end of the line for this distros is near.
Please be advised that we do not recommend any new ZoneMinder installations using CentOS 6. However, for the time being, ZoneMinder rpms will continue to be hosted at `zmrepo <https://www.zoneminder.com>`_.
How to Install Nightly Development Builds How to Install Nightly Development Builds
----------------------------------------- -----------------------------------------

View File

@ -8,6 +8,23 @@
ServerAdmin webmaster@localhost ServerAdmin webmaster@localhost
DocumentRoot "@WEB_PREFIX@" DocumentRoot "@WEB_PREFIX@"
# Order matters. This alias must come first.
Alias /zm/cache "@ZM_CACHEDIR@"
<Directory "@ZM_CACHEDIR@">
Options -Indexes +FollowSymLinks
AllowOverride None
<IfModule mod_authz_core.c>
# Apache 2.4
Require all granted
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order deny,allow
Allow from all
</IfModule>
</Directory>
Alias /zm "@WEB_PREFIX@" Alias /zm "@WEB_PREFIX@"
<Directory "@WEB_PREFIX@"> <Directory "@WEB_PREFIX@">
Options -Indexes +FollowSymLinks Options -Indexes +FollowSymLinks
@ -38,6 +55,31 @@
</IfModule> </IfModule>
</Directory> </Directory>
# For better visibility, the following directives have been migrated from the
# default .htaccess files included with the CakePHP project.
# Parameters not set here are inherited from the parent directive above.
<Directory "@ZM_WEBDIR@/api">
RewriteEngine on
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "@ZM_WEBDIR@/api/app">
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "@ZM_WEBDIR@/api/app/webroot">
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
RewriteBase /zm/api
</Directory>
# Use the first option to have Apache logs written to the general log # Use the first option to have Apache logs written to the general log
# directory, or the second to have them written to the regular Apache # directory, or the second to have them written to the regular Apache
# directory (you may have to change the path to that used on your system) # directory (you may have to change the path to that used on your system)

View File

@ -5,4 +5,3 @@ Name=ZoneMinder
Comment= Comment=
Icon=@PKGDATADIR@/icons/16x16/icon.xpm Icon=@PKGDATADIR@/icons/16x16/icon.xpm
URL=http://localhost/zm/\r URL=http://localhost/zm/\r
Categories=GNOME;AudioVideo;Video;Recorder;

View File

@ -400,7 +400,22 @@ our @options = (
type => $types{boolean}, type => $types{boolean},
category => 'system', category => 'system',
}, },
# PP - Google reCaptcha settings {
name => 'ZM_OPT_USE_EVENTNOTIFICATION',
default => 'no',
description => 'Enable 3rd party Event Notification Server',
help => q`
zmeventnotification is a 3rd party event notification server
that is used to get notifications for alarms detected by ZoneMinder
in real time. zmNinja requires this server for push notifications to
mobile phones. This option only enables the server if its already installed.
Please visit https://github.com/pliablepixels/zmeventserver for installation
instructions.
`,
type => $types{boolean},
category => 'system',
},
# Google reCaptcha settings
{ {
name => 'ZM_OPT_USE_GOOG_RECAPTCHA', name => 'ZM_OPT_USE_GOOG_RECAPTCHA',
default => 'no', default => 'no',
@ -424,7 +439,6 @@ our @options = (
}, },
{ {
name => 'ZM_OPT_GOOG_RECAPTCHA_SITEKEY', name => 'ZM_OPT_GOOG_RECAPTCHA_SITEKEY',
default => '...Insert your recaptcha site-key here...',
description => 'Your recaptcha site-key', description => 'Your recaptcha site-key',
help => q`You need to generate your keys from help => q`You need to generate your keys from
the Google reCaptcha website. the Google reCaptcha website.
@ -1709,26 +1723,6 @@ our @options = (
type => $types{abs_path}, type => $types{abs_path},
category => 'config', category => 'config',
}, },
{
name => 'ZM_SIGNAL_CHECK_POINTS',
default => '10',
description => 'How many points in a captured image to check for signal loss',
help => q`
For locally attached video cameras ZoneMinder can check for
signal loss by looking at a number of random points on each
captured image. If all of these points are set to the same
fixed colour then the camera is assumed to have lost signal.
When this happens any open events are closed and a short one
frame signal loss event is generated, as is another when the
signal returns. This option defines how many points on each
image to check. Note that this is a maximum, any points found
to not have the check colour will abort any further checks so
in most cases on a couple of points will actually be checked.
Network and file based cameras are never checked.
`,
type => $types{integer},
category => 'config',
},
{ {
name => 'ZM_V4L_MULTI_BUFFER', name => 'ZM_V4L_MULTI_BUFFER',
default => 'yes', default => 'yes',

View File

@ -47,7 +47,7 @@ sub new {
my $class = shift; my $class = shift;
my $id = shift; my $id = shift;
my $self = {}; my $self = {};
$self->{name} = "PelcoD"; $self->{name} = $class;
if ( !defined($id) ) { if ( !defined($id) ) {
Fatal( "No monitor defined when invoking protocol ".$self->{name} ); Fatal( "No monitor defined when invoking protocol ".$self->{name} );
} }
@ -83,7 +83,7 @@ sub open {
sub close { sub close {
my $self = shift; my $self = shift;
Fatal( "No close method defined for protocol ".$self->{name} ); Error( "No close method defined for protocol ".$self->{name} );
} }
sub loadMonitor { sub loadMonitor {

View File

@ -50,31 +50,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -44,31 +44,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -0,0 +1,195 @@
# ==========================================================================
#
# ZoneMinder D-Link DCS-3415 IP Control Protocol Module, 2018-03-04, 0.1
# Copyright (C) 2015-2018 Habib Kamei
#
# Modified for use with D-Link DCS-3415 IP Camera by Habib Kamei
# 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 implementation of the D-Link DCS-3415 device control protocol
#
#===========================================================================
package ZoneMinder::Control::DCS3415;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Control;
our @ISA = qw(ZoneMinder::Control);
# ==========================================================================
#
# D-Link DCS-3415 Control Protocol
#
# On ControlAddress use the format :
# USERNAME:PASSWORD@ADDRESS:PORT
# eg : admin:@10.1.2.1:80
# zoneuser:zonepass@192.168.0.20:40000
#
# ==========================================================================
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref( ) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open
{
my $self = shift;
$self->loadMonitor();
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
$self->{state} = 'open';
}
sub close
{
my $self = shift;
$self->{state} = 'closed';
}
sub printMsg
{
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
}
sub sendCmd
{
my $self = shift;
my $cmd = shift;
my $result = undef;
printMsg( $cmd, "Tx" );
my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/cgi-bin/viewer/$cmd" );
my $res = $self->{ua}->request($req);
if ( $res->is_success )
{
$result = !undef;
}
else
{
Error( "Error check failed:'".$res->status_line()."'" );
}
return( $result );
}
#Zoom In
sub Tele
{
my $self = shift;
Debug( "Zoom Tele" );
my $cmd = "camctrl.cgi?channel=0&camid=1&zoom=tele";
$self->sendCmd( $cmd );
}
#Zoom Out
sub Wide
{
my $self = shift;
Debug( "Zoom Wide" );
my $cmd = "camctrl.cgi?channel=0&camid=1&zoom=wide";
$self->sendCmd( $cmd );
}
#Focus Near
sub Near
{
my $self = shift;
Debug( "Focus Near" );
my $cmd = "camctrl.cgi?channel=0&camid=1&focus=near";
$self->sendCmd( $cmd );
}
#Focus Far
sub Far
{
my $self = shift;
Debug( "Focus Far" );
my $cmd = "camctrl.cgi?channel=0&camid=1&focus=far";
$self->sendCmd( $cmd );
}
1;
__END__
# Below is stub documentation for your module. You'd better edit it!
=head1 NAME
ZoneMinder::Database - Perl extension for blah blah blah
=head1 SYNOPSIS
use ZoneMinder::Database;
blah blah blah
=head1 DESCRIPTION
Stub documentation for ZoneMinder, created by h2xs. It looks like the
author of the extension was negligent enough to leave the stub
unedited.
Blah blah blah.
=head2 EXPORT
None by default.
=head1 SEE ALSO
Mention other useful documentation such as the documentation of
related modules or operating system documentation (such as man pages
in UNIX), or any relevant external documentation such as RFCs or
standards.
If you have a mailing list set up for your module, mention it here.
If you have a web site set up for your module, mention it here.
=head1 AUTHOR
Philip Coombes, E<lt>philip.coombes@zoneminder.comE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2001-2008 Philip Coombes
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

@ -79,31 +79,6 @@ use Time::HiRes qw( usleep );
# this script is reload at every command ,if i want the button on/off (Focus MAN) for OSD works... # this script is reload at every command ,if i want the button on/off (Focus MAN) for OSD works...
my $osd = "on"; my $osd = "on";
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -85,31 +85,6 @@ use Time::HiRes qw( usleep );
# this script is reload at every command ,if i want the button on/off (Focus MAN) for OSD works... # this script is reload at every command ,if i want the button on/off (Focus MAN) for OSD works...
my $osd = "on"; my $osd = "on";
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -63,33 +63,8 @@ our $VERSION = $ZoneMinder::Base::VERSION;
use ZoneMinder::Logger qw(:all); use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all); use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
my $logindetails = "";
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
our $stop_command; our $stop_command;
sub open sub open

View File

@ -55,31 +55,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
my $logindetails = "";
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
our $stop_command; our $stop_command;
sub open sub open

View File

@ -54,28 +54,6 @@ my $ChannelID = 1; # Usually...
my $DefaultFocusSpeed = 50; # Should be between 1 and 100 my $DefaultFocusSpeed = 50; # Should be between 1 and 100
my $DefaultIrisSpeed = 50; # Should be between 1 and 100 my $DefaultIrisSpeed = 50; # Should be between 1 and 100
sub new {
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD {
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open { sub open {
my $self = shift; my $self = shift;
$self->loadMonitor(); $self->loadMonitor();

View File

@ -44,31 +44,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -82,33 +82,6 @@ use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all); use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
my $logindetails = "";
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref( ) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -54,33 +54,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
my $logindetails = "";
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref( ) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -50,29 +50,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
use URI::Encode qw(); use URI::Encode qw();
sub new {
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD {
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open { sub open {
my $self = shift; my $self = shift;

View File

@ -44,31 +44,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -44,31 +44,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -74,33 +74,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
my $logindetails = "";
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref( ) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -44,31 +44,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -46,31 +46,6 @@ use Time::HiRes qw( usleep );
use constant SYNC => 0xff; use constant SYNC => 0xff;
use constant COMMAND_GAP => 100000; # In ms use constant COMMAND_GAP => 100000; # In ms
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -47,31 +47,6 @@ use constant STX => 0xa0;
use constant ETX => 0xaf; use constant ETX => 0xaf;
use constant COMMAND_GAP => 100000; # In ms use constant COMMAND_GAP => 100000; # In ms
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -72,33 +72,6 @@ use DateTime;
my ($username,$password,$host,$port); my ($username,$password,$host,$port);
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
my $logindetails = "";
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref( ) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -60,33 +60,8 @@ our $VERSION = $ZoneMinder::Base::VERSION;
use ZoneMinder::Logger qw(:all); use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all); use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
my $logindetails = "";
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
our $stop_command; our $stop_command;
sub open sub open

View File

@ -45,28 +45,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new {
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD {
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) ) {
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open { sub open {
my $self = shift; my $self = shift;

View File

@ -81,31 +81,6 @@ our $ADDRESS = '';
use ZoneMinder::Logger qw(:all); use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all); use ZoneMinder::Config qw(:all);
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -45,31 +45,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -46,31 +46,6 @@ use Time::HiRes qw( usleep );
use constant SYNC => 0xff; use constant SYNC => 0xff;
use constant COMMAND_GAP => 100000; # In ms use constant COMMAND_GAP => 100000; # In ms
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -44,33 +44,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
Debug( "Camera New" );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
Debug( "Camera AUTOLOAD" );
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -70,32 +70,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
my $logindetails = "";
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -52,31 +52,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -44,33 +44,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
Debug( "Camera New" );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
Debug( "Camera AUTOLOAD" );
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -49,33 +49,6 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep ); use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
my $logindetails = "";
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref( ) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open sub open
{ {
my $self = shift; my $self = shift;

View File

@ -35,6 +35,9 @@ require Date::Manip;
require File::Find; require File::Find;
require File::Path; require File::Path;
require File::Copy; require File::Copy;
require File::Slurp;
require File::Basename;
require Number::Bytes::Human;
#our @ISA = qw(ZoneMinder::Object); #our @ISA = qw(ZoneMinder::Object);
use parent qw(ZoneMinder::Object); use parent qw(ZoneMinder::Object);
@ -300,8 +303,8 @@ sub GenerateVideo {
$frame_rate = $fps; $frame_rate = $fps;
} }
my $width = $self->{MonitorWidth}; my $width = $self->{Width};
my $height = $self->{MonitorHeight}; my $height = $self->{Height};
my $video_size = " ${width}x${height}"; my $video_size = " ${width}x${height}";
if ( $scale ) { if ( $scale ) {
@ -347,13 +350,17 @@ sub delete {
Warning( "Can't Delete event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime} from $caller:$line\n" ); Warning( "Can't Delete event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime} from $caller:$line\n" );
return; return;
} }
if ( ! -e $event->Storage()->Path() ) {
Warning("Not deleting event because storage path doesn't exist");
return;
}
Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime}\n" ); Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime}\n" );
$ZoneMinder::Database::dbh->ping(); $ZoneMinder::Database::dbh->ping();
$ZoneMinder::Database::dbh->begin_work(); $ZoneMinder::Database::dbh->begin_work();
$event->lock_and_load(); #$event->lock_and_load();
if ( ! $Config{ZM_OPT_FAST_DELETE} ) { {
my $sql = 'DELETE FROM Frames WHERE EventId=?'; my $sql = 'DELETE FROM Frames WHERE EventId=?';
my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql ) my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
or Error( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() ); or Error( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
@ -375,19 +382,23 @@ sub delete {
$ZoneMinder::Database::dbh->commit(); $ZoneMinder::Database::dbh->commit();
return; return;
} }
}
# Do it individually to avoid locking up the table for new events
{
my $sql = 'DELETE FROM Events WHERE Id=?';
my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
or Error( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Error( "Can't execute '$sql': ".$sth->errstr() );
$sth->finish();
}
$ZoneMinder::Database::dbh->commit();
if ( (! $Config{ZM_OPT_FAST_DELETE}) and $event->Storage()->DoDelete() ) {
$event->delete_files( ); $event->delete_files( );
} else { } else {
Debug('Not deleting frames, stats and files for speed.'); Debug('Not deleting event files from '.$event->Path().' for speed.');
} }
# Do it individually to avoid locking up the table for new events
my $sql = 'DELETE FROM Events WHERE Id=?';
my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
or Error( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Error( "Can't execute '$sql': ".$sth->errstr() );
$sth->finish();
$ZoneMinder::Database::dbh->commit();
} # end sub delete } # end sub delete
sub delete_files { sub delete_files {
@ -410,8 +421,34 @@ sub delete_files {
if ( $event_path ) { if ( $event_path ) {
( $storage_path ) = ( $storage_path =~ /^(.*)$/ ); # De-taint ( $storage_path ) = ( $storage_path =~ /^(.*)$/ ); # De-taint
( $event_path ) = ( $event_path =~ /^(.*)$/ ); # De-taint ( $event_path ) = ( $event_path =~ /^(.*)$/ ); # De-taint
my $deleted = 0;
if ( $$Storage{Type} eq 's3fs' ) {
my ( $aws_id, $aws_secret, $aws_host, $aws_bucket ) = ( $$Storage{Url} =~ /^\s*([^:]+):([^@]+)@([^\/]*)\/(.+)\s*$/ );
eval {
require Net::Amazon::S3;
my $s3 = Net::Amazon::S3->new( {
aws_access_key_id => $aws_id,
aws_secret_access_key => $aws_secret,
( $aws_host ? ( host => $aws_host ) : () ),
});
my $bucket = $s3->bucket($aws_bucket);
if ( ! $bucket ) {
Error("S3 bucket $bucket not found.");
die;
}
if ( $bucket->delete_key( $event_path ) ) {
$deleted = 1;
} else {
Error("Failed to delete from S3:".$s3->err . ": " . $s3->errstr);
}
};
Error($@) if $@;
}
if ( ! $deleted ) {
my $command = "/bin/rm -rf $storage_path/$event_path"; my $command = "/bin/rm -rf $storage_path/$event_path";
ZoneMinder::General::executeShellCommand( $command ); ZoneMinder::General::executeShellCommand( $command );
}
} }
if ( $event->Scheme() eq 'Deep' ) { if ( $event->Scheme() eq 'Deep' ) {
@ -482,6 +519,22 @@ sub DiskSpace {
sub MoveTo { sub MoveTo {
my ( $self, $NewStorage ) = @_; my ( $self, $NewStorage ) = @_;
my $OldStorage = $self->Storage(undef);
my ( $OldPath ) = ( $self->Path() =~ /^(.*)$/ ); # De-taint
if ( ! -e $OldPath ) {
return "Old path $OldPath does not exist.";
}
# First determine if we can move it to the dest.
# We do this before bothering to lock the event
my ( $NewPath ) = ( $NewStorage->Path() =~ /^(.*)$/ ); # De-taint
if ( ! $$NewStorage{Id} ) {
return "New storage does not have an id. Moving will not happen.";
} elsif ( !$NewPath ) {
return "New path ($NewPath) is empty.";
} elsif ( ! -e $NewPath ) {
return "New path $NewPath does not exist.";
}
$ZoneMinder::Database::dbh->begin_work(); $ZoneMinder::Database::dbh->begin_work();
$self->lock_and_load(); $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.
@ -490,25 +543,12 @@ sub MoveTo {
return "Event has already been moved by someone else."; return "Event has already been moved by someone else.";
} }
my $OldStorage = $self->Storage(undef); if ( $$OldStorage{Id} != $$self{StorageId} ) {
my ( $OldPath ) = ( $self->Path() =~ /^(.*)$/ ); # De-taint $ZoneMinder::Database::dbh->commit();
return "Old Storage path changed, Event has moved somewhere else.";
}
$$self{Storage} = $NewStorage; $$self{Storage} = $NewStorage;
my ( $NewPath ) = ( $NewStorage->Path() =~ /^(.*)$/ ); # De-taint
if ( ! $$NewStorage{Id} ) {
$ZoneMinder::Database::dbh->commit();
return "New storage does not have an id. Moving will not happen.";
} elsif ( !$NewPath ) {
$ZoneMinder::Database::dbh->commit();
return "New path ($NewPath) is empty.";
} elsif ( ! -e $NewPath ) {
$ZoneMinder::Database::dbh->commit();
return "New path $NewPath does not exist.";
} elsif ( ! -e $OldPath ) {
$ZoneMinder::Database::dbh->commit();
return "Old path $OldPath does not exist.";
}
( $NewPath ) = ( $self->Path(undef) =~ /^(.*)$/ ); # De-taint ( $NewPath ) = ( $self->Path(undef) =~ /^(.*)$/ ); # De-taint
if ( $NewPath eq $OldPath ) { if ( $NewPath eq $OldPath ) {
$ZoneMinder::Database::dbh->commit(); $ZoneMinder::Database::dbh->commit();
@ -516,34 +556,93 @@ sub MoveTo {
} }
Debug("Moving event $$self{Id} from $OldPath to $NewPath"); Debug("Moving event $$self{Id} from $OldPath to $NewPath");
my $moved = 0;
if ( $$NewStorage{Type} eq 's3fs' ) {
my ( $aws_id, $aws_secret, $aws_host, $aws_bucket ) = ( $$NewStorage{Url} =~ /^\s*([^:]+):([^@]+)@([^\/]*)\/(.+)\s*$/ );
eval {
require Net::Amazon::S3;
my $s3 = Net::Amazon::S3->new( {
aws_access_key_id => $aws_id,
aws_secret_access_key => $aws_secret,
( $aws_host ? ( host => $aws_host ) : () ),
});
my $bucket = $s3->bucket($aws_bucket);
if ( ! $bucket ) {
Error("S3 bucket $bucket not found.");
die;
}
my $event_path = 'events/'.$self->RelativePath();
Info("Making dir ectory $event_path/");
if ( ! $bucket->add_key( $event_path.'/','' ) ) {
die "Unable to add key for $event_path/";
}
my @files = glob("$OldPath/*");
Debug("Files to move @files");
for my $file (@files) {
next if $file =~ /^\./;
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
my $starttime = time;
Debug("Moving file $file to $NewPath");
my $size = -s $file;
if ( ! $size ) {
Info("Not moving file with 0 size");
}
my $file_contents = File::Slurp::read_file($file);
if ( ! $file_contents ) {
die "Loaded empty file, but it had a size. Giving up";
}
my $filename = $event_path.'/'.File::Basename::basename($file);
if ( ! $bucket->add_key( $filename, $file_contents ) ) {
die "Unable to add key for $filename";
}
my $duration = time - $starttime;
Debug("PUT to S3 " . Number::Bytes::Human::format_bytes($size) . " in $duration seconds = " . Number::Bytes::Human::format_bytes($size/$duration) . "/sec");
} # end foreach file.
$moved = 1;
};
Error($@) if $@;
die $@ if $@;
} # end if s3
my $error = ''; my $error = '';
File::Path::make_path( $NewPath, {error => \my $err} ); if ( ! $moved ) {
if ( @$err ) { File::Path::make_path( $NewPath, {error => \my $err} );
for my $diag (@$err) { if ( @$err ) {
my ($file, $message) = %$diag; for my $diag (@$err) {
next if $message eq 'File exists'; my ($file, $message) = %$diag;
if ($file eq '') { next if $message eq 'File exists';
$error .= "general error: $message\n"; if ($file eq '') {
} else { $error .= "general error: $message\n";
$error .= "problem making $file: $message\n"; } else {
$error .= "problem making $file: $message\n";
}
} }
} }
} if ( $error ) {
if ( $error ) { $ZoneMinder::Database::dbh->commit();
$ZoneMinder::Database::dbh->commit(); return $error;
return $error;
}
my @files = glob("$OldPath/*");
for my $file (@files) {
next if $file =~ /^\./;
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
Debug("Moving file $file to $NewPath");
if ( ! File::Copy::copy( $file, $NewPath ) ) {
$error .= "Copy failed: for $file to $NewPath: $!";
last;
} }
} # end foreach file. my @files = glob("$OldPath/*");
for my $file (@files) {
next if $file =~ /^\./;
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
my $starttime = time;
Debug("Moving file $file to $NewPath");
my $size = -s $file;
if ( ! File::Copy::copy( $file, $NewPath ) ) {
$error .= "Copy failed: for $file to $NewPath: $!";
last;
}
my $duration = time - $starttime;
Debug("Copied " . Number::Bytes::Human::format_bytes($size) . " in $duration seconds = " . ($duration?Number::Bytes::Human::format_bytes($size/$duration):'inf') . "/sec");
} # end foreach file.
} # end if ! moved
if ( $error ) { if ( $error ) {
$ZoneMinder::Database::dbh->commit(); $ZoneMinder::Database::dbh->commit();
@ -558,8 +657,8 @@ sub MoveTo {
$ZoneMinder::Database::dbh->commit(); $ZoneMinder::Database::dbh->commit();
return $error; return $error;
} }
$self->delete_files( $OldStorage );
$ZoneMinder::Database::dbh->commit(); $ZoneMinder::Database::dbh->commit();
$self->delete_files( $OldStorage );
return $error; return $error;
} # end sub MoveTo } # end sub MoveTo

View File

@ -96,7 +96,7 @@ sub find_one {
sub Execute { sub Execute {
my $self = $_[0]; my $self = $_[0];
my $sql = $self->Sql(); my $sql = $self->Sql(undef);
if ( $self->{HasDiskPercent} ) { if ( $self->{HasDiskPercent} ) {
my $disk_percent = getDiskPercent( $$self{Storage} ? $$self{Storage}->Path() : () ); my $disk_percent = getDiskPercent( $$self{Storage} ? $$self{Storage}->Path() : () );
@ -130,9 +130,10 @@ sub Execute {
} }
sub Sql { sub Sql {
my $self = $_[0]; my $self = shift;
$$self{Sql} = shift if @_;
if ( ! $$self{Sql} ) { if ( ! $$self{Sql} ) {
my $filter_expr = ZoneMinder::General::jsonDecode( $self->{Query} ); my $filter_expr = ZoneMinder::General::jsonDecode($self->{Query});
my $sql = 'SELECT E.*, my $sql = 'SELECT E.*,
unix_timestamp(E.StartTime) as Time, unix_timestamp(E.StartTime) as Time,
M.Name as MonitorName, M.Name as MonitorName,
@ -171,7 +172,7 @@ sub Sql {
$self->{Sql} .= 'to_days( E.StartTime )'; $self->{Sql} .= 'to_days( E.StartTime )';
} elsif ( $term->{attr} eq 'StartDate' ) { } elsif ( $term->{attr} eq 'StartDate' ) {
$self->{Sql} .= 'to_days( E.StartTime )'; $self->{Sql} .= 'to_days( E.StartTime )';
} elsif ( $term->{attr} eq 'Time' ) { } elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' ) {
$self->{Sql} .= 'extract( hour_second from E.StartTime )'; $self->{Sql} .= 'extract( hour_second from E.StartTime )';
} elsif ( $term->{attr} eq 'Weekday' ) { } elsif ( $term->{attr} eq 'Weekday' ) {
$self->{Sql} .= 'weekday( E.StartTime )'; $self->{Sql} .= 'weekday( E.StartTime )';
@ -205,7 +206,7 @@ sub Sql {
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/; ( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) { foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) {
if ( $term->{attr} =~ /^Monitor/ ) { if ( $term->{attr} =~ /^MonitorName/ ) {
$value = "'$temp_value'"; $value = "'$temp_value'";
} elsif ( $term->{attr} eq 'ServerId' ) { } elsif ( $term->{attr} eq 'ServerId' ) {
Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})"); Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})");
@ -276,7 +277,13 @@ sub Sql {
} elsif ( $term->{op} eq '!~' ) { } elsif ( $term->{op} eq '!~' ) {
$self->{Sql} .= " not regexp $value"; $self->{Sql} .= " not regexp $value";
} elsif ( $term->{op} eq 'IS' ) { } elsif ( $term->{op} eq 'IS' ) {
$self->{Sql} .= " IS $value"; if ( $value eq 'Odd' ) {
$self->{Sql} .= ' % 2 = 1';
} elsif ( $value eq 'Even' ) {
$self->{Sql} .= ' % 2 = 0';
} else {
$self->{Sql} .= " IS $value";
}
} elsif ( $term->{op} eq 'IS NOT' ) { } elsif ( $term->{op} eq 'IS NOT' ) {
$self->{Sql} .= " IS NOT $value"; $self->{Sql} .= " IS NOT $value";
} elsif ( $term->{op} eq '=[]' ) { } elsif ( $term->{op} eq '=[]' ) {

View File

@ -147,7 +147,7 @@ our $mem_data = {
last_write_index => { type=>'uint32', seq=>$mem_seq++ }, last_write_index => { type=>'uint32', seq=>$mem_seq++ },
last_read_index => { type=>'uint32', seq=>$mem_seq++ }, last_read_index => { type=>'uint32', seq=>$mem_seq++ },
state => { type=>'uint32', seq=>$mem_seq++ }, state => { type=>'uint32', seq=>$mem_seq++ },
last_event => { type=>'uint32', seq=>$mem_seq++ }, last_event => { type=>'uint64', seq=>$mem_seq++ },
action => { type=>'uint32', seq=>$mem_seq++ }, action => { type=>'uint32', seq=>$mem_seq++ },
brightness => { type=>'int32', seq=>$mem_seq++ }, brightness => { type=>'int32', seq=>$mem_seq++ },
hue => { type=>'int32', seq=>$mem_seq++ }, hue => { type=>'int32', seq=>$mem_seq++ },
@ -166,6 +166,7 @@ our $mem_data = {
last_write_time => { type=>'time_t64', seq=>$mem_seq++ }, last_write_time => { type=>'time_t64', seq=>$mem_seq++ },
last_read_time => { type=>'time_t64', seq=>$mem_seq++ }, last_read_time => { type=>'time_t64', seq=>$mem_seq++ },
control_state => { type=>'uint8[256]', seq=>$mem_seq++ }, control_state => { type=>'uint8[256]', seq=>$mem_seq++ },
alarm_cause => { type=>'int8[256]', seq=>$mem_seq++ },
} }
}, },
trigger_data => { type=>'TriggerData', seq=>$mem_seq++, 'contents'=> { trigger_data => { type=>'TriggerData', seq=>$mem_seq++, 'contents'=> {
@ -315,6 +316,12 @@ sub zmMemRead {
my $type = $mem_data->{$section}->{contents}->{$element}->{type}; my $type = $mem_data->{$section}->{contents}->{$element}->{type};
my $size = $mem_data->{$section}->{contents}->{$element}->{size}; my $size = $mem_data->{$section}->{contents}->{$element}->{size};
if (!defined $offset || !defined $type || !defined $size) {
Error ("Invalid field:".$field." setting to undef and exiting zmMemRead");
zmMemInvalidate( $monitor );
return( undef );
}
my $data = zmMemGet( $monitor, $offset, $size ); my $data = zmMemGet( $monitor, $offset, $size );
if ( !defined($data) ) { if ( !defined($data) ) {
Error( "Unable to read '$field' from memory for monitor ".$monitor->{Id} ); Error( "Unable to read '$field' from memory for monitor ".$monitor->{Id} );
@ -820,6 +827,7 @@ colour Read/write location for the current monitor colour
contrast Read/write location for the current monitor contrast contrast Read/write location for the current monitor contrast
alarm_x Image x co-ordinate (from left) of the centre of the last motion event, -1 if none alarm_x Image x co-ordinate (from left) of the centre of the last motion event, -1 if none
alarm_y Image y co-ordinate (from top) of the centre of the last motion event, -1 if none alarm_y Image y co-ordinate (from top) of the centre of the last motion event, -1 if none
alarm_cause The current alarm event cause string along with zone names(s) alarmed
trigger_data The triggered event mapped memory section trigger_data The triggered event mapped memory section
size The size, in bytes of this section size The size, in bytes of this section

View File

@ -125,6 +125,7 @@ sub load {
if ( $data and %$data ) { if ( $data and %$data ) {
@$self{keys %$data} = values %$data; @$self{keys %$data} = values %$data;
} # end if } # end if
return $data;
} # end sub load } # end sub load
sub lock_and_load { sub lock_and_load {
@ -156,7 +157,7 @@ sub lock_and_load {
if ( $ZoneMinder::Database::dbh->errstr ) { if ( $ZoneMinder::Database::dbh->errstr ) {
Error( "Failure to load Object record for $$self{$primary_key}: Reason: " . $ZoneMinder::Database::dbh->errstr ); Error( "Failure to load Object record for $$self{$primary_key}: Reason: " . $ZoneMinder::Database::dbh->errstr );
} else { } else {
Debug("No Results Loading $type from $table WHERE $primary_key = $$self{$primary_key}"); Debug("No Results Lock and Loading $type from $table WHERE $primary_key = $$self{$primary_key}");
} # end if } # end if
} # end if } # end if
if ( $data and %$data ) { if ( $data and %$data ) {

View File

@ -42,6 +42,7 @@ our @ISA = qw(Exporter ZoneMinder::Base);
# will save memory. # will save memory.
our %EXPORT_TAGS = ( our %EXPORT_TAGS = (
'functions' => [ qw( 'functions' => [ qw(
CpuLoad
) ] ) ]
); );
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
@ -107,6 +108,17 @@ sub Hostname {
return $_[0]{Hostname}; return $_[0]{Hostname};
} # end sub Hostname } # end sub Hostname
sub CpuLoad {
my $output = qx(uptime);
my @sysloads = split ', ', (split ': ', $output)[-1];
if (join(', ',@sysloads) =~ /(\d+\.\d+)\s*,\s+(\d+\.\d+)\s*,\s+(\d+\.\d+)\s*$/) {
return @sysloads;
}
return (undef, undef, undef);
} # end sub CpuLoad
1; 1;
__END__ __END__
# Below is stub documentation for your module. You'd better edit it! # Below is stub documentation for your module. You'd better edit it!

View File

@ -113,6 +113,15 @@ sub Name {
return $_[0]{Name}; return $_[0]{Name};
} # end sub Path } # end sub Path
sub DoDelete {
my $self = shift;
$$self{DoDelete} = shift if @_;
if ( ! defined $$self{DoDelete} ) {
$$self{DoDelete} = 1;
}
return $$self{DoDelete};
}
sub Server { sub Server {
my $self = shift; my $self = shift;
if ( ! $$self{Server} ) { if ( ! $$self{Server} ) {

View File

@ -63,6 +63,7 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
my $report = 0; my $report = 0;
my $interactive = 0; my $interactive = 0;
my $continuous = 0; my $continuous = 0;
my $level = 1;
my $monitor_id = 0; my $monitor_id = 0;
my $version; my $version;
my $force = 0; my $force = 0;
@ -74,6 +75,7 @@ GetOptions(
continuous =>\$continuous, continuous =>\$continuous,
force =>\$force, force =>\$force,
interactive =>\$interactive, interactive =>\$interactive,
level =>\$level,
'monitor_id=i' =>\$monitor_id, 'monitor_id=i' =>\$monitor_id,
report =>\$report, report =>\$report,
'storage_id=i' =>\$storage_id, 'storage_id=i' =>\$storage_id,
@ -246,7 +248,7 @@ MAIN: while( $loop ) {
{ {
my @day_dirs = glob("$monitor_dir/[0-9][0-9]/[0-9][0-9]/[0-9][0-9]"); my @day_dirs = glob("$monitor_dir/[0-9][0-9]/[0-9][0-9]/[0-9][0-9]");
Debug(qq`Checking for Deep Events under using glob("$monitor_dir/[0-9][0-9]/[0-9][0-9]/[0-9][0-9]") returned `. scalar @day_dirs . " events"); Debug(qq`Checking for Deep Events under $$Storage{Path} using glob("$monitor_dir/[0-9][0-9]/[0-9][0-9]/[0-9][0-9]") returned `. scalar @day_dirs . ' events');
foreach my $day_dir ( @day_dirs ) { foreach my $day_dir ( @day_dirs ) {
Debug( "Checking day dir $day_dir" ); Debug( "Checking day dir $day_dir" );
( $day_dir ) = ( $day_dir =~ /^(.*)$/ ); # De-taint ( $day_dir ) = ( $day_dir =~ /^(.*)$/ ); # De-taint
@ -294,7 +296,7 @@ MAIN: while( $loop ) {
} # end foreach day dir } # end foreach day dir
} }
Debug("Checking for Medium Scheme Events under $monitor_dir"); Debug("Checking for Medium Scheme Events under $$Storage{Path}/$monitor_dir");
{ {
my @event_dirs = glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*"); my @event_dirs = glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*");
Debug(qq`glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") returned ` . scalar @event_dirs . " entries." ); Debug(qq`glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") returned ` . scalar @event_dirs . " entries." );
@ -485,7 +487,9 @@ MAIN: while( $loop ) {
redo MAIN; redo MAIN;
} }
if ( $level > 1 ) {
# Remove orphaned events (with no monitor) # Remove orphaned events (with no monitor)
# Shouldn't be possible anymore with FOREIGN KEYS in place
$cleaned = 0; $cleaned = 0;
Debug("Checking for Orphaned Events"); Debug("Checking for Orphaned Events");
my $selectOrphanedEventsSql = 'SELECT Events.Id, Events.Name my $selectOrphanedEventsSql = 'SELECT Events.Id, Events.Name
@ -507,6 +511,7 @@ MAIN: while( $loop ) {
} }
} }
redo MAIN if $cleaned; redo MAIN if $cleaned;
} # end if level > 1
# Remove empty events (with no frames) # Remove empty events (with no frames)
$cleaned = 0; $cleaned = 0;
@ -537,7 +542,7 @@ MAIN: while( $loop ) {
$cleaned = 0; $cleaned = 0;
Debug("Checking for Orphaned Frames"); Debug("Checking for Orphaned Frames");
my $selectOrphanedFramesSql = 'SELECT DISTINCT EventId FROM Frames my $selectOrphanedFramesSql = 'SELECT DISTINCT EventId FROM Frames
WHERE EventId NOT IN (SELECT Id FROM Events)'; WHERE (SELECT COUNT(*) FROM Events WHERE Events.Id=EventId)=0';
my $selectOrphanedFramesSth = $dbh->prepare_cached( $selectOrphanedFramesSql ) my $selectOrphanedFramesSth = $dbh->prepare_cached( $selectOrphanedFramesSql )
or Fatal( "Can't prepare '$selectOrphanedFramesSql': ".$dbh->errstr() ); or Fatal( "Can't prepare '$selectOrphanedFramesSql': ".$dbh->errstr() );
$res = $selectOrphanedFramesSth->execute() $res = $selectOrphanedFramesSth->execute()
@ -552,6 +557,7 @@ MAIN: while( $loop ) {
} }
redo MAIN if $cleaned; redo MAIN if $cleaned;
if ( $level > 1 ) {
# Remove orphaned stats records # Remove orphaned stats records
$cleaned = 0; $cleaned = 0;
Debug("Checking for Orphaned Stats"); Debug("Checking for Orphaned Stats");
@ -570,6 +576,7 @@ MAIN: while( $loop ) {
} }
} }
redo MAIN if ( $cleaned ); redo MAIN if ( $cleaned );
}
# New audit to close any events that were left open for longer than MIN_AGE seconds # New audit to close any events that were left open for longer than MIN_AGE seconds
my $selectUnclosedEventsSql = my $selectUnclosedEventsSql =

View File

@ -104,6 +104,10 @@ my @daemons = (
'zmtelemetry.pl' 'zmtelemetry.pl'
); );
if ($Config{ZM_OPT_USE_EVENTNOTIFICATION}) {
push @daemons,'zmeventnotification.pl';
}
my $command = shift @ARGV; my $command = shift @ARGV;
if( ! $command ) { if( ! $command ) {
print( STDERR "No command given\n" ); print( STDERR "No command given\n" );
@ -151,8 +155,8 @@ my $server_up = connect( CLIENT, $saddr );
if ( ! $server_up ) { if ( ! $server_up ) {
if ( $Config{ZM_SERVER_ID} ) { if ( $Config{ZM_SERVER_ID} ) {
use Sys::MemInfo qw(totalmem freemem totalswap freeswap); use Sys::MemInfo qw(totalmem freemem totalswap freeswap);
use Sys::CpuLoad; use ZoneMinder::Server qw(CpuLoad);
if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef, if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef,
'NotRunning', &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) { 'NotRunning', &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) {
Error("Failed Updating status of Server record to Not RUnning for Id=$Config{ZM_SERVER_ID}" . $dbh->errstr()); Error("Failed Updating status of Server record to Not RUnning for Id=$Config{ZM_SERVER_ID}" . $dbh->errstr());
@ -233,14 +237,14 @@ use Socket;
use IO::Handle; use IO::Handle;
use Time::HiRes qw(usleep); use Time::HiRes qw(usleep);
use Sys::MemInfo qw(totalmem freemem totalswap freeswap); use Sys::MemInfo qw(totalmem freemem totalswap freeswap);
use Sys::CpuLoad; use ZoneMinder::Server qw(CpuLoad);
#use Data::Dumper; #use Data::Dumper;
# We count 10 of these, so total timeout is this value *10. use constant KILL_DELAY => 60; # seconds to wait between sending TERM and sending KILL
use constant KILL_DELAY => 1; # seconds
our %cmd_hash; our %cmd_hash;
our %pid_hash; our %pid_hash;
our %terminating_processes;
sub run { sub run {
my $fd = 0; my $fd = 0;
@ -264,6 +268,7 @@ sub run {
Error( "Can't open pid file at " . ZM_PID ); Error( "Can't open pid file at " . ZM_PID );
} }
# Tell any existing processes to die, wait 1 second between TERM and KILL
killAll( 1 ); killAll( 1 );
dPrint( ZoneMinder::Logger::INFO, 'Socket should be open at ' .main::SOCK_FILE ); dPrint( ZoneMinder::Logger::INFO, 'Socket should be open at ' .main::SOCK_FILE );
@ -298,8 +303,7 @@ sub run {
if ( $Config{ZM_SERVER_ID} ) { if ( $Config{ZM_SERVER_ID} ) {
if ( ! ( $secs_count % 60 ) ) { if ( ! ( $secs_count % 60 ) ) {
$dbh = zmDbConnect() if ! $dbh->ping(); $dbh = zmDbConnect() if ! $dbh->ping();
my @cpuload = Sys::CpuLoad::load(); my @cpuload = CpuLoad();
dPrint( ZoneMinder::Logger::DEBUG, 'Updating Server record' );
if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,CpuLoad=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef, if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,CpuLoad=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef,
'Running', $cpuload[0], &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) { 'Running', $cpuload[0], &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) {
Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr()); Error("Failed Updating status of Server record for Id=$Config{ZM_SERVER_ID}".$dbh->errstr());
@ -362,7 +366,10 @@ sub run {
#print( "Select timed out\n" ); #print( "Select timed out\n" );
restartPending(); restartPending();
} }
}
check_for_processes_to_kill();
} # end while
dPrint( ZoneMinder::Logger::INFO, 'Server exiting at ' dPrint( ZoneMinder::Logger::INFO, 'Server exiting at '
.strftime( '%y/%m/%d %H:%M:%S', localtime() ) .strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n" ."\n"
@ -511,43 +518,33 @@ sub send_stop {
."\n" ."\n"
); );
$process->{keepalive} = !$final; $process->{keepalive} = !$final;
$process->{term_sent_at} = time;
$process->{pending} = 0;
$terminating_processes{$command} = $process;
kill( 'TERM', $pid ); kill( 'TERM', $pid );
return $pid; return $pid;
} # end sub send_stop } # end sub send_stop
sub kill_until_dead { sub check_for_processes_to_kill {
my ( $process ) = @_; # Turn off SIGCHLD
# Now check it has actually gone away, if not kill -9 it
my $count = 0;
my $sigset = POSIX::SigSet->new; my $sigset = POSIX::SigSet->new;
my $blockset = POSIX::SigSet->new(SIGCHLD); my $blockset = POSIX::SigSet->new(SIGCHLD);
sigprocmask(SIG_BLOCK, $blockset, $sigset ) or die "dying at block...\n"; sigprocmask(SIG_BLOCK, $blockset, $sigset ) or die "dying at block...\n";
while( $process and $$process{pid} and kill( 0, $$process{pid} ) ) { foreach my $command ( %terminating_processes ) {
if ( $count++ > 10 ) { my $process = $cmd_hash{$command};
dPrint( ZoneMinder::Logger::WARNING, "'$$process{command}' has not stopped at " if ( $$process{term_sent_at} - time > KILL_DELAY ) {
.strftime( '%y/%m/%d %H:%M:%S', localtime() ) dPrint(ZoneMinder::Logger::WARNING, "'$$process{command}' has not stopped at "
.". Sending KILL to pid $$process{pid}\n" .strftime('%y/%m/%d %H:%M:%S', localtime())
); .' after ' . KILL_DELAY . ' seconds.'
kill( 'KILL', $$process{pid} ); ." Sending KILL to pid $$process{pid}\n"
last; );
kill('KILL', $$process{pid});
} }
# THe purpose of the signal blocking is to simplify the concurrency
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
sleep( KILL_DELAY );
sigprocmask(SIG_BLOCK, $blockset, $sigset ) or die "dying at block...\n";
} }
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n"; sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
} } # end sub check_for_processess_to_kill
sub _stop {
my ($final, $process ) = @_;
my $pid = send_stop( $final, $process );
return if ! $pid;
delete( $cmd_hash{$$process{command}} );
kill_until_dead( $process );
}
sub stop { sub stop {
my ( $daemon, @args ) = @_; my ( $daemon, @args ) = @_;
@ -555,57 +552,49 @@ sub stop {
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'\n" ); dPrint( ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'\n" );
return(); return;
} }
_stop( 1, $process ); send_stop( 1, $process );
} }
# restart is the same as stop, except that we flag the processes for restarting once it dies
# One difference is that if we don't know about the process, then we start it.
sub restart { sub restart {
my $daemon = shift; my ( $daemon, @args ) = @_;
my @args = @_;
my $command = $daemon; my $command = join(' ', $daemon, @args );
$command .= ' '.join( ' ', ( @args ) ) if @args; dPrint(ZoneMinder::Logger::DEBUG, "Restarting $command\n");
dPrint ( ZoneMinder::Logger::DEBUG, "Restarting $command\n");
my $process = $cmd_hash{$command}; my $process = $cmd_hash{$command};
if ( $process ) { if ( !$process ) {
dPrint( ZoneMinder::Logger::DEBUG, "Have process" ); dPrint(ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'\n");
if ( $process->{pid} ) { start($daemon, @args);
dPrint( ZoneMinder::Logger::DEBUG, "Have process pid " .$process->{pid} ); return;
my $cpid = $process->{pid};
if ( defined($pid_hash{$cpid}) ) {
dPrint( ZoneMinder::Logger::DEBUG, "Have process pid hash " .$process->{pid} );
_stop( 0, $process );
return;
} else {
dPrint( ZoneMinder::Logger::DEBUG, "Not sending stop" );
}
}
} }
start( $daemon, @args ); # Start will be handled by the reaper
send_stop(0, $process);
return;
} }
sub reload { sub reload {
my $daemon = shift; my $daemon = shift;
my @args = @_; my @args = @_;
my $command = $daemon; my $command = join(' ', $daemon, @args ) ;
$command .= ' '.join( ' ', ( @args ) ) if ( @args );
my $process = $cmd_hash{$command}; my $process = $cmd_hash{$command};
if ( $process ) { if ( $process ) {
if ( $process->{pid} ) { if ( $process->{pid} ) {
kill( 'HUP', $process->{pid} ); kill('HUP', $process->{pid});
} }
} }
} }
sub logrot { sub logrot {
logReinit(); logReinit();
foreach my $process ( values( %pid_hash ) ) { foreach my $process ( values(%pid_hash) ) {
if ( $process->{pid} ) { if ( $process->{pid} ) {
# && $process->{command} =~ /^zm.*\.pl/ ) { # && $process->{command} =~ /^zm.*\.pl/ ) {
kill( 'HUP', $process->{pid} ); kill('HUP', $process->{pid});
} }
} }
} }
@ -616,16 +605,18 @@ sub reaper {
my $status = $?; my $status = $?;
my $process = $pid_hash{$cpid}; my $process = $pid_hash{$cpid};
delete( $pid_hash{$cpid} ); delete $pid_hash{$cpid};
if ( !$process ) { if ( !$process ) {
dPrint( ZoneMinder::Logger::INFO, "Can't find child with pid of '$cpid'\n" ); dPrint( ZoneMinder::Logger::INFO, "Can't find child with pid of '$cpid'\n" );
next; next;
} }
delete $terminating_processes{$$process{command}};
delete $$process{term_sent_at};
$process->{stopped} = time(); $process->{stopped} = time();
$process->{runtime} = ($process->{stopped}-$process->{started}); $process->{runtime} = ($process->{stopped}-$process->{started});
delete( $process->{pid} ); delete $process->{pid};
my $exit_status = $status>>8; my $exit_status = $status>>8;
my $exit_signal = $status&0xfe; my $exit_signal = $status&0xfe;
@ -672,6 +663,8 @@ sub reaper {
$process->{delay} = $Config{ZM_MAX_RESTART_DELAY}; $process->{delay} = $Config{ZM_MAX_RESTART_DELAY};
} }
} }
} else {
delete $cmd_hash{$$process{command}};
} }
} }
$SIG{CHLD} = \&reaper; $SIG{CHLD} = \&reaper;
@ -682,8 +675,8 @@ sub restartPending {
# Restart any pending processes # Restart any pending processes
foreach my $process ( values( %cmd_hash ) ) { foreach my $process ( values( %cmd_hash ) ) {
if ( $process->{pending} && $process->{pending} <= time() ) { if ( $process->{pending} && $process->{pending} <= time() ) {
dPrint( ZoneMinder::Logger::INFO, "Starting pending process, $process->{command}\n" ); dPrint(ZoneMinder::Logger::INFO, "Starting pending process, $process->{command}\n");
start( $process->{daemon}, @{$process->{args}} ); start($process->{daemon}, @{$process->{args}});
} }
} }
} }
@ -694,21 +687,12 @@ sub shutdownAll {
next if ! $pid_hash{$pid}; next if ! $pid_hash{$pid};
send_stop( 1, $pid_hash{$pid} ); send_stop( 1, $pid_hash{$pid} );
} }
foreach my $pid ( keys %pid_hash ) { while ( %terminating_processes ) {
# This is a quick fix because a SIGCHLD can happen and alter pid_hash while we are in here. check_for_processes_to_kill();
next if ! $pid_hash{$pid}; sleep(1) if %terminating_processes;
my $process = $pid_hash{$pid};
kill_until_dead( $process );
delete( $cmd_hash{$$process{command}} );
delete( $pid_hash{$pid} );
} }
if ( 0 ) { dPrint(ZoneMinder::Logger::INFO, "Server shutdown at "
killAll( 5 ); .strftime('%y/%m/%d %H:%M:%S', localtime())
}
dPrint( ZoneMinder::Logger::INFO, "Server shutdown at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() )
."\n" ."\n"
); );
unlink( main::SOCK_FILE ) or Error( "Unable to unlink " . main::SOCK_FILE .". Error message was: $!" ) if ( -e main::SOCK_FILE ); unlink( main::SOCK_FILE ) or Error( "Unable to unlink " . main::SOCK_FILE .". Error message was: $!" ) if ( -e main::SOCK_FILE );
@ -743,53 +727,54 @@ sub status {
my @args = @_; my @args = @_;
if ( defined($daemon) ) { if ( defined($daemon) ) {
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::DEBUG, "'$command' not running\n" ); dPrint(ZoneMinder::Logger::DEBUG, "'$command' not running\n");
return(); return;
} }
if ( $process->{pending} ) { if ( $process->{pending} ) {
dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at " dPrint(ZoneMinder::Logger::DEBUG, "'$command' pending at "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending} ) ) .strftime('%y/%m/%d %H:%M:%S', localtime($process->{pending}))
."\n" ."\n"
); );
} else { } else {
my $cpid = $process->{pid}; my $pid = $process->{pid};
if ( ! $pid_hash{$cpid} ) { if ( ! $pid_hash{$pid} ) {
dPrint( ZoneMinder::Logger::DEBUG, "'$command' not running\n" ); dPrint(ZoneMinder::Logger::DEBUG, "'$command' not running\n");
return(); return;
} }
} }
dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' running since " dPrint(ZoneMinder::Logger::DEBUG, "'$command' running since "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started} ) ) .strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
.", pid = $process->{pid}" .", pid = $process->{pid}"
); );
} else { } else {
foreach my $process ( values(%pid_hash) ) { foreach my $process ( values %pid_hash ) {
my $out_str = "'$process->{command}' running since " my $out_str = "'$process->{command}' running since "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ) .strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
.", pid = $process->{pid}" .", pid = $process->{pid}"
; ;
$out_str .= ", valid" if ( kill( 0, $process->{pid} ) ); $out_str .= ", valid" if ( kill(0, $process->{pid}) );
$out_str .= "\n"; $out_str .= "\n";
dPrint( ZoneMinder::Logger::DEBUG, $out_str ); dPrint(ZoneMinder::Logger::DEBUG, $out_str);
} }
foreach my $process ( values( %cmd_hash ) ) { foreach my $process ( values %cmd_hash ) {
if ( $process->{pending} ) { if ( $process->{pending} ) {
dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at " dPrint(ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at "
.strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending} ) ) .strftime( '%y/%m/%d %H:%M:%S', localtime($process->{pending}))
."\n" ."\n"
); );
} }
} # end foreach process } # end foreach process
} }
} } # end sub status
sub killAll { sub killAll {
my $delay = shift; my $delay = shift;
sleep( $delay ); # Why sleep before sending term?
#sleep( $delay );
my $killall; my $killall;
if ( '@HOST_OS@' eq 'BSD' ) { if ( '@HOST_OS@' eq 'BSD' ) {
$killall = 'killall -q -'; $killall = 'killall -q -';
@ -810,6 +795,5 @@ sub killAll {
qx( $cmd ); qx( $cmd );
} }
} }
1; 1;
__END__ __END__

View File

@ -239,7 +239,8 @@ sub getFilters {
FILTER: while( my $db_filter = $sth->fetchrow_hashref() ) { FILTER: while( my $db_filter = $sth->fetchrow_hashref() ) {
my $filter = new ZoneMinder::Filter( $$db_filter{Id}, $db_filter ); my $filter = new ZoneMinder::Filter( $$db_filter{Id}, $db_filter );
Debug( "Found filter '$db_filter->{Name}'\n" ); Debug( "Found filter '$db_filter->{Name}'\n" );
my $filter_sql = $filter->Sql(); # The undef here is to make sure the Sql gets regenerated because the Filter object may be cached
my $filter_sql = $filter->Sql(undef);
if ( ! $filter_sql ) { if ( ! $filter_sql ) {
Error( "Error parsing Sql. skipping filter '$db_filter->{Name}'\n" ); Error( "Error parsing Sql. skipping filter '$db_filter->{Name}'\n" );
@ -282,6 +283,8 @@ sub checkFilter {
foreach my $event ( @Events ) { foreach my $event ( @Events ) {
last if $zm_terminate; last if $zm_terminate;
my $Event = new ZoneMinder::Event( $$event{Id}, $event );
Debug( "Checking event $event->{Id}" ); Debug( "Checking event $event->{Id}" );
my $delete_ok = !undef; my $delete_ok = !undef;
$dbh->ping(); $dbh->ping();
@ -301,7 +304,7 @@ sub checkFilter {
} }
if ( $Config{ZM_OPT_EMAIL} && $filter->{AutoEmail} ) { if ( $Config{ZM_OPT_EMAIL} && $filter->{AutoEmail} ) {
if ( !$event->{Emailed} ) { if ( !$event->{Emailed} ) {
$delete_ok = undef if ( !sendEmail( $filter, $event ) ); $delete_ok = undef if ( !sendEmail( $filter, $Event ) );
} }
} }
if ( $Config{ZM_OPT_MESSAGE} && $filter->{AutoMessage} ) { if ( $Config{ZM_OPT_MESSAGE} && $filter->{AutoMessage} ) {
@ -321,26 +324,30 @@ sub checkFilter {
} }
if ( $filter->{AutoDelete} ) { if ( $filter->{AutoDelete} ) {
if ( $delete_ok ) { if ( $delete_ok ) {
my $Event = new ZoneMinder::Event( $$event{Id}, $event );
$Event->delete(); $Event->delete();
} else { } else {
Error( "Unable toto delete event $event->{Id} as previous operations failed\n" ); Error( "Unable toto delete event $event->{Id} as previous operations failed\n" );
} }
} # end if AutoDelete } # end if AutoDelete
if ( $filter->{AutoMove} ) { if ( $filter->{AutoMove} ) {
my $Event = new ZoneMinder::Event( $$event{Id}, $event );
my $NewStorage = new ZoneMinder::Storage( $filter->{AutoMoveTo} ); my $NewStorage = new ZoneMinder::Storage( $filter->{AutoMoveTo} );
$_ = $Event->MoveTo( $NewStorage ); $_ = $Event->MoveTo( $NewStorage );
Error($_) if $_; Error($_) if $_;
} }
if ( $filter->{UpdateDiskSpace} ) { if ( $filter->{UpdateDiskSpace} ) {
my $Event = new ZoneMinder::Event( $$event{Id}, $event );
$ZoneMinder::Database::dbh->begin_work(); $ZoneMinder::Database::dbh->begin_work();
$Event->lock_and_load(); $Event->lock_and_load();
my $old_diskspace = $$Event{DiskSpace}; my $old_diskspace = $$Event{DiskSpace};
if ( $old_diskspace != $Event->DiskSpace(undef) ) { my $new_diskspace = $Event->DiskSpace(undef);
if (
( (!defined $old_diskspace) and defined $new_diskspace)
or
( (defined $old_diskspace) and (defined $new_diskspace) and ( $old_diskspace != $Event->DiskSpace(undef) ) )
) {
$Event->save(); $Event->save();
} }
$ZoneMinder::Database::dbh->commit(); $ZoneMinder::Database::dbh->commit();
@ -391,25 +398,25 @@ sub generateVideo {
chomp( $output ); chomp( $output );
my $status = $? >> 8; my $status = $? >> 8;
if ( $status || logDebugging() ) { if ( $status || logDebugging() ) {
Debug( "Output: $output\n" ); Debug("Output: $output\n");
} }
if ( $status ) { if ( $status ) {
Error( "Video generation '$command' failed with status: $status\n" ); Error("Video generation '$command' failed with status: $status\n");
if ( wantarray() ) { if ( wantarray() ) {
return( undef, undef ); return( undef, undef );
} }
return( 0 ); return 0;
} else { } else {
my $sql = 'UPDATE Events SET Videoed = 1 WHERE Id = ?'; my $sql = 'UPDATE Events SET Videoed = 1 WHERE Id = ?';
my $sth = $dbh->prepare_cached( $sql ) my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() ); or Fatal( "Unable to prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute( $event->{Id} ) my $res = $sth->execute( $event->{Id} )
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() ); or Fatal("Unable toexecute '$sql': ".$sth->errstr());
if ( wantarray() ) { if ( wantarray() ) {
return( $format, $output ); return( $format, $output );
} }
} }
return( 1 ); return 1;
} }
# Returns an image absolute path for given event and frame # Returns an image absolute path for given event and frame
@ -417,13 +424,14 @@ sub generateVideo {
# If neither capture nor analyse image exists it will try to extract a frame from .mp4 file if exists # If neither capture nor analyse image exists it will try to extract a frame from .mp4 file if exists
# An empty string is returned if no one from methods above works # An empty string is returned if no one from methods above works
sub generateImage { sub generateImage {
my $event = shift; my $Event = shift;
my $frame = shift; my $frame = shift;
my $analyse = do { @_ ? shift : 0 }; # don't return analyse image by default my $analyse = @_ ? shift : 0; # don't return analyse image by default
my $capture_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-capture.jpg', getEventPath($event), $frame->{FrameId}); my $event_path = $Event->Path();
my $analyse_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-analyse.jpg', getEventPath($event), $frame->{FrameId}) if $analyse; my $capture_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-capture.jpg', $event_path, $frame->{FrameId});
my $video_path = sprintf('%s/%d-video.mp4', getEventPath($event), $event->{Id}); my $analyse_image_path = sprintf('%s/%0'.$Config{ZM_EVENT_IMAGE_DIGITS}.'d-analyse.jpg', $event_path, $frame->{FrameId}) if $analyse;
my $video_path = sprintf('%s/%d-video.mp4', $event_path, $Event->{Id});
my $image_path = ''; my $image_path = '';
# check if the image file exists. If the file doesn't exist and we use H264 try to extract it from .mp4 video # check if the image file exists. If the file doesn't exist and we use H264 try to extract it from .mp4 video
@ -431,8 +439,18 @@ sub generateImage {
$image_path = $analyse_image_path; $image_path = $analyse_image_path;
} elsif ( -r $capture_image_path ) { } elsif ( -r $capture_image_path ) {
$image_path = $capture_image_path; $image_path = $capture_image_path;
} elsif ( -e $video_path ) { } elsif ( -r $video_path ) {
if ( !system('ffmpeg -y -v 0 -i '.$video_path." -vf 'select=gte(n\\,".$frame->{FrameId}."),setpts=PTS-STARTPTS' -vframes 1 -f image2 ".$capture_image_path) ) { my $command ="ffmpeg -ss $$frame{Delta} -i '$video_path' -frames:v 1 '$capture_image_path'";
#$command = "ffmpeg -y -v 0 -i $video_path -vf 'select=gte(n\\,$$frame{FrameId}),setpts=PTS-STARTPTS' -vframes 1 -f image2 $capture_image_path";
my $output = qx($command);
chomp( $output );
my $status = $? >> 8;
if ( $status || logDebugging() ) {
Debug("Output: $output\n");
}
if ( $status ) {
Error("Failed $command status $status");
} else {
$image_path = $capture_image_path; $image_path = $capture_image_path;
} }
} }
@ -579,7 +597,7 @@ sub uploadArchFile {
sub substituteTags { sub substituteTags {
my $text = shift; my $text = shift;
my $filter = shift; my $filter = shift;
my $event = shift; my $Event = shift;
my $attachments_ref = shift; my $attachments_ref = shift;
# First we'd better check what we need to get # First we'd better check what we need to get
@ -587,35 +605,7 @@ sub substituteTags {
# monitor information? # monitor information?
my $need_monitor = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/; my $need_monitor = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/;
my $monitor = {}; my $Monitor = $Event->Monitor() if $need_monitor;
if ( $need_monitor ) {
my $db_now = strftime( '%Y-%m-%d %H:%M:%S', localtime() );
my $sql = "SELECT
M.Id,
count(E.Id) as EventCount,
count(if(E.Archived,1,NULL))
as ArchEventCount,
count(if(E.StartTime>'$db_now' - INTERVAL 1 HOUR && E.Archived = 0,1,NULL))
as HourEventCount,
count(if(E.StartTime>'$db_now' - INTERVAL 1 DAY && E.Archived = 0,1,NULL))
as DayEventCount,
count(if(E.StartTime>'$db_now' - INTERVAL 7 DAY && E.Archived = 0,1,NULL))
as WeekEventCount,
count(if(E.StartTime>'$db_now' - INTERVAL 1 MONTH && E.Archived = 0,1,NULL))
as MonthEventCount
FROM Monitors as M LEFT JOIN Events as E on E.MonitorId = M.Id
WHERE MonitorId = ?
GROUP BY E.MonitorId
ORDER BY Id"
;
my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{MonitorId} )
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
$monitor = $sth->fetchrow_hashref();
$sth->finish();
return() if ( !$monitor );
}
# Do we need the image information too? # Do we need the image information too?
my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA)%/; my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA)%/;
@ -629,8 +619,9 @@ sub substituteTags {
; ;
my $sth = $dbh->prepare_cached( $sql ) my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() ); or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} ) my $res = $sth->execute( $Event->{Id} )
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() ); or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
my $rows = 0;
while( my $frame = $sth->fetchrow_hashref() ) { while( my $frame = $sth->fetchrow_hashref() ) {
if ( !$first_alarm_frame ) { if ( !$first_alarm_frame ) {
$first_alarm_frame = $frame; $first_alarm_frame = $frame;
@ -639,62 +630,68 @@ sub substituteTags {
$max_alarm_frame = $frame; $max_alarm_frame = $frame;
$max_alarm_score = $frame->{Score}; $max_alarm_score = $frame->{Score};
} }
$rows ++;
} }
Debug("Frames: rows: $rows first alarm frame: $first_alarm_frame max_alaarm_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%/$event->{MonitorName}/g; $text =~ s/%MN%/$Event->{MonitorName}/g;
$text =~ s/%MET%/$monitor->{EventCount}/g; $text =~ s/%MET%/$Monitor->{TotalEvents}/g;
$text =~ s/%MEH%/$monitor->{HourEventCount}/g; $text =~ s/%MEH%/$Monitor->{HourEvents}/g;
$text =~ s/%MED%/$monitor->{DayEventCount}/g; $text =~ s/%MED%/$Monitor->{DayEvents}/g;
$text =~ s/%MEW%/$monitor->{WeekEventCount}/g; $text =~ s/%MEW%/$Monitor->{WeekEvents}/g;
$text =~ s/%MEM%/$monitor->{MonthEventCount}/g; $text =~ s/%MEM%/$Monitor->{MonthEvents}/g;
$text =~ s/%MEA%/$monitor->{ArchEventCount}/g; $text =~ s/%MEA%/$Monitor->{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=watchfeed&mid=$event->{MonitorId}&mode=stream/g; $text =~ s/%MPS%/$url?view=watchfeed&mid=$Event->{MonitorId}&mode=stream/g;
$text =~ s/%MPI%/$url?view=watchfeed&mid=$event->{MonitorId}&mode=still/g; $text =~ s/%MPI%/$url?view=watchfeed&mid=$Event->{MonitorId}&mode=still/g;
$text =~ s/%EP%/$url?view=event&mid=$event->{MonitorId}&eid=$event->{Id}/g; $text =~ s/%EP%/$url?view=event&mid=$Event->{MonitorId}&eid=$Event->{Id}/g;
$text =~ s/%EPS%/$url?view=event&mode=stream&mid=$event->{MonitorId}&eid=$event->{Id}/g; $text =~ s/%EPS%/$url?view=event&mode=stream&mid=$Event->{MonitorId}&eid=$Event->{Id}/g;
$text =~ s/%EPI%/$url?view=event&mode=still&mid=$event->{MonitorId}&eid=$event->{Id}/g; $text =~ s/%EPI%/$url?view=event&mode=still&mid=$Event->{MonitorId}&eid=$Event->{Id}/g;
$text =~ s/%EI%/$event->{Id}/g; $text =~ s/%EI%/$Event->{Id}/g;
$text =~ s/%EN%/$event->{Name}/g; $text =~ s/%EN%/$Event->{Name}/g;
$text =~ s/%EC%/$event->{Cause}/g; $text =~ s/%EC%/$Event->{Cause}/g;
$text =~ s/%ED%/$event->{Notes}/g; $text =~ s/%ED%/$Event->{Notes}/g;
$text =~ s/%ET%/$event->{StartTime}/g; $text =~ s/%ET%/$Event->{StartTime}/g;
$text =~ s/%EL%/$event->{Length}/g; $text =~ s/%EL%/$Event->{Length}/g;
$text =~ s/%EF%/$event->{Frames}/g; $text =~ s/%EF%/$Event->{Frames}/g;
$text =~ s/%EFA%/$event->{AlarmFrames}/g; $text =~ s/%EFA%/$Event->{AlarmFrames}/g;
$text =~ s/%EST%/$event->{TotScore}/g; $text =~ s/%EST%/$Event->{TotScore}/g;
$text =~ s/%ESA%/$event->{AvgScore}/g; $text =~ s/%ESA%/$Event->{AvgScore}/g;
$text =~ s/%ESM%/$event->{MaxScore}/g; $text =~ s/%ESM%/$Event->{MaxScore}/g;
if ( $first_alarm_frame ) { if ( $first_alarm_frame ) {
$text =~ s/%EPI1%/$url?view=frame&mid=$event->{MonitorId}&eid=$event->{Id}&fid=$first_alarm_frame->{FrameId}/g; $text =~ s/%EPI1%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$first_alarm_frame->{FrameId}/g;
$text =~ s/%EPIM%/$url?view=frame&mid=$event->{MonitorId}&eid=$event->{Id}&fid=$max_alarm_frame->{FrameId}/g; $text =~ s/%EPIM%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$max_alarm_frame->{FrameId}/g;
if ( $attachments_ref && $text =~ s/%EI1%//g ) { if ( $attachments_ref && $text =~ s/%EI1%//g ) {
my $path = generateImage( $event, $first_alarm_frame ); my $path = generateImage($Event, $first_alarm_frame);
if ( -e $path ) { if ( -e $path ) {
push( @$attachments_ref, { type=>'image/jpeg', path=>$path } ); push @$attachments_ref, { type=>'image/jpeg', path=>$path };
} }
} }
if ( $attachments_ref && $text =~ s/%EIM%//g ) { if ( $attachments_ref && ( $text =~ s/%EIM%//g ) ) {
# Don't attach the same image twice # Don't attach the same image twice
if ( !@$attachments_ref if ( !@$attachments_ref
|| ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} ) || ( $first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} )
) { ) {
my $path = generateImage( $event, $max_alarm_frame ); my $path = generateImage($Event, $max_alarm_frame);
if ( -e $path ) { if ( -e $path ) {
push( @$attachments_ref, { type=>'image/jpeg', path=>$path } ); push @$attachments_ref, { type=>'image/jpeg', path=>$path };
} else {
Warning("No image for EIM");
} }
} }
} }
if ( $attachments_ref && $text =~ s/%EI1A%//g ) { if ( $attachments_ref && $text =~ s/%EI1A%//g ) {
my $path = generateImage( $event, $first_alarm_frame, 'analyse' ); my $path = generateImage($Event, $first_alarm_frame, 'analyse');
if ( -e $path ) { if ( -e $path ) {
push( @$attachments_ref, { type=>'image/jpeg', path=>$path } ); push @$attachments_ref, { type=>'image/jpeg', path=>$path };
} else {
Warning("No image for EI1A");
} }
} }
if ( $attachments_ref && $text =~ s/%EIMA%//g ) { if ( $attachments_ref && $text =~ s/%EIMA%//g ) {
@ -702,9 +699,11 @@ sub substituteTags {
if ( !@$attachments_ref if ( !@$attachments_ref
|| ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} ) || ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} )
) { ) {
my $path = generateImage( $event, $max_alarm_frame, 'analyse'); my $path = generateImage($Event, $max_alarm_frame, 'analyse');
if ( -e $path ) { if ( -e $path ) {
push( @$attachments_ref, { type=>'image/jpeg', path=>$path } ); push @$attachments_ref, { type=>'image/jpeg', path=>$path };
} else {
Warning("No image for EIMA");
} }
} }
} }
@ -712,49 +711,49 @@ sub substituteTags {
if ( $attachments_ref && $Config{ZM_OPT_FFMPEG} ) { if ( $attachments_ref && $Config{ZM_OPT_FFMPEG} ) {
if ( $text =~ s/%EV%//g ) { if ( $text =~ s/%EV%//g ) {
my ( $format, $path ) = generateVideo( $filter, $event ); my ( $format, $path ) = generateVideo($filter, $Event);
if ( !$format ) { if ( !$format ) {
return( undef ); return undef;
} }
push( @$attachments_ref, { type=>"video/$format", path=>$path } ); push( @$attachments_ref, { type=>"video/$format", path=>$path } );
} }
if ( $text =~ s/%EVM%//g ) { if ( $text =~ s/%EVM%//g ) {
my ( $format, $path ) = generateVideo( $filter, $event, 1 ); my ( $format, $path ) = generateVideo($filter, $Event, 1);
if ( !$format ) { if ( !$format ) {
return( undef ); return undef;
} }
push( @$attachments_ref, { type=>"video/$format", path=>$path } ); push @$attachments_ref, { type=>"video/$format", path=>$path };
} }
} }
$text =~ s/%FN%/$filter->{Name}/g; $text =~ s/%FN%/$filter->{Name}/g;
( my $filter_name = $filter->{Name} ) =~ s/ /+/g; ( my $filter_name = $filter->{Name} ) =~ s/ /+/g;
$text =~ s/%FP%/$url?view=filter&mid=$event->{MonitorId}&filter_name=$filter_name/g; $text =~ s/%FP%/$url?view=filter&mid=$Event->{MonitorId}&filter_name=$filter_name/g;
return( $text ); return $text;
} # end subsitituteTags } # end subsitituteTags
sub sendEmail { sub sendEmail {
my $filter = shift; my $filter = shift;
my $event = shift; my $Event = shift;
if ( ! $Config{ZM_FROM_EMAIL} ) { if ( ! $Config{ZM_FROM_EMAIL} ) {
Error( "No 'from' email address defined, not sending email" ); Error("No 'from' email address defined, not sending email");
return( 0 ); return 0;
} }
if ( ! $Config{ZM_EMAIL_ADDRESS} ) { if ( ! $Config{ZM_EMAIL_ADDRESS} ) {
Error( 'No email address defined, not sending email' ); Error('No email address defined, not sending email');
return( 0 ); return 0;
} }
Info( "Creating notification email\n" ); Info("Creating notification email\n");
my $subject = substituteTags( $Config{ZM_EMAIL_SUBJECT}, $filter, $event ); my $subject = substituteTags($Config{ZM_EMAIL_SUBJECT}, $filter, $Event);
return( 0 ) if ( !$subject ); return 0 if !$subject;
my @attachments; my @attachments;
my $body = substituteTags( $Config{ZM_EMAIL_BODY}, $filter, $event, \@attachments ); my $body = substituteTags($Config{ZM_EMAIL_BODY}, $filter, $Event, \@attachments);
return( 0 ) if ( !$body ); return 0 if !$body;
Info( "Sending notification email '$subject'\n" ); Info("Sending notification email '$subject'\n");
eval { eval {
if ( $Config{ZM_NEW_MAIL_MODULES} ) { if ( $Config{ZM_NEW_MAIL_MODULES} ) {
@ -827,9 +826,9 @@ sub sendEmail {
Info( "Notification email sent\n" ); Info( "Notification email sent\n" );
} }
my $sql = 'update Events set Emailed = 1 where Id = ?'; my $sql = 'update Events set Emailed = 1 where Id = ?';
my $sth = $dbh->prepare_cached( $sql ) my $sth = $dbh->prepare_cached($sql)
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() ); or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( $event->{Id} ) my $res = $sth->execute($Event->{Id})
or Fatal( "Unable toexecute '$sql': ".$sth->errstr() ); or Fatal( "Unable toexecute '$sql': ".$sth->errstr() );
return( 1 ); return( 1 );

View File

@ -205,6 +205,7 @@ if ( $command =~ /^(?:start|restart)$/ ) {
$sql = 'SELECT * FROM Monitors'; $sql = 'SELECT * FROM Monitors';
} }
{
my $sth = $dbh->prepare_cached( $sql ) my $sth = $dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( @values ) my $res = $sth->execute( @values )
@ -229,9 +230,24 @@ if ( $command =~ /^(?:start|restart)$/ ) {
} }
} }
$sth->finish(); $sth->finish();
}
{
my $sql = 'SELECT Id FROM Filters WHERE Background=1';
my $sth = $dbh->prepare_cached($sql)
or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute()
or Fatal( "Can't execute: ".$sth->errstr() );
if ( $sth->rows ) {
while( my $filter = $sth->fetchrow_hashref() ) {
# This is now started unconditionally # This is now started unconditionally
runCommand('zmdc.pl start zmfilter.pl'); runCommand("zmdc.pl start zmfilter.pl --filter_id=$$filter{Id}");
}
} else {
runCommand('zmdc.pl start zmfilter.pl');
}
$sth->finish();
}
if ( $Config{ZM_RUN_AUDIT} ) { if ( $Config{ZM_RUN_AUDIT} ) {
if ( $Server and exists $$Server{'zmaudit'} and ! $$Server{'zmaudit'} ) { if ( $Server and exists $$Server{'zmaudit'} and ! $$Server{'zmaudit'} ) {
Debug("Not running zmaudit.pl because it is turned off for this server."); Debug("Not running zmaudit.pl because it is turned off for this server.");
@ -256,6 +272,9 @@ if ( $command =~ /^(?:start|restart)$/ ) {
if ( $Config{ZM_TELEMETRY_DATA} ) { if ( $Config{ZM_TELEMETRY_DATA} ) {
runCommand('zmdc.pl start zmtelemetry.pl'); runCommand('zmdc.pl start zmtelemetry.pl');
} }
if ($Config{ZM_OPT_USE_EVENTNOTIFICATION} ) {
runCommand('zmdc.pl start zmeventnotification.pl');
}
if ( $Server and exists $$Server{'zmstats'} and ! $$Server{'zmstats'} ) { if ( $Server and exists $$Server{'zmstats'} and ! $$Server{'zmstats'} ) {
Debug("Not running zmstats.pl because it is turned off for this server."); Debug("Not running zmstats.pl because it is turned off for this server.");
} else { } else {

View File

@ -53,67 +53,70 @@ GetOptions(
version => \$version version => \$version
); );
if ( $version ) { if ( $version ) {
print( ZoneMinder::Base::ZM_VERSION . "\n"); print( ZoneMinder::Base::ZM_VERSION . "\n");
exit(0); exit(0);
} }
if ( $help ) { if ( $help ) {
pod2usage(-exitstatus => -1); pod2usage(-exitstatus => -1);
} }
if ( ! defined $interval ) { if ( ! defined $interval ) {
$interval = eval($Config{ZM_TELEMETRY_INTERVAL}); $interval = eval($Config{ZM_TELEMETRY_INTERVAL});
} }
if ( $Config{ZM_TELEMETRY_DATA} or $force ) { if ( !($Config{ZM_TELEMETRY_DATA} or $force) ) {
print "Update agent starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n"; print "ZoneMinder Telemetry Agent not enabled. Exiting.\n";
exit(0);
}
print 'ZoneMinder Telemetry Agent starting at '.strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n";
my $lastCheck = $Config{ZM_TELEMETRY_LAST_UPLOAD}; my $lastCheck = $Config{ZM_TELEMETRY_LAST_UPLOAD};
while( 1 ) { while( 1 ) {
my $now = time(); my $now = time();
my $since_last_check = $now-$lastCheck; my $since_last_check = $now-$lastCheck;
Debug(" Last Check time (now($now) - lastCheck($lastCheck)) = $since_last_check > interval($interval) or force($force)"); Debug(" Last Check time (now($now) - lastCheck($lastCheck)) = $since_last_check > interval($interval) or force($force)");
if ( $since_last_check < 0 ) { if ( $since_last_check < 0 ) {
Warning( "Seconds since last check is negative! Which means that lastCheck is in the future!" ); Warning( 'Seconds since last check is negative! Which means that lastCheck is in the future!' );
next; next;
} }
if ( ( ($now-$lastCheck) > $interval ) or $force ) { if ( ( ($since_last_check) > $interval ) or $force ) {
print "Collecting data to send to ZoneMinder Telemetry server.\n"; print "Collecting data to send to ZoneMinder Telemetry server.\n";
my $dbh = zmDbConnect(); my $dbh = zmDbConnect();
# Build the telemetry hash # Build the telemetry hash
# We should keep *BSD systems in mind when calling system commands # We should keep *BSD systems in mind when calling system commands
my %telemetry; my %telemetry;
$telemetry{uuid} = getUUID($dbh); $telemetry{uuid} = getUUID($dbh);
$telemetry{ip} = getIP(); $telemetry{ip} = getIP();
$telemetry{timestamp} = strftime( '%Y-%m-%dT%H:%M:%S%z', localtime() ); $telemetry{timestamp} = strftime( '%Y-%m-%dT%H:%M:%S%z', localtime() );
$telemetry{monitor_count} = countQuery($dbh,'Monitors'); $telemetry{monitor_count} = countQuery($dbh,'Monitors');
$telemetry{event_count} = countQuery($dbh,'Events'); $telemetry{event_count} = countQuery($dbh,'Events');
$telemetry{architecture} = runSysCmd('uname -p'); $telemetry{architecture} = runSysCmd('uname -p');
($telemetry{kernel}, $telemetry{distro}, $telemetry{version}) = getDistro(); ($telemetry{kernel}, $telemetry{distro}, $telemetry{version}) = getDistro();
$telemetry{zm_version} = ZoneMinder::Base::ZM_VERSION; $telemetry{zm_version} = ZoneMinder::Base::ZM_VERSION;
$telemetry{system_memory} = totalmem(); $telemetry{system_memory} = totalmem();
$telemetry{processor_count} = cpu_count(); $telemetry{processor_count} = cpu_count();
$telemetry{monitors} = getMonitorRef($dbh); $telemetry{monitors} = getMonitorRef($dbh);
Info( 'Sending data to ZoneMinder Telemetry server.' ); Info('Sending data to ZoneMinder Telemetry server.');
my $result = jsonEncode( \%telemetry ); my $result = jsonEncode(\%telemetry);
if ( sendData($result) ) { if ( sendData($result) ) {
my $sql = q`UPDATE Config SET Value = ? WHERE Name = 'ZM_TELEMETRY_LAST_UPLOAD'`;
my $sql = q`UPDATE Config SET Value = ? WHERE Name = 'ZM_TELEMETRY_LAST_UPLOAD'`; 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($now) or die( "Can't execute: ".$sth->errstr() );
my $res = $sth->execute( $now ) or die( "Can't execute: ".$sth->errstr() ); $sth->finish();
$sth->finish(); $Config{ZM_TELEMETRY_LAST_UPLOAD} = $now;
$Config{ZM_TELEMETRY_LAST_UPLOAD} = $now; }
} zmDbDisconnect();
zmDbDisconnect(); } elsif ( -t STDIN ) {
} elsif ( -t STDIN ) { print "ZoneMinder Telemetry Agent sleeping for $interval seconds because ($now-$lastCheck=$since_last_check > $interval\n";
print "Update agent sleeping for 1 hour because ($now-$lastCheck=$since_last_check > $interval\n"; }
} $lastCheck = $now;
sleep( 3600 ); sleep($interval);
} } # end while
print 'Update agent exiting at '.strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n"; print 'ZoneMinder Telemetry Agent exiting at '.strftime('%y/%m/%d %H:%M:%S', localtime())."\n";
}
############### ###############
# SUBROUTINES # # SUBROUTINES #
@ -178,7 +181,7 @@ sub sendData {
# Retrieves the UUID from the database. Creates a new UUID if one does not exist. # Retrieves the UUID from the database. Creates a new UUID if one does not exist.
sub getUUID { sub getUUID {
my $dbh = shift; my $dbh = shift;
my $uuid= ""; my $uuid= '';
# Verify the current UUID is valid and not nil # Verify the current UUID is valid and not nil
if (( $Config{ZM_TELEMETRY_UUID} =~ /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i ) && ( $Config{ZM_TELEMETRY_UUID} ne '00000000-0000-0000-0000-000000000000' )) { if (( $Config{ZM_TELEMETRY_UUID} =~ /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i ) && ( $Config{ZM_TELEMETRY_UUID} ne '00000000-0000-0000-0000-000000000000' )) {

View File

@ -197,8 +197,6 @@ my $sql = " SELECT (SELECT max(Delta) FROM Frames WHERE EventId=Events.Id)-(SELE
Events.*, Events.*,
unix_timestamp(Events.StartTime) as Time, unix_timestamp(Events.StartTime) as Time,
M.Name as MonitorName, M.Name as MonitorName,
M.Width as MonitorWidth,
M.Height as MonitorHeight,
M.Palette M.Palette
FROM Events FROM Events
INNER JOIN Monitors as M on Events.MonitorId = M.Id INNER JOIN Monitors as M on Events.MonitorId = M.Id

View File

@ -147,11 +147,11 @@ while( 1 ) {
if ( !defined($image_time) ) { if ( !defined($image_time) ) {
# Can't read from shared data # Can't read from shared data
$restart = 1; $restart = 1;
Error( "Error reading shared data for $$monitor{Id} $$monitor{Name}\n"); Error("Error reading shared data for $$monitor{Id} $$monitor{Name}\n");
} elsif ( !$image_time ) { } elsif ( !$image_time ) {
# We can't get the last capture time so can't be sure it's died. # We can't get the last capture time so can't be sure it's died.
$restart = 1; $restart = 1;
Error( "Error getting last analyse time for $$monitor{Id} $$monitor{Name}\n"); Error("Last analyse time for $$monitor{Id} $$monitor{Name} was zero.\n");
} else { } else {
my $max_image_delay = ( $monitor->{MaxFPS} my $max_image_delay = ( $monitor->{MaxFPS}

View File

@ -53,6 +53,8 @@ protected:
int contrast; int contrast;
bool capture; bool capture;
bool record_audio; bool record_audio;
unsigned int bytes;
public: public:
Camera( unsigned int p_monitor_id, SourceType p_type, unsigned int p_width, unsigned int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ); Camera( unsigned int p_monitor_id, SourceType p_type, unsigned int p_width, unsigned int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio );
@ -74,6 +76,7 @@ public:
unsigned int SubpixelOrder() const { return( subpixelorder ); } unsigned int SubpixelOrder() const { return( subpixelorder ); }
unsigned int Pixels() const { return( pixels ); } unsigned int Pixels() const { return( pixels ); }
unsigned int ImageSize() const { return( imagesize ); } unsigned int ImageSize() const { return( imagesize ); }
unsigned int Bytes() const { return bytes; };
virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); } virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); }
virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); } virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); }
@ -89,6 +92,7 @@ public:
virtual int Capture( Image &image )=0; virtual int Capture( Image &image )=0;
virtual int PostCapture()=0; virtual int PostCapture()=0;
virtual int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) = 0; virtual int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) = 0;
virtual int Close()=0;
}; };
#endif // ZM_CAMERA_H #endif // ZM_CAMERA_H

View File

@ -194,7 +194,7 @@ public:
SockAddrUnix(); SockAddrUnix();
SockAddrUnix( const SockAddrUnix &addr ) : SockAddr( (const struct sockaddr *)&mAddrUn ), mAddrUn( addr.mAddrUn ) { SockAddrUnix( const SockAddrUnix &addr ) : SockAddr( (const struct sockaddr *)&mAddrUn ), mAddrUn( addr.mAddrUn ) {
} }
SockAddrUnix( const struct sockaddr_un *addr ) : SockAddr( (const struct sockaddr *)&mAddrUn ), mAddrUn( *addr ) { explicit SockAddrUnix( const struct sockaddr_un *addr ) : SockAddr( (const struct sockaddr *)&mAddrUn ), mAddrUn( *addr ) {
} }
bool resolve( const char *path, const char *proto ); bool resolve( const char *path, const char *proto );
@ -622,9 +622,9 @@ protected:
public: public:
Select(); Select();
Select( struct timeval timeout ); explicit Select( struct timeval timeout );
Select( int timeout ); explicit Select( int timeout );
Select( double timeout ); explicit Select( double timeout );
void setTimeout( int timeout ); void setTimeout( int timeout );
void setTimeout( double timeout ); void setTimeout( double timeout );

View File

@ -73,6 +73,7 @@ public:
void Initialise(); void Initialise();
void Terminate(); void Terminate();
int Close() { return 0; };
int PrimeCapture(); int PrimeCapture();
int PreCapture(); int PreCapture();

View File

@ -24,112 +24,128 @@
#include "zm_db.h" #include "zm_db.h"
MYSQL dbconn; MYSQL dbconn;
Mutex db_mutex;
int zmDbConnected = false; bool zmDbConnected = false;
void zmDbConnect() { bool zmDbConnect() {
// For some reason having these lines causes memory corruption and crashing on newer debian/ubuntu // For some reason having these lines causes memory corruption and crashing on newer debian/ubuntu
//if ( zmDbConnected ) // But they really need to be here in order to prevent a double open of mysql
//return; if ( zmDbConnected ) {
Warning("Calling zmDbConnect when already connected");
return true;
}
if ( !mysql_init( &dbconn ) ) { if ( !mysql_init(&dbconn) ) {
Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) ); Error("Can't initialise database connection: %s", mysql_error(&dbconn));
exit( mysql_errno( &dbconn ) ); return false;
} }
my_bool reconnect = 1; my_bool reconnect = 1;
if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) ) if ( mysql_options(&dbconn, MYSQL_OPT_RECONNECT, &reconnect) )
Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) ); Error("Can't set database auto reconnect option: %s", mysql_error(&dbconn));
if ( !staticConfig.DB_SSL_CA_CERT.empty() ) if ( !staticConfig.DB_SSL_CA_CERT.empty() )
mysql_ssl_set( &dbconn, staticConfig.DB_SSL_CLIENT_KEY.c_str(), staticConfig.DB_SSL_CLIENT_CERT.c_str(), staticConfig.DB_SSL_CA_CERT.c_str(), NULL, NULL ); mysql_ssl_set(&dbconn,
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":" ); staticConfig.DB_SSL_CLIENT_KEY.c_str(),
staticConfig.DB_SSL_CLIENT_CERT.c_str(),
staticConfig.DB_SSL_CA_CERT.c_str(),
NULL, NULL);
std::string::size_type colonIndex = staticConfig.DB_HOST.find(":");
if ( colonIndex == std::string::npos ) { if ( colonIndex == std::string::npos ) {
if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, NULL, 0 ) ) { if ( !mysql_real_connect(&dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, NULL, 0) ) {
Error( "Can't connect to server: %s", mysql_error( &dbconn ) ); Error( "Can't connect to server: %s", mysql_error(&dbconn));
exit( mysql_errno( &dbconn ) ); return false;
} }
} else { } else {
std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex ); std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex );
std::string dbPortOrSocket = staticConfig.DB_HOST.substr( colonIndex+1 ); std::string dbPortOrSocket = staticConfig.DB_HOST.substr( colonIndex+1 );
if ( dbPortOrSocket[0] == '/' ) { if ( dbPortOrSocket[0] == '/' ) {
if ( !mysql_real_connect( &dbconn, NULL, staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, dbPortOrSocket.c_str(), 0 ) ) { if ( !mysql_real_connect(&dbconn, NULL, staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, 0, dbPortOrSocket.c_str(), 0) ) {
Error( "Can't connect to server: %s", mysql_error( &dbconn ) ); Error("Can't connect to server: %s", mysql_error(&dbconn));
exit( mysql_errno( &dbconn ) ); return false;
} }
} else { } else {
if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, atoi(dbPortOrSocket.c_str()), NULL, 0 ) ) { if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), NULL, atoi(dbPortOrSocket.c_str()), NULL, 0 ) ) {
Error( "Can't connect to server: %s", mysql_error( &dbconn ) ); Error( "Can't connect to server: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) ); return false;
} }
} }
} }
if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) ) { if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) ) {
Error( "Can't select database: %s", mysql_error( &dbconn ) ); Error( "Can't select database: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) ); return false;
} }
zmDbConnected = true; zmDbConnected = true;
return zmDbConnected;
} }
void zmDbClose() { void zmDbClose() {
if ( zmDbConnected ) { if ( zmDbConnected ) {
db_mutex.lock();
mysql_close( &dbconn ); mysql_close( &dbconn );
// mysql_init() call implicitly mysql_library_init() but // mysql_init() call implicitly mysql_library_init() but
// mysql_close() does not call mysql_library_end() // mysql_close() does not call mysql_library_end()
mysql_library_end(); mysql_library_end();
zmDbConnected = false; zmDbConnected = false;
db_mutex.unlock();
} }
} }
MYSQL_RES * zmDbFetch( const char * query ) { MYSQL_RES * zmDbFetch(const char * query) {
if ( ! zmDbConnected ) { if ( ! zmDbConnected ) {
Error( "Not connected." ); Error("Not connected.");
return NULL; return NULL;
} }
db_mutex.lock();
if ( mysql_query( &dbconn, query ) ) { if ( mysql_query(&dbconn, query) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) ); Error("Can't run query: %s", mysql_error(&dbconn));
db_mutex.unlock();
return NULL; return NULL;
} }
Debug( 4, "Success running query: %s", query ); Debug(4, "Success running query: %s", query);
MYSQL_RES *result = mysql_store_result( &dbconn ); MYSQL_RES *result = mysql_store_result(&dbconn);
if ( !result ) { if ( !result ) {
Error( "Can't use query result: %s for query %s", mysql_error( &dbconn ), query ); Error("Can't use query result: %s for query %s", mysql_error(&dbconn), query);
return NULL;
} }
db_mutex.unlock();
return result; return result;
} // end MYSQL_RES * zmDbFetch( const char * query ); } // end MYSQL_RES * zmDbFetch(const char * query);
zmDbRow *zmDbFetchOne( const char *query ) { zmDbRow *zmDbFetchOne(const char *query) {
zmDbRow *row = new zmDbRow(); zmDbRow *row = new zmDbRow();
if ( row->fetch( query ) ) { if ( row->fetch(query) ) {
return row; return row;
} }
delete row; delete row;
return NULL; return NULL;
} }
MYSQL_RES *zmDbRow::fetch( const char *query ) { MYSQL_RES *zmDbRow::fetch(const char *query) {
result_set = zmDbFetch( query ); result_set = zmDbFetch(query);
if ( ! result_set ) return result_set; if ( ! result_set ) return result_set;
int n_rows = mysql_num_rows( result_set ); int n_rows = mysql_num_rows(result_set);
if ( n_rows != 1 ) { if ( n_rows != 1 ) {
Error( "Bogus number of lines return from query, %d returned for query %s.", n_rows, query ); Error("Bogus number of lines return from query, %d returned for query %s.", n_rows, query);
mysql_free_result( result_set ); mysql_free_result(result_set);
result_set = NULL; result_set = NULL;
return result_set; return result_set;
} }
row = mysql_fetch_row( result_set ); row = mysql_fetch_row(result_set);
if ( ! row ) { if ( ! row ) {
mysql_free_result( result_set ); mysql_free_result(result_set);
result_set = NULL; result_set = NULL;
Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) ); Error("Error getting row from query %s. Error is %s", query, mysql_error(&dbconn));
} else { } else {
Debug(5, "Success"); Debug(5, "Success");
} }
return result_set; return result_set;
} }
zmDbRow::~zmDbRow() { zmDbRow::~zmDbRow() {
if ( result_set ) if ( result_set ) {
mysql_free_result( result_set ); mysql_free_result(result_set);
result_set = NULL;
}
} }

View File

@ -21,37 +21,32 @@
#define ZM_DB_H #define ZM_DB_H
#include <mysql/mysql.h> #include <mysql/mysql.h>
#include "zm_thread.h"
class zmDbRow { class zmDbRow {
private: private:
MYSQL_RES *result_set; MYSQL_RES *result_set;
MYSQL_ROW row; MYSQL_ROW row;
public: public:
zmDbRow() { result_set = NULL; row = NULL; }; zmDbRow() { result_set = NULL; row = NULL; };
MYSQL_RES *fetch( const char *query ); MYSQL_RES *fetch( const char *query );
zmDbRow( MYSQL_RES *, MYSQL_ROW *row ); zmDbRow( MYSQL_RES *, MYSQL_ROW *row );
~zmDbRow(); ~zmDbRow();
char *operator[](unsigned int index) const { MYSQL_ROW mysql_row() const { return row; };
return row[index];
} char *operator[](unsigned int index) const {
return row[index];
}
}; };
#ifdef __cplusplus
extern "C" {
#endif
extern MYSQL dbconn; extern MYSQL dbconn;
extern Mutex db_mutex;
extern int zmDbConnected; bool zmDbConnect();
void zmDbConnect();
void zmDbClose(); void zmDbClose();
MYSQL_RES * zmDbFetch( const char *query ); MYSQL_RES * zmDbFetch( const char *query );
zmDbRow *zmDbFetchOne( const char *query ); zmDbRow *zmDbFetchOne( const char *query );
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif // ZM_DB_H #endif // ZM_DB_H

View File

@ -27,6 +27,7 @@
#include <getopt.h> #include <getopt.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <glob.h> #include <glob.h>
#include <cinttypes>
#include "zm.h" #include "zm.h"
#include "zm_db.h" #include "zm_db.h"
@ -73,6 +74,7 @@ Event::Event(
} }
Storage * storage = monitor->getStorage(); Storage * storage = monitor->getStorage();
scheme = storage->Scheme();
unsigned int state_id = 0; unsigned int state_id = 0;
zmDbRow dbrow; zmDbRow dbrow;
@ -80,8 +82,8 @@ Event::Event(
state_id = atoi(dbrow[0]); state_id = atoi(dbrow[0]);
} }
static char sql[ZM_SQL_MED_BUFSIZ]; char sql[ZM_SQL_MED_BUFSIZ];
struct tm *stime = localtime( &start_time.tv_sec ); struct tm *stime = localtime(&start_time.tv_sec);
snprintf( sql, sizeof(sql), "INSERT INTO Events ( MonitorId, StorageId, Name, StartTime, Width, Height, Cause, Notes, StateId, Orientation, Videoed, DefaultVideo, SaveJPEGs, Scheme ) values ( %d, %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s', %d, %d, %d, '', %d, '%s' )", snprintf( sql, sizeof(sql), "INSERT INTO Events ( MonitorId, StorageId, Name, StartTime, Width, Height, Cause, Notes, StateId, Orientation, Videoed, DefaultVideo, SaveJPEGs, Scheme ) values ( %d, %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s', %d, %d, %d, '', %d, '%s' )",
monitor->Id(), monitor->Id(),
storage->Id(), storage->Id(),
@ -96,13 +98,16 @@ Event::Event(
monitor->GetOptSaveJPEGs(), monitor->GetOptSaveJPEGs(),
storage->SchemeString().c_str() storage->SchemeString().c_str()
); );
if ( mysql_query( &dbconn, sql ) ) { db_mutex.lock();
Error( "Can't insert event: %s. sql was (%s)", mysql_error( &dbconn ), sql ); if ( mysql_query(&dbconn, sql) ) {
exit( mysql_errno( &dbconn ) ); Error("Can't insert event: %s. sql was (%s)", mysql_error(&dbconn), sql);
db_mutex.unlock();
return;
} }
id = mysql_insert_id( &dbconn ); id = mysql_insert_id(&dbconn);
db_mutex.unlock();
if ( untimedEvent ) { if ( untimedEvent ) {
Warning( "Event %d has zero time, setting to current", id ); Warning("Event %d has zero time, setting to current", id);
} }
end_time.tv_sec = 0; end_time.tv_sec = 0;
frames = 0; frames = 0;
@ -110,7 +115,6 @@ Event::Event(
tot_score = 0; tot_score = 0;
max_score = 0; max_score = 0;
struct stat statbuf;
char id_file[PATH_MAX]; char id_file[PATH_MAX];
if ( storage->Scheme() == Storage::DEEP ) { if ( storage->Scheme() == Storage::DEEP ) {
@ -144,7 +148,7 @@ Event::Event(
time_path_ptr += snprintf( time_path_ptr, sizeof(time_path)-(time_path_ptr-time_path), "%s%02d", i>3?"/":"", dt_parts[i] ); time_path_ptr += snprintf( time_path_ptr, sizeof(time_path)-(time_path_ptr-time_path), "%s%02d", i>3?"/":"", dt_parts[i] );
} }
// Create event id symlink // Create event id symlink
snprintf( id_file, sizeof(id_file), "%s/.%d", date_path, id ); snprintf( id_file, sizeof(id_file), "%s/.%" PRIu64, date_path, id );
if ( symlink( time_path, id_file ) < 0 ) if ( symlink( time_path, id_file ) < 0 )
Error( "Can't symlink %s -> %s: %s", id_file, path, strerror(errno)); Error( "Can't symlink %s -> %s: %s", id_file, path, strerror(errno));
} else if ( storage->Scheme() == Storage::MEDIUM ) { } else if ( storage->Scheme() == Storage::MEDIUM ) {
@ -157,14 +161,14 @@ Event::Event(
if ( errno != EEXIST ) if ( errno != EEXIST )
Error( "Can't mkdir %s: %s", path, strerror(errno)); Error( "Can't mkdir %s: %s", path, strerror(errno));
} }
path_ptr += snprintf( path_ptr, sizeof(path), "/%d", id ); path_ptr += snprintf( path_ptr, sizeof(path), "/%" PRIu64, id );
if ( mkdir( path, 0755 ) ) { if ( mkdir( path, 0755 ) ) {
// FIXME This should not be fatal. Should probably move to a different storage area. // FIXME This should not be fatal. Should probably move to a different storage area.
if ( errno != EEXIST ) if ( errno != EEXIST )
Error( "Can't mkdir %s: %s", path, strerror(errno)); Error( "Can't mkdir %s: %s", path, strerror(errno));
} }
} else { } else {
snprintf( path, sizeof(path), "%s/%d/%d", storage->Path(), monitor->Id(), id ); snprintf( path, sizeof(path), "%s/%d/%" PRIu64, storage->Path(), monitor->Id(), id );
if ( mkdir( path, 0755 ) ) { if ( mkdir( path, 0755 ) ) {
if ( errno != EEXIST ) { if ( errno != EEXIST ) {
Error( "Can't mkdir %s: %s", path, strerror(errno)); Error( "Can't mkdir %s: %s", path, strerror(errno));
@ -172,7 +176,7 @@ Event::Event(
} }
// Create empty id tag file // Create empty id tag file
snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); snprintf( id_file, sizeof(id_file), "%s/.%" PRIu64, path, id );
if ( FILE *id_fp = fopen( id_file, "w" ) ) if ( FILE *id_fp = fopen( id_file, "w" ) )
fclose( id_fp ); fclose( id_fp );
else else
@ -186,7 +190,7 @@ Event::Event(
/* Save as video */ /* Save as video */
if ( monitor->GetOptVideoWriter() != 0 ) { if ( monitor->GetOptVideoWriter() != 0 ) {
snprintf( video_name, sizeof(video_name), "%d-%s", id, "video.mp4" ); snprintf( video_name, sizeof(video_name), "%" PRIu64 "-%s", id, "video.mp4" );
snprintf( video_file, sizeof(video_file), staticConfig.video_file_format, path, video_name ); snprintf( video_file, sizeof(video_file), staticConfig.video_file_format, path, video_name );
Debug(1,"Writing video file to %s", video_file ); Debug(1,"Writing video file to %s", video_file );
@ -207,15 +211,6 @@ Event::Event(
delete videowriter; delete videowriter;
videowriter = NULL; videowriter = NULL;
} }
snprintf( timecodes_name, sizeof(timecodes_name), "%d-%s", id, "video.timecodes" );
snprintf( timecodes_file, sizeof(timecodes_file), staticConfig.video_file_format, path, timecodes_name );
/* Create timecodes file */
timecodes_fd = fopen(timecodes_file, "wb");
if ( timecodes_fd == NULL ) {
Error("Failed creating timecodes file");
}
} }
} else { } else {
/* No video object */ /* No video object */
@ -225,22 +220,8 @@ Event::Event(
} // Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent ) } // Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent )
Event::~Event() { Event::~Event() {
static char sql[ZM_SQL_MED_BUFSIZ];
struct DeltaTimeval delta_time;
DELTA_TIMEVAL(delta_time, end_time, start_time, DT_PREC_2);
Debug(2, "start_time:%d.%d end_time%d.%d", start_time.tv_sec, start_time.tv_usec, end_time.tv_sec, end_time.tv_usec );
if ( frames > last_db_frame ) { // We close the videowriter first, because if we finish the event, we might try to view the file, but we aren't done writing it yet.
Debug( 1, "Adding closing frame %d to DB", frames );
snprintf( sql, sizeof(sql),
"insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld )",
id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert frame: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
}
/* Close the video file */ /* Close the video file */
if ( videowriter != NULL ) { if ( videowriter != NULL ) {
@ -250,22 +231,43 @@ Event::~Event() {
} }
delete videowriter; delete videowriter;
videowriter = NULL; videowriter = NULL;
}
/* Close the timecodes file */ // Should not be static because we are multi-threaded
if ( timecodes_fd ) { char sql[ZM_SQL_MED_BUFSIZ];
fclose(timecodes_fd); struct DeltaTimeval delta_time;
timecodes_fd = NULL; DELTA_TIMEVAL(delta_time, end_time, start_time, DT_PREC_2);
Debug(2, "start_time:%d.%d end_time%d.%d", start_time.tv_sec, start_time.tv_usec, end_time.tv_sec, end_time.tv_usec);
if ( frames > last_db_frame ) {
Debug(1, "Adding closing frame %d to DB", frames);
snprintf(sql, sizeof(sql),
"INSERT INTO Frames ( EventId, FrameId, TimeStamp, Delta ) VALUES ( %" PRIu64 ", %d, from_unixtime( %ld ), %s%ld.%02ld )",
id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec);
db_mutex.lock();
if ( mysql_query(&dbconn, sql) ) {
Error("Can't insert frame: %s", mysql_error(&dbconn));
} else {
Debug(1,"Success writing last frame");
} }
db_mutex.unlock();
} }
snprintf( sql, sizeof(sql), "update Events set Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id ); snprintf(sql, sizeof(sql),
if ( mysql_query( &dbconn, sql ) ) { "UPDATE Events SET Name='%s %" PRIu64 "', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' WHERE Id = %" PRIu64,
Error( "Can't update event: %s", mysql_error( &dbconn ) ); monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id );
exit( mysql_errno( &dbconn ) ); db_mutex.lock();
while ( mysql_query(&dbconn, sql) ) {
Error("Can't update event: %s reason: %s", sql, mysql_error(&dbconn));
db_mutex.unlock();
sleep(1);
db_mutex.lock();
} }
} db_mutex.unlock();
void Event::createNotes( std::string &notes ) { } // Event::~Event()
void Event::createNotes(std::string &notes) {
notes.clear(); notes.clear();
for ( StringSetMap::const_iterator mapIter = noteSetMap.begin(); mapIter != noteSetMap.end(); ++mapIter ) { for ( StringSetMap::const_iterator mapIter = noteSetMap.begin(); mapIter != noteSetMap.end(); ++mapIter ) {
notes += mapIter->first; notes += mapIter->first;
@ -327,11 +329,7 @@ bool Event::WriteFrameVideo( const Image *image, const struct timeval timestamp,
Error("Failed encoding video frame"); Error("Failed encoding video frame");
} }
/* Add the frame to the timecodes file */ return true;
if ( timecodes_fd )
fprintf(timecodes_fd, "%u\n", timeMS);
return( true );
} }
void Event::updateNotes( const StringSetMap &newNoteSetMap ) { void Event::updateNotes( const StringSetMap &newNoteSetMap ) {
@ -426,10 +424,12 @@ void Event::updateNotes( const StringSetMap &newNoteSetMap ) {
mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() ); mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() );
snprintf( sql, sizeof(sql), "update Events set Notes = '%s' where Id = %d", escapedNotes, id ); snprintf( sql, sizeof(sql), "UPDATE Events SET Notes = '%s' WHERE Id = %" PRIu64, escapedNotes, id );
db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) { if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert event: %s", mysql_error( &dbconn ) ); Error( "Can't insert event: %s", mysql_error( &dbconn ) );
} }
db_mutex.unlock();
#endif #endif
} }
} }
@ -482,7 +482,7 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st
} }
int sql_len = strlen(sql); int sql_len = strlen(sql);
snprintf( sql+sql_len, sizeof(sql)-sql_len, "( %d, %d, from_unixtime(%ld), %s%ld.%02ld ), ", id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); snprintf( sql+sql_len, sizeof(sql)-sql_len, "( %" PRIu64 ", %d, from_unixtime(%ld), %s%ld.%02ld ), ", id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec );
frameCount++; frameCount++;
} }
@ -490,9 +490,11 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st
if ( frameCount ) { if ( frameCount ) {
Debug( 1, "Adding %d/%d frames to DB", frameCount, n_frames ); Debug( 1, "Adding %d/%d frames to DB", frameCount, n_frames );
*(sql+strlen(sql)-2) = '\0'; *(sql+strlen(sql)-2) = '\0';
db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) { if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert frames: %s, sql was (%s)", mysql_error( &dbconn ), sql ); Error( "Can't insert frames: %s, sql was (%s)", mysql_error( &dbconn ), sql );
} }
db_mutex.unlock();
last_db_frame = frames; last_db_frame = frames;
} else { } else {
Debug( 1, "No valid pre-capture frames to add" ); Debug( 1, "No valid pre-capture frames to add" );
@ -541,16 +543,21 @@ Debug(3, "Writing video");
Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, Event::frame_type_names[frame_type] ); Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, Event::frame_type_names[frame_type] );
static char sql[ZM_SQL_MED_BUFSIZ]; static char sql[ZM_SQL_MED_BUFSIZ];
snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) values ( %d, %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", id, frames, frame_type_names[frame_type], timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score ); snprintf(sql, sizeof(sql), "INSERT INTO Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) values ( %" PRIu64 ", %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", id, frames, frame_type_names[frame_type], timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score);
if ( mysql_query( &dbconn, sql ) ) { db_mutex.lock();
Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); if ( mysql_query(&dbconn, sql) ) {
exit( mysql_errno( &dbconn ) ); Error("Can't insert frame: %s", mysql_error(&dbconn));
Error("SQL was %s", sql);
db_mutex.unlock();
return;
} }
db_mutex.unlock();
last_db_frame = frames; last_db_frame = frames;
// We are writing a Bulk frame // We are writing a Bulk frame
if ( frame_type == BULK ) { if ( frame_type == BULK ) {
snprintf( sql, sizeof(sql), "update Events set Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", snprintf( sql, sizeof(sql),
"UPDATE Events SET Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %" PRIu64,
( delta_time.positive?"":"-" ), ( delta_time.positive?"":"-" ),
delta_time.sec, delta_time.fsec, delta_time.sec, delta_time.fsec,
frames, frames,
@ -560,10 +567,14 @@ Debug(3, "Writing video");
max_score, max_score,
id id
); );
if ( mysql_query( &dbconn, sql ) ) { db_mutex.lock();
Error( "Can't update event: %s", mysql_error( &dbconn ) ); while ( mysql_query(&dbconn, sql) ) {
exit( mysql_errno( &dbconn ) ); Error("Can't update event: %s", mysql_error(&dbconn));
db_mutex.unlock();
sleep(1);
db_mutex.lock();
} }
db_mutex.unlock();
} }
} // end if db_frame } // end if db_frame
@ -578,9 +589,9 @@ Debug(3, "Writing video");
max_score = score; max_score = score;
if ( alarm_image ) { if ( alarm_image ) {
snprintf( event_file, sizeof(event_file), staticConfig.analyse_file_format, path, frames ); snprintf(event_file, sizeof(event_file), staticConfig.analyse_file_format, path, frames);
Debug( 1, "Writing analysis frame %d", frames ); Debug(1, "Writing analysis frame %d", frames);
if ( monitor->GetOptSaveJPEGs() & 2 ) { if ( monitor->GetOptSaveJPEGs() & 2 ) {
WriteFrameImage(alarm_image, timestamp, event_file, true); WriteFrameImage(alarm_image, timestamp, event_file, true);
} }

View File

@ -73,7 +73,7 @@ class Event {
static int pre_alarm_count; static int pre_alarm_count;
static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES]; static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES];
unsigned int id; uint64_t id;
Monitor *monitor; Monitor *monitor;
struct timeval start_time; struct timeval start_time;
struct timeval end_time; struct timeval end_time;
@ -92,9 +92,9 @@ class Event {
char timecodes_name[PATH_MAX]; char timecodes_name[PATH_MAX];
char timecodes_file[PATH_MAX]; char timecodes_file[PATH_MAX];
int last_db_frame; int last_db_frame;
Storage::Schemes scheme;
void createNotes( std::string &notes ); void createNotes( std::string &notes );
Storage::Schemes scheme;
public: public:
static bool OpenFrameSocket( int ); static bool OpenFrameSocket( int );
@ -103,15 +103,15 @@ class Event {
Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent=false ); Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent=false );
~Event(); ~Event();
int Id() const { return( id ); } uint64_t Id() const { return id; }
const std::string &Cause() { return( cause ); } const std::string &Cause() { return cause; }
int Frames() const { return( frames ); } int Frames() const { return frames; }
int AlarmFrames() const { return( alarm_frames ); } int AlarmFrames() const { return alarm_frames; }
const struct timeval &StartTime() const { return( start_time ); } const struct timeval &StartTime() const { return start_time; }
const struct timeval &EndTime() const { return( end_time ); } const struct timeval &EndTime() const { return end_time; }
struct timeval &StartTime() { return( start_time ); } struct timeval &StartTime() { return start_time; }
struct timeval &EndTime() { return( end_time ); } struct timeval &EndTime() { return end_time; }
bool SendFrameImage( const Image *image, bool alarm_frame=false ); bool SendFrameImage( const Image *image, bool alarm_frame=false );
bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false ); bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false );
@ -132,7 +132,7 @@ class Event {
return( subpath ); return( subpath );
} }
static const char *getSubPath( time_t *time ) { static const char *getSubPath( time_t *time ) {
return( Event::getSubPath( localtime( time ) ) ); return Event::getSubPath( localtime( time ) );
} }
char* getEventFile(void) { char* getEventFile(void) {
@ -141,7 +141,7 @@ class Event {
public: public:
static int PreAlarmCount() { static int PreAlarmCount() {
return( pre_alarm_count ); return pre_alarm_count;
} }
static void EmptyPreAlarmFrames() { static void EmptyPreAlarmFrames() {
if ( pre_alarm_count > 0 ) { if ( pre_alarm_count > 0 ) {

View File

@ -43,30 +43,30 @@
bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) { bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) {
static char sql[ZM_SQL_SML_BUFSIZ]; static char sql[ZM_SQL_SML_BUFSIZ];
snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %d and unix_timestamp( EndTime ) > %ld order by Id asc limit 1", monitor_id, event_time ); snprintf(sql, sizeof(sql), "SELECT Id FROM Events WHERE MonitorId = %d AND unix_timestamp(EndTime) > %ld ORDER BY Id ASC LIMIT 1", monitor_id, event_time);
if ( mysql_query( &dbconn, sql ) ) { if ( mysql_query(&dbconn, sql) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) ); Error("Can't run query: %s", mysql_error(&dbconn));
exit( mysql_errno( &dbconn ) ); exit(mysql_errno(&dbconn));
} }
MYSQL_RES *result = mysql_store_result( &dbconn ); MYSQL_RES *result = mysql_store_result(&dbconn);
if ( !result ) { if ( !result ) {
Error( "Can't use query result: %s", mysql_error( &dbconn ) ); Error("Can't use query result: %s", mysql_error(&dbconn));
exit( mysql_errno( &dbconn ) ); exit( mysql_errno( &dbconn ) );
} }
MYSQL_ROW dbrow = mysql_fetch_row( result ); MYSQL_ROW dbrow = mysql_fetch_row(result);
if ( mysql_errno( &dbconn ) ) { if ( mysql_errno(&dbconn) ) {
Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); Error("Can't fetch row: %s", mysql_error(&dbconn));
exit( mysql_errno( &dbconn ) ); exit( mysql_errno(&dbconn));
} }
int init_event_id = atoi( dbrow[0] ); uint64_t init_event_id = atoll(dbrow[0]);
mysql_free_result( result ); mysql_free_result(result);
loadEventData( init_event_id ); loadEventData(init_event_id);
if ( event_time ) { if ( event_time ) {
curr_stream_time = event_time; curr_stream_time = event_time;
@ -84,47 +84,52 @@ bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) {
Debug( 3, "Skipping %ld frames", event_data->frame_count ); Debug( 3, "Skipping %ld frames", event_data->frame_count );
} }
} }
return( true ); return true;
} }
bool EventStream::loadInitialEventData( int init_event_id, unsigned int init_frame_id ) { bool EventStream::loadInitialEventData( uint64_t init_event_id, unsigned int init_frame_id ) {
loadEventData( init_event_id ); loadEventData(init_event_id);
if ( init_frame_id ) { if ( init_frame_id ) {
curr_stream_time = event_data->frames[init_frame_id-1].timestamp; if ( init_frame_id >= event_data->frame_count ) {
curr_frame_id = init_frame_id; Error("Invalid frame id specified. %d > %d", init_frame_id, event_data->frame_count );
curr_stream_time = event_data->start_time;
} else {
curr_stream_time = event_data->frames[init_frame_id-1].timestamp;
curr_frame_id = init_frame_id;
}
} else { } else {
curr_stream_time = event_data->start_time; curr_stream_time = event_data->start_time;
} }
return( true ); return true;
} }
bool EventStream::loadEventData( int event_id ) { bool EventStream::loadEventData(uint64_t event_id) {
static char sql[ZM_SQL_MED_BUFSIZ]; static char sql[ZM_SQL_MED_BUFSIZ];
snprintf( sql, sizeof(sql), "SELECT MonitorId, StorageId, Frames, unix_timestamp( StartTime ) AS StartTimestamp, (SELECT max(Delta)-min(Delta) FROM Frames WHERE EventId=Events.Id) AS Duration, DefaultVideo, Scheme FROM Events WHERE Id = %d", event_id ); snprintf(sql, sizeof(sql), "SELECT MonitorId, StorageId, Frames, unix_timestamp( StartTime ) AS StartTimestamp, (SELECT max(Delta)-min(Delta) FROM Frames WHERE EventId=Events.Id) AS Duration, DefaultVideo, Scheme FROM Events WHERE Id = %" PRIu64, event_id);
if ( mysql_query( &dbconn, sql ) ) { if ( mysql_query(&dbconn, sql) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) ); Error("Can't run query: %s", mysql_error(&dbconn));
exit( mysql_errno( &dbconn ) ); exit(mysql_errno(&dbconn));
} }
MYSQL_RES *result = mysql_store_result( &dbconn ); MYSQL_RES *result = mysql_store_result(&dbconn);
if ( !result ) { if ( !result ) {
Error( "Can't use query result: %s", mysql_error( &dbconn ) ); Error("Can't use query result: %s", mysql_error(&dbconn));
exit( mysql_errno( &dbconn ) ); exit(mysql_errno(&dbconn));
} }
if ( !mysql_num_rows( result ) ) { if ( !mysql_num_rows(result) ) {
Fatal( "Unable to load event %d, not found in DB", event_id ); Fatal("Unable to load event %d, not found in DB", event_id);
} }
MYSQL_ROW dbrow = mysql_fetch_row( result ); MYSQL_ROW dbrow = mysql_fetch_row(result);
if ( mysql_errno( &dbconn ) ) { if ( mysql_errno(&dbconn) ) {
Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); Error("Can't fetch row: %s", mysql_error(&dbconn));
exit( mysql_errno( &dbconn ) ); exit(mysql_errno(&dbconn));
} }
delete event_data; delete event_data;
@ -147,50 +152,53 @@ bool EventStream::loadEventData( int event_id ) {
} }
mysql_free_result( result ); mysql_free_result( result );
Storage * storage = new Storage( event_data->storage_id ); Storage * storage = new Storage(event_data->storage_id);
const char *storage_path = storage->Path(); const char *storage_path = storage->Path();
if ( event_data->scheme == Storage::DEEP ) { if ( event_data->scheme == Storage::DEEP ) {
struct tm *event_time = localtime( &event_data->start_time ); struct tm *event_time = localtime(&event_data->start_time);
if ( storage_path[0] == '/' ) if ( storage_path[0] == '/' )
snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", storage_path, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d",
storage_path, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec );
else else
snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d",
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec );
} else if ( event_data->scheme == Storage::MEDIUM ) { } else if ( event_data->scheme == Storage::MEDIUM ) {
struct tm *event_time = localtime( &event_data->start_time ); struct tm *event_time = localtime( &event_data->start_time );
if ( storage_path[0] == '/' ) if ( storage_path[0] == '/' )
snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%04d-%02d-%02d/%ld", snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%04d-%02d-%02d/%" PRIu64,
storage_path, event_data->monitor_id, event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday, event_data->event_id ); storage_path, event_data->monitor_id, event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday, event_data->event_id );
else else
snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%04d-%02d-%02d/%ld", snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%04d-%02d-%02d/%" PRIu64,
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday, staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday,
event_data->event_id ); event_data->event_id );
} else { } else {
if ( storage_path[0] == '/' ) if ( storage_path[0] == '/' )
snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%ld", storage_path, event_data->monitor_id, event_data->event_id ); snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%" PRIu64,
storage_path, event_data->monitor_id, event_data->event_id );
else else
snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%ld", staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_data->event_id ); snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%" PRIu64,
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id, event_data->event_id );
} }
delete storage; storage = NULL; delete storage; storage = NULL;
updateFrameRate( (double)event_data->frame_count/event_data->duration ); updateFrameRate( (double)event_data->frame_count/event_data->duration );
snprintf(sql, sizeof(sql), "SELECT FrameId, unix_timestamp( `TimeStamp` ), Delta FROM Frames where EventId = %" PRIu64 " ORDER BY FrameId ASC", event_id);
snprintf( sql, sizeof(sql), "select FrameId, unix_timestamp( `TimeStamp` ), Delta from Frames where EventId = %d order by FrameId asc", event_id ); if ( mysql_query(&dbconn, sql) ) {
if ( mysql_query( &dbconn, sql ) ) { Error("Can't run query: %s", mysql_error(&dbconn));
Error( "Can't run query: %s", mysql_error( &dbconn ) ); exit(mysql_errno(&dbconn));
exit( mysql_errno( &dbconn ) );
} }
result = mysql_store_result( &dbconn ); result = mysql_store_result(&dbconn);
if ( !result ) { if ( !result ) {
Error( "Can't use query result: %s", mysql_error( &dbconn ) ); Error("Can't use query result: %s", mysql_error(&dbconn));
exit( mysql_errno( &dbconn ) ); exit(mysql_errno(&dbconn));
} }
event_data->n_frames = mysql_num_rows( result ); event_data->n_frames = mysql_num_rows(result);
event_data->frames = new FrameData[event_data->frame_count]; event_data->frames = new FrameData[event_data->frame_count];
int last_id = 0; int last_id = 0;
@ -223,7 +231,7 @@ bool EventStream::loadEventData( int event_id ) {
exit( mysql_errno( &dbconn ) ); exit( mysql_errno( &dbconn ) );
} }
mysql_free_result( result ); mysql_free_result(result);
//for ( int i = 0; i < 250; i++ ) //for ( int i = 0; i < 250; i++ )
//{ //{
//Info( "%d -> %d @ %f (%d)", i+1, event_data->frames[i].timestamp, event_data->frames[i].delta, event_data->frames[i].in_db ); //Info( "%d -> %d @ %f (%d)", i+1, event_data->frames[i].timestamp, event_data->frames[i].delta, event_data->frames[i].in_db );
@ -231,33 +239,31 @@ bool EventStream::loadEventData( int event_id ) {
if ( event_data->video_file[0] ) { if ( event_data->video_file[0] ) {
char filepath[PATH_MAX]; char filepath[PATH_MAX];
snprintf( filepath, sizeof(filepath), "%s/%s", event_data->path, event_data->video_file ); snprintf(filepath, sizeof(filepath), "%s/%s", event_data->path, event_data->video_file);
ffmpeg_input = new FFmpeg_Input(); ffmpeg_input = new FFmpeg_Input();
if ( 0 > ffmpeg_input->Open( filepath ) ) { if ( 0 > ffmpeg_input->Open( filepath ) ) {
Warning("Unable to open ffmpeg_input %s/%s", event_data->path, event_data->video_file ); Warning("Unable to open ffmpeg_input %s/%s", event_data->path, event_data->video_file);
delete ffmpeg_input; delete ffmpeg_input;
ffmpeg_input = NULL; ffmpeg_input = NULL;
} }
} }
if ( forceEventChange || mode == MODE_ALL_GAPLESS ) { if ( forceEventChange || mode == MODE_ALL_GAPLESS ) {
if ( replay_rate > 0 ) if ( replay_rate > 0 )
curr_stream_time = event_data->frames[0].timestamp; curr_stream_time = event_data->frames[0].timestamp;
else else
curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp; curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp;
} }
Debug( 2, "Event:%ld, Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration ); Debug(2, "Event:%" PRIu64 ", Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration);
return( true ); return true;
} // bool EventStream::loadEventData( int event_id ) } // bool EventStream::loadEventData( int event_id )
void EventStream::processCommand( const CmdMsg *msg ) { void EventStream::processCommand(const CmdMsg *msg) {
Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ); Debug(2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0]);
// Check for incoming command // Check for incoming command
switch( (MsgCommand)msg->msg_data[0] ) { switch( (MsgCommand)msg->msg_data[0] ) {
case CMD_PAUSE : case CMD_PAUSE :
{
Debug( 1, "Got PAUSE command" ); Debug( 1, "Got PAUSE command" );
// Set paused flag // Set paused flag
@ -265,12 +271,9 @@ void EventStream::processCommand( const CmdMsg *msg ) {
replay_rate = ZM_RATE_BASE; replay_rate = ZM_RATE_BASE;
last_frame_sent = TV_2_FLOAT( now ); last_frame_sent = TV_2_FLOAT( now );
break; break;
}
case CMD_PLAY : case CMD_PLAY :
{
Debug( 1, "Got PLAY command" ); Debug( 1, "Got PLAY command" );
if ( paused ) { if ( paused ) {
// Clear paused flag
paused = false; paused = false;
} }
@ -284,30 +287,20 @@ void EventStream::processCommand( const CmdMsg *msg ) {
replay_rate = ZM_RATE_BASE; replay_rate = ZM_RATE_BASE;
break; break;
}
case CMD_VARPLAY : case CMD_VARPLAY :
{
Debug( 1, "Got VARPLAY command" ); Debug( 1, "Got VARPLAY command" );
if ( paused ) { if ( paused ) {
// Clear paused flag
paused = false; paused = false;
} }
replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768;
break; break;
}
case CMD_STOP : case CMD_STOP :
{
Debug( 1, "Got STOP command" ); Debug( 1, "Got STOP command" );
// Clear paused flag
paused = false; paused = false;
break; break;
}
case CMD_FASTFWD : case CMD_FASTFWD :
{
Debug( 1, "Got FAST FWD command" ); Debug( 1, "Got FAST FWD command" );
if ( paused ) { if ( paused ) {
// Clear paused flag
paused = false; paused = false;
} }
// Set play rate // Set play rate
@ -330,36 +323,21 @@ void EventStream::processCommand( const CmdMsg *msg ) {
break; break;
} }
break; break;
}
case CMD_SLOWFWD : case CMD_SLOWFWD :
{
Debug( 1, "Got SLOW FWD command" ); Debug( 1, "Got SLOW FWD command" );
// Set paused flag
paused = true; paused = true;
// Set play rate
replay_rate = ZM_RATE_BASE; replay_rate = ZM_RATE_BASE;
// Set step
step = 1; step = 1;
break; break;
}
case CMD_SLOWREV : case CMD_SLOWREV :
{
Debug( 1, "Got SLOW REV command" ); Debug( 1, "Got SLOW REV command" );
// Set paused flag
paused = true; paused = true;
// Set play rate
replay_rate = ZM_RATE_BASE; replay_rate = ZM_RATE_BASE;
// Set step
step = -1; step = -1;
break; break;
}
case CMD_FASTREV : case CMD_FASTREV :
{
Debug( 1, "Got FAST REV command" ); Debug( 1, "Got FAST REV command" );
if ( paused ) { paused = false;
// Clear paused flag
paused = false;
}
// Set play rate // Set play rate
switch ( replay_rate ) { switch ( replay_rate ) {
case -2 * ZM_RATE_BASE : case -2 * ZM_RATE_BASE :
@ -380,9 +358,7 @@ void EventStream::processCommand( const CmdMsg *msg ) {
break; break;
} }
break; break;
}
case CMD_ZOOMIN : case CMD_ZOOMIN :
{
x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
Debug( 1, "Got ZOOM IN command, to %d,%d", x, y ); Debug( 1, "Got ZOOM IN command, to %d,%d", x, y );
@ -406,10 +382,7 @@ void EventStream::processCommand( const CmdMsg *msg ) {
} }
send_frame = true; send_frame = true;
break; break;
}
case CMD_ZOOMOUT : case CMD_ZOOMOUT :
{
Debug( 1, "Got ZOOM OUT command" ); Debug( 1, "Got ZOOM OUT command" );
switch ( zoom ) { switch ( zoom ) {
case 500: case 500:
@ -431,22 +404,16 @@ void EventStream::processCommand( const CmdMsg *msg ) {
} }
send_frame = true; send_frame = true;
break; break;
}
case CMD_PAN : case CMD_PAN :
{
x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
Debug( 1, "Got PAN command, to %d,%d", x, y ); Debug( 1, "Got PAN command, to %d,%d", x, y );
break; break;
}
case CMD_SCALE : case CMD_SCALE :
{
scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
Debug( 1, "Got SCALE command, to %d", scale ); Debug( 1, "Got SCALE command, to %d", scale );
break; break;
}
case CMD_PREV : case CMD_PREV :
{
Debug( 1, "Got PREV command" ); Debug( 1, "Got PREV command" );
if ( replay_rate >= 0 ) if ( replay_rate >= 0 )
curr_frame_id = 0; curr_frame_id = 0;
@ -455,9 +422,7 @@ void EventStream::processCommand( const CmdMsg *msg ) {
paused = false; paused = false;
forceEventChange = true; forceEventChange = true;
break; break;
}
case CMD_NEXT : case CMD_NEXT :
{
Debug( 1, "Got NEXT command" ); Debug( 1, "Got NEXT command" );
if ( replay_rate >= 0 ) if ( replay_rate >= 0 )
curr_frame_id = event_data->frame_count+1; curr_frame_id = event_data->frame_count+1;
@ -466,7 +431,6 @@ void EventStream::processCommand( const CmdMsg *msg ) {
paused = false; paused = false;
forceEventChange = true; forceEventChange = true;
break; break;
}
case CMD_SEEK : case CMD_SEEK :
{ {
int offset = ((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]; int offset = ((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];
@ -476,35 +440,30 @@ void EventStream::processCommand( const CmdMsg *msg ) {
break; break;
} }
case CMD_QUERY : case CMD_QUERY :
{
Debug( 1, "Got QUERY command, sending STATUS" ); Debug( 1, "Got QUERY command, sending STATUS" );
break; break;
}
case CMD_QUIT : case CMD_QUIT :
{ Info("User initiated exit - CMD_QUIT");
Info ("User initiated exit - CMD_QUIT");
break; break;
}
default : default :
{
// Do nothing, for now // Do nothing, for now
} break;
} }
struct { struct {
int event; uint64_t event_id;
int progress; int progress;
int rate; int rate;
int zoom; int zoom;
bool paused; bool paused;
} status_data; } status_data;
status_data.event = event_data->event_id; status_data.event_id = event_data->event_id;
status_data.progress = (int)event_data->frames[curr_frame_id-1].offset; status_data.progress = (int)event_data->frames[curr_frame_id-1].offset;
status_data.rate = replay_rate; status_data.rate = replay_rate;
status_data.zoom = zoom; status_data.zoom = zoom;
status_data.paused = paused; status_data.paused = paused;
Debug( 2, "Event:%d, Paused:%d, progress:%d Rate:%d, Zoom:%d", Debug( 2, "Event:%" PRIu64 ", Paused:%d, progress:%d Rate:%d, Zoom:%d",
status_data.event, status_data.event_id,
status_data.paused, status_data.paused,
status_data.progress, status_data.progress,
status_data.rate, status_data.rate,
@ -513,19 +472,19 @@ void EventStream::processCommand( const CmdMsg *msg ) {
DataMsg status_msg; DataMsg status_msg;
status_msg.msg_type = MSG_DATA_EVENT; status_msg.msg_type = MSG_DATA_EVENT;
memcpy( &status_msg.msg_data, &status_data, sizeof(status_data) ); memcpy(&status_msg.msg_data, &status_data, sizeof(status_data));
if ( sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) ) < 0 ) { if ( sendto(sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr)) < 0 ) {
//if ( errno != EAGAIN ) //if ( errno != EAGAIN )
{ {
Error( "Can't sendto on sd %d: %s", sd, strerror(errno) ); Error("Can't sendto on sd %d: %s", sd, strerror(errno));
exit( -1 ); exit(-1);
} }
} }
// quit after sending a status, if this was a quit request // quit after sending a status, if this was a quit request
if ((MsgCommand)msg->msg_data[0]==CMD_QUIT) if ( (MsgCommand)msg->msg_data[0]==CMD_QUIT )
exit(0); exit(0);
updateFrameRate( (double)event_data->frame_count/event_data->duration ); updateFrameRate((double)event_data->frame_count/event_data->duration);
} }
void EventStream::checkEventLoaded() { void EventStream::checkEventLoaded() {
@ -533,10 +492,10 @@ void EventStream::checkEventLoaded() {
static char sql[ZM_SQL_SML_BUFSIZ]; static char sql[ZM_SQL_SML_BUFSIZ];
if ( curr_frame_id <= 0 ) { if ( curr_frame_id <= 0 ) {
snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id < %ld order by Id desc limit 1", event_data->monitor_id, event_data->event_id ); snprintf( sql, sizeof(sql), "SELECT Id FROM Events WHERE MonitorId = %ld AND Id < %" PRIu64 " ORDER BY Id DESC LIMIT 1", event_data->monitor_id, event_data->event_id );
reload_event = true; reload_event = true;
} else if ( (unsigned int)curr_frame_id > event_data->frame_count ) { } else if ( (unsigned int)curr_frame_id > event_data->frame_count ) {
snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id > %ld order by Id asc limit 1", event_data->monitor_id, event_data->event_id ); snprintf( sql, sizeof(sql), "SELECT Id FROM Events WHERE MonitorId = %ld AND Id > %" PRIu64 " ORDER BY Id ASC LIMIT 1", event_data->monitor_id, event_data->event_id );
reload_event = true; reload_event = true;
} }
@ -561,10 +520,10 @@ void EventStream::checkEventLoaded() {
} }
if ( dbrow ) { if ( dbrow ) {
int event_id = atoi(dbrow[0]); uint64_t event_id = atoll(dbrow[0]);
Debug( 1, "Loading new event %d", event_id ); Debug( 1, "Loading new event %" PRIu64, event_id );
loadEventData( event_id ); loadEventData(event_id);
Debug( 2, "Current frame id = %d", curr_frame_id ); Debug( 2, "Current frame id = %d", curr_frame_id );
if ( replay_rate < 0 ) if ( replay_rate < 0 )
@ -594,10 +553,10 @@ void EventStream::checkEventLoaded() {
Image * EventStream::getImage( ) { Image * EventStream::getImage( ) {
static char filepath[PATH_MAX]; static char filepath[PATH_MAX];
Debug( 2, "EventStream::getImage path(%s) frame(%d)", event_data->path, curr_frame_id ); Debug(2, "EventStream::getImage path(%s) frame(%d)", event_data->path, curr_frame_id);
snprintf( filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id ); snprintf( filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id);
Debug( 2, "EventStream::getImage path(%s) ", filepath, curr_frame_id ); Debug(2, "EventStream::getImage path(%s) ", filepath, curr_frame_id);
Image *image = new Image( filepath ); Image *image = new Image(filepath);
return image; return image;
} }
@ -627,13 +586,14 @@ bool EventStream::sendFrame( int delta_us ) {
#if HAVE_LIBAVCODEC #if HAVE_LIBAVCODEC
if ( type == STREAM_MPEG ) { if ( type == STREAM_MPEG ) {
Debug(2,"Streaming MPEG");
Image image( filepath ); Image image( filepath );
Image *send_image = prepareImage( &image ); Image *send_image = prepareImage(&image);
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());
fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); fprintf(stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType());
vid_stream->OpenStream(); vid_stream->OpenStream();
} }
/* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_us*1000 ); /* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_us*1000 );
@ -649,34 +609,35 @@ bool EventStream::sendFrame( int delta_us ) {
fprintf( stdout, "--ZoneMinderFrame\r\n" ); fprintf( stdout, "--ZoneMinderFrame\r\n" );
if ( type != STREAM_JPEG ) if ( (type != STREAM_JPEG) || (!filepath[0]) )
send_raw = false; send_raw = false;
if ( send_raw ) { if ( send_raw ) {
fdj = fopen( filepath, "rb" ); fdj = fopen(filepath, "rb");
if ( !fdj ) { if ( !fdj ) {
Error( "Can't open %s: %s", filepath, strerror(errno) ); Error("Can't open %s: %s", filepath, strerror(errno));
return( false ); return false;
} }
#if HAVE_SENDFILE #if HAVE_SENDFILE
if( fstat(fileno(fdj),&filestat) < 0 ) { if( fstat(fileno(fdj),&filestat) < 0 ) {
Error( "Failed getting information about file %s: %s", filepath, strerror(errno) ); Error( "Failed getting information about file %s: %s", filepath, strerror(errno) );
return( false ); return false;
} }
#else #else
img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); img_buffer_size = fread(img_buffer, 1, sizeof(temp_img_buffer), fdj);
#endif #endif
} else { } else {
Image *image = NULL; Image *image = NULL;
if ( filepath[0] ) { if ( filepath[0] ) {
image = new Image( filepath ); Debug(1, "Loading image");
image = new Image(filepath);
} else if ( ffmpeg_input ) { } else if ( ffmpeg_input ) {
// Get the frame from the mp4 input // Get the frame from the mp4 input
Debug(1,"Getting frame from ffmpeg"); Debug(1,"Getting frame from ffmpeg");
AVFrame *frame = ffmpeg_input->get_frame( ffmpeg_input->get_video_stream_id() ); AVFrame *frame = ffmpeg_input->get_frame( ffmpeg_input->get_video_stream_id() );
if ( frame ) { if ( frame ) {
image = new Image( frame ); image = new Image(frame);
av_frame_free(&frame); av_frame_free(&frame);
} else { } else {
Error("Failed getting a frame."); Error("Failed getting a frame.");
@ -687,16 +648,16 @@ bool EventStream::sendFrame( int delta_us ) {
return false; return false;
} }
Image *send_image = prepareImage( image ); Image *send_image = prepareImage(image);
switch( type ) { switch( type ) {
case STREAM_JPEG : case STREAM_JPEG :
send_image->EncodeJpeg( img_buffer, &img_buffer_size ); send_image->EncodeJpeg(img_buffer, &img_buffer_size);
break; break;
case STREAM_ZIP : case STREAM_ZIP :
#if HAVE_ZLIB_H #if HAVE_ZLIB_H
unsigned long zip_buffer_size; unsigned long zip_buffer_size;
send_image->Zip( img_buffer, &zip_buffer_size ); send_image->Zip(img_buffer, &zip_buffer_size);
img_buffer_size = zip_buffer_size; img_buffer_size = zip_buffer_size;
break; break;
#else #else
@ -717,16 +678,16 @@ bool EventStream::sendFrame( int delta_us ) {
switch( type ) { switch( type ) {
case STREAM_JPEG : case STREAM_JPEG :
fprintf( stdout, "Content-Type: image/jpeg\r\n" ); fputs( "Content-Type: image/jpeg\r\n", stdout );
break; break;
case STREAM_RAW : case STREAM_RAW :
fprintf( stdout, "Content-Type: image/x-rgb\r\n" ); fputs( "Content-Type: image/x-rgb\r\n", stdout );
break; break;
case STREAM_ZIP : case STREAM_ZIP :
fprintf( stdout, "Content-Type: image/x-rgbz\r\n" ); fputs( "Content-Type: image/x-rgbz\r\n", stdout );
break; break;
default : default :
Fatal( "Unexpected frame type %d", type ); Fatal("Unexpected frame type %d", type);
break; break;
} }
@ -760,11 +721,11 @@ bool EventStream::sendFrame( int delta_us ) {
} }
} }
fprintf( stdout, "\r\n\r\n" ); fputs("\r\n\r\n", stdout);
fflush( stdout ); fflush(stdout);
} }
last_frame_sent = TV_2_FLOAT( now ); last_frame_sent = TV_2_FLOAT(now);
return( true ); return true;
} }
void EventStream::runStream() { void EventStream::runStream() {
@ -773,20 +734,19 @@ void EventStream::runStream() {
checkInitialised(); checkInitialised();
if ( type == STREAM_JPEG ) if ( type == STREAM_JPEG )
fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" ); fputs("Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n", stdout);
if ( !event_data ) { if ( !event_data ) {
sendTextFrame( "No event data found" ); sendTextFrame("No event data found");
exit( 0 ); exit(0);
} }
Debug(3, "frame rate is: (%f)", (double)event_data->frame_count/event_data->duration ); Debug(3, "frame rate is: (%f)", (double)event_data->frame_count/event_data->duration);
updateFrameRate( (double)event_data->frame_count/event_data->duration ); updateFrameRate((double)event_data->frame_count/event_data->duration);
while( !zm_terminate ) { while( !zm_terminate ) {
gettimeofday( &now, NULL ); gettimeofday(&now, NULL);
unsigned int delta_us = 0; unsigned int delta_us = 0;
send_frame = false; send_frame = false;
@ -819,11 +779,11 @@ void EventStream::runStream() {
in_event = false; in_event = false;
} }
if ( !in_event ) { if ( !in_event ) {
double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
if ( actual_delta_time > 1 ) { if ( actual_delta_time > 1 ) {
static char frame_text[64]; static char frame_text[64];
snprintf( frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event ); snprintf(frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event);
if ( !sendTextFrame( frame_text ) ) if ( !sendTextFrame(frame_text) )
zm_terminate = true; zm_terminate = true;
} }
//else //else
@ -852,16 +812,16 @@ void EventStream::runStream() {
send_frame = true; send_frame = true;
} else if ( !send_frame ) { } else if ( !send_frame ) {
// We are paused, and doing nothing // We are paused, and doing nothing
double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
if ( actual_delta_time > MAX_STREAM_DELAY ) { if ( actual_delta_time > MAX_STREAM_DELAY ) {
// Send keepalive // Send keepalive
Debug( 2, "Sending keepalive frame" ); Debug(2, "Sending keepalive frame");
send_frame = true; send_frame = true;
} }
} }
if ( send_frame ) if ( send_frame )
if ( !sendFrame( delta_us ) ) if ( !sendFrame(delta_us) )
zm_terminate = true; zm_terminate = true;
curr_stream_time = frame_data->timestamp; curr_stream_time = frame_data->timestamp;
@ -886,3 +846,17 @@ void EventStream::runStream() {
closeComms(); closeComms();
} }
void EventStream::setStreamStart( uint64_t init_event_id, unsigned int init_frame_id=0 ) {
loadInitialEventData( init_event_id, init_frame_id );
if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) {
Fatal( "Unable to load monitor id %d for streaming", event_data->monitor_id );
return;
}
}
void EventStream::setStreamStart( int monitor_id, time_t event_time ) {
loadInitialEventData(monitor_id, event_time);
if ( !(monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY)) ) {
Fatal("Unable to load monitor id %d for streaming", monitor_id);
return;
}
}

View File

@ -54,7 +54,7 @@ class EventStream : public StreamBase {
}; };
struct EventData { struct EventData {
unsigned long event_id; uint64_t event_id;
unsigned long monitor_id; unsigned long monitor_id;
unsigned long storage_id; unsigned long storage_id;
unsigned long frame_count; unsigned long frame_count;
@ -83,8 +83,8 @@ class EventStream : public StreamBase {
FFmpeg_Input *ffmpeg_input; FFmpeg_Input *ffmpeg_input;
protected: protected:
bool loadEventData( int event_id ); bool loadEventData( uint64_t event_id );
bool loadInitialEventData( int init_event_id, unsigned int init_frame_id ); bool loadInitialEventData( uint64_t init_event_id, unsigned int init_frame_id );
bool loadInitialEventData( int monitor_id, time_t event_time ); bool loadInitialEventData( int monitor_id, time_t event_time );
void checkEventLoaded(); void checkEventLoaded();
@ -110,20 +110,8 @@ class EventStream : public StreamBase {
ffmpeg_input = NULL; ffmpeg_input = NULL;
} }
void setStreamStart( int init_event_id, unsigned int init_frame_id=0 ) { void setStreamStart( uint64_t init_event_id, unsigned int init_frame_id );
loadInitialEventData( init_event_id, init_frame_id ); void setStreamStart( int monitor_id, time_t event_time );
if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) {
Fatal( "Unable to load monitor id %d for streaming", event_data->monitor_id );
return;
}
}
void setStreamStart( int monitor_id, time_t event_time ) {
loadInitialEventData( monitor_id, event_time );
if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) {
Fatal( "Unable to load monitor id %d for streaming", monitor_id );
return;
}
}
void setStreamMode( StreamMode p_mode ) { void setStreamMode( StreamMode p_mode ) {
mode = p_mode; mode = p_mode;
} }

View File

@ -27,9 +27,9 @@ void FFMPEGInit() {
static bool bInit = false; static bool bInit = false;
if ( !bInit ) { if ( !bInit ) {
if ( logDebugging() ) //if ( logDebugging() )
av_log_set_level( AV_LOG_DEBUG ); //av_log_set_level( AV_LOG_DEBUG );
else //else
av_log_set_level( AV_LOG_QUIET ); av_log_set_level( AV_LOG_QUIET );
av_register_all(); av_register_all();
avformat_network_init(); avformat_network_init();
@ -243,7 +243,7 @@ void zm_dump_codecpar ( const AVCodecParameters *par ) {
} }
#endif #endif
void zm_dump_codec ( const AVCodecContext *codec ) { void zm_dump_codec(const AVCodecContext *codec) {
Debug(1, "Dumping codec_context codec_type(%d) codec_id(%d) width(%d) height(%d) timebase(%d/%d) format(%s)", Debug(1, "Dumping codec_context codec_type(%d) codec_id(%d) width(%d) height(%d) timebase(%d/%d) format(%s)",
codec->codec_type, codec->codec_type,
codec->codec_id, codec->codec_id,
@ -425,7 +425,7 @@ int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet
#endif #endif
# else # else
int frameComplete; int frameComplete = 0;
while ( !frameComplete ) { while ( !frameComplete ) {
if ( (ret = zm_avcodec_decode_video( context, frame, &frameComplete, &packet )) < 0 ) { if ( (ret = zm_avcodec_decode_video( context, frame, &frameComplete, &packet )) < 0 ) {
Error( "Unable to decode frame at frame: %s, continuing", Error( "Unable to decode frame at frame: %s, continuing",

View File

@ -129,15 +129,15 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
} else { } else {
Panic("Unexpected colours: %d",colours); Panic("Unexpected colours: %d",colours);
} }
} }
FfmpegCamera::~FfmpegCamera() { FfmpegCamera::~FfmpegCamera() {
if ( videoStore ) { if ( videoStore ) {
delete videoStore; delete videoStore;
videoStore = NULL;
} }
CloseFfmpeg(); Close();
if ( capture ) { if ( capture ) {
Terminate(); Terminate();
@ -146,13 +146,7 @@ FfmpegCamera::~FfmpegCamera() {
} }
void FfmpegCamera::Initialise() { void FfmpegCamera::Initialise() {
if ( logDebugging() ) FFMPEGInit();
av_log_set_level( AV_LOG_DEBUG );
else
av_log_set_level( AV_LOG_QUIET );
av_register_all();
avformat_network_init();
} }
void FfmpegCamera::Terminate() { void FfmpegCamera::Terminate() {
@ -161,7 +155,7 @@ void FfmpegCamera::Terminate() {
int FfmpegCamera::PrimeCapture() { int FfmpegCamera::PrimeCapture() {
if ( mCanCapture ) { if ( mCanCapture ) {
Info( "Priming capture from %s, CLosing", mPath.c_str() ); Info( "Priming capture from %s, CLosing", mPath.c_str() );
CloseFfmpeg(); Close();
} }
mVideoStreamId = -1; mVideoStreamId = -1;
mAudioStreamId = -1; mAudioStreamId = -1;
@ -171,7 +165,6 @@ int FfmpegCamera::PrimeCapture() {
} }
int FfmpegCamera::PreCapture() { int FfmpegCamera::PreCapture() {
Debug(1, "PreCapture");
// If Reopen was called, then ffmpeg is closed and we need to reopen it. // If Reopen was called, then ffmpeg is closed and we need to reopen it.
if ( ! mCanCapture ) if ( ! mCanCapture )
return OpenFfmpeg(); return OpenFfmpeg();
@ -188,7 +181,7 @@ int FfmpegCamera::Capture( Image &image ) {
int frameComplete = false; int frameComplete = false;
while ( !frameComplete ) { while ( !frameComplete ) {
int avResult = av_read_frame( mFormatContext, &packet ); int avResult = av_read_frame(mFormatContext, &packet);
char errbuf[AV_ERROR_MAX_STRING_SIZE]; char errbuf[AV_ERROR_MAX_STRING_SIZE];
if ( avResult < 0 ) { if ( avResult < 0 ) {
av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE); av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE);
@ -198,15 +191,10 @@ int FfmpegCamera::Capture( Image &image ) {
// Check for Connection failure. // Check for Connection failure.
(avResult == -110) (avResult == -110)
) { ) {
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf ); Info("Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf);
if ( ReopenFfmpeg() < 0 ) { } else {
// OpenFfmpeg will do enough logging. Error("Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf);
return -1;
}
continue;
} }
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf );
return -1; return -1;
} }
@ -304,6 +292,7 @@ int FfmpegCamera::Capture( Image &image ) {
} else { } else {
Debug( 4, "Different stream_index %d", packet.stream_index ); Debug( 4, "Different stream_index %d", packet.stream_index );
} // end if packet.stream_index == mVideoStreamId } // end if packet.stream_index == mVideoStreamId
bytes += packet.size;
zm_av_packet_unref( &packet ); zm_av_packet_unref( &packet );
} // end while ! frameComplete } // end while ! frameComplete
return 1; return 1;
@ -316,10 +305,6 @@ int FfmpegCamera::PostCapture() {
int FfmpegCamera::OpenFfmpeg() { int FfmpegCamera::OpenFfmpeg() {
Debug(2, "OpenFfmpeg called.");
uint32_t last_event_id = monitor->GetLastEventId() ;
uint32_t video_writer_event_id = monitor->GetVideoWriterEventId();
Debug(2, "last_event(%d), our current (%d)", last_event_id, video_writer_event_id);
int ret; int ret;
@ -353,24 +338,26 @@ int FfmpegCamera::OpenFfmpeg() {
Warning("Could not set rtsp_transport method '%s'\n", method.c_str()); Warning("Could not set rtsp_transport method '%s'\n", method.c_str());
} }
Debug ( 1, "Calling avformat_open_input for %s", mPath.c_str() ); Debug(1, "Calling avformat_open_input for %s", mPath.c_str());
mFormatContext = avformat_alloc_context( ); //mFormatContext = avformat_alloc_context( );
// Speed up find_stream_info // Speed up find_stream_info
//FIXME can speed up initial analysis but need sensible parameters... //FIXME can speed up initial analysis but need sensible parameters...
//mFormatContext->probesize = 32; //mFormatContext->probesize = 32;
//mFormatContext->max_analyze_duration = 32; //mFormatContext->max_analyze_duration = 32;
if ( avformat_open_input( &mFormatContext, mPath.c_str(), NULL, &opts ) != 0 ) if ( avformat_open_input(&mFormatContext, mPath.c_str(), NULL, &opts) != 0 )
#endif #endif
{ {
Error("Unable to open input %s due to: %s", mPath.c_str(), strerror(errno)); Error("Unable to open input %s due to: %s", mPath.c_str(), strerror(errno));
#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0)
av_close_input_file( mFormatContext ); av_close_input_file(mFormatContext);
#else #else
avformat_close_input( &mFormatContext ); if ( mFormatContext ) {
avformat_close_input(&mFormatContext);
mFormatContext = NULL;
}
#endif #endif
mFormatContext = NULL;
av_dict_free(&opts); av_dict_free(&opts);
return -1; return -1;
@ -639,15 +626,7 @@ int FfmpegCamera::OpenFfmpeg() {
return 0; return 0;
} // int FfmpegCamera::OpenFfmpeg() } // int FfmpegCamera::OpenFfmpeg()
int FfmpegCamera::ReopenFfmpeg() { int FfmpegCamera::Close() {
Debug(2, "ReopenFfmpeg called.");
CloseFfmpeg();
return OpenFfmpeg();
}
int FfmpegCamera::CloseFfmpeg() {
Debug(2, "CloseFfmpeg called."); Debug(2, "CloseFfmpeg called.");
@ -693,6 +672,11 @@ int FfmpegCamera::CloseFfmpeg() {
mFormatContext = NULL; mFormatContext = NULL;
} }
if ( videoStore ) {
delete videoStore;
videoStore = NULL;
}
return 0; return 0;
} // end FfmpegCamera::Close } // end FfmpegCamera::Close
@ -708,28 +692,24 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
while ( ! frameComplete ) { while ( ! frameComplete ) {
av_init_packet( &packet ); av_init_packet( &packet );
ret = av_read_frame( mFormatContext, &packet ); ret = av_read_frame(mFormatContext, &packet);
if ( ret < 0 ) { if ( ret < 0 ) {
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
if ( if (
// Check if EOF. // Check if EOF.
(ret == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) || (ret == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
// Check for Connection failure. // Check for Connection failure.
(ret == -110) (ret == -110)
) { ) {
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf); Info("Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf);
if ( ReopenFfmpeg() < 0 ) { } else {
// OpenFfmpeg will do enough logging. Error("Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf);
return -1;
}
continue;
} }
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf );
return -1; return -1;
} }
int keyframe = packet.flags & AV_PKT_FLAG_KEY; int keyframe = packet.flags & AV_PKT_FLAG_KEY;
bytes += packet.size;
dumpPacket(&packet); dumpPacket(&packet);
//Video recording //Video recording

View File

@ -74,11 +74,8 @@ class FfmpegCamera : public Camera {
AVPacket packet; AVPacket packet;
int OpenFfmpeg(); int OpenFfmpeg();
int ReopenFfmpeg(); int Close();
int CloseFfmpeg();
bool mIsOpening;
bool mCanCapture; bool mCanCapture;
int mOpenStart;
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT
VideoStore *videoStore; VideoStore *videoStore;

View File

@ -121,7 +121,7 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id ) {
} }
if ( (stream_id < 0 ) || ( packet.stream_index == stream_id ) ) { if ( (stream_id < 0 ) || ( packet.stream_index == stream_id ) ) {
Debug(1,"Packet is for our stream (%d)", packet.stream_index ); Debug(3,"Packet is for our stream (%d)", packet.stream_index );
AVCodecContext *context = streams[packet.stream_index].context; AVCodecContext *context = streams[packet.stream_index].context;
@ -169,9 +169,9 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id ) {
frameComplete = 1; frameComplete = 1;
# else # else
ret = zm_avcodec_decode_video( context, frame, &frameComplete, &packet ); ret = zm_avcodec_decode_video(context, frame, &frameComplete, &packet);
if ( ret < 0 ) { if ( ret < 0 ) {
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
Error( "Unable to decode frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); Error( "Unable to decode frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
zm_av_packet_unref( &packet ); zm_av_packet_unref( &packet );
continue; continue;
@ -179,7 +179,7 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id ) {
#endif #endif
} // end if it's the right stream } // end if it's the right stream
zm_av_packet_unref( &packet ); zm_av_packet_unref( &packet );
} // end while ! frameComplete } // end while ! frameComplete
return frame; return frame;

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