Merge branch 'storageareas' into zma_to_thread

This commit is contained in:
Isaac Connor 2018-04-14 11:03:08 -04:00
commit cd3046abb3
923 changed files with 20319 additions and 13274 deletions

View File

@ -23,7 +23,15 @@ addons:
- curl - curl
- sshfs - sshfs
- sed - sed
- binfmt-support
- qemu
- qemu-user-static
install:
- update-binfmts --enable qemu-arm
env: env:
global:
- SMPFLAGS=-j4
matrix: matrix:
- OS=el DIST=6 - OS=el DIST=6
- OS=el DIST=6 ARCH=i386 DOCKER_REPO=knnniggett/packpack - OS=el DIST=6 ARCH=i386 DOCKER_REPO=knnniggett/packpack
@ -34,6 +42,8 @@ env:
- OS=ubuntu DIST=xenial - OS=ubuntu DIST=xenial
- OS=ubuntu DIST=trusty ARCH=i386 - OS=ubuntu DIST=trusty ARCH=i386
- OS=ubuntu DIST=xenial ARCH=i386 - OS=ubuntu DIST=xenial ARCH=i386
- OS=raspbian DIST=stretch ARCH=armhf DOCKER_REPO=knnniggett/packpack
compiler: compiler:
- gcc - gcc
services: services:

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")
@ -484,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

@ -11,3 +11,6 @@ install(FILES ${dbfileslist} 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(FILES "${CMAKE_CURRENT_SOURCE_DIR}/triggers.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")

View File

@ -30,17 +30,6 @@ FOR EACH ROW
// //
DELIMITER ; DELIMITER ;
DROP TABLE IF EXISTS `Events_Day`;
CREATE TABLE `Events_Day` (
`EventId` int(10) unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL,
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Day_MonitorId_idx` (`MonitorId`),
KEY `Events_Day_StartTime_idx` (`StartTime`)
) ENGINE=@ZM_MYSQL_ENGINE@;
delimiter // delimiter //
DROP TRIGGER IF EXISTS Events_Day_delete_trigger// DROP TRIGGER IF EXISTS Events_Day_delete_trigger//
CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day
@ -71,18 +60,6 @@ FOR EACH ROW
// //
DELIMITER ;
DROP TABLE IF EXISTS `Events_Week`;
CREATE TABLE `Events_Week` (
`EventId` int(10) unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL,
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Week_MonitorId_idx` (`MonitorId`),
KEY `Events_Week_StartTime_idx` (`StartTime`)
) ENGINE=@ZM_MYSQL_ENGINE@;
delimiter // 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
@ -112,20 +89,6 @@ FOR EACH ROW
END; END;
// //
DELIMITER ;
DROP TABLE IF EXISTS `Events_Month`;
CREATE TABLE `Events_Month` (
`EventId` int(10) unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`StartTime` datetime default NULL,
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Month_MonitorId_idx` (`MonitorId`),
KEY `Events_Month_StartTime_idx` (`StartTime`)
) ENGINE=@ZM_MYSQL_ENGINE@;
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
@ -136,7 +99,6 @@ FOR EACH ROW BEGIN
END; END;
// //
DROP TRIGGER IF EXISTS Events_Month_update_trigger; DROP TRIGGER IF EXISTS Events_Month_update_trigger;
CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month
FOR EACH ROW FOR EACH ROW
@ -155,55 +117,26 @@ FOR EACH ROW
END; END;
// //
drop procedure if exists update_storage_stats//
DELIMITER ;
DROP TABLE IF EXISTS `Events_Archived`;
CREATE TABLE `Events_Archived` (
`EventId` int(10) unsigned NOT NULL,
`MonitorId` int(10) unsigned NOT NULL,
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`EventId`),
KEY `Events_Archived_MonitorId_idx` (`MonitorId`)
) ENGINE=@ZM_MYSQL_ENGINE@;
drop procedure if exists update_storage_stats;
delimiter //
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;
@ -227,7 +160,7 @@ BEGIN
WHERE Id=OLD.MonitorId; WHERE Id=OLD.MonitorId;
END IF; END IF;
END IF; END IF;
ELSE IF ( NEW.Archived AND diff ) THEN ELSEIF ( NEW.Archived AND diff ) THEN
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
END IF; END IF;
@ -256,10 +189,10 @@ FOR EACH ROW
INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
UPDATE Monitors SET UPDATE Monitors SET
HourEvents = COALESCE(DayEvents,0)+1, HourEvents = COALESCE(HourEvents,0)+1,
DayEvents = COALESCE(DayEvents,0)+1, DayEvents = COALESCE(DayEvents,0)+1,
WeekEvents = COALESCE(DayEvents,0)+1, WeekEvents = COALESCE(WeekEvents,0)+1,
MonthEvents = COALESCE(DayEvents,0)+1, MonthEvents = COALESCE(MonthEvents,0)+1,
TotalEvents = COALESCE(TotalEvents,0)+1 TotalEvents = COALESCE(TotalEvents,0)+1
WHERE Id=NEW.MonitorId; WHERE Id=NEW.MonitorId;
END; END;
@ -271,7 +204,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) - OLD.DiskSpace 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,
@ -228,40 +229,9 @@ CREATE TABLE `Events_Hour` (
KEY `Events_Hour_StartTime_idx` (`StartTime`) KEY `Events_Hour_StartTime_idx` (`StartTime`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
delimiter //
DROP TRIGGER IF EXISTS Events_Hour_delete_trigger//
CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour
FOR EACH ROW BEGIN
UPDATE Monitors SET
HourEvents = COALESCE(HourEvents,1)-1,
HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
WHERE Id=OLD.MonitorId;
END;
//
DROP TRIGGER IF EXISTS Events_Hour_update_trigger//
CREATE TRIGGER Events_Hour_update_trigger AFTER UPDATE ON Events_Hour
FOR EACH ROW
BEGIN
declare diff BIGINT default 0;
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( diff ) THEN
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
ELSE
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
END IF;
END IF;
END;
//
DELIMITER ;
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,
@ -270,40 +240,9 @@ CREATE TABLE `Events_Day` (
KEY `Events_Day_StartTime_idx` (`StartTime`) KEY `Events_Day_StartTime_idx` (`StartTime`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
delimiter //
DROP TRIGGER IF EXISTS Events_Day_delete_trigger//
CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day
FOR EACH ROW BEGIN
UPDATE Monitors SET
DayEvents = COALESCE(DayEvents,1)-1,
DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
WHERE Id=OLD.MonitorId;
END;
//
DROP TRIGGER IF EXISTS Events_Day_update_trigger;
CREATE TRIGGER Events_Day_update_trigger AFTER UPDATE ON Events_Day
FOR EACH ROW
BEGIN
declare diff BIGINT default 0;
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( diff ) THEN
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
ELSE
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
END IF;
END IF;
END;
//
DELIMITER ;
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,
@ -312,40 +251,9 @@ CREATE TABLE `Events_Week` (
KEY `Events_Week_StartTime_idx` (`StartTime`) KEY `Events_Week_StartTime_idx` (`StartTime`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
delimiter //
DROP TRIGGER IF EXISTS Events_Week_delete_trigger//
CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week
FOR EACH ROW BEGIN
UPDATE Monitors SET
WeekEvents = COALESCE(WeekEvents,1)-1,
WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
WHERE Id=OLD.MonitorId;
END;
//
DROP TRIGGER IF EXISTS Events_Week_update_trigger;
CREATE TRIGGER Events_Week_update_trigger AFTER UPDATE ON Events_Week
FOR EACH ROW
BEGIN
declare diff BIGINT default 0;
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( diff ) THEN
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
ELSE
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
END IF;
END IF;
END;
//
DELIMITER ;
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,
@ -354,177 +262,16 @@ CREATE TABLE `Events_Month` (
KEY `Events_Month_StartTime_idx` (`StartTime`) KEY `Events_Month_StartTime_idx` (`StartTime`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
delimiter //
DROP TRIGGER IF EXISTS Events_Month_delete_trigger//
CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month
FOR EACH ROW BEGIN
UPDATE Monitors SET
MonthEvents = COALESCE(MonthEvents,1)-1,
MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
WHERE Id=OLD.MonitorId;
END;
//
DROP TRIGGER IF EXISTS Events_Month_update_trigger;
CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month
FOR EACH ROW
BEGIN
declare diff BIGINT default 0;
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( diff ) THEN
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace) WHERE Monitors.Id=OLD.MonitorId;
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId;
ELSE
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
END IF;
END IF;
END;
//
DELIMITER ;
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`),
KEY `Events_Archived_MonitorId_idx` (`MonitorId`) KEY `Events_Archived_MonitorId_idx` (`MonitorId`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
drop procedure if exists update_storage_stats;
delimiter //
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//
CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events
FOR EACH ROW
BEGIN
declare diff BIGINT default 0;
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( NEW.StorageId = OLD.StorageID ) THEN
IF ( diff ) THEN
call update_storage_stats(OLD.StorageId, diff);
END IF;
ELSE
IF ( NEW.DiskSpace ) THEN
call update_storage_stats(NEW.StorageId, NEW.DiskSpace);
END IF;
IF ( OLD.DiskSpace ) THEN
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
END IF;
END IF;
UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
IF ( NEW.Archived != OLD.Archived ) THEN
IF ( NEW.Archived ) THEN
INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace);
UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId;
ELSEIF ( OLD.Archived ) THEN
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)-1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) WHERE Id=OLD.MonitorId;
ELSE
IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Monitors SET
ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0)
WHERE Id=OLD.MonitorId;
END IF;
END IF;
ELSE IF ( NEW.Archived AND diff ) THEN
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
END IF;
IF ( diff ) THEN
UPDATE Monitors SET TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=OLD.MonitorId;
END IF;
END;
//
delimiter ;
DROP TRIGGER IF EXISTS event_insert_trigger;
delimiter //
/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count.
* The DiskSpace will get update in the Event Update Trigger
*/
CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events
FOR EACH ROW
BEGIN
INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
UPDATE Monitors SET
HourEvents = COALESCE(DayEvents,0)+1,
DayEvents = COALESCE(DayEvents,0)+1,
WeekEvents = COALESCE(DayEvents,0)+1,
MonthEvents = COALESCE(DayEvents,0)+1,
TotalEvents = COALESCE(TotalEvents,0)+1
WHERE Id=NEW.MonitorId;
END;
//
DROP TRIGGER IF EXISTS event_delete_trigger//
CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events
FOR EACH ROW
BEGIN
IF ( OLD.DiskSpace ) THEN
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
END IF;
DELETE FROM Events_Hour WHERE EventId=OLD.Id;
DELETE FROM Events_Day WHERE EventId=OLD.Id;
DELETE FROM Events_Week WHERE EventId=OLD.Id;
DELETE FROM Events_Month WHERE EventId=OLD.Id;
IF ( OLD.Archived ) THEN
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
UPDATE Monitors SET
ArchivedEvents = COALESCE(ArchivedEvents,1) - 1,
ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),
TotalEvents = COALESCE(TotalEvents,1) - 1,
TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0)
WHERE Id=OLD.MonitorId;
ELSE
UPDATE Monitors SET
TotalEvents = COALESCE(TotalEvents,1)-1,
TotalEventDiskSpace=COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
WHERE Id=OLD.MonitorId;
END IF;
END;
//
delimiter ;
-- --
-- Table structure for table `Filters` -- Table structure for table `Filters`
-- --
@ -557,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,
@ -756,18 +503,18 @@ CREATE TABLE `Monitors` (
`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',
`Sequence` smallint(5) unsigned default NULL, `Sequence` smallint(5) unsigned default NULL,
`TotalEvents` int(10) unsigned, `TotalEvents` int(10) default NULL,
`TotalEventDiskSpace` bigint unsigned, `TotalEventDiskSpace` bigint default NULL,
`HourEvents` int(10) unsigned, `HourEvents` int(10) default NULL,
`HourEventDiskSpace` bigint unsigned, `HourEventDiskSpace` bigint default NULL,
`DayEvents` int(10) unsigned, `DayEvents` int(10) default NULL,
`DayEventDiskSpace` bigint unsigned, `DayEventDiskSpace` bigint default NULL,
`WeekEvents` int(10) unsigned, `WeekEvents` int(10) default NULL,
`WeekEventDiskSpace` bigint unsigned, `WeekEventDiskSpace` bigint default NULL,
`MonthEvents` int(10) unsigned, `MonthEvents` int(10) default NULL,
`MonthEventDiskSpace` bigint unsigned, `MonthEventDiskSpace` bigint default NULL,
`ArchivedEvents` int(10) unsigned, `ArchivedEvents` int(10) default NULL,
`ArchivedEventDiskSpace` bigint unsigned, `ArchivedEventDiskSpace` bigint default NULL,
`ZoneCount` TINYINT NOT NULL DEFAULT 0, `ZoneCount` TINYINT NOT NULL DEFAULT 0,
PRIMARY KEY (`Id`) PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
@ -949,33 +696,17 @@ CREATE TABLE `Zones` (
KEY `MonitorId` (`MonitorId`) KEY `MonitorId` (`MonitorId`)
) ENGINE=@ZM_MYSQL_ENGINE@; ) ENGINE=@ZM_MYSQL_ENGINE@;
DELIMITER //
DROP TRIGGER IF EXISTS Zone_Insert_Trigger//
CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones
FOR EACH ROW
BEGIN
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID;
END
//
DROP TRIGGER IF EXISTS Zone_Delete_Trigger//
CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones
FOR EACH ROW
BEGIN
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID;
END
//
DELIMITER ;
DROP TABLE IF EXISTS `Storage`; DROP TABLE IF EXISTS `Storage`;
CREATE TABLE `Storage` ( CREATE TABLE `Storage` (
`Id` smallint(5) unsigned NOT NULL auto_increment, `Id` smallint(5) unsigned NOT NULL auto_increment,
`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@;
@ -1040,6 +771,10 @@ INSERT INTO `Controls` VALUES (NULL,'Keekoon','Remote','Keekoon', 0, 0, 0, 0, 0,
INSERT INTO `Controls` VALUES (NULL,'HikVision','Local','',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,1,20,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,100,0,0,1,0,0,0,0,1,1,100,1,0,0,0); INSERT INTO `Controls` VALUES (NULL,'HikVision','Local','',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,1,20,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,100,0,0,1,0,0,0,0,1,1,100,1,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Maginon Supra IPC','cURL','MaginonIPC',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,4,0,1,1,1,0,0,1,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,'Maginon Supra IPC','cURL','MaginonIPC',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,4,0,1,1,1,0,0,1,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,'Floureon 1080P','Ffmpeg','Floureon',0,0,0,1,0,0,0,1,1,18,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1,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,1,20,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'Floureon 1080P','Ffmpeg','Floureon',0,0,0,1,0,0,0,1,1,18,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1,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,1,20,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,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-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
@ -1138,7 +873,7 @@ CREATE TABLE Maps (
PRIMARY KEY (`Id`) PRIMARY KEY (`Id`)
); );
DROP TABLE IF EXISTS MontageLayout; DROP TABLE IF EXISTS MontageLayouts;
CREATE TABLE MontageLayouts ( CREATE TABLE MontageLayouts (
`Id` int(10) unsigned NOT NULL auto_increment, `Id` int(10) unsigned NOT NULL auto_increment,
@ -1155,7 +890,7 @@ INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('4 Wide', '{ "default":{
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('5 Wide', '{ "default":{"float":"left", "width":"19%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' ); INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('5 Wide', '{ "default":{"float":"left", "width":"19%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' );
-- We generally don't alter triggers, we drop and re-create them, so let's keep them in a separate file that we can just source in update scripts. -- We generally don't alter triggers, we drop and re-create them, so let's keep them in a separate file that we can just source in update scripts.
source triggers.sql source @ZM_PATH_DATA@/db/triggers.sql
-- --
-- Apply the initial configuration -- Apply the initial configuration
-- --

View File

@ -145,4 +145,4 @@ WeekEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.I
MonthEvents=(SELECT COUNT(Id) FROM Events WHERE MonitorId=Monitors.Id AND StartTime > DATE_SUB( NOW(), INTERVAL 1 month)), MonthEvents=(SELECT COUNT(Id) FROM Events WHERE MonitorId=Monitors.Id AND StartTime > DATE_SUB( NOW(), INTERVAL 1 month)),
MonthEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.Id AND StartTime > DATE_SUB(NOW(), INTERVAL 1 month) AND DiskSpace IS NOT NULL), MonthEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.Id AND StartTime > DATE_SUB(NOW(), INTERVAL 1 month) AND DiskSpace IS NOT NULL),
ArchivedEvents=(SELECT COUNT(Id) FROM Events WHERE MonitorId=Monitors.Id AND Archived=1), ArchivedEvents=(SELECT COUNT(Id) FROM Events WHERE MonitorId=Monitors.Id AND Archived=1),
ArchivedEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.Id AND Archived=1 AND DiskSpace IS NOT NULL) ArchivedEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.Id AND Archived=1 AND DiskSpace IS NOT NULL);

View File

@ -176,7 +176,7 @@ BEGIN
WHERE Id=OLD.MonitorId; WHERE Id=OLD.MonitorId;
END IF; END IF;
END IF; END IF;
ELSE IF ( NEW.Archived AND diff ) THEN ELSEIF ( NEW.Archived AND diff ) THEN
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id; UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
END IF; END IF;
@ -205,10 +205,10 @@ FOR EACH ROW
INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0); INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
UPDATE Monitors SET UPDATE Monitors SET
HourEvents = COALESCE(DayEvents,0)+1, HourEvents = COALESCE(HourEvents,0)+1,
DayEvents = COALESCE(DayEvents,0)+1, DayEvents = COALESCE(DayEvents,0)+1,
WeekEvents = COALESCE(DayEvents,0)+1, WeekEvents = COALESCE(WeekEvents,0)+1,
MonthEvents = COALESCE(DayEvents,0)+1, MonthEvents = COALESCE(MonthEvents,0)+1,
TotalEvents = COALESCE(TotalEvents,0)+1 TotalEvents = COALESCE(TotalEvents,0)+1
WHERE Id=NEW.MonitorId; WHERE Id=NEW.MonitorId;
END; END;
@ -260,3 +260,17 @@ FOR EACH ROW
// //
DELIMITER ; DELIMITER ;
UPDATE Monitors SET
TotalEvents=(SELECT COUNT(*) FROM Events WHERE MonitorId=Monitors.Id),
TotalEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events WHERE MonitorId=Monitors.Id AND DiskSpace IS NOT NULL),
HourEvents=(SELECT COUNT(*) FROM Events_Hour WHERE MonitorId=Monitors.Id),
HourEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events_Hour WHERE MonitorId=Monitors.Id AND DiskSpace IS NOT NULL),
DayEvents=(SELECT COUNT(*) FROM Events_Day WHERE MonitorId=Monitors.Id),
DayEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events_Day WHERE MonitorId=Monitors.Id AND DiskSpace IS NOT NULL),
WeekEvents=(SELECT COUNT(Id) FROM Events_Week WHERE MonitorId=Monitors.Id),
WeekEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events_Week WHERE MonitorId=Monitors.Id AND DiskSpace IS NOT NULL),
MonthEvents=(SELECT COUNT(Id) FROM Events_Month WHERE MonitorId=Monitors.Id),
MonthEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events_Month WHERE MonitorId=Monitors.Id AND DiskSpace IS NOT NULL),
ArchivedEvents=(SELECT COUNT(Id) FROM Events_Archived WHERE MonitorId=Monitors.Id),
ArchivedEventDiskSpace=(SELECT SUM(DiskSpace) FROM Events_Archived WHERE MonitorId=Monitors.Id AND DiskSpace IS NOT NULL);

275
db/zm_update-1.31.39.sql Normal file
View File

@ -0,0 +1,275 @@
ALTER TABLE `Monitors` MODIFY `HourEvents` INT(10) DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `HourEventDiskSpace` BIGINT DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `DayEvents` INT(10) DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `DayEventDiskSpace` BIGINT DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `WeekEvents` INT(10) DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `WeekEventDiskSpace` BIGINT DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `MonthEvents` INT(10) DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `MonthEventDiskSpace` BIGINT DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `ArchivedEvents` INT(10) DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `ArchivedEventDiskSpace` BIGINT DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `TotalEvents` INT(10) DEFAULT NULL;
ALTER TABLE `Monitors` MODIFY `TotalEventDiskSpace` BIGINT DEFAULT NULL;
delimiter //
DROP TRIGGER IF EXISTS Events_Hour_delete_trigger//
CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour
FOR EACH ROW BEGIN
UPDATE Monitors SET
HourEvents = COALESCE(HourEvents,1)-1,
HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
WHERE Id=OLD.MonitorId;
END;
//
DROP TRIGGER IF EXISTS Events_Hour_update_trigger//
CREATE TRIGGER Events_Hour_update_trigger AFTER UPDATE ON Events_Hour
FOR EACH ROW
BEGIN
declare diff BIGINT default 0;
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( diff ) THEN
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
ELSE
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
END IF;
END IF;
END;
//
DELIMITER ;
delimiter //
DROP TRIGGER IF EXISTS Events_Day_delete_trigger//
CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day
FOR EACH ROW BEGIN
UPDATE Monitors SET
DayEvents = COALESCE(DayEvents,1)-1,
DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
WHERE Id=OLD.MonitorId;
END;
//
DROP TRIGGER IF EXISTS Events_Day_update_trigger;
CREATE TRIGGER Events_Day_update_trigger AFTER UPDATE ON Events_Day
FOR EACH ROW
BEGIN
declare diff BIGINT default 0;
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( diff ) THEN
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
ELSE
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
END IF;
END IF;
END;
//
delimiter //
DROP TRIGGER IF EXISTS Events_Week_delete_trigger//
CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week
FOR EACH ROW BEGIN
UPDATE Monitors SET
WeekEvents = COALESCE(WeekEvents,1)-1,
WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
WHERE Id=OLD.MonitorId;
END;
//
DROP TRIGGER IF EXISTS Events_Week_update_trigger;
CREATE TRIGGER Events_Week_update_trigger AFTER UPDATE ON Events_Week
FOR EACH ROW
BEGIN
declare diff BIGINT default 0;
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( diff ) THEN
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
ELSE
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
END IF;
END IF;
END;
//
DELIMITER ;
delimiter //
DROP TRIGGER IF EXISTS Events_Month_delete_trigger//
CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month
FOR EACH ROW BEGIN
UPDATE Monitors SET
MonthEvents = COALESCE(MonthEvents,1)-1,
MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
WHERE Id=OLD.MonitorId;
END;
//
DROP TRIGGER IF EXISTS Events_Month_update_trigger;
CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month
FOR EACH ROW
BEGIN
declare diff BIGINT default 0;
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( diff ) THEN
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace) WHERE Monitors.Id=OLD.MonitorId;
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId;
ELSE
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
END IF;
END IF;
END;
//
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//
CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events
FOR EACH ROW
BEGIN
declare diff BIGINT default 0;
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
IF ( NEW.StorageId = OLD.StorageID ) THEN
IF ( diff ) THEN
call update_storage_stats(OLD.StorageId, diff);
END IF;
ELSE
IF ( NEW.DiskSpace ) THEN
call update_storage_stats(NEW.StorageId, NEW.DiskSpace);
END IF;
IF ( OLD.DiskSpace ) THEN
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
END IF;
END IF;
UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
IF ( NEW.Archived != OLD.Archived ) THEN
IF ( NEW.Archived ) THEN
INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace);
UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId;
ELSEIF ( OLD.Archived ) THEN
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)-1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) WHERE Id=OLD.MonitorId;
ELSE
IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
UPDATE Monitors SET
ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0)
WHERE Id=OLD.MonitorId;
END IF;
END IF;
ELSEIF ( NEW.Archived AND diff ) THEN
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
END IF;
IF ( diff ) THEN
UPDATE Monitors SET TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=OLD.MonitorId;
END IF;
END;
//
delimiter ;
DROP TRIGGER IF EXISTS event_insert_trigger;
delimiter //
/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count.
* The DiskSpace will get update in the Event Update Trigger
*/
CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events
FOR EACH ROW
BEGIN
INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
UPDATE Monitors SET
HourEvents = COALESCE(HourEvents,0)+1,
DayEvents = COALESCE(DayEvents,0)+1,
WeekEvents = COALESCE(WeekEvents,0)+1,
MonthEvents = COALESCE(MonthEvents,0)+1,
TotalEvents = COALESCE(TotalEvents,0)+1
WHERE Id=NEW.MonitorId;
END;
//
DROP TRIGGER IF EXISTS event_delete_trigger//
CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events
FOR EACH ROW
BEGIN
IF ( OLD.DiskSpace ) THEN
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
END IF;
DELETE FROM Events_Hour WHERE EventId=OLD.Id;
DELETE FROM Events_Day WHERE EventId=OLD.Id;
DELETE FROM Events_Week WHERE EventId=OLD.Id;
DELETE FROM Events_Month WHERE EventId=OLD.Id;
IF ( OLD.Archived ) THEN
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
UPDATE Monitors SET
ArchivedEvents = COALESCE(ArchivedEvents,1) - 1,
ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),
TotalEvents = COALESCE(TotalEvents,1) - 1,
TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0)
WHERE Id=OLD.MonitorId;
ELSE
UPDATE Monitors SET
TotalEvents = COALESCE(TotalEvents,1)-1,
TotalEventDiskSpace=COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
WHERE Id=OLD.MonitorId;
END IF;
END;
//
DROP TRIGGER IF EXISTS Zone_Insert_Trigger//
CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones
FOR EACH ROW
BEGIN
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID;
END
//
DROP TRIGGER IF EXISTS Zone_Delete_Trigger//
CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones
FOR EACH ROW
BEGIN
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID;
END
//
DELIMITER ;

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;

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

@ -0,0 +1,32 @@
ALTER TABLE `Frames` MODIFY `Id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT;
ALTER TABLE `Events` MODIFY `Id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT;
ALTER TABLE `Frames` MODIFY `EventId` BIGINT UNSIGNED NOT NULL;
ALTER TABLE `Stats` MODIFY `EventId` BIGINT UNSIGNED NOT NULL;
ALTER TABLE `Events_Hour` MODIFY `EventId` BIGINT UNSIGNED NOT NULL;
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_TYPE='FOREIGN KEY'
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`"
));
ALTER TABLE `Events_Hour` ADD CONSTRAINT Events_Hour_EventId_fk FOREIGN KEY (`EventId`) REFERENCES `Events`(`Id`) ON DELETE CASCADE;
ALTER TABLE `Events_Day` MODIFY `EventId` BIGINT UNSIGNED NOT NULL;
ALTER TABLE `Events_Day` ADD CONSTRAINT Events_Day_EventId_fk FOREIGN KEY (`EventId`) REFERENCES `Events`(`Id`) ON DELETE CASCADE;
ALTER TABLE `Events_Week` MODIFY `EventId` BIGINT UNSIGNED NOT NULL;
ALTER TABLE `Events_Week` ADD CONSTRAINT Events_Week_EventId_fk FOREIGN KEY (`EventId`) REFERENCES `Events`(`Id`) ON DELETE CASCADE;
ALTER TABLE `Events_Month` MODIFY `EventId` BIGINT UNSIGNED NOT NULL;
ALTER TABLE `Events_Month` ADD CONSTRAINT Events_Month_EventId_fk FOREIGN KEY (`EventId`) REFERENCES `Events`(`Id`) ON DELETE CASCADE;
ALTER TABLE `Events_Archived` MODIFY `EventId` BIGINT UNSIGNED NOT NULL;
ALTER TABLE `Events_Archived` ADD CONSTRAINT Events_Archived_EventId_fk FOREIGN KEY (`EventId`) REFERENCES `Events`(`Id`) ON DELETE CASCADE;
ALTER TABLE `Stats` ADD CONSTRAINT Stats_MonitorId_fk FOREIGN KEY (`MonitorId`) REFERENCES `Monitors`(`Id`) ON DELETE CASCADE;
ALTER TABLE `Stats` ADD CONSTRAINT Stats_EventId_fk FOREIGN KEY (`EventId`) REFERENCES `Events`(`Id`) ON DELETE CASCADE;
ALTER TABLE `Stats` ADD CONSTRAINT Stats_ZoneId_fk FOREIGN KEY (`ZoneId`) REFERENCES `Zones`(`Id`) ON DELETE CASCADE;
ALTER TABLE `Frames` ADD CONSTRAINT Frames_EventId_fk FOREIGN KEY (`EventId`) REFERENCES `Events`(`Id`) ON DELETE CASCADE;
ALTER TABLE `Frames` DROP INDEX `Type`;

View File

@ -5,6 +5,9 @@ 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
. $i
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

View File

@ -83,6 +83,10 @@ BuildRequires: libcurl-devel
BuildRequires: libv4l-devel BuildRequires: libv4l-devel
BuildRequires: ffmpeg-devel BuildRequires: ffmpeg-devel
# 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}
%{?with_nginx:Requires: php-fpm} %{?with_nginx:Requires: php-fpm}

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

@ -5,6 +5,10 @@ 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
. $i
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

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

@ -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
,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
@ -61,7 +61,9 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libdigest-sha-perl ,libdigest-sha-perl
,libsys-cpu-perl, libsys-cpuload-perl, libsys-meminfo-perl ,libsys-cpu-perl, libsys-cpuload-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

@ -66,9 +66,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

@ -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,33 +1,55 @@
#! /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 CONFFILE in /etc/zm/conf.d/*.conf; do
. "$CONFFILE"
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/* /usr/share/zoneminder/www/cache
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
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
@ -37,26 +59,21 @@ if [ "$1" = "configure" ]; then
else else
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
zmupdate.pl --nointeractive
zmupdate.pl --nointeractive -f
# Add any new PTZ control configurations to the database (will not overwrite)
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
#DEBHELPER# #DEBHELPER#

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:
@ -44,7 +46,7 @@ Clone the ZoneMinder project if you have not done so already.
:: ::
git clone https://ZoneMinder/ZoneMinder git clone https://github.com/ZoneMinder/ZoneMinder
cd ZoneMinder cd ZoneMinder
Alternatively, if you have already cloned the repo and wish to update it, do the following. Alternatively, if you have already cloned the repo and wish to update it, do 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

@ -0,0 +1,6 @@
check process zmdc.pl with pidfile /run/zm/zm.pid
if failed unixsocket /run/zm/zmdc2.sock then restart
group zm
start program = "/bin/systemctl start zoneminder"
stop program = "/bin/systemctl stop zoneminder"
#if 4 restarts within 20 cycles then timeout

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.

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

@ -0,0 +1,603 @@
# ==========================================================================
#
# ZoneMinder Reolink IP Control Protocol Module, Date: 2016-01-19
# Converted for use with Reolink IP Camera by Chris Swertfeger
# Copyright (C) 2016 Chris Swertfeger
#
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This module contains the first implementation of the Reolink IP camera control
# protocol
#
package ZoneMinder::Control::Reolink;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Control;
our @ISA = qw(ZoneMinder::Control);
our %CamParams = ();
# ==========================================================================
#
# Reolink IP Control Protocol
# This script sends ONVIF compliant commands and may work with other cameras
# that require authentication
#
# The script was developed against a RLC-423 and RLC-420.
#
# Basic preset functions are supported, but more advanced features, which make
# use of abnormally high preset numbers (ir lamp control, tours, pan speed, etc)
# may or may not work.
#
#
# On ControlAddress use the format :
# USERNAME:PASSWORD@ADDRESS:PORT
# eg : admin:pass@10.1.2.1:8899
# admin:password@10.0.100.1:8899
#
# Use port 8000 by default for Reolink cameras
#
# Make sure and place a value in the Auto Stop Timeout field.
# Recommend starting with a value of 1 second, and adjust accordingly.
#
# ==========================================================================
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep );
use MIME::Base64;
use Digest::SHA;
use DateTime;
my ($username,$password,$host,$port);
sub open
{
my $self = shift;
$self->loadMonitor();
#
# Extract the username/password host/port from ControlAddress
#
if( $self->{Monitor}{ControlAddress} =~ /^([^:]+):([^@]+)@(.+)/ )
{ # user:pass@host...
$username = $1;
$password = $2;
$host = $3;
}
elsif( $self->{Monitor}{ControlAddress} =~ /^([^@]+)@(.+)/ )
{ # user@host...
$username = $1;
$host = $2;
}
else { # Just a host
$host = $self->{Monitor}{ControlAddress};
}
# Check if it is a host and port or just a host
if( $host =~ /([^:]+):(.+)/ )
{
$host = $1;
$port = $2;
}
else
{
$port = 80;
}
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 $msg = shift;
my $content_type = shift;
my $result = undef;
printMsg( $cmd, "Tx" );
my $server_endpoint = "http://".$host.":".$port."/$cmd";
my $req = HTTP::Request->new( POST => $server_endpoint );
$req->header('content-type' => $content_type);
$req->header('Host' => $host.":".$port);
$req->header('content-length' => length($msg));
$req->header('accept-encoding' => 'gzip, deflate');
$req->header('connection' => 'close');
$req->content($msg);
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
$result = !undef;
} else {
Error( "After sending PTZ command, camera returned the following error:'".$res->status_line()."'" );
}
return( $result );
}
sub getCamParams
{
my $self = shift;
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken></GetImagingSettings></s:Body></s:Envelope>';
my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/onvif/imaging";
my $req = HTTP::Request->new( POST => $server_endpoint );
$req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"');
$req->header('Host' => $host.":".$port);
$req->header('content-length' => length($msg));
$req->header('accept-encoding' => 'gzip, deflate');
$req->header('connection' => 'Close');
$req->content($msg);
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
# We should really use an xml or soap library to parse the xml tags
my $content = $res->decoded_content;
if ($content =~ /.*<tt:(Brightness)>(.+)<\/tt:Brightness>.*/) {
$CamParams{$1} = $2;
}
if ($content =~ /.*<tt:(Contrast)>(.+)<\/tt:Contrast>.*/) {
$CamParams{$1} = $2;
}
}
else
{
Error( "Unable to retrieve camera image settings:'".$res->status_line()."'" );
}
}
#autoStop
#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
sub autoStop
{
my $self = shift;
my $autostop = shift;
if( $autostop ) {
Debug( "Auto Stop" );
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
usleep( $autostop );
$self->sendCmd( $cmd, $msg, $content_type );
}
}
# Reset the Camera
sub reset
{
Debug( "Camera Reset" );
my $self = shift;
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $cmd = "";
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"';
$self->sendCmd( $cmd, $msg, $content_type );
}
#Up Arrow
sub moveConUp
{
Debug( "Move Up" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Down Arrow
sub moveConDown
{
Debug( "Move Down" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Left Arrow
sub moveConLeft
{
Debug( "Move Left" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Right Arrow
sub moveConRight
{
Debug( "Move Right" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Zoom In
sub zoomConTele
{
Debug( "Zoom Tele" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Zoom Out
sub zoomConWide
{
Debug( "Zoom Wide" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="-0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Diagonally Up Right Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConUpRight
{
Debug( "Move Diagonally Up Right" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Diagonally Down Right Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConDownRight
{
Debug( "Move Diagonally Down Right" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Diagonally Up Left Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConUpLeft
{
Debug( "Move Diagonally Up Left" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Diagonally Down Left Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConDownLeft
{
Debug( "Move Diagonally Down Left" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
}
#Stop
sub moveStop
{
Debug( "Move Stop" );
my $self = shift;
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd( $cmd, $msg, $content_type );
}
#Set Camera Preset
sub presetSet
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Set Preset $preset" );
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetPreset xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PresetToken>'.$preset.'</PresetToken></SetPreset></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/SetPreset"';
$self->sendCmd( $cmd, $msg, $content_type );
}
#Recall Camera Preset
sub presetGoto
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
my $num = sprintf("%03d", $preset);
$num=~ tr/ /0/;
Debug( "Goto Preset $preset" );
my $cmd = 'onvif/PTZ';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GotoPreset xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PresetToken>'.$num.'</PresetToken></GotoPreset></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"';
$self->sendCmd( $cmd, $msg, $content_type );
}
#Horizontal Patrol
#To be determined if this camera supports this feature
sub horizontalPatrol
{
Debug( "Horizontal Patrol" );
my $self = shift;
my $cmd = '';
my $msg ='';
my $content_type = '';
# $self->sendCmd( $cmd, $msg, $content_type );
Error( "PTZ Command not implemented in control script." );
}
#Horizontal Patrol Stop
#To be determined if this camera supports this feature
sub horizontalPatrolStop
{
Debug( "Horizontal Patrol Stop" );
my $self = shift;
my $cmd = '';
my $msg ='';
my $content_type = '';
# $self->sendCmd( $cmd, $msg, $content_type );
Error( "PTZ Command not implemented in control script." );
}
# Increase Brightness
sub irisAbsOpen
{
Debug( "Iris $CamParams{'Brightness'}" );
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'Brightness'});
my $step = $self->getParam( $params, 'step' );
my $max = 100;
$CamParams{'Brightness'} += $step;
$CamParams{'Brightness'} = $max if ($CamParams{'Brightness'} > $max);
my $cmd = 'onvif/imaging';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Brightness'}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
$self->sendCmd( $cmd, $msg, $content_type );
}
# Decrease Brightness
sub irisAbsClose
{
Debug( "Iris $CamParams{'Brightness'}" );
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'brightness'});
my $step = $self->getParam( $params, 'step' );
my $min = 0;
$CamParams{'Brightness'} -= $step;
$CamParams{'Brightness'} = $min if ($CamParams{'Brightness'} < $min);
my $cmd = 'onvif/imaging';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Brightness'}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
$self->sendCmd( $cmd, $msg, $content_type );
}
# Increase Contrast
sub whiteAbsIn
{
Debug( "Iris $CamParams{'Contrast'}" );
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'Contrast'});
my $step = $self->getParam( $params, 'step' );
my $max = 100;
$CamParams{'Contrast'} += $step;
$CamParams{'Contrast'} = $max if ($CamParams{'Contrast'} > $max);
my $cmd = 'onvif/imaging';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Contrast'}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
}
# Decrease Contrast
sub whiteAbsOut
{
Debug( "Iris $CamParams{'Contrast'}" );
my $self = shift;
my $params = shift;
$self->getCamParams() unless($CamParams{'Contrast'});
my $step = $self->getParam( $params, 'step' );
my $min = 0;
$CamParams{'Contrast'} -= $step;
$CamParams{'Contrast'} = $min if ($CamParams{'Contrast'} < $min);
my $cmd = 'onvif/imaging';
my $nonce;
for (0..20){$nonce .= chr(int(rand(254)));}
my $mydate = DateTime->now()->iso8601().'Z';
my $sha = Digest::SHA->new(1);
$sha->add($nonce.$mydate.$password);
my $digest = encode_base64($sha->digest,"");
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Contrast'}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
}
1;

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 frames, stats and files 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' ) {
@ -425,6 +462,9 @@ sub delete_files {
} # end sub delete_files } # end sub delete_files
sub Storage { sub Storage {
if ( @_ > 1 ) {
$_[0]{Storage} = $_[1];
}
if ( ! $_[0]{Storage} ) { if ( ! $_[0]{Storage} ) {
$_[0]{Storage} = new ZoneMinder::Storage( $_[0]{StorageId} ); $_[0]{Storage} = new ZoneMinder::Storage( $_[0]{StorageId} );
} }
@ -486,8 +526,7 @@ sub MoveTo {
$ZoneMinder::Database::dbh->commit(); $ZoneMinder::Database::dbh->commit();
return "Event has already been moved by someone else."; return "Event has already been moved by someone else.";
} }
my $OldStorage = $self->Storage(undef);
my $OldStorage = $self->Storage();
my ( $OldPath ) = ( $self->Path() =~ /^(.*)$/ ); # De-taint my ( $OldPath ) = ( $self->Path() =~ /^(.*)$/ ); # De-taint
$$self{Storage} = $NewStorage; $$self{Storage} = $NewStorage;
@ -513,33 +552,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
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();
@ -554,8 +653,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

@ -205,7 +205,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 +276,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

@ -1,4 +1,4 @@
# ========================================================================== ############################################################################
# #
# ZoneMinder Logger Module, $Date$, $Revision$ # ZoneMinder Logger Module, $Date$, $Revision$
# Copyright (C) 2001-2008 Philip Coombes # Copyright (C) 2001-2008 Philip Coombes
@ -17,7 +17,7 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# #
# ========================================================================== ############################################################################
# #
# This module contains the debug definitions and functions used by the rest # This module contains the debug definitions and functions used by the rest
# of the ZoneMinder scripts # of the ZoneMinder scripts
@ -75,17 +75,17 @@ our %EXPORT_TAGS = (
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } );
our @EXPORT = qw(); our @EXPORT = qw();
our $VERSION = $ZoneMinder::Base::VERSION; our $VERSION = $ZoneMinder::Base::VERSION;
# ========================================================================== ############################################################################
# #
# Logger Facilities # Logger Facilities
# #
# ========================================================================== ############################################################################
use ZoneMinder::Config qw(:all); use ZoneMinder::Config qw(:all);
@ -135,18 +135,19 @@ sub new {
$this->{initialised} = undef; $this->{initialised} = undef;
#$this->{id} = 'zmundef';
( $this->{id} ) = $0 =~ m|^(?:.*/)?([^/]+?)(?:\.[^/.]+)?$|; ( $this->{id} ) = $0 =~ m|^(?:.*/)?([^/]+?)(?:\.[^/.]+)?$|;
$this->{idRoot} = $this->{id}; $this->{idRoot} = $this->{id};
$this->{idArgs} = ''; $this->{idArgs} = '';
$this->{level} = INFO; $this->{level} = INFO;
# Detect if we are running in a terminal session, if so, default log level to INFO
$this->{hasTerm} = -t STDERR; $this->{hasTerm} = -t STDERR;
if ( $this->{hasTerm} ) { if ( $this->{hasTerm} ) {
$this->{termLevel} = INFO; $this->{termLevel} = INFO;
} else { } else {
$this->{termLevel} = NOLOG; $this->{termLevel} = NOLOG;
} }
$this->{databaseLevel} = NOLOG; $this->{databaseLevel} = NOLOG;
$this->{fileLevel} = NOLOG; $this->{fileLevel} = NOLOG;
$this->{syslogLevel} = NOLOG; $this->{syslogLevel} = NOLOG;
@ -156,7 +157,7 @@ if ( $this->{hasTerm} ) {
( $this->{fileName} = $0 ) =~ s|^.*/||; ( $this->{fileName} = $0 ) =~ s|^.*/||;
$this->{logPath} = $Config{ZM_PATH_LOGS}; $this->{logPath} = $Config{ZM_PATH_LOGS};
$this->{logFile} = $this->{logPath}.'/'.$this->{id}.".log"; $this->{logFile} = $this->{logPath}.'/'.$this->{id}.'.log';
$this->{trace} = 0; $this->{trace} = 0;
@ -175,9 +176,9 @@ sub BEGIN {
ZM_LOG_LEVEL_FILE => 0, ZM_LOG_LEVEL_FILE => 0,
ZM_LOG_LEVEL_SYSLOG => 0, ZM_LOG_LEVEL_SYSLOG => 0,
ZM_LOG_DEBUG => 0, ZM_LOG_DEBUG => 0,
ZM_LOG_DEBUG_TARGET => "", ZM_LOG_DEBUG_TARGET => '',
ZM_LOG_DEBUG_LEVEL => 1, ZM_LOG_DEBUG_LEVEL => 1,
ZM_LOG_DEBUG_FILE => "" ZM_LOG_DEBUG_FILE => ''
); );
while ( my ( $name, $value ) = each( %dbgConfig ) ) { while ( my ( $name, $value ) = each( %dbgConfig ) ) {
*{$name} = sub { $value }; *{$name} = sub { $value };
@ -196,13 +197,13 @@ sub initialise( @ ) {
my $this = shift; my $this = shift;
my %options = @_; my %options = @_;
$this->{id} = $options{id} if ( defined($options{id}) ); $this->{id} = $options{id} if defined($options{id});
$this->{logPath} = $options{logPath} if ( defined($options{logPath}) ); $this->{logPath} = $options{logPath} if defined($options{logPath});
my $tempLogFile; my $tempLogFile;
$tempLogFile = $this->{logPath}.'/'.$this->{id}.".log"; $tempLogFile = $this->{logPath}.'/'.$this->{id}.'.log';
$tempLogFile = $options{logFile} if ( defined($options{logFile}) ); $tempLogFile = $options{logFile} if defined($options{logFile});
if ( my $logFile = $this->getTargettedEnv('LOG_FILE') ) { if ( my $logFile = $this->getTargettedEnv('LOG_FILE') ) {
$tempLogFile = $logFile; $tempLogFile = $logFile;
} }
@ -213,7 +214,7 @@ sub initialise( @ ) {
my $tempFileLevel = $this->{fileLevel}; my $tempFileLevel = $this->{fileLevel};
my $tempSyslogLevel = $this->{syslogLevel}; my $tempSyslogLevel = $this->{syslogLevel};
$tempTermLevel = $options{termLevel} if ( defined($options{termLevel}) ); $tempTermLevel = $options{termLevel} if defined($options{termLevel});
if ( defined($options{databaseLevel}) ) { if ( defined($options{databaseLevel}) ) {
$tempDatabaseLevel = $options{databaseLevel}; $tempDatabaseLevel = $options{databaseLevel};
} else { } else {
@ -230,16 +231,16 @@ sub initialise( @ ) {
$tempSyslogLevel = $Config{ZM_LOG_LEVEL_SYSLOG}; $tempSyslogLevel = $Config{ZM_LOG_LEVEL_SYSLOG};
} }
if ( defined($ENV{'LOG_PRINT'}) ) { if ( defined($ENV{LOG_PRINT}) ) {
$tempTermLevel = $ENV{'LOG_PRINT'}? DEBUG : NOLOG; $tempTermLevel = $ENV{LOG_PRINT}? DEBUG : NOLOG;
} }
my $level; my $level;
$tempLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL')) ); $tempLevel = $level if defined($level = $this->getTargettedEnv('LOG_LEVEL'));
$tempTermLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_TERM')) ); $tempTermLevel = $level if defined($level = $this->getTargettedEnv('LOG_LEVEL_TERM'));
$tempDatabaseLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_DATABASE')) ); $tempDatabaseLevel = $level if defined($level = $this->getTargettedEnv('LOG_LEVEL_DATABASE'));
$tempFileLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_FILE')) ); $tempFileLevel = $level if defined($level = $this->getTargettedEnv('LOG_LEVEL_FILE'));
$tempSyslogLevel = $level if ( defined($level = $this->getTargettedEnv('LOG_LEVEL_SYSLOG')) ); $tempSyslogLevel = $level if defined($level = $this->getTargettedEnv('LOG_LEVEL_SYSLOG'));
if ( $Config{ZM_LOG_DEBUG} ) { if ( $Config{ZM_LOG_DEBUG} ) {
foreach my $target ( split( /\|/, $Config{ZM_LOG_DEBUG_TARGET} ) ) { foreach my $target ( split( /\|/, $Config{ZM_LOG_DEBUG_TARGET} ) ) {
@ -247,11 +248,11 @@ sub initialise( @ ) {
|| $target eq '_'.$this->{id} || $target eq '_'.$this->{id}
|| $target eq $this->{idRoot} || $target eq $this->{idRoot}
|| $target eq '_'.$this->{idRoot} || $target eq '_'.$this->{idRoot}
|| $target eq "" || $target eq ''
) { ) {
if ( $Config{ZM_LOG_DEBUG_LEVEL} > NOLOG ) { if ( $Config{ZM_LOG_DEBUG_LEVEL} > NOLOG ) {
$tempLevel = $this->limit( $Config{ZM_LOG_DEBUG_LEVEL} ); $tempLevel = $this->limit( $Config{ZM_LOG_DEBUG_LEVEL} );
if ( $Config{ZM_LOG_DEBUG_FILE} ne "" ) { if ( $Config{ZM_LOG_DEBUG_FILE} ne '' ) {
$tempLogFile = $Config{ZM_LOG_DEBUG_FILE}; $tempLogFile = $Config{ZM_LOG_DEBUG_FILE};
$tempFileLevel = $tempLevel; $tempFileLevel = $tempLevel;
} }
@ -269,9 +270,9 @@ sub initialise( @ ) {
$this->level( $tempLevel ); $this->level( $tempLevel );
$this->{trace} = $options{trace} if ( defined($options{trace}) ); $this->{trace} = $options{trace} if defined($options{trace});
$this->{autoFlush} = $ENV{'LOG_FLUSH'}?1:0 if ( defined($ENV{'LOG_FLUSH'}) ); $this->{autoFlush} = $ENV{LOG_FLUSH}?1:0 if defined($ENV{LOG_FLUSH});
$this->{initialised} = !undef; $this->{initialised} = !undef;
@ -303,28 +304,28 @@ sub reinitialise {
# Bit of a nasty hack to reopen connections to log files and the DB # Bit of a nasty hack to reopen connections to log files and the DB
my $syslogLevel = $this->syslogLevel(); my $syslogLevel = $this->syslogLevel();
$this->syslogLevel( NOLOG ); $this->syslogLevel( NOLOG );
$this->syslogLevel( $syslogLevel ) if ( $syslogLevel > NOLOG ); $this->syslogLevel($syslogLevel) if $syslogLevel > NOLOG;
my $logfileLevel = $this->fileLevel(); my $logfileLevel = $this->fileLevel();
$this->fileLevel( NOLOG ); $this->fileLevel(NOLOG);
$this->fileLevel( $logfileLevel ) if ( $logfileLevel > NOLOG ); $this->fileLevel($logfileLevel) if $logfileLevel > NOLOG;
my $databaseLevel = $this->databaseLevel(); my $databaseLevel = $this->databaseLevel();
$this->databaseLevel( NOLOG ); $this->databaseLevel(NOLOG);
$this->databaseLevel( $databaseLevel ) if ( $databaseLevel > NOLOG ); $this->databaseLevel($databaseLevel) if $databaseLevel > NOLOG;
my $screenLevel = $this->termLevel(); my $screenLevel = $this->termLevel();
$this->termLevel( NOLOG ); $this->termLevel(NOLOG);
$this->termLevel( $screenLevel ) if ( $screenLevel > NOLOG ); $this->termLevel($screenLevel) if $screenLevel > NOLOG;
} }
# Prevents undefined logging levels # Prevents undefined logging levels
sub limit { sub limit {
my $this = shift; my $this = shift;
my $level = shift; my $level = shift;
return( DEBUG ) if ( $level > DEBUG ); return(DEBUG) if $level > DEBUG;
return( NOLOG ) if ( $level < NOLOG ); return(NOLOG) if $level < NOLOG;
return( $level ); return($level);
} }
sub getTargettedEnv { sub getTargettedEnv {
@ -332,32 +333,32 @@ sub getTargettedEnv {
my $name = shift; my $name = shift;
my $envName = $name.'_'.$this->{id}; my $envName = $name.'_'.$this->{id};
my $value; my $value;
$value = $ENV{$envName} if ( defined($ENV{$envName}) ); $value = $ENV{$envName} if defined($ENV{$envName});
if ( !defined($value) && $this->{id} ne $this->{idRoot} ) { if ( !defined($value) and ($this->{id} ne $this->{idRoot}) ) {
$envName = $name.'_'.$this->{idRoot}; $envName = $name.'_'.$this->{idRoot};
$value = $ENV{$envName} if ( defined($ENV{$envName}) ); $value = $ENV{$envName} if defined($ENV{$envName});
} }
if ( !defined($value) ) { if ( !defined($value) ) {
$value = $ENV{$name} if ( defined($ENV{$name}) ); $value = $ENV{$name} if defined($ENV{$name});
} }
if ( defined($value) ) { if ( defined($value) ) {
( $value ) = $value =~ m/(.*)/; ( $value ) = $value =~ m/(.*)/;
} }
return( $value ); return $value;
} }
sub fetch { sub fetch {
if ( !$logger ) { if ( !$logger ) {
$logger = ZoneMinder::Logger->new(); $logger = ZoneMinder::Logger->new();
$logger->initialise( 'syslogLevel'=>INFO, 'databaseLevel'=>INFO ); $logger->initialise( syslogLevel=>INFO, databaseLevel=>INFO );
} }
return( $logger ); return $logger;
} }
sub id { sub id {
my $this = shift; my $this = shift;
my $id = shift; my $id = shift;
if ( defined($id) && $this->{id} ne $id ) { if ( defined($id) and ($this->{id} ne $id) ) {
# Remove whitespace # Remove whitespace
$id =~ s/\S//g; $id =~ s/\S//g;
# Replace non-alphanum with underscore # Replace non-alphanum with underscore
@ -371,37 +372,37 @@ sub id {
} }
} }
} }
return( $this->{id} ); return $this->{id};
} }
sub level { sub level {
my $this = shift; my $this = shift;
my $level = shift; my $level = shift;
if ( defined($level) ) { if ( defined($level) ) {
$this->{level} = $this->limit( $level ); $this->{level} = $this->limit($level);
# effectiveLevel is the highest logging level used by any of the outputs. # effectiveLevel is the highest logging level used by any of the outputs.
$this->{effectiveLevel} = NOLOG; $this->{effectiveLevel} = NOLOG;
$this->{effectiveLevel} = $this->{termLevel} if ( $this->{termLevel} > $this->{effectiveLevel} ); $this->{effectiveLevel} = $this->{termLevel} if $this->{termLevel} > $this->{effectiveLevel};
$this->{effectiveLevel} = $this->{databaseLevel} if ( $this->{databaseLevel} > $this->{effectiveLevel} ); $this->{effectiveLevel} = $this->{databaseLevel} if $this->{databaseLevel} > $this->{effectiveLevel};
$this->{effectiveLevel} = $this->{fileLevel} if ( $this->{fileLevel} > $this->{effectiveLevel} ); $this->{effectiveLevel} = $this->{fileLevel} if $this->{fileLevel} > $this->{effectiveLevel};
$this->{effectiveLevel} = $this->{syslogLevel} if ( $this->{syslogLevel} > $this->{effectiveLevel} ); $this->{effectiveLevel} = $this->{syslogLevel} if $this->{syslogLevel} > $this->{effectiveLevel};
# ICON: I am remarking this out because I don't see the point of having an effective level, if we are just going to set it to level. # ICON: I am remarking this out because I don't see the point of having an effective level, if we are just going to set it to level.
#$this->{effectiveLevel} = $this->{level} if ( $this->{level} > $this->{effectiveLevel} ); #$this->{effectiveLevel} = $this->{level} if ( $this->{level} > $this->{effectiveLevel} );
} }
return( $this->{level} ); return $this->{level};
} }
sub debugOn { sub debugOn {
my $this = shift; my $this = shift;
return( $this->{effectiveLevel} >= DEBUG ); return $this->{effectiveLevel} >= DEBUG;
} }
sub trace { sub trace {
my $this = shift; my $this = shift;
$this->{trace} = $_[0] if ( @_ ); $this->{trace} = $_[0] if @_;
return( $this->{trace} ); return $this->{trace};
} }
sub termLevel { sub termLevel {
@ -409,63 +410,62 @@ sub termLevel {
my $termLevel = shift; my $termLevel = shift;
if ( defined($termLevel) ) { if ( defined($termLevel) ) {
# What is the point of this next lint if we are just going to overwrite it with the next line? I propose we move it down one line or remove it altogether # What is the point of this next lint if we are just going to overwrite it with the next line? I propose we move it down one line or remove it altogether
$termLevel = NOLOG if ( !$this->{hasTerm} ); $termLevel = NOLOG if !$this->{hasTerm};
$termLevel = $this->limit( $termLevel ); $termLevel = $this->limit($termLevel);
if ( $this->{termLevel} != $termLevel ) { if ( $this->{termLevel} != $termLevel ) {
$this->{termLevel} = $termLevel; $this->{termLevel} = $termLevel;
} }
} }
return( $this->{termLevel} ); return $this->{termLevel};
} }
sub databaseLevel { sub databaseLevel {
my $this = shift; my $this = shift;
my $databaseLevel = shift; my $databaseLevel = shift;
if ( defined($databaseLevel) ) { if ( defined($databaseLevel) ) {
$databaseLevel = $this->limit( $databaseLevel ); $databaseLevel = $this->limit($databaseLevel);
if ( $this->{databaseLevel} != $databaseLevel ) { if ( $this->{databaseLevel} != $databaseLevel ) {
if ( $databaseLevel > NOLOG && $this->{databaseLevel} <= NOLOG ) { if ( $databaseLevel > NOLOG and $this->{databaseLevel} <= NOLOG ) {
if ( !$this->{dbh} ) { if ( !$this->{dbh} ) {
my $socket; my $socket;
my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ );
if ( defined($portOrSocket) ) { if ( defined($portOrSocket) ) {
if ( $portOrSocket =~ /^\// ) { if ( $portOrSocket =~ /^\// ) {
$socket = ";mysql_socket=".$portOrSocket; $socket = ';mysql_socket='.$portOrSocket;
} else { } else {
$socket = ";host=".$host.";port=".$portOrSocket; $socket = ';host='.$host.';port='.$portOrSocket;
} }
} else { } else {
$socket = ";host=".$Config{ZM_DB_HOST}; $socket = ';host='.$Config{ZM_DB_HOST};
} }
my $sslOptions = ""; my $sslOptions = '';
if ( $Config{ZM_DB_SSL_CA_CERT} ) { if ( $Config{ZM_DB_SSL_CA_CERT} ) {
$sslOptions = ';'.join(';', $sslOptions = join(';','',
"mysql_ssl=1", 'mysql_ssl=1',
"mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT}, 'mysql_ssl_ca_file='.$Config{ZM_DB_SSL_CA_CERT},
"mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY}, 'mysql_ssl_client_key='.$Config{ZM_DB_SSL_CLIENT_KEY},
"mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT} 'mysql_ssl_client_cert='.$Config{ZM_DB_SSL_CLIENT_CERT}
); );
} }
$this->{dbh} = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME} $this->{dbh} = DBI->connect( 'DBI:mysql:database='.$Config{ZM_DB_NAME}
.$socket.$sslOptions .$socket.$sslOptions
, $Config{ZM_DB_USER} , $Config{ZM_DB_USER}
, $Config{ZM_DB_PASS} , $Config{ZM_DB_PASS}
); );
if ( !$this->{dbh} ) { if ( !$this->{dbh} ) {
$databaseLevel = NOLOG; $databaseLevel = NOLOG;
Error( "Unable to write log entries to DB, can't connect to database '" Error( 'Unable to write log entries to DB, can\'t connect to database '
.$Config{ZM_DB_NAME} .$Config{ZM_DB_NAME}
."' on host '" .' on host '
.$Config{ZM_DB_HOST} .$Config{ZM_DB_HOST}
."'"
); );
} else { } else {
$this->{dbh}->{AutoCommit} = 1; $this->{dbh}->{AutoCommit} = 1;
Fatal( "Can't set AutoCommit on in database connection" ) Fatal('Can\'t set AutoCommit on in database connection' )
unless( $this->{dbh}->{AutoCommit} ); unless( $this->{dbh}->{AutoCommit} );
$this->{dbh}->{mysql_auto_reconnect} = 1; $this->{dbh}->{mysql_auto_reconnect} = 1;
Fatal( "Can't set mysql_auto_reconnect on in database connection" ) Fatal('Can\'t set mysql_auto_reconnect on in database connection' )
unless( $this->{dbh}->{mysql_auto_reconnect} ); unless( $this->{dbh}->{mysql_auto_reconnect} );
$this->{dbh}->trace( 0 ); $this->{dbh}->trace( 0 );
} }
@ -479,7 +479,7 @@ sub databaseLevel {
$this->{databaseLevel} = $databaseLevel; $this->{databaseLevel} = $databaseLevel;
} }
} }
return( $this->{databaseLevel} ); return $this->{databaseLevel};
} }
sub fileLevel { sub fileLevel {
@ -487,13 +487,12 @@ sub fileLevel {
my $fileLevel = shift; my $fileLevel = shift;
if ( defined($fileLevel) ) { if ( defined($fileLevel) ) {
$fileLevel = $this->limit($fileLevel); $fileLevel = $this->limit($fileLevel);
if ( $this->{fileLevel} != $fileLevel ) { # The filename might have changed, so always close and re-open
$this->closeFile() if ( $this->{fileLevel} > NOLOG ); $this->closeFile() if ( $this->{fileLevel} > NOLOG );
$this->{fileLevel} = $fileLevel; $this->{fileLevel} = $fileLevel;
$this->openFile() if ( $this->{fileLevel} > NOLOG ); $this->openFile() if ( $this->{fileLevel} > NOLOG );
}
} }
return( $this->{fileLevel} ); return $this->{fileLevel};
} }
sub syslogLevel { sub syslogLevel {
@ -512,7 +511,7 @@ sub syslogLevel {
sub openSyslog { sub openSyslog {
my $this = shift; my $this = shift;
openlog( $this->{id}, 'pid', "local1" ); openlog( $this->{id}, 'pid', 'local1' );
} }
sub closeSyslog { sub closeSyslog {
@ -532,27 +531,25 @@ sub logFile {
sub openFile { sub openFile {
my $this = shift; my $this = shift;
if ( open( $LOGFILE, ">>", $this->{logFile} ) ) { if ( open($LOGFILE, '>>', $this->{logFile}) ) {
$LOGFILE->autoflush() if ( $this->{autoFlush} ); $LOGFILE->autoflush() if $this->{autoFlush};
my $webUid = (getpwnam( $Config{ZM_WEB_USER} ))[2]; my $webUid = (getpwnam($Config{ZM_WEB_USER}))[2];
my $webGid = (getgrnam( $Config{ZM_WEB_GROUP} ))[2]; my $webGid = (getgrnam($Config{ZM_WEB_GROUP}))[2];
if ( $> == 0 ) { if ( $> == 0 ) {
chown( $webUid, $webGid, $this->{logFile} ) chown( $webUid, $webGid, $this->{logFile} )
or Fatal( "Can't change permissions on log file '" or Fatal("Can't change permissions on log file $$this{logFile}: $!");
.$this->{logFile}."': $!"
)
} }
} else { } else {
$this->fileLevel( NOLOG ); $this->fileLevel(NOLOG);
$this->termLevel( INFO ); $this->termLevel(INFO);
Error( "Can't open log file '".$this->{logFile}."': $!" ); Error("Can't open log file $$this{logFile}: $!");
} }
} }
sub closeFile { sub closeFile {
my $this = shift; #my $this = shift;
close( $LOGFILE ) if ( fileno($LOGFILE) ); close($LOGFILE) if fileno($LOGFILE);
} }
sub logPrint { sub logPrint {
@ -568,7 +565,7 @@ sub logPrint {
my ($seconds, $microseconds) = gettimeofday(); my ($seconds, $microseconds) = gettimeofday();
my $message = sprintf( my $message = sprintf(
'%s.%06d %s[%d].%s [%s]' '%s.%06d %s[%d].%s [%s]'
, strftime( '%x %H:%M:%S' ,localtime( $seconds ) ) , strftime('%x %H:%M:%S', localtime($seconds))
, $microseconds , $microseconds
, $this->{id} , $this->{id}
, $$ , $$
@ -576,42 +573,43 @@ sub logPrint {
, $string , $string
); );
if ( $this->{trace} ) { if ( $this->{trace} ) {
$message = Carp::shortmess( $message ); $message = Carp::shortmess($message);
} else { } else {
$message = $message."\n"; $message = $message."\n";
} }
if ( $level <= $this->{syslogLevel} ) { if ( $level <= $this->{syslogLevel} ) {
syslog( $priorities{$level}, $code." [%s]", $string ); syslog($priorities{$level}, $code.' [%s]', $string);
} }
print( $LOGFILE $message ) if ( $level <= $this->{fileLevel} ); print($LOGFILE $message) if $level <= $this->{fileLevel};
if ( $level <= $this->{databaseLevel} ) { if ( $level <= $this->{databaseLevel} ) {
my $sql = 'insert into Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) values ( ?, ?, ?, ?, ?, ?, ?, NULL )'; my $sql = 'INSERT INTO Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) VALUES ( ?, ?, ?, ?, ?, ?, ?, NULL )';
$this->{sth} = $this->{dbh}->prepare_cached( $sql ); $this->{sth} = $this->{dbh}->prepare_cached($sql);
if ( !$this->{sth} ) { if ( !$this->{sth} ) {
$this->{databaseLevel} = NOLOG; $this->{databaseLevel} = NOLOG;
Fatal( "Can't prepare log entry '$sql': ".$this->{dbh}->errstr() ); Error("Can't prepare log entry '$sql': ".$this->{dbh}->errstr());
} else {
my $res = $this->{sth}->execute($seconds+($microseconds/1000000.0)
, $this->{id}
, $$
, $level
, $code
, $string
, $this->{fileName}
);
if ( !$res ) {
$this->{databaseLevel} = NOLOG;
Error("Can't execute log entry '$sql': ".$this->{sth}->errstr());
}
} }
my $res = $this->{sth}->execute( $seconds+($microseconds/1000000.0) } # end if doing db logging
, $this->{id} print(STDERR $message) if $level <= $this->{termLevel};
, $$ } # end if level < effectivelevel
, $level
, $code
, $string
, $this->{fileName}
);
if ( !$res ) {
$this->{databaseLevel} = NOLOG;
Fatal( "Can't execute log entry '$sql': ".$this->{sth}->errstr() );
}
}
print( STDERR $message ) if ( $level <= $this->{termLevel} );
}
} }
sub logInit( ;@ ) { sub logInit( ;@ ) {
my %options = @_ ? @_ : (); my %options = @_ ? @_ : ();
$logger = ZoneMinder::Logger->new() if !$logger; $logger = ZoneMinder::Logger->new() if !$logger;
$logger->initialise( %options ); $logger->initialise(%options);
} }
sub logReinit { sub logReinit {
@ -619,14 +617,14 @@ sub logReinit {
} }
sub logTerm { sub logTerm {
return unless ( $logger ); return unless $logger;
$logger->terminate(); $logger->terminate();
$logger = undef; $logger = undef;
} }
sub logHupHandler { sub logHupHandler {
my $savedErrno = $!; my $savedErrno = $!;
return unless( $logger ); return unless $logger;
fetch()->reinitialise(); fetch()->reinitialise();
logSetSignal(); logSetSignal();
$! = $savedErrno; $! = $savedErrno;
@ -641,88 +639,87 @@ sub logClearSignal {
} }
sub logLevel { sub logLevel {
return( fetch()->level( @_ ) ); return fetch()->level(@_);
} }
sub logDebugging { sub logDebugging {
return( fetch()->debugOn() ); return fetch()->debugOn();
} }
sub logTermLevel { sub logTermLevel {
return( fetch()->termLevel( @_ ) ); return fetch()->termLevel(@_);
} }
sub logDatabaseLevel { sub logDatabaseLevel {
return( fetch()->databaseLevel( @_ ) ); return fetch()->databaseLevel(@_);
} }
sub logFileLevel { sub logFileLevel {
return( fetch()->fileLevel( @_ ) ); return fetch()->fileLevel(@_);
} }
sub logSyslogLevel { sub logSyslogLevel {
return( fetch()->syslogLevel( @_ ) ); return fetch()->syslogLevel(@_);
} }
sub Mark { sub Mark {
my $level = shift; my $level = shift;
$level = DEBUG unless( defined($level) ); $level = DEBUG unless defined($level);
my $tag = 'Mark'; my $tag = 'Mark';
fetch()->logPrint( $level, $tag ); fetch()->logPrint($level, $tag);
} }
sub Dump { sub Dump {
my $var = shift; my $var = shift;
my $label = shift; my $label = shift;
$label = 'VAR' unless( defined($label) ); $label = 'VAR' unless defined($label);
fetch()->logPrint( DEBUG, Data::Dumper->Dump( [ $var ], [ $label ] ) ); fetch()->logPrint(DEBUG, Data::Dumper->Dump([ $var ], [ $label ]));
} }
sub debug { sub debug {
my $log = shift; my $log = shift;
$log->logPrint( DEBUG, @_ ); $log->logPrint(DEBUG, @_);
} }
sub Debug( @ ) { sub Debug( @ ) {
fetch()->logPrint( DEBUG, @_ ); fetch()->logPrint(DEBUG, @_);
} }
sub Info( @ ) { sub Info( @ ) {
fetch()->logPrint( INFO, @_ ); fetch()->logPrint(INFO, @_);
} }
sub info { sub info {
my $log = shift; my $log = shift;
$log->logPrint( INFO, @_ ); $log->logPrint(INFO, @_);
} }
sub Warning( @ ) { sub Warning( @ ) {
fetch()->logPrint( WARNING, @_ ); fetch()->logPrint(WARNING, @_);
} }
sub warn { sub warn {
my $log = shift; my $log = shift;
$log->logPrint( WARNING, @_ ); $log->logPrint(WARNING, @_);
} }
sub Error( @ ) { sub Error( @ ) {
fetch()->logPrint( ERROR, @_ ); fetch()->logPrint(ERROR, @_);
} }
sub error { sub error {
my $log = shift; my $log = shift;
$log->logPrint( ERROR, @_ ); $log->logPrint(ERROR, @_);
} }
sub Fatal( @ ) { sub Fatal( @ ) {
fetch()->logPrint( FATAL, @_ ); fetch()->logPrint(FATAL, @_);
if ( $SIG{TERM} ne 'DEFAULT' ) { if ( $SIG{TERM} and ( $SIG{TERM} ne 'DEFAULT' ) ) {
$SIG{TERM}(); $SIG{TERM}();
} }
exit( -1 ); exit(-1);
} }
sub Panic( @ ) { sub Panic( @ ) {
fetch()->logPrint( PANIC, @_ ); fetch()->logPrint(PANIC, @_);
confess( $_[0] ); confess($_[0]);
} }
1; 1;
@ -741,10 +738,10 @@ logInit( 'myproc', DEBUG );
Debug( 'This is what is happening' ); Debug( 'This is what is happening' );
Info( 'Something interesting is happening' ); Info( 'Something interesting is happening' );
Warning( "Something might be going wrong." ); Warning( 'Something might be going wrong.' );
Error( "Something has gone wrong!!" ); Error( 'Something has gone wrong!!' );
Fatal( "Something has gone badly wrong, gotta stop!!" ); Fatal( 'Something has gone badly wrong, gotta stop!!' );
Panic( "Something fundamental has gone wrong, die with stack trace ); Panic( 'Something fundamental has gone wrong, die with stack trace' );
=head1 DESCRIPTION =head1 DESCRIPTION

View File

@ -166,6 +166,8 @@ 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 +317,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 +828,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

@ -246,7 +246,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 +294,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." );

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" );
@ -236,8 +240,8 @@ use Sys::MemInfo qw(totalmem freemem totalswap freeswap);
use Sys::CpuLoad; use Sys::CpuLoad;
#use Data::Dumper; #use Data::Dumper;
# We count 100 of these, so total timeout is this value *100. # We count 10 of these, so total timeout is this value *10.
use constant KILL_DELAY => 100*1000; # 1/10th of a second use constant KILL_DELAY => 1; # seconds
our %cmd_hash; our %cmd_hash;
our %pid_hash; our %pid_hash;
@ -289,9 +293,8 @@ sub run {
if ( $Config{ZM_SERVER_ID} ) { if ( $Config{ZM_SERVER_ID} ) {
require ZoneMinder::Server; require ZoneMinder::Server;
dPrint( ZoneMinder::Logger::INFO, 'Loading Server record' );
$Server = new ZoneMinder::Server( $Config{ZM_SERVER_ID} ); $Server = new ZoneMinder::Server( $Config{ZM_SERVER_ID} );
dPrint( ZoneMinder::Logger::INFO, 'Loading Server record have ' . $$Server{Name} ); dPrint( ZoneMinder::Logger::INFO, 'Loading Server record have ' . $$Server{Name} );
} }
while( 1 ) { while( 1 ) {
@ -300,7 +303,7 @@ sub run {
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 = Sys::CpuLoad::load();
dPrint( ZoneMinder::Logger::INFO, 'Updating Server record' ); 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());
@ -385,6 +388,7 @@ sub cPrint {
} }
} }
# I think the purpose of this is to echo the logs to the client process so it can then display them.
sub dPrint { sub dPrint {
my $logLevel = shift; my $logLevel = shift;
if ( fileno(CLIENT) ) { if ( fileno(CLIENT) ) {
@ -523,7 +527,7 @@ sub kill_until_dead {
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} ) ) { while( $process and $$process{pid} and kill( 0, $$process{pid} ) ) {
if ( $count++ > 100 ) { if ( $count++ > 10 ) {
dPrint( ZoneMinder::Logger::WARNING, "'$$process{command}' has not stopped at " dPrint( ZoneMinder::Logger::WARNING, "'$$process{command}' has not stopped at "
.strftime( '%y/%m/%d %H:%M:%S', localtime() ) .strftime( '%y/%m/%d %H:%M:%S', localtime() )
.". Sending KILL to pid $$process{pid}\n" .". Sending KILL to pid $$process{pid}\n"
@ -532,8 +536,9 @@ sub kill_until_dead {
last; last;
} }
# THe purpose of the signal blocking is to simplify the concurrency
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n"; sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
usleep( KILL_DELAY ); sleep( KILL_DELAY );
sigprocmask(SIG_BLOCK, $blockset, $sigset ) or die "dying at block...\n"; 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";

View File

@ -327,6 +327,7 @@ sub checkFilter {
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 $Event = new ZoneMinder::Event( $$event{Id}, $event );
my $NewStorage = new ZoneMinder::Storage( $filter->{AutoMoveTo} ); my $NewStorage = new ZoneMinder::Storage( $filter->{AutoMoveTo} );

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 )
@ -226,9 +227,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.");
@ -253,6 +269,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

@ -83,10 +83,10 @@ while( 1 ) {
$dbh = zmDbConnect(); $dbh = zmDbConnect();
} }
$dbh->do('DELETE FROM Events_Hour WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 hour)'); $dbh->do('DELETE FROM Events_Hour WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 hour)') or Error($dbh->errstr());
$dbh->do('DELETE FROM Events_Day WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 day)'); $dbh->do('DELETE FROM Events_Day WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 day)') or Error($dbh->errstr());
$dbh->do('DELETE FROM Events_Week WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 week)'); $dbh->do('DELETE FROM Events_Week WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 week)') or Error($dbh->errstr());
$dbh->do('DELETE FROM Events_Month WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 month)'); $dbh->do('DELETE FROM Events_Month WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 month)') or Error($dbh->errstr());
sleep( $Config{ZM_STATS_UPDATE_INTERVAL} ); sleep( $Config{ZM_STATS_UPDATE_INTERVAL} );
} # end while (1) } # end while (1)

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

@ -89,7 +89,7 @@ public:
bool IscURL() const { return( type == CURL_SRC ); } bool IscURL() const { return( type == CURL_SRC ); }
unsigned int Width() const { return( width ); } unsigned int Width() const { return( width ); }
unsigned int Height() const { return( height ); } unsigned int Height() const { return( height ); }
unsigned int Colours() const { return( colours ); } unsigned int Colours() const { return colours; }
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 ); }

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

@ -34,31 +34,31 @@ bool zmDbConnect() {
//if ( zmDbConnected ) //if ( zmDbConnected )
//return; //return;
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));
return false; 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) )
Error( "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, mysql_ssl_set(&dbconn,
staticConfig.DB_SSL_CLIENT_KEY.c_str(), staticConfig.DB_SSL_CLIENT_KEY.c_str(),
staticConfig.DB_SSL_CLIENT_CERT.c_str(), staticConfig.DB_SSL_CLIENT_CERT.c_str(),
staticConfig.DB_SSL_CA_CERT.c_str(), staticConfig.DB_SSL_CA_CERT.c_str(),
NULL, NULL ); NULL, NULL);
std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":" ); 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));
return false; 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));
return false; return false;
} }
} else { } else {
@ -70,7 +70,7 @@ bool zmDbConnect() {
} }
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; return zmDbConnected;
@ -78,67 +78,72 @@ bool zmDbConnect() {
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(); 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(); 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

@ -33,6 +33,8 @@ class zmDbRow {
zmDbRow( MYSQL_RES *, MYSQL_ROW *row ); zmDbRow( MYSQL_RES *, MYSQL_ROW *row );
~zmDbRow(); ~zmDbRow();
MYSQL_ROW mysql_row() const { return row; };
char *operator[](unsigned int index) const { char *operator[](unsigned int index) const {
return row[index]; return row[index];
} }

View File

@ -49,10 +49,10 @@ Event::Event(
const std::string &p_cause, const std::string &p_cause,
const StringSetMap &p_noteSetMap const StringSetMap &p_noteSetMap
) : ) :
monitor( p_monitor ), monitor(p_monitor),
start_time( p_start_time ), start_time(p_start_time),
cause( p_cause ), cause(p_cause),
noteSetMap( p_noteSetMap ) noteSetMap(p_noteSetMap)
{ {
std::string notes; std::string notes;
@ -71,6 +71,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;
@ -78,8 +79,12 @@ 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];
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(),
start_time.tv_sec, start_time.tv_sec,
@ -93,16 +98,16 @@ Event::Event(
monitor->GetOptSaveJPEGs(), monitor->GetOptSaveJPEGs(),
storage->SchemeString().c_str() storage->SchemeString().c_str()
); );
db_mutex.lock(); db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) { if ( mysql_query(&dbconn, sql) ) {
Error( "Can't insert event: %s. sql was (%s)", mysql_error( &dbconn ), sql ); Error("Can't insert event: %s. sql was (%s)", mysql_error(&dbconn), sql);
exit( mysql_errno( &dbconn ) ); db_mutex.unlock();
return;
} }
id = mysql_insert_id( &dbconn ); id = mysql_insert_id(&dbconn);
db_mutex.unlock(); 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;
@ -111,12 +116,10 @@ Event::Event(
max_score = 0; max_score = 0;
have_video_keyframe = false; have_video_keyframe = false;
char id_file[PATH_MAX]; struct tm *stime = localtime(&start_time.tv_sec);
struct tm *stime = localtime( &start_time.tv_sec );
if ( storage->Scheme() == Storage::DEEP ) { if ( storage->Scheme() == Storage::DEEP ) {
char *path_ptr = path; path = stringtf("%s/%d", storage->Path(), monitor->Id());
path_ptr += snprintf( path_ptr, sizeof(path), "%s/%d", storage->Path(), monitor->Id() );
int dt_parts[6]; int dt_parts[6];
dt_parts[0] = stime->tm_year-100; dt_parts[0] = stime->tm_year-100;
@ -130,55 +133,54 @@ Event::Event(
char time_path[PATH_MAX] = ""; char time_path[PATH_MAX] = "";
char *time_path_ptr = time_path; char *time_path_ptr = time_path;
for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) { for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) {
path_ptr += snprintf( path_ptr, sizeof(path)-(path_ptr-path), "/%02d", dt_parts[i] ); path += stringtf("/%02d", dt_parts[i]);
errno = 0; errno = 0;
if ( mkdir( path, 0755 ) ) { if ( mkdir(path.c_str(), 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.c_str(), strerror(errno));
} }
} }
if ( i == 2 ) if ( i == 2 )
strncpy( date_path, path, sizeof(date_path) ); strncpy(date_path, path.c_str(), sizeof(date_path));
else if ( i >= 3 ) else if ( i >= 3 )
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 ); std::string id_file = stringtf("%s/.%llu", date_path, id);
if ( symlink( time_path, id_file ) < 0 ) if ( symlink(time_path, id_file.c_str()) < 0 )
Error( "Can't symlink %s -> %s: %s", id_file, path, strerror(errno)); Error("Can't symlink %s -> %s: %s", id_file.c_str(), path.c_str(), strerror(errno));
} else if ( storage->Scheme() == Storage::MEDIUM ) { } else if ( storage->Scheme() == Storage::MEDIUM ) {
char *path_ptr = path; path = stringtf("%s/%d/%04d-%02d-%02d",
path_ptr += snprintf( path_ptr, sizeof(path), "%s/%d/%04d-%02d-%02d",
storage->Path(), monitor->Id(), stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday storage->Path(), monitor->Id(), stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday
); );
if ( mkdir( path, 0755 ) ) { if ( mkdir(path.c_str(), 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.c_str(), strerror(errno));
} }
path_ptr += snprintf( path_ptr, sizeof(path), "/%d", id ); path += stringtf("/%llu", id);
if ( mkdir( path, 0755 ) ) { if ( mkdir(path.c_str(), 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.c_str(), strerror(errno));
} }
} else { } else {
// Shallow Storage // Shallow Storage
snprintf( path, sizeof(path), "%s/%d/%d", storage->Path(), monitor->Id(), id ); path = stringtf("%s/%d/%llu", storage->Path(), monitor->Id(), id);
if ( mkdir( path, 0755 ) ) { if ( mkdir(path.c_str(), 0755) ) {
if ( errno != EEXIST ) { if ( errno != EEXIST ) {
Error( "Can't mkdir %s: %s", path, strerror(errno)); Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
} }
} }
// Create empty id tag file // Create empty id tag file
snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); std::string id_file = stringtf("%s/.%llu", path, id);
if ( FILE *id_fp = fopen( id_file, "w" ) ) if ( FILE *id_fp = fopen(id_file.c_str(), "w") )
fclose( id_fp ); fclose(id_fp);
else else
Error( "Can't fopen %s: %s", id_file, strerror(errno)); Error("Can't fopen %s: %s", id_file.c_str(), strerror(errno));
} // deep storage or not } // deep storage or not
last_db_frame = 0; last_db_frame = 0;
@ -197,9 +199,9 @@ Event::Event(
} }
} }
snprintf( video_name, sizeof(video_name), "%d-%s.%s", id, "video", container.c_str() ); snprintf(video_name, sizeof(video_name), "%llu-%s.%s", id, "video", container.c_str());
snprintf( video_file, sizeof(video_file), staticConfig.video_file_format, path, video_name ); snprintf(video_file, sizeof(video_file), staticConfig.video_file_format, path.c_str(), video_name);
Debug(1,"Writing video file to %s", video_file ); Debug(1,"Writing video file to %s", video_file);
Camera * camera = monitor->getCamera(); Camera * camera = monitor->getCamera();
videoStore = new VideoStore( videoStore = new VideoStore(
video_file, video_file,
@ -211,28 +213,14 @@ Event::Event(
if ( ! videoStore->open() ) { if ( ! videoStore->open() ) {
delete videoStore; delete videoStore;
videoStore = NULL; videoStore = NULL;
} }
} }
} // 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 );
db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't insert frame: %s", mysql_error( &dbconn ) );
}
db_mutex.unlock();
}
/* Close the video file */ /* Close the video file */
if ( videoStore ) { if ( videoStore ) {
@ -240,15 +228,41 @@ Event::~Event() {
videoStore = NULL; videoStore = NULL;
} }
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 ); // Should not be static because we are multi-threaded
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 ) {
Debug(1, "Adding closing frame %d to DB", frames);
snprintf(sql, sizeof(sql),
"INSERT INTO Frames ( EventId, FrameId, TimeStamp, Delta ) VALUES ( %llu, %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 %llu', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' where Id = %llu",
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 );
db_mutex.lock(); db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) { while ( mysql_query(&dbconn, sql) ) {
Error( "Can't update event: %s", mysql_error( &dbconn ) ); Error("Can't update event: %s reason: %s", sql, mysql_error(&dbconn));
db_mutex.unlock();
sleep(1);
db_mutex.lock();
} }
db_mutex.unlock(); db_mutex.unlock();
} // ~Event
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;
@ -380,10 +394,10 @@ void Event::updateNotes( const StringSetMap &newNoteSetMap ) {
#else #else
static char escapedNotes[ZM_SQL_MED_BUFSIZ]; static char escapedNotes[ZM_SQL_MED_BUFSIZ];
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 = %llu", escapedNotes, id);
db_mutex.lock(); db_mutex.lock();
snprintf( sql, sizeof(sql), "UPDATE Events SET Notes = '%s' WHERE Id = %d", escapedNotes, id );
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 ) );
} }
@ -392,13 +406,13 @@ void Event::updateNotes( const StringSetMap &newNoteSetMap ) {
} }
} }
void Event::AddFrames( int n_frames, Image **images, struct timeval **timestamps ) { void Event::AddFrames(int n_frames, Image **images, struct timeval **timestamps) {
for (int i = 0; i < n_frames; i += ZM_SQL_BATCH_SIZE) { for (int i = 0; i < n_frames; i += ZM_SQL_BATCH_SIZE) {
AddFramesInternal(n_frames, i, images, timestamps); AddFramesInternal(n_frames, i, images, timestamps);
} }
} }
void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ) { void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, struct timeval **timestamps) {
static char sql[ZM_SQL_LGE_BUFSIZ]; static char sql[ZM_SQL_LGE_BUFSIZ];
strncpy( sql, "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ", sizeof(sql) ); strncpy( sql, "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ", sizeof(sql) );
int frameCount = 0; int frameCount = 0;
@ -441,7 +455,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, "( %llu, %d, from_unixtime(%ld), %s%ld.%02ld ), ", id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec );
frameCount++; frameCount++;
} }
@ -465,7 +479,7 @@ void Event::AddPacket( ZMPacket *packet, int score, Image *alarm_image ) {
have_video_keyframe = have_video_keyframe || ( ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) && packet->keyframe ); have_video_keyframe = have_video_keyframe || ( ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) && packet->keyframe );
if ( videoStore ) { if ( videoStore ) {
if ( have_video_keyframe ) { if ( have_video_keyframe ) {
videoStore->writePacket( packet ); videoStore->writePacket(packet);
} else { } else {
Debug(2, "No video keyframe yet, not writing"); Debug(2, "No video keyframe yet, not writing");
} }
@ -474,7 +488,7 @@ void Event::AddPacket( ZMPacket *packet, int score, Image *alarm_image ) {
Debug(2,"AddPacket but no videostore?!"); Debug(2,"AddPacket but no videostore?!");
} }
if ( have_video_keyframe && ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) ) { if ( have_video_keyframe && ( packet->codec_type == AVMEDIA_TYPE_VIDEO ) ) {
AddFrame( packet->image, *packet->timestamp, score, alarm_image ); AddFrame(packet->image, *packet->timestamp, score, alarm_image);
} // end if is video } // end if is video
return; return;
} }
@ -514,28 +528,23 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
bool db_frame = ( frame_type != BULK ) || ((frames%config.bulk_frame_interval)==0) || !frames; bool db_frame = ( frame_type != BULK ) || ((frames%config.bulk_frame_interval)==0) || !frames;
if ( db_frame ) { if ( db_frame ) {
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 ( %llu, %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);
db_mutex.lock(); db_mutex.lock();
if ( mysql_query(&dbconn, sql) ) { if ( mysql_query(&dbconn, sql) ) {
Error("Can't insert frame: (%s) reason:%s", sql, mysql_error(&dbconn)); Error("Can't insert frame: %s", mysql_error(&dbconn));
zmDbClose(); Error("SQL was %s", sql);
if ( ! zmDbConnect() ) { db_mutex.unlock();
Error("Unable to connect to db"); return;
exit(mysql_errno(&dbconn));
}
if ( mysql_query(&dbconn, sql) ) {
Error("REALLY Can't insert frame: (%s) reason:%s", sql, mysql_error(&dbconn));
exit(mysql_errno(&dbconn));
}
} }
db_mutex.unlock(); 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 = %llu",
( delta_time.positive?"":"-" ), ( delta_time.positive?"":"-" ),
delta_time.sec, delta_time.fsec, delta_time.sec, delta_time.fsec,
frames, frames,
@ -546,8 +555,11 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
id id
); );
db_mutex.lock(); db_mutex.lock();
if ( mysql_query( &dbconn, sql ) ) { while ( mysql_query(&dbconn, sql) ) {
Error( "Can't update event: %s", mysql_error( &dbconn ) ); Error("Can't update event: %s", mysql_error(&dbconn));
db_mutex.unlock();
sleep(1);
db_mutex.lock();
} }
db_mutex.unlock(); db_mutex.unlock();
} }
@ -564,9 +576,9 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
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

@ -75,7 +75,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; unsigned long long int id;
Monitor *monitor; Monitor *monitor;
struct timeval start_time; struct timeval start_time;
struct timeval end_time; struct timeval end_time;
@ -85,15 +85,15 @@ class Event {
int alarm_frames; int alarm_frames;
unsigned int tot_score; unsigned int tot_score;
unsigned int max_score; unsigned int max_score;
char path[PATH_MAX]; std::string path;
VideoStore *videoStore; VideoStore *videoStore;
char video_name[64]; char video_name[64];
char video_file[PATH_MAX]; char video_file[PATH_MAX];
int last_db_frame; int last_db_frame;
bool have_video_keyframe; // a flag to tell us if we have had a video keyframe when writing an mp4. The first frame SHOULD be a video keyframe. bool have_video_keyframe; // a flag to tell us if we have had a video keyframe when writing an mp4. The first frame SHOULD be a video keyframe.
Storage::Schemes scheme;
void createNotes( std::string &notes ); void createNotes( std::string &notes );
Storage::Schemes scheme;
public: public:
static bool OpenFrameSocket( int ); static bool OpenFrameSocket( int );
@ -102,15 +102,15 @@ class Event {
Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap ); Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap );
~Event(); ~Event();
int Id() const { return( id ); } unsigned long long int 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] ); unsigned long long 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( unsigned long long 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(unsigned long long 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 = %llu", 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/%llu",
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/%llu",
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/%llu",
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/%llu",
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 = %llu 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:%llu, 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; unsigned long long 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:%llu, 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 < %llu 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 > %llu 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]); unsigned long long event_id = atoll(dbrow[0]);
Debug( 1, "Loading new event %d", event_id ); Debug( 1, "Loading new event %llu", 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,14 +553,15 @@ 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);
Image *image = new Image( filepath ); Debug(2, "EventStream::getImage path(%s) ", filepath, curr_frame_id);
Image *image = new Image(filepath);
return image; return image;
} }
bool EventStream::sendFrame( int delta_us ) { bool EventStream::sendFrame(int delta_us) {
Debug( 2, "Sending frame %d", curr_frame_id ); Debug(2, "Sending frame %d", curr_frame_id);
static char filepath[PATH_MAX]; static char filepath[PATH_MAX];
static struct stat filestat; static struct stat filestat;
@ -626,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 );
@ -648,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(), curr_frame_id ); AVFrame *frame = ffmpeg_input->get_frame( ffmpeg_input->get_video_stream_id(), curr_frame_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.");
@ -686,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
@ -716,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;
} }
@ -759,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() {
@ -772,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;
@ -818,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
@ -851,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;
@ -885,3 +846,17 @@ void EventStream::runStream() {
closeComms(); closeComms();
} }
void EventStream::setStreamStart( unsigned long long 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; unsigned long long 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( unsigned long long event_id );
bool loadInitialEventData( int init_event_id, unsigned int init_frame_id ); bool loadInitialEventData( unsigned long long 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( unsigned long long 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

@ -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",
@ -436,6 +436,7 @@ int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet
#endif #endif
return 1; return 1;
} // end int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet ) } // end int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet )
void dumpPacket(AVPacket *pkt, const char *text) { void dumpPacket(AVPacket *pkt, const char *text) {
char b[10240]; char b[10240];

View File

@ -300,11 +300,11 @@ void zm_dump_codecpar ( const AVCodecParameters *par );
#endif #endif
#if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100) #if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100)
#define zm_av_packet_unref( packet ) av_packet_unref( packet ) #define zm_av_packet_unref(packet) av_packet_unref(packet)
#define zm_av_packet_ref( dst, src ) av_packet_ref( dst, src ) #define zm_av_packet_ref(dst, src) av_packet_ref(dst, src)
#else #else
#define zm_av_packet_unref( packet ) av_free_packet( packet ) #define zm_av_packet_unref(packet) av_free_packet(packet)
unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src ); unsigned int zm_av_packet_ref(AVPacket *dst, AVPacket *src);
#endif #endif
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0) #if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
#define zm_avcodec_decode_video( context, rawFrame, frameComplete, packet ) avcodec_decode_video2( context, rawFrame, frameComplete, packet ) #define zm_avcodec_decode_video( context, rawFrame, frameComplete, packet ) avcodec_decode_video2( context, rawFrame, frameComplete, packet )

View File

@ -1,21 +1,21 @@
// //
// ZoneMinder Ffmpeg Camera Class Implementation, $Date: 2009-01-16 12:18:50 +0000 (Fri, 16 Jan 2009) $, $Revision: 2713 $ // ZoneMinder Ffmpeg Camera Class Implementation, $Date: 2009-01-16 12:18:50 +0000 (Fri, 16 Jan 2009) $, $Revision: 2713 $
// Copyright (C) 2001-2008 Philip Coombes // Copyright (C) 2001-2008 Philip Coombes
// //
// This program is free software; you can redistribute it and/or // This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License // modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// //
#include "zm.h" #include "zm.h"
@ -120,12 +120,12 @@ FfmpegCamera::~FfmpegCamera() {
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(); CloseFfmpeg();
} }
mVideoStreamId = -1; mVideoStreamId = -1;
mAudioStreamId = -1; mAudioStreamId = -1;
Info( "Priming capture from %s", mPath.c_str() ); Info("Priming capture from %s", mPath.c_str());
return ! OpenFfmpeg(); return ! OpenFfmpeg();
} }
@ -134,7 +134,7 @@ int FfmpegCamera::PreCapture() {
return 0; return 0;
} }
int FfmpegCamera::Capture( ZMPacket &zm_packet ) { int FfmpegCamera::Capture(ZMPacket &zm_packet) {
if ( ! mCanCapture ) { if ( ! mCanCapture ) {
return -1; return -1;
} }
@ -143,19 +143,22 @@ int FfmpegCamera::Capture( ZMPacket &zm_packet ) {
// If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread. // If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread.
if ( (ret = av_read_frame( mFormatContext, &packet )) < 0 ) { if ( (ret = av_read_frame(mFormatContext, &packet)) < 0 ) {
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.", av_make_error_string(ret).c_str() ); Info("Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret,
ReopenFfmpeg(); av_make_error_string(ret).c_str()
return 0; );
} } else {
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, av_make_error_string(ret).c_str() ); Error("Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret,
return -1; av_make_error_string(ret).c_str()
);
}
return -1;
} }
dumpPacket(&packet, "ffmpeg_camera in"); dumpPacket(&packet, "ffmpeg_camera in");
if ( 0 && ( packet.dts < 0 ) ) { if ( 0 && ( packet.dts < 0 ) ) {
@ -163,8 +166,8 @@ int FfmpegCamera::Capture( ZMPacket &zm_packet ) {
return 0; return 0;
} }
zm_packet.set_packet( &packet ); zm_packet.set_packet(&packet);
zm_av_packet_unref( &packet ); zm_av_packet_unref(&packet);
return 1; return 1;
} // FfmpegCamera::Capture } // FfmpegCamera::Capture
@ -175,13 +178,12 @@ int FfmpegCamera::PostCapture() {
int FfmpegCamera::OpenFfmpeg() { int FfmpegCamera::OpenFfmpeg() {
Debug(2, "OpenFfmpeg called.");
int ret; int ret;
// Open the input, not necessarily a file // Open the input, not necessarily a file
#if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0)
Debug ( 1, "Calling av_open_input_file" ); Debug(1, "Calling av_open_input_file");
if ( av_open_input_file( &mFormatContext, mPath.c_str(), NULL, 0, NULL ) != 0 ) if ( av_open_input_file(&mFormatContext, mPath.c_str(), NULL, 0, NULL) != 0 )
#else #else
// Handle options // Handle options
AVDictionary *opts = NULL; AVDictionary *opts = NULL;
@ -201,27 +203,33 @@ int FfmpegCamera::OpenFfmpeg() {
} else if ( method == "rtpRtspHttp" ) { } else if ( method == "rtpRtspHttp" ) {
ret = av_dict_set(&opts, "rtsp_transport", "http", 0); ret = av_dict_set(&opts, "rtsp_transport", "http", 0);
} else { } else {
Warning("Unknown method (%s)", method.c_str() ); Warning("Unknown method (%s)", method.c_str());
} }
if ( ret < 0 ) { if ( ret < 0 ) {
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_alloc_context 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
//FIXME can speed up initial analysis but need sensible parameters...
//mFormatContext->probesize = 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;
@ -229,7 +237,7 @@ int FfmpegCamera::OpenFfmpeg() {
AVDictionaryEntry *e = NULL; AVDictionaryEntry *e = NULL;
while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
Warning( "Option %s not recognized by ffmpeg", e->key); Warning("Option %s not recognized by ffmpeg", e->key);
} }
av_dict_free(&opts); av_dict_free(&opts);
@ -237,12 +245,12 @@ int FfmpegCamera::OpenFfmpeg() {
Debug(1, "Opened input"); Debug(1, "Opened input");
Info( "Stream open %s, parsing streams...", mPath.c_str() ); Info("Stream open %s, parsing streams...", mPath.c_str());
#if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0)
if ( av_find_stream_info( mFormatContext ) < 0 ) if ( av_find_stream_info(mFormatContext) < 0 )
#else #else
if ( avformat_find_stream_info( mFormatContext, 0 ) < 0 ) if ( avformat_find_stream_info(mFormatContext, 0) < 0 )
#endif #endif
{ {
Error("Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno)); Error("Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno));
@ -270,7 +278,7 @@ int FfmpegCamera::OpenFfmpeg() {
// if we break, then we won't find the audio stream // if we break, then we won't find the audio stream
continue; continue;
} else { } else {
Debug(2, "Have another video stream." ); Debug(2, "Have another video stream.");
} }
} }
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
@ -285,14 +293,14 @@ int FfmpegCamera::OpenFfmpeg() {
if ( mAudioStreamId == -1 ) { if ( mAudioStreamId == -1 ) {
mAudioStreamId = i; mAudioStreamId = i;
} else { } else {
Debug(2, "Have another audio stream." ); Debug(2, "Have another audio stream.");
} }
} }
} // end foreach stream } // end foreach stream
if ( mVideoStreamId == -1 ) if ( mVideoStreamId == -1 )
Fatal( "Unable to locate video stream in %s", mPath.c_str() ); Fatal("Unable to locate video stream in %s", mPath.c_str());
if ( mAudioStreamId == -1 ) if ( mAudioStreamId == -1 )
Debug( 3, "Unable to locate audio stream in %s", mPath.c_str() ); Debug(3, "Unable to locate audio stream in %s", mPath.c_str());
Debug(3, "Found video stream at index %d", mVideoStreamId); Debug(3, "Found video stream at index %d", mVideoStreamId);
Debug(3, "Found audio stream at index %d", mAudioStreamId); Debug(3, "Found audio stream at index %d", mAudioStreamId);
@ -319,7 +327,7 @@ int FfmpegCamera::OpenFfmpeg() {
if ( ! mVideoCodec ) { if ( ! mVideoCodec ) {
// Try to open an hwaccel codec. // Try to open an hwaccel codec.
if ( (mVideoCodec = avcodec_find_decoder_by_name("h264_vaapi")) == NULL ) { if ( (mVideoCodec = avcodec_find_decoder_by_name("h264_vaapi")) == NULL ) {
Debug(1, "Failed to find decoder (h264_vaapi)" ); Debug(1, "Failed to find decoder (h264_vaapi)" );
} else { } else {
Debug(1, "Success finding decoder (h264_vaapi)" ); Debug(1, "Success finding decoder (h264_vaapi)" );
@ -327,7 +335,7 @@ int FfmpegCamera::OpenFfmpeg() {
} }
if ( ! mVideoCodec ) { if ( ! mVideoCodec ) {
// Try to open an hwaccel codec. // Try to open an hwaccel codec.
if ( (mVideoCodec = avcodec_find_decoder_by_name("h264_qsv")) == NULL ) { if ( (mVideoCodec = avcodec_find_decoder_by_name("h264_qsv")) == NULL ) {
Debug(1, "Failed to find decoder (h264_qsv)" ); Debug(1, "Failed to find decoder (h264_qsv)" );
} else { } else {
Debug(1, "Success finding decoder (h264_qsv)" ); Debug(1, "Success finding decoder (h264_qsv)" );
@ -347,13 +355,20 @@ int FfmpegCamera::OpenFfmpeg() {
} }
} // end if h264 } // end if h264
#endif #endif
if ( mVideoCodecContext->codec_id == AV_CODEC_ID_H264 ) {
if ( (mVideoCodec = avcodec_find_decoder_by_name("h264_mmal")) == NULL ) {
Debug(1, "Failed to find decoder (h264_mmal)" );
} else {
Debug(1, "Success finding decoder (h264_mmal)" );
}
}
if ( (!mVideoCodec) and ( (mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id)) == NULL ) ) { if ( (!mVideoCodec) and ( (mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id)) == NULL ) ) {
// Try and get the codec from the codec context // Try and get the codec from the codec context
Error("Can't find codec for video stream from %s", mPath.c_str()); Error("Can't find codec for video stream from %s", mPath.c_str());
return -1; return -1;
} else { } else {
Debug(1, "Video Found decoder"); Debug(1, "Video Found decoder %s", mVideoCodec->name);
zm_dump_stream_format(mFormatContext, mVideoStreamId, 0, 0); zm_dump_stream_format(mFormatContext, mVideoStreamId, 0, 0);
// Open the codec // Open the codec
@ -366,15 +381,15 @@ int FfmpegCamera::OpenFfmpeg() {
#endif #endif
AVDictionaryEntry *e = NULL; AVDictionaryEntry *e = NULL;
while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
Warning( "Option %s not recognized by ffmpeg", e->key); Warning("Option %s not recognized by ffmpeg", e->key);
} }
Error( "Unable to open codec for video stream from %s", mPath.c_str() ); Error("Unable to open codec for video stream from %s", mPath.c_str());
av_dict_free(&opts); av_dict_free(&opts);
return -1; return -1;
} else { } else {
AVDictionaryEntry *e = NULL; AVDictionaryEntry *e = NULL;
if ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) { if ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
Warning( "Option %s not recognized by ffmpeg", e->key); Warning("Option %s not recognized by ffmpeg", e->key);
} }
av_dict_free(&opts); av_dict_free(&opts);
} }
@ -387,8 +402,8 @@ int FfmpegCamera::OpenFfmpeg() {
} }
if ( mAudioStreamId >= 0 ) { if ( mAudioStreamId >= 0 ) {
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
mAudioCodecContext = avcodec_alloc_context3( NULL ); mAudioCodecContext = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context( mAudioCodecContext, mFormatContext->streams[mAudioStreamId]->codecpar ); avcodec_parameters_to_context(mAudioCodecContext, mFormatContext->streams[mAudioStreamId]->codecpar);
#else #else
mAudioCodecContext = mFormatContext->streams[mAudioStreamId]->codec; mAudioCodecContext = mFormatContext->streams[mAudioStreamId]->codec;
#endif #endif
@ -399,20 +414,20 @@ int FfmpegCamera::OpenFfmpeg() {
zm_dump_stream_format(mFormatContext, mAudioStreamId, 0, 0); zm_dump_stream_format(mFormatContext, mAudioStreamId, 0, 0);
// Open the codec // Open the codec
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
Debug ( 1, "Calling avcodec_open" ); Debug(1, "Calling avcodec_open");
if ( avcodec_open(mAudioCodecContext, mAudioCodec) < 0 ) if ( avcodec_open(mAudioCodecContext, mAudioCodec) < 0 )
#else #else
Debug ( 1, "Calling avcodec_open2" ); Debug(1, "Calling avcodec_open2");
if ( avcodec_open2(mAudioCodecContext, mAudioCodec, 0) < 0 ) if ( avcodec_open2(mAudioCodecContext, mAudioCodec, 0) < 0 )
#endif #endif
Fatal( "Unable to open codec for video stream from %s", mPath.c_str() ); Fatal("Unable to open codec for video stream from %s", mPath.c_str());
} }
Debug ( 1, "Opened audio codec" ); Debug(1, "Opened audio codec");
} // end if have audio stream } // end if have audio stream
if ( (unsigned int)mVideoCodecContext->width != width || (unsigned int)mVideoCodecContext->height != height ) { if ( (unsigned int)mVideoCodecContext->width != width || (unsigned int)mVideoCodecContext->height != height ) {
Warning( "Monitor dimensions are %dx%d but camera is sending %dx%d", width, height, mVideoCodecContext->width, mVideoCodecContext->height ); Warning("Monitor dimensions are %dx%d but camera is sending %dx%d", width, height, mVideoCodecContext->width, mVideoCodecContext->height);
} }
mCanCapture = true; mCanCapture = true;
@ -420,14 +435,6 @@ int FfmpegCamera::OpenFfmpeg() {
return 1; return 1;
} // int FfmpegCamera::OpenFfmpeg() } // int FfmpegCamera::OpenFfmpeg()
int FfmpegCamera::ReopenFfmpeg() {
Debug(2, "ReopenFfmpeg called.");
CloseFfmpeg();
return OpenFfmpeg();
}
int FfmpegCamera::CloseFfmpeg() { int FfmpegCamera::CloseFfmpeg() {
Debug(2, "CloseFfmpeg called."); Debug(2, "CloseFfmpeg called.");
@ -461,14 +468,14 @@ int FfmpegCamera::CloseFfmpeg() {
if ( mFormatContext ) { if ( mFormatContext ) {
#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 ); avformat_close_input(&mFormatContext);
#endif #endif
mFormatContext = NULL; mFormatContext = NULL;
} }
return 0; return 0;
} // end int FfmpegCamera::CloseFfmpeg() } // end FfmpegCamera::Close
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT

View File

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

View File

@ -28,15 +28,15 @@ int FFmpeg_Input::Open( const char *filepath ) {
/** Open the input file to read from it. */ /** Open the input file to read from it. */
if ( (error = avformat_open_input( &input_format_context, filepath, NULL, NULL)) < 0 ) { if ( (error = avformat_open_input( &input_format_context, filepath, NULL, NULL)) < 0 ) {
Error("Could not open input file '%s' (error '%s')\n", Error("Could not open input file '%s' (error '%s')",
filepath, av_make_error_string(error).c_str() ); filepath, av_make_error_string(error).c_str());
input_format_context = NULL; input_format_context = NULL;
return error; return error;
} }
/** Get information on the input file (number of streams etc.). */ /** Get information on the input file (number of streams etc.). */
if ( (error = avformat_find_stream_info(input_format_context, NULL)) < 0 ) { if ( (error = avformat_find_stream_info(input_format_context, NULL)) < 0 ) {
Error( "Could not open find stream info (error '%s')\n", Error("Could not open find stream info (error '%s')",
av_make_error_string(error).c_str() ); av_make_error_string(error).c_str() );
avformat_close_input(&input_format_context); avformat_close_input(&input_format_context);
return error; return error;
@ -51,37 +51,37 @@ int FFmpeg_Input::Open( const char *filepath ) {
video_stream_id = i; video_stream_id = i;
// if we break, then we won't find the audio stream // if we break, then we won't find the audio stream
} else { } else {
Warning( "Have another video stream." ); Warning("Have another video stream.");
} }
} else if ( is_audio_stream( input_format_context->streams[i] ) ) { } else if ( is_audio_stream(input_format_context->streams[i]) ) {
if ( audio_stream_id == -1 ) { if ( audio_stream_id == -1 ) {
audio_stream_id = i; audio_stream_id = i;
} else { } else {
Warning( "Have another audio stream." ); Warning("Have another audio stream.");
} }
} }
streams[i].frame_count = 0; streams[i].frame_count = 0;
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
streams[i].context = avcodec_alloc_context3( NULL ); streams[i].context = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context( streams[i].context, input_format_context->streams[i]->codecpar ); avcodec_parameters_to_context(streams[i].context, input_format_context->streams[i]->codecpar);
#else #else
streams[i].context = input_format_context->streams[i]->codec; streams[i].context = input_format_context->streams[i]->codec;
#endif #endif
if ( !(streams[i].codec = avcodec_find_decoder(streams[i].context->codec_id)) ) { if ( !(streams[i].codec = avcodec_find_decoder(streams[i].context->codec_id)) ) {
Error( "Could not find input codec\n"); Error("Could not find input codec\n");
avformat_close_input(&input_format_context); avformat_close_input(&input_format_context);
return AVERROR_EXIT; return AVERROR_EXIT;
} else { } else {
Debug(1, "Using codec (%s) for stream %d", streams[i].codec->name, i ); Debug(1, "Using codec (%s) for stream %d", streams[i].codec->name, i);
} }
if ((error = avcodec_open2( streams[i].context, streams[i].codec, NULL)) < 0) { if ((error = avcodec_open2( streams[i].context, streams[i].codec, NULL)) < 0) {
Error( "Could not open input codec (error '%s')\n", Error("Could not open input codec (error '%s')\n",
av_make_error_string(error).c_str() ); av_make_error_string(error).c_str());
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
avcodec_free_context( &streams[i].context ); avcodec_free_context(&streams[i].context);
#endif #endif
avformat_close_input(&input_format_context); avformat_close_input(&input_format_context);
return error; return error;
@ -89,9 +89,9 @@ int FFmpeg_Input::Open( const char *filepath ) {
} // end foreach stream } // end foreach stream
if ( video_stream_id == -1 ) if ( video_stream_id == -1 )
Error( "Unable to locate video stream in %s", filepath ); Error("Unable to locate video stream in %s", filepath);
if ( audio_stream_id == -1 ) if ( audio_stream_id == -1 )
Debug( 3, "Unable to locate audio stream in %s", filepath ); Debug(3, "Unable to locate audio stream in %s", filepath);
return 0; return 0;
} // end int FFmpeg_Input::Open( const char * filepath ) } // end int FFmpeg_Input::Open( const char * filepath )
@ -99,9 +99,9 @@ int FFmpeg_Input::Open( const char *filepath ) {
int FFmpeg_Input::Close( ) { int FFmpeg_Input::Close( ) {
for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) { for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) {
if ( streams[i].context ) { if ( streams[i].context ) {
avcodec_close( streams[i].context ); avcodec_close(streams[i].context);
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
avcodec_free_context(& streams[i].context ); avcodec_free_context(&streams[i].context);
#endif #endif
streams[i].context = NULL; streams[i].context = NULL;
} }
@ -109,20 +109,20 @@ int FFmpeg_Input::Close( ) {
if ( input_format_context ) { if ( input_format_context ) {
#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0)
av_close_input_file( input_format_context ); av_close_input_file(input_format_context);
#else #else
avformat_close_input( &input_format_context ); avformat_close_input(&input_format_context);
#endif #endif
input_format_context = NULL; input_format_context = NULL;
} }
return 1; return 1;
} // end int FFmpeg_Input::Close() } // end int FFmpeg_Input::Close()
AVFrame *FFmpeg_Input::get_frame( int stream_id, int frame_number ) { AVFrame *FFmpeg_Input::get_frame(int stream_id, int frame_number) {
Debug(1, "Getting frame from stream %d, frame_number(%d)", stream_id, frame_number ); Debug(1, "Getting frame from stream %d, frame_number(%d)", stream_id, frame_number);
AVPacket packet; AVPacket packet;
av_init_packet( &packet ); av_init_packet(&packet);
AVFrame *frame = zm_av_frame_alloc(); AVFrame *frame = zm_av_frame_alloc();
while ( frame_number >= streams[stream_id].frame_count ) { while ( frame_number >= streams[stream_id].frame_count ) {
@ -135,10 +135,10 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id, int frame_number ) {
// Check for Connection failure. // Check for Connection failure.
(ret == -110) (ret == -110)
) { ) {
Info( "av_read_frame returned %s.", av_make_error_string(ret).c_str() ); Info("av_read_frame returned %s.", av_make_error_string(ret).c_str());
} else { } else {
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, Error("Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret,
av_make_error_string(ret).c_str() ); av_make_error_string(ret).c_str());
} }
return NULL; return NULL;
} }
@ -148,7 +148,7 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id, int frame_number ) {
return NULL; return NULL;
} }
if ( ! zm_receive_frame( streams[packet.stream_index].context, frame, packet ) ) { if ( ! zm_receive_frame(streams[packet.stream_index].context, frame, packet) ) {
Error("Unable to get frame %d, continuing", streams[packet.stream_index].frame_count); Error("Unable to get frame %d, continuing", streams[packet.stream_index].frame_count);
zm_av_packet_unref( &packet ); zm_av_packet_unref( &packet );
continue; continue;
@ -157,7 +157,7 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id, int frame_number ) {
streams[packet.stream_index].frame_count += 1; streams[packet.stream_index].frame_count += 1;
} }
zm_av_packet_unref( &packet ); zm_av_packet_unref(&packet);
if ( frame_number == -1 ) if ( frame_number == -1 )
break; break;

View File

@ -768,7 +768,7 @@ bool Image::ReadRaw( const char *filename ) {
return false; return false;
} }
if ( statbuf.st_size != size ) { if ( (unsigned int)statbuf.st_size != size ) {
fclose(infile); fclose(infile);
Error("Raw file size mismatch, expected %d bytes, found %ld", size, statbuf.st_size); Error("Raw file size mismatch, expected %d bytes, found %ld", size, statbuf.st_size);
return false; return false;
@ -803,57 +803,51 @@ bool Image::WriteRaw( const char *filename ) const {
return true; return true;
} }
bool Image::ReadJpeg( const char *filename, unsigned int p_colours, unsigned int p_subpixelorder) bool Image::ReadJpeg(const char *filename, unsigned int p_colours, unsigned int p_subpixelorder) {
{
unsigned int new_width, new_height, new_colours, new_subpixelorder; unsigned int new_width, new_height, new_colours, new_subpixelorder;
struct jpeg_decompress_struct *cinfo = readjpg_dcinfo; struct jpeg_decompress_struct *cinfo = readjpg_dcinfo;
if ( !cinfo ) if ( !cinfo ) {
{
cinfo = readjpg_dcinfo = new jpeg_decompress_struct; cinfo = readjpg_dcinfo = new jpeg_decompress_struct;
cinfo->err = jpeg_std_error( &jpg_err.pub ); cinfo->err = jpeg_std_error(&jpg_err.pub);
jpg_err.pub.error_exit = zm_jpeg_error_exit; jpg_err.pub.error_exit = zm_jpeg_error_exit;
jpg_err.pub.emit_message = zm_jpeg_emit_message; jpg_err.pub.emit_message = zm_jpeg_emit_message;
jpeg_create_decompress( cinfo ); jpeg_create_decompress(cinfo);
} }
FILE *infile; FILE *infile;
if ( (infile = fopen( filename, "rb" )) == NULL ) if ( (infile = fopen(filename, "rb")) == NULL ) {
{ Error("Can't open %s: %s", filename, strerror(errno));
Error( "Can't open %s: %s", filename, strerror(errno) ); return false;
return( false );
} }
if ( setjmp( jpg_err.setjmp_buffer ) ) if ( setjmp(jpg_err.setjmp_buffer) ) {
{ jpeg_abort_decompress(cinfo);
jpeg_abort_decompress( cinfo ); fclose(infile);
fclose( infile ); return false;
return( false );
} }
jpeg_stdio_src( cinfo, infile ); jpeg_stdio_src(cinfo, infile);
jpeg_read_header( cinfo, TRUE ); jpeg_read_header(cinfo, TRUE);
if ( cinfo->num_components != 1 && cinfo->num_components != 3 ) if ( cinfo->num_components != 1 && cinfo->num_components != 3 ) {
{
Error( "Unexpected colours when reading jpeg image: %d", colours ); Error( "Unexpected colours when reading jpeg image: %d", colours );
jpeg_abort_decompress( cinfo ); jpeg_abort_decompress(cinfo);
fclose( infile ); fclose(infile);
return( false ); return false;
} }
/* Check if the image has at least one huffman table defined. If not, use the standard ones */ /* Check if the image has at least one huffman table defined. If not, use the standard ones */
/* This is required for the MJPEG capture palette of USB devices */ /* This is required for the MJPEG capture palette of USB devices */
if(cinfo->dc_huff_tbl_ptrs[0] == NULL) { if ( cinfo->dc_huff_tbl_ptrs[0] == NULL ) {
zm_use_std_huff_tables(cinfo); zm_use_std_huff_tables(cinfo);
} }
new_width = cinfo->image_width; new_width = cinfo->image_width;
new_height = cinfo->image_height; new_height = cinfo->image_height;
if ( width != new_width || height != new_height ) if ( width != new_width || height != new_height ) {
{
Debug(9,"Image dimensions differ. Old: %ux%u New: %ux%u",width,height,new_width,new_height); Debug(9,"Image dimensions differ. Old: %ux%u New: %ux%u",width,height,new_width,new_height);
} }
@ -1663,30 +1657,24 @@ void Image::Blend( const Image &image, int transparency )
AssignDirect( width, height, colours, subpixelorder, new_buffer, size, ZM_BUFTYPE_ZM); AssignDirect( width, height, colours, subpixelorder, new_buffer, size, ZM_BUFTYPE_ZM);
} }
Image *Image::Merge( unsigned int n_images, Image *images[] ) Image *Image::Merge( unsigned int n_images, Image *images[] ) {
{ if ( n_images == 1 ) return new Image(*images[0]);
if ( n_images <= 0 ) return( 0 );
if ( n_images == 1 ) return( new Image( *images[0] ) );
unsigned int width = images[0]->width; unsigned int width = images[0]->width;
unsigned int height = images[0]->height; unsigned int height = images[0]->height;
unsigned int colours = images[0]->colours; unsigned int colours = images[0]->colours;
for ( unsigned int i = 1; i < n_images; i++ ) for ( unsigned int i = 1; i < n_images; i++ ) {
{ if ( !(width == images[i]->width && height == images[i]->height && colours == images[i]->colours) ) {
if ( !(width == images[i]->width && height == images[i]->height && colours == images[i]->colours) )
{
Panic( "Attempt to merge different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d", width, height, colours, images[i]->width, images[i]->height, images[i]->colours, i ); Panic( "Attempt to merge different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d", width, height, colours, images[i]->width, images[i]->height, images[i]->colours, i );
} }
} }
Image *result = new Image( width, height, images[0]->colours, images[0]->subpixelorder); Image *result = new Image( width, height, images[0]->colours, images[0]->subpixelorder);
unsigned int size = result->size; unsigned int size = result->size;
for ( unsigned int i = 0; i < size; i++ ) for ( unsigned int i = 0; i < size; i++ ) {
{
unsigned int total = 0; unsigned int total = 0;
uint8_t *pdest = result->buffer; uint8_t *pdest = result->buffer;
for ( unsigned int j = 0; j < n_images; j++ ) for ( unsigned int j = 0; j < n_images; j++ ) {
{
uint8_t *psrc = images[j]->buffer; uint8_t *psrc = images[j]->buffer;
total += *psrc; total += *psrc;
psrc++; psrc++;
@ -1694,21 +1682,17 @@ Image *Image::Merge( unsigned int n_images, Image *images[] )
*pdest = total/n_images; *pdest = total/n_images;
pdest++; pdest++;
} }
return( result ); return result;
} }
Image *Image::Merge( unsigned int n_images, Image *images[], double weight ) Image *Image::Merge( unsigned int n_images, Image *images[], double weight ) {
{ if ( n_images == 1 ) return new Image(*images[0]);
if ( n_images <= 0 ) return( 0 );
if ( n_images == 1 ) return( new Image( *images[0] ) );
unsigned int width = images[0]->width; unsigned int width = images[0]->width;
unsigned int height = images[0]->height; unsigned int height = images[0]->height;
unsigned int colours = images[0]->colours; unsigned int colours = images[0]->colours;
for ( unsigned int i = 1; i < n_images; i++ ) for ( unsigned int i = 1; i < n_images; i++ ) {
{ if ( !(width == images[i]->width && height == images[i]->height && colours == images[i]->colours) ) {
if ( !(width == images[i]->width && height == images[i]->height && colours == images[i]->colours) )
{
Panic( "Attempt to merge different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d", width, height, colours, images[i]->width, images[i]->height, images[i]->colours, i ); Panic( "Attempt to merge different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d", width, height, colours, images[i]->width, images[i]->height, images[i]->colours, i );
} }
} }
@ -1716,55 +1700,46 @@ Image *Image::Merge( unsigned int n_images, Image *images[], double weight )
Image *result = new Image( *images[0] ); Image *result = new Image( *images[0] );
unsigned int size = result->size; unsigned int size = result->size;
double factor = 1.0*weight; double factor = 1.0*weight;
for ( unsigned int i = 1; i < n_images; i++ ) for ( unsigned int i = 1; i < n_images; i++ ) {
{
uint8_t *pdest = result->buffer; uint8_t *pdest = result->buffer;
uint8_t *psrc = images[i]->buffer; uint8_t *psrc = images[i]->buffer;
for ( unsigned int j = 0; j < size; j++ ) for ( unsigned int j = 0; j < size; j++ ) {
{
*pdest = (uint8_t)(((*pdest)*(1.0-factor))+((*psrc)*factor)); *pdest = (uint8_t)(((*pdest)*(1.0-factor))+((*psrc)*factor));
pdest++; pdest++;
psrc++; psrc++;
} }
factor *= weight; factor *= weight;
} }
return( result ); return result;
} }
Image *Image::Highlight( unsigned int n_images, Image *images[], const Rgb threshold, const Rgb ref_colour ) Image *Image::Highlight( unsigned int n_images, Image *images[], const Rgb threshold, const Rgb ref_colour )
{ {
if ( n_images <= 0 ) return( 0 ); if ( n_images == 1 ) return new Image(*images[0]);
if ( n_images == 1 ) return( new Image( *images[0] ) );
unsigned int width = images[0]->width; unsigned int width = images[0]->width;
unsigned int height = images[0]->height; unsigned int height = images[0]->height;
unsigned int colours = images[0]->colours; unsigned int colours = images[0]->colours;
for ( unsigned int i = 1; i < n_images; i++ ) for ( unsigned int i = 1; i < n_images; i++ ) {
{ if ( !(width == images[i]->width && height == images[i]->height && colours == images[i]->colours) ) {
if ( !(width == images[i]->width && height == images[i]->height && colours == images[i]->colours) )
{
Panic( "Attempt to highlight different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d", width, height, colours, images[i]->width, images[i]->height, images[i]->colours, i ); Panic( "Attempt to highlight different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d", width, height, colours, images[i]->width, images[i]->height, images[i]->colours, i );
} }
} }
Image *result = new Image( width, height, images[0]->colours, images[0]->subpixelorder ); Image *result = new Image( width, height, images[0]->colours, images[0]->subpixelorder );
unsigned int size = result->size; unsigned int size = result->size;
for ( unsigned int c = 0; c < colours; c++ ) for ( unsigned int c = 0; c < colours; c++ ) {
{
unsigned int ref_colour_rgb = RGB_VAL(ref_colour,c); unsigned int ref_colour_rgb = RGB_VAL(ref_colour,c);
for ( unsigned int i = 0; i < size; i++ ) for ( unsigned int i = 0; i < size; i++ ) {
{
unsigned int count = 0; unsigned int count = 0;
uint8_t *pdest = result->buffer+c; uint8_t *pdest = result->buffer+c;
for ( unsigned int j = 0; j < n_images; j++ ) for ( unsigned int j = 0; j < n_images; j++ ) {
{
uint8_t *psrc = images[j]->buffer+c; uint8_t *psrc = images[j]->buffer+c;
unsigned int diff = ((*psrc)-ref_colour_rgb) > 0 ? (*psrc)-ref_colour_rgb : ref_colour_rgb - (*psrc); unsigned int diff = ((*psrc)-ref_colour_rgb) > 0 ? (*psrc)-ref_colour_rgb : ref_colour_rgb - (*psrc);
if (diff >= RGB_VAL(threshold,c)) if (diff >= RGB_VAL(threshold,c)) {
{
count++; count++;
} }
psrc += colours; psrc += colours;

View File

@ -668,7 +668,7 @@ LocalCamera::~LocalCamera() {
sws_freeContext(imgConversionContext); sws_freeContext(imgConversionContext);
imgConversionContext = NULL; imgConversionContext = NULL;
av_frame_free( &tmpPicture ); av_frame_free(&tmpPicture);
} }
#endif #endif
} // end LocalCamera::~LocalCamera } // end LocalCamera::~LocalCamera
@ -1091,54 +1091,51 @@ void LocalCamera::Initialise() {
void LocalCamera::Terminate() { void LocalCamera::Terminate() {
#if ZM_HAS_V4L2 #if ZM_HAS_V4L2
if ( v4l_version == 2 ) { if ( v4l_version == 2 ) {
Debug( 3, "Terminating video stream" ); Debug(3, "Terminating video stream");
//enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
// enum v4l2_buf_type type = v4l2_data.fmt.type; // enum v4l2_buf_type type = v4l2_data.fmt.type;
enum v4l2_buf_type type = (v4l2_buf_type)v4l2_data.fmt.type; enum v4l2_buf_type type = (v4l2_buf_type)v4l2_data.fmt.type;
if ( vidioctl( vid_fd, VIDIOC_STREAMOFF, &type ) < 0 ) if ( vidioctl(vid_fd, VIDIOC_STREAMOFF, &type) < 0 )
Error( "Failed to stop capture stream: %s", strerror(errno) ); Error("Failed to stop capture stream: %s", strerror(errno));
Debug( 3, "Unmapping video buffers" ); Debug(3, "Unmapping video buffers");
for ( unsigned int i = 0; i < v4l2_data.reqbufs.count; i++ ) { for ( unsigned int i = 0; i < v4l2_data.reqbufs.count; i++ ) {
#if HAVE_LIBSWSCALE #if HAVE_LIBSWSCALE
/* Free capture pictures */ /* Free capture pictures */
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
av_frame_free( &capturePictures[i] ); av_frame_free(&capturePictures[i]);
#else #else
av_freep( &capturePictures[i] ); av_freep(&capturePictures[i]);
#endif #endif
#endif #endif
if ( munmap( v4l2_data.buffers[i].start, v4l2_data.buffers[i].length ) < 0 ) if ( munmap(v4l2_data.buffers[i].start, v4l2_data.buffers[i].length) < 0 )
Error( "Failed to munmap buffer %d: %s", i, strerror(errno) ); Error("Failed to munmap buffer %d: %s", i, strerror(errno));
} }
} else } else
#endif // ZM_HAS_V4L2 #endif // ZM_HAS_V4L2
#if ZM_HAS_V4L1 #if ZM_HAS_V4L1
if ( v4l_version == 1 ) { if ( v4l_version == 1 ) {
#if HAVE_LIBSWSCALE #if HAVE_LIBSWSCALE
for(int i=0; i < v4l1_data.frames.frames; i++) { for( int i=0; i < v4l1_data.frames.frames; i++ ) {
/* Free capture pictures */ /* Free capture pictures */
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
av_frame_free( &capturePictures[i] ); av_frame_free(&capturePictures[i]);
#else #else
av_freep( &capturePictures[i] ); av_freep(&capturePictures[i]);
#endif #endif
} }
#endif #endif
Debug( 3, "Unmapping video buffers" ); Debug(3, "Unmapping video buffers");
if ( munmap((char*)v4l1_data.bufptr, v4l1_data.frames.size) < 0 ) if ( munmap((char*)v4l1_data.bufptr, v4l1_data.frames.size) < 0 )
Error( "Failed to munmap buffers: %s", strerror(errno) ); Error("Failed to munmap buffers: %s", strerror(errno));
delete[] v4l1_data.buffers; delete[] v4l1_data.buffers;
} // end if using v4l1 } // end if using v4l1
#endif // ZM_HAS_V4L1 #endif // ZM_HAS_V4L1
close( vid_fd ); close(vid_fd);
} // end LocalCamera::Terminate } // end LocalCamera::Terminate
uint32_t LocalCamera::AutoSelectFormat(int p_colours) { uint32_t LocalCamera::AutoSelectFormat(int p_colours) {

View File

@ -62,22 +62,22 @@ void Logger::usrHandler( int sig ) {
logger->level( logger->level()+1 ); logger->level( logger->level()+1 );
else if ( sig == SIGUSR2 ) else if ( sig == SIGUSR2 )
logger->level( logger->level()-1 ); logger->level( logger->level()-1 );
Info( "Logger - Level changed to %d", logger->level() ); Info("Logger - Level changed to %d", logger->level());
} }
Logger::Logger() : Logger::Logger() :
mLevel( INFO ), mLevel(INFO),
mTermLevel( NOLOG ), mTerminalLevel(NOLOG),
mDatabaseLevel( NOLOG ), mDatabaseLevel(NOLOG),
mFileLevel( NOLOG ), mFileLevel(NOLOG),
mSyslogLevel( NOLOG ), mSyslogLevel(NOLOG),
mEffectiveLevel( NOLOG ), mEffectiveLevel(NOLOG),
//mLogPath( staticConfig.PATH_LOGS.c_str() ), //mLogPath( staticConfig.PATH_LOGS.c_str() ),
//mLogFile( mLogPath+"/"+mId+".log" ), //mLogFile( mLogPath+"/"+mId+".log" ),
mDbConnected( false ), mDbConnected(false),
mLogFileFP( NULL ), mLogFileFP(NULL),
mHasTerm( false ), mHasTerminal(false),
mFlush( false ) { mFlush(false) {
if ( smInstance ) { if ( smInstance ) {
Panic( "Attempt to create second instance of Logger class" ); Panic( "Attempt to create second instance of Logger class" );
@ -99,7 +99,7 @@ Logger::Logger() :
char code[4] = ""; char code[4] = "";
for ( int i = DEBUG1; i <= DEBUG9; i++ ) { for ( int i = DEBUG1; i <= DEBUG9; i++ ) {
snprintf( code, sizeof(code), "DB%d", i ); snprintf(code, sizeof(code), "DB%d", i);
smCodes[i] = code; smCodes[i] = code;
smSyslogPriorities[i] = LOG_DEBUG; smSyslogPriorities[i] = LOG_DEBUG;
} }
@ -108,13 +108,14 @@ Logger::Logger() :
} }
if ( fileno(stderr) && isatty(fileno(stderr)) ) if ( fileno(stderr) && isatty(fileno(stderr)) )
mHasTerm = true; mHasTerminal = true;
} }
Logger::~Logger() { Logger::~Logger() {
terminate(); terminate();
smCodes.clear(); smCodes.clear();
smSyslogPriorities.clear(); smSyslogPriorities.clear();
smInitialised = false;
#if 0 #if 0
for ( StringMap::iterator itr = smCodes.begin(); itr != smCodes.end(); itr ++ ) { for ( StringMap::iterator itr = smCodes.begin(); itr != smCodes.end(); itr ++ ) {
smCodes.erase( itr ); smCodes.erase( itr );
@ -125,7 +126,7 @@ Logger::~Logger() {
#endif #endif
} }
void Logger::initialise( const std::string &id, const Options &options ) { void Logger::initialise(const std::string &id, const Options &options) {
char *envPtr; char *envPtr;
if ( !id.empty() ) if ( !id.empty() )
@ -133,7 +134,7 @@ void Logger::initialise( const std::string &id, const Options &options ) {
std::string tempLogFile; std::string tempLogFile;
if ( (envPtr = getTargettedEnv( "LOG_FILE" )) ) if ( (envPtr = getTargettedEnv("LOG_FILE")) )
tempLogFile = envPtr; tempLogFile = envPtr;
else if ( options.mLogFile.size() ) else if ( options.mLogFile.size() )
tempLogFile = options.mLogFile; tempLogFile = options.mLogFile;
@ -145,13 +146,13 @@ void Logger::initialise( const std::string &id, const Options &options ) {
} }
Level tempLevel = INFO; Level tempLevel = INFO;
Level tempTermLevel = mTermLevel; Level tempTerminalLevel = mTerminalLevel;
Level tempDatabaseLevel = mDatabaseLevel; Level tempDatabaseLevel = mDatabaseLevel;
Level tempFileLevel = mFileLevel; Level tempFileLevel = mFileLevel;
Level tempSyslogLevel = mSyslogLevel; Level tempSyslogLevel = mSyslogLevel;
if ( options.mTermLevel != NOOPT ) if ( options.mTerminalLevel != NOOPT )
tempTermLevel = options.mTermLevel; tempTerminalLevel = options.mTerminalLevel;
if ( options.mDatabaseLevel != NOOPT ) if ( options.mDatabaseLevel != NOOPT )
tempDatabaseLevel = options.mDatabaseLevel; tempDatabaseLevel = options.mDatabaseLevel;
@ -170,22 +171,22 @@ void Logger::initialise( const std::string &id, const Options &options ) {
// Legacy // Legacy
if ( (envPtr = getenv( "LOG_PRINT" )) ) if ( (envPtr = getenv( "LOG_PRINT" )) )
tempTermLevel = atoi(envPtr) ? DEBUG9 : NOLOG; tempTerminalLevel = atoi(envPtr) ? DEBUG9 : NOLOG;
if ( (envPtr = getTargettedEnv( "LOG_LEVEL" )) ) if ( (envPtr = getTargettedEnv("LOG_LEVEL")) )
tempLevel = atoi(envPtr); tempLevel = atoi(envPtr);
if ( (envPtr = getTargettedEnv( "LOG_LEVEL_TERM" )) ) if ( (envPtr = getTargettedEnv("LOG_LEVEL_TERM")) )
tempTermLevel = atoi(envPtr); tempTerminalLevel = atoi(envPtr);
if ( (envPtr = getTargettedEnv( "LOG_LEVEL_DATABASE" )) ) if ( (envPtr = getTargettedEnv("LOG_LEVEL_DATABASE")) )
tempDatabaseLevel = atoi(envPtr); tempDatabaseLevel = atoi(envPtr);
if ( (envPtr = getTargettedEnv( "LOG_LEVEL_FILE" )) ) if ( (envPtr = getTargettedEnv("LOG_LEVEL_FILE")) )
tempFileLevel = atoi(envPtr); tempFileLevel = atoi(envPtr);
if ( (envPtr = getTargettedEnv( "LOG_LEVEL_SYSLOG" )) ) if ( (envPtr = getTargettedEnv("LOG_LEVEL_SYSLOG")) )
tempSyslogLevel = atoi(envPtr); tempSyslogLevel = atoi(envPtr);
if ( config.log_debug ) { if ( config.log_debug ) {
StringVector targets = split( config.log_debug_target, "|" ); StringVector targets = split(config.log_debug_target, "|");
for ( unsigned int i = 0; i < targets.size(); i++ ) { for ( unsigned int i = 0; i < targets.size(); i++ ) {
const std::string &target = targets[i]; const std::string &target = targets[i];
if ( target == mId || target == "_"+mId || target == "_"+mIdRoot || target == "" ) { if ( target == mId || target == "_"+mId || target == "_"+mIdRoot || target == "" ) {
@ -202,20 +203,19 @@ void Logger::initialise( const std::string &id, const Options &options ) {
// if we don't have debug turned on, then the max effective log level is INFO // if we don't have debug turned on, then the max effective log level is INFO
if ( tempSyslogLevel > INFO ) tempSyslogLevel = INFO; if ( tempSyslogLevel > INFO ) tempSyslogLevel = INFO;
if ( tempFileLevel > INFO ) tempFileLevel = INFO; if ( tempFileLevel > INFO ) tempFileLevel = INFO;
if ( tempTermLevel > INFO ) tempTermLevel = INFO; if ( tempTerminalLevel > INFO ) tempTerminalLevel = INFO;
if ( tempDatabaseLevel > INFO ) tempDatabaseLevel = INFO; if ( tempDatabaseLevel > INFO ) tempDatabaseLevel = INFO;
if ( tempLevel > INFO ) tempLevel = INFO; if ( tempLevel > INFO ) tempLevel = INFO;
} // end if config.log_debug } // end if config.log_debug
logFile(tempLogFile);
logFile( tempLogFile ); terminalLevel(tempTerminalLevel);
databaseLevel(tempDatabaseLevel);
fileLevel(tempFileLevel);
syslogLevel(tempSyslogLevel);
termLevel( tempTermLevel ); level(tempLevel);
databaseLevel( tempDatabaseLevel );
fileLevel( tempFileLevel );
syslogLevel( tempSyslogLevel );
level( tempLevel );
mFlush = false; mFlush = false;
if ( (envPtr = getenv("LOG_FLUSH")) ) { if ( (envPtr = getenv("LOG_FLUSH")) ) {
@ -224,27 +224,27 @@ void Logger::initialise( const std::string &id, const Options &options ) {
mFlush = true; mFlush = true;
} }
//mRuntime = (envPtr = getenv( "LOG_RUNTIME")) ? atoi( envPtr ) : false;
{ {
struct sigaction action; struct sigaction action;
memset( &action, 0, sizeof(action) ); memset(&action, 0, sizeof(action));
action.sa_handler = usrHandler; action.sa_handler = usrHandler;
action.sa_flags = SA_RESTART; action.sa_flags = SA_RESTART;
if ( sigaction( SIGUSR1, &action, 0 ) < 0 ) { // Does this REALLY need to be fatal?
Fatal( "sigaction(), error = %s", strerror(errno) ); if ( sigaction(SIGUSR1, &action, 0) < 0 ) {
Fatal("sigaction(), error = %s", strerror(errno));
} }
if ( sigaction( SIGUSR2, &action, 0 ) < 0) { if ( sigaction(SIGUSR2, &action, 0) < 0) {
Fatal( "sigaction(), error = %s", strerror(errno) ); Fatal("sigaction(), error = %s", strerror(errno));
} }
} }
mInitialised = true; mInitialised = true;
Debug( 1, "LogOpts: level=%s/%s, screen=%s, database=%s, logfile=%s->%s, syslog=%s", Debug(1, "LogOpts: level=%s/%s, screen=%s, database=%s, logfile=%s->%s, syslog=%s",
smCodes[mLevel].c_str(), smCodes[mLevel].c_str(),
smCodes[mEffectiveLevel].c_str(), smCodes[mEffectiveLevel].c_str(),
smCodes[mTermLevel].c_str(), smCodes[mTerminalLevel].c_str(),
smCodes[mDatabaseLevel].c_str(), smCodes[mDatabaseLevel].c_str(),
smCodes[mFileLevel].c_str(), smCodes[mFileLevel].c_str(),
mLogFile.c_str(), mLogFile.c_str(),
@ -253,7 +253,7 @@ void Logger::initialise( const std::string &id, const Options &options ) {
} }
void Logger::terminate() { void Logger::terminate() {
Debug(1, "Terminating Logger" ); Debug(1, "Terminating Logger");
if ( mFileLevel > NOLOG ) if ( mFileLevel > NOLOG )
closeFile(); closeFile();
@ -265,66 +265,67 @@ void Logger::terminate() {
closeDatabase(); closeDatabase();
} }
bool Logger::boolEnv( const std::string &name, bool defaultValue ) { // These don't belong here, they have nothing to do with logging
const char *envPtr = getenv( name.c_str() ); bool Logger::boolEnv(const std::string &name, bool defaultValue) {
return( envPtr ? atoi( envPtr ) : defaultValue ); const char *envPtr = getenv(name.c_str());
return envPtr ? atoi(envPtr) : defaultValue;
} }
int Logger::intEnv( const std::string &name, bool defaultValue ) { int Logger::intEnv(const std::string &name, bool defaultValue) {
const char *envPtr = getenv( name.c_str() ); const char *envPtr = getenv(name.c_str());
return( envPtr ? atoi( envPtr ) : defaultValue ); return envPtr ? atoi(envPtr) : defaultValue;
} }
std::string Logger::strEnv( const std::string &name, const std::string &defaultValue ) { std::string Logger::strEnv(const std::string &name, const std::string &defaultValue) {
const char *envPtr = getenv( name.c_str() ); const char *envPtr = getenv(name.c_str());
return( envPtr ? envPtr : defaultValue ); return envPtr ? envPtr : defaultValue;
} }
char *Logger::getTargettedEnv( const std::string &name ) { char *Logger::getTargettedEnv(const std::string &name) {
std::string envName = name+"_"+mId; std::string envName = name+"_"+mId;
char *envPtr = getenv( envName.c_str() ); char *envPtr = getenv(envName.c_str());
if ( !envPtr && mId != mIdRoot ) { if ( !envPtr && mId != mIdRoot ) {
envName = name+"_"+mIdRoot; envName = name+"_"+mIdRoot;
envPtr = getenv( envName.c_str() ); envPtr = getenv(envName.c_str());
} }
if ( !envPtr ) if ( !envPtr )
envPtr = getenv( name.c_str() ); envPtr = getenv(name.c_str());
return envPtr; return envPtr;
} }
const std::string &Logger::id( const std::string &id ) { const std::string &Logger::id(const std::string &id) {
std::string tempId = id; std::string tempId = id;
size_t pos; size_t pos;
// Remove whitespace // Remove whitespace
while ( (pos = tempId.find_first_of( " \t" )) != std::string::npos ) { while ( (pos = tempId.find_first_of( " \t" )) != std::string::npos ) {
tempId.replace( pos, 1, "" ); tempId.replace(pos, 1, "");
} }
// Replace non-alphanum with underscore // Replace non-alphanum with underscore
while ( (pos = tempId.find_first_not_of( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" )) != std::string::npos ) { while ( (pos = tempId.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_")) != std::string::npos ) {
tempId.replace( pos, 1, "_" ); tempId.replace(pos, 1, "_");
} }
if ( mId != tempId ) { if ( mId != tempId ) {
mId = tempId; mId = tempId;
pos = mId.find( '_' ); pos = mId.find('_');
if ( pos != std::string::npos ) { if ( pos != std::string::npos ) {
mIdRoot = mId.substr( 0, pos ); mIdRoot = mId.substr(0, pos);
if ( ++pos < mId.size() ) if ( ++pos < mId.size() )
mIdArgs = mId.substr( pos ); mIdArgs = mId.substr(pos);
} }
} }
return( mId ); return mId;
} }
Logger::Level Logger::level( Logger::Level level ) { Logger::Level Logger::level(Logger::Level level) {
if ( level > NOOPT ) { if ( level > NOOPT ) {
level = limit(level); level = limit(level);
if ( mLevel != level ) if ( mLevel != level )
mLevel = level; mLevel = level;
mEffectiveLevel = NOLOG; mEffectiveLevel = NOLOG;
if ( mTermLevel > mEffectiveLevel ) if ( mTerminalLevel > mEffectiveLevel )
mEffectiveLevel = mTermLevel; mEffectiveLevel = mTerminalLevel;
if ( mDatabaseLevel > mEffectiveLevel ) if ( mDatabaseLevel > mEffectiveLevel )
mEffectiveLevel = mDatabaseLevel; mEffectiveLevel = mDatabaseLevel;
if ( mFileLevel > mEffectiveLevel ) if ( mFileLevel > mEffectiveLevel )
@ -334,18 +335,18 @@ Logger::Level Logger::level( Logger::Level level ) {
if ( mEffectiveLevel > mLevel) if ( mEffectiveLevel > mLevel)
mEffectiveLevel = mLevel; mEffectiveLevel = mLevel;
} }
return( mLevel ); return mLevel;
} }
Logger::Level Logger::termLevel( Logger::Level termLevel ) { Logger::Level Logger::terminalLevel( Logger::Level terminalLevel ) {
if ( termLevel > NOOPT ) { if ( terminalLevel > NOOPT ) {
if ( !mHasTerm ) if ( !mHasTerminal )
termLevel = NOLOG; terminalLevel = NOLOG;
termLevel = limit(termLevel); terminalLevel = limit(terminalLevel);
if ( mTermLevel != termLevel ) if ( mTerminalLevel != terminalLevel )
mTermLevel = termLevel; mTerminalLevel = terminalLevel;
} }
return( mTermLevel ); return mTerminalLevel;
} }
Logger::Level Logger::databaseLevel( Logger::Level databaseLevel ) { Logger::Level Logger::databaseLevel( Logger::Level databaseLevel ) {
@ -359,7 +360,7 @@ Logger::Level Logger::databaseLevel( Logger::Level databaseLevel ) {
} // end if ( mDatabaseLevel != databaseLevel ) } // end if ( mDatabaseLevel != databaseLevel )
} // end if ( databaseLevel > NOOPT ) } // end if ( databaseLevel > NOOPT )
return( mDatabaseLevel ); return mDatabaseLevel;
} }
Logger::Level Logger::fileLevel( Logger::Level fileLevel ) { Logger::Level Logger::fileLevel( Logger::Level fileLevel ) {
@ -372,7 +373,7 @@ Logger::Level Logger::fileLevel( Logger::Level fileLevel ) {
if ( mFileLevel > NOLOG ) if ( mFileLevel > NOLOG )
openFile(); openFile();
} }
return( mFileLevel ); return mFileLevel;
} }
Logger::Level Logger::syslogLevel( Logger::Level syslogLevel ) { Logger::Level Logger::syslogLevel( Logger::Level syslogLevel ) {
@ -386,7 +387,7 @@ Logger::Level Logger::syslogLevel( Logger::Level syslogLevel ) {
openSyslog(); openSyslog();
} }
} }
return( mSyslogLevel ); return mSyslogLevel;
} }
void Logger::logFile( const std::string &logFile ) { void Logger::logFile( const std::string &logFile ) {
@ -448,9 +449,9 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
const char *classString = smCodes[level].c_str(); const char *classString = smCodes[level].c_str();
if ( level < PANIC || level > DEBUG9 ) if ( level < PANIC || level > DEBUG9 )
Panic( "Invalid logger level %d", level ); Panic("Invalid logger level %d", level);
gettimeofday( &timeVal, NULL ); gettimeofday(&timeVal, NULL);
#if 0 #if 0
if ( logRuntime ) { if ( logRuntime ) {
@ -462,8 +463,8 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
} else { } else {
#endif #endif
char *timePtr = timeString; char *timePtr = timeString;
timePtr += strftime( timePtr, sizeof(timeString), "%x %H:%M:%S", localtime(&timeVal.tv_sec) ); timePtr += strftime(timePtr, sizeof(timeString), "%x %H:%M:%S", localtime(&timeVal.tv_sec));
snprintf( timePtr, sizeof(timeString)-(timePtr-timeString), ".%06ld", timeVal.tv_usec ); snprintf(timePtr, sizeof(timeString)-(timePtr-timeString), ".%06ld", timeVal.tv_usec);
#if 0 #if 0
} }
#endif #endif
@ -517,7 +518,7 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
char *syslogEnd = logPtr; char *syslogEnd = logPtr;
strncpy( logPtr, "]\n", sizeof(logString)-(logPtr-logString) ); strncpy( logPtr, "]\n", sizeof(logString)-(logPtr-logString) );
if ( level <= mTermLevel ) { if ( level <= mTerminalLevel ) {
puts( logString ); puts( logString );
fflush( stdout ); fflush( stdout );
} }
@ -541,10 +542,10 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
mysql_real_escape_string( &dbconn, escapedString, syslogStart, strlen(syslogStart) ); mysql_real_escape_string( &dbconn, escapedString, syslogStart, strlen(syslogStart) );
snprintf( sql, sizeof(sql), "insert into Logs ( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line ) values ( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )", timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line ); snprintf( sql, sizeof(sql), "insert into Logs ( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line ) values ( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )", timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line );
if ( mysql_query( &dbconn, sql ) ) { if (mysql_query(&dbconn, sql)) {
Level tempDatabaseLevel = mDatabaseLevel; Level tempDatabaseLevel = mDatabaseLevel;
databaseLevel( NOLOG ); databaseLevel(NOLOG);
Error( "Can't insert log entry: sql(%s) error(%s)", sql, mysql_error( &dbconn ) ); Error("Can't insert log entry: sql(%s) error(%s)", sql, mysql_error(&dbconn));
databaseLevel(tempDatabaseLevel); databaseLevel(tempDatabaseLevel);
} }
db_mutex.unlock(); db_mutex.unlock();
@ -561,7 +562,7 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
zmDbClose(); zmDbClose();
if ( level <= PANIC ) if ( level <= PANIC )
abort(); abort();
exit( -1 ); exit(-1);
} }
} }

View File

@ -58,7 +58,7 @@ public:
class Options { class Options {
public: public:
int mTermLevel; int mTerminalLevel;
int mDatabaseLevel; int mDatabaseLevel;
int mFileLevel; int mFileLevel;
int mSyslogLevel; int mSyslogLevel;
@ -67,8 +67,8 @@ public:
std::string mLogFile; std::string mLogFile;
public: public:
Options( Level termLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) : Options( Level terminalLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) :
mTermLevel( termLevel ), mTerminalLevel( terminalLevel ),
mDatabaseLevel( databaseLevel ), mDatabaseLevel( databaseLevel ),
mFileLevel( fileLevel ), mFileLevel( fileLevel ),
mSyslogLevel( syslogLevel ), mSyslogLevel( syslogLevel ),
@ -93,7 +93,7 @@ private:
std::string mIdArgs; std::string mIdArgs;
Level mLevel; // Level that is currently in operation Level mLevel; // Level that is currently in operation
Level mTermLevel; // Maximum level output via terminal Level mTerminalLevel; // Maximum level output via terminal
Level mDatabaseLevel; // Maximum level output via database Level mDatabaseLevel; // Maximum level output via database
Level mFileLevel; // Maximum level output via file Level mFileLevel; // Maximum level output via file
Level mSyslogLevel; // Maximum level output via syslog Level mSyslogLevel; // Maximum level output via syslog
@ -104,25 +104,23 @@ private:
std::string mLogFile; std::string mLogFile;
FILE *mLogFileFP; FILE *mLogFileFP;
bool mHasTerm; bool mHasTerminal;
bool mFlush; bool mFlush;
private: private:
static void usrHandler( int sig ); static void usrHandler(int sig);
public: public:
friend void logInit( const char *name, const Options &options ); friend void logInit(const char *name, const Options &options);
friend void logTerm(); friend void logTerm();
static Logger *fetch() static Logger *fetch() {
{ if ( !smInstance ) {
if ( !smInstance )
{
smInstance = new Logger(); smInstance = new Logger();
Options options; Options options;
smInstance->initialise( "undef", options ); smInstance->initialise( "undef", options );
} }
return( smInstance ); return smInstance;
} }
private: private:
@ -130,51 +128,48 @@ private:
~Logger(); ~Logger();
public: public:
void initialise( const std::string &id, const Options &options ); void initialise(const std::string &id, const Options &options);
void terminate(); void terminate();
private: private:
int limit( int level ) int limit(int level) {
{
if ( level > DEBUG9 ) if ( level > DEBUG9 )
return( DEBUG9 ); return DEBUG9;
if ( level < NOLOG ) if ( level < NOLOG )
return( NOLOG ); return NOLOG;
return( level ); return level;
} }
bool boolEnv( const std::string &name, bool defaultValue=false ); bool boolEnv(const std::string &name, bool defaultValue=false);
int intEnv( const std::string &name, bool defaultValue=0 ); int intEnv(const std::string &name, bool defaultValue=0);
std::string strEnv( const std::string &name, const std::string &defaultValue="" ); std::string strEnv(const std::string &name, const std::string &defaultValue="");
char *getTargettedEnv( const std::string &name ); char *getTargettedEnv(const std::string &name);
void loadEnv(); void loadEnv();
public: public:
const std::string &id() const const std::string &id() const {
{ return mId;
return( mId );
} }
const std::string &id( const std::string &id ); const std::string &id(const std::string &id);
Level level() const Level level() const {
{ return mLevel;
return( mLevel );
} }
Level level( Level=NOOPT ); Level level(Level=NOOPT);
bool debugOn() { bool debugOn() {
return( mEffectiveLevel >= DEBUG1 ); return mEffectiveLevel >= DEBUG1;
} }
Level termLevel( Level=NOOPT ); Level terminalLevel(Level=NOOPT);
Level databaseLevel( Level=NOOPT ); Level databaseLevel(Level=NOOPT);
Level fileLevel( Level=NOOPT ); Level fileLevel(Level=NOOPT);
Level syslogLevel( Level=NOOPT ); Level syslogLevel(Level=NOOPT);
private: private:
void logFile( const std::string &logFile ); void logFile(const std::string &logFile);
void openFile(); void openFile();
void closeFile(); void closeFile();
void openSyslog(); void openSyslog();
@ -182,22 +177,19 @@ private:
void closeDatabase(); void closeDatabase();
public: public:
void logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... ); void logPrint(bool hex, const char * const filepath, const int line, const int level, const char *fstring, ...);
}; };
void logInit( const char *name, const Logger::Options &options=Logger::Options() ); void logInit(const char *name, const Logger::Options &options=Logger::Options());
void logTerm(); void logTerm();
inline const std::string &logId() { inline const std::string &logId() {
return( Logger::fetch()->id() ); return Logger::fetch()->id();
} }
inline Logger::Level logLevel() { inline Logger::Level logLevel() {
return( Logger::fetch()->level() ); return Logger::fetch()->level();
}
inline void logCapLevel( Logger::Level level ) {
Logger::fetch()->level( level );
} }
inline Logger::Level logDebugging() { inline Logger::Level logDebugging() {
return( Logger::fetch()->debugOn() ); return Logger::fetch()->debugOn();
} }
#define logPrintf(logLevel,params...) {\ #define logPrintf(logLevel,params...) {\

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +1,28 @@
// //
// ZoneMinder Monitor Class Interfaces, $Date$, $Revision$ // ZoneMinder Monitor Class Interfaces, $Date$, $Revision$
// Copyright (C) 2001-2008 Philip Coombes // Copyright (C) 2001-2008 Philip Coombes
// //
// This program is free software; you can redistribute it and/or // This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License // modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2 // as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version. // of the License, or (at your option) any later version.
// //
// This program is distributed in the hope that it will be useful, // This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// //
#ifndef ZM_MONITOR_H #ifndef ZM_MONITOR_H
#define ZM_MONITOR_H #define ZM_MONITOR_H
#include <vector> #include <vector>
#include <sstream> #include <sstream>
#include <thread>
#include "zm.h" #include "zm.h"
#include "zm_coord.h" #include "zm_coord.h"
@ -70,7 +71,17 @@ public:
NODECT NODECT
} Function; } Function;
typedef enum { typedef enum {
LOCAL,
REMOTE,
FILE,
FFMPEG,
LIBVLC,
CURL,
NVSOCKET,
} CameraType;
typedef enum {
ROTATE_0=1, ROTATE_0=1,
ROTATE_90, ROTATE_90,
ROTATE_180, ROTATE_180,
@ -103,7 +114,7 @@ protected:
/* sizeof(SharedData) expected to be 344 bytes on 32bit and 64bit */ /* sizeof(SharedData) expected to be 344 bytes on 32bit and 64bit */
typedef struct { typedef struct {
uint32_t size; /* +0 */ uint32_t size; /* +0 */
uint32_t last_write_index; /* +4 */ uint32_t last_write_index; /* +4 */
uint32_t last_read_index; /* +8 */ uint32_t last_read_index; /* +8 */
uint32_t state; /* +12 */ uint32_t state; /* +12 */
uint32_t last_event_id; /* +16 */ uint32_t last_event_id; /* +16 */
@ -121,10 +132,10 @@ protected:
uint32_t imagesize; /* +52 */ uint32_t imagesize; /* +52 */
uint32_t epadding1; /* +56 */ uint32_t epadding1; /* +56 */
uint32_t epadding2; /* +60 */ uint32_t epadding2; /* +60 */
/* /*
** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038. ** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038.
** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16. ** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16.
*/ */
union { /* +64 */ union { /* +64 */
time_t startup_time; /* When the zmc process started. zmwatch uses this to see how long the process has been running without getting any images */ time_t startup_time; /* When the zmc process started. zmwatch uses this to see how long the process has been running without getting any images */
uint64_t extrapad1; uint64_t extrapad1;
@ -139,6 +150,8 @@ protected:
}; };
uint8_t control_state[256]; /* +88 */ uint8_t control_state[256]; /* +88 */
char alarm_cause[256];
} SharedData; } SharedData;
typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState; typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState;
@ -158,7 +171,7 @@ protected:
//sizeOf(VideoStoreData) expected to be 4104 bytes on 32bit and 64bit //sizeOf(VideoStoreData) expected to be 4104 bytes on 32bit and 64bit
typedef struct { typedef struct {
uint32_t size; uint32_t size;
uint32_t current_event; uint64_t current_event;
char event_file[4096]; char event_file[4096];
timeval recording; // used as both bool and a pointer to the timestamp when recording should begin timeval recording; // used as both bool and a pointer to the timestamp when recording should begin
} VideoStoreData; } VideoStoreData;
@ -188,27 +201,18 @@ protected:
volatile TriggerData *trigger_data; volatile TriggerData *trigger_data;
volatile VideoStoreData *video_store_data; volatile VideoStoreData *video_store_data;
int last_state; int last_state;
int last_event_id; uint64_t last_event_id;
public: public:
MonitorLink( int p_id, const char *p_name ); MonitorLink( int p_id, const char *p_name );
~MonitorLink(); ~MonitorLink();
inline int Id() const { inline int Id() const { return id; }
return( id ); inline const char *Name() const { return name; }
}
inline const char *Name() const {
return( name );
}
inline bool isConnected() const { inline bool isConnected() const { return connected; }
return( connected ); inline time_t getLastConnectTime() const { return last_connect_time; }
}
inline time_t getLastConnectTime() const {
return( last_connect_time );
}
bool connect(); bool connect();
bool disconnect(); bool disconnect();
@ -221,11 +225,29 @@ protected:
protected: protected:
// These are read from the DB and thereafter remain unchanged // These are read from the DB and thereafter remain unchanged
unsigned int id; unsigned int id;
char name[64]; char name[64];
unsigned int server_id; // Id of the Server object unsigned int server_id; // Id of the Server object
unsigned int storage_id; // Id of the Storage Object, which currently will just provide a path, but in future may do more. unsigned int storage_id; // Id of the Storage Object, which currently will just provide a path, but in future may do more.
CameraType type;
Function function; // What the monitor is doing Function function; // What the monitor is doing
bool enabled; // Whether the monitor is enabled or asleep bool enabled; // Whether the monitor is enabled or asleep
std::string protocol;
std::string method;
std::string options;
std::string host;
std::string port;
std::string user;
std::string pass;
std::string path;
char device[64];
int palette;
int channel;
int format;
unsigned int camera_width;
unsigned int camera_height;
unsigned int width; // Normally the same as the camera, but not if partly rotated unsigned int width; // Normally the same as the camera, but not if partly rotated
unsigned int height; // Normally the same as the camera, but not if partly rotated unsigned int height; // Normally the same as the camera, but not if partly rotated
bool v4l_multi_buffer; bool v4l_multi_buffer;
@ -234,8 +256,9 @@ protected:
unsigned int deinterlacing; unsigned int deinterlacing;
unsigned int deinterlacing_value; unsigned int deinterlacing_value;
bool videoRecording; bool videoRecording;
bool rtsp_describe;
int savejpegspref; int savejpegs;
int colours; int colours;
VideoWriter videowriter; VideoWriter videowriter;
std::string encoderparams; std::string encoderparams;
@ -245,10 +268,13 @@ protected:
_AVPIXELFORMAT imagePixFormat; _AVPIXELFORMAT imagePixFormat;
unsigned int subpixelorder; unsigned int subpixelorder;
bool record_audio; // Whether to store the audio that we receive bool record_audio; // Whether to store the audio that we receive
int brightness; // The statically saved brightness of the camera int brightness; // The statically saved brightness of the camera
int contrast; // The statically saved contrast of the camera int contrast; // The statically saved contrast of the camera
int hue; // The statically saved hue of the camera int hue; // The statically saved hue of the camera
int colour; // The statically saved colour of the camera int colour; // The statically saved colour of the camera
char event_prefix[64]; // The prefix applied to event names as they are created char event_prefix[64]; // The prefix applied to event names as they are created
char label_format[64]; // The format of the timestamp on the images char label_format[64]; // The format of the timestamp on the images
Coord label_coord; // The coordinates of the timestamp on the images Coord label_coord; // The coordinates of the timestamp on the images
@ -272,7 +298,7 @@ protected:
int fps_report_interval; // How many images should be captured/processed between reporting the current FPS int fps_report_interval; // How many images should be captured/processed between reporting the current FPS
int ref_blend_perc; // Percentage of new image going into reference image. int ref_blend_perc; // Percentage of new image going into reference image.
int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm. int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm.
bool track_motion; // Whether this monitor tries to track detected motion bool track_motion; // Whether this monitor tries to track detected motion
Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected
bool embed_exif; // Whether to embed Exif data into each image frame or not bool embed_exif; // Whether to embed Exif data into each image frame or not
@ -292,7 +318,7 @@ protected:
int first_alarm_count; int first_alarm_count;
int last_alarm_count; int last_alarm_count;
bool last_signal; bool last_signal;
int last_section_mod; int last_section_mod;
int buffer_count; int buffer_count;
int prealarm_count; int prealarm_count;
State state; State state;
@ -318,8 +344,8 @@ protected:
TriggerData *trigger_data; TriggerData *trigger_data;
VideoStoreData *video_store_data; VideoStoreData *video_store_data;
struct timeval *shared_timestamps; struct timeval *shared_timestamps;
unsigned char *shared_images; unsigned char *shared_images;
ZMPacket *image_buffer; ZMPacket *image_buffer;
ZMPacket next_buffer; /* Used by four field deinterlacing */ ZMPacket next_buffer; /* Used by four field deinterlacing */
@ -335,16 +361,18 @@ protected:
Image **images; Image **images;
const unsigned char *privacy_bitmask; const unsigned char *privacy_bitmask;
std::thread *event_delete_thread; // Used to close events, but continue processing.
int n_linked_monitors; int n_linked_monitors;
MonitorLink **linked_monitors; MonitorLink **linked_monitors;
public: public:
explicit Monitor( int p_id ); explicit Monitor();
explicit Monitor(int p_id);
// OurCheckAlarms seems to be unused. Check it on zm_monitor.cpp for more info. // OurCheckAlarms seems to be unused. Check it on zm_monitor.cpp for more info.
//bool OurCheckAlarms( Zone *zone, const Image *pImage ); //bool OurCheckAlarms( Zone *zone, const Image *pImage );
Monitor( Monitor(
int p_id, int p_id,
const char *p_name, const char *p_name,
unsigned int p_server_id, unsigned int p_server_id,
@ -398,37 +426,31 @@ public:
inline int ShmValid() const { inline int ShmValid() const {
return( shared_data->valid ); return( shared_data->valid );
} }
Camera *getCamera();
inline int Id() const { inline int Id() const { return id; }
return( id ); inline const char *Name() const { return name; }
}
inline const char *Name() const {
return( name );
}
inline Storage *getStorage() { inline Storage *getStorage() {
if ( ! storage ) { if ( ! storage ) {
storage = new Storage( storage_id ); storage = new Storage(storage_id);
} }
return( storage ); return storage;
} }
inline Function GetFunction() const { inline Function GetFunction() const {
return( function ); return function;
}
inline Camera *getCamera() {
return camera;
} }
inline bool Enabled() { inline bool Enabled() {
if ( function <= MONITOR ) if ( function <= MONITOR )
return( false ); return false;
return( enabled ); return enabled;
} }
inline const char *EventPrefix() const { inline const char *EventPrefix() const {
return( event_prefix ); return event_prefix;
} }
inline bool Ready() { inline bool Ready() {
if ( function <= MONITOR ) { if ( function <= MONITOR ) {
Error("Should not be calling Ready if the function doesn't include motion detection"); Error("Should not be calling Ready if the function doesn't include motion detection");
return( false ); return false;
} }
if ( image_count >= ready_count ) { if ( image_count >= ready_count ) {
return true; return true;
@ -438,57 +460,59 @@ public:
} }
inline bool Active() { inline bool Active() {
if ( function <= MONITOR ) if ( function <= MONITOR )
return( false ); return false;
return( enabled && shared_data->active ); return( enabled && shared_data->active );
} }
inline bool Exif() { inline bool Exif() {
return( embed_exif ); return embed_exif;
} }
inline bool RecordAudio() { inline bool RecordAudio() {
return record_audio; return record_audio;
} }
/*
inline Purpose Purpose() { return purpose };
inline Purpose Purpose( Purpose p ) { purpose = p; };
*/
Orientation getOrientation() const; Orientation getOrientation() const;
unsigned int Width() const { return width; } unsigned int Width() const { return width; }
unsigned int Height() const { return height; } unsigned int Height() const { return height; }
unsigned int Colours() const; unsigned int Colours() const;
unsigned int SubpixelOrder() const; unsigned int SubpixelOrder() const;
int GetOptSaveJPEGs() const { return( savejpegspref ); } int GetOptSaveJPEGs() const { return savejpegs; }
VideoWriter GetOptVideoWriter() const { return( videowriter ); } VideoWriter GetOptVideoWriter() const { return videowriter; }
const std::vector<EncoderParameter_t>* GetOptEncoderParams() const { return( &encoderparamsvec ); } const std::vector<EncoderParameter_t>* GetOptEncoderParams() const { return &encoderparamsvec; }
const std::string &GetEncoderOptions() const { return( encoderparams ); } const std::string &GetEncoderOptions() const { return encoderparams; }
const std::string &OutputCodec() const { return output_codec; } const std::string &OutputCodec() const { return output_codec; }
const std::string &OutputContainer() const { return output_container; } const std::string &OutputContainer() const { return output_container; }
uint32_t GetVideoWriterEventId() const { return video_store_data->current_event; } uint64_t GetVideoWriterEventId() const { return video_store_data->current_event; }
void SetVideoWriterEventId( uint32_t p_event_id ) { video_store_data->current_event = p_event_id; } void SetVideoWriterEventId( uint64_t p_event_id ) { video_store_data->current_event = p_event_id; }
unsigned int GetPreEventCount() const { return pre_event_count; }; unsigned int GetPreEventCount() const { return pre_event_count; };
State GetState() const; State GetState() const;
int GetImage( int index=-1, int scale=100 ); int GetImage( int index=-1, int scale=100 );
ZMPacket *getSnapshot( int index=-1 ); ZMPacket *getSnapshot( int index=-1 ) const;
struct timeval GetTimestamp( int index=-1 ); struct timeval GetTimestamp( int index=-1 ) const;
void UpdateAdaptiveSkip(); void UpdateAdaptiveSkip();
useconds_t GetAnalysisRate(); useconds_t GetAnalysisRate();
unsigned int GetAnalysisUpdateDelay() const { return( analysis_update_delay ); } unsigned int GetAnalysisUpdateDelay() const { return analysis_update_delay; }
int GetCaptureDelay() const { return( capture_delay ); } int GetCaptureDelay() const { return capture_delay; }
int GetAlarmCaptureDelay() const { return( alarm_capture_delay ); } int GetAlarmCaptureDelay() const { return alarm_capture_delay; }
unsigned int GetLastReadIndex() const; unsigned int GetLastReadIndex() const;
unsigned int GetLastWriteIndex() const; unsigned int GetLastWriteIndex() const;
uint32_t GetLastEventId() const; uint64_t GetLastEventId() const;
double GetFPS() const; double GetFPS() const;
void UpdateAnalysisFPS(); void UpdateAnalysisFPS();
void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" ); void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" );
void ForceAlarmOff(); void ForceAlarmOff();
void CancelForced(); void CancelForced();
TriggerState GetTriggerState() const { return( (TriggerState)(trigger_data?trigger_data->trigger_state:TRIGGER_CANCEL )); } TriggerState GetTriggerState() const { return (TriggerState)(trigger_data?trigger_data->trigger_state:TRIGGER_CANCEL ); }
inline time_t getStartupTime() const { inline time_t getStartupTime() const { return shared_data->startup_time; }
return( shared_data->startup_time ); inline void setStartupTime( time_t p_time ) { shared_data->startup_time = p_time; }
}
inline void setStartupTime( time_t p_time ) {
shared_data->startup_time = p_time;
}
void get_ref_image(); void get_ref_image();
void actionReload(); void actionReload();
@ -525,15 +549,17 @@ public:
bool DumpSettings( char *output, bool verbose ); bool DumpSettings( char *output, bool verbose );
void DumpZoneImage( const char *zone_string=0 ); void DumpZoneImage( const char *zone_string=0 );
static int LoadMonitors(std::string sql, Monitor **&monitors, Purpose purpose); // Returns # of Monitors loaded, 0 on failure.
#if ZM_HAS_V4L #if ZM_HAS_V4L
static int LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose ); static int LoadLocalMonitors(const char *device, Monitor **&monitors, Purpose purpose);
#endif // ZM_HAS_V4L #endif // ZM_HAS_V4L
static int LoadRemoteMonitors( const char *protocol, const char *host, const char*port, const char*path, Monitor **&monitors, Purpose purpose ); static int LoadRemoteMonitors(const char *protocol, const char *host, const char*port, const char*path, Monitor **&monitors, Purpose purpose);
static int LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ); static int LoadFileMonitors(const char *file, Monitor **&monitors, Purpose purpose);
#if HAVE_LIBAVFORMAT #if HAVE_LIBAVFORMAT
static int LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose ); static int LoadFfmpegMonitors(const char *file, Monitor **&monitors, Purpose purpose);
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT
static Monitor *Load( unsigned int id, bool load_zones, Purpose purpose ); static Monitor *Load(unsigned int id, bool load_zones, Purpose purpose);
void Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose);
//void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y ); //void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y );
//void StreamImages( int scale=100, int maxfps=10, time_t ttl=0, int msq_id=0 ); //void StreamImages( int scale=100, int maxfps=10, time_t ttl=0, int msq_id=0 );
//void StreamImagesRaw( int scale=100, int maxfps=10, time_t ttl=0 ); //void StreamImagesRaw( int scale=100, int maxfps=10, time_t ttl=0 );

View File

@ -27,28 +27,28 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <glob.h> #include <glob.h>
bool MonitorStream::checkSwapPath( const char *path, bool create_path ) { bool MonitorStream::checkSwapPath(const char *path, bool create_path) {
struct stat stat_buf; struct stat stat_buf;
if ( stat( path, &stat_buf ) < 0 ) { if ( stat(path, &stat_buf) < 0 ) {
if ( create_path && errno == ENOENT ) { if ( create_path && errno == ENOENT ) {
Debug( 3, "Swap path '%s' missing, creating", path ); Debug(3, "Swap path '%s' missing, creating", path);
if ( mkdir( path, 0755 ) ) { if ( mkdir(path, 0755) ) {
Error( "Can't mkdir %s: %s", path, strerror(errno)); Error("Can't mkdir %s: %s", path, strerror(errno));
return( false ); return false;
} }
if ( stat( path, &stat_buf ) < 0 ) { if ( stat(path, &stat_buf) < 0 ) {
Error( "Can't stat '%s': %s", path, strerror(errno) ); Error("Can't stat '%s': %s", path, strerror(errno));
return( false ); return false;
} }
} else { } else {
Error( "Can't stat '%s': %s", path, strerror(errno) ); Error("Can't stat '%s': %s", path, strerror(errno));
return( false ); return false;
} }
} }
if ( !S_ISDIR(stat_buf.st_mode) ) { if ( !S_ISDIR(stat_buf.st_mode) ) {
Error( "Swap image path '%s' is not a directory", path ); Error("Swap image path '%s' is not a directory", path);
return( false ); return false;
} }
uid_t uid = getuid(); uid_t uid = getuid();
@ -67,73 +67,51 @@ bool MonitorStream::checkSwapPath( const char *path, bool create_path ) {
} }
if ( (stat_buf.st_mode & mask) != mask ) { if ( (stat_buf.st_mode & mask) != mask ) {
Error( "Insufficient permissions on swap image path '%s'", path ); Error("Insufficient permissions on swap image path '%s'", path);
return( false ); return false;
} }
return( true ); return true;
} // end bool MonitorStream::checkSwapPath( const char *path, bool create_path ) } // end bool MonitorStream::checkSwapPath( const char *path, bool create_path )
void MonitorStream::processCommand( const CmdMsg *msg ) { void MonitorStream::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
paused = true; paused = true;
// Set delayed flag
delayed = true; delayed = true;
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;
// Set delayed_play flag
delayed = true; delayed = true;
} }
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;
// Set delayed_play flag
delayed = true; delayed = true;
} }
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;
// Clear delayed_play flag
delayed = false; delayed = 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 delayed_play flag
delayed = true; delayed = true;
} }
// Set play rate // Set play rate
switch ( replay_rate ) switch ( replay_rate ) {
{
case 2 * ZM_RATE_BASE : case 2 * ZM_RATE_BASE :
replay_rate = 5 * ZM_RATE_BASE; replay_rate = 5 * ZM_RATE_BASE;
break; break;
@ -152,40 +130,24 @@ void MonitorStream::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 delayed flag
delayed = true; delayed = 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 delayed flag
delayed = true; delayed = 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 ) { if ( paused ) {
// Clear paused flag
paused = false; paused = false;
// Set delayed_play flag
delayed = true; delayed = true;
} }
// Set play rate // Set play rate
@ -208,9 +170,7 @@ void MonitorStream::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 );
@ -233,9 +193,7 @@ void MonitorStream::processCommand( const CmdMsg *msg ) {
break; break;
} }
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:
@ -256,36 +214,25 @@ void MonitorStream::processCommand( const CmdMsg *msg ) {
break; break;
} }
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_QUIT : case CMD_QUIT :
{ Info("User initiated exit - CMD_QUIT");
Info ("User initiated exit - CMD_QUIT");
break; break;
}
case CMD_QUERY : case CMD_QUERY :
{ Debug(1, "Got QUERY command, sending STATUS");
Debug( 1, "Got QUERY command, sending STATUS" );
break; break;
}
default : default :
{ Error("Got unexpected command %d", msg->msg_data[0]);
Error( "Got unexpected command %d", msg->msg_data[0] );
break; break;
} } // end switch command
}
struct { struct {
int id; int id;
@ -316,7 +263,7 @@ void MonitorStream::processCommand( const CmdMsg *msg ) {
//status_data.enabled = monitor->shared_data->active; //status_data.enabled = monitor->shared_data->active;
status_data.enabled = monitor->trigger_data->trigger_state!=Monitor::TRIGGER_OFF; status_data.enabled = monitor->trigger_data->trigger_state!=Monitor::TRIGGER_OFF;
status_data.forced = monitor->trigger_data->trigger_state==Monitor::TRIGGER_ON; status_data.forced = monitor->trigger_data->trigger_state==Monitor::TRIGGER_ON;
Debug( 2, "Buffer Level:%d, Delayed:%d, Paused:%d, Rate:%d, delay:%.3f, Zoom:%d, Enabled:%d Forced:%d", Debug(2, "Buffer Level:%d, Delayed:%d, Paused:%d, Rate:%d, delay:%.3f, Zoom:%d, Enabled:%d Forced:%d",
status_data.buffer_level, status_data.buffer_level,
status_data.delayed, status_data.delayed,
status_data.paused, status_data.paused,
@ -329,16 +276,16 @@ void MonitorStream::processCommand( const CmdMsg *msg ) {
DataMsg status_msg; DataMsg status_msg;
status_msg.msg_type = MSG_DATA_WATCH; status_msg.msg_type = MSG_DATA_WATCH;
memcpy( &status_msg.msg_data, &status_data, sizeof(status_data) ); memcpy(&status_msg.msg_data, &status_data, sizeof(status_data));
int nbytes = 0; int nbytes = 0;
if ( (nbytes = sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) )) < 0 ) { if ( (nbytes = 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 );
} }
} }
Debug(2, "NUmber of bytes sent to (%s): (%d)", rem_addr.sun_path, nbytes ); Debug(2, "Number of bytes sent to (%s): (%d)", rem_addr.sun_path, nbytes);
// quit after sending a status, if this was a quit request // 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 ) {
@ -346,11 +293,11 @@ Debug(2, "NUmber of bytes sent to (%s): (%d)", rem_addr.sun_path, nbytes );
exit(0); exit(0);
} }
Debug(2,"Updating framrate"); Debug(2,"Updating framerate");
updateFrameRate( monitor->GetFPS() ); updateFrameRate(monitor->GetFPS());
} // end void MonitorStream::processCommand( const CmdMsg *msg ) } // end void MonitorStream::processCommand(const CmdMsg *msg)
bool MonitorStream::sendFrame( const char *filepath, struct timeval *timestamp ) { bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp) {
bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE));
if ( if (
@ -361,71 +308,70 @@ bool MonitorStream::sendFrame( const char *filepath, struct timeval *timestamp )
send_raw = false; send_raw = false;
if ( !send_raw ) { if ( !send_raw ) {
Image temp_image( filepath ); Image temp_image(filepath);
return( sendFrame( &temp_image, timestamp ) ); return sendFrame(&temp_image, timestamp);
} else { } else {
int img_buffer_size = 0; int img_buffer_size = 0;
static unsigned char img_buffer[ZM_MAX_IMAGE_SIZE]; static unsigned char img_buffer[ZM_MAX_IMAGE_SIZE];
FILE *fdj = NULL; FILE *fdj = NULL;
if ( (fdj = fopen( filepath, "r" )) ) { if ( (fdj = fopen(filepath, "r")) ) {
img_buffer_size = fread( img_buffer, 1, sizeof(img_buffer), fdj ); img_buffer_size = fread(img_buffer, 1, sizeof(img_buffer), fdj);
fclose( fdj ); fclose(fdj);
} else { } else {
Error( "Can't open %s: %s", filepath, strerror(errno) ); Error("Can't open %s: %s", filepath, strerror(errno));
return( false ); return false;
} }
// Calculate how long it takes to actually send the frame // Calculate how long it takes to actually send the frame
struct timeval frameStartTime; struct timeval frameStartTime;
gettimeofday( &frameStartTime, NULL ); gettimeofday(&frameStartTime, NULL);
fprintf( stdout, "--ZoneMinderFrame\r\n" ); fputs("--ZoneMinderFrame\r\nContent-Type: image/jpeg\r\n\r\n", stdout );
fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size ); fprintf(stdout, "Content-Length: %d\r\n", img_buffer_size);
fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) {
if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) {
if ( ! zm_terminate ) if ( ! zm_terminate )
Error( "Unable to send stream frame: %s", strerror(errno) ); Error("Unable to send stream frame: %s", strerror(errno));
return( false ); return false;
} }
fprintf( stdout, "\r\n\r\n" ); fputs("\r\n\r\n", stdout);
fflush( stdout ); fflush(stdout);
struct timeval frameEndTime; struct timeval frameEndTime;
gettimeofday( &frameEndTime, NULL ); gettimeofday(&frameEndTime, NULL);
int frameSendTime = tvDiffMsec( frameStartTime, frameEndTime ); int frameSendTime = tvDiffMsec(frameStartTime, frameEndTime);
if ( frameSendTime > 1000/maxfps ) { if ( frameSendTime > 1000/maxfps ) {
maxfps /= 2; maxfps /= 2;
Info( "Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps ); Info("Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps);
} }
last_frame_sent = TV_2_FLOAT( now ); last_frame_sent = TV_2_FLOAT(now);
return( true ); return true;
} }
return( false ); return false;
} } // end bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp)
bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp ) { bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) {
Image *send_image = prepareImage( image ); Image *send_image = prepareImage(image);
if ( !config.timestamp_on_capture && timestamp ) if ( !config.timestamp_on_capture && timestamp )
monitor->TimestampImage( send_image, timestamp ); monitor->TimestampImage(send_image, timestamp);
#if HAVE_LIBAVCODEC #if HAVE_LIBAVCODEC
if ( type == STREAM_MPEG ) { if ( type == STREAM_MPEG ) {
if ( !vid_stream ) { if ( !vid_stream ) {
vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() ); vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height());
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();
} }
static struct timeval base_time; static struct timeval base_time;
struct DeltaTimeval delta_time; struct DeltaTimeval delta_time;
if ( !frame_count ) if ( !frame_count )
base_time = *timestamp; base_time = *timestamp;
DELTA_TIMEVAL( delta_time, *timestamp, base_time, DT_PREC_3 ); DELTA_TIMEVAL(delta_time, *timestamp, base_time, DT_PREC_3);
/* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_time.delta ); /* double pts = */ vid_stream->EncodeFrame(send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_time.delta);
} else } else
#endif // HAVE_LIBAVCODEC #endif // HAVE_LIBAVCODEC
{ {
@ -436,36 +382,38 @@ bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp ) {
// Calculate how long it takes to actually send the frame // Calculate how long it takes to actually send the frame
struct timeval frameStartTime; struct timeval frameStartTime;
gettimeofday( &frameStartTime, NULL ); gettimeofday(&frameStartTime, NULL);
fprintf( stdout, "--ZoneMinderFrame\r\n" ); fputs("--ZoneMinderFrame\r\n", stdout);
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);
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);
img_buffer = (uint8_t*)send_image->Buffer(); img_buffer = (uint8_t*)send_image->Buffer();
img_buffer_size = send_image->Size(); img_buffer_size = send_image->Size();
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);
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;
default : default :
Fatal( "Unexpected frame type %d", type ); Error("Unexpected frame type %d", type);
break; return false;
} }
fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size);
if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) {
if ( !zm_terminate ) if ( !zm_terminate ){
Error( "Unable to send stream frame: %s", strerror(errno) ); // If the pipe was closed, we will get signalled SIGPIPE to exit, which will set zm_terminate
return( false ); Error("Unable to send stream frame: %s", strerror(errno));
}
return false;
} }
fprintf( stdout, "\r\n\r\n" ); fputs("\r\n\r\n",stdout);
fflush( stdout ); fflush( stdout );
struct timeval frameEndTime; struct timeval frameEndTime;
@ -484,27 +432,27 @@ bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp ) {
void MonitorStream::runStream() { void MonitorStream::runStream() {
if ( type == STREAM_SINGLE ) { if ( type == STREAM_SINGLE ) {
// Not yet migrated over to stream class // Not yet migrated over to stream class
SingleImage( scale ); SingleImage(scale);
return; return;
} }
openComms(); openComms();
if ( ! checkInitialised() ) { if ( !checkInitialised() ) {
Error("Not initialized"); Error("Not initialized");
return; return;
} }
updateFrameRate( monitor->GetFPS() ); updateFrameRate(monitor->GetFPS());
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);
// point to end which is theoretically not a valid value because all indexes are % image_buffer_count // point to end which is theoretically not a valid value because all indexes are % image_buffer_count
unsigned int last_read_index = monitor->image_buffer_count; unsigned int last_read_index = monitor->image_buffer_count;
time_t stream_start_time; time_t stream_start_time;
time( &stream_start_time ); time(&stream_start_time);
frame_count = 0; frame_count = 0;
@ -513,48 +461,47 @@ void MonitorStream::runStream() {
temp_read_index = temp_image_buffer_count; temp_read_index = temp_image_buffer_count;
temp_write_index = temp_image_buffer_count; temp_write_index = temp_image_buffer_count;
char *swap_path = 0; std::string swap_path;
bool buffered_playback = false; bool buffered_playback = false;
// Last image and timestamp when paused, will be resent occasionally to prevent timeout
Image *paused_image = NULL;
struct timeval paused_timestamp;
// 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id // 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id
const int max_swap_len_suffix = 15; const int max_swap_len_suffix = 15;
int swap_path_length = staticConfig.PATH_SWAP.length() + 1; // +1 for NULL terminator int swap_path_length = staticConfig.PATH_SWAP.length() + 1; // +1 for NULL terminator
int subfolder1_length = snprintf(NULL, 0, "/zmswap-m%d", monitor->Id() ) + 1; int subfolder1_length = snprintf(NULL, 0, "/zmswap-m%d", monitor->Id()) + 1;
int subfolder2_length = snprintf(NULL, 0, "/zmswap-q%06d", connkey ) + 1; int subfolder2_length = snprintf(NULL, 0, "/zmswap-q%06d", connkey) + 1;
int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length; int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length;
if ( connkey && playback_buffer > 0 ) { if ( connkey && ( playback_buffer > 0 ) ) {
if ( total_swap_path_length + max_swap_len_suffix > PATH_MAX ) { if ( total_swap_path_length + max_swap_len_suffix > PATH_MAX ) {
Error( "Swap Path is too long. %d > %d ", total_swap_path_length+max_swap_len_suffix, PATH_MAX ); Error("Swap Path is too long. %d > %d ", total_swap_path_length+max_swap_len_suffix, PATH_MAX);
} else { } else {
swap_path = (char *)malloc( total_swap_path_length+max_swap_len_suffix ); swap_path = staticConfig.PATH_SWAP;
strncpy( swap_path, staticConfig.PATH_SWAP.c_str(), swap_path_length );
Debug( 3, "Checking swap path folder: %s", swap_path ); Debug( 3, "Checking swap path folder: %s", swap_path.c_str() );
if ( checkSwapPath( swap_path, false ) ) { if ( checkSwapPath(swap_path.c_str(), false) ) {
// Append the subfolder name /zmswap-m{monitor-id} to the end of swap_path swap_path += stringtf("/zmswap-m%d", monitor->Id());
int ndx = swap_path_length - 1; // Array index of the NULL terminator
snprintf( &(swap_path[ndx]), subfolder1_length, "/zmswap-m%d", monitor->Id() );
Debug( 4, "Checking swap path subfolder: %s", swap_path ); Debug(4, "Checking swap path subfolder: %s", swap_path.c_str());
if ( checkSwapPath( swap_path, true ) ) { if ( checkSwapPath(swap_path.c_str(), true) ) {
// Append the subfolder name /zmswap-q{connection key} to the end of swap_path swap_path += stringtf("/zmswap-q%06d", connkey);
ndx = swap_path_length+subfolder1_length - 2; // Array index of the NULL terminator
snprintf( &(swap_path[ndx]), subfolder2_length, "/zmswap-q%06d", connkey );
Debug( 4, "Checking swap path subfolder: %s", swap_path ); Debug(4, "Checking swap path subfolder: %s", swap_path.c_str());
if ( checkSwapPath( swap_path, true ) ) { if ( checkSwapPath(swap_path.c_str(), true) ) {
buffered_playback = true; buffered_playback = true;
} }
} }
} }
if ( !buffered_playback ) { if ( !buffered_playback ) {
Error( "Unable to validate swap image path, disabling buffered playback" ); Error("Unable to validate swap image path, disabling buffered playback");
} else { } else {
Debug( 2, "Assigning temporary buffer" ); Debug(2, "Assigning temporary buffer");
temp_image_buffer = new SwapImage[temp_image_buffer_count]; temp_image_buffer = new SwapImage[temp_image_buffer_count];
memset( temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count ); memset( temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count );
Debug( 2, "Assigned temporary buffer" ); Debug( 2, "Assigned temporary buffer" );
@ -567,19 +514,20 @@ void MonitorStream::runStream() {
float max_secs_since_last_sent_frame = 10.0; //should be > keep alive amount (5 secs) float max_secs_since_last_sent_frame = 10.0; //should be > keep alive amount (5 secs)
while ( !zm_terminate ) { while ( !zm_terminate ) {
bool got_command = false; bool got_command = false;
if ( feof( stdout ) || ferror( stdout ) || !monitor->ShmValid() ) { if ( feof(stdout) ) {
if ( feof( stdout ) ) { Debug(2,"feof stdout");
Debug(2,"feof stdout"); break;
} else if ( ferror( stdout ) ) { } else if ( ferror(stdout) ) {
Debug(2,"ferror stdout"); Debug(2,"ferror stdout");
} else if ( !monitor->ShmValid() ) { break;
Debug(2,"monitor not valid.... maybe we should wait until it comes back."); } else if ( !monitor->ShmValid() ) {
} Debug(2,"monitor not valid.... maybe we should wait until it comes back.");
break; break;
} }
gettimeofday( &now, NULL ); gettimeofday(&now, NULL);
bool was_paused = paused;
if ( connkey ) { if ( connkey ) {
while(checkCommandQueue()) { while(checkCommandQueue()) {
Debug(2, "Have checking command Queue for connkey: %d", connkey ); Debug(2, "Have checking command Queue for connkey: %d", connkey );
@ -587,7 +535,19 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
} }
} }
//bool frame_sent = false; if ( paused ) {
if ( !was_paused ) {
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
Debug(1,"Saving paused image from index %d",index);
paused_image = new Image( *monitor->image_buffer[index].image );
paused_timestamp = *(monitor->image_buffer[index].timestamp);
}
} else if ( paused_image ) {
Debug(1,"Clearing paused_image");
delete paused_image;
paused_image = NULL;
}
if ( buffered_playback && delayed ) { if ( buffered_playback && delayed ) {
if ( temp_read_index == temp_write_index ) { if ( temp_read_index == temp_write_index ) {
// Go back to live viewing // Go back to live viewing
@ -599,14 +559,14 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
replay_rate = ZM_RATE_BASE; replay_rate = ZM_RATE_BASE;
} else { } else {
if ( !paused ) { if ( !paused ) {
int temp_index = MOD_ADD( temp_read_index, 0, temp_image_buffer_count ); int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
//Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index ); //Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index );
SwapImage *swap_image = &temp_image_buffer[temp_index]; SwapImage *swap_image = &temp_image_buffer[temp_index];
if ( !swap_image->valid ) { if ( !swap_image->valid ) {
paused = true; paused = true;
delayed = true; delayed = true;
temp_read_index = MOD_ADD( temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count ); temp_read_index = MOD_ADD(temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count);
} else { } else {
//Debug( 3, "siT: %f, lfT: %f", TV_2_FLOAT( swap_image->timestamp ), TV_2_FLOAT( last_frame_timestamp ) ); //Debug( 3, "siT: %f, lfT: %f", TV_2_FLOAT( swap_image->timestamp ), TV_2_FLOAT( last_frame_timestamp ) );
double expected_delta_time = ((TV_2_FLOAT( swap_image->timestamp ) - TV_2_FLOAT( last_frame_timestamp )) * ZM_RATE_BASE)/replay_rate; double expected_delta_time = ((TV_2_FLOAT( swap_image->timestamp ) - TV_2_FLOAT( last_frame_timestamp )) * ZM_RATE_BASE)/replay_rate;
@ -619,12 +579,12 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
if ( temp_index%frame_mod == 0 ) { if ( temp_index%frame_mod == 0 ) {
Debug( 2, "Sending delayed frame %d", temp_index ); Debug( 2, "Sending delayed frame %d", temp_index );
// Send the next frame // Send the next frame
if ( ! sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) ) if ( ! sendFrame(temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp) )
zm_terminate = true; zm_terminate = true;
memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) ); memcpy(&last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp));
//frame_sent = true; //frame_sent = true;
} }
temp_read_index = MOD_ADD( temp_read_index, (replay_rate>0?1:-1), temp_image_buffer_count ); temp_read_index = MOD_ADD(temp_read_index, (replay_rate>0?1:-1), temp_image_buffer_count);
} }
} }
} else if ( step != 0 ) { } else if ( step != 0 ) {
@ -639,7 +599,8 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
//frame_sent = true; //frame_sent = true;
step = 0; step = 0;
} else { } else {
int temp_index = MOD_ADD( temp_read_index, 0, temp_image_buffer_count ); //paused?
int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent;
if ( got_command || actual_delta_time > 5 ) { if ( got_command || actual_delta_time > 5 ) {
@ -650,8 +611,9 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
zm_terminate = true; zm_terminate = true;
//frame_sent = true; //frame_sent = true;
} }
} } // end if (!paused) or step or paused
} } // end if have exceeded buffer or not
if ( temp_read_index == temp_write_index ) { if ( temp_read_index == temp_write_index ) {
// Go back to live viewing // Go back to live viewing
Warning( "Rewound over write index, resuming live play" ); Warning( "Rewound over write index, resuming live play" );
@ -662,10 +624,12 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
replay_rate = ZM_RATE_BASE; replay_rate = ZM_RATE_BASE;
} }
} // end if ( buffered_playback && delayed ) } // end if ( buffered_playback && delayed )
if ( last_read_index != monitor->shared_data->last_write_index ) { if ( last_read_index != monitor->shared_data->last_write_index ) {
// have a new image to send
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary
last_read_index = monitor->shared_data->last_write_index; last_read_index = monitor->shared_data->last_write_index;
Debug( 3, "index: %d: frame_mod: %d frame count: %d", index, frame_mod, frame_count ); Debug( 2, "index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)", index, frame_mod, frame_count, paused, delayed );
if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) { if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) {
if ( !paused && !delayed ) { if ( !paused && !delayed ) {
// Send the next frame // Send the next frame
@ -674,24 +638,38 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
Image *image = snap->image;// This works because connect rebuilds the image_buffer links Image *image = snap->image;// This works because connect rebuilds the image_buffer links
struct timeval *timestamp = snap->timestamp; struct timeval *timestamp = snap->timestamp;
if ( !sendFrame( image, timestamp ) ) { if ( !sendFrame(snap->image, snap->timestamp) ) {
Debug(2, "sendFrame failed, quiting."); Debug(2, "sendFrame failed, quiting.");
zm_terminate = true; zm_terminate = true;
} }
// Perhaps we should use NOW instead.
last_frame_timestamp = *timestamp; last_frame_timestamp = *timestamp;
//frame_sent = true; //frame_sent = true;
temp_read_index = temp_write_index; temp_read_index = temp_write_index;
} else {
double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
if ( actual_delta_time > 5 ) {
if ( paused_image ) {
// Send keepalive
Debug(2, "Sending keepalive frame ");
// Send the next frame
if ( !sendFrame(paused_image, &paused_timestamp) )
zm_terminate = true;
} else {
Debug(2, "Would have sent keepalive frame, but had no paused_image ");
}
}
} }
} // end if should send frame } // end if should send frame
if ( buffered_playback ) { if ( buffered_playback && !paused ) {
if ( monitor->shared_data->valid ) { if ( monitor->shared_data->valid ) {
if ( monitor->shared_timestamps[index].tv_sec ) { if ( monitor->shared_timestamps[index].tv_sec ) {
int temp_index = temp_write_index%temp_image_buffer_count; int temp_index = temp_write_index%temp_image_buffer_count;
Debug( 2, "Storing frame %d", temp_index ); Debug(2, "Storing frame %d", temp_index);
if ( !temp_image_buffer[temp_index].valid ) { if ( !temp_image_buffer[temp_index].valid ) {
snprintf( temp_image_buffer[temp_index].file_name, sizeof(temp_image_buffer[0].file_name), "%s/zmswap-i%05d.jpg", swap_path, temp_index ); snprintf( temp_image_buffer[temp_index].file_name, sizeof(temp_image_buffer[0].file_name), "%s/zmswap-i%05d.jpg", swap_path.c_str(), temp_index );
temp_image_buffer[temp_index].valid = true; temp_image_buffer[temp_index].valid = true;
} }
temp_image_buffer[temp_index].timestamp = monitor->shared_timestamps[index]; temp_image_buffer[temp_index].timestamp = monitor->shared_timestamps[index];
@ -700,9 +678,7 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
if ( temp_write_index == temp_read_index ) { if ( temp_write_index == temp_read_index ) {
// Go back to live viewing // Go back to live viewing
Warning( "Exceeded temporary buffer, resuming live play" ); Warning( "Exceeded temporary buffer, resuming live play" );
// Clear paused flag
paused = false; paused = false;
// Clear delayed_play flag
delayed = false; delayed = false;
replay_rate = ZM_RATE_BASE; replay_rate = ZM_RATE_BASE;
} }
@ -720,7 +696,7 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
unsigned long sleep_time = (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))); unsigned long sleep_time = (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2)));
Debug(4, "Sleeping for (%d)", sleep_time); Debug(4, "Sleeping for (%d)", sleep_time);
usleep( sleep_time ); usleep(sleep_time);
if ( ttl ) { if ( ttl ) {
if ( (now.tv_sec - stream_start_time) > ttl ) { if ( (now.tv_sec - stream_start_time) > ttl ) {
Debug(2, "now(%d) - start(%d) > ttl(%d) break", now.tv_sec, stream_start_time, ttl); Debug(2, "now(%d) - start(%d) > ttl(%d) break", now.tv_sec, stream_start_time, ttl);
@ -731,50 +707,49 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey );
// If we didn't capture above, because frame_mod was bad? Then last_frame_sent will not have a value. // If we didn't capture above, because frame_mod was bad? Then last_frame_sent will not have a value.
last_frame_sent = now.tv_sec; last_frame_sent = now.tv_sec;
Warning( "no last_frame_sent. Shouldn't happen. frame_mod was (%d) frame_count (%d) ", frame_mod, frame_count ); Warning( "no last_frame_sent. Shouldn't happen. frame_mod was (%d) frame_count (%d) ", frame_mod, frame_count );
} else if ( (TV_2_FLOAT( now ) - last_frame_sent) > max_secs_since_last_sent_frame ) { } else if ( (!paused) && ( (TV_2_FLOAT( now ) - last_frame_sent) > max_secs_since_last_sent_frame ) ) {
Error( "Terminating, last frame sent time %f secs more than maximum of %f", TV_2_FLOAT( now ) - last_frame_sent, max_secs_since_last_sent_frame ); Error( "Terminating, last frame sent time %f secs more than maximum of %f", TV_2_FLOAT( now ) - last_frame_sent, max_secs_since_last_sent_frame );
break; break;
} }
} // end while } // end while
if ( buffered_playback ) { if ( buffered_playback ) {
Debug( 1, "Cleaning swap files from %s", swap_path ); Debug(1, "Cleaning swap files from %s", swap_path.c_str());
struct stat stat_buf; struct stat stat_buf;
if ( stat( swap_path, &stat_buf ) < 0 ) { if ( stat(swap_path.c_str(), &stat_buf) < 0 ) {
if ( errno != ENOENT ) { if ( errno != ENOENT ) {
Error( "Can't stat '%s': %s", swap_path, strerror(errno) ); Error("Can't stat '%s': %s", swap_path.c_str(), strerror(errno));
} }
} else if ( !S_ISDIR(stat_buf.st_mode) ) { } else if ( !S_ISDIR(stat_buf.st_mode) ) {
Error( "Swap image path '%s' is not a directory", swap_path ); Error("Swap image path '%s' is not a directory", swap_path.c_str());
} else { } else {
char glob_pattern[PATH_MAX] = ""; char glob_pattern[PATH_MAX] = "";
snprintf( glob_pattern, sizeof(glob_pattern), "%s/*.*", swap_path ); snprintf(glob_pattern, sizeof(glob_pattern), "%s/*.*", swap_path.c_str());
glob_t pglob; glob_t pglob;
int glob_status = glob( glob_pattern, 0, 0, &pglob ); int glob_status = glob(glob_pattern, 0, 0, &pglob);
if ( glob_status != 0 ) { if ( glob_status != 0 ) {
if ( glob_status < 0 ) { if ( glob_status < 0 ) {
Error( "Can't glob '%s': %s", glob_pattern, strerror(errno) ); Error("Can't glob '%s': %s", glob_pattern, strerror(errno));
} else { } else {
Debug( 1, "Can't glob '%s': %d", glob_pattern, glob_status ); Debug(1, "Can't glob '%s': %d", glob_pattern, glob_status);
} }
} else { } else {
for ( unsigned int i = 0; i < pglob.gl_pathc; i++ ) { for ( unsigned int i = 0; i < pglob.gl_pathc; i++ ) {
if ( unlink( pglob.gl_pathv[i] ) < 0 ) { if ( unlink(pglob.gl_pathv[i]) < 0 ) {
Error( "Can't unlink '%s': %s", pglob.gl_pathv[i], strerror(errno) ); Error("Can't unlink '%s': %s", pglob.gl_pathv[i], strerror(errno));
} }
} }
} }
globfree( &pglob ); globfree( &pglob );
if ( rmdir( swap_path ) < 0 ) { if ( rmdir(swap_path.c_str()) < 0 ) {
Error( "Can't rmdir '%s': %s", swap_path, strerror(errno) ); Error( "Can't rmdir '%s': %s", swap_path.c_str(), strerror(errno) );
} }
} // end if checking for swap_path } // end if checking for swap_path
} // end if buffered_playback } // end if buffered_playback
if ( swap_path ) free( swap_path );
closeComms(); closeComms();
} } // end MonitorStream::runStream
void MonitorStream::SingleImage( int scale ) { void MonitorStream::SingleImage( int scale ) {
int img_buffer_size = 0; int img_buffer_size = 0;

View File

@ -58,7 +58,9 @@ class MonitorStream : public StreamBase {
void SingleImageZip( int scale=100 ); void SingleImageZip( int scale=100 );
public: public:
MonitorStream() : ttl(0), playback_buffer(0), delayed(false), frame_count(0) { MonitorStream() :
temp_image_buffer(NULL), temp_image_buffer_count(0), temp_read_index(0), temp_write_index(0),
ttl(0), playback_buffer(0), delayed(false), frame_count(0) {
} }
void setStreamBuffer( int p_playback_buffer ) { void setStreamBuffer( int p_playback_buffer ) {
playback_buffer = p_playback_buffer; playback_buffer = p_playback_buffer;

View File

@ -293,7 +293,7 @@ const char *VideoStream::MimeType( ) const {
return mime_type; return mime_type;
} }
void VideoStream::OpenStream( ) { bool VideoStream::OpenStream( ) {
int ret; int ret;
/* now that all the parameters are set, we can open the /* now that all the parameters are set, we can open the
@ -308,7 +308,8 @@ void VideoStream::OpenStream( ) {
if ( (ret = avcodec_open2(codec_context, codec, 0)) < 0 ) if ( (ret = avcodec_open2(codec_context, codec, 0)) < 0 )
#endif #endif
{ {
Fatal( "Could not open codec. Error code %d \"%s\"", ret, av_err2str(ret) ); Error("Could not open codec. Error code %d \"%s\"", ret, av_err2str(ret));
return false;
} }
Debug( 1, "Opened codec" ); Debug( 1, "Opened codec" );
@ -316,22 +317,24 @@ void VideoStream::OpenStream( ) {
/* allocate the encoded raw picture */ /* allocate the encoded raw picture */
opicture = zm_av_frame_alloc( ); opicture = zm_av_frame_alloc( );
if ( !opicture ) { if ( !opicture ) {
Panic( "Could not allocate opicture" ); Error("Could not allocate opicture");
return false;
} }
opicture->width = codec_context->width; opicture->width = codec_context->width;
opicture->height = codec_context->height; opicture->height = codec_context->height;
opicture->format = codec_context->pix_fmt; opicture->format = codec_context->pix_fmt;
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
int size = av_image_get_buffer_size( codec_context->pix_fmt, codec_context->width, codec_context->height, 1 ); int size = av_image_get_buffer_size(codec_context->pix_fmt, codec_context->width, codec_context->height, 1);
#else #else
int size = avpicture_get_size( codec_context->pix_fmt, codec_context->width, codec_context->height ); int size = avpicture_get_size(codec_context->pix_fmt, codec_context->width, codec_context->height);
#endif #endif
uint8_t *opicture_buf = (uint8_t *)av_malloc( size ); uint8_t *opicture_buf = (uint8_t *)av_malloc(size);
if ( !opicture_buf ) { if ( !opicture_buf ) {
av_frame_free( &opicture ); av_frame_free( &opicture );
Panic( "Could not allocate opicture_buf" ); Error( "Could not allocate opicture_buf" );
return false;
} }
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
av_image_fill_arrays(opicture->data, opicture->linesize, av_image_fill_arrays(opicture->data, opicture->linesize,
@ -352,7 +355,8 @@ void VideoStream::OpenStream( ) {
tmp_opicture = avcodec_alloc_frame( ); tmp_opicture = avcodec_alloc_frame( );
#endif #endif
if ( !tmp_opicture ) { if ( !tmp_opicture ) {
Panic( "Could not allocate tmp_opicture" ); Error( "Could not allocate tmp_opicture" );
return false;
} }
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
int size = av_image_get_buffer_size( pf, codec_context->width, codec_context->height,1 ); int size = av_image_get_buffer_size( pf, codec_context->width, codec_context->height,1 );
@ -362,7 +366,8 @@ void VideoStream::OpenStream( ) {
uint8_t *tmp_opicture_buf = (uint8_t *)av_malloc( size ); uint8_t *tmp_opicture_buf = (uint8_t *)av_malloc( size );
if ( !tmp_opicture_buf ) { if ( !tmp_opicture_buf ) {
av_frame_free( &tmp_opicture ); av_frame_free( &tmp_opicture );
Panic( "Could not allocate tmp_opicture_buf" ); Error( "Could not allocate tmp_opicture_buf" );
return false;
} }
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
av_image_fill_arrays(tmp_opicture->data, av_image_fill_arrays(tmp_opicture->data,
@ -385,12 +390,14 @@ void VideoStream::OpenStream( ) {
ret = url_fopen( &ofc->pb, filename, AVIO_FLAG_WRITE ); ret = url_fopen( &ofc->pb, filename, AVIO_FLAG_WRITE );
#endif #endif
if ( ret < 0 ) { if ( ret < 0 ) {
Fatal( "Could not open '%s'", filename ); Error("Could not open '%s'", filename);
return false;
} }
Debug( 1, "Opened output \"%s\"", filename ); Debug(1, "Opened output \"%s\"", filename);
} else { } else {
Fatal( "of->flags & AVFMT_NOFILE" ); Error( "of->flags & AVFMT_NOFILE" );
return false;
} }
video_outbuf = NULL; video_outbuf = NULL;
@ -417,14 +424,16 @@ void VideoStream::OpenStream( ) {
#endif #endif
#if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0)
ret = av_write_header( ofc ); ret = av_write_header(ofc);
#else #else
ret = avformat_write_header(ofc, NULL); ret = avformat_write_header(ofc, NULL);
#endif #endif
if ( ret < 0 ) { if ( ret < 0 ) {
Fatal( "?_write_header failed with error %d \"%s\"", ret, av_err2str( ret ) ); Error("?_write_header failed with error %d \"%s\"", ret, av_err2str(ret));
} return false;
}
return true;
} }
VideoStream::VideoStream( const char *in_filename, const char *in_format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height ) : VideoStream::VideoStream( const char *in_filename, const char *in_format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height ) :
@ -550,19 +559,20 @@ VideoStream::~VideoStream( ) {
} }
double VideoStream::EncodeFrame( const uint8_t *buffer, int buffer_size, bool _add_timestamp, unsigned int _timestamp ) { double VideoStream::EncodeFrame( const uint8_t *buffer, int buffer_size, bool _add_timestamp, unsigned int _timestamp ) {
if ( pthread_mutex_lock( buffer_copy_lock ) != 0 ) { if ( pthread_mutex_lock(buffer_copy_lock) != 0 ) {
Fatal( "EncodeFrame: pthread_mutex_lock failed." ); Fatal( "EncodeFrame: pthread_mutex_lock failed." );
} }
if (buffer_copy_size < buffer_size) { if (buffer_copy_size < buffer_size) {
if ( buffer_copy ) { if ( buffer_copy ) {
av_free( buffer_copy ); av_free(buffer_copy);
} }
// Allocate a buffer to store source images for the streaming thread to encode. // Allocate a buffer to store source images for the streaming thread to encode.
buffer_copy = (uint8_t *)av_malloc( buffer_size ); buffer_copy = (uint8_t *)av_malloc(buffer_size);
if ( !buffer_copy ) { if ( !buffer_copy ) {
Panic( "Could not allocate buffer_copy" ); Error( "Could not allocate buffer_copy" );
pthread_mutex_unlock(buffer_copy_lock);
return 0; return 0;
} }
buffer_copy_size = buffer_size; buffer_copy_size = buffer_size;
@ -573,7 +583,7 @@ double VideoStream::EncodeFrame( const uint8_t *buffer, int buffer_size, bool _a
buffer_copy_used = buffer_size; buffer_copy_used = buffer_size;
memcpy(buffer_copy, buffer, buffer_size); memcpy(buffer_copy, buffer, buffer_size);
if ( pthread_mutex_unlock( buffer_copy_lock ) != 0 ) { if ( pthread_mutex_unlock(buffer_copy_lock) != 0 ) {
Fatal( "EncodeFrame: pthread_mutex_unlock failed." ); Fatal( "EncodeFrame: pthread_mutex_unlock failed." );
} }

View File

@ -1,21 +1,21 @@
/* /*
* ZoneMinder MPEG Interface, $Date$, $Revision$ * ZoneMinder MPEG Interface, $Date$, $Revision$
* Copyright (C) 2001-2008 Philip Coombes * Copyright (C) 2001-2008 Philip Coombes
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#ifndef ZM_MPEG_H #ifndef ZM_MPEG_H
#define ZM_MPEG_H #define ZM_MPEG_H
@ -51,7 +51,7 @@ protected:
uint8_t *video_outbuf; uint8_t *video_outbuf;
int video_outbuf_size; int video_outbuf_size;
double last_pts; double last_pts;
pthread_t streaming_thread; pthread_t streaming_thread;
bool do_streaming; bool do_streaming;
bool add_timestamp; bool add_timestamp;
@ -78,7 +78,7 @@ public:
VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height ); VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height );
~VideoStream(); ~VideoStream();
const char *MimeType() const; const char *MimeType() const;
void OpenStream(); bool OpenStream();
double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 ); double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 );
}; };

View File

@ -191,8 +191,8 @@ Image *ZMPacket::set_image( Image *i ) {
return image; return image;
} }
AVPacket *ZMPacket::set_packet( AVPacket *p ) { AVPacket *ZMPacket::set_packet(AVPacket *p) {
if ( zm_av_packet_ref( &packet, p ) < 0 ) { if ( zm_av_packet_ref(&packet, p) < 0 ) {
Error("error refing packet"); Error("error refing packet");
} }
dumpPacket(&packet, "zmpacket:"); dumpPacket(&packet, "zmpacket:");

View File

@ -39,48 +39,49 @@ StreamBase::~StreamBase() {
closeComms(); closeComms();
} }
bool StreamBase::loadMonitor( int monitor_id ) { bool StreamBase::loadMonitor(int monitor_id) {
if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) ) { if ( !(monitor = Monitor::Load(monitor_id, false, Monitor::QUERY)) ) {
Fatal( "Unable to load monitor id %d for streaming", monitor_id ); Error("Unable to load monitor id %d for streaming", monitor_id);
return( false ); return false;
} }
if ( ! monitor->connect() ) { if ( ! monitor->connect() ) {
Fatal( "Unable to connect to monitor id %d for streaming", monitor_id ); Error("Unable to connect to monitor id %d for streaming", monitor_id);
return( false ); return false;
} }
return( true ); return true;
} }
bool StreamBase::checkInitialised() { bool StreamBase::checkInitialised() {
if ( !monitor ) { if ( !monitor ) {
Fatal( "Cannot stream, not initialised" ); Fatal( "Cannot stream, not initialised" );
return( false ); return false;
} }
return( true ); return true;
} }
void StreamBase::updateFrameRate( double fps ) { void StreamBase::updateFrameRate(double fps) {
base_fps = fps; base_fps = fps;
effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE; effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE;
frame_mod = 1; frame_mod = 1;
Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod ); Debug(3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod);
// Min frame repeat? // Min frame repeat?
while( effective_fps > maxfps ) { while( effective_fps > maxfps ) {
effective_fps /= 2.0; effective_fps /= 2.0;
frame_mod *= 2; frame_mod *= 2;
Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod ); Debug(3, "EffectiveFPS:%.2f, FrameMod:%d", effective_fps, frame_mod);
} }
} }
bool StreamBase::checkCommandQueue() { bool StreamBase::checkCommandQueue() {
if ( sd >= 0 ) { if ( sd >= 0 ) {
CmdMsg msg; CmdMsg msg;
memset( &msg, 0, sizeof(msg) ); memset(&msg, 0, sizeof(msg));
int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 ); int nbytes = recvfrom(sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0);
if ( nbytes < 0 ) { if ( nbytes < 0 ) {
if ( errno != EAGAIN ) { if ( errno != EAGAIN ) {
Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) ); Error("recvfrom(), errno = %d, error = %s", errno, strerror(errno));
return false;
} }
} }
//else if ( (nbytes != sizeof(msg)) ) //else if ( (nbytes != sizeof(msg)) )
@ -88,14 +89,14 @@ bool StreamBase::checkCommandQueue() {
//Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes ); //Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes );
//} //}
else { else {
Debug(2, "Message length is (%d)", nbytes ); Debug(2, "Message length is (%d)", nbytes);
processCommand( &msg ); processCommand(&msg);
return( true ); return true;
} }
} else { } else {
Error("sd is < 0"); Warning("No sd in checkCommandQueue, comms not open?");
} }
return( false ); return false;
} }
Image *StreamBase::prepareImage( Image *image ) { Image *StreamBase::prepareImage( Image *image ) {
@ -152,18 +153,18 @@ Image *StreamBase::prepareImage( Image *image ) {
if ( mag != ZM_SCALE_BASE ) { if ( mag != ZM_SCALE_BASE ) {
if ( act_mag != ZM_SCALE_BASE ) { if ( act_mag != ZM_SCALE_BASE ) {
Debug( 3, "Magnifying by %d", mag ); Debug(3, "Magnifying by %d", mag);
if ( !image_copied ) { if ( !image_copied ) {
static Image copy_image; static Image copy_image;
copy_image.Assign( *image ); copy_image.Assign(*image);
image = &copy_image; image = &copy_image;
image_copied = true; image_copied = true;
} }
image->Scale( mag ); image->Scale(mag);
} }
} }
Debug( 3, "Real image width = %d, height = %d", image->Width(), image->Height() ); Debug(3, "Real image width = %d, height = %d", image->Width(), image->Height());
if ( disp_image_width < virt_image_width || disp_image_height < virt_image_height ) { if ( disp_image_width < virt_image_width || disp_image_height < virt_image_height ) {
static Box last_crop; static Box last_crop;
@ -222,23 +223,23 @@ Image *StreamBase::prepareImage( Image *image ) {
last_x = x; last_x = x;
last_y = y; last_y = y;
return( image ); return image;
} }
bool StreamBase::sendTextFrame( const char *frame_text ) { bool StreamBase::sendTextFrame( const char *frame_text ) {
Debug( 2, "Sending text frame '%s'", frame_text ); Debug(2, "Sending text frame '%s'", frame_text);
Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() ); Image image(monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder());
image.Annotate( frame_text, image.centreCoord( frame_text ) ); image.Annotate(frame_text, image.centreCoord(frame_text));
if ( scale != 100 ) { if ( scale != 100 ) {
image.Scale( scale ); image.Scale(scale);
} }
#if HAVE_LIBAVCODEC #if HAVE_LIBAVCODEC
if ( type == STREAM_MPEG ) { if ( type == STREAM_MPEG ) {
if ( !vid_stream ) { if ( !vid_stream ) {
vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() ); vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), 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( image.Buffer(), image.Size() ); /* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() );
@ -248,30 +249,29 @@ bool StreamBase::sendTextFrame( const char *frame_text ) {
static unsigned char buffer[ZM_MAX_IMAGE_SIZE]; static unsigned char buffer[ZM_MAX_IMAGE_SIZE];
int n_bytes = 0; int n_bytes = 0;
image.EncodeJpeg( buffer, &n_bytes ); image.EncodeJpeg(buffer, &n_bytes);
fprintf( stdout, "--ZoneMinderFrame\r\n" ); fputs("--ZoneMinderFrame\r\nContent-Type: image/jpeg\r\n\r\n", stdout);
fprintf( stdout, "Content-Length: %d\r\n", n_bytes ); fprintf(stdout, "Content-Length: %d\r\n", n_bytes);
fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); if ( fwrite(buffer, n_bytes, 1, stdout) != 1 ) {
if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 ) { Error("Unable to send stream text frame: %s", strerror(errno));
Error( "Unable to send stream text frame: %s", strerror(errno) ); return false;
return( false );
} }
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 StreamBase::openComms() { void StreamBase::openComms() {
if ( connkey > 0 ) { if ( connkey > 0 ) {
unsigned int length = snprintf( sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", staticConfig.PATH_SOCKS.c_str(), connkey); unsigned int length = snprintf(sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", staticConfig.PATH_SOCKS.c_str(), connkey);
if ( length >= sizeof(sock_path_lock) ) { if ( length >= sizeof(sock_path_lock) ) {
Warning("Socket lock path was truncated."); Warning("Socket lock path was truncated.");
} }
Debug( 1, "Trying to open the lock on %s", sock_path_lock ); Debug(1, "Trying to open the lock on %s", sock_path_lock);
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR); lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
if ( lock_fd <= 0 ) { if ( lock_fd <= 0 ) {
@ -285,46 +285,46 @@ void StreamBase::openComms() {
Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd); Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd);
} }
sd = socket( AF_UNIX, SOCK_DGRAM, 0 ); sd = socket(AF_UNIX, SOCK_DGRAM, 0);
if ( sd < 0 ) { if ( sd < 0 ) {
Fatal( "Can't create socket: %s", strerror(errno) ); Fatal("Can't create socket: %s", strerror(errno));
} else { } else {
Debug(1, "Have socket %d", sd ); Debug(1, "Have socket %d", sd);
} }
length = snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", staticConfig.PATH_SOCKS.c_str(), connkey ); length = snprintf(loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", staticConfig.PATH_SOCKS.c_str(), connkey);
if ( length >= sizeof(loc_sock_path) ) { if ( length >= sizeof(loc_sock_path) ) {
Warning("Socket path was truncated."); Warning("Socket path was truncated.");
length = sizeof(loc_sock_path)-1; length = sizeof(loc_sock_path)-1;
} }
// Unlink before bind, in case it already exists // Unlink before bind, in case it already exists
unlink( loc_sock_path ); unlink(loc_sock_path);
if ( sizeof(loc_addr.sun_path) < length ) { if ( sizeof(loc_addr.sun_path) < length ) {
Error("Not enough space %d in loc_addr.sun_path for socket file %s", sizeof(loc_addr.sun_path), loc_sock_path ); Error("Not enough space %d in loc_addr.sun_path for socket file %s", sizeof(loc_addr.sun_path), loc_sock_path);
} }
strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) ); strncpy(loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path));
loc_addr.sun_family = AF_UNIX; loc_addr.sun_family = AF_UNIX;
Debug(3, "Binding to %s", loc_sock_path ); Debug(3, "Binding to %s", loc_sock_path);
if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)+1 ) < 0 ) { if ( bind(sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)+1) < 0 ) {
Fatal( "Can't bind: %s", strerror(errno) ); Fatal("Can't bind: %s", strerror(errno));
} }
snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", staticConfig.PATH_SOCKS.c_str(), connkey ); snprintf(rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", staticConfig.PATH_SOCKS.c_str(), connkey);
strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path)-1 ); strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path)-1);
rem_addr.sun_family = AF_UNIX; rem_addr.sun_family = AF_UNIX;
} // end if connKey > 0 } // end if connKey > 0
Debug(2, "comms open" ); Debug(2, "comms open");
} // end void StreamBase::openComms() } // end void StreamBase::openComms()
void StreamBase::closeComms() { void StreamBase::closeComms() {
if ( connkey > 0 ) { if ( connkey > 0 ) {
if ( sd >= 0 ) { if ( sd >= 0 ) {
close( sd ); close(sd);
sd = -1; sd = -1;
} }
if ( loc_sock_path[0] ) { if ( loc_sock_path[0] ) {
unlink( loc_sock_path ); unlink(loc_sock_path);
} }
if ( lock_fd > 0 ) { if ( lock_fd > 0 ) {
close(lock_fd); //close it rather than unlock it incase it got deleted. close(lock_fd); //close it rather than unlock it incase it got deleted.
@ -332,4 +332,4 @@ void StreamBase::closeComms() {
//unlink(sock_path_lock); //unlink(sock_path_lock);
} }
} }
} } // end void StreamBase::closeComms

View File

@ -27,14 +27,18 @@
SWScale::SWScale() : gotdefaults(false), swscale_ctx(NULL), input_avframe(NULL), output_avframe(NULL) { SWScale::SWScale() : gotdefaults(false), swscale_ctx(NULL), input_avframe(NULL), output_avframe(NULL) {
Debug(4,"SWScale object created"); Debug(4,"SWScale object created");
}
bool SWScale::init() {
/* Allocate AVFrame for the input */ /* Allocate AVFrame for the input */
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
input_avframe = av_frame_alloc(); input_avframe = av_frame_alloc();
#else #else
input_avframe = avcodec_alloc_frame(); input_avframe = avcodec_alloc_frame();
#endif #endif
if(input_avframe == NULL) { if ( input_avframe == NULL ) {
Fatal("Failed allocating AVFrame for the input"); Error("Failed allocating AVFrame for the input");
return false;
} }
/* Allocate AVFrame for the output */ /* Allocate AVFrame for the output */
@ -43,21 +47,23 @@ SWScale::SWScale() : gotdefaults(false), swscale_ctx(NULL), input_avframe(NULL),
#else #else
output_avframe = avcodec_alloc_frame(); output_avframe = avcodec_alloc_frame();
#endif #endif
if(output_avframe == NULL) { if ( output_avframe == NULL ) {
Fatal("Failed allocating AVFrame for the output"); Error("Failed allocating AVFrame for the output");
return false;
} }
return true;
} }
SWScale::~SWScale() { SWScale::~SWScale() {
/* Free up everything */ /* Free up everything */
av_frame_free( &input_avframe ); if ( input_avframe )
//input_avframe = NULL; av_frame_free(&input_avframe);
av_frame_free( &output_avframe ); if ( output_avframe )
//output_avframe = NULL; av_frame_free(&output_avframe);
if(swscale_ctx) { if ( swscale_ctx ) {
sws_freeContext(swscale_ctx); sws_freeContext(swscale_ctx);
swscale_ctx = NULL; swscale_ctx = NULL;
} }

View File

@ -10,6 +10,7 @@ class SWScale {
public: public:
SWScale(); SWScale();
~SWScale(); ~SWScale();
bool init();
int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size); int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size);
int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size); int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size);

View File

@ -30,10 +30,10 @@
struct timespec getTimeout( int secs ) { struct timespec getTimeout( int secs ) {
struct timespec timeout; struct timespec timeout;
struct timeval temp_timeout; struct timeval temp_timeout;
gettimeofday( &temp_timeout, 0 ); gettimeofday(&temp_timeout, 0);
timeout.tv_sec = temp_timeout.tv_sec + secs; timeout.tv_sec = temp_timeout.tv_sec + secs;
timeout.tv_nsec = temp_timeout.tv_usec*1000; timeout.tv_nsec = temp_timeout.tv_usec*1000;
return( timeout ); return timeout;
} }
struct timespec getTimeout( double secs ) { struct timespec getTimeout( double secs ) {
@ -46,23 +46,23 @@ struct timespec getTimeout( double secs ) {
timeout.tv_sec += 1; timeout.tv_sec += 1;
timeout.tv_nsec -= 1000000000; timeout.tv_nsec -= 1000000000;
} }
return( timeout ); return timeout;
} }
Mutex::Mutex() { Mutex::Mutex() {
if ( pthread_mutex_init( &mMutex, NULL ) < 0 ) if ( pthread_mutex_init(&mMutex, NULL) < 0 )
Fatal( "Unable to create pthread mutex: %s", strerror(errno) ); Error("Unable to create pthread mutex: %s", strerror(errno));
} }
Mutex::~Mutex() { Mutex::~Mutex() {
if ( locked() ) if ( locked() )
Warning( "Destroying mutex when locked" ); Warning("Destroying mutex when locked");
if ( pthread_mutex_destroy( &mMutex ) < 0 ) if ( pthread_mutex_destroy(&mMutex) < 0 )
Fatal( "Unable to destroy pthread mutex: %s", strerror(errno) ); Error("Unable to destroy pthread mutex: %s", strerror(errno));
} }
void Mutex::lock() { void Mutex::lock() {
if ( pthread_mutex_lock( &mMutex ) < 0 ) if ( pthread_mutex_lock(&mMutex) < 0 )
throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) ); throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) );
//Debug(3, "Lock"); //Debug(3, "Lock");
} }
@ -101,7 +101,7 @@ Condition::Condition( Mutex &mutex ) : mMutex( mutex ) {
Condition::~Condition() { Condition::~Condition() {
if ( pthread_cond_destroy( &mCondition ) < 0 ) if ( pthread_cond_destroy( &mCondition ) < 0 )
Fatal( "Unable to destroy pthread condition: %s", strerror(errno) ); Error("Unable to destroy pthread condition: %s", strerror(errno));
} }
void Condition::wait() { void Condition::wait() {

View File

@ -54,7 +54,7 @@ private:
pthread_t pid() { return( pthread_self() ); } pthread_t pid() { return( pthread_self() ); }
#endif #endif
public: public:
explicit ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) { explicit ThreadException( const std::string &message ) : Exception( stringtf("(%d) ", (long int)pid())+message ) {
} }
}; };
@ -97,61 +97,53 @@ private:
ScopedMutex( const ScopedMutex & ); ScopedMutex( const ScopedMutex & );
}; };
class Condition class Condition {
{
private: private:
Mutex &mMutex; Mutex &mMutex;
pthread_cond_t mCondition; pthread_cond_t mCondition;
public: public:
Condition( Mutex &mutex ); explicit Condition(Mutex &mutex);
~Condition(); ~Condition();
void wait(); void wait();
bool wait( int secs ); bool wait(int secs);
bool wait( double secs ); bool wait(double secs);
void signal(); void signal();
void broadcast(); void broadcast();
}; };
class Semaphore : public Condition class Semaphore : public Condition {
{
private: private:
Mutex mMutex; Mutex mMutex;
public: public:
Semaphore() : Condition( mMutex ) Semaphore() : Condition(mMutex) {
{
} }
void wait() void wait() {
{
mMutex.lock(); mMutex.lock();
Condition::wait(); Condition::wait();
mMutex.unlock(); mMutex.unlock();
} }
bool wait( int secs ) bool wait(int secs) {
{
mMutex.lock(); mMutex.lock();
bool result = Condition::wait( secs ); bool result = Condition::wait(secs);
mMutex.unlock(); mMutex.unlock();
return( result ); return result;
} }
bool wait( double secs ) bool wait(double secs) {
{
mMutex.lock(); mMutex.lock();
bool result = Condition::wait( secs ); bool result = Condition::wait(secs);
mMutex.unlock(); mMutex.unlock();
return( result ); return result;
} }
void signal() void signal() {
{
mMutex.lock(); mMutex.lock();
Condition::signal(); Condition::signal();
mMutex.unlock(); mMutex.unlock();
} }
void broadcast() void broadcast() {
{
mMutex.lock(); mMutex.lock();
Condition::broadcast(); Condition::broadcast();
mMutex.unlock(); mMutex.unlock();
@ -169,7 +161,7 @@ public:
__attribute__((used)) ThreadData() : mValue(0), mCondition( mMutex ) { __attribute__((used)) ThreadData() : mValue(0), mCondition( mMutex ) {
mChanged = false; mChanged = false;
} }
__attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex ) { explicit __attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex ) {
mChanged = false; mChanged = false;
} }
//~ThreadData() {} //~ThreadData() {}
@ -200,8 +192,7 @@ public:
__attribute__((used)) void updateValueBroadcast( const T value ); __attribute__((used)) void updateValueBroadcast( const T value );
}; };
class Thread class Thread {
{
public: public:
typedef void *(*ThreadFunc)( void * ); typedef void *(*ThreadFunc)( void * );
@ -224,8 +215,7 @@ protected:
virtual ~Thread(); virtual ~Thread();
#ifndef SOLARIS #ifndef SOLARIS
pid_t id() const pid_t id() const {
{
pid_t tid; pid_t tid;
#ifdef __FreeBSD__ #ifdef __FreeBSD__
long lwpid; long lwpid;
@ -239,16 +229,14 @@ protected:
tid=syscall(SYS_gettid); tid=syscall(SYS_gettid);
#endif #endif
#endif #endif
return tid; return tid;
} }
#else #else
pthread_t id() const pthread_t id() const {
{ return pthread_self();
return( pthread_self() );
} }
#endif #endif
void exit( int p_status = 0 ) void exit( int p_status = 0 ) {
{
//INFO( "Exiting" ); //INFO( "Exiting" );
pthread_exit( (void *)&p_status ); pthread_exit( (void *)&p_status );
} }
@ -260,12 +248,11 @@ public:
void start(); void start();
void join(); void join();
void kill( int signal ); void kill( int signal );
bool isThread() bool isThread() {
{
return( mPid > -1 && pthread_equal( pthread_self(), mThread ) ); return( mPid > -1 && pthread_equal( pthread_self(), mThread ) );
} }
bool isStarted() const { return( mStarted ); } bool isStarted() const { return mStarted; }
bool isRunning() const { return( mRunning ); } bool isRunning() const { return mRunning; }
}; };
#endif // ZM_THREAD_H #endif // ZM_THREAD_H

View File

@ -77,8 +77,6 @@ struct DeltaTimeval
#define MSEC_PER_SEC 1000 #define MSEC_PER_SEC 1000
extern struct timeval tv; extern struct timeval tv;
typedef typeof(tv.tv_sec) ast_time_t;
typedef typeof(tv.tv_usec) ast_suseconds_t;
inline int tvDiffUsec( struct timeval first, struct timeval last ) inline int tvDiffUsec( struct timeval first, struct timeval last )
{ {

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