Merge branch 'storageareas' into origin/zma_to_thread
This commit is contained in:
commit
077ad75516
|
@ -5,7 +5,7 @@ CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour
|
||||||
FOR EACH ROW BEGIN
|
FOR EACH ROW BEGIN
|
||||||
UPDATE Monitors SET
|
UPDATE Monitors SET
|
||||||
HourEvents = COALESCE(HourEvents,1)-1,
|
HourEvents = COALESCE(HourEvents,1)-1,
|
||||||
HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
|
HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
|
||||||
WHERE Id=OLD.MonitorId;
|
WHERE Id=OLD.MonitorId;
|
||||||
END;
|
END;
|
||||||
//
|
//
|
||||||
|
@ -20,23 +20,21 @@ FOR EACH ROW
|
||||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||||
IF ( diff ) THEN
|
IF ( diff ) THEN
|
||||||
IF ( NEW.MonitorID != OLD.MonitorID ) 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=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId;
|
||||||
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
|
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||||
ELSE
|
ELSE
|
||||||
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
|
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
//
|
//
|
||||||
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
|
||||||
FOR EACH ROW BEGIN
|
FOR EACH ROW BEGIN
|
||||||
UPDATE Monitors SET
|
UPDATE Monitors SET
|
||||||
DayEvents = COALESCE(DayEvents,1)-1,
|
DayEvents = GREATEST(COALESCE(DayEvents,1)-1,0),
|
||||||
DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
|
DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
|
||||||
WHERE Id=OLD.MonitorId;
|
WHERE Id=OLD.MonitorId;
|
||||||
END;
|
END;
|
||||||
//
|
//
|
||||||
|
@ -50,10 +48,10 @@ FOR EACH ROW
|
||||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||||
IF ( diff ) THEN
|
IF ( diff ) THEN
|
||||||
IF ( NEW.MonitorID != OLD.MonitorID ) 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=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId;
|
||||||
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
|
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||||
ELSE
|
ELSE
|
||||||
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
|
UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
|
@ -65,7 +63,7 @@ CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week
|
||||||
FOR EACH ROW BEGIN
|
FOR EACH ROW BEGIN
|
||||||
UPDATE Monitors SET
|
UPDATE Monitors SET
|
||||||
WeekEvents = COALESCE(WeekEvents,1)-1,
|
WeekEvents = COALESCE(WeekEvents,1)-1,
|
||||||
WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
|
WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
|
||||||
WHERE Id=OLD.MonitorId;
|
WHERE Id=OLD.MonitorId;
|
||||||
END;
|
END;
|
||||||
//
|
//
|
||||||
|
@ -79,10 +77,10 @@ FOR EACH ROW
|
||||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||||
IF ( diff ) THEN
|
IF ( diff ) THEN
|
||||||
IF ( NEW.MonitorID != OLD.MonitorID ) 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=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId;
|
||||||
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
|
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||||
ELSE
|
ELSE
|
||||||
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
|
UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
|
@ -93,7 +91,7 @@ CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month
|
||||||
FOR EACH ROW BEGIN
|
FOR EACH ROW BEGIN
|
||||||
UPDATE Monitors SET
|
UPDATE Monitors SET
|
||||||
MonthEvents = COALESCE(MonthEvents,1)-1,
|
MonthEvents = COALESCE(MonthEvents,1)-1,
|
||||||
MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
|
MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
|
||||||
WHERE Id=OLD.MonitorId;
|
WHERE Id=OLD.MonitorId;
|
||||||
END;
|
END;
|
||||||
//
|
//
|
||||||
|
@ -107,10 +105,10 @@ FOR EACH ROW
|
||||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||||
IF ( diff ) THEN
|
IF ( diff ) THEN
|
||||||
IF ( NEW.MonitorID != OLD.MonitorID ) 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=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace),0) WHERE Monitors.Id=OLD.MonitorId;
|
||||||
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId;
|
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId;
|
||||||
ELSE
|
ELSE
|
||||||
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
|
UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
|
@ -128,14 +126,14 @@ BEGIN
|
||||||
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
|
||||||
UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + diff WHERE Id = OLD.StorageId;
|
UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) + diff,0) WHERE Id = OLD.StorageId;
|
||||||
END IF;
|
END IF;
|
||||||
ELSE
|
ELSE
|
||||||
IF ( NEW.DiskSpace ) THEN
|
IF ( NEW.DiskSpace ) THEN
|
||||||
UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Id = NEW.StorageId;
|
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
|
||||||
UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) - OLD.DiskSpace WHERE Id = OLD.StorageId;
|
UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - OLD.DiskSpace,0) WHERE Id = OLD.StorageId;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
|
@ -150,12 +148,16 @@ BEGIN
|
||||||
UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId;
|
UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId;
|
||||||
ELSEIF ( OLD.Archived ) THEN
|
ELSEIF ( OLD.Archived ) THEN
|
||||||
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
|
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;
|
UPDATE Monitors
|
||||||
|
SET
|
||||||
|
ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,0)-1,0),
|
||||||
|
ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0)
|
||||||
|
WHERE Id=OLD.MonitorId;
|
||||||
ELSE
|
ELSE
|
||||||
IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
|
IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
|
||||||
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||||
UPDATE Monitors SET
|
UPDATE Monitors SET
|
||||||
ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0)
|
ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0)
|
||||||
WHERE Id=OLD.MonitorId;
|
WHERE Id=OLD.MonitorId;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
|
@ -164,18 +166,18 @@ BEGIN
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF ( diff ) THEN
|
IF ( diff ) THEN
|
||||||
UPDATE Monitors SET TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=OLD.MonitorId;
|
UPDATE Monitors
|
||||||
|
SET
|
||||||
|
TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0)
|
||||||
|
WHERE Id=OLD.MonitorId;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
END;
|
END;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
delimiter ;
|
DROP TRIGGER IF EXISTS event_insert_trigger//
|
||||||
|
|
||||||
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 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
|
* The DiskSpace will get update in the Event Update Trigger
|
||||||
*/
|
*/
|
||||||
|
@ -203,7 +205,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
|
||||||
UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) - CAST(OLD.DiskSpace AS SIGNED) WHERE Id = OLD.StorageId;
|
UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) 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;
|
||||||
|
@ -212,15 +214,15 @@ BEGIN
|
||||||
IF ( OLD.Archived ) THEN
|
IF ( OLD.Archived ) THEN
|
||||||
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
|
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
|
||||||
UPDATE Monitors SET
|
UPDATE Monitors SET
|
||||||
ArchivedEvents = COALESCE(ArchivedEvents,1) - 1,
|
ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,1) - 1,0),
|
||||||
ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),
|
ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0),
|
||||||
TotalEvents = COALESCE(TotalEvents,1) - 1,
|
TotalEvents = GREATEST(COALESCE(TotalEvents,1) - 1,0),
|
||||||
TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0)
|
TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0)
|
||||||
WHERE Id=OLD.MonitorId;
|
WHERE Id=OLD.MonitorId;
|
||||||
ELSE
|
ELSE
|
||||||
UPDATE Monitors SET
|
UPDATE Monitors SET
|
||||||
TotalEvents = COALESCE(TotalEvents,1)-1,
|
TotalEvents = GREATEST(COALESCE(TotalEvents,1)-1,0),
|
||||||
TotalEventDiskSpace=COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
|
TotalEventDiskSpace=GREATEST(COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
|
||||||
WHERE Id=OLD.MonitorId;
|
WHERE Id=OLD.MonitorId;
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
|
|
|
@ -68,6 +68,7 @@ CREATE TABLE `Controls` (
|
||||||
`CanWake` tinyint(3) unsigned NOT NULL default '0',
|
`CanWake` tinyint(3) unsigned NOT NULL default '0',
|
||||||
`CanSleep` tinyint(3) unsigned NOT NULL default '0',
|
`CanSleep` tinyint(3) unsigned NOT NULL default '0',
|
||||||
`CanReset` tinyint(3) unsigned NOT NULL default '0',
|
`CanReset` tinyint(3) unsigned NOT NULL default '0',
|
||||||
|
`CanReboot` tinyint(3) unsigned NOT NULL default '0',
|
||||||
`CanZoom` tinyint(3) unsigned NOT NULL default '0',
|
`CanZoom` tinyint(3) unsigned NOT NULL default '0',
|
||||||
`CanAutoZoom` tinyint(3) unsigned NOT NULL default '0',
|
`CanAutoZoom` tinyint(3) unsigned NOT NULL default '0',
|
||||||
`CanZoomAbs` tinyint(3) unsigned NOT NULL default '0',
|
`CanZoomAbs` tinyint(3) unsigned NOT NULL default '0',
|
||||||
|
@ -499,7 +500,7 @@ CREATE TABLE `Monitors` (
|
||||||
`ReturnDelay` smallint(5) unsigned,
|
`ReturnDelay` smallint(5) unsigned,
|
||||||
`DefaultRate` smallint(5) unsigned NOT NULL default '100',
|
`DefaultRate` smallint(5) unsigned NOT NULL default '100',
|
||||||
`DefaultScale` smallint(5) unsigned NOT NULL default '100',
|
`DefaultScale` smallint(5) unsigned NOT NULL default '100',
|
||||||
`DefaultCodec` enum('auto','H264','H265','MJPEG') NOT NULL default 'auto',
|
`DefaultCodec` enum('auto','MP4','MJPEG') NOT NULL default 'auto',
|
||||||
`SignalCheckPoints` INT UNSIGNED NOT NULL default '0',
|
`SignalCheckPoints` INT UNSIGNED NOT NULL default '0',
|
||||||
`SignalCheckColour` varchar(32) NOT NULL default '#0000BE',
|
`SignalCheckColour` varchar(32) NOT NULL default '#0000BE',
|
||||||
`WebColour` varchar(32) NOT NULL default 'red',
|
`WebColour` varchar(32) NOT NULL default 'red',
|
||||||
|
@ -574,6 +575,7 @@ CREATE TABLE `Servers` (
|
||||||
`zmstats` BOOLEAN NOT NULL DEFAULT FALSE,
|
`zmstats` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
`zmaudit` BOOLEAN NOT NULL DEFAULT FALSE,
|
`zmaudit` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
`zmtrigger` BOOLEAN NOT NULL DEFAULT FALSE,
|
`zmtrigger` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
`zmeventnotification` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
PRIMARY KEY (`Id`)
|
PRIMARY KEY (`Id`)
|
||||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||||
|
|
||||||
|
@ -638,7 +640,7 @@ CREATE TABLE `Users` (
|
||||||
`Devices` enum('None','View','Edit') NOT NULL default 'None',
|
`Devices` enum('None','View','Edit') NOT NULL default 'None',
|
||||||
`System` enum('None','View','Edit') NOT NULL default 'None',
|
`System` enum('None','View','Edit') NOT NULL default 'None',
|
||||||
`MaxBandwidth` varchar(16),
|
`MaxBandwidth` varchar(16),
|
||||||
`MonitorIds` tinytext,
|
`MonitorIds` text,
|
||||||
PRIMARY KEY (`Id`),
|
PRIMARY KEY (`Id`),
|
||||||
UNIQUE KEY `UC_Username` (`Username`)
|
UNIQUE KEY `UC_Username` (`Username`)
|
||||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||||
|
@ -751,46 +753,46 @@ insert into Filters values (NULL,'Update DiskSpace','{"terms":[{"attr":"DiskSpac
|
||||||
--
|
--
|
||||||
-- Add in some sample control protocol definitions
|
-- Add in some sample control protocol definitions
|
||||||
--
|
--
|
||||||
INSERT INTO Controls VALUES (NULL,'Pelco-D','Local','PelcoD',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
|
INSERT INTO Controls VALUES (NULL,'Pelco-D','Local','PelcoD',1,1,0,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
|
||||||
INSERT INTO Controls VALUES (NULL,'Pelco-P','Local','PelcoP',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
|
INSERT INTO Controls VALUES (NULL,'Pelco-P','Local','PelcoP',1,1,0,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
|
||||||
INSERT INTO Controls VALUES (NULL,'Sony VISCA','Local','Visca',1,1,0,1,0,0,0,1,0,16384,10,4000,1,1,6,1,1,1,0,1,0,1536,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,3,1,1,1,1,0,1,1,0,1,-15578,15578,100,10000,1,1,50,1,254,1,-7789,7789,100,5000,1,1,50,1,254,0,0);
|
INSERT INTO Controls VALUES (NULL,'Sony VISCA','Local','Visca',1,1,0,0,1,0,0,0,1,0,16384,10,4000,1,1,6,1,1,1,0,1,0,1536,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,3,1,1,1,1,0,1,1,0,1,-15578,15578,100,10000,1,1,50,1,254,1,-7789,7789,100,5000,1,1,50,1,254,0,0);
|
||||||
INSERT INTO Controls VALUES (NULL,'Axis API v2','Remote','AxisV2',0,0,0,1,0,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,1,1,1,1,1,0,1,0,1,-360,360,1,90,0,NULL,NULL,0,NULL,1,-360,360,1,90,0,NULL,NULL,0,NULL,0,0);
|
INSERT INTO Controls VALUES (NULL,'Axis API v2','Remote','AxisV2',0,0,0,0,1,0,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,1,1,0,1,0,0,9999,10,2500,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,1,1,1,1,1,0,1,0,1,-360,360,1,90,0,NULL,NULL,0,NULL,1,-360,360,1,90,0,NULL,NULL,0,NULL,0,0);
|
||||||
INSERT INTO Controls VALUES (NULL,'Panasonic IP','Remote','PanasonicIP',0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0);
|
INSERT INTO Controls VALUES (NULL,'Panasonic IP','Remote','PanasonicIP',0,0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0);
|
||||||
INSERT INTO Controls VALUES (NULL,'Neu-Fusion NCS370','Remote','Ncs370',0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,24,1,0,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0);
|
INSERT INTO Controls VALUES (NULL,'Neu-Fusion NCS370','Remote','Ncs370',0,0,0,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,24,1,0,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0);
|
||||||
INSERT INTO Controls VALUES (NULL,'AirLink SkyIPCam 7xx','Remote','SkyIPCam7xx',0,0,1,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,1,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0);
|
INSERT INTO Controls VALUES (NULL,'AirLink SkyIPCam 7xx','Remote','SkyIPCam7xx',0,0,1,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,8,1,1,1,0,1,0,1,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,1,NULL,NULL,NULL,NULL,0,NULL,NULL,0,NULL,0,0);
|
||||||
INSERT INTO Controls VALUES (NULL,'Pelco-D','Ffmpeg','PelcoD',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
|
INSERT INTO Controls VALUES (NULL,'Pelco-D','Ffmpeg','PelcoD',1,1,0,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
|
||||||
INSERT INTO Controls VALUES (NULL,'Pelco-P','Ffmpeg','PelcoP',1,1,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
|
INSERT INTO Controls VALUES (NULL,'Pelco-P','Ffmpeg','PelcoP',1,1,0,0,1,1,0,0,1,NULL,NULL,NULL,NULL,1,0,3,1,1,0,0,1,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,1,0,1,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,20,1,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,63,1,254,1,NULL,NULL,NULL,NULL,1,0,63,1,254,0,0);
|
||||||
INSERT INTO Controls VALUES (NULL,'Foscam FI8620','Ffmpeg','FI8620_Y2k',0,0,0,1,0,0,0,1,1,10,1,10,1,1,63,1,1,0,0,1,1,63,1,63,1,1,63,1,1,0,0,1,0,0,0,0,1,0,255,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,1,8,0,1,1,1,0,0,0,1,1,1,360,1,360,1,1,63,0,0,1,1,90,1,90,1,1,63,0,0,0,0);
|
INSERT INTO Controls VALUES (NULL,'Foscam FI8620','Ffmpeg','FI8620_Y2k',0,0,0,0,1,0,0,0,1,1,10,1,10,1,1,63,1,1,0,0,1,1,63,1,63,1,1,63,1,1,0,0,1,0,0,0,0,1,0,255,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,1,8,0,1,1,1,0,0,0,1,1,1,360,1,360,1,1,63,0,0,1,1,90,1,90,1,1,63,0,0,0,0);
|
||||||
INSERT INTO Controls VALUES (NULL,'Foscam FI8608W','Ffmpeg','FI8608W_Y2k',1,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,1,8,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,128,0,0,1,0,0,0,0,1,1,128,0,0,0,0);
|
INSERT INTO Controls VALUES (NULL,'Foscam FI8608W','Ffmpeg','FI8608W_Y2k',1,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,255,1,8,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,128,0,0,1,0,0,0,0,1,1,128,0,0,0,0);
|
||||||
INSERT INTO Controls VALUES (NULL,'Foscam FI8908W','Remote','FI8908W',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,0,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,'Foscam FI8908W','Remote','FI8908W',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,0,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,'Foscam FI9821W','Ffmpeg','FI9821W_Y2k',1,0,1,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,100,1,1,0,0,1,0,100,0,100,1,0,100,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,100,0,100,1,0,100,1,16,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0);
|
INSERT INTO Controls VALUES (NULL,'Foscam FI9821W','Ffmpeg','FI9821W_Y2k',1,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,1,0,100,1,1,0,0,1,0,100,0,100,1,0,100,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,100,0,100,1,0,100,1,16,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0);
|
||||||
INSERT INTO Controls VALUES (NULL,'Loftek Sentinel','Remote','LoftekSentinel',0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,255,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,6,1,1,0,0,0,1,10,0,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,'Loftek Sentinel','Remote','LoftekSentinel',0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,255,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,6,1,1,0,0,0,1,10,0,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,'Toshiba IK-WB11A','Remote','Toshiba_IK_WB11A',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,10,0,1,1,0,1,0,1,0,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,'Toshiba IK-WB11A','Remote','Toshiba_IK_WB11A',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,1,10,0,1,1,0,1,0,1,0,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,'WanscamPT','Remote','Wanscam',1,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,1,0,1,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,16,0,0,0,0,0,1,16,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,'WanscamPT','Remote','Wanscam',1,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,1,0,1,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,16,0,0,0,0,0,1,16,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,'3S Domo N5071', 'Remote', '3S', 0, 0, 1, 1, 0, 1, 1, 0, 0, 9999, 0, 9999, 0, 0, 0, 1, 1, 1, 1, 0, 0, 9999, 20, 9999, 0, 0, 0, 1, 1, 1, 1, 0, 0, 9999, 1, 9999, 0, 0, 0, 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, 0, 1, 1, 0, 0, 0, 0, 1, -180, 180, 40, 100, 1, 40, 100, 0, 0, 1, -180, 180, 40, 100, 1, 40, 100, 0, 0, 0, 0);
|
INSERT INTO Controls VALUES (NULL,'3S Domo N5071', 'Remote', '3S', 0, 0, 1, 0,1, 0, 1, 1, 0, 0, 9999, 0, 9999, 0, 0, 0, 1, 1, 1, 1, 0, 0, 9999, 20, 9999, 0, 0, 0, 1, 1, 1, 1, 0, 0, 9999, 1, 9999, 0, 0, 0, 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, 0, 1, 1, 0, 0, 0, 0, 1, -180, 180, 40, 100, 1, 40, 100, 0, 0, 1, -180, 180, 40, 100, 1, 40, 100, 0, 0, 0, 0);
|
||||||
INSERT INTO Controls VALUES (NULL,'ONVIF Camera','Ffmpeg','onvif',0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,255,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,6,1,1,0,0,0,1,10,0,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,'ONVIF Camera','Ffmpeg','onvif',0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,255,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,6,1,1,0,0,0,1,10,0,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,'Foscam 9831W','Ffmpeg','FI9831W',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,16,1,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,0,0,0,0,0,0,0);
|
INSERT INTO `Controls` VALUES (NULL,'Foscam 9831W','Ffmpeg','FI9831W',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,1,16,1,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,0,0,0,0,0,0,0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'Foscam FI8918W','Ffmpeg','FI8918W',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,8,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0);
|
INSERT INTO `Controls` VALUES (NULL,'Foscam FI8918W','Ffmpeg','FI8918W',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,1,8,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'SunEyes SP-P1802SWPTZ','Libvlc','SPP1802SWPTZ',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,8,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,64,0,0,1,0,0,0,0,1,0,64,0,0,0,0);
|
INSERT INTO `Controls` VALUES (NULL,'SunEyes SP-P1802SWPTZ','Libvlc','SPP1802SWPTZ',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,8,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,64,0,0,1,0,0,0,0,1,0,64,0,0,0,0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'Wanscam HW0025','Libvlc','WanscamHW0025', 1, 1, 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, 0, 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, 16, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 350, 0, 0, 1, 0, 10, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 0, 0, 0, 0);
|
INSERT INTO `Controls` VALUES (NULL,'Wanscam HW0025','Libvlc','WanscamHW0025', 1, 1, 1, 0,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, 0, 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, 16, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 350, 0, 0, 1, 0, 10, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 0, 0, 0, 0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'IPCC 7210W','Remote','IPCC7210W', 1, 1, 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, 0, 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, 16, 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,'IPCC 7210W','Remote','IPCC7210W', 1, 1, 1, 0,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, 0, 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, 16, 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,'Vivotek ePTZ','Remote','Vivotek_ePTZ',0,0,1,1,0,0,0,1,0,0,0,0,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,1,0,0,0,0,1,0,5,0,0,1,0,0,0,0,1,0,5,0,0,0,0);
|
INSERT INTO `Controls` VALUES (NULL,'Vivotek ePTZ','Remote','Vivotek_ePTZ',0,0,1,0,1,0,0,0,1,0,0,0,0,1,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,1,0,0,0,0,1,0,5,0,0,1,0,0,0,0,1,0,5,0,0,0,0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'Netcat ONVIF','Ffmpeg','Netcat',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,1,0,1,0,0,0,100,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,100,5,5,0,0,0,1,255,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,'Netcat ONVIF','Ffmpeg','Netcat',0,0,1,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,100,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,100,5,5,0,0,0,1,255,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,'Keekoon','Remote','Keekoon', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 6, 0, 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,'Keekoon','Remote','Keekoon', 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 6, 0, 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,'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,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,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,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-423','Ffmpeg','Reolink',0,0,1,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,64,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,0,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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);
|
INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',0,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);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,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,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,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,'Dericam P2','Ffmpeg','DericamP2',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,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0);
|
INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',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,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,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,1,1,1,0,1,0,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,'Trendnet','Remote','Trendnet',1,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,1,1,1,0,1,0,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,'PSIA','Remote','PSIA',0,0,0,1,0,0,1,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,0,1,0,0,0,0,1,-100,100,0,0,1,0,0,0,0,1,-100,100,0,0,0,0);
|
INSERT INTO `Controls` VALUES (NULL,'PSIA','Remote','PSIA',0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,0,1,0,0,0,0,1,-100,100,0,0,1,0,0,0,0,1,-100,100,0,0,0,0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'Dahua','Remote','Dahua',0,0,0,1,0,0,1,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,0,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,'Dahua','Remote','Dahua',0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,0,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,'FOSCAMR2C','Libvlc','FOSCAMR2C',1,1,1,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,0,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,0,0);
|
INSERT INTO `Controls` VALUES (NULL,'FOSCAMR2C','Libvlc','FOSCAMR2C',1,1,1,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,0,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,0,0);
|
||||||
INSERT INTO `Controls` VALUES (NULL,'Amcrest HTTP API','Ffmpeg','Amcrest_HTTP',0,0,1,1,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,1,5,0,0,1,0,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,5);
|
INSERT INTO `Controls` VALUES (NULL,'Amcrest HTTP API','Ffmpeg','Amcrest_HTTP',0,0,1,0,1,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,1,5,0,0,1,0,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,5);
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Add some monitor preset values
|
-- Add some monitor preset values
|
||||||
|
|
|
@ -322,7 +322,7 @@ WHERE NOT EXISTS (
|
||||||
--
|
--
|
||||||
-- Hide USE_DEEP_STORAGE from user to prevent accidental event loss
|
-- Hide USE_DEEP_STORAGE from user to prevent accidental event loss
|
||||||
--
|
--
|
||||||
UPDATE `zm`.`Config` SET `Category`='hidden' WHERE `Name`='ZM_USE_DEEP_STORAGE';
|
UPDATE Config SET Category='hidden' WHERE Name='ZM_USE_DEEP_STORAGE';
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Add Id column to State
|
-- Add Id column to State
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||||
|
AND table_name = 'Servers'
|
||||||
|
AND column_name = 'zmeventnotification'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column zmeventnotification already exists in Servers'",
|
||||||
|
"ALTER TABLE Servers ADD `zmeventnotification` BOOLEAN NOT NULL DEFAULT FALSE AFTER `zmtrigger`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||||
|
AND table_name = 'Monitors'
|
||||||
|
AND column_name = 'DefaultCodec'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column DefaultCodec already exists in Monitors'",
|
||||||
|
"ALTER TABLE Monitors ADD `DefaultCodec` enum('auto','MP4','MJPEG') NOT NULL default 'auto' AFTER `DefaultScale`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
|
||||||
|
AND table_name = 'Controls'
|
||||||
|
AND column_name = 'CanReboot'
|
||||||
|
) > 0,
|
||||||
|
"SELECT 'Column CanReboot already exists in Controls'",
|
||||||
|
"ALTER TABLE Controls ADD `CanReboot` tinyint(3) unsigned NOT NULL default '0' AFTER `CanReset`"
|
||||||
|
));
|
||||||
|
|
||||||
|
PREPARE stmt FROM @s;
|
||||||
|
EXECUTE stmt;
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE Users MODIFY MonitorIds text;
|
|
@ -0,0 +1,283 @@
|
||||||
|
|
||||||
|
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=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),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=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),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;
|
||||||
|
//
|
||||||
|
|
||||||
|
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 = GREATEST(COALESCE(DayEvents,1)-1,0),
|
||||||
|
DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),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=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),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=GREATEST(COALESCE(DayEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
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=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),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=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),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=GREATEST(COALESCE(WeekEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
//
|
||||||
|
|
||||||
|
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=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),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=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace),0) 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=GREATEST(COALESCE(MonthEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
END;
|
||||||
|
//
|
||||||
|
|
||||||
|
drop procedure if exists update_storage_stats//
|
||||||
|
|
||||||
|
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
|
||||||
|
UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) + diff,0) WHERE Id = OLD.StorageId;
|
||||||
|
END IF;
|
||||||
|
ELSE
|
||||||
|
IF ( NEW.DiskSpace ) THEN
|
||||||
|
UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Id = NEW.StorageId;
|
||||||
|
END IF;
|
||||||
|
IF ( OLD.DiskSpace ) THEN
|
||||||
|
UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - OLD.DiskSpace,0) WHERE Id = OLD.StorageId;
|
||||||
|
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 = GREATEST(COALESCE(ArchivedEvents,0)-1,0),
|
||||||
|
ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),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 = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),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 = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0)
|
||||||
|
WHERE Id=OLD.MonitorId;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
END;
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
DROP TRIGGER IF EXISTS event_insert_trigger//
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) WHERE Id = OLD.StorageId;
|
||||||
|
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 = GREATEST(COALESCE(ArchivedEvents,1) - 1,0),
|
||||||
|
ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0),
|
||||||
|
TotalEvents = GREATEST(COALESCE(TotalEvents,1) - 1,0),
|
||||||
|
TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0)
|
||||||
|
WHERE Id=OLD.MonitorId;
|
||||||
|
ELSE
|
||||||
|
UPDATE Monitors SET
|
||||||
|
TotalEvents = GREATEST(COALESCE(TotalEvents,1)-1,0),
|
||||||
|
TotalEventDiskSpace=GREATEST(COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),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 ;
|
||||||
|
|
||||||
|
REPLACE INTO Events_Hour SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 hour);
|
||||||
|
REPLACE INTO Events_Day SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 day);
|
||||||
|
REPLACE INTO Events_Week SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 week);
|
||||||
|
REPLACE INTO Events_Month SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 month);
|
||||||
|
REPLACE INTO Events_Archived SELECT Id,MonitorId,DiskSpace FROM Events WHERE Archived=1;
|
||||||
|
|
||||||
|
UPDATE Monitors INNER JOIN (
|
||||||
|
SELECT MonitorId,
|
||||||
|
COUNT(Id) AS TotalEvents,
|
||||||
|
SUM(DiskSpace) AS TotalEventDiskSpace,
|
||||||
|
SUM(IF(Archived,1,0)) AS ArchivedEvents,
|
||||||
|
SUM(IF(Archived,DiskSpace,0)) AS ArchivedEventDiskSpace,
|
||||||
|
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents,
|
||||||
|
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace,
|
||||||
|
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents,
|
||||||
|
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace,
|
||||||
|
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents,
|
||||||
|
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace,
|
||||||
|
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents,
|
||||||
|
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace
|
||||||
|
FROM Events GROUP BY MonitorId
|
||||||
|
) AS E ON E.MonitorId=Monitors.Id SET
|
||||||
|
Monitors.TotalEvents = E.TotalEvents,
|
||||||
|
Monitors.TotalEventDiskSpace = E.TotalEventDiskSpace,
|
||||||
|
Monitors.ArchivedEvents = E.ArchivedEvents,
|
||||||
|
Monitors.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace,
|
||||||
|
Monitors.HourEvents = E.HourEvents,
|
||||||
|
Monitors.HourEventDiskSpace = E.HourEventDiskSpace,
|
||||||
|
Monitors.DayEvents = E.DayEvents,
|
||||||
|
Monitors.DayEventDiskSpace = E.DayEventDiskSpace,
|
||||||
|
Monitors.WeekEvents = E.WeekEvents,
|
||||||
|
Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace,
|
||||||
|
Monitors.MonthEvents = E.MonthEvents,
|
||||||
|
Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace;
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
%global _hardened_build 1
|
%global _hardened_build 1
|
||||||
|
|
||||||
Name: zoneminder
|
Name: zoneminder
|
||||||
Version: 1.33.0
|
Version: 1.33.6
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: A camera monitoring and analysis tool
|
Summary: A camera monitoring and analysis tool
|
||||||
Group: System Environment/Daemons
|
Group: System Environment/Daemons
|
||||||
|
@ -317,7 +317,7 @@ EOF
|
||||||
|
|
||||||
%files common
|
%files common
|
||||||
%license COPYING
|
%license COPYING
|
||||||
%doc AUTHORS README.md distros/redhat/readme/README distros/redhat/readme/README.httpd distros/redhat/readme/README.nginx distros/redhat/readme/README.https
|
%doc README.md distros/redhat/readme/README distros/redhat/readme/README.httpd distros/redhat/readme/README.nginx distros/redhat/readme/README.https
|
||||||
|
|
||||||
# We want these two folders to have "normal" read permission
|
# We want these two folders to have "normal" read permission
|
||||||
# compared to the folder contents
|
# compared to the folder contents
|
||||||
|
@ -352,6 +352,7 @@ EOF
|
||||||
%{_bindir}/zmx10.pl
|
%{_bindir}/zmx10.pl
|
||||||
%{_bindir}/zmonvif-probe.pl
|
%{_bindir}/zmonvif-probe.pl
|
||||||
%{_bindir}/zmstats.pl
|
%{_bindir}/zmstats.pl
|
||||||
|
%{_bindir}/zmrecover.pl
|
||||||
|
|
||||||
%{perl_vendorlib}/ZoneMinder*
|
%{perl_vendorlib}/ZoneMinder*
|
||||||
%{perl_vendorlib}/ONVIF*
|
%{perl_vendorlib}/ONVIF*
|
||||||
|
@ -409,8 +410,14 @@ EOF
|
||||||
%dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload
|
%dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Sun Apr 07 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.6-1
|
||||||
|
- Bump to 1.33.6 Development
|
||||||
|
|
||||||
|
* Sat Mar 30 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.4-1
|
||||||
|
- Bump to 1.33.4 Development
|
||||||
|
|
||||||
* Tue Dec 11 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.0-1
|
* Tue Dec 11 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.0-1
|
||||||
- Bump tp 1.33.0 Development
|
- Bump to 1.33.0 Development
|
||||||
|
|
||||||
* Sat Dec 08 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.32.3-1
|
* Sat Dec 08 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.32.3-1
|
||||||
- 1.32.3 Release
|
- 1.32.3 Release
|
||||||
|
|
|
@ -3,6 +3,147 @@ Debian
|
||||||
|
|
||||||
.. contents::
|
.. contents::
|
||||||
|
|
||||||
|
Easy Way: Debian Stretch
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
This procedure will guide you through the installation of ZoneMinder on Debian 9 (Stretch). This section has been tested with ZoneMinder 1.32.3 on Debian 9.8.
|
||||||
|
|
||||||
|
**Step 1:** Make sure your system is up to date
|
||||||
|
|
||||||
|
Open a console and use ``su`` command to become Root.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
apt update
|
||||||
|
apt upgrade
|
||||||
|
|
||||||
|
|
||||||
|
**Step 2:** Setup Sudo (optional but recommended)
|
||||||
|
|
||||||
|
By default Debian does not come with sudo, so you have to install it and configure it manually. This step is optional but recommended and the following instructions assume that you have setup sudo. If you prefer to setup ZoneMinder as root, do it at your own risk and adapt the following instructions accordingly.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
apt install sudo
|
||||||
|
usermod -a -G sudo <username>
|
||||||
|
exit
|
||||||
|
|
||||||
|
Now your terminal session is back under your normal user. You can check that you are now part of the sudo group with the command ``groups``, "sudo" should appear in the list. If not, run ``newgrp sudo`` and check again with ``groups``.
|
||||||
|
|
||||||
|
|
||||||
|
**Step 3:** Install Apache and MySQL
|
||||||
|
|
||||||
|
These are not dependencies for the ZoneMinder package as they could be installed elsewhere. If they are not installed yet in your system, you have to trigger their installation manually.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo apt install apache2 mysql-server
|
||||||
|
|
||||||
|
**Step 4:** Add ZoneMinder's Package repository to your apt sources
|
||||||
|
|
||||||
|
ZoneMinder's Debian packages are not included in Debian's official package repositories. To be able to install ZoneMinder with APT, you have to edit the list of apt sources and add ZoneMinder's repository.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo nano /etc/apt/sources.list
|
||||||
|
|
||||||
|
Add the following to the bottom of the file
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
# ZoneMinder repository
|
||||||
|
deb https://zmrepo.zoneminder.com/debian/release stretch/
|
||||||
|
|
||||||
|
CTRL+o and <Enter> to save
|
||||||
|
CTRL+x to exit
|
||||||
|
|
||||||
|
Because ZoneMinder's package repository provides a secure connection through HTTPS, apt must be enabled for HTTPS.
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo apt install apt-transport-https
|
||||||
|
|
||||||
|
Finally, download the GPG key for ZoneMinder's repository:
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo wget -O - https://zmrepo.zoneminder.com/debian/archive-keyring.gpg | sudo apt-key add -
|
||||||
|
|
||||||
|
|
||||||
|
**Step 5:** Install ZoneMinder
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install zoneminder
|
||||||
|
|
||||||
|
**Step 6:** Read the Readme
|
||||||
|
|
||||||
|
The rest of the install process is covered in the README.Debian, so feel free to have
|
||||||
|
a read.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
gunzip /usr/share/doc/zoneminder/README.Debian.gz
|
||||||
|
cat /usr/share/doc/zoneminder/README.Debian
|
||||||
|
|
||||||
|
|
||||||
|
**Step 7:** Enable ZoneMinder service
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo systemctl enable zoneminder.service
|
||||||
|
|
||||||
|
**Step 8:** Configure Apache
|
||||||
|
|
||||||
|
The following commands will setup the default /zm virtual directory and configure
|
||||||
|
required apache modules.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo a2enconf zoneminder
|
||||||
|
sudo a2enmod rewrite
|
||||||
|
sudo a2enmod cgi # this is done automatically when installing the package. Redo this command manually only for troubleshooting.
|
||||||
|
|
||||||
|
|
||||||
|
**Step 9:** Edit Timezone in PHP
|
||||||
|
|
||||||
|
Automated way:
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo sed -i "s/;date.timezone =/date.timezone = $(sed 's/\//\\\//' /etc/timezone)/g" /etc/php/7.0/apache2/php.ini
|
||||||
|
|
||||||
|
Manual way
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo nano /etc/php/7.0/apache2/php.ini
|
||||||
|
|
||||||
|
Search for [Date] (Ctrl + w then type Date and press Enter) and change
|
||||||
|
date.timezone for your time zone. Don't forget to remove the ; from in front
|
||||||
|
of date.timezone.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
[Date]
|
||||||
|
; Defines the default timezone used by the date functions
|
||||||
|
; http://php.net/date.timezone
|
||||||
|
date.timezone = America/New_York
|
||||||
|
|
||||||
|
CTRL+o then [Enter] to save
|
||||||
|
|
||||||
|
CTRL+x to exit
|
||||||
|
|
||||||
|
|
||||||
|
**Step 10:** Start ZoneMinder
|
||||||
|
|
||||||
|
Reload Apache to enable your changes and then start ZoneMinder.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
sudo systemctl reload apache2
|
||||||
|
sudo systemctl start zoneminder
|
||||||
|
|
||||||
|
You are now ready to go with ZoneMinder. Open a browser and type either ``localhost/zm`` one the local machine or ``{IP-OF-ZM-SERVER}/zm`` if you connect from a remote computer.
|
||||||
|
|
||||||
|
|
||||||
Easy Way: Debian Jessie
|
Easy Way: Debian Jessie
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
@ -81,7 +222,7 @@ should you choose to change default database user and password.
|
||||||
cat /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf
|
cat /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf
|
||||||
echo 'grant lock tables,alter,create,select,insert,update,delete,index on zm.* to 'zmuser'@localhost identified by "zmpass";' | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
echo 'grant lock tables,alter,create,select,insert,update,delete,index on zm.* to 'zmuser'@localhost identified by "zmpass";' | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql
|
||||||
|
|
||||||
** Step 8:** zm.conf Permissions
|
**Step 8:** zm.conf Permissions
|
||||||
|
|
||||||
Adjust permissions to the zm.conf file to allow web account to access it.
|
Adjust permissions to the zm.conf file to allow web account to access it.
|
||||||
|
|
||||||
|
@ -129,6 +270,7 @@ CTRL+x to exit
|
||||||
|
|
||||||
|
|
||||||
**Step 12:** Please check the configuration
|
**Step 12:** Please check the configuration
|
||||||
|
|
||||||
Zoneminder 1.32.x
|
Zoneminder 1.32.x
|
||||||
1. Check path of ZM_PATH in '/etc/zm/conf.d/zmcustom.conf' is ZM_PATH_ZMS=/zm/cgi-bin/nph-zms
|
1. Check path of ZM_PATH in '/etc/zm/conf.d/zmcustom.conf' is ZM_PATH_ZMS=/zm/cgi-bin/nph-zms
|
||||||
::
|
::
|
||||||
|
|
|
@ -158,7 +158,7 @@ The list of available Mock config files are available here:
|
||||||
ls /etc/mock/*rpmfusion_free.cfg
|
ls /etc/mock/*rpmfusion_free.cfg
|
||||||
|
|
||||||
|
|
||||||
You choose the config file based on the desired distro (e.g. el6, el7, f20, f21) and basearch (e.g. x86, x86_64, arhmhfp). Notice that, when specifying the Mock config as a commandline parameter, you should leave off the ".cfg" filename extension.
|
You choose the config file based on the desired distro (e.g. el7, f29, f30) and basearch (e.g. x86, x86_64, arhmhfp). Notice that, when specifying the Mock config as a commandline parameter, you should leave off the ".cfg" filename extension.
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
************
|
************
|
||||||
|
@ -188,8 +188,8 @@ Now clone the ZoneMinder git repository from your home folder:
|
||||||
::
|
::
|
||||||
|
|
||||||
cd
|
cd
|
||||||
git clone https://github.com/ZoneMinder/ZoneMinder
|
git clone https://github.com/ZoneMinder/zoneminder
|
||||||
cd ZoneMinder
|
cd zoneminder
|
||||||
|
|
||||||
This will create a sub-folder called ZoneMinder, which will contain the latest development source code.
|
This will create a sub-folder called ZoneMinder, which will contain the latest development source code.
|
||||||
|
|
||||||
|
@ -197,27 +197,27 @@ If you have previsouly cloned the ZoneMinder git repo and wish to update it to t
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
cd ~/ZoneMinder
|
cd ~/zoneminder
|
||||||
git pull origin master
|
git pull origin master
|
||||||
|
|
||||||
Get the crud submodule tarball:
|
Get the crud submodule tarball:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
spectool -f -g -R -s 1 ~/ZoneMinder/distros/redhat/zoneminder.spec
|
spectool -f -g -R -s 1 ~/zoneminder/distros/redhat/zoneminder.spec
|
||||||
|
|
||||||
At this point, you can make changes to the source code. Depending on what you want to do with those changes, you generally want to create a new branch first:
|
At this point, you can make changes to the source code. Depending on what you want to do with those changes, you generally want to create a new branch first:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
cd ~/ZoneMinder
|
cd ~/zoneminder
|
||||||
git checkout -b mynewbranch
|
git checkout -b mynewbranch
|
||||||
|
|
||||||
Again, depending on what you want to do with those changes, you may want to commit your changes:
|
Again, depending on what you want to do with those changes, you may want to commit your changes:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
cd ~/ZoneMinder
|
cd ~/zoneminder
|
||||||
git add .
|
git add .
|
||||||
git commit
|
git commit
|
||||||
|
|
||||||
|
@ -225,28 +225,28 @@ Once you have made your changes, it is time to turn your work into a new tarball
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
less ~/ZoneMinder/distros/redhat/zoneminder.spec
|
less ~/zoneminder/distros/redhat/zoneminder.spec
|
||||||
|
|
||||||
Scroll down until you see the Version field. Note the value, which will be in the format x.xx.x. Now create the tarball with the following command:
|
Scroll down until you see the Version field. Note the value, which will be in the format x.xx.x. Now create the tarball with the following command:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
cd ~/ZoneMinder
|
cd ~/zoneminder
|
||||||
git archive --prefix=ZoneMinder-1.31.1/ -o ~/rpmbuild/SOURCES/zoneminder-1.31.1.tar.gz HEAD
|
git archive --prefix=zoneminder-1.33.4/ -o ~/rpmbuild/SOURCES/zoneminder-1.33.4.tar.gz HEAD
|
||||||
|
|
||||||
Replace "1.31.1" with the Version shown in the rpm specfile.
|
Replace "1.33.4" with the Version shown in the rpm specfile.
|
||||||
|
|
||||||
From the root of the local ZoneMinder git repo, execute the following:
|
From the root of the local ZoneMinder git repo, execute the following:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
cd ~/ZoneMinder
|
cd ~/zoneminder
|
||||||
rpmbuild -bs --nodeps distros/redhat/zoneminder.spec
|
rpmbuild -bs --nodeps distros/redhat/zoneminder.spec
|
||||||
|
|
||||||
This step will create a source rpm and it will tell you where it was saved. For example:
|
This step will create a source rpm and it will tell you where it was saved. For example:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
Wrote: /home/abauer/rpmbuild/SRPMS/zoneminder-1.31.1-1.fc26.src.rpm
|
Wrote: /home/abauer/rpmbuild/SRPMS/zoneminder-1.33.4-1.fc26.src.rpm
|
||||||
|
|
||||||
Now follow the previous instructions `Build from SRPM`_ which describe how to build that source rpm into an rpm.
|
Now follow the previous instructions `Build from SRPM`_ which describe how to build that source rpm into an rpm.
|
||||||
|
|
|
@ -30,6 +30,7 @@ use warnings;
|
||||||
|
|
||||||
require Exporter;
|
require Exporter;
|
||||||
require ZoneMinder::Base;
|
require ZoneMinder::Base;
|
||||||
|
|
||||||
use ZoneMinder::ConfigData qw(:all);
|
use ZoneMinder::ConfigData qw(:all);
|
||||||
|
|
||||||
our @ISA = qw(Exporter ZoneMinder::Base);
|
our @ISA = qw(Exporter ZoneMinder::Base);
|
||||||
|
@ -57,20 +58,22 @@ our %EXPORT_TAGS = (
|
||||||
push( @{$EXPORT_TAGS{config}}, @EXPORT_CONFIG );
|
push( @{$EXPORT_TAGS{config}}, @EXPORT_CONFIG );
|
||||||
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;
|
||||||
|
|
||||||
use constant ZM_PID => "@ZM_PID@"; # Path to the ZoneMinder run pid file
|
use constant ZM_PID => '@ZM_PID@'; # Path to the ZoneMinder run pid file
|
||||||
use constant ZM_CONFIG => "@ZM_CONFIG@"; # Path to the ZoneMinder config file
|
use constant ZM_CONFIG => '@ZM_CONFIG@'; # Path to the ZoneMinder config file
|
||||||
use constant ZM_CONFIG_SUBDIR => "@ZM_CONFIG_SUBDIR@"; # Path to the ZoneMinder config subfolder
|
use constant ZM_CONFIG_SUBDIR => '@ZM_CONFIG_SUBDIR@'; # Path to the ZoneMinder config subfolder
|
||||||
|
|
||||||
use Carp;
|
use Carp;
|
||||||
|
require ZoneMinder::Database;
|
||||||
|
|
||||||
# Load the config from the database into the symbol table
|
# Load the config from the database into the symbol table
|
||||||
BEGIN {
|
BEGIN {
|
||||||
|
require ZoneMinder::Database;
|
||||||
|
|
||||||
# Process name, value pairs from the main config file first
|
# Process name, value pairs from the main config file first
|
||||||
my $config_file = ZM_CONFIG;
|
my $config_file = ZM_CONFIG;
|
||||||
|
@ -78,52 +81,25 @@ BEGIN {
|
||||||
|
|
||||||
# Search for user created config files. If one or more are found then
|
# Search for user created config files. If one or more are found then
|
||||||
# update the Config hash with those values
|
# update the Config hash with those values
|
||||||
if ( -d ZM_CONFIG_SUBDIR ) {
|
if ( ZM_CONFIG_SUBDIR and -d ZM_CONFIG_SUBDIR ) {
|
||||||
if ( -R ZM_CONFIG_SUBDIR ) {
|
if ( -R ZM_CONFIG_SUBDIR ) {
|
||||||
foreach my $filename ( glob ZM_CONFIG_SUBDIR."/*.conf" ) {
|
foreach my $filename ( glob ZM_CONFIG_SUBDIR.'/*.conf' ) {
|
||||||
process_configfile($filename);
|
process_configfile($filename);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
print( STDERR "WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on ".ZM_CONFIG_SUBDIR.".\n" );
|
print( STDERR 'WARNING: ZoneMinder configuration subfolder found but is not readable. Check folder permissions on '.ZM_CONFIG_SUBDIR.".\n" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use DBI;
|
my $dbh = ZoneMinder::Database::zmDbConnect();
|
||||||
my $socket;
|
|
||||||
my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ );
|
|
||||||
|
|
||||||
if ( defined($portOrSocket) ) {
|
|
||||||
if ( $portOrSocket =~ /^\// ) {
|
|
||||||
$socket = ';mysql_socket='.$portOrSocket;
|
|
||||||
} else {
|
|
||||||
$socket = ';host='.$host.';port='.$portOrSocket;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$socket = ';host='.$Config{ZM_DB_HOST};
|
|
||||||
}
|
|
||||||
my $sslOptions = '';
|
|
||||||
if ( $Config{ZM_DB_SSL_CA_CERT} ) {
|
|
||||||
$sslOptions = ';'.join(';',
|
|
||||||
"mysql_ssl=1",
|
|
||||||
"mysql_ssl_ca_file=".$Config{ZM_DB_SSL_CA_CERT},
|
|
||||||
"mysql_ssl_client_key=".$Config{ZM_DB_SSL_CLIENT_KEY},
|
|
||||||
"mysql_ssl_client_cert=".$Config{ZM_DB_SSL_CLIENT_CERT}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
|
|
||||||
.$socket.$sslOptions
|
|
||||||
, $Config{ZM_DB_USER}
|
|
||||||
, $Config{ZM_DB_PASS}
|
|
||||||
) or croak( "Can't connect to db" );
|
|
||||||
my $sql = 'SELECT Name,Value FROM Config';
|
my $sql = 'SELECT Name,Value FROM Config';
|
||||||
my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() );
|
my $sth = $dbh->prepare_cached($sql) or croak("Can't prepare '$sql': ".$dbh->errstr());
|
||||||
my $res = $sth->execute() or croak( "Can't execute: ".$sth->errstr() );
|
my $res = $sth->execute() or croak("Can't execute: ".$sth->errstr());
|
||||||
while( my $config = $sth->fetchrow_hashref() ) {
|
while( my $config = $sth->fetchrow_hashref() ) {
|
||||||
$Config{$config->{Name}} = $config->{Value};
|
$Config{$config->{Name}} = $config->{Value};
|
||||||
}
|
}
|
||||||
$sth->finish();
|
$sth->finish();
|
||||||
#$dbh->disconnect();
|
|
||||||
#
|
|
||||||
if ( ! $Config{ZM_SERVER_ID} ) {
|
if ( ! $Config{ZM_SERVER_ID} ) {
|
||||||
$Config{ZM_SERVER_ID} = undef;
|
$Config{ZM_SERVER_ID} = undef;
|
||||||
$sth = $dbh->prepare_cached( 'SELECT * FROM Servers WHERE Name=?' );
|
$sth = $dbh->prepare_cached( 'SELECT * FROM Servers WHERE Name=?' );
|
||||||
|
@ -143,25 +119,30 @@ BEGIN {
|
||||||
sub process_configfile {
|
sub process_configfile {
|
||||||
my $config_file = shift;
|
my $config_file = shift;
|
||||||
|
|
||||||
if ( -R $config_file ) {
|
if ( ! -R $config_file ) {
|
||||||
open( my $CONFIG, '<', $config_file )
|
print(STDERR "WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $config_file\n");
|
||||||
or croak( "Can't open config file '$config_file': $!" );
|
return;
|
||||||
foreach my $str ( <$CONFIG> ) {
|
|
||||||
next if ( $str =~ /^\s*$/ );
|
|
||||||
next if ( $str =~ /^\s*#/ );
|
|
||||||
my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*[\'"]*(.*?)[\'"]*\s*$/;
|
|
||||||
if ( ! $name ) {
|
|
||||||
print( STDERR "Warning, bad line in $config_file: $str\n" );
|
|
||||||
next;
|
|
||||||
} # end if
|
|
||||||
$name =~ tr/a-z/A-Z/;
|
|
||||||
$Config{$name} = $value;
|
|
||||||
}
|
|
||||||
close( $CONFIG );
|
|
||||||
} else {
|
|
||||||
print( STDERR "WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $config_file\n" );
|
|
||||||
}
|
}
|
||||||
}
|
open( my $CONFIG, '<', $config_file )
|
||||||
|
or croak("Can't open config file '$config_file': $!");
|
||||||
|
foreach my $str ( <$CONFIG> ) {
|
||||||
|
next if ( $str =~ /^\s*$/ );
|
||||||
|
next if ( $str =~ /^\s*#/ );
|
||||||
|
my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*[\'"]*(.*?)[\'"]*\s*$/;
|
||||||
|
if ( !$name ) {
|
||||||
|
print(STDERR "Warning, bad line in $config_file: $str\n");
|
||||||
|
next;
|
||||||
|
} # end if
|
||||||
|
$name =~ tr/a-z/A-Z/;
|
||||||
|
#if ( !$ZoneMinder::ConfigData::options_hash{$name} ) {
|
||||||
|
#print(STDERR "Warning, unknown config option name $name in $config_file\n");
|
||||||
|
#} else {
|
||||||
|
#print(STDERR "Warning, known config option name $name in $config_file\n");
|
||||||
|
#}
|
||||||
|
$Config{$name} = $value;
|
||||||
|
} # end foreach config line
|
||||||
|
close($CONFIG);
|
||||||
|
} # end sub process_configfile
|
||||||
|
|
||||||
} # end BEGIN
|
} # end BEGIN
|
||||||
|
|
||||||
|
|
|
@ -462,6 +462,18 @@ our @options = (
|
||||||
type => $types{string},
|
type => $types{string},
|
||||||
category => 'system',
|
category => 'system',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name => 'ZM_SYSTEM_SHUTDOWN',
|
||||||
|
default => 'true',
|
||||||
|
description => 'Allow Admin users to power off or restart the system from the ZoneMinder UI.',
|
||||||
|
help => 'The system will need to have sudo installed and the following added to /etc/sudoers~~
|
||||||
|
~~
|
||||||
|
@ZM_WEB_USER@ ALL=NOPASSWD: /sbin/shutdown~~
|
||||||
|
~~
|
||||||
|
to perform the shutdown or reboot',
|
||||||
|
type => $types{boolean},
|
||||||
|
category => 'system',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name => 'ZM_USE_DEEP_STORAGE',
|
name => 'ZM_USE_DEEP_STORAGE',
|
||||||
default => 'yes',
|
default => 'yes',
|
||||||
|
@ -3952,7 +3964,7 @@ our %options_hash = map { ( $_->{name}, $_ ) } @options;
|
||||||
# This function should never need to be called explicitly, except if
|
# This function should never need to be called explicitly, except if
|
||||||
# this module is 'require'd rather than 'use'd. See zmconfgen.pl.
|
# this module is 'require'd rather than 'use'd. See zmconfgen.pl.
|
||||||
sub initialiseConfig {
|
sub initialiseConfig {
|
||||||
return if ( $configInitialised );
|
return if $configInitialised;
|
||||||
|
|
||||||
# Do some initial data munging to finish the data structures
|
# Do some initial data munging to finish the data structures
|
||||||
# Create option ids
|
# Create option ids
|
||||||
|
|
|
@ -67,7 +67,7 @@ sub AUTOLOAD {
|
||||||
if ( exists($self->{$name}) ) {
|
if ( exists($self->{$name}) ) {
|
||||||
return $self->{$name};
|
return $self->{$name};
|
||||||
}
|
}
|
||||||
Error("Can't access $name member of object of class $class");
|
Error("Can't access $name $AUTOLOAD member of object of class $class");
|
||||||
}
|
}
|
||||||
|
|
||||||
sub getKey {
|
sub getKey {
|
||||||
|
|
|
@ -39,6 +39,8 @@ sub AUTOLOAD
|
||||||
my $class = ref($self) || croak( "$self not object" );
|
my $class = ref($self) || croak( "$self not object" );
|
||||||
my $name = $AUTOLOAD;
|
my $name = $AUTOLOAD;
|
||||||
$name =~ s/.*://;
|
$name =~ s/.*://;
|
||||||
|
## This seems odd... if the method existed would we even be here?
|
||||||
|
## https://perldoc.perl.org/perlsub.html#Autoloading
|
||||||
if ( exists($self->{$name}) )
|
if ( exists($self->{$name}) )
|
||||||
{
|
{
|
||||||
return( $self->{$name} );
|
return( $self->{$name} );
|
||||||
|
@ -46,9 +48,17 @@ sub AUTOLOAD
|
||||||
Fatal( "Can't access $name member of object of class $class" );
|
Fatal( "Can't access $name member of object of class $class" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# FIXME: Do we really have to open a new connection every time?
|
||||||
|
|
||||||
|
#Digest usernbme="bdmin", reblm="Login to 4K05DB3PAJE98BE", nonae="1720242756",
|
||||||
|
#uri="/agi-bin/ptz.agi?bation=getStbtus&ahbnnel=1", response="10dd925b26ebd559353734635b859b8b",
|
||||||
|
#opbque="1a99677524b4ae63bbe3a132b2e9b38e3b163ebd", qop=buth, na=00000001, anonae="ab1bb5d43aa5d542"
|
||||||
|
|
||||||
sub open
|
sub open
|
||||||
{
|
{
|
||||||
|
#Debug("&open invoked by: " . (caller(1))[3]);
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
my $cgi = shift || '/cgi-bin/configManager.cgi?action=getConfig&name=Ptz';
|
||||||
$self->loadMonitor();
|
$self->loadMonitor();
|
||||||
|
|
||||||
# The Dahua camera firmware API supports the concept of having multiple
|
# The Dahua camera firmware API supports the concept of having multiple
|
||||||
|
@ -73,60 +83,55 @@ sub open
|
||||||
}
|
}
|
||||||
|
|
||||||
use LWP::UserAgent;
|
use LWP::UserAgent;
|
||||||
$self->{ua} = LWP::UserAgent->new;
|
$self->{ua} = LWP::UserAgent->new(keep_alive => 1);
|
||||||
$self->{ua}->agent("ZoneMinder Control Agent/".$ZoneMinder::Base::ZM_VERSION);
|
$self->{ua}->agent("ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION);
|
||||||
$self->{state} = 'closed';
|
$self->{state} = 'closed';
|
||||||
# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string)
|
# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string)
|
||||||
Debug("sendCmd credentials control address:'".$ADDRESS
|
|
||||||
."' realm:'" . $REALM
|
|
||||||
. "' username:'" . $USERNAME
|
|
||||||
. "' password:'".$PASSWORD
|
|
||||||
."'"
|
|
||||||
);
|
|
||||||
$self->{ua}->credentials($ADDRESS, $REALM, $USERNAME, $PASSWORD);
|
$self->{ua}->credentials($ADDRESS, $REALM, $USERNAME, $PASSWORD);
|
||||||
|
|
||||||
# Detect REALM
|
# Detect REALM
|
||||||
my $get_config_url = $PROTOCOL . $ADDRESS . "/cgi-bin/configManager.cgi?action=getConfig&name=Ptz";
|
my $url = $PROTOCOL . $ADDRESS . $cgi;
|
||||||
my $req = HTTP::Request->new(GET=>$get_config_url);
|
my $req = HTTP::Request->new(GET=>$url);
|
||||||
my $res = $self->{ua}->request($req);
|
my $res = $self->{ua}->request($req);
|
||||||
|
|
||||||
if ($res->is_success) {
|
if ($res->is_success) {
|
||||||
$self->{state} = 'open';
|
$self->{state} = 'open';
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $res->status_line() eq '401 Unauthorized' ) {
|
if ( $res->status_line() eq '401 Unauthorized' ) {
|
||||||
my $headers = $res->headers();
|
my $headers = $res->headers();
|
||||||
foreach my $k (keys %$headers) {
|
|
||||||
Debug("Initial Header $k => $$headers{$k}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($$headers{'www-authenticate'}) {
|
if ($$headers{'www-authenticate'}) {
|
||||||
my ($auth, $tokens) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
|
my ($auth, $tokens) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
|
||||||
|
Debug("Tokens: " . $tokens);
|
||||||
|
## FIXME: This is necessary because the Dahua spec does not match reality
|
||||||
if ($tokens =~ /\w+="([^"]+)"/i) {
|
if ($tokens =~ /\w+="([^"]+)"/i) {
|
||||||
if ($REALM ne $1) {
|
if ($REALM ne $1) {
|
||||||
$REALM = $1;
|
$REALM = $1;
|
||||||
Debug("Changing REALM to '" . $REALM . "'");
|
Debug("Changing REALM to '" . $REALM . "'");
|
||||||
$self->{ua}->credentials($ADDRESS, $REALM, $USERNAME, $PASSWORD);
|
$self->{ua}->credentials($ADDRESS, $REALM, $USERNAME, $PASSWORD);
|
||||||
my $req = HTTP::Request->new(GET=>$get_config_url);
|
my $req = HTTP::Request->new(GET=>$url);
|
||||||
$res = $self->{ua}->request($req);
|
$res = $self->{ua}->request($req);
|
||||||
|
|
||||||
if ($res->is_success()) {
|
if ($res->is_success()) {
|
||||||
$self->{state} = 'open';
|
$self->{state} = 'open';
|
||||||
return;
|
Debug('Authentication succeeded...');
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
Debug('Authentication still failed after updating REALM' . $res->status_line);
|
Debug('Authentication still failed after updating REALM' . $res->status_line);
|
||||||
$headers = $res->headers();
|
$headers = $res->headers();
|
||||||
foreach my $k ( keys %$headers ) {
|
foreach my $k ( keys %$headers ) {
|
||||||
Debug("Initial Header $k => $$headers{$k}");
|
Debug("Initial Header $k => $$headers{$k}");
|
||||||
} # end foreach
|
} # end foreach
|
||||||
} else {
|
} else { ## NOTE: Each of these else conditions is fatal as the command will not be
|
||||||
Error('Authentication failed, not a REALM problem');
|
## executed. No use going further.
|
||||||
|
Fatal('Authentication failed: Check username and password.');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Error('Failed to match realm in tokens');
|
Fatal('Authentication failed: Incorrect realm.');
|
||||||
} # end if
|
} # end if
|
||||||
} else {
|
} else {
|
||||||
Error('No WWW-Authenticate Header');
|
Fatal('Authentication failed: No www-authenticate header returned.');
|
||||||
} # end if headers
|
} # end if headers
|
||||||
} # end if $res->status_line() eq '401 Unauthorized'
|
} # end if $res->status_line() eq '401 Unauthorized'
|
||||||
}
|
}
|
||||||
|
@ -146,45 +151,40 @@ sub printMsg
|
||||||
Debug( $msg."[".$msg_len."]" );
|
Debug( $msg."[".$msg_len."]" );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub sendGetRequest {
|
sub _sendGetRequest {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $url_path = shift;
|
my $url_path = shift;
|
||||||
|
|
||||||
my $result = undef;
|
# Attempt to reuse the connection
|
||||||
|
|
||||||
|
# FIXME: I think we need some sort of keepalive/heartbeat sent to the camera
|
||||||
|
# in order to keep the session alive. As it is, it appears that the
|
||||||
|
# ua's authentication times out or some such.
|
||||||
|
#
|
||||||
|
# This might be of some use:
|
||||||
|
# {"method":"global.keepAlive","params":{"timeout":300,"active":false},"id":1518,"session":"dae233a51c0693519395209b271411b6"}[!http]
|
||||||
|
# The web browser interface POSTs commands as JSON using js
|
||||||
|
|
||||||
my $url = $PROTOCOL . $ADDRESS . $url_path;
|
my $url = $PROTOCOL . $ADDRESS . $url_path;
|
||||||
my $req = HTTP::Request->new(GET=>$url);
|
my $req = HTTP::Request->new(GET => $url);
|
||||||
|
|
||||||
my $res = $self->{ua}->request($req);
|
my $res = $self->{ua}->request($req);
|
||||||
|
|
||||||
if ($res->is_success) {
|
if ($res->is_success) {
|
||||||
$result = !undef;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
if ($res->status_line() eq '401 Unauthorized') {
|
return($self->open($url_path)); # if we have to, open a new connection
|
||||||
Debug("Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD);
|
|
||||||
Debug("Content was " . $res->content() );
|
|
||||||
my $res = $self->{ua}->request($req);
|
|
||||||
if ($res->is_success) {
|
|
||||||
$result = !undef;
|
|
||||||
} else {
|
|
||||||
Error("Content was " . $res->content() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( ! $result ) {
|
|
||||||
Error("Error check failed: '".$res->status_line());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return($result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub sendPtzCommand
|
sub _sendPtzCommand
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $action = shift;
|
my $action = shift;
|
||||||
my $command_code = shift;
|
my $command_code = shift;
|
||||||
my $arg1 = shift;
|
my $arg1 = shift || 0;
|
||||||
my $arg2 = shift;
|
my $arg2 = shift || 0;
|
||||||
my $arg3 = shift;
|
my $arg3 = shift || 0;
|
||||||
|
my $arg4 = shift || 0;
|
||||||
|
|
||||||
my $channel = $self->{dahua_channel_number};
|
my $channel = $self->{dahua_channel_number};
|
||||||
|
|
||||||
|
@ -194,10 +194,12 @@ sub sendPtzCommand
|
||||||
$url_path .= "code=" . $command_code . "&";
|
$url_path .= "code=" . $command_code . "&";
|
||||||
$url_path .= "arg1=" . $arg1 . "&";
|
$url_path .= "arg1=" . $arg1 . "&";
|
||||||
$url_path .= "arg2=" . $arg2 . "&";
|
$url_path .= "arg2=" . $arg2 . "&";
|
||||||
$url_path .= "arg3=" . $arg3;
|
$url_path .= "arg3=" . $arg3 . "&";
|
||||||
$self->sendGetRequest($url_path);
|
$url_path .= "arg4=" . $arg4;
|
||||||
|
return $self->_sendGetRequest($url_path);
|
||||||
}
|
}
|
||||||
sub sendMomentaryPtzCommand
|
|
||||||
|
sub _sendMomentaryPtzCommand
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $command_code = shift;
|
my $command_code = shift;
|
||||||
|
@ -206,92 +208,195 @@ sub sendMomentaryPtzCommand
|
||||||
my $arg3 = shift;
|
my $arg3 = shift;
|
||||||
my $duration_ms = shift;
|
my $duration_ms = shift;
|
||||||
|
|
||||||
$self->sendPtzCommand("start", $command_code, $arg1, $arg2, $arg3);
|
$self->_sendPtzCommand("start", $command_code, $arg1, $arg2, $arg3);
|
||||||
my $duration_ns = $duration_ms * 1000;
|
my $duration_ns = $duration_ms * 1000;
|
||||||
usleep($duration_ns);
|
usleep($duration_ns);
|
||||||
$self->sendPtzCommand("stop", $command_code, $arg1, $arg2, $arg3);
|
$self->_sendPtzCommand("stop", $command_code, $arg1, $arg2, $arg3);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _sendAbsolutePositionCommand
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $arg1 = shift;
|
||||||
|
my $arg2 = shift;
|
||||||
|
my $arg3 = shift;
|
||||||
|
my $arg4 = shift;
|
||||||
|
|
||||||
|
$self->_sendPtzCommand("start", "PositionABS", $arg1, $arg2, $arg3, $arg4);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConLeft
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug("Move Up Left");
|
||||||
|
$self->_sendMomentaryPtzCommand("Left", 0, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConRight
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Right" );
|
||||||
|
$self->_sendMomentaryPtzCommand("Right", 0, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConUp
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Up" );
|
||||||
|
$self->_sendMomentaryPtzCommand("Up", 0, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConDown
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Down" );
|
||||||
|
$self->_sendMomentaryPtzCommand("Down", 0, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConUpRight
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Diagonally Up Right" );
|
||||||
|
$self->_sendMomentaryPtzCommand("RightUp", 1, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConDownRight
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Diagonally Down Right" );
|
||||||
|
$self->_sendMomentaryPtzCommand("RightDown", 1, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConUpLeft
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Diagonally Up Left" );
|
||||||
|
$self->_sendMomentaryPtzCommand("LeftUp", 1, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveConDownLeft
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Diagonally Up Right" );
|
||||||
|
$self->_sendMomentaryPtzCommand("LeftDown", 1, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub zoomConTele
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Zoom Tele" );
|
||||||
|
$self->_sendMomentaryPtzCommand("ZoomTele", 0, 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub zoomConWide
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Zoom Wide" );
|
||||||
|
$self->_sendMomentaryPtzCommand("ZoomWide", 0, 1, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelUpLeft
|
sub moveRelUpLeft
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug("Move Up Left");
|
Debug("Move Up Left");
|
||||||
$self->sendMomentaryPtzCommand("LeftUp", 4, 4, 0, 500);
|
$self->_sendMomentaryPtzCommand("LeftUp", 4, 4, 0, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelUp
|
sub moveRelUp
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug("Move Up");
|
Debug("Move Up");
|
||||||
$self->sendMomentaryPtzCommand("Up", 0, 4, 0, 500);
|
$self->_sendMomentaryPtzCommand("Up", 0, 4, 0, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelUpRight
|
sub moveRelUpRight
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug("Move Up Right");
|
Debug("Move Up Right");
|
||||||
$self->sendMomentaryPtzCommand("RightUp", 0, 4, 0, 500);
|
$self->_sendMomentaryPtzCommand("RightUp", 0, 4, 0, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelLeft
|
sub moveRelLeft
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug("Move Left");
|
Debug("Move Left");
|
||||||
$self->sendMomentaryPtzCommand("Left", 0, 4, 0, 500);
|
$self->_sendMomentaryPtzCommand("Left", 0, 4, 0, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelRight
|
sub moveRelRight
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug("Move Right");
|
Debug("Move Right");
|
||||||
$self->sendMomentaryPtzCommand("Right", 0, 4, 0, 500);
|
$self->_sendMomentaryPtzCommand("Right", 0, 4, 0, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelDownLeft
|
sub moveRelDownLeft
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug("Move Down Left");
|
Debug("Move Down Left");
|
||||||
$self->sendMomentaryPtzCommand("LeftDown", 4, 4, 0, 500);
|
$self->_sendMomentaryPtzCommand("LeftDown", 4, 4, 0, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelDown
|
sub moveRelDown
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug("Move Down");
|
Debug("Move Down");
|
||||||
$self->sendMomentaryPtzCommand("Down", 0, 4, 0, 500);
|
$self->_sendMomentaryPtzCommand("Down", 0, 4, 0, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub moveRelDownRight
|
sub moveRelDownRight
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug("Move Down Right");
|
Debug("Move Down Right");
|
||||||
$self->sendMomentaryPtzCommand("RightDown", 4, 4, 0, 500);
|
$self->_sendMomentaryPtzCommand("RightDown", 4, 4, 0, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub zoomRelTele
|
sub zoomRelTele
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug("Zoom Relative Tele");
|
Debug("Zoom Relative Tele");
|
||||||
$self->sendMomentaryPtzCommand("ZoomTele", 0, 0, 0, 500);
|
$self->_sendMomentaryPtzCommand("ZoomTele", 0, 0, 0, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub zoomRelWide
|
sub zoomRelWide
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
Debug("Zoom Relative Wide");
|
Debug("Zoom Relative Wide");
|
||||||
$self->sendMomentaryPtzCommand("ZoomWide", 0, 0, 0, 500);
|
$self->_sendMomentaryPtzCommand("ZoomWide", 0, 0, 0, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub focusRelNear
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
my $response = $self->_sendPtzCommand("start", "FocusNear", 0, 1, 0, 0);
|
||||||
|
Debug("focusRelNear response: " . $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub focusRelFar
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
my $response = $self->_sendPtzCommand("start", "FocusFar", 0, 1, 0, 0);
|
||||||
|
Debug("focusRelFar response: " . $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveStop
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Stop" );
|
||||||
|
# The command does not matter here, just the stop...
|
||||||
|
$self->_sendPtzCommand("stop", "Up", 0, 0, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
sub presetClear
|
sub presetClear
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $preset_id = $self->getParam($params, 'preset');
|
my $preset_id = $self->getParam($params, 'preset');
|
||||||
$self->sendPtzCommand("start", "ClearPreset", 0, $preset_id, 0);
|
$self->_sendPtzCommand("start", "ClearPreset", 0, $preset_id, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub presetSet
|
sub presetSet
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
@ -308,8 +413,8 @@ sub presetSet
|
||||||
my $control_preset_row = $sth->fetchrow_hashref();
|
my $control_preset_row = $sth->fetchrow_hashref();
|
||||||
my $new_label_name = $control_preset_row->{'Label'};
|
my $new_label_name = $control_preset_row->{'Label'};
|
||||||
|
|
||||||
$self->sendPtzCommand("start", "SetPreset", 0, $preset_id, 0);
|
$self->_sendPtzCommand("start", "SetPreset", 0, $preset_id, 0);
|
||||||
$self->sendPtzCommand("start", "SetPresetName", $preset_id, $new_label_name, 0);
|
$self->_sendPtzCommand("start", "SetPresetName", $preset_id, $new_label_name, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub presetGoto
|
sub presetGoto
|
||||||
|
@ -318,12 +423,39 @@ sub presetGoto
|
||||||
my $params = shift;
|
my $params = shift;
|
||||||
my $preset_id = $self->getParam($params, 'preset');
|
my $preset_id = $self->getParam($params, 'preset');
|
||||||
|
|
||||||
$self->sendPtzCommand("start", "GotoPreset", 0, $preset_id, 0);
|
$self->_sendPtzCommand("start", "GotoPreset", 0, $preset_id, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub presetHome
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
$self->_sendAbsolutePositionCommand( 0, 0, 0, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub reset
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Camera Reset" );
|
||||||
|
$self->_sendPtzCommand("Reset", 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub reboot
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Camera Reboot" );
|
||||||
|
my $cmd = "/cgi-bin/magicBox.cgi?action=reboot";
|
||||||
|
$self->_sendGetRequest($cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
|
||||||
|
=pod
|
||||||
|
|
||||||
|
=encoding utf8
|
||||||
|
|
||||||
=head1 NAME
|
=head1 NAME
|
||||||
|
|
||||||
ZoneMinder::Control::Dahua - Perl module for Dahua cameras
|
ZoneMinder::Control::Dahua - Perl module for Dahua cameras
|
||||||
|
@ -337,10 +469,6 @@ place this in /usr/share/perl5/ZoneMinder/Control
|
||||||
|
|
||||||
This module is an implementation of the Dahua IP camera HTTP control API.
|
This module is an implementation of the Dahua IP camera HTTP control API.
|
||||||
|
|
||||||
=head2 EXPORT
|
|
||||||
|
|
||||||
None by default.
|
|
||||||
|
|
||||||
=head1 COPYRIGHT AND LICENSE
|
=head1 COPYRIGHT AND LICENSE
|
||||||
|
|
||||||
Copyright (C) 2018 ZoneMinder LLC
|
Copyright (C) 2018 ZoneMinder LLC
|
||||||
|
@ -359,4 +487,138 @@ 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.
|
||||||
|
|
||||||
|
=head1 Private Methods
|
||||||
|
|
||||||
|
Methods intended for use internally but documented here for future developers.
|
||||||
|
|
||||||
|
=head2 _sendAbsolutePositionCommand( $arg1, $arg2, $arg3, $arg4 )
|
||||||
|
|
||||||
|
Where:
|
||||||
|
|
||||||
|
$arg1 = Horizontal angle 0° to 360°
|
||||||
|
$arg2 = Vertical angle 0° to -90°
|
||||||
|
$arg3 = Zoom multiplier
|
||||||
|
$arg4 = Speed 1 to 8
|
||||||
|
|
||||||
|
This is an private method used to send an absolute position command to the
|
||||||
|
camera.
|
||||||
|
|
||||||
|
=head1 Public Methods
|
||||||
|
|
||||||
|
Methods made available to control.pl via ZoneMinder::Control
|
||||||
|
|
||||||
|
=head2 Notes:
|
||||||
|
|
||||||
|
=over 1
|
||||||
|
|
||||||
|
Which methods are invoked depends on which types of movement are selected in
|
||||||
|
the camera control type. For example: if the 'Can Move Continuous' option is
|
||||||
|
checked, then methods including 'Con' in their names are invoked. Likewise if
|
||||||
|
the 'Can Move Relative" option is checked, then methods including 'Rel' in
|
||||||
|
their names are invoked.
|
||||||
|
|
||||||
|
|
||||||
|
At present, these types of movement are prioritized and exclusive. This applies
|
||||||
|
to all types of movement, not just PTZ, but focus, iris, etc. as well. The options
|
||||||
|
are tested in the following order:
|
||||||
|
|
||||||
|
1. Continuous
|
||||||
|
|
||||||
|
2. Relative
|
||||||
|
|
||||||
|
3. Absolute
|
||||||
|
|
||||||
|
These types are exclusive meaning that the first one that matches is the one
|
||||||
|
ZoneMinder will use to control with. It would be nice to allow the user to
|
||||||
|
select the type used given that some cameras support all three types of
|
||||||
|
movement.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head2 new
|
||||||
|
|
||||||
|
This method instantiates a new control object based upon this control module
|
||||||
|
and sets the 'id' attribute to the value passed in.
|
||||||
|
|
||||||
|
=head2 open
|
||||||
|
|
||||||
|
This method opens an HTTP connection to the camera. It handles authentication,
|
||||||
|
etc. Upon success it sets the 'state' attribute to 'open.'
|
||||||
|
|
||||||
|
=head2 close
|
||||||
|
|
||||||
|
This method effectively closes the HTTP connection to the camera. It sets the
|
||||||
|
'state' attribute to 'close.'
|
||||||
|
|
||||||
|
=head2 printMsg
|
||||||
|
|
||||||
|
This method appears to be used for debugging.
|
||||||
|
|
||||||
|
=head2 moveCon<direction>
|
||||||
|
|
||||||
|
This set of methods invoke continuous movement in the direction indicated by
|
||||||
|
the <direction> portion of their name. They accept no arguments and move the
|
||||||
|
camera at a speed of 1 for 0ms. The speed index of 1 is the lowest of the
|
||||||
|
accepted range of 1-8.
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
|
||||||
|
This is not true continuous movmement as currently implemented.
|
||||||
|
|
||||||
|
=head2 focusCon<range>
|
||||||
|
|
||||||
|
This set of methods invoke continuous focus in the range direction indicated
|
||||||
|
by the <range> portion of their name. They accept no arguments.
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
|
||||||
|
This is not true continuous movmement as currently implemented.
|
||||||
|
|
||||||
|
=head2 moveRel<direction>
|
||||||
|
|
||||||
|
This set of methods invoke relatvie movement in the direction indicated by
|
||||||
|
the <direction> portion of their name. They accept no arguments and move the
|
||||||
|
camera at a speed of 4 for 500ms. The speed index of 4 is half-way between
|
||||||
|
the accepted range of 1-8.
|
||||||
|
|
||||||
|
=head2 focusRel<range>
|
||||||
|
|
||||||
|
This set of methods invoke realtive focus in the range direction indicated by
|
||||||
|
the <range> portion of their name. They accept no arguments.
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
|
||||||
|
This only just does work. The Dahua API specifies "multiples" as the input.
|
||||||
|
We pass in a 1 for that as it does not seem to matter what number (0-8) is
|
||||||
|
provided, the camera focus behaves the same.
|
||||||
|
|
||||||
|
=head2 moveStop
|
||||||
|
|
||||||
|
This method attempts to stop the camera. The problem is that if continuous
|
||||||
|
motion is occurring in multiple directions, this will only stop the motion
|
||||||
|
in the 'Up' direction. Dahua does not support an "all-stop" command.
|
||||||
|
|
||||||
|
=head2 presetHome
|
||||||
|
|
||||||
|
This method "homes" the camera to a preset position. It accepts no arguments.
|
||||||
|
When either continuous or relative movement is enabled, pressing the center
|
||||||
|
button on the movement controls invokes this method.
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
|
||||||
|
The Dahua protocol does not appear to support a preset Home feature. We could
|
||||||
|
allow the user to assign a preset slot as the "home" slot. Dahua does appear
|
||||||
|
to support naming presets which may lend itself to this sort of thing. At
|
||||||
|
this point, we'll just send the camera back to center and zoom wide. (0°,0°,0)
|
||||||
|
|
||||||
|
=head2 reset
|
||||||
|
|
||||||
|
This method will reset the PTZ controls to their "default." It is not clear
|
||||||
|
what that is.
|
||||||
|
|
||||||
|
=head2 reboot
|
||||||
|
|
||||||
|
This method performs a reboot of the camera. This will take the camera offline
|
||||||
|
for the time it takes to reboot.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
|
@ -40,7 +40,7 @@ sub open
|
||||||
|
|
||||||
use LWP::UserAgent;
|
use LWP::UserAgent;
|
||||||
$self->{ua} = LWP::UserAgent->new;
|
$self->{ua} = LWP::UserAgent->new;
|
||||||
$self->{ua}->agent( "ZoneMinder Control Agent/".$ZoneMinder::Base::ZM_VERSION );
|
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
|
||||||
$self->{state} = 'closed';
|
$self->{state} = 'closed';
|
||||||
Debug( "sendCmd credentials control address:'".$ADDRESS
|
Debug( "sendCmd credentials control address:'".$ADDRESS
|
||||||
."' realm:'" . $REALM
|
."' realm:'" . $REALM
|
||||||
|
|
|
@ -45,7 +45,7 @@ sub open {
|
||||||
|
|
||||||
use LWP::UserAgent;
|
use LWP::UserAgent;
|
||||||
$self->{ua} = LWP::UserAgent->new;
|
$self->{ua} = LWP::UserAgent->new;
|
||||||
$self->{ua}->agent('ZoneMinder Control Agent/'.$ZoneMinder::Base::ZM_VERSION);
|
$self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
|
||||||
$self->{state} = 'closed';
|
$self->{state} = 'closed';
|
||||||
# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string)
|
# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string)
|
||||||
Debug ( "sendCmd credentials control address:'".$ADDRESS
|
Debug ( "sendCmd credentials control address:'".$ADDRESS
|
||||||
|
@ -120,6 +120,7 @@ sub sendCmd {
|
||||||
|
|
||||||
Debug('sendCmd command: ' . $url);
|
Debug('sendCmd command: ' . $url);
|
||||||
if ( $res->is_success ) {
|
if ( $res->is_success ) {
|
||||||
|
Debug($res->content);
|
||||||
return !undef;
|
return !undef;
|
||||||
}
|
}
|
||||||
Error("Error check failed: '".$res->status_line()."' cmd:'".$cmd."'");
|
Error("Error check failed: '".$res->status_line()."' cmd:'".$cmd."'");
|
||||||
|
@ -155,6 +156,7 @@ sub sendCmdPost {
|
||||||
Debug("sendCmdPost credentials control to: $PROTOCOL$ADDRESS$url realm:'" . $REALM . "' username:'" . $USERNAME . "' password:'".$PASSWORD."'");
|
Debug("sendCmdPost credentials control to: $PROTOCOL$ADDRESS$url realm:'" . $REALM . "' username:'" . $USERNAME . "' password:'".$PASSWORD."'");
|
||||||
|
|
||||||
if ( $res->is_success ) {
|
if ( $res->is_success ) {
|
||||||
|
Debug($res->content);
|
||||||
return !undef;
|
return !undef;
|
||||||
}
|
}
|
||||||
Error("sendCmdPost Error check failed: '".$res->status_line()."' cmd:");
|
Error("sendCmdPost Error check failed: '".$res->status_line()."' cmd:");
|
||||||
|
|
|
@ -65,7 +65,8 @@ our $VERSION = $ZoneMinder::Base::VERSION;
|
||||||
# ==========================================================================
|
# ==========================================================================
|
||||||
|
|
||||||
use ZoneMinder::Logger qw(:all);
|
use ZoneMinder::Logger qw(:all);
|
||||||
use ZoneMinder::Config qw(:all);
|
|
||||||
|
require ZoneMinder::Config;
|
||||||
|
|
||||||
our $dbh = undef;
|
our $dbh = undef;
|
||||||
|
|
||||||
|
@ -87,25 +88,25 @@ sub zmDbConnect {
|
||||||
$socket = ';host='.$host.';port='.$portOrSocket;
|
$socket = ';host='.$host.';port='.$portOrSocket;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$socket = ';host='.$Config{ZM_DB_HOST};
|
$socket = ';host='.$ZoneMinder::Config::Config{ZM_DB_HOST};
|
||||||
}
|
}
|
||||||
|
|
||||||
my $sslOptions = '';
|
my $sslOptions = '';
|
||||||
if ( $Config{ZM_DB_SSL_CA_CERT} ) {
|
if ( $ZoneMinder::Config::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='.$ZoneMinder::Config::Config{ZM_DB_SSL_CA_CERT},
|
||||||
'mysql_ssl_client_key='.$Config{ZM_DB_SSL_CLIENT_KEY},
|
'mysql_ssl_client_key='.$ZoneMinder::Config::Config{ZM_DB_SSL_CLIENT_KEY},
|
||||||
'mysql_ssl_client_cert='.$Config{ZM_DB_SSL_CLIENT_CERT}
|
'mysql_ssl_client_cert='.$ZoneMinder::Config::Config{ZM_DB_SSL_CLIENT_CERT}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
eval {
|
eval {
|
||||||
$dbh = DBI->connect(
|
$dbh = DBI->connect(
|
||||||
'DBI:mysql:database='.$Config{ZM_DB_NAME}
|
'DBI:mysql:database='.$ZoneMinder::Config::Config{ZM_DB_NAME}
|
||||||
.$socket . $sslOptions . ($options?join(';', '', map { $_.'='.$$options{$_} } keys %{$options} ) : '')
|
.$socket . $sslOptions . ($options?join(';', '', map { $_.'='.$$options{$_} } keys %{$options} ) : '')
|
||||||
, $Config{ZM_DB_USER}
|
, $ZoneMinder::Config::Config{ZM_DB_USER}
|
||||||
, $Config{ZM_DB_PASS}
|
, $ZoneMinder::Config::Config{ZM_DB_PASS}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
if ( !$dbh or $@ ) {
|
if ( !$dbh or $@ ) {
|
||||||
|
@ -124,7 +125,7 @@ sub zmDbConnect {
|
||||||
} # end sub zmDbConnect
|
} # end sub zmDbConnect
|
||||||
|
|
||||||
sub zmDbDisconnect {
|
sub zmDbDisconnect {
|
||||||
if ( defined( $dbh ) ) {
|
if ( defined($dbh) ) {
|
||||||
$dbh->disconnect() or Error('Error disconnecting db? ' . $dbh->errstr());
|
$dbh->disconnect() or Error('Error disconnecting db? ' . $dbh->errstr());
|
||||||
$dbh = undef;
|
$dbh = undef;
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,7 +177,7 @@ sub Sql {
|
||||||
$self->{Sql} .= 'to_days( E.StartTime )';
|
$self->{Sql} .= 'to_days( E.StartTime )';
|
||||||
} elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' ) {
|
} elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' ) {
|
||||||
$self->{Sql} .= 'extract( hour_second from E.StartTime )';
|
$self->{Sql} .= 'extract( hour_second from E.StartTime )';
|
||||||
} elsif ( $term->{attr} eq 'Weekday' ) {
|
} elsif ( $term->{attr} eq 'Weekday' or $term->{attr} eq 'StartWeekday' ) {
|
||||||
$self->{Sql} .= 'weekday( E.StartTime )';
|
$self->{Sql} .= 'weekday( E.StartTime )';
|
||||||
|
|
||||||
# EndTIme options
|
# EndTIme options
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
############################################################################
|
############################################################################
|
||||||
#
|
#
|
||||||
# ZoneMinder Logger Module, $Date$, $Revision$
|
# ZoneMinder Logger Module
|
||||||
# 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
|
||||||
|
@ -87,7 +87,7 @@ our $VERSION = $ZoneMinder::Base::VERSION;
|
||||||
#
|
#
|
||||||
############################################################################
|
############################################################################
|
||||||
|
|
||||||
use ZoneMinder::Config qw(:all);
|
require ZoneMinder::Config;
|
||||||
|
|
||||||
use DBI;
|
use DBI;
|
||||||
use Carp;
|
use Carp;
|
||||||
|
@ -156,7 +156,7 @@ sub new {
|
||||||
$this->{autoFlush} = 1;
|
$this->{autoFlush} = 1;
|
||||||
|
|
||||||
( $this->{fileName} = $0 ) =~ s|^.*/||;
|
( $this->{fileName} = $0 ) =~ s|^.*/||;
|
||||||
$this->{logPath} = $Config{ZM_PATH_LOGS};
|
$this->{logPath} = $ZoneMinder::Config::Config{ZM_PATH_LOGS};
|
||||||
$this->{logFile} = $this->{logPath}.'/'.$this->{id}.'.log';
|
$this->{logFile} = $this->{logPath}.'/'.$this->{id}.'.log';
|
||||||
($this->{logFile}) = $this->{logFile} =~ /^([\w\.\/]+)$/;
|
($this->{logFile}) = $this->{logFile} =~ /^([\w\.\/]+)$/;
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ sub new {
|
||||||
sub BEGIN {
|
sub BEGIN {
|
||||||
# Fake the config variables that are used in case they are not defined yet
|
# Fake the config variables that are used in case they are not defined yet
|
||||||
# Only really necessary to support upgrade from previous version
|
# Only really necessary to support upgrade from previous version
|
||||||
if ( !eval('defined($Config{ZM_LOG_DEBUG})') ) {
|
if ( !eval('defined($ZoneMinder::Config::Config{ZM_LOG_DEBUG})') ) {
|
||||||
no strict 'subs';
|
no strict 'subs';
|
||||||
no strict 'refs';
|
no strict 'refs';
|
||||||
my %dbgConfig = (
|
my %dbgConfig = (
|
||||||
|
@ -221,17 +221,17 @@ sub initialise( @ ) {
|
||||||
if ( defined($options{databaseLevel}) ) {
|
if ( defined($options{databaseLevel}) ) {
|
||||||
$tempDatabaseLevel = $options{databaseLevel};
|
$tempDatabaseLevel = $options{databaseLevel};
|
||||||
} else {
|
} else {
|
||||||
$tempDatabaseLevel = $Config{ZM_LOG_LEVEL_DATABASE};
|
$tempDatabaseLevel = $ZoneMinder::Config::Config{ZM_LOG_LEVEL_DATABASE};
|
||||||
}
|
}
|
||||||
if ( defined($options{fileLevel}) ) {
|
if ( defined($options{fileLevel}) ) {
|
||||||
$tempFileLevel = $options{fileLevel};
|
$tempFileLevel = $options{fileLevel};
|
||||||
} else {
|
} else {
|
||||||
$tempFileLevel = $Config{ZM_LOG_LEVEL_FILE};
|
$tempFileLevel = $ZoneMinder::Config::Config{ZM_LOG_LEVEL_FILE};
|
||||||
}
|
}
|
||||||
if ( defined($options{syslogLevel}) ) {
|
if ( defined($options{syslogLevel}) ) {
|
||||||
$tempSyslogLevel = $options{syslogLevel};
|
$tempSyslogLevel = $options{syslogLevel};
|
||||||
} else {
|
} else {
|
||||||
$tempSyslogLevel = $Config{ZM_LOG_LEVEL_SYSLOG};
|
$tempSyslogLevel = $ZoneMinder::Config::Config{ZM_LOG_LEVEL_SYSLOG};
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( defined($ENV{LOG_PRINT}) ) {
|
if ( defined($ENV{LOG_PRINT}) ) {
|
||||||
|
@ -245,19 +245,19 @@ sub initialise( @ ) {
|
||||||
$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 ( $ZoneMinder::Config::Config{ZM_LOG_DEBUG} ) {
|
||||||
# Splitting on an empty string doesn't return an empty string, it returns an empty array
|
# Splitting on an empty string doesn't return an empty string, it returns an empty array
|
||||||
foreach my $target ( $Config{ZM_LOG_DEBUG_TARGET} ? split(/\|/, $Config{ZM_LOG_DEBUG_TARGET}) : '' ) {
|
foreach my $target ( $ZoneMinder::Config::Config{ZM_LOG_DEBUG_TARGET} ? split(/\|/, $ZoneMinder::Config::Config{ZM_LOG_DEBUG_TARGET}) : '' ) {
|
||||||
if ( $target eq $this->{id}
|
if ( $target eq $this->{id}
|
||||||
|| $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 ( $ZoneMinder::Config::Config{ZM_LOG_DEBUG_LEVEL} > NOLOG ) {
|
||||||
$tempLevel = $this->limit( $Config{ZM_LOG_DEBUG_LEVEL} );
|
$tempLevel = $this->limit( $ZoneMinder::Config::Config{ZM_LOG_DEBUG_LEVEL} );
|
||||||
if ( $Config{ZM_LOG_DEBUG_FILE} ne '' ) {
|
if ( $ZoneMinder::Config::Config{ZM_LOG_DEBUG_FILE} ne '' ) {
|
||||||
$tempLogFile = $Config{ZM_LOG_DEBUG_FILE};
|
$tempLogFile = $ZoneMinder::Config::Config{ZM_LOG_DEBUG_FILE};
|
||||||
$tempFileLevel = $tempLevel;
|
$tempFileLevel = $tempLevel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -501,8 +501,8 @@ sub openFile {
|
||||||
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($ZoneMinder::Config::Config{ZM_WEB_USER}))[2];
|
||||||
my $webGid = (getgrnam($Config{ZM_WEB_GROUP}))[2];
|
my $webGid = (getgrnam($ZoneMinder::Config::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 $$this{logFile}: $!");
|
or Fatal("Can't change permissions on log file $$this{logFile}: $!");
|
||||||
|
@ -577,7 +577,7 @@ sub logPrint {
|
||||||
my $res = $this->{sth}->execute(
|
my $res = $this->{sth}->execute(
|
||||||
$seconds+($microseconds/1000000.0),
|
$seconds+($microseconds/1000000.0),
|
||||||
$this->{id},
|
$this->{id},
|
||||||
($Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : undef),
|
($ZoneMinder::Config::Config{ZM_SERVER_ID} ? $ZoneMinder::Config::Config{ZM_SERVER_ID} : undef),
|
||||||
$$,
|
$$,
|
||||||
$level,
|
$level,
|
||||||
$codes{$level},
|
$codes{$level},
|
||||||
|
|
|
@ -766,7 +766,7 @@ FROM Frames WHERE EventId=?';
|
||||||
$res = $selectUnclosedEventsSth->execute()
|
$res = $selectUnclosedEventsSth->execute()
|
||||||
or Fatal( "Can't execute: ".$selectUnclosedEventsSth->errstr() );
|
or Fatal( "Can't execute: ".$selectUnclosedEventsSth->errstr() );
|
||||||
while( my $event = $selectUnclosedEventsSth->fetchrow_hashref() ) {
|
while( my $event = $selectUnclosedEventsSth->fetchrow_hashref() ) {
|
||||||
aud_print( "Found open event '$event->{Id}' at $$event{StartTime}" );
|
aud_print( "Found open event '$event->{Id}' on Monitor $event->{MonitorId} at $$event{StartTime}" );
|
||||||
if ( confirm( 'close', 'closing' ) ) {
|
if ( confirm( 'close', 'closing' ) ) {
|
||||||
if ( ! ( $res = $selectFrameDataSth->execute($event->{Id}) ) ) {
|
if ( ! ( $res = $selectFrameDataSth->execute($event->{Id}) ) ) {
|
||||||
Error( "Can't execute: $selectFrameDataSql:".$selectFrameDataSth->errstr() );
|
Error( "Can't execute: $selectFrameDataSql:".$selectFrameDataSth->errstr() );
|
||||||
|
@ -792,13 +792,13 @@ FROM Frames WHERE EventId=?';
|
||||||
$frame->{MaxScore},
|
$frame->{MaxScore},
|
||||||
RECOVER_TEXT,
|
RECOVER_TEXT,
|
||||||
$event->{Id}
|
$event->{Id}
|
||||||
) or Error( "Can't execute: ".$updateUnclosedEventsSth->errstr() );
|
) or Error( 'Can\'t execute: '.$updateUnclosedEventsSth->errstr() );
|
||||||
} else {
|
} else {
|
||||||
Error('SHOULD DELETE');
|
Error('SHOULD DELETE');
|
||||||
} # end if has frame data
|
} # end if has frame data
|
||||||
}
|
}
|
||||||
} # end while unclosed event
|
} # end while unclosed event
|
||||||
Debug("Done closing open events.");
|
Debug('Done closing open events.');
|
||||||
|
|
||||||
# Now delete any old image files
|
# Now delete any old image files
|
||||||
if ( my @old_files = grep { -M > $max_image_age } <$image_path/*.{jpg,gif,wbmp}> ) {
|
if ( my @old_files = grep { -M > $max_image_age } <$image_path/*.{jpg,gif,wbmp}> ) {
|
||||||
|
@ -1005,17 +1005,17 @@ sub delete_empty_directories {
|
||||||
Error("delete_empty_directories: Can't open directory '/$_[0]': $!" );
|
Error("delete_empty_directories: Can't open directory '/$_[0]': $!" );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
my @contents = map { ( $_ eq '.' or $_ eq '..' ) ? () : $_ } readdir( $DIR );
|
my @contents = map { ( $_ eq '.' or $_ eq '..' ) ? () : $_ } readdir($DIR);
|
||||||
#Debug("delete_empty_directories $_[0] has " . @contents .' entries:' . ( @contents <= 2 ? join(',',@contents) : '' ));
|
#Debug("delete_empty_directories $_[0] has " . @contents .' entries:' . ( @contents <= 2 ? join(',',@contents) : '' ));
|
||||||
my @dirs = map { -d $_[0].'/'.$_ ? $_ : () } @contents;
|
my @dirs = map { -d $_[0].'/'.$_ ? $_ : () } @contents;
|
||||||
if ( @dirs ) {
|
if ( @dirs ) {
|
||||||
Debug("Have " . @dirs . " dirs in $_[0]");
|
Debug('Have ' . @dirs . " dirs in $_[0]");
|
||||||
foreach ( @dirs ) {
|
foreach ( @dirs ) {
|
||||||
delete_empty_directories( $_[0].'/'.$_ );
|
delete_empty_directories($_[0].'/'.$_);
|
||||||
}
|
}
|
||||||
#Reload, since we may now be empty
|
#Reload, since we may now be empty
|
||||||
rewinddir $DIR;
|
rewinddir $DIR;
|
||||||
@contents = map { ($_ eq '.' or $_ eq '..') ? () : $_ } readdir( $DIR );
|
@contents = map { ($_ eq '.' or $_ eq '..') ? () : $_ } readdir($DIR);
|
||||||
}
|
}
|
||||||
closedir($DIR);
|
closedir($DIR);
|
||||||
if ( ! @contents ) {
|
if ( ! @contents ) {
|
||||||
|
|
|
@ -30,7 +30,7 @@ use autouse 'Pod::Usage'=>qw(pod2usage);
|
||||||
use POSIX qw/strftime EPIPE/;
|
use POSIX qw/strftime EPIPE/;
|
||||||
use Socket;
|
use Socket;
|
||||||
#use Data::Dumper;
|
#use Data::Dumper;
|
||||||
use Module::Load::Conditional qw{can_load};;
|
use Module::Load::Conditional qw{can_load};
|
||||||
|
|
||||||
use constant MAX_CONNECT_DELAY => 15;
|
use constant MAX_CONNECT_DELAY => 15;
|
||||||
use constant MAX_COMMAND_WAIT => 1800;
|
use constant MAX_COMMAND_WAIT => 1800;
|
||||||
|
@ -43,7 +43,7 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
||||||
|
|
||||||
logInit();
|
logInit();
|
||||||
|
|
||||||
my $arg_string = join( " ", @ARGV );
|
my $arg_string = join(' ', @ARGV);
|
||||||
|
|
||||||
my $id;
|
my $id;
|
||||||
my %options;
|
my %options;
|
||||||
|
@ -63,23 +63,47 @@ GetOptions(
|
||||||
'autostop' =>\$options{autostop},
|
'autostop' =>\$options{autostop},
|
||||||
) or pod2usage(-exitstatus => -1);
|
) or pod2usage(-exitstatus => -1);
|
||||||
|
|
||||||
if ( !$id || !$options{command} ) {
|
if ( !$id ) {
|
||||||
print( STDERR "Please give a valid monitor id and command\n" );
|
print(STDERR "Please give a valid monitor id\n");
|
||||||
pod2usage(-exitstatus => -1);
|
pod2usage(-exitstatus => -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
( $id ) = $id =~ /^(\w+)$/;
|
( $id ) = $id =~ /^(\w+)$/;
|
||||||
|
|
||||||
Debug("zmcontrol: arg string: $arg_string");
|
|
||||||
|
|
||||||
my $sock_file = $Config{ZM_PATH_SOCKS}.'/zmcontrol-'.$id.'.sock';
|
my $sock_file = $Config{ZM_PATH_SOCKS}.'/zmcontrol-'.$id.'.sock';
|
||||||
|
Debug("zmcontrol: arg string: $arg_string sock file $sock_file");
|
||||||
|
|
||||||
socket(CLIENT, PF_UNIX, SOCK_STREAM, 0)
|
socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!");
|
||||||
or Fatal("Can't open socket: $!");
|
|
||||||
|
|
||||||
my $saddr = sockaddr_un($sock_file);
|
my $saddr = sockaddr_un($sock_file);
|
||||||
my $server_up = connect(CLIENT, $saddr);
|
|
||||||
if ( !$server_up ) {
|
if ( $options{command} ) {
|
||||||
|
# Have a command, so we are the client, connect to the server and send it.
|
||||||
|
|
||||||
|
my $tries = 10;
|
||||||
|
my $server_up;
|
||||||
|
while ( $tries and ! ( $server_up = connect(CLIENT, $saddr) ) ) {
|
||||||
|
Debug("Failed to connect to $server_up at $sock_file");
|
||||||
|
runCommand("zmdc.pl start zmcontrol.pl --id=$id");
|
||||||
|
sleep 1;
|
||||||
|
$tries -= 1;
|
||||||
|
}
|
||||||
|
if ( $server_up ) {
|
||||||
|
# The server is there, connect to it
|
||||||
|
#print( "Writing commands\n" );
|
||||||
|
CLIENT->autoflush();
|
||||||
|
|
||||||
|
if ( $options{command} ) {
|
||||||
|
my $message = jsonEncode(\%options);
|
||||||
|
print(CLIENT $message);
|
||||||
|
}
|
||||||
|
shutdown(CLIENT, 1);
|
||||||
|
} else {
|
||||||
|
Error("Unable to connect to zmcontrol server at $sock_file");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
# The server isn't there
|
# The server isn't there
|
||||||
my $monitor = zmDbGetMonitorAndControl($id);
|
my $monitor = zmDbGetMonitorAndControl($id);
|
||||||
if ( !$monitor ) {
|
if ( !$monitor ) {
|
||||||
|
@ -113,97 +137,72 @@ if ( !$server_up ) {
|
||||||
Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR");
|
Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( my $cpid = fork() ) {
|
Info("Control server $id/$protocol starting at "
|
||||||
logReinit();
|
.strftime('%y/%m/%d %H:%M:%S', localtime())
|
||||||
|
);
|
||||||
|
|
||||||
# Parent process just sleep and fall through
|
$0 = $0." --id=$id";
|
||||||
socket(CLIENT, PF_UNIX, SOCK_STREAM, 0)
|
|
||||||
or die("Can't open socket: $!");
|
|
||||||
my $attempts = 0;
|
|
||||||
while ( !connect(CLIENT, $saddr) ) {
|
|
||||||
$attempts++;
|
|
||||||
Fatal("Can't connect: $! after $attempts attempts to $sock_file") if $attempts > MAX_CONNECT_DELAY;
|
|
||||||
sleep(1);
|
|
||||||
}
|
|
||||||
} elsif ( defined($cpid) ) {
|
|
||||||
close(STDOUT);
|
|
||||||
close(STDERR);
|
|
||||||
|
|
||||||
setpgrp();
|
my $control = "ZoneMinder::Control::$protocol"->new($id);
|
||||||
|
my $control_key = $control->getKey();
|
||||||
|
$control->loadMonitor();
|
||||||
|
|
||||||
logReinit();
|
$control->open();
|
||||||
|
|
||||||
Info("Control server $id/$protocol starting at "
|
# If we have a command when starting up, then do it.
|
||||||
.strftime('%y/%m/%d %H:%M:%S', localtime())
|
if ( $options{command} ) {
|
||||||
);
|
my $command = $options{command};
|
||||||
|
$control->$command(\%options);
|
||||||
$0 = $0." --id $id";
|
|
||||||
|
|
||||||
my $control = "ZoneMinder::Control::$protocol"->new($id);
|
|
||||||
my $control_key = $control->getKey();
|
|
||||||
$control->loadMonitor();
|
|
||||||
|
|
||||||
$control->open();
|
|
||||||
|
|
||||||
socket(SERVER, PF_UNIX, SOCK_STREAM, 0)
|
|
||||||
or Fatal("Can't open socket: $!");
|
|
||||||
unlink($sock_file);
|
|
||||||
bind(SERVER, $saddr) or Fatal("Can't bind: $!");
|
|
||||||
listen(SERVER, SOMAXCONN) or Fatal("Can't listen: $!");
|
|
||||||
|
|
||||||
my $rin = '';
|
|
||||||
vec( $rin, fileno(SERVER), 1 ) = 1;
|
|
||||||
my $win = $rin;
|
|
||||||
my $ein = $win;
|
|
||||||
my $timeout = MAX_COMMAND_WAIT;
|
|
||||||
while( 1 ) {
|
|
||||||
my $nfound = select(my $rout = $rin, undef, undef, $timeout);
|
|
||||||
if ( $nfound > 0 ) {
|
|
||||||
if ( vec( $rout, fileno(SERVER), 1 ) ) {
|
|
||||||
my $paddr = accept(CLIENT, SERVER);
|
|
||||||
my $message = <CLIENT>;
|
|
||||||
|
|
||||||
next if !$message;
|
|
||||||
|
|
||||||
my $params = jsonDecode($message);
|
|
||||||
#Debug( Dumper( $params ) );
|
|
||||||
|
|
||||||
my $command = $params->{command};
|
|
||||||
close( CLIENT );
|
|
||||||
if ( $command eq 'quit' ) {
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
$control->$command($params);
|
|
||||||
} else {
|
|
||||||
Fatal('Bogus descriptor');
|
|
||||||
}
|
|
||||||
} elsif ( $nfound < 0 ) {
|
|
||||||
if ( $! == EPIPE ) {
|
|
||||||
Error("Can't select: $!");
|
|
||||||
} else {
|
|
||||||
Fatal("Can't select: $!");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
#print( "Select timed out\n" );
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
} # end while forever
|
|
||||||
Info("Control server $id/$protocol exiting");
|
|
||||||
unlink($sock_file);
|
|
||||||
$control->close();
|
|
||||||
exit(0);
|
|
||||||
} else {
|
|
||||||
Fatal("Can't fork: $!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
socket(SERVER, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!");
|
||||||
|
unlink($sock_file);
|
||||||
|
bind(SERVER, $saddr) or Fatal("Can't bind: $!");
|
||||||
|
listen(SERVER, SOMAXCONN) or Fatal("Can't listen: $!");
|
||||||
|
|
||||||
|
my $rin = '';
|
||||||
|
vec( $rin, fileno(SERVER), 1 ) = 1;
|
||||||
|
my $win = $rin;
|
||||||
|
my $ein = $win;
|
||||||
|
my $timeout = MAX_COMMAND_WAIT;
|
||||||
|
while( 1 ) {
|
||||||
|
my $nfound = select(my $rout = $rin, undef, undef, $timeout);
|
||||||
|
if ( $nfound > 0 ) {
|
||||||
|
if ( vec( $rout, fileno(SERVER), 1 ) ) {
|
||||||
|
my $paddr = accept(CLIENT, SERVER);
|
||||||
|
my $message = <CLIENT>;
|
||||||
|
|
||||||
|
next if !$message;
|
||||||
|
|
||||||
|
my $params = jsonDecode($message);
|
||||||
|
#Debug( Dumper( $params ) );
|
||||||
|
|
||||||
|
my $command = $params->{command};
|
||||||
|
close( CLIENT );
|
||||||
|
if ( $command eq 'quit' ) {
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
$control->$command($params);
|
||||||
|
} else {
|
||||||
|
Fatal('Bogus descriptor');
|
||||||
|
}
|
||||||
|
} elsif ( $nfound < 0 ) {
|
||||||
|
if ( $! == EPIPE ) {
|
||||||
|
Error("Can't select: $!");
|
||||||
|
} else {
|
||||||
|
Fatal("Can't select: $!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#print( "Select timed out\n" );
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
} # end while forever
|
||||||
|
Info("Control server $id/$protocol exiting");
|
||||||
|
unlink($sock_file);
|
||||||
|
$control->close();
|
||||||
|
exit(0);
|
||||||
} # end if !server up
|
} # end if !server up
|
||||||
|
|
||||||
# The server is there, connect to it
|
|
||||||
#print( "Writing commands\n" );
|
|
||||||
CLIENT->autoflush();
|
|
||||||
|
|
||||||
my $message = jsonEncode(\%options);
|
|
||||||
print(CLIENT $message);
|
|
||||||
shutdown(CLIENT, 1);
|
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
||||||
|
@ -216,7 +215,7 @@ zmcontrol.pl - ZoneMinder control script
|
||||||
|
|
||||||
=head1 SYNOPSIS
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
zmcontrol.pl --id {monitor_id} --command={command} [various options]
|
zmcontrol.pl --id {monitor_id} [--command={command}] [various options]
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,7 @@ my @daemons = (
|
||||||
'zmupdate.pl',
|
'zmupdate.pl',
|
||||||
'zmstats.pl',
|
'zmstats.pl',
|
||||||
'zmtrack.pl',
|
'zmtrack.pl',
|
||||||
|
'zmcontrol.pl',
|
||||||
'zmtelemetry.pl'
|
'zmtelemetry.pl'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -217,14 +217,17 @@ if ( $command =~ /^(?:start|restart)$/ ) {
|
||||||
runCommand("zmdc.pl start zmc -m $monitor->{Id}");
|
runCommand("zmdc.pl start zmc -m $monitor->{Id}");
|
||||||
}
|
}
|
||||||
if ( $Config{ZM_OPT_CONTROL} ) {
|
if ( $Config{ZM_OPT_CONTROL} ) {
|
||||||
if ( $monitor->{Controllable} && $monitor->{TrackMotion} ) {
|
if ( $monitor->{Controllable} ) {
|
||||||
if ( $monitor->{Function} eq 'Modect' || $monitor->{Function} eq 'Mocord' ) {
|
runCommand("zmdc.pl start zmcontrol.pl --id $monitor->{Id}");
|
||||||
runCommand("zmdc.pl start zmtrack.pl -m $monitor->{Id}");
|
if ( $monitor->{TrackMotion} ) {
|
||||||
} else {
|
if ( $monitor->{Function} eq 'Modect' || $monitor->{Function} eq 'Mocord' ) {
|
||||||
Warning('Monitor is set to track motion, but does not have motion detection enabled.');
|
runCommand("zmdc.pl start zmtrack.pl -m $monitor->{Id}");
|
||||||
} # end if Has motion enabled
|
} else {
|
||||||
} # end if track motion
|
Warning('Monitor is set to track motion, but does not have motion detection enabled.');
|
||||||
} # end if ZM_OPT_CONTROL
|
} # end if Has motion enabled
|
||||||
|
} # end if track motion
|
||||||
|
} # end if controllable
|
||||||
|
} # end if ZM_OPT_CONTROL
|
||||||
} # end if function is not none or Website
|
} # end if function is not none or Website
|
||||||
} # end foreach monitor
|
} # end foreach monitor
|
||||||
$sth->finish();
|
$sth->finish();
|
||||||
|
@ -272,7 +275,11 @@ if ( $command =~ /^(?:start|restart)$/ ) {
|
||||||
runCommand('zmdc.pl start zmtelemetry.pl');
|
runCommand('zmdc.pl start zmtelemetry.pl');
|
||||||
}
|
}
|
||||||
if ($Config{ZM_OPT_USE_EVENTNOTIFICATION} ) {
|
if ($Config{ZM_OPT_USE_EVENTNOTIFICATION} ) {
|
||||||
|
if ( $Server and exists $$Server{'zmeventnotification'} and ! $$Server{'zmeventnotification'} ) {
|
||||||
|
Debug("Not running zmnotification.pl because it is turned off for this server.");
|
||||||
|
} else {
|
||||||
runCommand('zmdc.pl start zmeventnotification.pl');
|
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.');
|
||||||
|
|
|
@ -18,9 +18,7 @@ use constant START_DELAY => 30; # To give everything else time to start
|
||||||
|
|
||||||
@EXTRA_PERL_LIB@
|
@EXTRA_PERL_LIB@
|
||||||
use ZoneMinder;
|
use ZoneMinder;
|
||||||
use POSIX;
|
|
||||||
use DBI;
|
use DBI;
|
||||||
use autouse 'Data::Dumper'=>qw(Dumper);
|
|
||||||
|
|
||||||
$| = 1;
|
$| = 1;
|
||||||
|
|
||||||
|
@ -31,15 +29,15 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
||||||
logInit();
|
logInit();
|
||||||
logSetSignal();
|
logSetSignal();
|
||||||
|
|
||||||
Info("Stats Daemon starting in ".START_DELAY." seconds");
|
Info('Stats Daemon starting in '.START_DELAY.' seconds');
|
||||||
sleep(START_DELAY);
|
sleep(START_DELAY);
|
||||||
|
|
||||||
my $dbh = zmDbConnect();
|
my $dbh = zmDbConnect();
|
||||||
|
|
||||||
while( 1 ) {
|
while( 1 ) {
|
||||||
while ( ! ( $dbh and $dbh->ping() ) ) {
|
while ( ! ( $dbh and $dbh->ping() ) ) {
|
||||||
Info("Reconnecting to db");
|
Info('Reconnecting to db');
|
||||||
if ( ! ( $dbh = zmDbConnect() ) ) {
|
if ( !($dbh = zmDbConnect()) ) {
|
||||||
#What we do here is not that important, so just skip this interval
|
#What we do here is not that important, so just skip this interval
|
||||||
sleep($Config{ZM_STATS_UPDATE_INTERVAL});
|
sleep($Config{ZM_STATS_UPDATE_INTERVAL});
|
||||||
}
|
}
|
||||||
|
@ -53,7 +51,7 @@ while( 1 ) {
|
||||||
sleep($Config{ZM_STATS_UPDATE_INTERVAL});
|
sleep($Config{ZM_STATS_UPDATE_INTERVAL});
|
||||||
} # end while (1)
|
} # end while (1)
|
||||||
|
|
||||||
Info("Stats Daemon exiting");
|
Info('Stats Daemon exiting');
|
||||||
exit();
|
exit();
|
||||||
1;
|
1;
|
||||||
__END__
|
__END__
|
||||||
|
|
|
@ -923,10 +923,10 @@ if ( $version ) {
|
||||||
die( "Can't find upgrade from version '$version'" );
|
die( "Can't find upgrade from version '$version'" );
|
||||||
}
|
}
|
||||||
# Re-enable the privacy popup after each upgrade
|
# Re-enable the privacy popup after each upgrade
|
||||||
my $sql = "update Config set Value = 1 where Name = 'ZM_SHOW_PRIVACY'";
|
#my $sql = "update Config set Value = 1 where Name = 'ZM_SHOW_PRIVACY'";
|
||||||
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
#my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
|
||||||
my $res = $sth->execute( ) or die( "Can't execute: ".$sth->errstr() );
|
#my $res = $sth->execute( ) or die( "Can't execute: ".$sth->errstr() );
|
||||||
$sth->finish();
|
#$sth->finish();
|
||||||
print( "\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n" );
|
print( "\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n" );
|
||||||
}
|
}
|
||||||
zmDbDisconnect();
|
zmDbDisconnect();
|
||||||
|
|
|
@ -198,6 +198,9 @@ Event::Event(
|
||||||
|
|
||||||
video_name[0] = 0;
|
video_name[0] = 0;
|
||||||
|
|
||||||
|
snprintf(snapshot_file, sizeof(snapshot_file), "%s/snapshot.jpg", path);
|
||||||
|
snprintf(alarm_file, sizeof(alarm_file), "%s/alarm.jpg", path);
|
||||||
|
|
||||||
/* Save as video */
|
/* Save as video */
|
||||||
|
|
||||||
if ( monitor->GetOptVideoWriter() != 0 ) {
|
if ( monitor->GetOptVideoWriter() != 0 ) {
|
||||||
|
@ -254,7 +257,7 @@ Event::~Event() {
|
||||||
WriteDbFrames();
|
WriteDbFrames();
|
||||||
|
|
||||||
// Should not be static because we might be multi-threaded
|
// Should not be static because we might be multi-threaded
|
||||||
char sql[ZM_SQL_MED_BUFSIZ];
|
char sql[ZM_SQL_LGE_BUFSIZ];
|
||||||
snprintf(sql, sizeof(sql),
|
snprintf(sql, sizeof(sql),
|
||||||
"UPDATE Events SET Name='%s %" PRIu64 "', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' WHERE Id = %" PRIu64,
|
"UPDATE Events SET Name='%s %" PRIu64 "', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' WHERE Id = %" PRIu64,
|
||||||
monitor->EventPrefix(), id, end_time.tv_sec,
|
monitor->EventPrefix(), id, end_time.tv_sec,
|
||||||
|
@ -442,9 +445,9 @@ void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, str
|
||||||
|
|
||||||
frames++;
|
frames++;
|
||||||
|
|
||||||
static char event_file[PATH_MAX];
|
|
||||||
snprintf(event_file, sizeof(event_file), staticConfig.capture_file_format, path.c_str(), frames);
|
|
||||||
if ( save_jpegs & 1 ) {
|
if ( save_jpegs & 1 ) {
|
||||||
|
static char event_file[PATH_MAX];
|
||||||
|
snprintf(event_file, sizeof(event_file), staticConfig.capture_file_format, path.c_str(), frames);
|
||||||
Debug(1, "Writing pre-capture frame %d", frames);
|
Debug(1, "Writing pre-capture frame %d", frames);
|
||||||
WriteFrameImage(images[i], *(timestamps[i]), event_file);
|
WriteFrameImage(images[i], *(timestamps[i]), event_file);
|
||||||
} else {
|
} else {
|
||||||
|
@ -453,7 +456,6 @@ void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, str
|
||||||
// neccessarily be of the motion. But some events are less than 10 frames,
|
// neccessarily be of the motion. But some events are less than 10 frames,
|
||||||
// so I am changing this to 1, but we should overwrite it later with a better snapshot.
|
// so I am changing this to 1, but we should overwrite it later with a better snapshot.
|
||||||
if ( frames == 1 ) {
|
if ( frames == 1 ) {
|
||||||
std::string snapshot_file = path + "/snapshot.jpg";
|
|
||||||
WriteFrameImage(images[i], *(timestamps[i]), snapshot_file.c_str());
|
WriteFrameImage(images[i], *(timestamps[i]), snapshot_file.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -541,29 +543,30 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
|
||||||
|
|
||||||
frames++;
|
frames++;
|
||||||
|
|
||||||
static char event_file[PATH_MAX];
|
bool write_to_db = false;
|
||||||
snprintf(event_file, sizeof(event_file), staticConfig.capture_file_format, path.c_str(), frames);
|
|
||||||
|
|
||||||
if ( save_jpegs & 1 ) {
|
if ( save_jpegs & 1 ) {
|
||||||
|
static char event_file[PATH_MAX];
|
||||||
|
snprintf(event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames);
|
||||||
Debug(1, "Writing capture frame %d to %s", frames, event_file);
|
Debug(1, "Writing capture frame %d to %s", frames, event_file);
|
||||||
if ( ! WriteFrameImage(image, timestamp, event_file) ) {
|
if ( ! WriteFrameImage(image, timestamp, event_file) ) {
|
||||||
Error("Failed to write frame image");
|
Error("Failed to write frame image");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//If this is the first frame, we should add a thumbnail to the event directory
|
//If this is the first frame, we should add a thumbnail to the event directory
|
||||||
// On the first frame, max_score will be zero, this effectively makes us write out a thumbnail
|
if ( (frames == 1) || (score > (int)max_score) ) {
|
||||||
// for the first frame as well.
|
write_to_db = true; // web ui might show this as thumbnail, so db needs to know about it.
|
||||||
if ( frames == 1 || score > (int)max_score ) {
|
|
||||||
std::string snapshot_file = path + "/snapshot.jpg";
|
|
||||||
WriteFrameImage(image, timestamp, snapshot_file.c_str());
|
WriteFrameImage(image, timestamp, snapshot_file.c_str());
|
||||||
}
|
}
|
||||||
// The first frame with a score will be the frame that alarmed the event
|
// The first frame with a score will be the frame that alarmed the event
|
||||||
if (!alarm_frame_written && score > 0) {
|
if ( (!alarm_frame_written) && (score > 0) ) {
|
||||||
|
write_to_db = true; // OD processing will need it, so the db needs to know about it
|
||||||
alarm_frame_written = true;
|
alarm_frame_written = true;
|
||||||
char alarm_file[PATH_MAX];
|
|
||||||
snprintf(alarm_file, sizeof(alarm_file), "%s/alarm.jpg", path.c_str());
|
|
||||||
WriteFrameImage(image, timestamp, alarm_file);
|
WriteFrameImage(image, timestamp, alarm_file);
|
||||||
}
|
}
|
||||||
|
} // end if save_jpegs
|
||||||
|
if ( videowriter != NULL ) {
|
||||||
|
WriteFrameVideo(image, timestamp, videowriter);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DeltaTimeval delta_time;
|
struct DeltaTimeval delta_time;
|
||||||
|
@ -580,7 +583,7 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
|
||||||
static char sql[ZM_SQL_MED_BUFSIZ];
|
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||||
|
|
||||||
frame_data.push(new Frame(id, frames, frame_type, timestamp, delta_time, score));
|
frame_data.push(new Frame(id, frames, frame_type, timestamp, delta_time, score));
|
||||||
if ( frame_data.size() > 20 ) {
|
if ( write_to_db || ( frame_data.size() > 20 ) ) {
|
||||||
WriteDbFrames();
|
WriteDbFrames();
|
||||||
Debug(1, "Adding 20 frames to DB");
|
Debug(1, "Adding 20 frames to DB");
|
||||||
last_db_frame = frames;
|
last_db_frame = frames;
|
||||||
|
@ -629,5 +632,6 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // end if frame_type == ALARM
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,10 @@ class Event {
|
||||||
unsigned int max_score;
|
unsigned int max_score;
|
||||||
std::string path;
|
std::string path;
|
||||||
VideoStore *videoStore;
|
VideoStore *videoStore;
|
||||||
char video_name[64];
|
char snapshot_file[PATH_MAX];
|
||||||
|
char alarm_file[PATH_MAX];
|
||||||
|
VideoWriter* videowriter;
|
||||||
|
char video_name[PATH_MAX];
|
||||||
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.
|
||||||
|
|
|
@ -228,7 +228,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
||||||
//timestamp = atof(dbrow[1]);
|
//timestamp = atof(dbrow[1]);
|
||||||
double delta = atof(dbrow[2]);
|
double delta = atof(dbrow[2]);
|
||||||
int id_diff = id - last_id;
|
int id_diff = id - last_id;
|
||||||
double frame_delta = id_diff ? (delta-last_delta)/id_diff : (delta - last_delta);
|
double frame_delta = id_diff ? (delta-last_delta)/id_diff : (delta-last_delta);
|
||||||
// Fill in data between bulk frames
|
// Fill in data between bulk frames
|
||||||
if ( id_diff > 1 ) {
|
if ( id_diff > 1 ) {
|
||||||
for ( int i = last_id+1; i < id; i++ ) {
|
for ( int i = last_id+1; i < id; i++ ) {
|
||||||
|
@ -262,8 +262,8 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
|
@ -355,6 +355,7 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
||||||
replay_rate = 50 * ZM_RATE_BASE;
|
replay_rate = 50 * ZM_RATE_BASE;
|
||||||
break;
|
break;
|
||||||
default :
|
default :
|
||||||
|
Debug(1,"Defaulting replay_rate to 2*ZM_RATE_BASE because it is %d", replay_rate);
|
||||||
replay_rate = 2 * ZM_RATE_BASE;
|
replay_rate = 2 * ZM_RATE_BASE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -509,11 +510,12 @@ 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));
|
||||||
|
Debug(1,"Size of msg %d", 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
|
||||||
|
@ -521,7 +523,7 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
||||||
updateFrameRate((double)event_data->frame_count/event_data->duration);
|
updateFrameRate((double)event_data->frame_count/event_data->duration);
|
||||||
}
|
} // void EventStream::processCommand(const CmdMsg *msg)
|
||||||
|
|
||||||
void EventStream::checkEventLoaded() {
|
void EventStream::checkEventLoaded() {
|
||||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
static char sql[ZM_SQL_SML_BUFSIZ];
|
||||||
|
@ -532,6 +534,7 @@ void EventStream::checkEventLoaded() {
|
||||||
snprintf(sql, sizeof(sql), "SELECT Id FROM Events WHERE MonitorId = %ld AND Id > %" PRIu64 " ORDER BY Id ASC LIMIT 1", event_data->monitor_id, event_data->event_id);
|
snprintf(sql, sizeof(sql), "SELECT Id FROM Events WHERE MonitorId = %ld AND Id > %" PRIu64 " ORDER BY Id ASC LIMIT 1", event_data->monitor_id, event_data->event_id);
|
||||||
} else {
|
} else {
|
||||||
// No event change required
|
// No event change required
|
||||||
|
//Debug(3, "No event change required");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -673,10 +676,6 @@ Debug(1, "Loading image");
|
||||||
// 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;
|
AVFrame *frame;
|
||||||
if ( curr_frame_id == 1 ) {
|
|
||||||
// Special case, first frame, we want to send the initial keyframe.
|
|
||||||
frame = ffmpeg_input->get_frame( ffmpeg_input->get_video_stream_id(), 0 );
|
|
||||||
}
|
|
||||||
FrameData *frame_data = &event_data->frames[curr_frame_id-1];
|
FrameData *frame_data = &event_data->frames[curr_frame_id-1];
|
||||||
frame = ffmpeg_input->get_frame( ffmpeg_input->get_video_stream_id(), frame_data->offset );
|
frame = ffmpeg_input->get_frame( ffmpeg_input->get_video_stream_id(), frame_data->offset );
|
||||||
if ( frame ) {
|
if ( frame ) {
|
||||||
|
@ -787,22 +786,31 @@ void EventStream::runStream() {
|
||||||
|
|
||||||
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);
|
||||||
|
gettimeofday(&start, NULL);
|
||||||
|
uint64_t start_usec = start.tv_sec * 1000000 + start.tv_usec;
|
||||||
|
uint64_t last_frame_offset = 0;
|
||||||
|
|
||||||
while ( !zm_terminate ) {
|
while ( !zm_terminate ) {
|
||||||
gettimeofday(&now, NULL);
|
gettimeofday(&now, NULL);
|
||||||
|
|
||||||
unsigned int delta_us = 0;
|
int delta_us = 0;
|
||||||
send_frame = false;
|
send_frame = false;
|
||||||
|
|
||||||
// commands may set send_frame to true
|
if ( connkey ) {
|
||||||
while ( checkCommandQueue() && !zm_terminate ) {
|
// commands may set send_frame to true
|
||||||
// The idea is to loop here processing all commands before proceeding.
|
while ( checkCommandQueue() && !zm_terminate ) {
|
||||||
}
|
// The idea is to loop here processing all commands before proceeding.
|
||||||
|
Debug(1, "Have command queue");
|
||||||
|
}
|
||||||
|
Debug(1, "Done command queue");
|
||||||
|
|
||||||
// Update modified time of the socket .lock file so that we can tell which ones are stale.
|
// Update modified time of the socket .lock file so that we can tell which ones are stale.
|
||||||
if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) {
|
if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) {
|
||||||
touch(sock_path_lock);
|
touch(sock_path_lock);
|
||||||
last_comm_update = now;
|
last_comm_update = now;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Debug(1, "Not checking command queue");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( step != 0 )
|
if ( step != 0 )
|
||||||
|
@ -818,6 +826,7 @@ void EventStream::runStream() {
|
||||||
//Info( "cfid:%d", curr_frame_id );
|
//Info( "cfid:%d", curr_frame_id );
|
||||||
//Info( "fdt:%d", frame_data->timestamp );
|
//Info( "fdt:%d", frame_data->timestamp );
|
||||||
if ( !paused ) {
|
if ( !paused ) {
|
||||||
|
Debug(3,"Not paused");
|
||||||
bool in_event = true;
|
bool in_event = true;
|
||||||
double time_to_event = 0;
|
double time_to_event = 0;
|
||||||
if ( replay_rate > 0 ) {
|
if ( replay_rate > 0 ) {
|
||||||
|
@ -840,6 +849,7 @@ void EventStream::runStream() {
|
||||||
}
|
}
|
||||||
//else
|
//else
|
||||||
//{
|
//{
|
||||||
|
Debug(2,"Sleeping because paused");
|
||||||
usleep(STREAM_PAUSE_WAIT);
|
usleep(STREAM_PAUSE_WAIT);
|
||||||
//curr_stream_time += (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000));
|
//curr_stream_time += (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000));
|
||||||
curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000);
|
curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000);
|
||||||
|
@ -854,15 +864,17 @@ Debug(3,"cur_frame_id (%d-1) mod frame_mod(%d)",curr_frame_id, frame_mod);
|
||||||
// so if it is 2, then we send every other frame, if is it 4 then every fourth frame, etc.
|
// so if it is 2, then we send every other frame, if is it 4 then every fourth frame, etc.
|
||||||
if ( (frame_mod == 1) || (((curr_frame_id-1)%frame_mod) == 0) ) {
|
if ( (frame_mod == 1) || (((curr_frame_id-1)%frame_mod) == 0) ) {
|
||||||
delta_us = (unsigned int)(frame_data->delta * 1000000);
|
delta_us = (unsigned int)(frame_data->delta * 1000000);
|
||||||
Debug(3,"frame delta %u ", delta_us);
|
Debug(3,"frame delta %uus ", delta_us);
|
||||||
// if effective > base we should speed up frame delivery
|
// if effective > base we should speed up frame delivery
|
||||||
delta_us = (unsigned int)((delta_us * base_fps)/effective_fps);
|
delta_us = (unsigned int)((delta_us * base_fps)/effective_fps);
|
||||||
Debug(3,"delta %u = base_fps(%f)/effective fps(%f)", delta_us, base_fps, effective_fps);
|
Debug(3,"delta %u = base_fps(%f)/effective fps(%f)", delta_us, base_fps, effective_fps);
|
||||||
// but must not exceed maxfps
|
// but must not exceed maxfps
|
||||||
delta_us = max(delta_us, 1000000 / maxfps);
|
delta_us = max(delta_us, 1000000 / maxfps);
|
||||||
|
Debug(3,"delta %u = base_fps(%f)/effective fps(%f) from 30fps", delta_us, base_fps, effective_fps);
|
||||||
send_frame = true;
|
send_frame = true;
|
||||||
}
|
}
|
||||||
} else if ( step != 0 ) {
|
} else if ( step != 0 ) {
|
||||||
|
Debug(2,"Paused with step");
|
||||||
// We are paused and are just stepping forward or backward one frame
|
// We are paused and are just stepping forward or backward one frame
|
||||||
step = 0;
|
step = 0;
|
||||||
send_frame = true;
|
send_frame = true;
|
||||||
|
@ -873,28 +885,76 @@ Debug(3,"cur_frame_id (%d-1) mod frame_mod(%d)",curr_frame_id, frame_mod);
|
||||||
// Send keepalive
|
// Send keepalive
|
||||||
Debug(2, "Sending keepalive frame");
|
Debug(2, "Sending keepalive frame");
|
||||||
send_frame = true;
|
send_frame = true;
|
||||||
|
//} else {
|
||||||
|
//Debug(2, "Not Sending keepalive frame");
|
||||||
}
|
}
|
||||||
} // end if streaming stepping or doing nothing
|
} // end if streaming stepping or doing nothing
|
||||||
|
|
||||||
if ( send_frame )
|
if ( send_frame ) {
|
||||||
|
//Debug(3,"sending frame");
|
||||||
if ( !sendFrame(delta_us) )
|
if ( !sendFrame(delta_us) )
|
||||||
zm_terminate = true;
|
zm_terminate = true;
|
||||||
|
//} else {
|
||||||
|
//Debug(3,"Not sending frame");
|
||||||
|
}
|
||||||
|
|
||||||
curr_stream_time = frame_data->timestamp;
|
curr_stream_time = frame_data->timestamp;
|
||||||
|
|
||||||
if ( !paused ) {
|
if ( !paused ) {
|
||||||
curr_frame_id += (replay_rate>0) ? 1 : -1;
|
// +/- 1? What if we are skipping frames?
|
||||||
|
curr_frame_id += (replay_rate>0) ? frame_mod : -1*frame_mod;
|
||||||
|
|
||||||
if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) {
|
if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) {
|
||||||
Debug(2, "Have mode==MODE_SINGLE and at end of event, looping back to start");
|
Debug(2, "Have mode==MODE_SINGLE and at end of event, looping back to start");
|
||||||
curr_frame_id = 1;
|
curr_frame_id = 1;
|
||||||
}
|
}
|
||||||
|
frame_data = &event_data->frames[curr_frame_id-1];
|
||||||
|
|
||||||
|
// sending the frame may have taken some time, so reload now
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
uint64_t now_usec = (now.tv_sec * 1000000 + now.tv_usec);
|
||||||
|
// frame_data->delta is the time since last frame as a float in seconds
|
||||||
|
// but what if we are skipping frames? We need the distance from the last frame sent
|
||||||
|
// Also, what about reverse? needs to be absolute value
|
||||||
|
|
||||||
|
// There are two ways to go about this, not sure which is correct.
|
||||||
|
// you can calculate the relationship between now and the start
|
||||||
|
// or calc the relationship from the last frame. I think from the start is better as it self-corrects
|
||||||
|
|
||||||
|
if ( last_frame_offset ) {
|
||||||
|
// We assume that we are going forward and the next frame is in the future.
|
||||||
|
delta_us = frame_data->offset * 1000000 - (now_usec-start_usec);
|
||||||
|
// - (now_usec - start_usec);
|
||||||
|
Debug(2, "New delta_us now %" PRIu64 " - start %" PRIu64 " = %d offset %" PRId64 " - elapsed = %dusec",
|
||||||
|
now_usec, start_usec, now_usec-start_usec, frame_data->offset * 1000000, delta_us);
|
||||||
|
} else {
|
||||||
|
Debug(2, "No last frame_offset, no sleep");
|
||||||
|
delta_us = 0;
|
||||||
|
}
|
||||||
|
last_frame_offset = frame_data->offset * 1000000;
|
||||||
|
|
||||||
if ( send_frame && type != STREAM_MPEG ) {
|
if ( send_frame && type != STREAM_MPEG ) {
|
||||||
Debug(3, "dUs: %d", delta_us);
|
if ( delta_us > 0 ) {
|
||||||
if ( delta_us )
|
Debug( 3, "dUs: %d", delta_us );
|
||||||
usleep(delta_us);
|
usleep(delta_us);
|
||||||
|
Debug(3, "Done sleeping: %d usec", delta_us);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
delta_us = ((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*(replay_rate?abs(replay_rate*2):2)));
|
||||||
|
|
||||||
|
Debug(2,"Sleeping %d because 1000000 * ZM_RATE_BASE(%d) / ( base_fps (%f), replay_rate(%d)",
|
||||||
|
(unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))),
|
||||||
|
ZM_RATE_BASE,
|
||||||
|
(base_fps?base_fps:1),
|
||||||
|
(replay_rate?abs(replay_rate*2):200)
|
||||||
|
);
|
||||||
|
if ( delta_us > 0 and delta_us < 100000 ) {
|
||||||
usleep((unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))));
|
usleep((unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))));
|
||||||
|
} else {
|
||||||
|
//Error("Not sleeping!");
|
||||||
|
usleep(100000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} // end while ! zm_terminate
|
} // end while ! zm_terminate
|
||||||
#if HAVE_LIBAVCODEC
|
#if HAVE_LIBAVCODEC
|
||||||
|
|
|
@ -79,6 +79,7 @@ class EventStream : public StreamBase {
|
||||||
int curr_frame_id;
|
int curr_frame_id;
|
||||||
double curr_stream_time;
|
double curr_stream_time;
|
||||||
bool send_frame;
|
bool send_frame;
|
||||||
|
struct timeval start; // clock time when started the event
|
||||||
|
|
||||||
EventData *event_data;
|
EventData *event_data;
|
||||||
FFmpeg_Input *ffmpeg_input;
|
FFmpeg_Input *ffmpeg_input;
|
||||||
|
@ -95,6 +96,7 @@ class EventStream : public StreamBase {
|
||||||
public:
|
public:
|
||||||
EventStream() {
|
EventStream() {
|
||||||
mode = DEFAULT_MODE;
|
mode = DEFAULT_MODE;
|
||||||
|
replay_rate = DEFAULT_RATE;
|
||||||
|
|
||||||
forceEventChange = false;
|
forceEventChange = false;
|
||||||
|
|
||||||
|
@ -109,7 +111,6 @@ class EventStream : public StreamBase {
|
||||||
input_codec = 0;
|
input_codec = 0;
|
||||||
|
|
||||||
ffmpeg_input = NULL;
|
ffmpeg_input = NULL;
|
||||||
|
|
||||||
}
|
}
|
||||||
void setStreamStart( uint64_t init_event_id, unsigned int init_frame_id );
|
void setStreamStart( uint64_t init_event_id, unsigned int init_frame_id );
|
||||||
void setStreamStart( int monitor_id, time_t event_time );
|
void setStreamStart( int monitor_id, time_t event_time );
|
||||||
|
|
|
@ -65,8 +65,9 @@ void log_libav_callback( void *ptr, int level, const char *fmt, va_list vargs )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool bInit = false;
|
||||||
|
|
||||||
void FFMPEGInit() {
|
void FFMPEGInit() {
|
||||||
static bool bInit = false;
|
|
||||||
|
|
||||||
if ( !bInit ) {
|
if ( !bInit ) {
|
||||||
if ( logDebugging() )
|
if ( logDebugging() )
|
||||||
|
@ -77,7 +78,7 @@ void FFMPEGInit() {
|
||||||
av_log_set_callback(log_libav_callback);
|
av_log_set_callback(log_libav_callback);
|
||||||
else
|
else
|
||||||
Info("Not enabling ffmpeg logs, as LOG_FFMPEG is disabled in options");
|
Info("Not enabling ffmpeg logs, as LOG_FFMPEG is disabled in options");
|
||||||
#if LIBAVCODEC_VERSION_CHECK(58, 18, 0, 64, 0)
|
#if LIBAVFORMAT_VERSION_CHECK(58, 9, 0, 64, 0)
|
||||||
#else
|
#else
|
||||||
av_register_all();
|
av_register_all();
|
||||||
#endif
|
#endif
|
||||||
|
@ -86,6 +87,11 @@ void FFMPEGInit() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FFMPEGDeInit() {
|
||||||
|
avformat_network_deinit();
|
||||||
|
bInit = false;
|
||||||
|
}
|
||||||
|
|
||||||
#if HAVE_LIBAVUTIL
|
#if HAVE_LIBAVUTIL
|
||||||
enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder) {
|
enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder) {
|
||||||
enum _AVPIXELFORMAT pf;
|
enum _AVPIXELFORMAT pf;
|
||||||
|
@ -279,7 +285,9 @@ static void zm_log_fps(double d, const char *postfix) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void zm_dump_frame(const AVFrame *frame,const char *text) {
|
void zm_dump_frame(const AVFrame *frame,const char *text) {
|
||||||
Debug(1, "%s: format %d %s sample_rate %" PRIu32 " nb_samples %d channels %d layout %d",
|
Debug(1, "%s: format %d %s sample_rate %" PRIu32 " nb_samples %d channels %d"
|
||||||
|
" duration %" PRId64
|
||||||
|
" layout %d pts %" PRId64,
|
||||||
text,
|
text,
|
||||||
frame->format,
|
frame->format,
|
||||||
av_get_sample_fmt_name((AVSampleFormat)frame->format),
|
av_get_sample_fmt_name((AVSampleFormat)frame->format),
|
||||||
|
@ -287,10 +295,12 @@ void zm_dump_frame(const AVFrame *frame,const char *text) {
|
||||||
frame->nb_samples,
|
frame->nb_samples,
|
||||||
#if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100)
|
#if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100)
|
||||||
frame->channels,
|
frame->channels,
|
||||||
|
frame->pkt_duration,
|
||||||
#else
|
#else
|
||||||
0,
|
0, 0,
|
||||||
#endif
|
#endif
|
||||||
frame->channel_layout
|
frame->channel_layout,
|
||||||
|
frame->pts
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,7 +320,8 @@ 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)\n"
|
||||||
|
"gop_size %d max_b_frames %d me_cmp %d me_range %d qmin %d qmax %d",
|
||||||
codec->codec_type,
|
codec->codec_type,
|
||||||
codec->codec_id,
|
codec->codec_id,
|
||||||
codec->width,
|
codec->width,
|
||||||
|
@ -318,11 +329,17 @@ void zm_dump_codec(const AVCodecContext *codec) {
|
||||||
codec->time_base.num,
|
codec->time_base.num,
|
||||||
codec->time_base.den,
|
codec->time_base.den,
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
(codec->pix_fmt == AV_PIX_FMT_NONE ? "none" : av_get_pix_fmt_name(codec->pix_fmt))
|
(codec->pix_fmt == AV_PIX_FMT_NONE ? "none" : av_get_pix_fmt_name(codec->pix_fmt)),
|
||||||
#else
|
#else
|
||||||
"unsupported on avconv"
|
"unsupported on avconv",
|
||||||
#endif
|
#endif
|
||||||
);
|
codec->gop_size,
|
||||||
|
codec->max_b_frames,
|
||||||
|
codec->me_cmp,
|
||||||
|
codec->me_range,
|
||||||
|
codec->qmin,
|
||||||
|
codec->qmax
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* "user interface" functions */
|
/* "user interface" functions */
|
||||||
|
@ -346,9 +363,9 @@ void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output)
|
||||||
Debug(1, "ids [0x%x]", st->id);
|
Debug(1, "ids [0x%x]", st->id);
|
||||||
if (lang)
|
if (lang)
|
||||||
Debug(1, "language (%s)", lang->value);
|
Debug(1, "language (%s)", lang->value);
|
||||||
Debug(1, "frames:%d, frame_size:%d stream timebase: %d/%d codec timebase: %d/%d",
|
Debug(1, "frames:%d, frame_size:%d stream timebase: %d/%d",
|
||||||
st->codec_info_nb_frames, codec->frame_size, st->time_base.num, st->time_base.den,
|
st->codec_info_nb_frames, codec->frame_size,
|
||||||
st->codec->time_base.num, st->codec->time_base.den
|
st->time_base.num, st->time_base.den
|
||||||
);
|
);
|
||||||
avcodec_string(buf, sizeof(buf), st->codec, is_output);
|
avcodec_string(buf, sizeof(buf), st->codec, is_output);
|
||||||
Debug(1, "codec: %s", buf);
|
Debug(1, "codec: %s", buf);
|
||||||
|
@ -367,17 +384,14 @@ void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output)
|
||||||
display_aspect_ratio.num, display_aspect_ratio.den);
|
display_aspect_ratio.num, display_aspect_ratio.den);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( st->codec->codec_type == AVMEDIA_TYPE_VIDEO ) {
|
if ( codec->codec_type == AVMEDIA_TYPE_VIDEO ) {
|
||||||
int fps = st->avg_frame_rate.den && st->avg_frame_rate.num;
|
int fps = st->avg_frame_rate.den && st->avg_frame_rate.num;
|
||||||
int tbn = st->time_base.den && st->time_base.num;
|
int tbn = st->time_base.den && st->time_base.num;
|
||||||
int tbc = st->codec->time_base.den && st->codec->time_base.num;
|
|
||||||
|
|
||||||
if (fps)
|
if (fps)
|
||||||
zm_log_fps(av_q2d(st->avg_frame_rate), "fps");
|
zm_log_fps(av_q2d(st->avg_frame_rate), "fps");
|
||||||
if (tbn)
|
if (tbn)
|
||||||
zm_log_fps(1 / av_q2d(st->time_base), "stream tb numerator");
|
zm_log_fps(1 / av_q2d(st->time_base), "stream tb numerator");
|
||||||
if (tbc)
|
|
||||||
zm_log_fps(1 / av_q2d(st->codec->time_base), "codec time base:");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (st->disposition & AV_DISPOSITION_DEFAULT)
|
if (st->disposition & AV_DISPOSITION_DEFAULT)
|
||||||
|
@ -424,6 +438,10 @@ unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src ) {
|
||||||
av_new_packet(dst,src->size);
|
av_new_packet(dst,src->size);
|
||||||
memcpy(dst->data, src->data, src->size);
|
memcpy(dst->data, src->data, src->size);
|
||||||
dst->flags = src->flags;
|
dst->flags = src->flags;
|
||||||
|
dst->pts = src->pts;
|
||||||
|
dst->dts = src->dts;
|
||||||
|
dst->duration = src->duration;
|
||||||
|
dst->stream_index = src->stream_index;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -443,6 +461,14 @@ bool is_video_stream( AVStream * stream ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_video_context( AVCodecContext *codec_context ) {
|
||||||
|
return
|
||||||
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||||
|
( codec_context->codec_type == AVMEDIA_TYPE_VIDEO );
|
||||||
|
#else
|
||||||
|
( codec_context->codec_type == CODEC_TYPE_VIDEO );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool is_audio_stream( AVStream * stream ) {
|
bool is_audio_stream( AVStream * stream ) {
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
|
@ -459,6 +485,15 @@ bool is_audio_stream( AVStream * stream ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_audio_context( AVCodecContext *codec_context ) {
|
||||||
|
return
|
||||||
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||||
|
( codec_context->codec_type == AVMEDIA_TYPE_AUDIO );
|
||||||
|
#else
|
||||||
|
( codec_context->codec_type == CODEC_TYPE_AUDIO );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet ) {
|
int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet ) {
|
||||||
int ret;
|
int ret;
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
|
@ -493,15 +528,20 @@ int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet
|
||||||
# else
|
# else
|
||||||
int frameComplete = 0;
|
int frameComplete = 0;
|
||||||
while ( !frameComplete ) {
|
while ( !frameComplete ) {
|
||||||
if ( (ret = zm_avcodec_decode_video( context, frame, &frameComplete, &packet )) < 0 ) {
|
if ( is_video_context(context) ) {
|
||||||
Error( "Unable to decode frame at frame: %s, continuing",
|
ret = zm_avcodec_decode_video(context, frame, &frameComplete, &packet);
|
||||||
av_make_error_string(ret).c_str() );
|
} else {
|
||||||
|
ret = avcodec_decode_audio4(context, frame, &frameComplete, &packet);
|
||||||
|
}
|
||||||
|
if ( ret < 0 ) {
|
||||||
|
Error("Unable to decode frame: %s", av_make_error_string(ret).c_str());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
} // end while !frameComplete
|
||||||
#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(AVStream *stream, AVPacket *pkt, const char *text) {
|
void dumpPacket(AVStream *stream, AVPacket *pkt, const char *text) {
|
||||||
char b[10240];
|
char b[10240];
|
||||||
|
|
||||||
|
|
|
@ -199,6 +199,7 @@ extern "C" {
|
||||||
|
|
||||||
/* A single function to initialize ffmpeg, to avoid multiple initializations */
|
/* A single function to initialize ffmpeg, to avoid multiple initializations */
|
||||||
void FFMPEGInit();
|
void FFMPEGInit();
|
||||||
|
void FFMPEGDeInit();
|
||||||
|
|
||||||
#if HAVE_LIBAVUTIL
|
#if HAVE_LIBAVUTIL
|
||||||
enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder);
|
enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder);
|
||||||
|
@ -331,8 +332,11 @@ unsigned int zm_av_packet_ref(AVPacket *dst, AVPacket *src);
|
||||||
|
|
||||||
int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt);
|
int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt);
|
||||||
|
|
||||||
bool is_video_stream( AVStream * stream );
|
bool is_video_stream(AVStream *);
|
||||||
bool is_audio_stream( AVStream * stream );
|
bool is_audio_stream(AVStream *);
|
||||||
|
bool is_video_context(AVCodec *);
|
||||||
|
bool is_audio_context(AVCodec *);
|
||||||
|
|
||||||
int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet );
|
int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet );
|
||||||
void dumpPacket(AVStream *, AVPacket *,const char *text="");
|
void dumpPacket(AVStream *, AVPacket *,const char *text="");
|
||||||
#ifndef HAVE_LIBSWRESAMPLE
|
#ifndef HAVE_LIBSWRESAMPLE
|
||||||
|
|
|
@ -144,7 +144,10 @@ FfmpegCamera::~FfmpegCamera() {
|
||||||
|
|
||||||
Close();
|
Close();
|
||||||
|
|
||||||
avformat_network_deinit();
|
if ( capture ) {
|
||||||
|
Terminate();
|
||||||
|
}
|
||||||
|
FFMPEGDeInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
int FfmpegCamera::PrimeCapture() {
|
int FfmpegCamera::PrimeCapture() {
|
||||||
|
@ -239,6 +242,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
} else {
|
} else {
|
||||||
Warning("Unknown method (%s)", method.c_str());
|
Warning("Unknown method (%s)", method.c_str());
|
||||||
}
|
}
|
||||||
|
//#av_dict_set(&opts, "timeout", "10000000", 0); // in microseconds.
|
||||||
|
|
||||||
if ( ret < 0 ) {
|
if ( ret < 0 ) {
|
||||||
Warning("Could not set rtsp_transport method '%s'", method.c_str());
|
Warning("Could not set rtsp_transport method '%s'", method.c_str());
|
||||||
|
@ -411,22 +415,21 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
|
|
||||||
// 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" );
|
ret = avcodec_open(mVideoCodecContext, mVideoCodec);
|
||||||
if ( avcodec_open(mVideoCodecContext, mVideoCodec) < 0 ){
|
|
||||||
#else
|
#else
|
||||||
Debug ( 1, "Calling video avcodec_open2" );
|
|
||||||
ret = avcodec_open2(mVideoCodecContext, mVideoCodec, &opts);
|
ret = avcodec_open2(mVideoCodecContext, mVideoCodec, &opts);
|
||||||
|
#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);
|
||||||
}
|
}
|
||||||
av_dict_free(&opts);
|
|
||||||
#endif
|
|
||||||
if ( ret < 0 ) {
|
if ( ret < 0 ) {
|
||||||
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);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} // end if success opening codec
|
zm_dump_codec(mVideoCodecContext);
|
||||||
|
}
|
||||||
|
|
||||||
if ( mVideoCodecContext->hwaccel != NULL ) {
|
if ( mVideoCodecContext->hwaccel != NULL ) {
|
||||||
Debug(1, "HWACCEL in use");
|
Debug(1, "HWACCEL in use");
|
||||||
|
@ -434,15 +437,23 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
Debug(1, "HWACCEL not in use");
|
Debug(1, "HWACCEL not in use");
|
||||||
}
|
}
|
||||||
if ( mAudioStreamId >= 0 ) {
|
if ( mAudioStreamId >= 0 ) {
|
||||||
|
if ( (mAudioCodec = avcodec_find_decoder(
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
mAudioCodecContext = avcodec_alloc_context3(NULL);
|
mFormatContext->streams[mAudioStreamId]->codecpar->codec_id
|
||||||
avcodec_parameters_to_context(mAudioCodecContext, mFormatContext->streams[mAudioStreamId]->codecpar);
|
|
||||||
#else
|
#else
|
||||||
mAudioCodecContext = mFormatContext->streams[mAudioStreamId]->codec;
|
mFormatContext->streams[mAudioStreamId]->codec->codec_id
|
||||||
#endif
|
#endif
|
||||||
if ( (mAudioCodec = avcodec_find_decoder(mAudioCodecContext->codec_id)) == NULL ) {
|
)) == NULL ) {
|
||||||
Debug(1, "Can't find codec for audio stream from %s", mPath.c_str());
|
Debug(1, "Can't find codec for audio stream from %s", mPath.c_str());
|
||||||
} else {
|
} else {
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
|
mAudioCodecContext = avcodec_alloc_context3(mAudioCodec);
|
||||||
|
avcodec_parameters_to_context( mAudioCodecContext, mFormatContext->streams[mAudioStreamId]->codecpar );
|
||||||
|
#else
|
||||||
|
mAudioCodecContext = mFormatContext->streams[mAudioStreamId]->codec;
|
||||||
|
// = avcodec_alloc_context3(mAudioCodec);
|
||||||
|
#endif
|
||||||
|
|
||||||
Debug(1, "Audio Found decoder");
|
Debug(1, "Audio Found decoder");
|
||||||
zm_dump_stream_format(mFormatContext, mAudioStreamId, 0, 0);
|
zm_dump_stream_format(mFormatContext, mAudioStreamId, 0, 0);
|
||||||
// Open the codec
|
// Open the codec
|
||||||
|
@ -483,6 +494,11 @@ int FfmpegCamera::Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ( videoStore ) {
|
||||||
|
delete videoStore;
|
||||||
|
videoStore = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if ( mVideoCodecContext ) {
|
if ( mVideoCodecContext ) {
|
||||||
avcodec_close(mVideoCodecContext);
|
avcodec_close(mVideoCodecContext);
|
||||||
Debug(1,"After codec close");
|
Debug(1,"After codec close");
|
||||||
|
@ -513,7 +529,7 @@ int FfmpegCamera::Close() {
|
||||||
|
|
||||||
int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
|
int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
|
||||||
//FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
//FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
||||||
|
//Debug(4, "FfmpegInterruptCallback");
|
||||||
return zm_terminate;
|
return zm_terminate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -164,9 +164,10 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id ) {
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
ret = avcodec_send_packet(context, &packet);
|
ret = avcodec_send_packet(context, &packet);
|
||||||
if ( ret < 0 ) {
|
if ( ret < 0 ) {
|
||||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||||
Error( "Unable to send packet at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
|
Error("Unable to send packet at frame %d: %s, continuing",
|
||||||
zm_av_packet_unref( &packet );
|
streams[packet.stream_index].frame_count, errbuf);
|
||||||
|
zm_av_packet_unref(&packet);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,38 +244,44 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id, double at ) {
|
||||||
seek_target = av_rescale_q(seek_target, AV_TIME_BASE_Q, input_format_context->streams[stream_id]->time_base);
|
seek_target = av_rescale_q(seek_target, AV_TIME_BASE_Q, input_format_context->streams[stream_id]->time_base);
|
||||||
Debug(1, "Getting frame from stream %d at %" PRId64, stream_id, seek_target);
|
Debug(1, "Getting frame from stream %d at %" PRId64, stream_id, seek_target);
|
||||||
|
|
||||||
if ( frame ) {
|
|
||||||
if ( (frame->pts + frame->pkt_duration) > seek_target ) {
|
|
||||||
// The current frame is still the valid picture.
|
|
||||||
Debug(2,"Returning previous frame which is still good");
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
if ( frame->pts < seek_target ) {
|
|
||||||
Debug(2, "Frame pts %" PRId64 " duration %" PRId64, frame->pts, frame->pkt_duration);
|
|
||||||
while ( frame && (frame->pts < seek_target) ) {
|
|
||||||
if ( ! get_frame(stream_id) )
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
if ( frame ) {
|
|
||||||
if ( ( ret = av_seek_frame(input_format_context, stream_id, seek_target, AVSEEK_FLAG_ANY) < 0 ) ) {
|
|
||||||
Error("Unable to seek in stream");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
if ( !frame ) {
|
||||||
// Must go for a keyframe
|
// Don't have a frame yet, so get a keyframe before the timestamp
|
||||||
if ( ( ret = av_seek_frame(input_format_context, stream_id, seek_target,
|
if ( ( ret = av_seek_frame(
|
||||||
AVSEEK_FLAG_FRAME
|
input_format_context, stream_id, seek_target, AVSEEK_FLAG_FRAME
|
||||||
) < 0 ) ) {
|
) < 0 ) ) {
|
||||||
Error("Unable to seek in stream");
|
Error("Unable to seek in stream");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
// Have to grab a frame to update our current frame to know where we are
|
||||||
|
get_frame(stream_id);
|
||||||
|
} // end if ! frame
|
||||||
|
|
||||||
|
if ( frame->pts > seek_target ) {
|
||||||
|
zm_dump_frame(frame, "frame->pts > seek_target, seek backwards");
|
||||||
|
// our frame must be beyond our seek target. so go backwards to before it
|
||||||
|
if ( ( ret = av_seek_frame(input_format_context, stream_id, seek_target,
|
||||||
|
AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME
|
||||||
|
) < 0 ) ) {
|
||||||
|
Error("Unable to seek in stream");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// Have to grab a frame to update our current frame to know where we are
|
||||||
|
get_frame(stream_id);
|
||||||
|
zm_dump_frame(frame, "frame->pts > seek_target, got");
|
||||||
|
} // end if frame->pts > seek_target
|
||||||
|
|
||||||
|
// Seeking seems to typically seek to a keyframe, so then we have to decode until we get the frame we want.
|
||||||
|
if ( frame->pts <= seek_target ) {
|
||||||
|
zm_dump_frame(frame, "pts <= seek_target");
|
||||||
|
while ( frame && (frame->pts < seek_target) ) {
|
||||||
|
if ( ! get_frame(stream_id) )
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
return get_frame(stream_id);
|
return get_frame(stream_id);
|
||||||
|
|
||||||
} // end AVFrame *FFmpeg_Input::get_frame( int stream_id, struct timeval at)
|
} // end AVFrame *FFmpeg_Input::get_frame( int stream_id, struct timeval at)
|
||||||
|
|
|
@ -97,7 +97,7 @@ int FileCamera::PreCapture() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileCamera::Capture( ZMPacket &zm_packet ) {
|
int FileCamera::Capture( ZMPacket &zm_packet ) {
|
||||||
return zm_packet.image->ReadJpeg( path, colours, subpixelorder ) ;
|
return zm_packet.image->ReadJpeg( path, colours, subpixelorder ) ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileCamera::PostCapture() {
|
int FileCamera::PostCapture() {
|
||||||
|
|
|
@ -253,8 +253,6 @@ void Logger::initialise(const std::string &id, const Options &options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::terminate() {
|
void Logger::terminate() {
|
||||||
Debug(1, "Terminating Logger");
|
|
||||||
|
|
||||||
if ( mFileLevel > NOLOG )
|
if ( mFileLevel > NOLOG )
|
||||||
closeFile();
|
closeFile();
|
||||||
|
|
||||||
|
@ -573,6 +571,7 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co
|
||||||
|
|
||||||
free(filecopy);
|
free(filecopy);
|
||||||
if ( level <= FATAL ) {
|
if ( level <= FATAL ) {
|
||||||
|
log_mutex.unlock();
|
||||||
logTerm();
|
logTerm();
|
||||||
zmDbClose();
|
zmDbClose();
|
||||||
if ( level <= PANIC )
|
if ( level <= PANIC )
|
||||||
|
|
|
@ -487,7 +487,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
||||||
// Change \n to actual line feeds
|
// Change \n to actual line feeds
|
||||||
char *token_ptr = label_format;
|
char *token_ptr = label_format;
|
||||||
const char *token_string = "\n";
|
const char *token_string = "\n";
|
||||||
while( ( token_ptr = strstr(token_ptr, token_string) ) ) {
|
while ( ( token_ptr = strstr(token_ptr, token_string) ) ) {
|
||||||
if ( *(token_ptr+1) ) {
|
if ( *(token_ptr+1) ) {
|
||||||
*token_ptr = '\n';
|
*token_ptr = '\n';
|
||||||
token_ptr++;
|
token_ptr++;
|
||||||
|
@ -762,10 +762,15 @@ Monitor *Monitor::Load(unsigned int p_id, bool load_zones, Purpose purpose) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if ( config.record_diag_images ) {
|
||||||
|
diag_path_r = stringtf("%s/%d/diag-r.jpg", storage->Path(), id);
|
||||||
|
diag_path_d = stringtf("%s/%d/diag-d.jpg", storage->Path(), id);
|
||||||
|
}
|
||||||
|
|
||||||
return monitor;
|
return monitor;
|
||||||
} // end Monitor *Monitor::Load(unsigned int p_id, bool load_zones, Purpose purpose)
|
} // end Monitor *Monitor::Load(unsigned int p_id, bool load_zones, Purpose purpose)
|
||||||
|
|
||||||
|
|
||||||
bool Monitor::connect() {
|
bool Monitor::connect() {
|
||||||
Debug(3, "Connecting to monitor. Purpose is %d", purpose );
|
Debug(3, "Connecting to monitor. Purpose is %d", purpose );
|
||||||
#if ZM_MEM_MAPPED
|
#if ZM_MEM_MAPPED
|
||||||
|
@ -1121,6 +1126,7 @@ unsigned int Monitor::GetLastWriteIndex() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t Monitor::GetLastEventId() const {
|
uint64_t Monitor::GetLastEventId() const {
|
||||||
|
#if 0
|
||||||
Debug(2, "mem_ptr(%x), size(%d) State(%d) last_read_index(%d) last_read_time(%d) last_event(%" PRIu64 ")",
|
Debug(2, "mem_ptr(%x), size(%d) State(%d) last_read_index(%d) last_read_time(%d) last_event(%" PRIu64 ")",
|
||||||
mem_ptr,
|
mem_ptr,
|
||||||
shared_data->size,
|
shared_data->size,
|
||||||
|
@ -1129,6 +1135,7 @@ uint64_t Monitor::GetLastEventId() const {
|
||||||
shared_data->last_read_time,
|
shared_data->last_read_time,
|
||||||
shared_data->last_event_id
|
shared_data->last_event_id
|
||||||
);
|
);
|
||||||
|
#endif
|
||||||
return shared_data->last_event_id;
|
return shared_data->last_event_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1189,11 +1196,12 @@ double Monitor::GetFPS() const {
|
||||||
|
|
||||||
/* I think this returns the # of micro seconds that we should sleep in order to maintain the desired analysis rate */
|
/* I think this returns the # of micro seconds that we should sleep in order to maintain the desired analysis rate */
|
||||||
useconds_t Monitor::GetAnalysisRate() {
|
useconds_t Monitor::GetAnalysisRate() {
|
||||||
|
<<<<<<< HEAD
|
||||||
capture_fps = GetFPS();
|
capture_fps = GetFPS();
|
||||||
if ( !analysis_fps_limit ) {
|
if ( !analysis_fps_limit ) {
|
||||||
return 0;
|
return 0;
|
||||||
} else if ( analysis_fps_limit > capture_fps ) {
|
} else if ( analysis_fps_limit > capture_fps ) {
|
||||||
Warning( "Analysis fps (%.2f) is greater than capturing fps (%.2f)", analysis_fps_limit, capture_fps );
|
Warning("Analysis fps (%.2f) is greater than capturing fps (%.2f)", analysis_fps_limit, capture_fps);
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return( ( 1000000 / analysis_fps_limit ) - ( 1000000 / capture_fps ) );
|
return( ( 1000000 / analysis_fps_limit ) - ( 1000000 / capture_fps ) );
|
||||||
|
@ -1204,10 +1212,10 @@ void Monitor::UpdateAdaptiveSkip() {
|
||||||
if ( config.opt_adaptive_skip ) {
|
if ( config.opt_adaptive_skip ) {
|
||||||
double capturing_fps = GetFPS();
|
double capturing_fps = GetFPS();
|
||||||
if ( adaptive_skip && analysis_fps && ( analysis_fps < capturing_fps ) ) {
|
if ( adaptive_skip && analysis_fps && ( analysis_fps < capturing_fps ) ) {
|
||||||
Info( "Analysis fps (%.2f) is lower than capturing fps (%.2f), disabling adaptive skip feature", analysis_fps, capturing_fps );
|
Info("Analysis fps (%.2f) is lower than capturing fps (%.2f), disabling adaptive skip feature", analysis_fps, capturing_fps);
|
||||||
adaptive_skip = false;
|
adaptive_skip = false;
|
||||||
} else if ( !adaptive_skip && ( !analysis_fps || ( analysis_fps >= capturing_fps ) ) ) {
|
} else if ( !adaptive_skip && ( !analysis_fps || ( analysis_fps >= capturing_fps ) ) ) {
|
||||||
Info( "Enabling adaptive skip feature" );
|
Info("Enabling adaptive skip feature");
|
||||||
adaptive_skip = true;
|
adaptive_skip = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1218,8 +1226,8 @@ void Monitor::UpdateAdaptiveSkip() {
|
||||||
void Monitor::ForceAlarmOn( int force_score, const char *force_cause, const char *force_text ) {
|
void Monitor::ForceAlarmOn( int force_score, const char *force_cause, const char *force_text ) {
|
||||||
trigger_data->trigger_state = TRIGGER_ON;
|
trigger_data->trigger_state = TRIGGER_ON;
|
||||||
trigger_data->trigger_score = force_score;
|
trigger_data->trigger_score = force_score;
|
||||||
strncpy( trigger_data->trigger_cause, force_cause, sizeof(trigger_data->trigger_cause)-1 );
|
strncpy(trigger_data->trigger_cause, force_cause, sizeof(trigger_data->trigger_cause)-1);
|
||||||
strncpy( trigger_data->trigger_text, force_text, sizeof(trigger_data->trigger_text)-1 );
|
strncpy(trigger_data->trigger_text, force_text, sizeof(trigger_data->trigger_text)-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Monitor::ForceAlarmOff() {
|
void Monitor::ForceAlarmOff() {
|
||||||
|
@ -1266,7 +1274,7 @@ void Monitor::actionResume() {
|
||||||
shared_data->action |= RESUME;
|
shared_data->action |= RESUME;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Monitor::actionBrightness( int p_brightness ) {
|
int Monitor::actionBrightness(int p_brightness) {
|
||||||
if ( purpose != CAPTURE ) {
|
if ( purpose != CAPTURE ) {
|
||||||
if ( p_brightness >= 0 ) {
|
if ( p_brightness >= 0 ) {
|
||||||
shared_data->brightness = p_brightness;
|
shared_data->brightness = p_brightness;
|
||||||
|
@ -1274,10 +1282,10 @@ int Monitor::actionBrightness( int p_brightness ) {
|
||||||
int wait_loops = 10;
|
int wait_loops = 10;
|
||||||
while ( shared_data->action & SET_SETTINGS ) {
|
while ( shared_data->action & SET_SETTINGS ) {
|
||||||
if ( wait_loops-- ) {
|
if ( wait_loops-- ) {
|
||||||
usleep( 100000 );
|
usleep(100000);
|
||||||
} else {
|
} else {
|
||||||
Warning( "Timed out waiting to set brightness" );
|
Warning("Timed out waiting to set brightness");
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1285,19 +1293,19 @@ int Monitor::actionBrightness( int p_brightness ) {
|
||||||
int wait_loops = 10;
|
int wait_loops = 10;
|
||||||
while ( shared_data->action & GET_SETTINGS ) {
|
while ( shared_data->action & GET_SETTINGS ) {
|
||||||
if ( wait_loops-- ) {
|
if ( wait_loops-- ) {
|
||||||
usleep( 100000 );
|
usleep(100000);
|
||||||
} else {
|
} else {
|
||||||
Warning( "Timed out waiting to get brightness" );
|
Warning("Timed out waiting to get brightness");
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return( shared_data->brightness );
|
return shared_data->brightness;
|
||||||
}
|
}
|
||||||
return( camera->Brightness( p_brightness ) );
|
return camera->Brightness(p_brightness);
|
||||||
}
|
} // end int Monitor::actionBrightness(int p_brightness)
|
||||||
|
|
||||||
int Monitor::actionContrast( int p_contrast ) {
|
int Monitor::actionContrast(int p_contrast) {
|
||||||
if ( purpose != CAPTURE ) {
|
if ( purpose != CAPTURE ) {
|
||||||
if ( p_contrast >= 0 ) {
|
if ( p_contrast >= 0 ) {
|
||||||
shared_data->contrast = p_contrast;
|
shared_data->contrast = p_contrast;
|
||||||
|
@ -1305,10 +1313,10 @@ int Monitor::actionContrast( int p_contrast ) {
|
||||||
int wait_loops = 10;
|
int wait_loops = 10;
|
||||||
while ( shared_data->action & SET_SETTINGS ) {
|
while ( shared_data->action & SET_SETTINGS ) {
|
||||||
if ( wait_loops-- ) {
|
if ( wait_loops-- ) {
|
||||||
usleep( 100000 );
|
usleep(100000);
|
||||||
} else {
|
} else {
|
||||||
Warning( "Timed out waiting to set contrast" );
|
Warning("Timed out waiting to set contrast");
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1316,19 +1324,19 @@ int Monitor::actionContrast( int p_contrast ) {
|
||||||
int wait_loops = 10;
|
int wait_loops = 10;
|
||||||
while ( shared_data->action & GET_SETTINGS ) {
|
while ( shared_data->action & GET_SETTINGS ) {
|
||||||
if ( wait_loops-- ) {
|
if ( wait_loops-- ) {
|
||||||
usleep( 100000 );
|
usleep(100000);
|
||||||
} else {
|
} else {
|
||||||
Warning( "Timed out waiting to get contrast" );
|
Warning("Timed out waiting to get contrast");
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return( shared_data->contrast );
|
return shared_data->contrast;
|
||||||
}
|
}
|
||||||
return( camera->Contrast( p_contrast ) );
|
return camera->Contrast(p_contrast);
|
||||||
}
|
} // end int Monitor::actionContrast(int p_contrast)
|
||||||
|
|
||||||
int Monitor::actionHue( int p_hue ) {
|
int Monitor::actionHue(int p_hue) {
|
||||||
if ( purpose != CAPTURE ) {
|
if ( purpose != CAPTURE ) {
|
||||||
if ( p_hue >= 0 ) {
|
if ( p_hue >= 0 ) {
|
||||||
shared_data->hue = p_hue;
|
shared_data->hue = p_hue;
|
||||||
|
@ -1336,10 +1344,10 @@ int Monitor::actionHue( int p_hue ) {
|
||||||
int wait_loops = 10;
|
int wait_loops = 10;
|
||||||
while ( shared_data->action & SET_SETTINGS ) {
|
while ( shared_data->action & SET_SETTINGS ) {
|
||||||
if ( wait_loops-- ) {
|
if ( wait_loops-- ) {
|
||||||
usleep( 100000 );
|
usleep(100000);
|
||||||
} else {
|
} else {
|
||||||
Warning( "Timed out waiting to set hue" );
|
Warning("Timed out waiting to set hue");
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1347,19 +1355,19 @@ int Monitor::actionHue( int p_hue ) {
|
||||||
int wait_loops = 10;
|
int wait_loops = 10;
|
||||||
while ( shared_data->action & GET_SETTINGS ) {
|
while ( shared_data->action & GET_SETTINGS ) {
|
||||||
if ( wait_loops-- ) {
|
if ( wait_loops-- ) {
|
||||||
usleep( 100000 );
|
usleep(100000);
|
||||||
} else {
|
} else {
|
||||||
Warning( "Timed out waiting to get hue" );
|
Warning("Timed out waiting to get hue");
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return( shared_data->hue );
|
return shared_data->hue;
|
||||||
}
|
}
|
||||||
return( camera->Hue( p_hue ) );
|
return camera->Hue(p_hue);
|
||||||
}
|
} // end int Monitor::actionHue(int p_hue)
|
||||||
|
|
||||||
int Monitor::actionColour( int p_colour ) {
|
int Monitor::actionColour(int p_colour) {
|
||||||
if ( purpose != CAPTURE ) {
|
if ( purpose != CAPTURE ) {
|
||||||
if ( p_colour >= 0 ) {
|
if ( p_colour >= 0 ) {
|
||||||
shared_data->colour = p_colour;
|
shared_data->colour = p_colour;
|
||||||
|
@ -1367,10 +1375,10 @@ int Monitor::actionColour( int p_colour ) {
|
||||||
int wait_loops = 10;
|
int wait_loops = 10;
|
||||||
while ( shared_data->action & SET_SETTINGS ) {
|
while ( shared_data->action & SET_SETTINGS ) {
|
||||||
if ( wait_loops-- ) {
|
if ( wait_loops-- ) {
|
||||||
usleep( 100000 );
|
usleep(100000);
|
||||||
} else {
|
} else {
|
||||||
Warning( "Timed out waiting to set colour" );
|
Warning("Timed out waiting to set colour");
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1378,19 +1386,19 @@ int Monitor::actionColour( int p_colour ) {
|
||||||
int wait_loops = 10;
|
int wait_loops = 10;
|
||||||
while ( shared_data->action & GET_SETTINGS ) {
|
while ( shared_data->action & GET_SETTINGS ) {
|
||||||
if ( wait_loops-- ) {
|
if ( wait_loops-- ) {
|
||||||
usleep( 100000 );
|
usleep(100000);
|
||||||
} else {
|
} else {
|
||||||
Warning( "Timed out waiting to get colour" );
|
Warning("Timed out waiting to get colour");
|
||||||
return( -1 );
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return( shared_data->colour );
|
return shared_data->colour;
|
||||||
}
|
}
|
||||||
return( camera->Colour( p_colour ) );
|
return camera->Colour(p_colour);
|
||||||
}
|
} // end int Monitor::actionColour(int p_colour)
|
||||||
|
|
||||||
void Monitor::DumpZoneImage( const char *zone_string ) {
|
void Monitor::DumpZoneImage(const char *zone_string) {
|
||||||
int exclude_id = 0;
|
int exclude_id = 0;
|
||||||
int extra_colour = 0;
|
int extra_colour = 0;
|
||||||
Polygon extra_zone;
|
Polygon extra_zone;
|
||||||
|
@ -1431,7 +1439,7 @@ void Monitor::DumpZoneImage( const char *zone_string ) {
|
||||||
zone_image->Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB);
|
zone_image->Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB);
|
||||||
}
|
}
|
||||||
|
|
||||||
for( int i = 0; i < n_zones; i++ ) {
|
for ( int i = 0; i < n_zones; i++ ) {
|
||||||
if ( exclude_id && (!extra_colour || extra_zone.getNumCoords()) && zones[i]->Id() == exclude_id )
|
if ( exclude_id && (!extra_colour || extra_zone.getNumCoords()) && zones[i]->Id() == exclude_id )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -1451,33 +1459,33 @@ void Monitor::DumpZoneImage( const char *zone_string ) {
|
||||||
colour = RGB_WHITE;
|
colour = RGB_WHITE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
zone_image->Fill( colour, 2, zones[i]->GetPolygon() );
|
zone_image->Fill(colour, 2, zones[i]->GetPolygon());
|
||||||
zone_image->Outline( colour, zones[i]->GetPolygon() );
|
zone_image->Outline(colour, zones[i]->GetPolygon());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( extra_zone.getNumCoords() ) {
|
if ( extra_zone.getNumCoords() ) {
|
||||||
zone_image->Fill( extra_colour, 2, extra_zone );
|
zone_image->Fill(extra_colour, 2, extra_zone);
|
||||||
zone_image->Outline( extra_colour, extra_zone );
|
zone_image->Outline(extra_colour, extra_zone);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char filename[PATH_MAX];
|
static char filename[PATH_MAX];
|
||||||
snprintf( filename, sizeof(filename), "Zones%d.jpg", id );
|
snprintf(filename, sizeof(filename), "Zones%d.jpg", id);
|
||||||
zone_image->WriteJpeg( filename );
|
zone_image->WriteJpeg(filename);
|
||||||
delete zone_image;
|
delete zone_image;
|
||||||
}
|
} // end void Monitor::DumpZoneImage(const char *zone_string)
|
||||||
|
|
||||||
void Monitor::DumpImage( Image *dump_image ) const {
|
void Monitor::DumpImage(Image *dump_image) const {
|
||||||
if ( image_count && !(image_count%10) ) {
|
if ( image_count && !(image_count%10) ) {
|
||||||
static char filename[PATH_MAX];
|
static char filename[PATH_MAX];
|
||||||
static char new_filename[PATH_MAX];
|
static char new_filename[PATH_MAX];
|
||||||
snprintf( filename, sizeof(filename), "Monitor%d.jpg", id );
|
snprintf(filename, sizeof(filename), "Monitor%d.jpg", id);
|
||||||
snprintf( new_filename, sizeof(new_filename), "Monitor%d-new.jpg", id );
|
snprintf(new_filename, sizeof(new_filename), "Monitor%d-new.jpg", id);
|
||||||
if ( dump_image->WriteJpeg( new_filename ) )
|
if ( dump_image->WriteJpeg(new_filename) )
|
||||||
rename( new_filename, filename );
|
rename(new_filename, filename);
|
||||||
}
|
}
|
||||||
}
|
} // end void Monitor::DumpImage(Image *dump_image)
|
||||||
|
|
||||||
bool Monitor::CheckSignal( const Image *image ) {
|
bool Monitor::CheckSignal(const Image *image) {
|
||||||
static bool static_undef = true;
|
static bool static_undef = true;
|
||||||
/* RGB24 colors */
|
/* RGB24 colors */
|
||||||
static uint8_t red_val;
|
static uint8_t red_val;
|
||||||
|
@ -1543,11 +1551,11 @@ bool Monitor::CheckSignal( const Image *image ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // end for < signal_check_points
|
} // end for < signal_check_points
|
||||||
Debug(1,"SignalCheck: %d points, colour_val(%d)", signal_check_points, colour_val);
|
Debug(1, "SignalCheck: %d points, colour_val(%d)", signal_check_points, colour_val);
|
||||||
return false;
|
return false;
|
||||||
} // end if signal_check_points
|
} // end if signal_check_points
|
||||||
return true;
|
return true;
|
||||||
}
|
} // end bool Monitor::CheckSignal(const Image *image)
|
||||||
|
|
||||||
void Monitor::CheckAction() {
|
void Monitor::CheckAction() {
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
|
@ -1698,6 +1706,10 @@ bool Monitor::Analyse() {
|
||||||
score += 100;
|
score += 100;
|
||||||
}
|
}
|
||||||
Warning("%s: %s", SIGNAL_CAUSE, signalText);
|
Warning("%s: %s", SIGNAL_CAUSE, signalText);
|
||||||
|
if ( event && !signal ) {
|
||||||
|
Info("%s: %03d - Closing event %" PRIu64 ", signal loss", name, image_count, event->Id());
|
||||||
|
closeEvent();
|
||||||
|
}
|
||||||
if ( !event ) {
|
if ( !event ) {
|
||||||
if ( cause.length() )
|
if ( cause.length() )
|
||||||
cause += ", ";
|
cause += ", ";
|
||||||
|
@ -1770,19 +1782,18 @@ bool Monitor::Analyse() {
|
||||||
|
|
||||||
if ( event ) {
|
if ( event ) {
|
||||||
Debug(2,"Have event in mocard");
|
Debug(2,"Have event in mocard");
|
||||||
if ( section_length ) {
|
if ( section_length
|
||||||
// TODO: Wouldn't this be clearer if we just did something like if now - event->start > section_length ?
|
&& ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length )
|
||||||
int section_mod = timestamp->tv_sec % section_length;
|
&& ( ! ( timestamp->tv_sec % section_length ) )
|
||||||
Debug(3, "Section length (%d) Last Section Mod(%d), tv_sec(%d) new section mod(%d)",
|
) {
|
||||||
section_length, last_section_mod, timestamp->tv_sec, section_mod);
|
|
||||||
// This is not clear, but basically due to pauses, etc we might not get section_mod == 0
|
Info("%s: %03d - Closing event %" PRIu64 ", section end forced %d - %d = %d >= %d",
|
||||||
if ( ( section_mod < last_section_mod ) && ( timestamp->tv_sec >= 10 ) ) {
|
name, image_count, event->Id(),
|
||||||
Info("%s: %03d - Closing event %llu, section end forced ", name, analysis_image_count, event->Id());
|
timestamp->tv_sec, video_store_data->recording.tv_sec,
|
||||||
closeEvent();
|
timestamp->tv_sec - video_store_data->recording.tv_sec,
|
||||||
last_section_mod = 0;
|
section_length
|
||||||
} else {
|
);
|
||||||
last_section_mod = section_mod;
|
closeEvent();
|
||||||
}
|
|
||||||
} // end if section_length
|
} // end if section_length
|
||||||
} // end if event
|
} // end if event
|
||||||
|
|
||||||
|
@ -1936,7 +1947,6 @@ bool Monitor::Analyse() {
|
||||||
closeEvent();
|
closeEvent();
|
||||||
}
|
}
|
||||||
shared_data->state = state = IDLE;
|
shared_data->state = state = IDLE;
|
||||||
last_section_mod = 0;
|
|
||||||
trigger_data->trigger_state = TRIGGER_CANCEL;
|
trigger_data->trigger_state = TRIGGER_CANCEL;
|
||||||
} // end if ( trigger_data->trigger_state != TRIGGER_OFF )
|
} // end if ( trigger_data->trigger_state != TRIGGER_OFF )
|
||||||
|
|
||||||
|
@ -1987,7 +1997,7 @@ void Monitor::Reload() {
|
||||||
Debug(1, "Reloading monitor %s", name);
|
Debug(1, "Reloading monitor %s", name);
|
||||||
|
|
||||||
if ( event ) {
|
if ( event ) {
|
||||||
Info( "%s: %03d - Closing event %" PRIu64 ", reloading", name, image_count, event->Id() );
|
Info("%s: %03d - Closing event %" PRIu64 ", reloading", name, image_count, event->Id());
|
||||||
closeEvent();
|
closeEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2040,9 +2050,9 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
|
||||||
char link_id_str[8];
|
char link_id_str[8];
|
||||||
char *dest_ptr = link_id_str;
|
char *dest_ptr = link_id_str;
|
||||||
const char *src_ptr = p_linked_monitors;
|
const char *src_ptr = p_linked_monitors;
|
||||||
while( 1 ) {
|
while ( 1 ) {
|
||||||
dest_ptr = link_id_str;
|
dest_ptr = link_id_str;
|
||||||
while( *src_ptr >= '0' && *src_ptr <= '9' ) {
|
while ( *src_ptr >= '0' && *src_ptr <= '9' ) {
|
||||||
if ( (dest_ptr-link_id_str) < (unsigned int)(sizeof(link_id_str)-1) ) {
|
if ( (dest_ptr-link_id_str) < (unsigned int)(sizeof(link_id_str)-1) ) {
|
||||||
*dest_ptr++ = *src_ptr++;
|
*dest_ptr++ = *src_ptr++;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2120,7 +2130,7 @@ int Monitor::LoadMonitors(std::string sql, Monitor **&monitors, Purpose purpose)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int n_monitors = mysql_num_rows(result);
|
int n_monitors = mysql_num_rows(result);
|
||||||
Debug( 1, "Got %d monitors", n_monitors );
|
Debug(1, "Got %d monitors", n_monitors);
|
||||||
delete[] monitors;
|
delete[] monitors;
|
||||||
monitors = new Monitor *[n_monitors];
|
monitors = new Monitor *[n_monitors];
|
||||||
for( int i=0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
|
for( int i=0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
|
||||||
|
@ -2135,7 +2145,7 @@ int Monitor::LoadMonitors(std::string sql, Monitor **&monitors, Purpose purpose)
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
|
|
||||||
return n_monitors;
|
return n_monitors;
|
||||||
}
|
} // end int Monitor::LoadMonitors(std::string sql, Monitor **&monitors, Purpose purpose)
|
||||||
|
|
||||||
#if ZM_HAS_V4L
|
#if ZM_HAS_V4L
|
||||||
int Monitor::LoadLocalMonitors(const char *device, Monitor **&monitors, Purpose purpose) {
|
int Monitor::LoadLocalMonitors(const char *device, Monitor **&monitors, Purpose purpose) {
|
||||||
|
@ -2147,7 +2157,7 @@ int Monitor::LoadLocalMonitors(const char *device, Monitor **&monitors, Purpose
|
||||||
if ( staticConfig.SERVER_ID )
|
if ( staticConfig.SERVER_ID )
|
||||||
sql += stringtf(" AND ServerId=%d", staticConfig.SERVER_ID);
|
sql += stringtf(" AND ServerId=%d", staticConfig.SERVER_ID);
|
||||||
return LoadMonitors(sql, monitors, purpose);
|
return LoadMonitors(sql, monitors, purpose);
|
||||||
}
|
} // end int Monitor::LoadLocalMonitors(const char *device, Monitor **&monitors, Purpose purpose)
|
||||||
#endif // ZM_HAS_V4L
|
#endif // ZM_HAS_V4L
|
||||||
|
|
||||||
int Monitor::LoadRemoteMonitors(const char *protocol, const char *host, const char *port, const char *path, Monitor **&monitors, Purpose purpose) {
|
int Monitor::LoadRemoteMonitors(const char *protocol, const char *host, const char *port, const char *path, Monitor **&monitors, Purpose purpose) {
|
||||||
|
@ -2158,17 +2168,17 @@ int Monitor::LoadRemoteMonitors(const char *protocol, const char *host, const ch
|
||||||
if ( protocol )
|
if ( protocol )
|
||||||
sql += stringtf(" AND Protocol = '%s' and Host = '%s' and Port = '%s' and Path = '%s'", protocol, host, port, path);
|
sql += stringtf(" AND Protocol = '%s' and Host = '%s' and Port = '%s' and Path = '%s'", protocol, host, port, path);
|
||||||
return LoadMonitors(sql, monitors, purpose);
|
return LoadMonitors(sql, monitors, purpose);
|
||||||
}
|
} // end int Monitor::LoadRemoteMonitors
|
||||||
|
|
||||||
int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ) {
|
int Monitor::LoadFileMonitors(const char *file, Monitor **&monitors, Purpose purpose) {
|
||||||
std::string sql = load_monitor_sql + " WHERE Function != 'None' AND Type = 'File'";
|
std::string sql = load_monitor_sql + " WHERE Function != 'None' AND Type = 'File'";
|
||||||
if ( file[0] )
|
if ( file[0] )
|
||||||
sql += " AND Path='" + std::string(file) + "'";
|
sql += " AND Path='" + std::string(file) + "'";
|
||||||
if ( staticConfig.SERVER_ID ) {
|
if ( staticConfig.SERVER_ID ) {
|
||||||
sql += stringtf( " AND ServerId=%d", staticConfig.SERVER_ID );
|
sql += stringtf(" AND ServerId=%d", staticConfig.SERVER_ID);
|
||||||
}
|
}
|
||||||
return LoadMonitors(sql, monitors, purpose);
|
return LoadMonitors(sql, monitors, purpose);
|
||||||
}
|
} // end int Monitor::LoadFileMonitors
|
||||||
|
|
||||||
#if HAVE_LIBAVFORMAT
|
#if HAVE_LIBAVFORMAT
|
||||||
int Monitor::LoadFfmpegMonitors(const char *file, Monitor **&monitors, Purpose purpose) {
|
int Monitor::LoadFfmpegMonitors(const char *file, Monitor **&monitors, Purpose purpose) {
|
||||||
|
@ -2180,7 +2190,7 @@ int Monitor::LoadFfmpegMonitors(const char *file, Monitor **&monitors, Purpose p
|
||||||
sql += stringtf(" AND ServerId=%d", staticConfig.SERVER_ID);
|
sql += stringtf(" AND ServerId=%d", staticConfig.SERVER_ID);
|
||||||
}
|
}
|
||||||
return LoadMonitors(sql, monitors, purpose);
|
return LoadMonitors(sql, monitors, purpose);
|
||||||
}
|
} // end int Monitor::LoadFfmpegMonitors
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
||||||
/* Returns 0 on success, even if no new images are available (transient error)
|
/* Returns 0 on success, even if no new images are available (transient error)
|
||||||
|
@ -2385,10 +2395,10 @@ int Monitor::Capture() {
|
||||||
shared_data->action &= ~GET_SETTINGS;
|
shared_data->action &= ~GET_SETTINGS;
|
||||||
}
|
}
|
||||||
if ( shared_data->action & SET_SETTINGS ) {
|
if ( shared_data->action & SET_SETTINGS ) {
|
||||||
camera->Brightness( shared_data->brightness );
|
camera->Brightness(shared_data->brightness);
|
||||||
camera->Hue( shared_data->hue );
|
camera->Hue(shared_data->hue);
|
||||||
camera->Colour( shared_data->colour );
|
camera->Colour(shared_data->colour);
|
||||||
camera->Contrast( shared_data->contrast );
|
camera->Contrast(shared_data->contrast);
|
||||||
shared_data->action &= ~SET_SETTINGS;
|
shared_data->action &= ~SET_SETTINGS;
|
||||||
}
|
}
|
||||||
return captureResult;
|
return captureResult;
|
||||||
|
@ -2425,12 +2435,12 @@ void Monitor::TimestampImage( Image *ts_image, const struct timeval *ts_time ) c
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*d_ptr++ = *s_ptr++;
|
|
||||||
}
|
}
|
||||||
*d_ptr = '\0';
|
*d_ptr++ = *s_ptr++;
|
||||||
ts_image->Annotate( label_text, label_coord, label_size );
|
} // end while
|
||||||
}
|
*d_ptr = '\0';
|
||||||
}
|
ts_image->Annotate( label_text, label_coord, label_size );
|
||||||
|
} // end void Monitor::TimestampImage
|
||||||
|
|
||||||
bool Monitor::closeEvent() {
|
bool Monitor::closeEvent() {
|
||||||
if ( event ) {
|
if ( event ) {
|
||||||
|
@ -2448,24 +2458,22 @@ bool Monitor::closeEvent() {
|
||||||
event = NULL;
|
event = NULL;
|
||||||
delete e;
|
delete e;
|
||||||
e = NULL;
|
e = NULL;
|
||||||
}, event);
|
}, event);
|
||||||
#else
|
#else
|
||||||
delete event;
|
delete event;
|
||||||
event = NULL;
|
event = NULL;
|
||||||
#endif
|
#endif
|
||||||
video_store_data->recording = (struct timeval){0};
|
video_store_data->recording = (struct timeval){0};
|
||||||
return true;
|
return true;
|
||||||
}
|
} // end bool Monitor::closeEvent()
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &zoneSet ) {
|
unsigned int Monitor::DetectMotion(const Image &comp_image, Event::StringSet &zoneSet) {
|
||||||
bool alarm = false;
|
bool alarm = false;
|
||||||
unsigned int score = 0;
|
unsigned int score = 0;
|
||||||
|
|
||||||
if ( n_zones <= 0 ) return alarm;
|
if ( n_zones <= 0 ) return alarm;
|
||||||
|
|
||||||
Storage *storage = this->getStorage();
|
ref_image.Delta(comp_image, &delta_image);
|
||||||
|
|
||||||
if ( config.record_diag_images ) {
|
if ( config.record_diag_images ) {
|
||||||
static char diag_path[PATH_MAX] = "";
|
static char diag_path[PATH_MAX] = "";
|
||||||
|
@ -2489,14 +2497,14 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z
|
||||||
for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) {
|
for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) {
|
||||||
Zone *zone = zones[n_zone];
|
Zone *zone = zones[n_zone];
|
||||||
// need previous alarmed state for preclusive zone, so don't clear just yet
|
// need previous alarmed state for preclusive zone, so don't clear just yet
|
||||||
if (!zone->IsPreclusive())
|
if ( !zone->IsPreclusive() )
|
||||||
zone->ClearAlarm();
|
zone->ClearAlarm();
|
||||||
if ( !zone->IsInactive() ) {
|
if ( !zone->IsInactive() ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Debug(3, "Blanking inactive zone %s", zone->Label());
|
Debug(3, "Blanking inactive zone %s", zone->Label());
|
||||||
delta_image.Fill(RGB_BLACK, zone->GetPolygon());
|
delta_image.Fill(RGB_BLACK, zone->GetPolygon());
|
||||||
}
|
} // end foreach zone
|
||||||
|
|
||||||
// Check preclusive zones first
|
// Check preclusive zones first
|
||||||
for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) {
|
for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) {
|
||||||
|
@ -2506,30 +2514,32 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z
|
||||||
}
|
}
|
||||||
int old_zone_score = zone->Score();
|
int old_zone_score = zone->Score();
|
||||||
bool old_zone_alarmed = zone->Alarmed();
|
bool old_zone_alarmed = zone->Alarmed();
|
||||||
Debug( 3, "Checking preclusive zone %s - old score: %d, state: %s", zone->Label(),old_zone_score, zone->Alarmed()?"alarmed":"quiet" );
|
Debug(3, "Checking preclusive zone %s - old score: %d, state: %s",
|
||||||
if ( zone->CheckAlarms( &delta_image ) ) {
|
zone->Label(),old_zone_score, zone->Alarmed()?"alarmed":"quiet");
|
||||||
|
if ( zone->CheckAlarms(&delta_image) ) {
|
||||||
alarm = true;
|
alarm = true;
|
||||||
score += zone->Score();
|
score += zone->Score();
|
||||||
zone->SetAlarm();
|
zone->SetAlarm();
|
||||||
Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() );
|
Debug(3, "Zone is alarmed, zone score = %d", zone->Score());
|
||||||
zoneSet.insert( zone->Label() );
|
zoneSet.insert(zone->Label());
|
||||||
//zone->ResetStats();
|
//zone->ResetStats();
|
||||||
} else {
|
} else {
|
||||||
// check if end of alarm
|
// check if end of alarm
|
||||||
if (old_zone_alarmed) {
|
if ( old_zone_alarmed ) {
|
||||||
Debug(3, "Preclusive Zone %s alarm Ends. Previous score: %d", zone->Label(), old_zone_score);
|
Debug(3, "Preclusive Zone %s alarm Ends. Previous score: %d",
|
||||||
if (old_zone_score > 0) {
|
zone->Label(), old_zone_score);
|
||||||
|
if ( old_zone_score > 0 ) {
|
||||||
zone->SetExtendAlarmCount(zone->GetExtendAlarmFrames());
|
zone->SetExtendAlarmCount(zone->GetExtendAlarmFrames());
|
||||||
}
|
}
|
||||||
if (zone->CheckExtendAlarmCount()) {
|
if ( zone->CheckExtendAlarmCount() ) {
|
||||||
alarm=true;
|
alarm = true;
|
||||||
zone->SetAlarm();
|
zone->SetAlarm();
|
||||||
} else {
|
} else {
|
||||||
zone->ClearAlarm();
|
zone->ClearAlarm();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // end if CheckAlarms
|
||||||
}
|
} // end foreach zone
|
||||||
|
|
||||||
Coord alarm_centre;
|
Coord alarm_centre;
|
||||||
int top_score = -1;
|
int top_score = -1;
|
||||||
|
@ -2544,13 +2554,13 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z
|
||||||
if ( !zone->IsActive() || zone->IsPreclusive()) {
|
if ( !zone->IsActive() || zone->IsPreclusive()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Debug( 3, "Checking active zone %s", zone->Label() );
|
Debug(3, "Checking active zone %s", zone->Label());
|
||||||
if ( zone->CheckAlarms( &delta_image ) ) {
|
if ( zone->CheckAlarms(&delta_image) ) {
|
||||||
alarm = true;
|
alarm = true;
|
||||||
score += zone->Score();
|
score += zone->Score();
|
||||||
zone->SetAlarm();
|
zone->SetAlarm();
|
||||||
Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() );
|
Debug(3, "Zone is alarmed, zone score = %d", zone->Score());
|
||||||
zoneSet.insert( zone->Label() );
|
zoneSet.insert(zone->Label());
|
||||||
if ( config.opt_control && track_motion ) {
|
if ( config.opt_control && track_motion ) {
|
||||||
if ( (int)zone->Score() > top_score ) {
|
if ( (int)zone->Score() > top_score ) {
|
||||||
top_score = zone->Score();
|
top_score = zone->Score();
|
||||||
|
@ -2558,7 +2568,7 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // end foreach zone
|
||||||
|
|
||||||
if ( alarm ) {
|
if ( alarm ) {
|
||||||
for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) {
|
for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) {
|
||||||
|
@ -2567,12 +2577,12 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z
|
||||||
if ( !zone->IsInclusive() ) {
|
if ( !zone->IsInclusive() ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Debug( 3, "Checking inclusive zone %s", zone->Label() );
|
Debug(3, "Checking inclusive zone %s", zone->Label());
|
||||||
if ( zone->CheckAlarms( &delta_image ) ) {
|
if ( zone->CheckAlarms(&delta_image) ) {
|
||||||
alarm = true;
|
alarm = true;
|
||||||
score += zone->Score();
|
score += zone->Score();
|
||||||
zone->SetAlarm();
|
zone->SetAlarm();
|
||||||
Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() );
|
Debug(3, "Zone is alarmed, zone score = %d", zone->Score());
|
||||||
zoneSet.insert( zone->Label() );
|
zoneSet.insert( zone->Label() );
|
||||||
if ( config.opt_control && track_motion ) {
|
if ( config.opt_control && track_motion ) {
|
||||||
if ( zone->Score() > (unsigned int)top_score ) {
|
if ( zone->Score() > (unsigned int)top_score ) {
|
||||||
|
@ -2580,8 +2590,8 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z
|
||||||
alarm_centre = zone->GetAlarmCentre();
|
alarm_centre = zone->GetAlarmCentre();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // end if CheckAlarm
|
||||||
}
|
} // end foreach zone
|
||||||
} else {
|
} else {
|
||||||
// Find all alarm pixels in exclusive zones
|
// Find all alarm pixels in exclusive zones
|
||||||
for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) {
|
for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) {
|
||||||
|
@ -2589,32 +2599,33 @@ unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &z
|
||||||
if ( !zone->IsExclusive() ) {
|
if ( !zone->IsExclusive() ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Debug( 3, "Checking exclusive zone %s", zone->Label() );
|
Debug(3, "Checking exclusive zone %s", zone->Label());
|
||||||
if ( zone->CheckAlarms( &delta_image ) ) {
|
if ( zone->CheckAlarms(&delta_image) ) {
|
||||||
alarm = true;
|
alarm = true;
|
||||||
score += zone->Score();
|
score += zone->Score();
|
||||||
zone->SetAlarm();
|
zone->SetAlarm();
|
||||||
Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() );
|
Debug(3, "Zone is alarmed, zone score = %d", zone->Score());
|
||||||
zoneSet.insert( zone->Label() );
|
zoneSet.insert(zone->Label());
|
||||||
}
|
}
|
||||||
}
|
} // end foreach zone
|
||||||
} // end if alarm or not
|
} // end if alarm or not
|
||||||
}
|
} // end if alarm
|
||||||
|
|
||||||
if ( top_score > 0 ) {
|
if ( top_score > 0 ) {
|
||||||
shared_data->alarm_x = alarm_centre.X();
|
shared_data->alarm_x = alarm_centre.X();
|
||||||
shared_data->alarm_y = alarm_centre.Y();
|
shared_data->alarm_y = alarm_centre.Y();
|
||||||
|
|
||||||
Info( "Got alarm centre at %d,%d, at count %d", shared_data->alarm_x, shared_data->alarm_y, analysis_image_count );
|
Info("Got alarm centre at %d,%d, at count %d",
|
||||||
|
shared_data->alarm_x, shared_data->alarm_y, analysis_image_count);
|
||||||
} else {
|
} else {
|
||||||
shared_data->alarm_x = shared_data->alarm_y = -1;
|
shared_data->alarm_x = shared_data->alarm_y = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a small and innocent hack to prevent scores of 0 being returned in alarm state
|
// This is a small and innocent hack to prevent scores of 0 being returned in alarm state
|
||||||
return score?score:alarm;
|
return score ? score : alarm;
|
||||||
}
|
} // end MotionDetect
|
||||||
|
|
||||||
bool Monitor::DumpSettings( char *output, bool verbose ) {
|
bool Monitor::DumpSettings(char *output, bool verbose) {
|
||||||
output[0] = 0;
|
output[0] = 0;
|
||||||
|
|
||||||
sprintf( output+strlen(output), "Id : %d\n", id );
|
sprintf( output+strlen(output), "Id : %d\n", id );
|
||||||
|
@ -2720,9 +2731,9 @@ void Monitor::get_ref_image() {
|
||||||
snap->unlock();
|
snap->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Group *> Monitor::Groups() {
|
std::vector<Group *> Monitor::Groups() {
|
||||||
// At the moment, only load groups once.
|
// At the moment, only load groups once.
|
||||||
if ( ! groups.size() ) {
|
if ( !groups.size() ) {
|
||||||
std::string sql = stringtf(
|
std::string sql = stringtf(
|
||||||
"SELECT Id,ParentId,Name FROM Groups WHERE Groups.Id IN "
|
"SELECT Id,ParentId,Name FROM Groups WHERE Groups.Id IN "
|
||||||
"(SELECT GroupId FROM Groups_Monitors WHERE MonitorId=%d)",id);
|
"(SELECT GroupId FROM Groups_Monitors WHERE MonitorId=%d)",id);
|
||||||
|
@ -2742,14 +2753,13 @@ std::vector<Group *> Monitor::Groups() {
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
}
|
}
|
||||||
return groups;
|
return groups;
|
||||||
}
|
} // end Monitor::Groups()
|
||||||
|
|
||||||
StringVector Monitor::GroupNames() {
|
StringVector Monitor::GroupNames() {
|
||||||
StringVector groupnames;
|
StringVector groupnames;
|
||||||
for(Group * g: Groups()) {
|
for ( Group * g: Groups() ) {
|
||||||
groupnames.push_back(std::string(g->Name()));
|
groupnames.push_back(std::string(g->Name()));
|
||||||
Debug(1,"Groups: %s", g->Name());
|
Debug(1,"Groups: %s", g->Name());
|
||||||
}
|
}
|
||||||
return groupnames;
|
return groupnames;
|
||||||
}
|
} // end Monitor::GroupNames()
|
||||||
|
|
||||||
|
|
|
@ -311,10 +311,12 @@ protected:
|
||||||
|
|
||||||
unsigned int last_camera_bytes;
|
unsigned int last_camera_bytes;
|
||||||
|
|
||||||
Image delta_image;
|
Image delta_image;
|
||||||
Image ref_image;
|
Image ref_image;
|
||||||
Image alarm_image; // Used in creating analysis images, will be initialized in Analysis
|
Image alarm_image; // Used in creating analysis images, will be initialized in Analysis
|
||||||
Image write_image; // Used when creating snapshot images
|
Image write_image; // Used when creating snapshot images
|
||||||
|
std::string diag_path_r;
|
||||||
|
std::string diag_path_d;
|
||||||
|
|
||||||
Purpose purpose; // What this monitor has been created to do
|
Purpose purpose; // What this monitor has been created to do
|
||||||
int event_count;
|
int event_count;
|
||||||
|
@ -337,15 +339,15 @@ protected:
|
||||||
EventCloseMode event_close_mode;
|
EventCloseMode event_close_mode;
|
||||||
|
|
||||||
#if ZM_MEM_MAPPED
|
#if ZM_MEM_MAPPED
|
||||||
int map_fd;
|
int map_fd;
|
||||||
char mem_file[PATH_MAX];
|
char mem_file[PATH_MAX];
|
||||||
#else // ZM_MEM_MAPPED
|
#else // ZM_MEM_MAPPED
|
||||||
int shm_id;
|
int shm_id;
|
||||||
#endif // ZM_MEM_MAPPED
|
#endif // ZM_MEM_MAPPED
|
||||||
off_t mem_size;
|
off_t mem_size;
|
||||||
unsigned char *mem_ptr;
|
unsigned char *mem_ptr;
|
||||||
SharedData *shared_data;
|
SharedData *shared_data;
|
||||||
TriggerData *trigger_data;
|
TriggerData *trigger_data;
|
||||||
VideoStoreData *video_store_data;
|
VideoStoreData *video_store_data;
|
||||||
|
|
||||||
struct timeval *shared_timestamps;
|
struct timeval *shared_timestamps;
|
||||||
|
|
|
@ -255,7 +255,7 @@ void zm_packetqueue::clearQueue() {
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
ZMPacket *packet = NULL;
|
ZMPacket *packet = NULL;
|
||||||
int delete_count = 0;
|
int delete_count = 0;
|
||||||
while(!pktQueue.empty()) {
|
while ( !pktQueue.empty() ) {
|
||||||
packet = pktQueue.front();
|
packet = pktQueue.front();
|
||||||
packet_counts[packet->packet.stream_index] -= 1;
|
packet_counts[packet->packet.stream_index] -= 1;
|
||||||
pktQueue.pop_front();
|
pktQueue.pop_front();
|
||||||
|
@ -288,7 +288,6 @@ ZMPacket *zm_packetqueue::get_analysis_packet() {
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
while ( ((! pktQueue.size()) || ( analysis_it == pktQueue.end() )) && !zm_terminate ) {
|
while ( ((! pktQueue.size()) || ( analysis_it == pktQueue.end() )) && !zm_terminate ) {
|
||||||
Debug(2,"waiting. Queue size %d analysis_it == end? %d", pktQueue.size(), ( analysis_it == pktQueue.end() ) );
|
Debug(2,"waiting. Queue size %d analysis_it == end? %d", pktQueue.size(), ( analysis_it == pktQueue.end() ) );
|
||||||
|
|
||||||
condition->wait();
|
condition->wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,29 +54,34 @@ bool StreamBase::loadMonitor(int monitor_id) {
|
||||||
|
|
||||||
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;
|
|
||||||
frame_mod = 1;
|
frame_mod = 1;
|
||||||
if ( !fps ) {
|
if ( (fps < 0) || !fps || isinf(fps) ) {
|
||||||
Debug(1, "Zero fps in updateFrameRate. Setting frame_mod=1 and effective_fps=0.0");
|
Debug(1, "Zero or negative fps %f in updateFrameRate. Setting frame_mod=1 and effective_fps=0.0", fps);
|
||||||
effective_fps = 0.0;
|
effective_fps = 0.0;
|
||||||
|
base_fps = 0.0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
base_fps = fps;
|
||||||
effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE;
|
effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE;
|
||||||
Debug(3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod);
|
frame_mod = 1;
|
||||||
|
Debug(3, "FPS:%.2f, MaxFPS:%.2f, BaseFPS:%.2f, EffectiveFPS:%.2f, FrameMod:%d, replay_rate(%d)",
|
||||||
|
fps, maxfps, base_fps, effective_fps, frame_mod, replay_rate);
|
||||||
// Min frame repeat?
|
// Min frame repeat?
|
||||||
while( effective_fps > maxfps ) {
|
// We want to keep the frame skip easy... problem is ... if effective = 31 and max = 30 then we end up with 15.5 fps.
|
||||||
|
while ( effective_fps > maxfps ) {
|
||||||
effective_fps /= 2.0;
|
effective_fps /= 2.0;
|
||||||
frame_mod *= 2;
|
frame_mod *= 2;
|
||||||
Debug(3, "EffectiveFPS:%.2f, FrameMod:%d", effective_fps, frame_mod);
|
Debug(3, "Changing fps to be < max %.2f EffectiveFPS:%.2f, FrameMod:%d",
|
||||||
|
maxfps, effective_fps, frame_mod);
|
||||||
}
|
}
|
||||||
}
|
} // void StreamBase::updateFrameRate(double fps)
|
||||||
|
|
||||||
bool StreamBase::checkCommandQueue() {
|
bool StreamBase::checkCommandQueue() {
|
||||||
if ( sd >= 0 ) {
|
if ( sd >= 0 ) {
|
||||||
|
@ -352,7 +357,7 @@ void StreamBase::openComms() {
|
||||||
|
|
||||||
gettimeofday(&last_comm_update, NULL);
|
gettimeofday(&last_comm_update, NULL);
|
||||||
} // end if connKey > 0
|
} // end if connKey > 0
|
||||||
Debug(3, "comms open");
|
Debug(3, "comms open at %s", loc_sock_path);
|
||||||
} // end void StreamBase::openComms()
|
} // end void StreamBase::openComms()
|
||||||
|
|
||||||
void StreamBase::closeComms() {
|
void StreamBase::closeComms() {
|
||||||
|
|
|
@ -160,6 +160,7 @@ public:
|
||||||
scale = DEFAULT_SCALE;
|
scale = DEFAULT_SCALE;
|
||||||
}
|
}
|
||||||
void setStreamReplayRate( int p_rate ) {
|
void setStreamReplayRate( int p_rate ) {
|
||||||
|
Debug(2,"Setting replay_rate %d", p_rate);
|
||||||
replay_rate = p_rate;
|
replay_rate = p_rate;
|
||||||
}
|
}
|
||||||
void setStreamMaxFPS( double p_maxfps ) {
|
void setStreamMaxFPS( double p_maxfps ) {
|
||||||
|
|
|
@ -249,15 +249,19 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) {
|
||||||
|
|
||||||
//Function to check Username length
|
//Function to check Username length
|
||||||
bool checkUser ( const char *username) {
|
bool checkUser ( const char *username) {
|
||||||
if ( strlen(username) > 32) {
|
if ( ! username )
|
||||||
return false;
|
return false;
|
||||||
}
|
if ( strlen(username) > 32 )
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//Function to check password length
|
//Function to check password length
|
||||||
bool checkPass (const char *password) {
|
bool checkPass (const char *password) {
|
||||||
if ( strlen(password) > 64) {
|
if ( !password )
|
||||||
return false;
|
return false;
|
||||||
}
|
if ( strlen(password) > 64 )
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -42,9 +42,11 @@ int audio_in_stream_index;
|
||||||
|
|
||||||
AVCodec *video_out_codec;
|
AVCodec *video_out_codec;
|
||||||
AVCodecContext *video_out_ctx;
|
AVCodecContext *video_out_ctx;
|
||||||
|
AVStream *video_out_stream;
|
||||||
|
|
||||||
AVStream *video_in_stream;
|
AVStream *video_in_stream;
|
||||||
AVStream *audio_in_stream;
|
|
||||||
|
AVStream *audio_in_stream;
|
||||||
|
|
||||||
// Move this into the object so that we aren't constantly allocating/deallocating it on the stack
|
// Move this into the object so that we aren't constantly allocating/deallocating it on the stack
|
||||||
AVPacket opkt;
|
AVPacket opkt;
|
||||||
|
@ -63,6 +65,7 @@ int audio_in_stream_index;
|
||||||
unsigned int frame_count;
|
unsigned int frame_count;
|
||||||
|
|
||||||
// The following are used when encoding the audio stream to AAC
|
// The following are used when encoding the audio stream to AAC
|
||||||
|
AVStream *audio_out_stream;
|
||||||
AVCodec *audio_out_codec;
|
AVCodec *audio_out_codec;
|
||||||
AVCodecContext *audio_out_ctx;
|
AVCodecContext *audio_out_ctx;
|
||||||
#ifdef HAVE_LIBSWRESAMPLE
|
#ifdef HAVE_LIBSWRESAMPLE
|
||||||
|
@ -98,6 +101,7 @@ int audio_in_stream_index;
|
||||||
int64_t audio_next_dts;
|
int64_t audio_next_dts;
|
||||||
|
|
||||||
bool setup_resampler();
|
bool setup_resampler();
|
||||||
|
int resample_audio();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VideoStore(
|
VideoStore(
|
||||||
|
|
|
@ -292,7 +292,7 @@ int main(int argc, char *argv[]) {
|
||||||
struct DeltaTimeval delta_time;
|
struct DeltaTimeval delta_time;
|
||||||
|
|
||||||
while ( !zm_terminate ) {
|
while ( !zm_terminate ) {
|
||||||
sigprocmask(SIG_BLOCK, &block_set, 0);
|
//sigprocmask(SIG_BLOCK, &block_set, 0);
|
||||||
for ( int i = 0; i < n_monitors; i++ ) {
|
for ( int i = 0; i < n_monitors; i++ ) {
|
||||||
|
|
||||||
monitors[i]->CheckAction();
|
monitors[i]->CheckAction();
|
||||||
|
@ -345,8 +345,6 @@ int main(int argc, char *argv[]) {
|
||||||
last_capture_times[i] = now;
|
last_capture_times[i] = now;
|
||||||
|
|
||||||
} // end foreach n_monitors
|
} // end foreach n_monitors
|
||||||
//Debug(2,"unblocking");
|
|
||||||
sigprocmask(SIG_UNBLOCK, &block_set, 0);
|
|
||||||
if ( result < 0 ) {
|
if ( result < 0 ) {
|
||||||
// Failure, try reconnecting
|
// Failure, try reconnecting
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
|
45
src/zms.cpp
45
src/zms.cpp
|
@ -85,6 +85,9 @@ int main( int argc, const char *argv[] ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
zmLoadConfig();
|
zmLoadConfig();
|
||||||
|
char log_id_string[32] = "zms";
|
||||||
|
logInit( log_id_string );
|
||||||
|
Debug(1,"rate %d", rate);
|
||||||
|
|
||||||
const char *query = getenv("QUERY_STRING");
|
const char *query = getenv("QUERY_STRING");
|
||||||
if ( query ) {
|
if ( query ) {
|
||||||
|
@ -95,7 +98,7 @@ int main( int argc, const char *argv[] ) {
|
||||||
char *q_ptr = temp_query;
|
char *q_ptr = temp_query;
|
||||||
char *parms[16]; // Shouldn't be more than this
|
char *parms[16]; // Shouldn't be more than this
|
||||||
int parm_no = 0;
|
int parm_no = 0;
|
||||||
while( (parm_no < 16) && (parms[parm_no] = strtok(q_ptr, "&")) ) {
|
while ( (parm_no < 16) && (parms[parm_no] = strtok(q_ptr, "&")) ) {
|
||||||
parm_no++;
|
parm_no++;
|
||||||
q_ptr = NULL;
|
q_ptr = NULL;
|
||||||
}
|
}
|
||||||
|
@ -130,6 +133,7 @@ int main( int argc, const char *argv[] ) {
|
||||||
scale = atoi( value );
|
scale = atoi( value );
|
||||||
} else if ( !strcmp( name, "rate" ) ) {
|
} else if ( !strcmp( name, "rate" ) ) {
|
||||||
rate = atoi( value );
|
rate = atoi( value );
|
||||||
|
Debug(2,"Setting rate to %d from %s", rate, value);
|
||||||
} else if ( !strcmp( name, "maxfps" ) ) {
|
} else if ( !strcmp( name, "maxfps" ) ) {
|
||||||
maxfps = atof( value );
|
maxfps = atof( value );
|
||||||
} else if ( !strcmp( name, "bitrate" ) ) {
|
} else if ( !strcmp( name, "bitrate" ) ) {
|
||||||
|
@ -152,34 +156,21 @@ int main( int argc, const char *argv[] ) {
|
||||||
connkey = atoi(value);
|
connkey = atoi(value);
|
||||||
} else if ( !strcmp( name, "buffer" ) ) {
|
} else if ( !strcmp( name, "buffer" ) ) {
|
||||||
playback_buffer = atoi(value);
|
playback_buffer = atoi(value);
|
||||||
} else if ( config.opt_use_auth ) {
|
} else if ( !strcmp( name, "auth" ) ) {
|
||||||
if ( strcmp( config.auth_relay, "none" ) == 0 ) {
|
strncpy( auth, value, sizeof(auth)-1 );
|
||||||
if ( !strcmp( name, "user" ) ) {
|
} else if ( !strcmp( name, "user" ) ) {
|
||||||
username = value;
|
username = UriDecode( value );
|
||||||
}
|
} else if ( !strcmp( name, "pass" ) ) {
|
||||||
} else {
|
password = UriDecode(value);
|
||||||
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
|
Debug(1, "Have %s for password", password.c_str());
|
||||||
{
|
} else {
|
||||||
if ( !strcmp( name, "auth" ) ) {
|
Debug(1, "Unknown parameter passed to zms %s=%s", name, value);
|
||||||
strncpy( auth, value, sizeof(auth)-1 );
|
} // end if possible parameter names
|
||||||
}
|
|
||||||
}
|
|
||||||
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
|
|
||||||
{
|
|
||||||
if ( !strcmp( name, "user" ) ) {
|
|
||||||
username = UriDecode( value );
|
|
||||||
}
|
|
||||||
if ( !strcmp( name, "pass" ) ) {
|
|
||||||
password = UriDecode( value );
|
|
||||||
Debug( 1, "Have %s for password", password.c_str() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // end foreach parm
|
} // end foreach parm
|
||||||
|
} else {
|
||||||
|
Fatal("No query string.");
|
||||||
} // end if query
|
} // end if query
|
||||||
|
|
||||||
char log_id_string[32] = "zms";
|
|
||||||
if ( monitor_id ) {
|
if ( monitor_id ) {
|
||||||
snprintf(log_id_string, sizeof(log_id_string), "zms_m%d", monitor_id);
|
snprintf(log_id_string, sizeof(log_id_string), "zms_m%d", monitor_id);
|
||||||
} else {
|
} else {
|
||||||
|
@ -323,5 +314,5 @@ int main( int argc, const char *argv[] ) {
|
||||||
logTerm();
|
logTerm();
|
||||||
zmDbClose();
|
zmDbClose();
|
||||||
|
|
||||||
return( 0 );
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
224
src/zmu.cpp
224
src/zmu.cpp
|
@ -394,7 +394,7 @@ int main(int argc, char *argv[]) {
|
||||||
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
} // end getopt loop
|
||||||
|
|
||||||
if ( optind < argc ) {
|
if ( optind < argc ) {
|
||||||
fprintf(stderr, "Extraneous options, ");
|
fprintf(stderr, "Extraneous options, ");
|
||||||
|
@ -425,50 +425,44 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
if ( config.opt_use_auth ) {
|
if ( config.opt_use_auth ) {
|
||||||
if ( strcmp(config.auth_relay, "none") == 0 ) {
|
if ( strcmp(config.auth_relay, "none") == 0 ) {
|
||||||
if ( !checkUser(username)) {
|
|
||||||
fprintf(stderr, "Error, username greater than allowed 32 characters\n");
|
|
||||||
exit_zmu(-1);
|
|
||||||
}
|
|
||||||
if ( !username ) {
|
if ( !username ) {
|
||||||
fprintf(stderr, "Error, username must be supplied\n");
|
Error("Username must be supplied");
|
||||||
exit_zmu(-1);
|
exit_zmu(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( username ) {
|
|
||||||
user = zmLoadUser(username);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ( !(username && password) && !auth ) {
|
|
||||||
fprintf(stderr, "Error, username and password or auth string must be supplied\n");
|
|
||||||
exit_zmu(-1);
|
|
||||||
}
|
|
||||||
if ( !checkUser(username)) {
|
if ( !checkUser(username)) {
|
||||||
fprintf(stderr, "Error, username greater than allowed 32 characters\n");
|
Error("Username greater than allowed 32 characters");
|
||||||
exit_zmu(-1);
|
exit_zmu(-1);
|
||||||
}
|
}
|
||||||
if ( !checkPass(password)) {
|
|
||||||
fprintf(stderr, "Error, password greater than allowed 64 characters\n");
|
user = zmLoadUser(username);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if ( !(username && password) && !auth ) {
|
||||||
|
Error("Username and password or auth string must be supplied");
|
||||||
exit_zmu(-1);
|
exit_zmu(-1);
|
||||||
}
|
}
|
||||||
//if ( strcmp( config.auth_relay, "hashed" ) == 0 )
|
if ( auth ) {
|
||||||
{
|
user = zmLoadAuthUser(auth, false);
|
||||||
if ( auth ) {
|
|
||||||
user = zmLoadAuthUser(auth, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//else if ( strcmp( config.auth_relay, "plain" ) == 0 )
|
if ( username && password ) {
|
||||||
{
|
if ( !checkUser(username)) {
|
||||||
if ( username && password ) {
|
Error("username greater than allowed 32 characters");
|
||||||
user = zmLoadUser(username, password);
|
exit_zmu(-1);
|
||||||
}
|
}
|
||||||
}
|
if ( !checkPass(password)) {
|
||||||
}
|
Error("password greater than allowed 64 characters");
|
||||||
|
exit_zmu(-1);
|
||||||
|
}
|
||||||
|
user = zmLoadUser(username, password);
|
||||||
|
} // end if username && password
|
||||||
|
} // end if relay or not
|
||||||
if ( !user ) {
|
if ( !user ) {
|
||||||
fprintf(stderr, "Error, unable to authenticate user\n");
|
Error("Unable to authenticate user");
|
||||||
return exit_zmu(-1);
|
exit_zmu(-1);
|
||||||
}
|
}
|
||||||
if ( !ValidateAccess(user, mon_id, function) ) {
|
if ( !ValidateAccess(user, mon_id, function) ) {
|
||||||
fprintf(stderr, "Error, insufficient privileges for requested action\n");
|
Error("Insufficient privileges for requested action");
|
||||||
exit_zmu(-1);
|
exit_zmu(-1);
|
||||||
}
|
}
|
||||||
} // end if auth
|
} // end if auth
|
||||||
|
@ -503,201 +497,201 @@ int main(int argc, char *argv[]) {
|
||||||
if ( verbose ) {
|
if ( verbose ) {
|
||||||
char timestamp_str[64] = "None";
|
char timestamp_str[64] = "None";
|
||||||
if ( timestamp.tv_sec )
|
if ( timestamp.tv_sec )
|
||||||
strftime( timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", localtime( ×tamp.tv_sec ) );
|
strftime(timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", localtime(×tamp.tv_sec));
|
||||||
if ( image_idx == -1 )
|
if ( image_idx == -1 )
|
||||||
printf( "Time of last image capture: %s.%02ld\n", timestamp_str, timestamp.tv_usec/10000 );
|
printf("Time of last image capture: %s.%02ld\n", timestamp_str, timestamp.tv_usec/10000);
|
||||||
else
|
else
|
||||||
printf( "Time of image %d capture: %s.%02ld\n", image_idx, timestamp_str, timestamp.tv_usec/10000 );
|
printf("Time of image %d capture: %s.%02ld\n", image_idx, timestamp_str, timestamp.tv_usec/10000);
|
||||||
} else {
|
} else {
|
||||||
if ( have_output ) printf( "%c", separator );
|
if ( have_output ) printf("%c", separator);
|
||||||
printf( "%ld.%02ld", timestamp.tv_sec, timestamp.tv_usec/10000 );
|
printf("%ld.%02ld", timestamp.tv_sec, timestamp.tv_usec/10000);
|
||||||
have_output = true;
|
have_output = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( function & ZMU_READ_IDX ) {
|
if ( function & ZMU_READ_IDX ) {
|
||||||
if ( verbose )
|
if ( verbose )
|
||||||
printf( "Last read index: %d\n", monitor->GetLastReadIndex() );
|
printf("Last read index: %d\n", monitor->GetLastReadIndex());
|
||||||
else {
|
else {
|
||||||
if ( have_output ) printf( "%c", separator );
|
if ( have_output ) printf("%c", separator);
|
||||||
printf( "%d", monitor->GetLastReadIndex() );
|
printf("%d", monitor->GetLastReadIndex());
|
||||||
have_output = true;
|
have_output = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( function & ZMU_WRITE_IDX ) {
|
if ( function & ZMU_WRITE_IDX ) {
|
||||||
if ( verbose )
|
if ( verbose ) {
|
||||||
printf( "Last write index: %d\n", monitor->GetLastWriteIndex() );
|
printf("Last write index: %d\n", monitor->GetLastWriteIndex());
|
||||||
else {
|
} else {
|
||||||
if ( have_output ) printf( "%c", separator );
|
if ( have_output ) printf("%c", separator);
|
||||||
printf( "%d", monitor->GetLastWriteIndex() );
|
printf("%d", monitor->GetLastWriteIndex());
|
||||||
have_output = true;
|
have_output = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( function & ZMU_EVENT ) {
|
if ( function & ZMU_EVENT ) {
|
||||||
if ( verbose )
|
if ( verbose ) {
|
||||||
printf( "Last event id: %" PRIu64 "\n", monitor->GetLastEventId() );
|
printf("Last event id: %" PRIu64 "\n", monitor->GetLastEventId());
|
||||||
else {
|
} else {
|
||||||
if ( have_output ) printf( "%c", separator );
|
if ( have_output ) printf("%c", separator);
|
||||||
printf( "%" PRIu64, monitor->GetLastEventId() );
|
printf("%" PRIu64, monitor->GetLastEventId());
|
||||||
have_output = true;
|
have_output = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( function & ZMU_FPS ) {
|
if ( function & ZMU_FPS ) {
|
||||||
if ( verbose )
|
if ( verbose )
|
||||||
printf( "Current capture rate: %.2f frames per second\n", monitor->GetFPS() );
|
printf("Current capture rate: %.2f frames per second\n", monitor->GetFPS());
|
||||||
else {
|
else {
|
||||||
if ( have_output ) printf( "%c", separator );
|
if ( have_output ) printf("%c", separator);
|
||||||
printf( "%.2f", monitor->GetFPS() );
|
printf("%.2f", monitor->GetFPS());
|
||||||
have_output = true;
|
have_output = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( function & ZMU_IMAGE ) {
|
if ( function & ZMU_IMAGE ) {
|
||||||
if ( verbose ) {
|
if ( verbose ) {
|
||||||
if ( image_idx == -1 )
|
if ( image_idx == -1 )
|
||||||
printf( "Dumping last image captured to Monitor%d.jpg", monitor->Id() );
|
printf("Dumping last image captured to Monitor%d.jpg", monitor->Id());
|
||||||
else
|
else
|
||||||
printf( "Dumping buffer image %d to Monitor%d.jpg", image_idx, monitor->Id() );
|
printf("Dumping buffer image %d to Monitor%d.jpg", image_idx, monitor->Id());
|
||||||
if ( scale != -1 )
|
if ( scale != -1 )
|
||||||
printf( ", scaling by %d%%", scale );
|
printf(", scaling by %d%%", scale);
|
||||||
printf( "\n" );
|
printf("\n");
|
||||||
}
|
}
|
||||||
monitor->GetImage( image_idx, scale>0?scale:100 );
|
monitor->GetImage(image_idx, scale>0?scale:100);
|
||||||
}
|
}
|
||||||
if ( function & ZMU_ZONES ) {
|
if ( function & ZMU_ZONES ) {
|
||||||
if ( verbose )
|
if ( verbose )
|
||||||
printf( "Dumping zone image to Zones%d.jpg\n", monitor->Id() );
|
printf("Dumping zone image to Zones%d.jpg\n", monitor->Id());
|
||||||
monitor->DumpZoneImage( zoneString );
|
monitor->DumpZoneImage(zoneString);
|
||||||
}
|
}
|
||||||
if ( function & ZMU_ALARM ) {
|
if ( function & ZMU_ALARM ) {
|
||||||
if ( verbose )
|
if ( verbose )
|
||||||
printf( "Forcing alarm on\n" );
|
printf("Forcing alarm on\n");
|
||||||
monitor->ForceAlarmOn( config.forced_alarm_score, "Forced Web" );
|
monitor->ForceAlarmOn(config.forced_alarm_score, "Forced Web");
|
||||||
while ( monitor->GetState() != Monitor::ALARM ) {
|
while ( monitor->GetState() != Monitor::ALARM ) {
|
||||||
// Wait for monitor to notice.
|
// Wait for monitor to notice.
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
}
|
}
|
||||||
printf( "Alarmed event id: %" PRIu64 "\n", monitor->GetLastEventId() );
|
printf("Alarmed event id: %" PRIu64 "\n", monitor->GetLastEventId());
|
||||||
}
|
}
|
||||||
if ( function & ZMU_NOALARM ) {
|
if ( function & ZMU_NOALARM ) {
|
||||||
if ( verbose )
|
if ( verbose )
|
||||||
printf( "Forcing alarm off\n" );
|
printf("Forcing alarm off\n");
|
||||||
monitor->ForceAlarmOff();
|
monitor->ForceAlarmOff();
|
||||||
}
|
}
|
||||||
if ( function & ZMU_CANCEL ) {
|
if ( function & ZMU_CANCEL ) {
|
||||||
if ( verbose )
|
if ( verbose )
|
||||||
printf( "Cancelling forced alarm on/off\n" );
|
printf("Cancelling forced alarm on/off\n");
|
||||||
monitor->CancelForced();
|
monitor->CancelForced();
|
||||||
}
|
}
|
||||||
if ( function & ZMU_RELOAD ) {
|
if ( function & ZMU_RELOAD ) {
|
||||||
if ( verbose )
|
if ( verbose )
|
||||||
printf( "Reloading monitor settings\n" );
|
printf("Reloading monitor settings\n");
|
||||||
monitor->actionReload();
|
monitor->actionReload();
|
||||||
}
|
}
|
||||||
if ( function & ZMU_ENABLE ) {
|
if ( function & ZMU_ENABLE ) {
|
||||||
if ( verbose )
|
if ( verbose )
|
||||||
printf( "Enabling event generation\n" );
|
printf("Enabling event generation\n");
|
||||||
monitor->actionEnable();
|
monitor->actionEnable();
|
||||||
}
|
}
|
||||||
if ( function & ZMU_DISABLE ) {
|
if ( function & ZMU_DISABLE ) {
|
||||||
if ( verbose )
|
if ( verbose )
|
||||||
printf( "Disabling event generation\n" );
|
printf("Disabling event generation\n");
|
||||||
monitor->actionDisable();
|
monitor->actionDisable();
|
||||||
}
|
}
|
||||||
if ( function & ZMU_SUSPEND ) {
|
if ( function & ZMU_SUSPEND ) {
|
||||||
if ( verbose )
|
if ( verbose )
|
||||||
printf( "Suspending event generation\n" );
|
printf("Suspending event generation\n");
|
||||||
monitor->actionSuspend();
|
monitor->actionSuspend();
|
||||||
}
|
}
|
||||||
if ( function & ZMU_RESUME ) {
|
if ( function & ZMU_RESUME ) {
|
||||||
if ( verbose )
|
if ( verbose )
|
||||||
printf( "Resuming event generation\n" );
|
printf("Resuming event generation\n");
|
||||||
monitor->actionResume();
|
monitor->actionResume();
|
||||||
}
|
}
|
||||||
if ( function & ZMU_QUERY ) {
|
if ( function & ZMU_QUERY ) {
|
||||||
char monString[16382] = "";
|
char monString[16382] = "";
|
||||||
monitor->DumpSettings( monString, verbose );
|
monitor->DumpSettings(monString, verbose);
|
||||||
printf( "%s\n", monString );
|
printf("%s\n", monString);
|
||||||
}
|
}
|
||||||
if ( function & ZMU_BRIGHTNESS ) {
|
if ( function & ZMU_BRIGHTNESS ) {
|
||||||
if ( verbose ) {
|
if ( verbose ) {
|
||||||
if ( brightness >= 0 )
|
if ( brightness >= 0 )
|
||||||
printf( "New brightness: %d\n", monitor->actionBrightness( brightness ) );
|
printf("New brightness: %d\n", monitor->actionBrightness(brightness));
|
||||||
else
|
else
|
||||||
printf( "Current brightness: %d\n", monitor->actionBrightness() );
|
printf("Current brightness: %d\n", monitor->actionBrightness());
|
||||||
} else {
|
} else {
|
||||||
if ( have_output ) printf( "%c", separator );
|
if ( have_output ) printf("%c", separator);
|
||||||
if ( brightness >= 0 )
|
if ( brightness >= 0 )
|
||||||
printf( "%d", monitor->actionBrightness( brightness ) );
|
printf("%d", monitor->actionBrightness(brightness));
|
||||||
else
|
else
|
||||||
printf( "%d", monitor->actionBrightness() );
|
printf("%d", monitor->actionBrightness());
|
||||||
have_output = true;
|
have_output = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( function & ZMU_CONTRAST ) {
|
if ( function & ZMU_CONTRAST ) {
|
||||||
if ( verbose ) {
|
if ( verbose ) {
|
||||||
if ( contrast >= 0 )
|
if ( contrast >= 0 )
|
||||||
printf( "New brightness: %d\n", monitor->actionContrast( contrast ) );
|
printf("New brightness: %d\n", monitor->actionContrast(contrast));
|
||||||
else
|
else
|
||||||
printf( "Current contrast: %d\n", monitor->actionContrast() );
|
printf("Current contrast: %d\n", monitor->actionContrast());
|
||||||
} else {
|
} else {
|
||||||
if ( have_output ) printf( "%c", separator );
|
if ( have_output ) printf("%c", separator);
|
||||||
if ( contrast >= 0 )
|
if ( contrast >= 0 )
|
||||||
printf( "%d", monitor->actionContrast( contrast ) );
|
printf("%d", monitor->actionContrast(contrast));
|
||||||
else
|
else
|
||||||
printf( "%d", monitor->actionContrast() );
|
printf("%d", monitor->actionContrast());
|
||||||
have_output = true;
|
have_output = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( function & ZMU_HUE ) {
|
if ( function & ZMU_HUE ) {
|
||||||
if ( verbose ) {
|
if ( verbose ) {
|
||||||
if ( hue >= 0 )
|
if ( hue >= 0 )
|
||||||
printf( "New hue: %d\n", monitor->actionHue( hue ) );
|
printf("New hue: %d\n", monitor->actionHue(hue));
|
||||||
else
|
else
|
||||||
printf( "Current hue: %d\n", monitor->actionHue() );
|
printf("Current hue: %d\n", monitor->actionHue());
|
||||||
} else {
|
} else {
|
||||||
if ( have_output ) printf( "%c", separator );
|
if ( have_output ) printf("%c", separator);
|
||||||
if ( hue >= 0 )
|
if ( hue >= 0 )
|
||||||
printf( "%d", monitor->actionHue( hue ) );
|
printf("%d", monitor->actionHue(hue));
|
||||||
else
|
else
|
||||||
printf( "%d", monitor->actionHue() );
|
printf("%d", monitor->actionHue());
|
||||||
have_output = true;
|
have_output = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( function & ZMU_COLOUR ) {
|
if ( function & ZMU_COLOUR ) {
|
||||||
if ( verbose ) {
|
if ( verbose ) {
|
||||||
if ( colour >= 0 )
|
if ( colour >= 0 )
|
||||||
printf( "New colour: %d\n", monitor->actionColour( colour ) );
|
printf("New colour: %d\n", monitor->actionColour(colour));
|
||||||
else
|
else
|
||||||
printf( "Current colour: %d\n", monitor->actionColour() );
|
printf("Current colour: %d\n", monitor->actionColour());
|
||||||
} else {
|
} else {
|
||||||
if ( have_output ) printf( "%c", separator );
|
if ( have_output ) printf("%c", separator);
|
||||||
if ( colour >= 0 )
|
if ( colour >= 0 )
|
||||||
printf( "%d", monitor->actionColour( colour ) );
|
printf("%d", monitor->actionColour(colour));
|
||||||
else
|
else
|
||||||
printf( "%d", monitor->actionColour() );
|
printf("%d", monitor->actionColour());
|
||||||
have_output = true;
|
have_output = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( have_output ) {
|
if ( have_output ) {
|
||||||
printf( "\n" );
|
printf("\n");
|
||||||
}
|
}
|
||||||
if ( !function ) {
|
if ( !function ) {
|
||||||
Usage();
|
Usage();
|
||||||
}
|
}
|
||||||
delete monitor;
|
delete monitor;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Error, invalid monitor id %d\n", mon_id);
|
Error("Invalid monitor id %d", mon_id);
|
||||||
exit_zmu(-1);
|
exit_zmu(-1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ( function & ZMU_QUERY ) {
|
if ( function & ZMU_QUERY ) {
|
||||||
#if ZM_HAS_V4L
|
#if ZM_HAS_V4L
|
||||||
char vidString[0x10000] = "";
|
char vidString[0x10000] = "";
|
||||||
bool ok = LocalCamera::GetCurrentSettings( device, vidString, v4lVersion, verbose );
|
bool ok = LocalCamera::GetCurrentSettings(device, vidString, v4lVersion, verbose);
|
||||||
printf( "%s", vidString );
|
printf("%s", vidString);
|
||||||
exit_zmu( ok?0:-1 );
|
exit_zmu(ok ? 0 : -1);
|
||||||
#else // ZM_HAS_V4L
|
#else // ZM_HAS_V4L
|
||||||
fprintf( stderr, "Error, video4linux is required for device querying\n" );
|
Error("Video4linux is required for device querying");
|
||||||
exit_zmu( -1 );
|
exit_zmu(-1);
|
||||||
#endif // ZM_HAS_V4L
|
#endif // ZM_HAS_V4L
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -708,25 +702,25 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
sql += " order by Id asc";
|
sql += " order by Id asc";
|
||||||
|
|
||||||
if ( mysql_query( &dbconn, sql.c_str() ) ) {
|
if ( mysql_query(&dbconn, sql.c_str()) ) {
|
||||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||||
exit_zmu( mysql_errno( &dbconn ) );
|
exit_zmu(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_zmu( mysql_errno( &dbconn ) );
|
exit_zmu(mysql_errno(&dbconn));
|
||||||
}
|
}
|
||||||
Debug( 1, "Got %d monitors", mysql_num_rows( result ) );
|
Debug(1, "Got %d monitors", mysql_num_rows(result));
|
||||||
|
|
||||||
printf( "%4s%5s%6s%9s%14s%6s%6s%8s%8s\n", "Id", "Func", "State", "TrgState", "LastImgTim", "RdIdx", "WrIdx", "LastEvt", "FrmRate" );
|
printf("%4s%5s%6s%9s%14s%6s%6s%8s%8s\n", "Id", "Func", "State", "TrgState", "LastImgTim", "RdIdx", "WrIdx", "LastEvt", "FrmRate");
|
||||||
for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) {
|
for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
|
||||||
int mon_id = atoi(dbrow[0]);
|
int mon_id = atoi(dbrow[0]);
|
||||||
int function = atoi(dbrow[1]);
|
int function = atoi(dbrow[1]);
|
||||||
if ( !user || user->canAccess( mon_id ) ) {
|
if ( !user || user->canAccess(mon_id) ) {
|
||||||
if ( function > 1 ) {
|
if ( function > 1 ) {
|
||||||
Monitor *monitor = Monitor::Load( mon_id, false, Monitor::QUERY );
|
Monitor *monitor = Monitor::Load(mon_id, false, Monitor::QUERY);
|
||||||
if ( monitor && monitor->connect() ) {
|
if ( monitor && monitor->connect() ) {
|
||||||
struct timeval tv = monitor->GetTimestamp();
|
struct timeval tv = monitor->GetTimestamp();
|
||||||
printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8" PRIu64 "%8.2f\n",
|
printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8" PRIu64 "%8.2f\n",
|
||||||
|
@ -744,7 +738,7 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
struct timeval tv = { 0, 0 };
|
struct timeval tv = { 0, 0 };
|
||||||
printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n",
|
printf("%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n",
|
||||||
mon_id,
|
mon_id,
|
||||||
function,
|
function,
|
||||||
0,
|
0,
|
||||||
|
@ -755,11 +749,11 @@ int main(int argc, char *argv[]) {
|
||||||
0,
|
0,
|
||||||
0.0
|
0.0
|
||||||
);
|
);
|
||||||
}
|
} // end if function filter
|
||||||
}
|
} // endif !user || canAccess(mon_id)
|
||||||
}
|
} // end foreach row
|
||||||
mysql_free_result( result );
|
mysql_free_result(result);
|
||||||
}
|
} // end if function && ZMU_LIST
|
||||||
}
|
}
|
||||||
delete user;
|
delete user;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
error_reporting(E_ERROR);
|
||||||
|
|
||||||
if ( empty($_REQUEST['id']) && empty($_REQUEST['eids']) ) {
|
if ( empty($_REQUEST['id']) && empty($_REQUEST['eids']) ) {
|
||||||
ajaxError('No event id(s) supplied');
|
ajaxError('No event id(s) supplied');
|
||||||
|
@ -6,160 +7,144 @@ if ( empty($_REQUEST['id']) && empty($_REQUEST['eids']) ) {
|
||||||
|
|
||||||
if ( canView('Events') ) {
|
if ( canView('Events') ) {
|
||||||
switch ( $_REQUEST['action'] ) {
|
switch ( $_REQUEST['action'] ) {
|
||||||
case 'video' :
|
case 'video' :
|
||||||
{
|
if ( empty($_REQUEST['videoFormat']) ) {
|
||||||
if ( empty($_REQUEST['videoFormat']) ) {
|
ajaxError('Video Generation Failure, no format given');
|
||||||
ajaxError('Video Generation Failure, no format given');
|
} elseif ( empty($_REQUEST['rate']) ) {
|
||||||
} elseif ( empty($_REQUEST['rate']) ) {
|
ajaxError('Video Generation Failure, no rate given');
|
||||||
ajaxError('Video Generation Failure, no rate given');
|
} elseif ( empty($_REQUEST['scale']) ) {
|
||||||
} elseif ( empty($_REQUEST['scale']) ) {
|
ajaxError('Video Generation Failure, no scale given');
|
||||||
ajaxError('Video Generation Failure, no scale given');
|
} else {
|
||||||
} else {
|
$sql = 'SELECT E.*,M.Name AS MonitorName,M.DefaultRate,M.DefaultScale FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE E.Id = ?'.monitorLimitSql();
|
||||||
$sql = 'SELECT E.*,M.Name AS MonitorName,M.DefaultRate,M.DefaultScale FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE E.Id = ?'.monitorLimitSql();
|
if ( !($event = dbFetchOne($sql, NULL, array( $_REQUEST['id']))) ) {
|
||||||
if ( !($event = dbFetchOne($sql, NULL, array( $_REQUEST['id']))) ) {
|
ajaxError('Video Generation Failure, Unable to load event');
|
||||||
ajaxError('Video Generation Failure, Unable to load event');
|
} else {
|
||||||
} else {
|
if ( $videoFile = createVideo($event, $_REQUEST['videoFormat'], $_REQUEST['rate'], $_REQUEST['scale'], !empty($_REQUEST['overwrite'])) )
|
||||||
if ( $videoFile = createVideo($event, $_REQUEST['videoFormat'], $_REQUEST['rate'], $_REQUEST['scale'], !empty($_REQUEST['overwrite'])) )
|
ajaxResponse(array('response'=>$videoFile));
|
||||||
ajaxResponse(array('response'=>$videoFile));
|
else
|
||||||
else
|
ajaxError('Video Generation Failed');
|
||||||
ajaxError('Video Generation Failed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$ok = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case 'deleteVideo' :
|
}
|
||||||
{
|
$ok = true;
|
||||||
unlink( $videoFiles[$_REQUEST['id']] );
|
break;
|
||||||
unset( $videoFiles[$_REQUEST['id']] );
|
case 'deleteVideo' :
|
||||||
ajaxResponse();
|
unlink( $videoFiles[$_REQUEST['id']] );
|
||||||
break;
|
unset( $videoFiles[$_REQUEST['id']] );
|
||||||
}
|
ajaxResponse();
|
||||||
case 'export' :
|
break;
|
||||||
{
|
case 'export' :
|
||||||
require_once(ZM_SKIN_PATH.'/includes/export_functions.php');
|
require_once(ZM_SKIN_PATH.'/includes/export_functions.php');
|
||||||
|
|
||||||
# We use session vars in here, so we need to restart the session
|
# We use session vars in here, so we need to restart the session
|
||||||
# because we stopped it in index.php to improve concurrency.
|
# because we stopped it in index.php to improve concurrency.
|
||||||
session_start();
|
zm_session_start();
|
||||||
|
|
||||||
if ( !empty($_REQUEST['exportDetail']) )
|
if ( !empty($_REQUEST['exportDetail']) )
|
||||||
$exportDetail = $_SESSION['export']['detail'] = $_REQUEST['exportDetail'];
|
$exportDetail = $_SESSION['export']['detail'] = $_REQUEST['exportDetail'];
|
||||||
else
|
else
|
||||||
$exportDetail = false;
|
$exportDetail = false;
|
||||||
|
|
||||||
if ( !empty($_REQUEST['exportFrames']) )
|
if ( !empty($_REQUEST['exportFrames']) )
|
||||||
$exportFrames = $_SESSION['export']['frames'] = $_REQUEST['exportFrames'];
|
$exportFrames = $_SESSION['export']['frames'] = $_REQUEST['exportFrames'];
|
||||||
else
|
else
|
||||||
$exportFrames = false;
|
$exportFrames = false;
|
||||||
|
|
||||||
if ( !empty($_REQUEST['exportImages']) )
|
if ( !empty($_REQUEST['exportImages']) )
|
||||||
$exportImages = $_SESSION['export']['images'] = $_REQUEST['exportImages'];
|
$exportImages = $_SESSION['export']['images'] = $_REQUEST['exportImages'];
|
||||||
else
|
else
|
||||||
$exportImages = false;
|
$exportImages = false;
|
||||||
|
|
||||||
if ( !empty($_REQUEST['exportVideo']) )
|
if ( !empty($_REQUEST['exportVideo']) )
|
||||||
$exportVideo = $_SESSION['export']['video'] = $_REQUEST['exportVideo'];
|
$exportVideo = $_SESSION['export']['video'] = $_REQUEST['exportVideo'];
|
||||||
else
|
else
|
||||||
$exportVideo = false;
|
$exportVideo = false;
|
||||||
|
|
||||||
if ( !empty($_REQUEST['exportMisc']) )
|
if ( !empty($_REQUEST['exportMisc']) )
|
||||||
$exportMisc = $_SESSION['export']['misc'] = $_REQUEST['exportMisc'];
|
$exportMisc = $_SESSION['export']['misc'] = $_REQUEST['exportMisc'];
|
||||||
else
|
else
|
||||||
$exportMisc = false;
|
$exportMisc = false;
|
||||||
|
|
||||||
if ( !empty($_REQUEST['exportFormat']) )
|
if ( !empty($_REQUEST['exportFormat']) )
|
||||||
$exportFormat = $_SESSION['export']['format'] = $_REQUEST['exportFormat'];
|
$exportFormat = $_SESSION['export']['format'] = $_REQUEST['exportFormat'];
|
||||||
else
|
else
|
||||||
$exportFormat = '';
|
$exportFormat = '';
|
||||||
|
|
||||||
if ( !empty($_REQUEST['exportCompress']) )
|
if ( !empty($_REQUEST['exportCompress']) )
|
||||||
$exportCompress = $_SESSION['export']['compress'] = $_REQUEST['exportCompress'];
|
$exportCompress = $_SESSION['export']['compress'] = $_REQUEST['exportCompress'];
|
||||||
else
|
else
|
||||||
$exportCompress = false;
|
$exportCompress = false;
|
||||||
|
|
||||||
session_write_close();
|
session_write_close();
|
||||||
|
|
||||||
$exportIds = !empty($_REQUEST['eids'])?$_REQUEST['eids']:$_REQUEST['id'];
|
$exportIds = !empty($_REQUEST['eids']) ? $_REQUEST['eids'] : $_REQUEST['id'];
|
||||||
if ( $exportFile = exportEvents(
|
if ( $exportFile = exportEvents(
|
||||||
$exportIds,
|
$exportIds,
|
||||||
(isset($_REQUEST['connkey'])?$_REQUEST['connkey']:''),
|
(isset($_REQUEST['connkey'])?$_REQUEST['connkey']:''),
|
||||||
$exportDetail,
|
$exportDetail,
|
||||||
$exportFrames,
|
$exportFrames,
|
||||||
$exportImages,
|
$exportImages,
|
||||||
$exportVideo,
|
$exportVideo,
|
||||||
$exportMisc,
|
$exportMisc,
|
||||||
$exportFormat,
|
$exportFormat,
|
||||||
$exportCompress
|
$exportCompress
|
||||||
) )
|
) )
|
||||||
ajaxResponse(array('exportFile'=>$exportFile));
|
ajaxResponse(array('exportFile'=>$exportFile));
|
||||||
else
|
else
|
||||||
ajaxError('Export Failed');
|
ajaxError('Export Failed');
|
||||||
break;
|
break;
|
||||||
}
|
case 'download' :
|
||||||
case 'download' :
|
require_once(ZM_SKIN_PATH.'/includes/export_functions.php');
|
||||||
{
|
$exportVideo = 1;
|
||||||
require_once(ZM_SKIN_PATH.'/includes/export_functions.php');
|
$exportFormat = $_REQUEST['exportFormat'];
|
||||||
$exportVideo = 1;
|
$exportStructure = 'flat';
|
||||||
$exportFormat = $_REQUEST['exportFormat'];
|
$exportIds = !empty($_REQUEST['eids'])?$_REQUEST['eids']:$_REQUEST['id'];
|
||||||
$exportStructure = 'flat';
|
if ( $exportFile = exportEvents(
|
||||||
$exportIds = !empty($_REQUEST['eids'])?$_REQUEST['eids']:$_REQUEST['id'];
|
$exportIds,
|
||||||
if ( $exportFile = exportEvents(
|
(isset($_REQUEST['connkey'])?$_REQUEST['connkey']:''),
|
||||||
$exportIds,
|
false,false, false,
|
||||||
(isset($_REQUEST['connkey'])?$_REQUEST['connkey']:''),
|
$exportVideo, false, $exportFormat, $exportStructure ) )
|
||||||
false,false, false,
|
ajaxResponse(array('exportFile'=>$exportFile));
|
||||||
$exportVideo, false, $exportFormat, $exportStructure ) )
|
else
|
||||||
ajaxResponse(array('exportFile'=>$exportFile));
|
ajaxError('Export Failed');
|
||||||
else
|
break;
|
||||||
ajaxError('Export Failed');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} // end if canView('Events')
|
} // end if canView('Events')
|
||||||
|
|
||||||
if ( canEdit('Events') ) {
|
if ( canEdit('Events') ) {
|
||||||
switch ( $_REQUEST['action'] ) {
|
switch ( $_REQUEST['action'] ) {
|
||||||
case 'rename' :
|
case 'rename' :
|
||||||
{
|
if ( !empty($_REQUEST['eventName']) )
|
||||||
if ( !empty($_REQUEST['eventName']) )
|
dbQuery('UPDATE Events SET Name = ? WHERE Id = ?', array($_REQUEST['eventName'], $_REQUEST['id']));
|
||||||
dbQuery('UPDATE Events SET Name = ? WHERE Id = ?', array($_REQUEST['eventName'], $_REQUEST['id']));
|
else
|
||||||
else
|
ajaxError('No new event name supplied');
|
||||||
ajaxError('No new event name supplied');
|
ajaxResponse(array('refreshEvent'=>true, 'refreshParent'=>true));
|
||||||
ajaxResponse(array('refreshEvent'=>true, 'refreshParent'=>true));
|
break;
|
||||||
break;
|
case 'eventdetail' :
|
||||||
}
|
dbQuery(
|
||||||
case 'eventdetail' :
|
'UPDATE Events SET Cause = ?, Notes = ? WHERE Id = ?',
|
||||||
{
|
array($_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['id'])
|
||||||
dbQuery(
|
);
|
||||||
'UPDATE Events SET Cause = ?, Notes = ? WHERE Id = ?',
|
ajaxResponse(array('refreshEvent'=>true, 'refreshParent'=>true));
|
||||||
array($_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['id'])
|
break;
|
||||||
);
|
case 'archive' :
|
||||||
ajaxResponse(array('refreshEvent'=>true, 'refreshParent'=>true));
|
case 'unarchive' :
|
||||||
break;
|
$archiveVal = ($_REQUEST['action'] == 'archive')?1:0;
|
||||||
}
|
dbQuery(
|
||||||
case 'archive' :
|
'UPDATE Events SET Archived = ? WHERE Id = ?',
|
||||||
case 'unarchive' :
|
array($archiveVal, $_REQUEST['id'])
|
||||||
{
|
);
|
||||||
$archiveVal = ($_REQUEST['action'] == 'archive')?1:0;
|
ajaxResponse(array('refreshEvent'=>true, 'refreshParent'=>false));
|
||||||
dbQuery(
|
break;
|
||||||
'UPDATE Events SET Archived = ? WHERE Id = ?',
|
case 'delete' :
|
||||||
array($archiveVal, $_REQUEST['id'])
|
$Event = new ZM\Event($_REQUEST['id']);
|
||||||
);
|
if ( ! $Event->Id() ) {
|
||||||
ajaxResponse(array('refreshEvent'=>true, 'refreshParent'=>false));
|
ajaxResponse(array('refreshEvent'=>false, 'refreshParent'=>true, 'message'=> 'Event not found.'));
|
||||||
break;
|
} else {
|
||||||
}
|
$Event->delete();
|
||||||
case 'delete' :
|
ajaxResponse(array('refreshEvent'=>false, 'refreshParent'=>true));
|
||||||
{
|
}
|
||||||
$Event = new Event($_REQUEST['id']);
|
break;
|
||||||
if ( ! $Event->Id() ) {
|
} // end switch action
|
||||||
ajaxResponse(array('refreshEvent'=>false, 'refreshParent'=>true, 'message'=> 'Event not found.'));
|
|
||||||
} else {
|
|
||||||
$Event->delete();
|
|
||||||
ajaxResponse(array('refreshEvent'=>false, 'refreshParent'=>true));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // end if canEdit('Events')
|
} // end if canEdit('Events')
|
||||||
|
|
||||||
ajaxError('Unrecognised action or insufficient permissions');
|
ajaxError('Unrecognised action or insufficient permissions');
|
||||||
|
|
|
@ -19,7 +19,7 @@ switch ( $_REQUEST['task'] ) {
|
||||||
else
|
else
|
||||||
$line = NULL;
|
$line = NULL;
|
||||||
|
|
||||||
$levels = array_flip(Logger::$codes);
|
$levels = array_flip(ZM\Logger::$codes);
|
||||||
if ( !isset($levels[$_POST['level']]) )
|
if ( !isset($levels[$_POST['level']]) )
|
||||||
ZM\Panic("Unexpected logger level '".$_POST['level']."'");
|
ZM\Panic("Unexpected logger level '".$_POST['level']."'");
|
||||||
$level = $levels[$_POST['level']];
|
$level = $levels[$_POST['level']];
|
||||||
|
@ -322,7 +322,7 @@ switch ( $_REQUEST['task'] ) {
|
||||||
$classLevel = ZM\Logger::FATAL;
|
$classLevel = ZM\Logger::FATAL;
|
||||||
elseif ( $classLevel > ZM\Logger::DEBUG )
|
elseif ( $classLevel > ZM\Logger::DEBUG )
|
||||||
$classLevel = ZM\Logger::DEBUG;
|
$classLevel = ZM\Logger::DEBUG;
|
||||||
$logClass = 'log-'.strtolower(Logger::$codes[$classLevel]);
|
$logClass = 'log-'.strtolower(ZM\Logger::$codes[$classLevel]);
|
||||||
fprintf( $exportFP, " <tr class=\"%s\"><td>%s</td><td>%s</td><td>%s</td><td>%d</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", $logClass, $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] );
|
fprintf( $exportFP, " <tr class=\"%s\"><td>%s</td><td>%s</td><td>%s</td><td>%d</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n", $logClass, $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] );
|
||||||
}
|
}
|
||||||
fwrite( $exportFP,
|
fwrite( $exportFP,
|
||||||
|
|
|
@ -10,8 +10,7 @@ if ( !($_REQUEST['connkey'] && $_REQUEST['command']) ) {
|
||||||
ajaxError("Unexpected received message type '$type'");
|
ajaxError("Unexpected received message type '$type'");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mkdir(ZM_PATH_SOCKS) ) {
|
mkdir(ZM_PATH_SOCKS);
|
||||||
}
|
|
||||||
|
|
||||||
# The file that we point ftok to has to exist, and only exist if zms is running, so we are pointing it at the .sock
|
# The file that we point ftok to has to exist, and only exist if zms is running, so we are pointing it at the .sock
|
||||||
$key = ftok(ZM_PATH_SOCKS.'/zms-'.sprintf('%06d',$_REQUEST['connkey']).'s.sock', 'Z');
|
$key = ftok(ZM_PATH_SOCKS.'/zms-'.sprintf('%06d',$_REQUEST['connkey']).'s.sock', 'Z');
|
||||||
|
@ -26,7 +25,7 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
||||||
ZM\Warning("sock file $localSocketFile already exists?! Is someone else talking to zms?");
|
ZM\Warning("sock file $localSocketFile already exists?! Is someone else talking to zms?");
|
||||||
// They could be. We can maybe have concurrent requests from a browser.
|
// They could be. We can maybe have concurrent requests from a browser.
|
||||||
}
|
}
|
||||||
if ( ! socket_bind( $socket, $localSocketFile ) ) {
|
if ( !socket_bind( $socket, $localSocketFile ) ) {
|
||||||
ajaxError("socket_bind( $localSocketFile ) failed: ".socket_strerror(socket_last_error()) );
|
ajaxError("socket_bind( $localSocketFile ) failed: ".socket_strerror(socket_last_error()) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,18 +51,20 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
||||||
$msg = pack( 'lcN', MSG_CMD, $_REQUEST['command'], $_REQUEST['offset'] );
|
$msg = pack( 'lcN', MSG_CMD, $_REQUEST['command'], $_REQUEST['offset'] );
|
||||||
break;
|
break;
|
||||||
default :
|
default :
|
||||||
|
ZM\Logger::Debug('Sending command ' . $_REQUEST['command']);
|
||||||
$msg = pack( 'lc', MSG_CMD, $_REQUEST['command'] );
|
$msg = pack( 'lc', MSG_CMD, $_REQUEST['command'] );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$remSockFile = ZM_PATH_SOCKS.'/zms-'.sprintf('%06d',$_REQUEST['connkey']).'s.sock';
|
$remSockFile = ZM_PATH_SOCKS.'/zms-'.sprintf('%06d',$_REQUEST['connkey']).'s.sock';
|
||||||
$max_socket_tries = 10;
|
// Pi can take up to 3 seconds for zms to start up.
|
||||||
|
$max_socket_tries = 1000;
|
||||||
// FIXME This should not exceed web_ajax_timeout
|
// FIXME This should not exceed web_ajax_timeout
|
||||||
while ( !file_exists($remSockFile) && $max_socket_tries-- ) { //sometimes we are too fast for our own good, if it hasn't been setup yet give it a second.
|
while ( !file_exists($remSockFile) && $max_socket_tries-- ) { //sometimes we are too fast for our own good, if it hasn't been setup yet give it a second.
|
||||||
// WHY? We will just send another one...
|
// WHY? We will just send another one...
|
||||||
// ANSWER: Because otherwise we get a log of errors logged
|
// ANSWER: Because otherwise we get a log of errors logged
|
||||||
|
|
||||||
//Logger::Debug("$remSockFile does not exist, waiting, current " . (time() - $start_time) . ' seconds' );
|
//ZM\Logger::Debug("$remSockFile does not exist, waiting, current " . (time() - $start_time) . ' seconds' );
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,43 +92,35 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
||||||
ajaxError( "Socket closed $remSockFile" );
|
ajaxError( "Socket closed $remSockFile" );
|
||||||
} else if ( $numSockets == 0 ) {
|
} else if ( $numSockets == 0 ) {
|
||||||
ZM\Error( "Timed out waiting for msg $remSockFile" );
|
ZM\Error( "Timed out waiting for msg $remSockFile" );
|
||||||
socket_Set_nonblock($socket);
|
socket_set_nonblock($socket);
|
||||||
#ajaxError( "Timed out waiting for msg $remSockFile" );
|
#ajaxError("Timed out waiting for msg $remSockFile");
|
||||||
} else if ( $numSockets > 0 ) {
|
} else if ( $numSockets > 0 ) {
|
||||||
if ( count($rSockets) != 1 ) {
|
if ( count($rSockets) != 1 ) {
|
||||||
ZM\Error( 'Bogus return from select, '.count($rSockets).' sockets available' );
|
ZM\Error('Bogus return from select, '.count($rSockets).' sockets available');
|
||||||
ajaxError( 'Bogus return from select, '.count($rSockets).' sockets available' );
|
ajaxError('Bogus return from select, '.count($rSockets).' sockets available');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch( $nbytes = @socket_recvfrom( $socket, $msg, MSG_DATA_SIZE, 0, $remSockFile ) ) {
|
switch( $nbytes = @socket_recvfrom( $socket, $msg, MSG_DATA_SIZE, 0, $remSockFile ) ) {
|
||||||
case -1 :
|
case -1 :
|
||||||
{
|
ajaxError("socket_recvfrom( $remSockFile ) failed: ".socket_strerror(socket_last_error()));
|
||||||
ajaxError( "socket_recvfrom( $remSockFile ) failed: ".socket_strerror(socket_last_error()) );
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case 0 :
|
case 0 :
|
||||||
{
|
ajaxError('No data to read from socket');
|
||||||
ajaxError( 'No data to read from socket' );
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default :
|
default :
|
||||||
{
|
|
||||||
if ( $nbytes != MSG_DATA_SIZE )
|
if ( $nbytes != MSG_DATA_SIZE )
|
||||||
ajaxError( "Got unexpected message size, got $nbytes, expected ".MSG_DATA_SIZE );
|
ajaxError("Got unexpected message size, got $nbytes, expected ".MSG_DATA_SIZE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
$data = unpack('ltype', $msg);
|
||||||
$data = unpack( 'ltype', $msg );
|
|
||||||
switch ( $data['type'] ) {
|
switch ( $data['type'] ) {
|
||||||
case MSG_DATA_WATCH :
|
case MSG_DATA_WATCH :
|
||||||
{
|
$data = unpack('ltype/imonitor/istate/dfps/ilevel/irate/ddelay/izoom/Cdelayed/Cpaused/Cenabled/Cforced', $msg);
|
||||||
$data = unpack( "ltype/imonitor/istate/dfps/ilevel/irate/ddelay/izoom/Cdelayed/Cpaused/Cenabled/Cforced", $msg );
|
ZM\Logger::Debug('FPS: ' . $data['fps']);
|
||||||
ZM\Logger::Debug("FPS: " . $data['fps'] );
|
|
||||||
$data['fps'] = round( $data['fps'], 2 );
|
$data['fps'] = round( $data['fps'], 2 );
|
||||||
ZM\Logger::Debug("FPS: " . $data['fps'] );
|
ZM\Logger::Debug('FPS: ' . $data['fps'] );
|
||||||
$data['rate'] /= RATE_BASE;
|
$data['rate'] /= RATE_BASE;
|
||||||
$data['delay'] = round( $data['delay'], 2 );
|
$data['delay'] = round( $data['delay'], 2 );
|
||||||
$data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 );
|
$data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 );
|
||||||
|
@ -140,11 +133,14 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
||||||
}
|
}
|
||||||
ajaxResponse( array( 'status'=>$data ) );
|
ajaxResponse( array( 'status'=>$data ) );
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case MSG_DATA_EVENT :
|
case MSG_DATA_EVENT :
|
||||||
{
|
if ( version_compare( phpversion(), '5.6.0', '<') ) {
|
||||||
$data = unpack( "ltype/Pevent/iprogress/irate/izoom/Cpaused", $msg );
|
ZM\Logger::Debug('Using old unpack methods to handle 64bit event id');
|
||||||
//$data['progress'] = sprintf( "%.2f", $data['progress'] );
|
$data = unpack('ltype/ieventlow/ieventhigh/iprogress/irate/izoom/Cpaused', $msg);
|
||||||
|
$data['event'] = $data['eventhigh'] << 32 | $data['eventlow'];
|
||||||
|
} else {
|
||||||
|
$data = unpack('ltype/Qevent/iprogress/irate/izoom/Cpaused', $msg);
|
||||||
|
}
|
||||||
$data['rate'] /= RATE_BASE;
|
$data['rate'] /= RATE_BASE;
|
||||||
$data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 );
|
$data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 );
|
||||||
if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' ) {
|
if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' ) {
|
||||||
|
@ -154,13 +150,10 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
||||||
$data['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS);
|
$data['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ajaxResponse( array( 'status'=>$data ) );
|
ajaxResponse(array('status'=>$data));
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default :
|
default :
|
||||||
{
|
ajaxError("Unexpected received message type '$type'");
|
||||||
ajaxError( "Unexpected received message type '$type'" );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sem_release($semaphore);
|
sem_release($semaphore);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -67,7 +67,6 @@ class AppController extends Controller {
|
||||||
|
|
||||||
# For use throughout the app. If not logged in, this will be null.
|
# For use throughout the app. If not logged in, this will be null.
|
||||||
global $user;
|
global $user;
|
||||||
$user = $this->Session->read('user');
|
|
||||||
|
|
||||||
if ( ZM_OPT_USE_AUTH ) {
|
if ( ZM_OPT_USE_AUTH ) {
|
||||||
require_once __DIR__ .'/../../../includes/auth.php';
|
require_once __DIR__ .'/../../../includes/auth.php';
|
||||||
|
|
|
@ -1,14 +1,88 @@
|
||||||
<?php
|
<?php
|
||||||
App::uses('Component', 'Controller');
|
App::uses('Component', 'Controller');
|
||||||
|
|
||||||
class FilterComponent extends Component {
|
class FilterComponent extends Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valid MySQL operands that can be used from namedParams with two operands
|
||||||
|
* where the right-hand side (RHS) is a literal.
|
||||||
|
* These came from https://dev.mysql.com/doc/refman/8.0/en/non-typed-operators.html
|
||||||
|
*/
|
||||||
|
public $twoOperandSQLOperands = array(
|
||||||
|
'AND',
|
||||||
|
'&&',
|
||||||
|
'=',
|
||||||
|
//':=',
|
||||||
|
//'BETWEEN ... AND ...',
|
||||||
|
//'BINARY',
|
||||||
|
'&',
|
||||||
|
'~',
|
||||||
|
//'|',
|
||||||
|
'^',
|
||||||
|
//'CASE',
|
||||||
|
'DIV',
|
||||||
|
'/',
|
||||||
|
'=',
|
||||||
|
'<=>',
|
||||||
|
'>',
|
||||||
|
'>=',
|
||||||
|
'IS',
|
||||||
|
'IS NOT',
|
||||||
|
//'IS NOT NULL',
|
||||||
|
//'IS NULL',
|
||||||
|
//'->',
|
||||||
|
//'->>',
|
||||||
|
'<<',
|
||||||
|
'<',
|
||||||
|
'<=',
|
||||||
|
'LIKE',
|
||||||
|
'-',
|
||||||
|
'%',
|
||||||
|
'MOD',
|
||||||
|
//'NOT',
|
||||||
|
//'!',
|
||||||
|
//'NOT BETWEEN ... AND ...',
|
||||||
|
'!=',
|
||||||
|
'<>',
|
||||||
|
'NOT LIKE',
|
||||||
|
'NOT REGEXP',
|
||||||
|
// `or` operators aren't safe as they can
|
||||||
|
// be used to skip an existing condition
|
||||||
|
// enforcing access to only certain
|
||||||
|
// monitors/events.
|
||||||
|
//'||',
|
||||||
|
//'OR',
|
||||||
|
'+',
|
||||||
|
'REGEXP',
|
||||||
|
'>>',
|
||||||
|
'RLIKE',
|
||||||
|
'SOUNDS LIKE',
|
||||||
|
//'*',
|
||||||
|
'-',
|
||||||
|
//'XOR',
|
||||||
|
);
|
||||||
|
|
||||||
// Build a CakePHP find() condition based on the named parameters
|
// Build a CakePHP find() condition based on the named parameters
|
||||||
// that are passed in
|
// that are passed in
|
||||||
public function buildFilter($namedParams) {
|
public function buildFilter($namedParams) {
|
||||||
if ($namedParams) {
|
$conditions = array();
|
||||||
$conditions = array();
|
if ($namedParams) {
|
||||||
|
|
||||||
foreach ($namedParams as $attribute => $value) {
|
foreach ($namedParams as $attribute => $value) {
|
||||||
|
// We need to sanitize $attribute to avoid SQL injection.
|
||||||
|
$lhs = trim($attribute);
|
||||||
|
$matches = NULL;
|
||||||
|
if (preg_match('/^(?P<field>[a-z0-9]+)(?P<operator>.+)?$/i', $lhs, $matches) !== 1) {
|
||||||
|
throw new Exception('Invalid argument before `:`: ' . $lhs);
|
||||||
|
}
|
||||||
|
$operator = trim($matches['operator']);
|
||||||
|
|
||||||
|
// Only allow operators on our allow list. No operator
|
||||||
|
// specified defaults to `=` by cakePHP.
|
||||||
|
if ($operator != '' && !in_array($operator, $this->twoOperandSQLOperands)) {
|
||||||
|
throw new Exception('Invalid operator: ' . $operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
$lhs = '`' . $matches['field'] . '` ' . $operator;
|
||||||
// If the named param contains an array, we want to turn it into an IN condition
|
// If the named param contains an array, we want to turn it into an IN condition
|
||||||
// Otherwise, we add it right into the $conditions array
|
// Otherwise, we add it right into the $conditions array
|
||||||
if (is_array($value)) {
|
if (is_array($value)) {
|
||||||
|
@ -18,10 +92,10 @@ class FilterComponent extends Component {
|
||||||
array_push($array, $term);
|
array_push($array, $term);
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = array($attribute => $array);
|
$query = array($lhs => $array);
|
||||||
array_push($conditions, $query);
|
array_push($conditions, $query);
|
||||||
} else {
|
} else {
|
||||||
$conditions[$attribute] = $value;
|
$conditions[$lhs] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,9 +44,8 @@ class EventsController extends AppController {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $this->request->params['named'] ) {
|
if ( $this->request->params['named'] ) {
|
||||||
//$this->FilterComponent = $this->Components->load('Filter');
|
$this->FilterComponent = $this->Components->load('Filter');
|
||||||
//$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
|
$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
|
||||||
$conditions = $this->request->params['named'];
|
|
||||||
} else {
|
} else {
|
||||||
$conditions = array();
|
$conditions = array();
|
||||||
}
|
}
|
||||||
|
@ -143,7 +142,7 @@ class EventsController extends AppController {
|
||||||
'event' => $event,
|
'event' => $event,
|
||||||
'_serialize' => array('event')
|
'_serialize' => array('event')
|
||||||
));
|
));
|
||||||
}
|
} // end function view
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -234,18 +233,34 @@ class EventsController extends AppController {
|
||||||
|
|
||||||
public function search() {
|
public function search() {
|
||||||
$this->Event->recursive = -1;
|
$this->Event->recursive = -1;
|
||||||
|
// Unmodified conditions to pass to find()
|
||||||
|
$find_conditions = array();
|
||||||
|
// Conditions to be filtered by buildFilter
|
||||||
$conditions = array();
|
$conditions = array();
|
||||||
|
|
||||||
foreach ($this->params['named'] as $param_name => $value) {
|
foreach ($this->params['named'] as $param_name => $value) {
|
||||||
// Transform params into mysql
|
// Transform params into conditions
|
||||||
if ( preg_match('/interval/i', $value, $matches) ) {
|
if ( preg_match('/^\s?interval\s?/i', $value) ) {
|
||||||
$condition = array("$param_name >= (date_sub(now(), $value))");
|
if (preg_match('/^[a-z0-9]+$/i', $param_name) !== 1) {
|
||||||
|
throw new Exception('Invalid field name: ' . $param_name);
|
||||||
|
}
|
||||||
|
$matches = NULL;
|
||||||
|
$value = preg_replace('/^\s?interval\s?/i', '', $value);
|
||||||
|
if (preg_match('/^(?P<expr>[ -.:0-9\']+)\s+(?P<unit>[_a-z]+)$/i', trim($value), $matches) !== 1) {
|
||||||
|
throw new Exception('Invalid interval: ' . $value);
|
||||||
|
}
|
||||||
|
$expr = trim($matches['expr']);
|
||||||
|
$unit = trim($matches['unit']);
|
||||||
|
array_push($find_conditions, "$param_name >= DATE_SUB(NOW(), INTERVAL $expr $unit)");
|
||||||
} else {
|
} else {
|
||||||
$condition = array($param_name => $value);
|
$conditions[$param_name] = $value;
|
||||||
}
|
}
|
||||||
array_push($conditions, $condition);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->FilterComponent = $this->Components->load('Filter');
|
||||||
|
$conditions = $this->FilterComponent->buildFilter($conditions);
|
||||||
|
array_push($conditions, $find_conditions);
|
||||||
|
|
||||||
$results = $this->Event->find('all', array(
|
$results = $this->Event->find('all', array(
|
||||||
'conditions' => $conditions
|
'conditions' => $conditions
|
||||||
));
|
));
|
||||||
|
@ -261,18 +276,35 @@ class EventsController extends AppController {
|
||||||
// consoleEvents/1 hour/AlarmFrames >=: 1/AlarmFrames <=: 20.json
|
// consoleEvents/1 hour/AlarmFrames >=: 1/AlarmFrames <=: 20.json
|
||||||
|
|
||||||
public function consoleEvents($interval = null) {
|
public function consoleEvents($interval = null) {
|
||||||
|
$matches = NULL;
|
||||||
|
// https://dev.mysql.com/doc/refman/5.5/en/expressions.html#temporal-intervals
|
||||||
|
// Examples: `'1-1' YEAR_MONTH`, `'-1 10' DAY_HOUR`, `'1.999999' SECOND_MICROSECOND`
|
||||||
|
if (preg_match('/^(?P<expr>[ -.:0-9\']+)\s+(?P<unit>[_a-z]+)$/i', trim($interval), $matches) !== 1) {
|
||||||
|
throw new Exception('Invalid interval: ' . $interval);
|
||||||
|
}
|
||||||
|
$expr = trim($matches['expr']);
|
||||||
|
$unit = trim($matches['unit']);
|
||||||
|
|
||||||
$this->Event->recursive = -1;
|
$this->Event->recursive = -1;
|
||||||
$results = array();
|
$results = array();
|
||||||
|
$this->FilterComponent = $this->Components->load('Filter');
|
||||||
$moreconditions = '';
|
if ( $this->request->params['named'] ) {
|
||||||
foreach ($this->request->params['named'] as $name => $param) {
|
$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
|
||||||
$moreconditions = $moreconditions . ' AND '.$name.$param;
|
} else {
|
||||||
}
|
$conditions = array();
|
||||||
|
}
|
||||||
$query = $this->Event->query("SELECT MonitorId, COUNT(*) AS Count FROM Events WHERE (StartTime >= (DATE_SUB(NOW(), interval $interval)) $moreconditions) GROUP BY MonitorId;");
|
array_push($conditions, array("StartTime >= DATE_SUB(NOW(), INTERVAL $expr $unit)"));
|
||||||
|
$query = $this->Event->find('all', array(
|
||||||
|
'fields' => array(
|
||||||
|
'MonitorId',
|
||||||
|
'COUNT(*) AS Count',
|
||||||
|
),
|
||||||
|
'conditions' => $conditions,
|
||||||
|
'group' => 'MonitorId',
|
||||||
|
));
|
||||||
|
|
||||||
foreach ($query as $result) {
|
foreach ($query as $result) {
|
||||||
$results[$result['Events']['MonitorId']] = $result[0]['Count'];
|
$results[$result['Event']['MonitorId']] = $result[0]['Count'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->set(array(
|
$this->set(array(
|
||||||
|
|
|
@ -48,8 +48,7 @@ class HostController extends AppController {
|
||||||
|
|
||||||
// clears out session
|
// clears out session
|
||||||
function logout() {
|
function logout() {
|
||||||
global $user;
|
userLogout();
|
||||||
$this->Session->Write('user', null);
|
|
||||||
|
|
||||||
$this->set(array(
|
$this->set(array(
|
||||||
'result' => 'ok',
|
'result' => 'ok',
|
||||||
|
@ -67,7 +66,7 @@ class HostController extends AppController {
|
||||||
if ( $isZmAuth ) {
|
if ( $isZmAuth ) {
|
||||||
// In future, we may want to completely move to AUTH_HASH_LOGINS and return &auth= for all cases
|
// In future, we may want to completely move to AUTH_HASH_LOGINS and return &auth= for all cases
|
||||||
require_once __DIR__ .'/../../../includes/auth.php'; # in the event we directly call getCredentials.json
|
require_once __DIR__ .'/../../../includes/auth.php'; # in the event we directly call getCredentials.json
|
||||||
$this->Session->read('user'); # this is needed for command line/curl to recognize a session
|
|
||||||
$zmAuthRelay = $this->Config->find('first',array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY')))['Config']['Value'];
|
$zmAuthRelay = $this->Config->find('first',array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY')))['Config']['Value'];
|
||||||
if ( $zmAuthRelay == 'hashed' ) {
|
if ( $zmAuthRelay == 'hashed' ) {
|
||||||
$zmAuthHashIps = $this->Config->find('first',array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_IPS')))['Config']['Value'];
|
$zmAuthHashIps = $this->Config->find('first',array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_IPS')))['Config']['Value'];
|
||||||
|
@ -75,7 +74,7 @@ class HostController extends AppController {
|
||||||
$credentials = 'auth='.generateAuthHash($zmAuthHashIps,true);
|
$credentials = 'auth='.generateAuthHash($zmAuthHashIps,true);
|
||||||
} else {
|
} else {
|
||||||
// user will need to append the store password here
|
// user will need to append the store password here
|
||||||
$credentials = 'user='.$this->Session->read('user.Username').'&pass=';
|
$credentials = 'user='.$this->Session->read('Username').'&pass=';
|
||||||
$appendPassword = 1;
|
$appendPassword = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,6 +95,7 @@ class HostController extends AppController {
|
||||||
// If $mid is set, only return disk usage for that monitor
|
// If $mid is set, only return disk usage for that monitor
|
||||||
// Else, return an array of total disk usage, and per-monitor
|
// Else, return an array of total disk usage, and per-monitor
|
||||||
// usage.
|
// usage.
|
||||||
|
// This function is deprecated. Use the Storage object or monitor object instead
|
||||||
function getDiskPercent($mid = null) {
|
function getDiskPercent($mid = null) {
|
||||||
$this->loadModel('Config');
|
$this->loadModel('Config');
|
||||||
$this->loadModel('Monitor');
|
$this->loadModel('Monitor');
|
||||||
|
@ -118,7 +118,7 @@ class HostController extends AppController {
|
||||||
|
|
||||||
if ( $mid ) {
|
if ( $mid ) {
|
||||||
// Get disk usage for $mid
|
// Get disk usage for $mid
|
||||||
Logger::Debug("Executing du -s0 $zm_dir_events/$mid | awk '{print $1}'");
|
ZM\Logger::Debug("Executing du -s0 $zm_dir_events/$mid | awk '{print $1}'");
|
||||||
$usage = shell_exec("du -s0 $zm_dir_events/$mid | awk '{print $1}'");
|
$usage = shell_exec("du -s0 $zm_dir_events/$mid | awk '{print $1}'");
|
||||||
} else {
|
} else {
|
||||||
$monitors = $this->Monitor->find('all', array(
|
$monitors = $this->Monitor->find('all', array(
|
||||||
|
|
|
@ -40,8 +40,7 @@ class MonitorsController extends AppController {
|
||||||
|
|
||||||
if ( $this->request->params['named'] ) {
|
if ( $this->request->params['named'] ) {
|
||||||
$this->FilterComponent = $this->Components->load('Filter');
|
$this->FilterComponent = $this->Components->load('Filter');
|
||||||
//$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
|
$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
|
||||||
$conditions = $this->request->params['named'];
|
|
||||||
} else {
|
} else {
|
||||||
$conditions = array();
|
$conditions = array();
|
||||||
}
|
}
|
||||||
|
@ -183,7 +182,7 @@ class MonitorsController extends AppController {
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if ( !defined('ZM_SERVER_ID')) {
|
if ( !defined('ZM_SERVER_ID')) {
|
||||||
Logger::Debug("Not defined ZM_SERVER_ID");
|
ZM\Logger::Debug("Not defined ZM_SERVER_ID");
|
||||||
}
|
}
|
||||||
$this->daemonControl($this->Monitor->id, 'start');
|
$this->daemonControl($this->Monitor->id, 'start');
|
||||||
}
|
}
|
||||||
|
@ -318,6 +317,10 @@ class MonitorsController extends AppController {
|
||||||
throw new NotFoundException(__('Invalid monitor'));
|
throw new NotFoundException(__('Invalid monitor'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (preg_match('/^[a-z]+$/i', $daemon) !== 1) {
|
||||||
|
throw new BadRequestException(__('Invalid command'));
|
||||||
|
}
|
||||||
|
|
||||||
$monitor = $this->Monitor->find('first', array(
|
$monitor = $this->Monitor->find('first', array(
|
||||||
'fields' => array('Id', 'Type', 'Device'),
|
'fields' => array('Id', 'Type', 'Device'),
|
||||||
'conditions' => array('Id' => $id)
|
'conditions' => array('Id' => $id)
|
||||||
|
@ -384,7 +387,7 @@ class MonitorsController extends AppController {
|
||||||
}
|
}
|
||||||
|
|
||||||
$shellcmd = escapeshellcmd("$zm_path_bin/zmdc.pl $command $daemon $args");
|
$shellcmd = escapeshellcmd("$zm_path_bin/zmdc.pl $command $daemon $args");
|
||||||
Logger::Debug("Command $shellcmd");
|
ZM\Logger::Debug("Command $shellcmd");
|
||||||
$status = exec($shellcmd);
|
$status = exec($shellcmd);
|
||||||
$status_text .= $status."\n";
|
$status_text .= $status."\n";
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ class ZonesController extends AppController {
|
||||||
parent::beforeFilter();
|
parent::beforeFilter();
|
||||||
|
|
||||||
global $user;
|
global $user;
|
||||||
$canView = (!$user) || $user['Monitors'] != 'None';
|
$canView = (!$user) || ($user['Monitors'] != 'None');
|
||||||
if ( !$canView ) {
|
if ( !$canView ) {
|
||||||
throw new UnauthorizedException(__('Insufficient Privileges'));
|
throw new UnauthorizedException(__('Insufficient Privileges'));
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
require_once __DIR__ .'/../../../includes/Event.php';
|
||||||
|
|
||||||
App::uses('AppModel', 'Model');
|
App::uses('AppModel', 'Model');
|
||||||
/**
|
/**
|
||||||
* Event Model
|
* Event Model
|
||||||
|
@ -100,20 +102,20 @@ class Event extends AppModel {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
public function Relative_Path($event) {
|
public function Relative_Path() {
|
||||||
$event_path = '';
|
$Event = new ZM\Event($this->id);
|
||||||
|
return $Event->Relative_Path();
|
||||||
if ( $event['Scheme'] == 'Deep' ) {
|
|
||||||
$event_path = $event['MonitorId'] .'/'.strftime('%y/%m/%d/%H/%M/%S', strtotime($event['StartTime']));
|
|
||||||
} else if ( $event['Scheme'] == 'Medium' ) {
|
|
||||||
$event_path = $event['MonitorId'] .'/'.strftime('%Y-%m-%d', strtotime($event['StartTime'])) . '/'.$event['Id'];
|
|
||||||
} else {
|
|
||||||
$event_path = $event['MonitorId'] .'/'.$event['Id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $event_path;
|
|
||||||
} // end function Relative_Path()
|
} // end function Relative_Path()
|
||||||
|
|
||||||
|
public function Path() {
|
||||||
|
$Event = new ZM\Event($this->id);
|
||||||
|
return $Event->Path();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Link_Path() {
|
||||||
|
$Event = new ZM\Event($this->id);
|
||||||
|
return $Event->Link_Path();
|
||||||
|
}
|
||||||
|
|
||||||
public function fileExists($event) {
|
public function fileExists($event) {
|
||||||
//$data = $this->findById($id);
|
//$data = $this->findById($id);
|
||||||
|
@ -121,19 +123,25 @@ class Event extends AppModel {
|
||||||
$storage = $this->Storage->findById($event['StorageId']);
|
$storage = $this->Storage->findById($event['StorageId']);
|
||||||
|
|
||||||
if ( $event['DefaultVideo'] ) {
|
if ( $event['DefaultVideo'] ) {
|
||||||
if ( file_exists($storage['Storage']['Path'].'/'.$this->Relative_Path($event).'/'.$event['DefaultVideo']) ) {
|
if ( file_exists($this->Path().'/'.$event['DefaultVideo']) ) {
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
Logger::Debug("FIle does not exist at " . $storage['Storage']['Path'].'/'.$this->Relative_Path($event).'/'.$event['DefaultVideo'] );
|
ZM\Logger::Debug('File does not exist at ' . $this->Path().'/'.$event['DefaultVideo'] );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Logger::Debug("No DefaultVideo in Event" . $this->Event);
|
ZM\Logger::Debug('No DefaultVideo in Event' . $this->Event);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // end function fileExists($event)
|
} // end function fileExists($event)
|
||||||
|
|
||||||
public function fileSize($event) {
|
public function fileSize($event) {
|
||||||
$storage = $this->Storage->findById($event['StorageId']);
|
return filesize($this->Path().'/'.$event['DefaultVideo']);
|
||||||
return filesize($storage['Storage']['Path'].'/'.$this->Relative_Path($event).'/'.$event['DefaultVideo']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function beforeDelete($cascade=true) {
|
||||||
|
$Event = new ZM\Event($this->id);
|
||||||
|
$Event->delete();
|
||||||
|
// Event->delete() will do it all, so cake doesn't have to do anything.
|
||||||
|
return false;
|
||||||
|
} // end function afterDelete
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
require_once( 'database.php' );
|
namespace ZM;
|
||||||
require_once( 'Server.php' );
|
|
||||||
|
require_once('database.php');
|
||||||
|
|
||||||
class Control {
|
class Control {
|
||||||
|
|
||||||
|
@ -13,6 +14,7 @@ private $defaults = array(
|
||||||
'CanMoveCon' => 0,
|
'CanMoveCon' => 0,
|
||||||
'CanPan' => 0,
|
'CanPan' => 0,
|
||||||
'CanReset' => 0,
|
'CanReset' => 0,
|
||||||
|
'CanReboot' => 0,
|
||||||
'CanSleep' => 0,
|
'CanSleep' => 0,
|
||||||
'CanWake' => 0,
|
'CanWake' => 0,
|
||||||
'MinPanRange' => NULL,
|
'MinPanRange' => NULL,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
namespace ZM;
|
namespace ZM;
|
||||||
|
require_once('Storage.php');
|
||||||
|
require_once('functions.php');
|
||||||
|
|
||||||
$event_cache = array();
|
$event_cache = array();
|
||||||
|
|
||||||
|
@ -41,9 +43,9 @@ class Event {
|
||||||
$row = NULL;
|
$row = NULL;
|
||||||
if ( $IdOrRow ) {
|
if ( $IdOrRow ) {
|
||||||
if ( is_integer($IdOrRow) or is_numeric($IdOrRow) ) {
|
if ( is_integer($IdOrRow) or is_numeric($IdOrRow) ) {
|
||||||
$row = dbFetchOne('SELECT *,unix_timestamp(StartTime) as Time FROM Events WHERE Id=?', NULL, array($IdOrRow));
|
$row = dbFetchOne('SELECT *,unix_timestamp(StartTime) AS Time FROM Events WHERE Id=?', NULL, array($IdOrRow));
|
||||||
if ( ! $row ) {
|
if ( ! $row ) {
|
||||||
Error('Unable to load Event record for Id=' . $IdOrRow );
|
Error('Unable to load Event record for Id=' . $IdOrRow);
|
||||||
}
|
}
|
||||||
} elseif ( is_array($IdOrRow) ) {
|
} elseif ( is_array($IdOrRow) ) {
|
||||||
$row = $IdOrRow;
|
$row = $IdOrRow;
|
||||||
|
@ -51,8 +53,7 @@ class Event {
|
||||||
$backTrace = debug_backtrace();
|
$backTrace = debug_backtrace();
|
||||||
$file = $backTrace[1]['file'];
|
$file = $backTrace[1]['file'];
|
||||||
$line = $backTrace[1]['line'];
|
$line = $backTrace[1]['line'];
|
||||||
Error("Unknown argument passed to Event Constructor from $file:$line)");
|
Error("Unknown argument passed to Event Constructor from $file:$line) Id was $IdOrRow");
|
||||||
Error("Unknown argument passed to Event Constructor ($IdOrRow)");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,11 +94,11 @@ class Event {
|
||||||
return new Monitor();
|
return new Monitor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __call( $fn, array $args){
|
public function __call($fn, array $args){
|
||||||
if ( count( $args ) ) {
|
if ( count($args) ) {
|
||||||
$this->{$fn} = $args[0];
|
$this->{$fn} = $args[0];
|
||||||
}
|
}
|
||||||
if ( array_key_exists( $fn, $this ) ) {
|
if ( array_key_exists($fn, $this) ) {
|
||||||
return $this->{$fn};
|
return $this->{$fn};
|
||||||
|
|
||||||
$backTrace = debug_backtrace();
|
$backTrace = debug_backtrace();
|
||||||
|
@ -107,7 +108,7 @@ class Event {
|
||||||
$file = $backTrace[1]['file'];
|
$file = $backTrace[1]['file'];
|
||||||
$line = $backTrace[1]['line'];
|
$line = $backTrace[1]['line'];
|
||||||
Warning("Unknown function call Event->$fn from $file:$line");
|
Warning("Unknown function call Event->$fn from $file:$line");
|
||||||
Warning(print_r( $this, true ));
|
Warning(print_r($this, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,18 +121,23 @@ class Event {
|
||||||
|
|
||||||
public function Path() {
|
public function Path() {
|
||||||
$Storage = $this->Storage();
|
$Storage = $this->Storage();
|
||||||
return $Storage->Path().'/'.$this->Relative_Path();
|
if ( $Storage->Path() and $this->Relative_Path() ) {
|
||||||
|
return $Storage->Path().'/'.$this->Relative_Path();
|
||||||
|
} else {
|
||||||
|
Error('Event Path not complete. Storage: '.$Storage->Path().' relative: '.$this->Relative_Path());
|
||||||
|
return '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Relative_Path() {
|
public function Relative_Path() {
|
||||||
$event_path = '';
|
$event_path = '';
|
||||||
|
|
||||||
if ( $this->{'Scheme'} == 'Deep' ) {
|
if ( $this->{'Scheme'} == 'Deep' ) {
|
||||||
$event_path = $this->{'MonitorId'} .'/'.strftime( '%y/%m/%d/%H/%M/%S', $this->Time()) ;
|
$event_path = $this->{'MonitorId'}.'/'.strftime('%y/%m/%d/%H/%M/%S', $this->Time());
|
||||||
} else if ( $this->{'Scheme'} == 'Medium' ) {
|
} else if ( $this->{'Scheme'} == 'Medium' ) {
|
||||||
$event_path = $this->{'MonitorId'} .'/'.strftime( '%Y-%m-%d', $this->Time() ) . '/'.$this->{'Id'};
|
$event_path = $this->{'MonitorId'}.'/'.strftime('%Y-%m-%d', $this->Time()).'/'.$this->{'Id'};
|
||||||
} else {
|
} else {
|
||||||
$event_path = $this->{'MonitorId'} .'/'.$this->{'Id'};
|
$event_path = $this->{'MonitorId'}.'/'.$this->{'Id'};
|
||||||
}
|
}
|
||||||
|
|
||||||
return $event_path;
|
return $event_path;
|
||||||
|
@ -139,24 +145,26 @@ class Event {
|
||||||
|
|
||||||
public function Link_Path() {
|
public function Link_Path() {
|
||||||
if ( $this->{'Scheme'} == 'Deep' ) {
|
if ( $this->{'Scheme'} == 'Deep' ) {
|
||||||
return $this->{'MonitorId'} .'/'.strftime( '%y/%m/%d/.', $this->Time()).$this->{'Id'};
|
return $this->{'MonitorId'}.'/'.strftime('%y/%m/%d/.', $this->Time()).$this->{'Id'};
|
||||||
}
|
}
|
||||||
Error('Calling Link_Path when not using deep storage');
|
Error('Calling Link_Path when not using deep storage');
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete() {
|
public function delete() {
|
||||||
# This wouldn't work with foreign keys
|
if ( ! $this->{'Id'} ) {
|
||||||
dbQuery( 'DELETE FROM Events WHERE Id = ?', array($this->{'Id'}) );
|
Error('Event delete on event with empty Id');
|
||||||
|
return;
|
||||||
|
}
|
||||||
if ( !ZM_OPT_FAST_DELETE ) {
|
if ( !ZM_OPT_FAST_DELETE ) {
|
||||||
dbQuery( 'DELETE FROM Stats WHERE EventId = ?', array($this->{'Id'}) );
|
dbQuery('DELETE FROM Stats WHERE EventId = ?', array($this->{'Id'}));
|
||||||
dbQuery( 'DELETE FROM Frames WHERE EventId = ?', array($this->{'Id'}) );
|
dbQuery('DELETE FROM Frames WHERE EventId = ?', array($this->{'Id'}));
|
||||||
if ( $this->{'Scheme'} == 'Deep' ) {
|
if ( $this->{'Scheme'} == 'Deep' ) {
|
||||||
|
|
||||||
# Assumption: All events have a start time
|
# Assumption: All events have a start time
|
||||||
$start_date = date_parse( $this->{'StartTime'} );
|
$start_date = date_parse($this->{'StartTime'});
|
||||||
if ( ! $start_date ) {
|
if ( ! $start_date ) {
|
||||||
Error('Unable to parse start time for event ' . $this->{'Id'} . ' not deleting files.' );
|
Error('Unable to parse start time for event ' . $this->{'Id'} . ' not deleting files.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$start_date['year'] = $start_date['year'] % 100;
|
$start_date['year'] = $start_date['year'] % 100;
|
||||||
|
@ -164,37 +172,42 @@ class Event {
|
||||||
# So this is because ZM creates a link under the day pointing to the time that the event happened.
|
# So this is because ZM creates a link under the day pointing to the time that the event happened.
|
||||||
$link_path = $this->Link_Path();
|
$link_path = $this->Link_Path();
|
||||||
if ( ! $link_path ) {
|
if ( ! $link_path ) {
|
||||||
Error('Unable to determine link path for event ' . $this->{'Id'} . ' not deleting files.' );
|
Error('Unable to determine link path for event '.$this->{'Id'}.' not deleting files.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$Storage = $this->Storage();
|
$Storage = $this->Storage();
|
||||||
$eventlink_path = $Storage->Path().'/'.$link_path;
|
$eventlink_path = $Storage->Path().'/'.$link_path;
|
||||||
|
|
||||||
if ( $id_files = glob( $eventlink_path ) ) {
|
if ( $id_files = glob($eventlink_path) ) {
|
||||||
if ( ! $eventPath = readlink($id_files[0]) ) {
|
if ( ! $eventPath = readlink($id_files[0]) ) {
|
||||||
Error("Unable to read link at $id_files[0]");
|
Error("Unable to read link at $id_files[0]");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
# I know we are using arrays here, but really there can only ever be 1 in the array
|
# I know we are using arrays here, but really there can only ever be 1 in the array
|
||||||
$eventPath = preg_replace( '/\.'.$this->{'Id'}.'$/', $eventPath, $id_files[0] );
|
$eventPath = preg_replace('/\.'.$this->{'Id'}.'$/', $eventPath, $id_files[0]);
|
||||||
deletePath( $eventPath );
|
deletePath($eventPath);
|
||||||
deletePath( $id_files[0] );
|
deletePath($id_files[0]);
|
||||||
$pathParts = explode( '/', $eventPath );
|
$pathParts = explode('/', $eventPath);
|
||||||
for ( $i = count($pathParts)-1; $i >= 2; $i-- ) {
|
for ( $i = count($pathParts)-1; $i >= 2; $i-- ) {
|
||||||
$deletePath = join( '/', array_slice( $pathParts, 0, $i ) );
|
$deletePath = join('/', array_slice($pathParts, 0, $i));
|
||||||
if ( !glob( $deletePath."/*" ) ) {
|
if ( !glob($deletePath.'/*') ) {
|
||||||
deletePath( $deletePath );
|
deletePath($deletePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Warning( "Found no event files under $eventlink_path" );
|
Warning("Found no event files under $eventlink_path");
|
||||||
} # end if found files
|
} # end if found files
|
||||||
} else {
|
} else {
|
||||||
$eventPath = $this->Path();
|
$eventPath = $this->Path();
|
||||||
deletePath( $eventPath );
|
if ( ! $eventPath ) {
|
||||||
|
Error('No event Path in Event delete. Not deleting');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
deletePath($eventPath);
|
||||||
} # USE_DEEP_STORAGE OR NOT
|
} # USE_DEEP_STORAGE OR NOT
|
||||||
} # ! ZM_OPT_FAST_DELETE
|
} # ! ZM_OPT_FAST_DELETE
|
||||||
|
dbQuery('DELETE FROM Events WHERE Id = ?', array($this->{'Id'}));
|
||||||
} # end Event->delete
|
} # end Event->delete
|
||||||
|
|
||||||
public function getStreamSrc( $args=array(), $querySep='&' ) {
|
public function getStreamSrc( $args=array(), $querySep='&' ) {
|
||||||
|
@ -413,31 +426,31 @@ class Event {
|
||||||
|
|
||||||
$captPath = $eventPath.'/'.$captImage;
|
$captPath = $eventPath.'/'.$captImage;
|
||||||
if ( ! file_exists($captPath) ) {
|
if ( ! file_exists($captPath) ) {
|
||||||
Error( "Capture file does not exist at $captPath" );
|
Error("Capture file does not exist at $captPath");
|
||||||
}
|
}
|
||||||
|
|
||||||
//echo "CI:$captImage, CP:$captPath, TCP:$captPath<br>";
|
//echo "CI:$captImage, CP:$captPath, TCP:$captPath<br>";
|
||||||
|
|
||||||
$analImage = sprintf( '%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyse.jpg', $frame['FrameId'] );
|
$analImage = sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyse.jpg', $frame['FrameId']);
|
||||||
$analPath = $eventPath.'/'.$analImage;
|
$analPath = $eventPath.'/'.$analImage;
|
||||||
|
|
||||||
//echo "AI:$analImage, AP:$analPath, TAP:$analPath<br>";
|
//echo "AI:$analImage, AP:$analPath, TAP:$analPath<br>";
|
||||||
|
|
||||||
$alarmFrame = $frame['Type']=='Alarm';
|
$alarmFrame = $frame['Type']=='Alarm';
|
||||||
|
|
||||||
$hasAnalImage = $alarmFrame && file_exists( $analPath ) && filesize( $analPath );
|
$hasAnalImage = $alarmFrame && file_exists($analPath) && filesize($analPath);
|
||||||
$isAnalImage = $hasAnalImage && !$captureOnly;
|
$isAnalImage = $hasAnalImage && !$captureOnly;
|
||||||
|
|
||||||
if ( !ZM_WEB_SCALE_THUMBS || $scale >= SCALE_BASE || !function_exists( 'imagecreatefromjpeg' ) ) {
|
if ( !ZM_WEB_SCALE_THUMBS || $scale >= SCALE_BASE || !function_exists('imagecreatefromjpeg') ) {
|
||||||
$imagePath = $thumbPath = $isAnalImage?$analPath:$captPath;
|
$imagePath = $thumbPath = $isAnalImage ? $analPath : $captPath;
|
||||||
$imageFile = $imagePath;
|
$imageFile = $imagePath;
|
||||||
$thumbFile = $thumbPath;
|
$thumbFile = $thumbPath;
|
||||||
} else {
|
} else {
|
||||||
if ( version_compare( phpversion(), '4.3.10', '>=') )
|
if ( version_compare( phpversion(), '4.3.10', '>=') )
|
||||||
$fraction = sprintf( '%.3F', $scale/SCALE_BASE );
|
$fraction = sprintf('%.3F', $scale/SCALE_BASE);
|
||||||
else
|
else
|
||||||
$fraction = sprintf( '%.3f', $scale/SCALE_BASE );
|
$fraction = sprintf('%.3f', $scale/SCALE_BASE);
|
||||||
$scale = (int)round( $scale );
|
$scale = (int)round($scale);
|
||||||
|
|
||||||
$thumbCaptPath = preg_replace( '/\.jpg$/', "-$scale.jpg", $captPath );
|
$thumbCaptPath = preg_replace( '/\.jpg$/', "-$scale.jpg", $captPath );
|
||||||
$thumbAnalPath = preg_replace( '/\.jpg$/', "-$scale.jpg", $analPath );
|
$thumbAnalPath = preg_replace( '/\.jpg$/', "-$scale.jpg", $analPath );
|
||||||
|
@ -567,8 +580,8 @@ class Event {
|
||||||
if ( file_exists( $this->Path().'/'.$this->DefaultVideo() ) ) {
|
if ( file_exists( $this->Path().'/'.$this->DefaultVideo() ) ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
$Storage= $this->Storage();
|
$Storage= $this->Storage();
|
||||||
$Server = $Storage->ServerId() ? $Storage->Server() : $this->Monitor()->Server();
|
$Server = $Storage->ServerId() ? $Storage->Server() : $this->Monitor()->Server();
|
||||||
if ( $Server->Id() != ZM_SERVER_ID ) {
|
if ( $Server->Id() != ZM_SERVER_ID ) {
|
||||||
|
|
||||||
$url = $Server->UrlToApi() . '/events/'.$this->{'Id'}.'.json';
|
$url = $Server->UrlToApi() . '/events/'.$this->{'Id'}.'.json';
|
||||||
|
@ -635,14 +648,14 @@ class Event {
|
||||||
'content' => ''
|
'content' => ''
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$context = stream_context_create($options);
|
$context = stream_context_create($options);
|
||||||
try {
|
try {
|
||||||
$result = file_get_contents($url, false, $context);
|
$result = file_get_contents($url, false, $context);
|
||||||
if ($result === FALSE) { /* Handle error */
|
if ( $result === FALSE ) { /* Handle error */
|
||||||
Error("Error restarting zmc using $url");
|
Error("Error restarting zmc using $url");
|
||||||
}
|
}
|
||||||
$event_data = json_decode($result,true);
|
$event_data = json_decode($result,true);
|
||||||
Logger::Debug(print_r($event_data['event']['Event'],1));
|
Logger::Debug(print_r($event_data['event']['Event'], 1));
|
||||||
return $event_data['event']['Event']['fileSize'];
|
return $event_data['event']['Event']['fileSize'];
|
||||||
} catch ( Exception $e ) {
|
} catch ( Exception $e ) {
|
||||||
Error("Except $e thrown trying to get event data");
|
Error("Except $e thrown trying to get event data");
|
||||||
|
|
|
@ -214,11 +214,13 @@ public $defaults = array(
|
||||||
$url = '?user='.$_SESSION['username'];
|
$url = '?user='.$_SESSION['username'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$url .= '&view=filter&action=control&command='.$command.'&Id='.$this->Id().'&ServerId'.$Server->Id();
|
$url .= '&view=filter&action=control&command='.$command.'&Id='.$this->Id().'&ServerId='.$Server->Id();
|
||||||
Logger::Debug("sending command to $url");
|
Logger::Debug("sending command to $url");
|
||||||
$data = array();
|
$data = array();
|
||||||
if ( defined('ZM_ENABLE_CSRF_MAGIC') )
|
if ( defined('ZM_ENABLE_CSRF_MAGIC') ) {
|
||||||
$data['__csrf_magic'] = csrf_get_secret();
|
require_once( 'includes/csrf/csrf-magic.php' );
|
||||||
|
$data['__csrf_magic'] = csrf_get_tokens();
|
||||||
|
}
|
||||||
|
|
||||||
// use key 'http' even if you send the request to https://...
|
// use key 'http' even if you send the request to https://...
|
||||||
$options = array(
|
$options = array(
|
||||||
|
|
|
@ -67,10 +67,11 @@ private $defaults = array(
|
||||||
'Encoder' => 'auto',
|
'Encoder' => 'auto',
|
||||||
'OutputContainer' => 'auto',
|
'OutputContainer' => 'auto',
|
||||||
'Triggers' => null,
|
'Triggers' => null,
|
||||||
|
'AnalysisUpdateDelay' => 0,
|
||||||
'MaxFPS' => null,
|
'MaxFPS' => null,
|
||||||
'AlarmMaxFPS' => null,
|
'AlarmMaxFPS' => null,
|
||||||
'FPSReportIneterval' => 100,
|
'FPSReportInterval' => 100,
|
||||||
'RefBlencPerc' => 6,
|
'RefBlendPerc' => 6,
|
||||||
'AlarmRefBlendPerc' => 6,
|
'AlarmRefBlendPerc' => 6,
|
||||||
'Controllable' => 0,
|
'Controllable' => 0,
|
||||||
'ControlId' => null,
|
'ControlId' => null,
|
||||||
|
@ -81,7 +82,6 @@ private $defaults = array(
|
||||||
'TrackDelay' => null,
|
'TrackDelay' => null,
|
||||||
'ReturnLocation' => -1,
|
'ReturnLocation' => -1,
|
||||||
'ReturnDelay' => null,
|
'ReturnDelay' => null,
|
||||||
'DefaultView' => 'Events',
|
|
||||||
'DefaultRate' => 100,
|
'DefaultRate' => 100,
|
||||||
'DefaultScale' => 100,
|
'DefaultScale' => 100,
|
||||||
'SignalCheckPoints' => 0,
|
'SignalCheckPoints' => 0,
|
||||||
|
@ -117,6 +117,7 @@ private $control_fields = array(
|
||||||
'CanWake' => '0',
|
'CanWake' => '0',
|
||||||
'CanSleep' => '0',
|
'CanSleep' => '0',
|
||||||
'CanReset' => '0',
|
'CanReset' => '0',
|
||||||
|
'CanReboot' => '0',
|
||||||
'CanZoom' => '0',
|
'CanZoom' => '0',
|
||||||
'CanAutoZoom' => '0',
|
'CanAutoZoom' => '0',
|
||||||
'CanZoomAbs' => '0',
|
'CanZoomAbs' => '0',
|
||||||
|
@ -311,7 +312,7 @@ private $control_fields = array(
|
||||||
$args['rand'] = time();
|
$args['rand'] = time();
|
||||||
}
|
}
|
||||||
|
|
||||||
$streamSrc .= '?'.http_build_query($args,'', $querySep);
|
$streamSrc .= '?'.http_build_query($args, '', $querySep);
|
||||||
|
|
||||||
return $streamSrc;
|
return $streamSrc;
|
||||||
} // end function getStreamSrc
|
} // end function getStreamSrc
|
||||||
|
@ -656,7 +657,8 @@ private $control_fields = array(
|
||||||
} // end function Source
|
} // end function Source
|
||||||
|
|
||||||
public function UrlToIndex() {
|
public function UrlToIndex() {
|
||||||
return $this->Server()->UrlToIndex(ZM_MIN_STREAMING_PORT ? (ZM_MIN_STREAMING_PORT+$this->Id()) : null);
|
return $this->Server()->UrlToIndex();
|
||||||
|
//ZM_MIN_STREAMING_PORT ? (ZM_MIN_STREAMING_PORT+$this->Id()) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end class Monitor
|
} // end class Monitor
|
||||||
|
|
|
@ -7,17 +7,18 @@ $server_cache = array();
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
private $defaults = array(
|
private $defaults = array(
|
||||||
'Id' => null,
|
'Id' => null,
|
||||||
'Name' => '',
|
'Name' => '',
|
||||||
'Protocol' => '',
|
'Protocol' => '',
|
||||||
'Hostname' => '',
|
'Hostname' => '',
|
||||||
'Port' => null,
|
'Port' => null,
|
||||||
'PathToIndex' => null,
|
'PathToIndex' => null,
|
||||||
'PathToZMS' => ZM_PATH_ZMS,
|
'PathToZMS' => ZM_PATH_ZMS,
|
||||||
'PathToApi' => '/zm/api',
|
'PathToApi' => '/zm/api',
|
||||||
'zmaudit' => 1,
|
'zmaudit' => 1,
|
||||||
'zmstats' => 1,
|
'zmstats' => 1,
|
||||||
'zmtrigger' => 0,
|
'zmtrigger' => 0,
|
||||||
|
'zmeventnotification' => 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
public function __construct($IdOrRow = NULL) {
|
public function __construct($IdOrRow = NULL) {
|
||||||
|
|
|
@ -28,13 +28,14 @@ if ( ! canView('Control', $_REQUEST['mid']) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once('control_functions.php');
|
require_once('includes/control_functions.php');
|
||||||
require_once('Monitor.php');
|
require_once('includes/Monitor.php');
|
||||||
$mid = validInt($_REQUEST['mid']);
|
$mid = validInt($_REQUEST['mid']);
|
||||||
if ( $action == 'control' ) {
|
if ( $action == 'control' ) {
|
||||||
$monitor = new ZM\Monitor($mid);
|
$monitor = new ZM\Monitor($mid);
|
||||||
|
|
||||||
$ctrlCommand = buildControlCommand($monitor);
|
$ctrlCommand = buildControlCommand($monitor);
|
||||||
sendControlCommand($monitor->Id(), $ctrlCommand);
|
sendControlCommand($monitor->Id(), $ctrlCommand);
|
||||||
|
$view = 'none';
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -27,28 +27,32 @@ if ( $action == 'donate' && isset($_REQUEST['option']) ) {
|
||||||
$option = $_REQUEST['option'];
|
$option = $_REQUEST['option'];
|
||||||
switch( $option ) {
|
switch( $option ) {
|
||||||
case 'go' :
|
case 'go' :
|
||||||
// Ignore this, the caller will open the page itself
|
// Ignore this, the caller will open the page itself
|
||||||
break;
|
break;
|
||||||
case 'hour' :
|
case 'hour' :
|
||||||
case 'day' :
|
case 'day' :
|
||||||
case 'week' :
|
case 'week' :
|
||||||
case 'month' :
|
case 'month' :
|
||||||
$nextReminder = time();
|
$nextReminder = time();
|
||||||
if ( $option == 'hour' ) {
|
if ( $option == 'hour' ) {
|
||||||
$nextReminder += 60*60;
|
$nextReminder += 60*60;
|
||||||
} elseif ( $option == 'day' ) {
|
} elseif ( $option == 'day' ) {
|
||||||
$nextReminder += 24*60*60;
|
$nextReminder += 24*60*60;
|
||||||
} elseif ( $option == 'week' ) {
|
} elseif ( $option == 'week' ) {
|
||||||
$nextReminder += 7*24*60*60;
|
$nextReminder += 7*24*60*60;
|
||||||
} elseif ( $option == 'month' ) {
|
} elseif ( $option == 'month' ) {
|
||||||
$nextReminder += 30*24*60*60;
|
$nextReminder += 30*24*60*60;
|
||||||
}
|
}
|
||||||
dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_DONATE_REMINDER_TIME'");
|
dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_DONATE_REMINDER_TIME'");
|
||||||
break;
|
break;
|
||||||
case 'never' :
|
case 'never' :
|
||||||
case 'already' :
|
case 'already' :
|
||||||
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_DYN_SHOW_DONATE_REMINDER'");
|
dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_DYN_SHOW_DONATE_REMINDER'");
|
||||||
break;
|
break;
|
||||||
|
default :
|
||||||
|
Warning("Unknown value for option in donate: $option");
|
||||||
|
break;
|
||||||
} // end switch option
|
} // end switch option
|
||||||
|
$view = 'none';
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -31,21 +31,21 @@ if ( !canEdit('Events') ) {
|
||||||
|
|
||||||
if ( $action == 'archive' ) {
|
if ( $action == 'archive' ) {
|
||||||
$dbConn->beginTransaction();
|
$dbConn->beginTransaction();
|
||||||
foreach( getAffectedIds('markEid') as $markEid ) {
|
foreach( getAffectedIds('eids') as $markEid ) {
|
||||||
dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array(1, $markEid));
|
dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array(1, $markEid));
|
||||||
}
|
}
|
||||||
$dbConn->commit();
|
$dbConn->commit();
|
||||||
$refreshParent = true;
|
$refreshParent = true;
|
||||||
} else if ( $action == 'unarchive' ) {
|
} else if ( $action == 'unarchive' ) {
|
||||||
$dbConn->beginTransaction();
|
$dbConn->beginTransaction();
|
||||||
foreach( getAffectedIds('markEid') as $markEid ) {
|
foreach( getAffectedIds('eids') as $markEid ) {
|
||||||
dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array(0, $markEid));
|
dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array(0, $markEid));
|
||||||
}
|
}
|
||||||
$dbConn->commit();
|
$dbConn->commit();
|
||||||
$refreshParent = true;
|
$refreshParent = true;
|
||||||
} else if ( $action == 'delete' ) {
|
} else if ( $action == 'delete' ) {
|
||||||
$dbConn->beginTransaction();
|
$dbConn->beginTransaction();
|
||||||
foreach ( getAffectedIds('markEid') as $markEid ) {
|
foreach ( getAffectedIds('eids') as $markEid ) {
|
||||||
deleteEvent($markEid);
|
deleteEvent($markEid);
|
||||||
}
|
}
|
||||||
$dbConn->commit();
|
$dbConn->commit();
|
||||||
|
|
|
@ -81,7 +81,7 @@ if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) {
|
||||||
} else {
|
} else {
|
||||||
dbQuery('INSERT INTO Filters SET'.$sql);
|
dbQuery('INSERT INTO Filters SET'.$sql);
|
||||||
$_REQUEST['Id'] = dbInsertId();
|
$_REQUEST['Id'] = dbInsertId();
|
||||||
$filter = new Filter($_REQUEST['Id']);
|
$filter = new ZM\Filter($_REQUEST['Id']);
|
||||||
}
|
}
|
||||||
if ( !empty($_REQUEST['filter']['Background']) )
|
if ( !empty($_REQUEST['filter']['Background']) )
|
||||||
$filter->control('start');
|
$filter->control('start');
|
||||||
|
|
|
@ -18,33 +18,6 @@
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
//
|
//
|
||||||
|
|
||||||
// I think this is saving a Monitor from ajax...
|
|
||||||
|
|
||||||
if ( isset($_REQUEST['object']) and $_REQUEST['object'] == 'Monitor' ) {
|
|
||||||
if ( $action == 'save' ) {
|
|
||||||
foreach ( $_REQUEST['mids'] as $mid ) {
|
|
||||||
$mid = ValidInt($mid);
|
|
||||||
if ( ! canEdit('Monitors', $mid) ) {
|
|
||||||
ZM\Warning("Cannot edit monitor $mid");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$Monitor = new ZM\Monitor($mid);
|
|
||||||
if ( $Monitor->Type() != 'WebSite' ) {
|
|
||||||
$Monitor->zmaControl('stop');
|
|
||||||
$Monitor->zmcControl('stop');
|
|
||||||
}
|
|
||||||
$Monitor->save($_REQUEST['newMonitor']);
|
|
||||||
if ( $Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) {
|
|
||||||
$Monitor->zmcControl('start');
|
|
||||||
if ( $Monitor->Enabled() ) {
|
|
||||||
$Monitor->zmaControl('start');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // end foreach mid
|
|
||||||
$refreshParent = true;
|
|
||||||
} // end if action == save
|
|
||||||
} // end if object is Monitor
|
|
||||||
|
|
||||||
// Monitor edit actions, monitor id derived, require edit permissions for that monitor
|
// Monitor edit actions, monitor id derived, require edit permissions for that monitor
|
||||||
if ( ! canEdit('Monitors') ) {
|
if ( ! canEdit('Monitors') ) {
|
||||||
ZM\Warning("Monitor actions require Monitors Permissions");
|
ZM\Warning("Monitor actions require Monitors Permissions");
|
||||||
|
@ -113,10 +86,15 @@ if ( $action == 'monitor' ) {
|
||||||
unlink($OldStorage->Path().'/'.$saferOldName);
|
unlink($OldStorage->Path().'/'.$saferOldName);
|
||||||
|
|
||||||
$NewStorage = new ZM\Storage($_REQUEST['newMonitor']['StorageId']);
|
$NewStorage = new ZM\Storage($_REQUEST['newMonitor']['StorageId']);
|
||||||
if ( ! file_exists($NewStorage->Path().'/'.$mid) )
|
if ( !file_exists($NewStorage->Path().'/'.$mid) ) {
|
||||||
mkdir($NewStorage->Path().'/'.$mid, 0755);
|
if ( !mkdir($NewStorage->Path().'/'.$mid, 0755) ) {
|
||||||
|
Error('Unable to mkdir ' . $NewStorage->Path().'/'.$mid);
|
||||||
|
}
|
||||||
|
}
|
||||||
$saferNewName = basename($_REQUEST['newMonitor']['Name']);
|
$saferNewName = basename($_REQUEST['newMonitor']['Name']);
|
||||||
symlink($mid, $NewStorage->Path().'/'.$saferNewName);
|
if ( !symlink($NewStorage->Path().'/'.$mid, $NewStorage->Path().'/'.$saferNewName) ) {
|
||||||
|
Warning('Unable to symlink ' . $NewStorage->Path().'/'.$mid . ' to ' . $NewStorage->Path().'/'.$saferNewName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ( isset($changes['Width']) || isset($changes['Height']) ) {
|
if ( isset($changes['Width']) || isset($changes['Height']) ) {
|
||||||
$newW = $_REQUEST['newMonitor']['Width'];
|
$newW = $_REQUEST['newMonitor']['Width'];
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
//
|
||||||
|
// ZoneMinder web action file
|
||||||
|
// Copyright (C) 2019 ZoneMinder LLC
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Monitor edit actions, monitor id derived, require edit permissions for that monitor
|
||||||
|
if ( ! canEdit('Monitors') ) {
|
||||||
|
ZM\Warning("Monitor actions require Monitors Permissions");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $action == 'save' ) {
|
||||||
|
foreach ( $_REQUEST['mids'] as $mid ) {
|
||||||
|
$mid = ValidInt($mid);
|
||||||
|
if ( ! canEdit('Monitors', $mid) ) {
|
||||||
|
ZM\Warning("Cannot edit monitor $mid");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$Monitor = new ZM\Monitor($mid);
|
||||||
|
if ( $Monitor->Type() != 'WebSite' ) {
|
||||||
|
$Monitor->zmaControl('stop');
|
||||||
|
$Monitor->zmcControl('stop');
|
||||||
|
}
|
||||||
|
$Monitor->save($_REQUEST['newMonitor']);
|
||||||
|
if ( $Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) {
|
||||||
|
$Monitor->zmcControl('start');
|
||||||
|
if ( $Monitor->Enabled() ) {
|
||||||
|
$Monitor->zmaControl('start');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // end foreach mid
|
||||||
|
$refreshParent = true;
|
||||||
|
$view = 'none';
|
||||||
|
} else {
|
||||||
|
ZM\Warning("Unknown action $action in Monitor");
|
||||||
|
} // end if action == Delete
|
||||||
|
?>
|
|
@ -29,10 +29,10 @@ if ( isset($_REQUEST['object']) ) {
|
||||||
if ( $action == 'Save' ) {
|
if ( $action == 'Save' ) {
|
||||||
$Layout = null;
|
$Layout = null;
|
||||||
if ( $_REQUEST['Name'] != '' ) {
|
if ( $_REQUEST['Name'] != '' ) {
|
||||||
$Layout = new MontageLayout();
|
$Layout = new ZM\MontageLayout();
|
||||||
$Layout->Name($_REQUEST['Name']);
|
$Layout->Name($_REQUEST['Name']);
|
||||||
} else {
|
} else {
|
||||||
$Layout = new MontageLayout($_REQUEST['zmMontageLayout']);
|
$Layout = new ZM\MontageLayout($_REQUEST['zmMontageLayout']);
|
||||||
}
|
}
|
||||||
$Layout->Positions($_REQUEST['Positions']);
|
$Layout->Positions($_REQUEST['Positions']);
|
||||||
$Layout->save();
|
$Layout->save();
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
//
|
||||||
|
// ZoneMinder web action file
|
||||||
|
// Copyright (C) 2019 ZoneMinder LLC
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
if ( !canEdit('System') ) {
|
||||||
|
ZM\Warning('Need System permissions to shutdown server');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( $action ) {
|
||||||
|
$when = isset($_POST['when']) and $_POST['when'] == 'now' ? 'now' : '+1';
|
||||||
|
if ( $action == 'shutdown' ) {
|
||||||
|
$output = array();
|
||||||
|
$rc = 0;
|
||||||
|
exec("sudo -n /sbin/shutdown -P $when 2>&1", $output, $rc);
|
||||||
|
#exec('sudo -n /bin/systemctl poweroff -i 2>&1', $output, $rc);
|
||||||
|
ZM\Logger::Debug("Shutdown output $rc " . implode("\n",$output));
|
||||||
|
#ZM\Logger::Debug("Shutdown output " . shell_exec('/bin/systemctl poweroff -i 2>&1'));
|
||||||
|
} else if ( $action == 'restart' ) {
|
||||||
|
$output = array();
|
||||||
|
exec("sudo -n /sbin/shutdown -r $when 2>&1", $output);
|
||||||
|
#exec('sudo -n /bin/systemctl reboot -i 2>&1', $output);
|
||||||
|
ZM\Logger::Debug("Shutdown output " . implode("\n",$output));
|
||||||
|
} else if ( $action == 'cancel' ) {
|
||||||
|
$output = array();
|
||||||
|
exec('sudo /sbin/shutdown -c 2>&1', $output);
|
||||||
|
}
|
||||||
|
} # end if action
|
||||||
|
?>
|
|
@ -82,11 +82,6 @@ function userLogin($username='', $password='', $passwordHashed=false) {
|
||||||
session_start();
|
session_start();
|
||||||
$close_session = 1;
|
$close_session = 1;
|
||||||
}
|
}
|
||||||
$_SESSION['username'] = $username;
|
|
||||||
if ( ZM_AUTH_RELAY == 'plain' ) {
|
|
||||||
// Need to save this in session
|
|
||||||
$_SESSION['password'] = $password;
|
|
||||||
}
|
|
||||||
$_SESSION['remoteAddr'] = $_SERVER['REMOTE_ADDR']; // To help prevent session hijacking
|
$_SESSION['remoteAddr'] = $_SERVER['REMOTE_ADDR']; // To help prevent session hijacking
|
||||||
if ( $dbUser = dbFetchOne($sql, NULL, $sql_values) ) {
|
if ( $dbUser = dbFetchOne($sql, NULL, $sql_values) ) {
|
||||||
ZM\Info("Login successful for user \"$username\"");
|
ZM\Info("Login successful for user \"$username\"");
|
||||||
|
@ -95,6 +90,11 @@ function userLogin($username='', $password='', $passwordHashed=false) {
|
||||||
if ( ZM_AUTH_TYPE == 'builtin' ) {
|
if ( ZM_AUTH_TYPE == 'builtin' ) {
|
||||||
$_SESSION['passwordHash'] = $user['Password'];
|
$_SESSION['passwordHash'] = $user['Password'];
|
||||||
}
|
}
|
||||||
|
$_SESSION['username'] = $user['Username'];
|
||||||
|
if ( ZM_AUTH_RELAY == 'plain' ) {
|
||||||
|
// Need to save this in session, can't use the value in User because it is hashed
|
||||||
|
$_SESSION['password'] = $_REQUEST['password'];
|
||||||
|
}
|
||||||
zm_session_regenerate_id();
|
zm_session_regenerate_id();
|
||||||
} else {
|
} else {
|
||||||
ZM\Warning("Login denied for user \"$username\"");
|
ZM\Warning("Login denied for user \"$username\"");
|
||||||
|
@ -124,17 +124,19 @@ function getAuthUser($auth) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$values = array();
|
||||||
if ( isset($_SESSION['username']) ) {
|
if ( isset($_SESSION['username']) ) {
|
||||||
# Most of the time we will be logged in already and the session will have our username, so we can significantly speed up our hash testing by only looking at our user.
|
# Most of the time we will be logged in already and the session will have our username, so we can significantly speed up our hash testing by only looking at our user.
|
||||||
# Only really important if you have a lot of users.
|
# Only really important if you have a lot of users.
|
||||||
$sql = "SELECT * FROM Users WHERE Enabled = 1 AND Username='".$_SESSION['username']."'";
|
$sql = 'SELECT * FROM Users WHERE Enabled = 1 AND Username=?';
|
||||||
|
array_push($values, $_SESSION['username']);
|
||||||
} else {
|
} else {
|
||||||
$sql = 'SELECT * FROM Users WHERE Enabled = 1';
|
$sql = 'SELECT * FROM Users WHERE Enabled = 1';
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ( dbFetchAll($sql) as $user ) {
|
foreach ( dbFetchAll($sql, NULL, $values) as $user ) {
|
||||||
$now = time();
|
$now = time();
|
||||||
for ( $i = 0; $i < ZM_AUTH_HASH_TTL; $i++, $now -= (3600) ) { // Try for last two hours
|
for ( $i = 0; $i < ZM_AUTH_HASH_TTL; $i++, $now -= ZM_AUTH_HASH_TTL * 1800 ) { // Try for last two hours
|
||||||
$time = localtime($now);
|
$time = localtime($now);
|
||||||
$authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$remoteAddr.$time[2].$time[3].$time[4].$time[5];
|
$authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$remoteAddr.$time[2].$time[3].$time[4].$time[5];
|
||||||
$authHash = md5($authKey);
|
$authHash = md5($authKey);
|
||||||
|
@ -205,19 +207,20 @@ function canEdit($area, $mid=false) {
|
||||||
return ( $user[$area] == 'Edit' && ( !$mid || visibleMonitor($mid) ));
|
return ( $user[$area] == 'Edit' && ( !$mid || visibleMonitor($mid) ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
global $user;
|
||||||
if ( ZM_OPT_USE_AUTH ) {
|
if ( ZM_OPT_USE_AUTH ) {
|
||||||
|
$close_session = 0;
|
||||||
|
if ( !is_session_started() ) {
|
||||||
|
zm_session_start();
|
||||||
|
$close_session = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if ( isset($_SESSION['username']) ) {
|
if ( isset($_SESSION['username']) ) {
|
||||||
# Need to refresh permissions and validate that the user still exists
|
# Need to refresh permissions and validate that the user still exists
|
||||||
$sql = 'SELECT * FROM Users WHERE Enabled=1 AND Username=?';
|
$sql = 'SELECT * FROM Users WHERE Enabled=1 AND Username=?';
|
||||||
$user = dbFetchOne($sql, NULL, array($_SESSION['username']));
|
$user = dbFetchOne($sql, NULL, array($_SESSION['username']));
|
||||||
}
|
}
|
||||||
|
|
||||||
$close_session = 0;
|
|
||||||
if ( !is_session_started() ) {
|
|
||||||
session_start();
|
|
||||||
$close_session = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ZM_AUTH_RELAY == 'plain' ) {
|
if ( ZM_AUTH_RELAY == 'plain' ) {
|
||||||
// Need to save this in session
|
// Need to save this in session
|
||||||
$_SESSION['password'] = $password;
|
$_SESSION['password'] = $password;
|
||||||
|
|
|
@ -735,29 +735,29 @@ function buildControlCommand( $monitor ) {
|
||||||
return( $ctrlCommand );
|
return( $ctrlCommand );
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendControlCommand($mid,$command) {
|
function sendControlCommand($mid, $command) {
|
||||||
// Either connects to running zmcontrol.pl or runs zmcontrol.pl to send the command.
|
// Either connects to running zmcontrol.pl or runs zmcontrol.pl to send the command.
|
||||||
$socket = socket_create( AF_UNIX, SOCK_STREAM, 0 );
|
$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
|
||||||
if ( $socket < 0 ) {
|
if ( $socket < 0 ) {
|
||||||
Fatal( 'socket_create() failed: '.socket_strerror($socket) );
|
Fatal('socket_create() failed: '.socket_strerror($socket));
|
||||||
}
|
}
|
||||||
$sockFile = ZM_PATH_SOCKS.'/zmcontrol-'.$mid.'.sock';
|
$sockFile = ZM_PATH_SOCKS.'/zmcontrol-'.$mid.'.sock';
|
||||||
if ( @socket_connect( $socket, $sockFile ) ) {
|
if ( @socket_connect($socket, $sockFile) ) {
|
||||||
$options = array();
|
$options = array();
|
||||||
foreach ( explode( ' ', $command ) as $option ) {
|
foreach ( explode(' ', $command) as $option ) {
|
||||||
if ( preg_match( '/--([^=]+)(?:=(.+))?/', $option, $matches ) ) {
|
if ( preg_match('/--([^=]+)(?:=(.+))?/', $option, $matches) ) {
|
||||||
$options[$matches[1]] = $matches[2]?$matches[2]:1;
|
$options[$matches[1]] = $matches[2]?$matches[2]:1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$optionString = jsonEncode( $options );
|
$optionString = jsonEncode($options);
|
||||||
if ( !socket_write( $socket, $optionString ) ) {
|
if ( !socket_write($socket, $optionString) ) {
|
||||||
Fatal( "Can't write to control socket: ".socket_strerror(socket_last_error($socket)) );
|
Fatal("Can't write to control socket: ".socket_strerror(socket_last_error($socket)));
|
||||||
}
|
}
|
||||||
socket_close( $socket );
|
socket_close($socket);
|
||||||
} else if ( $command != 'quit' ) {
|
} else if ( $command != 'quit' ) {
|
||||||
$command .= ' --id='.$mid;
|
$command .= ' --id='.$mid;
|
||||||
|
|
||||||
// Can't connect so use script
|
// Can't connect so use script
|
||||||
$ctrlOutput = exec( escapeshellcmd( $command ) );
|
$ctrlOutput = exec(escapeshellcmd($command));
|
||||||
}
|
}
|
||||||
} // end function sendControlCommand( $mid, $command )
|
} // end function sendControlCommand($mid, $command)
|
||||||
|
|
|
@ -9,183 +9,185 @@
|
||||||
// The wrapper must be set BEFORE onreadystatechange is written to, since
|
// The wrapper must be set BEFORE onreadystatechange is written to, since
|
||||||
// a bug in ActiveXObject prevents us from properly testing for it.
|
// a bug in ActiveXObject prevents us from properly testing for it.
|
||||||
CsrfMagic = function(real) {
|
CsrfMagic = function(real) {
|
||||||
// try to make it ourselves, if you didn't pass it
|
// try to make it ourselves, if you didn't pass it
|
||||||
if (!real) try { real = new XMLHttpRequest; } catch (e) {;}
|
if (!real) try {real = new XMLHttpRequest;} catch (e) {;}
|
||||||
if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) {;}
|
if (!real) try {real = new ActiveXObject('Msxml2.XMLHTTP');} catch (e) {;}
|
||||||
if (!real) try { real = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {;}
|
if (!real) try {real = new ActiveXObject('Microsoft.XMLHTTP');} catch (e) {;}
|
||||||
if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP.4.0'); } catch (e) {;}
|
if (!real) try {real = new ActiveXObject('Msxml2.XMLHTTP.4.0');} catch (e) {;}
|
||||||
this.csrf = real;
|
this.csrf = real;
|
||||||
// properties
|
// properties
|
||||||
var csrfMagic = this;
|
var csrfMagic = this;
|
||||||
real.onreadystatechange = function() {
|
real.onreadystatechange = function() {
|
||||||
csrfMagic._updateProps();
|
|
||||||
return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null;
|
|
||||||
};
|
|
||||||
csrfMagic._updateProps();
|
csrfMagic._updateProps();
|
||||||
}
|
return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null;
|
||||||
|
};
|
||||||
|
csrfMagic._updateProps();
|
||||||
|
};
|
||||||
|
|
||||||
CsrfMagic.prototype = {
|
CsrfMagic.prototype = {
|
||||||
|
|
||||||
open: function(method, url, async, username, password) {
|
open: function(method, url, async, username, password) {
|
||||||
if (method == 'POST') this.csrf_isPost = true;
|
if (method == 'POST') this.csrf_isPost = true;
|
||||||
// deal with Opera bug, thanks jQuery
|
// deal with Opera bug, thanks jQuery
|
||||||
if (username) return this.csrf_open(method, url, async, username, password);
|
if (username) return this.csrf_open(method, url, async, username, password);
|
||||||
else return this.csrf_open(method, url, async);
|
else return this.csrf_open(method, url, async);
|
||||||
},
|
},
|
||||||
csrf_open: function(method, url, async, username, password) {
|
csrf_open: function(method, url, async, username, password) {
|
||||||
if (username) return this.csrf.open(method, url, async, username, password);
|
if (username) return this.csrf.open(method, url, async, username, password);
|
||||||
else return this.csrf.open(method, url, async);
|
else return this.csrf.open(method, url, async);
|
||||||
},
|
},
|
||||||
|
|
||||||
send: function(data) {
|
send: function(data) {
|
||||||
if (!this.csrf_isPost) return this.csrf_send(data);
|
if (!this.csrf_isPost) return this.csrf_send(data);
|
||||||
prepend = csrfMagicName + '=' + csrfMagicToken + '&';
|
prepend = csrfMagicName + '=' + csrfMagicToken + '&';
|
||||||
// XXX: Removed to eliminate 'Refused to set unsafe header "Content-length" ' errors in modern browsers
|
// XXX: Removed to eliminate 'Refused to set unsafe header "Content-length" ' errors in modern browsers
|
||||||
// if (this.csrf_purportedLength === undefined) {
|
// if (this.csrf_purportedLength === undefined) {
|
||||||
// this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length);
|
// this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length);
|
||||||
// delete this.csrf_purportedLength;
|
// delete this.csrf_purportedLength;
|
||||||
// }
|
// }
|
||||||
delete this.csrf_isPost;
|
delete this.csrf_isPost;
|
||||||
return this.csrf_send(prepend + data);
|
return this.csrf_send(prepend + data);
|
||||||
},
|
},
|
||||||
csrf_send: function(data) {
|
csrf_send: function(data) {
|
||||||
return this.csrf.send(data);
|
return this.csrf.send(data);
|
||||||
},
|
},
|
||||||
|
|
||||||
setRequestHeader: function(header, value) {
|
setRequestHeader: function(header, value) {
|
||||||
// We have to auto-set this at the end, since we don't know how long the
|
// We have to auto-set this at the end, since we don't know how long the
|
||||||
// nonce is when added to the data.
|
// nonce is when added to the data.
|
||||||
if (this.csrf_isPost && header == "Content-length") {
|
if (this.csrf_isPost && header == "Content-length") {
|
||||||
this.csrf_purportedLength = value;
|
this.csrf_purportedLength = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return this.csrf_setRequestHeader(header, value);
|
return this.csrf_setRequestHeader(header, value);
|
||||||
},
|
},
|
||||||
csrf_setRequestHeader: function(header, value) {
|
csrf_setRequestHeader: function(header, value) {
|
||||||
return this.csrf.setRequestHeader(header, value);
|
return this.csrf.setRequestHeader(header, value);
|
||||||
},
|
},
|
||||||
|
|
||||||
abort: function() {
|
abort: function() {
|
||||||
return this.csrf.abort();
|
return this.csrf.abort();
|
||||||
},
|
},
|
||||||
getAllResponseHeaders: function() {
|
getAllResponseHeaders: function() {
|
||||||
return this.csrf.getAllResponseHeaders();
|
return this.csrf.getAllResponseHeaders();
|
||||||
},
|
},
|
||||||
getResponseHeader: function(header) {
|
getResponseHeader: function(header) {
|
||||||
return this.csrf.getResponseHeader(header);
|
return this.csrf.getResponseHeader(header);
|
||||||
} // ,
|
} // ,
|
||||||
}
|
};
|
||||||
|
|
||||||
// proprietary
|
// proprietary
|
||||||
CsrfMagic.prototype._updateProps = function() {
|
CsrfMagic.prototype._updateProps = function() {
|
||||||
this.readyState = this.csrf.readyState;
|
this.readyState = this.csrf.readyState;
|
||||||
if (this.readyState == 4) {
|
if (this.readyState == 4) {
|
||||||
this.responseText = this.csrf.responseText;
|
this.responseText = this.csrf.responseText;
|
||||||
this.responseXML = this.csrf.responseXML;
|
this.responseXML = this.csrf.responseXML;
|
||||||
this.status = this.csrf.status;
|
this.status = this.csrf.status;
|
||||||
this.statusText = this.csrf.statusText;
|
this.statusText = this.csrf.statusText;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
CsrfMagic.process = function(base) {
|
CsrfMagic.process = function(base) {
|
||||||
if(typeof base == 'object') {
|
if ( typeof base == 'object' ) {
|
||||||
base[csrfMagicName] = csrfMagicToken;
|
base[csrfMagicName] = csrfMagicToken;
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
var prepend = csrfMagicName + '=' + csrfMagicToken;
|
var prepend = csrfMagicName + '=' + csrfMagicToken;
|
||||||
if (base) return prepend + '&' + base;
|
if ( base ) return prepend + '&' + base;
|
||||||
return prepend;
|
return prepend;
|
||||||
}
|
};
|
||||||
|
|
||||||
// callback function for when everything on the page has loaded
|
// callback function for when everything on the page has loaded
|
||||||
CsrfMagic.end = function() {
|
CsrfMagic.end = function() {
|
||||||
// This rewrites forms AGAIN, so in case buffering didn't work this
|
// This rewrites forms AGAIN, so in case buffering didn't work this
|
||||||
// certainly will.
|
// certainly will.
|
||||||
forms = document.getElementsByTagName('form');
|
forms = document.getElementsByTagName('form');
|
||||||
for (var i = 0; i < forms.length; i++) {
|
for (var i = 0; i < forms.length; i++) {
|
||||||
form = forms[i];
|
form = forms[i];
|
||||||
if (form.method.toUpperCase() !== 'POST') continue;
|
if (form.method.toUpperCase() !== 'POST') continue;
|
||||||
if (form.elements[csrfMagicName]) continue;
|
if (form.elements[csrfMagicName]) continue;
|
||||||
var input = document.createElement('input');
|
var input = document.createElement('input');
|
||||||
input.setAttribute('name', csrfMagicName);
|
input.setAttribute('name', csrfMagicName);
|
||||||
input.setAttribute('value', csrfMagicToken);
|
input.setAttribute('value', csrfMagicToken);
|
||||||
input.setAttribute('type', 'hidden');
|
input.setAttribute('type', 'hidden');
|
||||||
form.appendChild(input);
|
form.appendChild(input);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// Sets things up for Mozilla/Opera/nice browsers
|
// Sets things up for Mozilla/Opera/nice browsers
|
||||||
// We very specifically match against Internet Explorer, since they haven't
|
// We very specifically match against Internet Explorer, since they haven't
|
||||||
// implemented prototypes correctly yet.
|
// implemented prototypes correctly yet.
|
||||||
if (window.XMLHttpRequest && window.XMLHttpRequest.prototype && '\v' != 'v') {
|
if ( window.XMLHttpRequest && window.XMLHttpRequest.prototype && '\v' != 'v' ) {
|
||||||
var x = XMLHttpRequest.prototype;
|
var x = XMLHttpRequest.prototype;
|
||||||
var c = CsrfMagic.prototype;
|
var c = CsrfMagic.prototype;
|
||||||
|
|
||||||
// Save the original functions
|
// Save the original functions
|
||||||
x.csrf_open = x.open;
|
x.csrf_open = x.open;
|
||||||
x.csrf_send = x.send;
|
x.csrf_send = x.send;
|
||||||
x.csrf_setRequestHeader = x.setRequestHeader;
|
x.csrf_setRequestHeader = x.setRequestHeader;
|
||||||
|
|
||||||
// Notice that CsrfMagic is itself an instantiatable object, but only
|
// Notice that CsrfMagic is itself an instantiatable object, but only
|
||||||
// open, send and setRequestHeader are necessary as decorators.
|
// open, send and setRequestHeader are necessary as decorators.
|
||||||
x.open = c.open;
|
x.open = c.open;
|
||||||
x.send = c.send;
|
x.send = c.send;
|
||||||
x.setRequestHeader = c.setRequestHeader;
|
x.setRequestHeader = c.setRequestHeader;
|
||||||
} else {
|
} else {
|
||||||
// The only way we can do this is by modifying a library you have been
|
// The only way we can do this is by modifying a library you have been
|
||||||
// using. We support YUI, script.aculo.us, prototype, MooTools,
|
// using. We support YUI, script.aculo.us, prototype, MooTools,
|
||||||
// jQuery, Ext and Dojo.
|
// jQuery, Ext and Dojo.
|
||||||
if (window.jQuery) {
|
if ( window.jQuery ) {
|
||||||
// jQuery didn't implement a new XMLHttpRequest function, so we have
|
// jQuery didn't implement a new XMLHttpRequest function, so we have
|
||||||
// to do this the hard way.
|
// to do this the hard way.
|
||||||
jQuery.csrf_ajax = jQuery.ajax;
|
jQuery.csrf_ajax = jQuery.ajax;
|
||||||
jQuery.ajax = function( s ) {
|
jQuery.ajax = function( s ) {
|
||||||
if (s.type && s.type.toUpperCase() == 'POST') {
|
if (s.type && s.type.toUpperCase() == 'POST') {
|
||||||
s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
|
s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
|
||||||
if ( s.data && s.processData && typeof s.data != "string" ) {
|
if ( s.data && s.processData && typeof s.data != "string" ) {
|
||||||
s.data = jQuery.param(s.data);
|
s.data = jQuery.param(s.data);
|
||||||
}
|
|
||||||
s.data = CsrfMagic.process(s.data);
|
|
||||||
}
|
|
||||||
return jQuery.csrf_ajax( s );
|
|
||||||
}
|
}
|
||||||
}
|
s.data = CsrfMagic.process(s.data);
|
||||||
if (window.Prototype) {
|
}
|
||||||
// This works for script.aculo.us too
|
return jQuery.csrf_ajax(s);
|
||||||
Ajax.csrf_getTransport = Ajax.getTransport;
|
};
|
||||||
Ajax.getTransport = function() {
|
}
|
||||||
return new CsrfMagic(Ajax.csrf_getTransport());
|
if ( window.Prototype ) {
|
||||||
}
|
// This works for script.aculo.us too
|
||||||
}
|
Ajax.csrf_getTransport = Ajax.getTransport;
|
||||||
if (window.MooTools) {
|
Ajax.getTransport = function() {
|
||||||
Browser.csrf_Request = Browser.Request;
|
return new CsrfMagic(Ajax.csrf_getTransport());
|
||||||
Browser.Request = function () {
|
};
|
||||||
return new CsrfMagic(Browser.csrf_Request());
|
}
|
||||||
}
|
if ( window.MooTools ) {
|
||||||
}
|
Browser.csrf_Request = Browser.Request;
|
||||||
if (window.YAHOO) {
|
Browser.Request = function() {
|
||||||
// old YUI API
|
return new CsrfMagic(Browser.csrf_Request());
|
||||||
YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject;
|
};
|
||||||
YAHOO.util.Connect.createXhrObject = function (transaction) {
|
}
|
||||||
obj = YAHOO.util.Connect.csrf_createXhrObject(transaction);
|
if ( window.YAHOO ) {
|
||||||
obj.conn = new CsrfMagic(obj.conn);
|
// old YUI API
|
||||||
return obj;
|
YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject;
|
||||||
}
|
YAHOO.util.Connect.createXhrObject = function(transaction) {
|
||||||
}
|
obj = YAHOO.util.Connect.csrf_createXhrObject(transaction);
|
||||||
if (window.Ext) {
|
obj.conn = new CsrfMagic(obj.conn);
|
||||||
// Ext can use other js libraries as loaders, so it has to come last
|
return obj;
|
||||||
// Ext's implementation is pretty identical to Yahoo's, but we duplicate
|
};
|
||||||
// it for comprehensiveness's sake.
|
}
|
||||||
Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject;
|
if ( window.Ext ) {
|
||||||
Ext.lib.Ajax.createXhrObject = function (transaction) {
|
// Ext can use other js libraries as loaders, so it has to come last
|
||||||
obj = Ext.lib.Ajax.csrf_createXhrObject(transaction);
|
// Ext's implementation is pretty identical to Yahoo's, but we duplicate
|
||||||
obj.conn = new CsrfMagic(obj.conn);
|
// it for comprehensiveness's sake.
|
||||||
return obj;
|
Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject;
|
||||||
}
|
Ext.lib.Ajax.createXhrObject = function(transaction) {
|
||||||
}
|
obj = Ext.lib.Ajax.csrf_createXhrObject(transaction);
|
||||||
if (window.dojo) {
|
obj.conn = new CsrfMagic(obj.conn);
|
||||||
// NOTE: this doesn't work with latest dojo
|
return obj;
|
||||||
dojo.csrf__xhrObj = dojo._xhrObj;
|
};
|
||||||
dojo._xhrObj = function () {
|
}
|
||||||
return new CsrfMagic(dojo.csrf__xhrObj());
|
if ( window.dojo ) {
|
||||||
}
|
// NOTE: this doesn't work with latest dojo
|
||||||
}
|
dojo.csrf__xhrObj = dojo._xhrObj;
|
||||||
}
|
dojo._xhrObj = function() {
|
||||||
|
return new CsrfMagic(dojo.csrf__xhrObj());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -157,7 +157,8 @@ function csrf_ob_handler($buffer, $flags) {
|
||||||
$input = "<input type='hidden' name='$name' value=\"$tokens\"$endslash>";
|
$input = "<input type='hidden' name='$name' value=\"$tokens\"$endslash>";
|
||||||
$buffer = preg_replace('#(<form[^>]*method\s*=\s*["\']post["\'][^>]*>)#i', '$1' . $input, $buffer);
|
$buffer = preg_replace('#(<form[^>]*method\s*=\s*["\']post["\'][^>]*>)#i', '$1' . $input, $buffer);
|
||||||
if ($GLOBALS['csrf']['frame-breaker']) {
|
if ($GLOBALS['csrf']['frame-breaker']) {
|
||||||
$buffer = str_ireplace('</head>', '<script nonce="'.$cspNonce.'">if (top != self) {top.location.href = self.location.href;}</script></head>', $buffer);
|
$buffer = str_ireplace('</head>', '<script nonce="'.$cspNonce.'">if (top != self) {top.location.href = self.location.href;}</script>
|
||||||
|
</head>', $buffer);
|
||||||
}
|
}
|
||||||
if ($js = $GLOBALS['csrf']['rewrite-js']) {
|
if ($js = $GLOBALS['csrf']['rewrite-js']) {
|
||||||
$buffer = str_ireplace(
|
$buffer = str_ireplace(
|
||||||
|
@ -165,7 +166,8 @@ function csrf_ob_handler($buffer, $flags) {
|
||||||
'<script nonce="'.$cspNonce.'">'.
|
'<script nonce="'.$cspNonce.'">'.
|
||||||
'var csrfMagicToken = "'.$tokens.'";'.
|
'var csrfMagicToken = "'.$tokens.'";'.
|
||||||
'var csrfMagicName = "'.$name.'";</script>'.
|
'var csrfMagicName = "'.$name.'";</script>'.
|
||||||
'<script src="'.$js.'"></script></head>',
|
'<script src="'.$js.'"></script>
|
||||||
|
</head>',
|
||||||
$buffer
|
$buffer
|
||||||
);
|
);
|
||||||
$script = '<script nonce="'.$cspNonce.'">CsrfMagic.end();</script>';
|
$script = '<script nonce="'.$cspNonce.'">CsrfMagic.end();</script>';
|
||||||
|
@ -209,6 +211,7 @@ break;
|
||||||
}
|
}
|
||||||
$ok = true;
|
$ok = true;
|
||||||
} while (false);
|
} while (false);
|
||||||
|
|
||||||
if ($fatal && !$ok) {
|
if ($fatal && !$ok) {
|
||||||
$callback = $GLOBALS['csrf']['callback'];
|
$callback = $GLOBALS['csrf']['callback'];
|
||||||
if (trim($tokens, 'A..Za..z0..9:;,') !== '') $tokens = 'hidden';
|
if (trim($tokens, 'A..Za..z0..9:;,') !== '') $tokens = 'hidden';
|
||||||
|
@ -293,6 +296,7 @@ function csrf_callback($tokens) {
|
||||||
// Don't make it too easy for users to inflict a CSRF attack on themselves.
|
// Don't make it too easy for users to inflict a CSRF attack on themselves.
|
||||||
echo "<p><strong>Only try again if you weren't sent to this page by someone as this is potentially a sign of an attack.</strong></p>";
|
echo "<p><strong>Only try again if you weren't sent to this page by someone as this is potentially a sign of an attack.</strong></p>";
|
||||||
echo "<form method='post' action=''>$data<input type='submit' value='Try again' /></form>";
|
echo "<form method='post' action=''>$data<input type='submit' value='Try again' /></form>";
|
||||||
|
ZM\Logger::Debug("Failed csrf check");
|
||||||
}
|
}
|
||||||
echo "<p>Debug: $tokens</p></body></html>
|
echo "<p>Debug: $tokens</p></body></html>
|
||||||
";
|
";
|
||||||
|
|
|
@ -283,12 +283,12 @@ function outputImageStream( $id, $src, $width, $height, $title='' ) {
|
||||||
echo getImageStreamHTML( $id, $src, $width, $height, $title );
|
echo getImageStreamHTML( $id, $src, $width, $height, $title );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// width and height MUST be valid and include the px
|
||||||
function getImageStreamHTML( $id, $src, $width, $height, $title='' ) {
|
function getImageStreamHTML( $id, $src, $width, $height, $title='' ) {
|
||||||
if ( canStreamIframe() ) {
|
if ( canStreamIframe() ) {
|
||||||
return '<iframe id="'.$id.'" src="'.$src.'" alt="'. validHtmlStr($title) .'" '.($width? ' width="'. validInt($width).'"' : '').($height?' height="'.validInt($height).'"' : '' ).'/>';
|
return '<iframe id="'.$id.'" src="'.$src.'" alt="'. validHtmlStr($title) .'" '.($width? ' width="'. validInt($width).'"' : '').($height?' height="'.validInt($height).'"' : '' ).'/>';
|
||||||
} else {
|
} else {
|
||||||
return '<img id="'.$id.'" src="'.$src.'" alt="'. validHtmlStr($title) .'" style="'.($width? ' width:'.$width.'px;' : '' ).($height ? ' height:'. $height.'px;' : '' ).'"/>';
|
return '<img id="'.$id.'" src="'.$src.'" alt="'. validHtmlStr($title) .'" style="'.($width? 'width:'.$width.';' : '' ).($height ? ' height:'. $height.';' : '' ).'"/>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,7 +356,7 @@ function getWebSiteUrl( $id, $src, $width, $height, $title='' ) {
|
||||||
if (array_key_exists('X-Frame-Options', $header)) {
|
if (array_key_exists('X-Frame-Options', $header)) {
|
||||||
$header = $header['X-Frame-Options'];
|
$header = $header['X-Frame-Options'];
|
||||||
if ( stripos($header, 'sameorigin') === 0 )
|
if ( stripos($header, 'sameorigin') === 0 )
|
||||||
Warning("Web site $src has X-Frame-Options set to sameorigin. An X-Frame-Options browser plugin is required to display this site.");
|
ZM\Warning("Web site $src has X-Frame-Options set to sameorigin. An X-Frame-Options browser plugin is required to display this site.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return '<object id="'.$id.'" data="'.$src.'" alt="'.$title.'" width="'.$width.'" height="'.$height.'"></object>';
|
return '<object id="'.$id.'" data="'.$src.'" alt="'.$title.'" width="'.$width.'" height="'.$height.'"></object>';
|
||||||
|
@ -807,7 +807,7 @@ function canStreamNative() {
|
||||||
|
|
||||||
function canStreamApplet() {
|
function canStreamApplet() {
|
||||||
if ( (ZM_OPT_CAMBOZOLA && !file_exists( ZM_PATH_WEB.'/'.ZM_PATH_CAMBOZOLA )) ) {
|
if ( (ZM_OPT_CAMBOZOLA && !file_exists( ZM_PATH_WEB.'/'.ZM_PATH_CAMBOZOLA )) ) {
|
||||||
Warning ( 'ZM_OPT_CAMBOZOLA is enabled, but the system cannot find '.ZM_PATH_WEB.'/'.ZM_PATH_CAMBOZOLA );
|
ZM\Warning('ZM_OPT_CAMBOZOLA is enabled, but the system cannot find '.ZM_PATH_WEB.'/'.ZM_PATH_CAMBOZOLA);
|
||||||
}
|
}
|
||||||
|
|
||||||
return( (ZM_OPT_CAMBOZOLA && file_exists( ZM_PATH_WEB.'/'.ZM_PATH_CAMBOZOLA )) );
|
return( (ZM_OPT_CAMBOZOLA && file_exists( ZM_PATH_WEB.'/'.ZM_PATH_CAMBOZOLA )) );
|
||||||
|
@ -1106,22 +1106,32 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') {
|
||||||
$StorageArea = NULL;
|
$StorageArea = NULL;
|
||||||
|
|
||||||
$terms = isset($filter['Query']) ? $filter['Query']['terms'] : NULL;
|
$terms = isset($filter['Query']) ? $filter['Query']['terms'] : NULL;
|
||||||
|
if ( ! isset($terms) ) {
|
||||||
|
$backTrace = debug_backtrace();
|
||||||
|
$file = $backTrace[1]['file'];
|
||||||
|
$line = $backTrace[1]['line'];
|
||||||
|
ZM\Warning("No terms in filter from $file:$line");
|
||||||
|
ZM\Warning(print_r($filter,true));
|
||||||
|
}
|
||||||
if ( isset($terms) && count($terms) ) {
|
if ( isset($terms) && count($terms) ) {
|
||||||
for ( $i = 0; $i < count($terms); $i++ ) {
|
for ( $i = 0; $i < count($terms); $i++ ) {
|
||||||
if ( isset($terms[$i]['cnj']) && array_key_exists($terms[$i]['cnj'], $validQueryConjunctionTypes) ) {
|
|
||||||
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][cnj]").'='.urlencode($terms[$i]['cnj']);
|
$term = $terms[$i];
|
||||||
$filter['sql'] .= ' '.$terms[$i]['cnj'].' ';
|
|
||||||
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][cnj]\" value=\"".htmlspecialchars($terms[$i]['cnj'])."\"/>\n";
|
if ( isset($term['cnj']) && array_key_exists($term['cnj'], $validQueryConjunctionTypes) ) {
|
||||||
|
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][cnj]").'='.urlencode($term['cnj']);
|
||||||
|
$filter['sql'] .= ' '.$term['cnj'].' ';
|
||||||
|
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][cnj]\" value=\"".htmlspecialchars($term['cnj'])."\"/>\n";
|
||||||
}
|
}
|
||||||
if ( isset($terms[$i]['obr']) && (string)(int)$terms[$i]['obr'] == $terms[$i]['obr'] ) {
|
if ( isset($term['obr']) && (string)(int)$term['obr'] == $term['obr'] ) {
|
||||||
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][obr]").'='.urlencode($terms[$i]['obr']);
|
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][obr]").'='.urlencode($term['obr']);
|
||||||
$filter['sql'] .= ' '.str_repeat('(', $terms[$i]['obr']).' ';
|
$filter['sql'] .= ' '.str_repeat('(', $term['obr']).' ';
|
||||||
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][obr]\" value=\"".htmlspecialchars($terms[$i]['obr'])."\"/>\n";
|
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][obr]\" value=\"".htmlspecialchars($term['obr'])."\"/>\n";
|
||||||
}
|
}
|
||||||
if ( isset($terms[$i]['attr']) ) {
|
if ( isset($term['attr']) ) {
|
||||||
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][attr]").'='.urlencode($terms[$i]['attr']);
|
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][attr]").'='.urlencode($term['attr']);
|
||||||
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][attr]\" value=\"".htmlspecialchars($terms[$i]['attr'])."\"/>\n";
|
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][attr]\" value=\"".htmlspecialchars($term['attr'])."\"/>\n";
|
||||||
switch ( $terms[$i]['attr'] ) {
|
switch ( $term['attr'] ) {
|
||||||
case 'MonitorName':
|
case 'MonitorName':
|
||||||
$filter['sql'] .= 'M.Name';
|
$filter['sql'] .= 'M.Name';
|
||||||
break;
|
break;
|
||||||
|
@ -1189,7 +1199,7 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') {
|
||||||
case 'Notes':
|
case 'Notes':
|
||||||
case 'StateId':
|
case 'StateId':
|
||||||
case 'Archived':
|
case 'Archived':
|
||||||
$filter['sql'] .= 'E.'.$terms[$i]['attr'];
|
$filter['sql'] .= 'E.'.$term['attr'];
|
||||||
break;
|
break;
|
||||||
case 'DiskPercent':
|
case 'DiskPercent':
|
||||||
// Need to specify a storage area, so need to look through other terms looking for a storage area, else we default to ZM_EVENTS_PATH
|
// Need to specify a storage area, so need to look through other terms looking for a storage area, else we default to ZM_EVENTS_PATH
|
||||||
|
@ -1209,7 +1219,7 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') {
|
||||||
// Need to specify a storage area, so need to look through other terms looking for a storage area, else we default to ZM_EVENTS_PATH
|
// Need to specify a storage area, so need to look through other terms looking for a storage area, else we default to ZM_EVENTS_PATH
|
||||||
if ( ! $StorageArea ) {
|
if ( ! $StorageArea ) {
|
||||||
for ( $j = $i; $j < count($terms); $j++ ) {
|
for ( $j = $i; $j < count($terms); $j++ ) {
|
||||||
if ( isset($terms[$i]['attr']) and $terms[$i]['attr'] == 'StorageId' and isset($terms[$j]['val']) ) {
|
if ( isset($terms[$j]['attr']) and $terms[$j]['attr'] == 'StorageId' and isset($terms[$j]['val']) ) {
|
||||||
$StorageArea = ZM\Storage::find_one(array('Id'=>$terms[$j]['val']));
|
$StorageArea = ZM\Storage::find_one(array('Id'=>$terms[$j]['val']));
|
||||||
}
|
}
|
||||||
} // end foreach remaining term
|
} // end foreach remaining term
|
||||||
|
@ -1221,8 +1231,8 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$valueList = array();
|
$valueList = array();
|
||||||
foreach ( preg_split( '/["\'\s]*?,["\'\s]*?/', preg_replace( '/^["\']+?(.+)["\']+?$/', '$1', $terms[$i]['val'] ) ) as $value ) {
|
foreach ( preg_split( '/["\'\s]*?,["\'\s]*?/', preg_replace( '/^["\']+?(.+)["\']+?$/', '$1', $term['val'] ) ) as $value ) {
|
||||||
switch ( $terms[$i]['attr'] ) {
|
switch ( $term['attr'] ) {
|
||||||
case 'MonitorName':
|
case 'MonitorName':
|
||||||
case 'Name':
|
case 'Name':
|
||||||
case 'Cause':
|
case 'Cause':
|
||||||
|
@ -1270,16 +1280,16 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$valueList[] = $value;
|
$valueList[] = $value;
|
||||||
}
|
} // end foreach value
|
||||||
|
|
||||||
switch ( $terms[$i]['op'] ) {
|
switch ( $term['op'] ) {
|
||||||
case '=' :
|
case '=' :
|
||||||
case '!=' :
|
case '!=' :
|
||||||
case '>=' :
|
case '>=' :
|
||||||
case '>' :
|
case '>' :
|
||||||
case '<' :
|
case '<' :
|
||||||
case '<=' :
|
case '<=' :
|
||||||
$filter['sql'] .= ' '.$terms[$i]['op'].' '. $value;
|
$filter['sql'] .= ' '.$term['op'].' '. $value;
|
||||||
break;
|
break;
|
||||||
case '=~' :
|
case '=~' :
|
||||||
$filter['sql'] .= ' regexp '.$value;
|
$filter['sql'] .= ' regexp '.$value;
|
||||||
|
@ -1307,28 +1317,39 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') {
|
||||||
$filter['sql'] .= " IS NOT $value";
|
$filter['sql'] .= " IS NOT $value";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Warning("Invalid operator in filter: " . $terms[$i]['op'] );
|
ZM\Warning("Invalid operator in filter: " . $term['op'] );
|
||||||
}
|
} // end switch op
|
||||||
|
|
||||||
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][op]").'='.urlencode($terms[$i]['op']);
|
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][op]").'='.urlencode($term['op']);
|
||||||
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][op]\" value=\"".htmlspecialchars($terms[$i]['op'])."\"/>\n";
|
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][op]\" value=\"".htmlspecialchars($term['op'])."\"/>\n";
|
||||||
if ( isset($terms[$i]['val']) ) {
|
if ( isset($term['val']) ) {
|
||||||
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][val]").'='.urlencode($terms[$i]['val']);
|
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][val]").'='.urlencode($term['val']);
|
||||||
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][val]\" value=\"".htmlspecialchars($terms[$i]['val'])."\"/>\n";
|
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][val]\" value=\"".htmlspecialchars($term['val'])."\"/>\n";
|
||||||
}
|
}
|
||||||
} // end foreach term
|
} // end if ( isset($term['attr']) )
|
||||||
if ( isset($terms[$i]['cbr']) && (string)(int)$terms[$i]['cbr'] == $terms[$i]['cbr'] ) {
|
if ( isset($term['cbr']) && (string)(int)$term['cbr'] == $term['cbr'] ) {
|
||||||
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][cbr]").'='.urlencode($terms[$i]['cbr']);
|
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][cbr]").'='.urlencode($term['cbr']);
|
||||||
$filter['sql'] .= ' '.str_repeat( ')', $terms[$i]['cbr'] ).' ';
|
$filter['sql'] .= ' '.str_repeat( ')', $term['cbr'] ).' ';
|
||||||
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][cbr]\" value=\"".htmlspecialchars($terms[$i]['cbr'])."\"/>\n";
|
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][cbr]\" value=\"".htmlspecialchars($term['cbr'])."\"/>\n";
|
||||||
}
|
}
|
||||||
}
|
} // end foreach term
|
||||||
if ( $filter['sql'] )
|
if ( $filter['sql'] )
|
||||||
$filter['sql'] = ' and ( '.$filter['sql'].' )';
|
$filter['sql'] = ' and ( '.$filter['sql'].' )';
|
||||||
if ( $saveToSession ) {
|
if ( $saveToSession ) {
|
||||||
$_SESSION['filter'] = $filter;
|
$_SESSION['filter'] = $filter;
|
||||||
}
|
}
|
||||||
}
|
} // end if terms
|
||||||
|
|
||||||
|
#if ( 0 ) {
|
||||||
|
#// ICON I feel like these should be here, but not yet
|
||||||
|
#if ( isset($filter['Query']['sort_field']) ) {
|
||||||
|
#$filter['sql'] .= ' ORDER BY ' . $filter['Query']['sort_field'] . (
|
||||||
|
#( $filter['Query']['sort_asc'] ? ' ASC' : ' DESC' ) );
|
||||||
|
#}
|
||||||
|
#if ( $filter['Query']['limit'] ) {
|
||||||
|
#$filter['sql'] .= ' LIMIT ' . validInt($filter['Query']['limit']);
|
||||||
|
#}
|
||||||
|
#}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Please note that the filter is passed in by copy, so you need to use the return value from this function.
|
// Please note that the filter is passed in by copy, so you need to use the return value from this function.
|
||||||
|
@ -2235,19 +2256,18 @@ function validHtmlStr( $input ) {
|
||||||
return( htmlspecialchars( $input, ENT_QUOTES ) );
|
return( htmlspecialchars( $input, ENT_QUOTES ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStreamHTML( $monitor, $options = array() ) {
|
function getStreamHTML($monitor, $options = array()) {
|
||||||
|
|
||||||
if ( isset($options['scale']) and $options['scale'] and ( $options['scale'] != 100 ) ) {
|
if ( isset($options['scale']) and $options['scale'] and ($options['scale'] != 100) ) {
|
||||||
//Warning("Scale to " . $options['scale'] );
|
$options['width'] = reScale($monitor->Width(), $options['scale']).'px';
|
||||||
$options['width'] = reScale( $monitor->Width(), $options['scale'] );
|
$options['height'] = reScale($monitor->Height(), $options['scale']).'px';
|
||||||
$options['height'] = reScale( $monitor->Height(), $options['scale'] );
|
|
||||||
} else {
|
} else {
|
||||||
# scale is empty or 100
|
# scale is empty or 100
|
||||||
# There may be a fixed width applied though, in which case we need to leave the height empty
|
# There may be a fixed width applied though, in which case we need to leave the height empty
|
||||||
if ( ! ( isset($options['width']) and $options['width'] ) ) {
|
if ( ! ( isset($options['width']) and $options['width'] ) ) {
|
||||||
$options['width'] = $monitor->Width();
|
$options['width'] = $monitor->Width().'px';
|
||||||
if ( ! ( isset($options['height']) and $options['height'] ) ) {
|
if ( ! ( isset($options['height']) and $options['height'] ) ) {
|
||||||
$options['height'] = $monitor->Height();
|
$options['height'] = $monitor->Height().'px';
|
||||||
}
|
}
|
||||||
} else if ( ! isset($options['height']) ) {
|
} else if ( ! isset($options['height']) ) {
|
||||||
$options['height'] = '';
|
$options['height'] = '';
|
||||||
|
@ -2271,10 +2291,10 @@ function getStreamHTML( $monitor, $options = array() ) {
|
||||||
//FIXME, the width and height of the image need to be scaled.
|
//FIXME, the width and height of the image need to be scaled.
|
||||||
} else if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
|
} else if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
|
||||||
$streamSrc = $monitor->getStreamSrc( array(
|
$streamSrc = $monitor->getStreamSrc( array(
|
||||||
'mode'=>'mpeg',
|
'mode' => 'mpeg',
|
||||||
'scale'=>(isset($options['scale'])?$options['scale']:100),
|
'scale' => (isset($options['scale'])?$options['scale']:100),
|
||||||
'bitrate'=>ZM_WEB_VIDEO_BITRATE,
|
'bitrate'=> ZM_WEB_VIDEO_BITRATE,
|
||||||
'maxfps'=>ZM_WEB_VIDEO_MAXFPS,
|
'maxfps' => ZM_WEB_VIDEO_MAXFPS,
|
||||||
'format' => ZM_MPEG_LIVE_FORMAT
|
'format' => ZM_MPEG_LIVE_FORMAT
|
||||||
) );
|
) );
|
||||||
return getVideoStreamHTML( 'liveStream'.$monitor->Id(), $streamSrc, $options['width'], $options['height'], ZM_MPEG_LIVE_FORMAT, $monitor->Name() );
|
return getVideoStreamHTML( 'liveStream'.$monitor->Id(), $streamSrc, $options['width'], $options['height'], ZM_MPEG_LIVE_FORMAT, $monitor->Name() );
|
||||||
|
@ -2295,7 +2315,7 @@ function getStreamHTML( $monitor, $options = array() ) {
|
||||||
ZM\Info( 'The system has fallen back to single jpeg mode for streaming. Consider enabling Cambozola or upgrading the client browser.' );
|
ZM\Info( 'The system has fallen back to single jpeg mode for streaming. Consider enabling Cambozola or upgrading the client browser.' );
|
||||||
}
|
}
|
||||||
$options['mode'] = 'single';
|
$options['mode'] = 'single';
|
||||||
$streamSrc = $monitor->getStreamSrc( $options );
|
$streamSrc = $monitor->getStreamSrc($options);
|
||||||
return getImageStill( 'liveStream'.$monitor->Id(), $streamSrc, $options['width'], $options['height'], $monitor->Name());
|
return getImageStill( 'liveStream'.$monitor->Id(), $streamSrc, $options['width'], $options['height'], $monitor->Name());
|
||||||
}
|
}
|
||||||
} // end function getStreamHTML
|
} // end function getStreamHTML
|
||||||
|
|
|
@ -16,9 +16,9 @@ class Logger {
|
||||||
|
|
||||||
private $initialised = false;
|
private $initialised = false;
|
||||||
|
|
||||||
private $id = "web";
|
private $id = 'web';
|
||||||
private $idRoot = "web";
|
private $idRoot = 'web';
|
||||||
private $idArgs = "";
|
private $idArgs = '';
|
||||||
private $useErrorLog = true;
|
private $useErrorLog = true;
|
||||||
|
|
||||||
private $level = self::INFO;
|
private $level = self::INFO;
|
||||||
|
@ -32,17 +32,17 @@ class Logger {
|
||||||
private $hasTerm = false;
|
private $hasTerm = false;
|
||||||
|
|
||||||
private $logPath = ZM_PATH_LOGS;
|
private $logPath = ZM_PATH_LOGS;
|
||||||
private $logFile = "";
|
private $logFile = '';
|
||||||
private $logFd = NULL;
|
private $logFd = NULL;
|
||||||
|
|
||||||
public static $codes = array(
|
public static $codes = array(
|
||||||
self::DEBUG => "DBG",
|
self::DEBUG => 'DBG',
|
||||||
self::INFO => "INF",
|
self::INFO => 'INF',
|
||||||
self::WARNING => "WAR",
|
self::WARNING => 'WAR',
|
||||||
self::ERROR => "ERR",
|
self::ERROR => 'ERR',
|
||||||
self::FATAL => "FAT",
|
self::FATAL => 'FAT',
|
||||||
self::PANIC => "PNC",
|
self::PANIC => 'PNC',
|
||||||
self::NOLOG => "OFF",
|
self::NOLOG => 'OFF',
|
||||||
);
|
);
|
||||||
private static $syslogPriorities = array(
|
private static $syslogPriorities = array(
|
||||||
self::DEBUG => LOG_DEBUG,
|
self::DEBUG => LOG_DEBUG,
|
||||||
|
@ -63,7 +63,7 @@ class Logger {
|
||||||
|
|
||||||
private function __construct() {
|
private function __construct() {
|
||||||
$this->hasTerm = (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']));
|
$this->hasTerm = (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']));
|
||||||
$this->logFile = $this->logPath."/".$this->id.".log";
|
$this->logFile = $this->logPath.'/'.$this->id.'.log';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __destruct() {
|
public function __destruct() {
|
||||||
|
@ -78,12 +78,12 @@ class Logger {
|
||||||
//$this->useErrorLog = $options['useErrorLog'];
|
//$this->useErrorLog = $options['useErrorLog'];
|
||||||
if ( isset($options['logPath']) ) {
|
if ( isset($options['logPath']) ) {
|
||||||
$this->logPath = $options['logPath'];
|
$this->logPath = $options['logPath'];
|
||||||
$tempLogFile = $this->logPath."/".$this->id.".log";
|
$tempLogFile = $this->logPath.'/'.$this->id.'.log';
|
||||||
}
|
}
|
||||||
if ( isset($options['logFile']) )
|
if ( isset($options['logFile']) )
|
||||||
$tempLogFile = $options['logFile'];
|
$tempLogFile = $options['logFile'];
|
||||||
else
|
else
|
||||||
$tempLogFile = $this->logPath."/".$this->id.".log";
|
$tempLogFile = $this->logPath.'/'.$this->id.'.log';
|
||||||
if ( !is_null($logFile = $this->getTargettedEnv('LOG_FILE')) )
|
if ( !is_null($logFile = $this->getTargettedEnv('LOG_FILE')) )
|
||||||
$tempLogFile = $logFile;
|
$tempLogFile = $logFile;
|
||||||
|
|
||||||
|
@ -177,10 +177,10 @@ class Logger {
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getTargettedEnv( $name ) {
|
private function getTargettedEnv( $name ) {
|
||||||
$envName = $name."_".$this->id;
|
$envName = $name.'_'.$this->id;
|
||||||
$value = getenv( $envName );
|
$value = getenv( $envName );
|
||||||
if ( $value === false && $this->id != $this->idRoot )
|
if ( $value === false && $this->id != $this->idRoot )
|
||||||
$value = getenv( $name."_".$this->idRoot );
|
$value = getenv( $name.'_'.$this->idRoot );
|
||||||
if ( $value === false )
|
if ( $value === false )
|
||||||
$value = getenv( $name );
|
$value = getenv( $name );
|
||||||
return( $value !== false ? $value : NULL );
|
return( $value !== false ? $value : NULL );
|
||||||
|
@ -269,7 +269,7 @@ class Logger {
|
||||||
if ( $this->databaseLevel > self::NOLOG ) {
|
if ( $this->databaseLevel > self::NOLOG ) {
|
||||||
if ( (include_once 'database.php') === FALSE ) {
|
if ( (include_once 'database.php') === FALSE ) {
|
||||||
$this->databaseLevel = self::NOLOG;
|
$this->databaseLevel = self::NOLOG;
|
||||||
Warning( "Unable to write log entries to DB, database.php not found" );
|
Warning( 'Unable to write log entries to DB, database.php not found' );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -424,7 +424,7 @@ class Logger {
|
||||||
function logInit( $options=array() ) {
|
function logInit( $options=array() ) {
|
||||||
$logger = Logger::fetch();
|
$logger = Logger::fetch();
|
||||||
$logger->initialise( $options );
|
$logger->initialise( $options );
|
||||||
set_error_handler( 'ErrorHandler' );
|
set_error_handler( 'ZM\ErrorHandler' );
|
||||||
}
|
}
|
||||||
|
|
||||||
function logToDatabase( $level=NULL ) {
|
function logToDatabase( $level=NULL ) {
|
||||||
|
@ -487,7 +487,7 @@ function Panic( $string ) {
|
||||||
function ErrorHandler( $error, $string, $file, $line ) {
|
function ErrorHandler( $error, $string, $file, $line ) {
|
||||||
if ( ! (error_reporting() & $error) ) {
|
if ( ! (error_reporting() & $error) ) {
|
||||||
// This error code is not included in error_reporting
|
// This error code is not included in error_reporting
|
||||||
return( false );
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ( $error ) {
|
switch ( $error ) {
|
||||||
|
@ -507,7 +507,7 @@ function ErrorHandler( $error, $string, $file, $line ) {
|
||||||
Panic( "Unknown error type: [$error] $string" );
|
Panic( "Unknown error type: [$error] $string" );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return( true );
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
// 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.
|
||||||
//
|
//
|
||||||
namespace ZM;
|
|
||||||
|
|
||||||
error_reporting(E_ALL);
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
@ -38,7 +37,7 @@ if ( version_compare(phpversion(), '4.1.0', '<') ) {
|
||||||
if ( true ) {
|
if ( true ) {
|
||||||
ob_start();
|
ob_start();
|
||||||
phpinfo(INFO_VARIABLES);
|
phpinfo(INFO_VARIABLES);
|
||||||
$fp = fopen('/tmp/env.html', 'w');
|
$fp = fopen('/tmp/env.html', 'w+');
|
||||||
fwrite($fp, ob_get_contents());
|
fwrite($fp, ob_get_contents());
|
||||||
fclose($fp);
|
fclose($fp);
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
|
@ -70,8 +69,15 @@ define('ZM_BASE_PROTOCOL', $protocol);
|
||||||
// Use relative URL's instead
|
// Use relative URL's instead
|
||||||
define('ZM_BASE_URL', '');
|
define('ZM_BASE_URL', '');
|
||||||
|
|
||||||
// Verify the system, php, and mysql timezones all match
|
|
||||||
require_once('includes/functions.php');
|
require_once('includes/functions.php');
|
||||||
|
if ( $_SERVER['REQUEST_METHOD'] == 'OPTIONS' ) {
|
||||||
|
ZM\Logger::Debug("OPTIONS Method, only doing CORS");
|
||||||
|
# Add Cross domain access headers
|
||||||
|
CORSHeaders();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the system, php, and mysql timezones all match
|
||||||
check_timezone();
|
check_timezone();
|
||||||
|
|
||||||
if ( isset($_GET['skin']) ) {
|
if ( isset($_GET['skin']) ) {
|
||||||
|
@ -152,7 +158,7 @@ CORSHeaders();
|
||||||
|
|
||||||
// Check for valid content dirs
|
// Check for valid content dirs
|
||||||
if ( !is_writable(ZM_DIR_EVENTS) ) {
|
if ( !is_writable(ZM_DIR_EVENTS) ) {
|
||||||
Warning("Cannot write to event folder ".ZM_DIR_EVENTS.". Check that it exists and is owned by the web account user.");
|
ZM\Warning("Cannot write to event folder ".ZM_DIR_EVENTS.". Check that it exists and is owned by the web account user.");
|
||||||
}
|
}
|
||||||
|
|
||||||
# Globals
|
# Globals
|
||||||
|
@ -174,7 +180,7 @@ if ( isset($_REQUEST['request']) )
|
||||||
require_once('includes/auth.php');
|
require_once('includes/auth.php');
|
||||||
|
|
||||||
foreach ( getSkinIncludes('skin.php') as $includeFile ) {
|
foreach ( getSkinIncludes('skin.php') as $includeFile ) {
|
||||||
#Logger::Debug("including $includeFile");
|
#ZM\Logger::Debug("including $includeFile");
|
||||||
require_once $includeFile;
|
require_once $includeFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +193,7 @@ isset($view) || $view = NULL;
|
||||||
isset($request) || $request = NULL;
|
isset($request) || $request = NULL;
|
||||||
isset($action) || $action = NULL;
|
isset($action) || $action = NULL;
|
||||||
|
|
||||||
Logger::Debug("View: $view Request: $request Action: $action");
|
ZM\Logger::Debug("View: $view Request: $request Action: $action User: " . ( isset($user) ? $user['Username'] : 'none' ));
|
||||||
if (
|
if (
|
||||||
ZM_ENABLE_CSRF_MAGIC &&
|
ZM_ENABLE_CSRF_MAGIC &&
|
||||||
( $action != 'login' ) &&
|
( $action != 'login' ) &&
|
||||||
|
@ -198,26 +204,31 @@ if (
|
||||||
( $view != 'archive' )
|
( $view != 'archive' )
|
||||||
) {
|
) {
|
||||||
require_once( 'includes/csrf/csrf-magic.php' );
|
require_once( 'includes/csrf/csrf-magic.php' );
|
||||||
#Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
|
#ZM\Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\"");
|
||||||
csrf_check();
|
csrf_check();
|
||||||
}
|
}
|
||||||
|
|
||||||
# Need to include actions because it does auth
|
# Need to include actions because it does auth
|
||||||
if ( $action ) {
|
if ( $action ) {
|
||||||
if ( file_exists('includes/actions/'.$view.'.php') ) {
|
if ( file_exists('includes/actions/'.$view.'.php') ) {
|
||||||
Logger::Debug("Including includes/actions/$view.php");
|
ZM\Logger::Debug("Including includes/actions/$view.php");
|
||||||
require_once('includes/actions/'.$view.'.php');
|
require_once('includes/actions/'.$view.'.php');
|
||||||
} else {
|
} else {
|
||||||
Warning("No includes/actions/$view.php for action $action");
|
ZM\Warning("No includes/actions/$view.php for action $action");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# If I put this here, it protects all views and popups, but it has to go after actions.php because actions.php does the actual logging in.
|
# If I put this here, it protects all views and popups, but it has to go after actions.php because actions.php does the actual logging in.
|
||||||
if ( ZM_OPT_USE_AUTH and !isset($user) and ($view != 'login') ) {
|
if ( ZM_OPT_USE_AUTH and !isset($user) and ($view != 'login') ) {
|
||||||
Logger::Debug('Redirecting to login');
|
/* AJAX check */
|
||||||
# We adjust the view instead of redirecting so that we can store the original url and just to it after logging in
|
if ( !empty($_SERVER['HTTP_X_REQUESTED_WITH'])
|
||||||
$view = 'login';
|
&& strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest' ) {
|
||||||
#$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=login';
|
header('HTTP/1.1 401 Unauthorized');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
ZM\Logger::Debug('Redirecting to login');
|
||||||
|
$view = 'none';
|
||||||
|
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=login';
|
||||||
$request = null;
|
$request = null;
|
||||||
} else if ( ZM_SHOW_PRIVACY && ($view != 'privacy') && ($view != 'options') && (!$request) && canEdit('System') ) {
|
} else if ( ZM_SHOW_PRIVACY && ($view != 'privacy') && ($view != 'options') && (!$request) && canEdit('System') ) {
|
||||||
$view = 'none';
|
$view = 'none';
|
||||||
|
@ -228,7 +239,7 @@ if ( ZM_OPT_USE_AUTH and !isset($user) and ($view != 'login') ) {
|
||||||
CSPHeaders($view, $cspNonce);
|
CSPHeaders($view, $cspNonce);
|
||||||
|
|
||||||
if ( $redirect ) {
|
if ( $redirect ) {
|
||||||
Logger::Debug("Redirecting to $redirect");
|
ZM\Logger::Debug("Redirecting to $redirect");
|
||||||
header('Location: '.$redirect);
|
header('Location: '.$redirect);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -236,7 +247,7 @@ if ( $redirect ) {
|
||||||
if ( $request ) {
|
if ( $request ) {
|
||||||
foreach ( getSkinIncludes('ajax/'.$request.'.php', true, true) as $includeFile ) {
|
foreach ( getSkinIncludes('ajax/'.$request.'.php', true, true) as $includeFile ) {
|
||||||
if ( !file_exists($includeFile) )
|
if ( !file_exists($includeFile) )
|
||||||
Fatal("Request '$request' does not exist");
|
ZM\Fatal("Request '$request' does not exist");
|
||||||
require_once $includeFile;
|
require_once $includeFile;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -245,7 +256,7 @@ if ( $request ) {
|
||||||
if ( $includeFiles = getSkinIncludes('views/'.$view.'.php', true, true) ) {
|
if ( $includeFiles = getSkinIncludes('views/'.$view.'.php', true, true) ) {
|
||||||
foreach ( $includeFiles as $includeFile ) {
|
foreach ( $includeFiles as $includeFile ) {
|
||||||
if ( !file_exists($includeFile) )
|
if ( !file_exists($includeFile) )
|
||||||
Fatal("View '$view' does not exist");
|
ZM\Fatal("View '$view' does not exist");
|
||||||
require_once $includeFile;
|
require_once $includeFile;
|
||||||
}
|
}
|
||||||
// If the view overrides $view to 'error', and the user is not logged in, then the
|
// If the view overrides $view to 'error', and the user is not logged in, then the
|
||||||
|
|
|
@ -216,6 +216,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Podržava Relativno kretanje',
|
'CanMoveRel' => 'Podržava Relativno kretanje',
|
||||||
'CanPan' => 'Podržava Pomak' ,
|
'CanPan' => 'Podržava Pomak' ,
|
||||||
'CanReset' => 'PodržavaReset',
|
'CanReset' => 'PodržavaReset',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Podržava presetove',
|
'CanSetPresets' => 'Podržava presetove',
|
||||||
'CanSleep' => 'Podržava Sleep',
|
'CanSleep' => 'Podržava Sleep',
|
||||||
'CanTilt' => 'Podržava nagib',
|
'CanTilt' => 'Podržava nagib',
|
||||||
|
|
|
@ -209,6 +209,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Can Move Relative',
|
'CanMoveRel' => 'Can Move Relative',
|
||||||
'CanPan' => 'Can Pan' ,
|
'CanPan' => 'Can Pan' ,
|
||||||
'CanReset' => 'Can Reset',
|
'CanReset' => 'Can Reset',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Can Set Presets',
|
'CanSetPresets' => 'Can Set Presets',
|
||||||
'CanSleep' => 'Can Sleep',
|
'CanSleep' => 'Can Sleep',
|
||||||
'CanTilt' => 'Can Tilt',
|
'CanTilt' => 'Can Tilt',
|
||||||
|
|
|
@ -205,6 +205,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => '可以相对移动',
|
'CanMoveRel' => '可以相对移动',
|
||||||
'CanPan' => '可以平移' ,
|
'CanPan' => '可以平移' ,
|
||||||
'CanReset' => '可以复位',
|
'CanReset' => '可以复位',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => '可以进行预设',
|
'CanSetPresets' => '可以进行预设',
|
||||||
'CanSleep' => '可以休眠',
|
'CanSleep' => '可以休眠',
|
||||||
'CanTilt' => '可以倾斜',
|
'CanTilt' => '可以倾斜',
|
||||||
|
|
|
@ -205,6 +205,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Umí relativní pohyb',
|
'CanMoveRel' => 'Umí relativní pohyb',
|
||||||
'CanPan' => 'Umí otáčení',
|
'CanPan' => 'Umí otáčení',
|
||||||
'CanReset' => 'Umí reset',
|
'CanReset' => 'Umí reset',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Umí navolit předvolby',
|
'CanSetPresets' => 'Umí navolit předvolby',
|
||||||
'CanSleep' => 'Může spát',
|
'CanSleep' => 'Může spát',
|
||||||
'CanTilt' => 'Umí náklon',
|
'CanTilt' => 'Umí náklon',
|
||||||
|
|
|
@ -207,6 +207,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Kann relative Bewegung',
|
'CanMoveRel' => 'Kann relative Bewegung',
|
||||||
'CanPan' => 'Kann Pan' ,
|
'CanPan' => 'Kann Pan' ,
|
||||||
'CanReset' => 'Kann Reset',
|
'CanReset' => 'Kann Reset',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Kann Voreinstellungen setzen',
|
'CanSetPresets' => 'Kann Voreinstellungen setzen',
|
||||||
'CanSleep' => 'Kann Sleep',
|
'CanSleep' => 'Kann Sleep',
|
||||||
'CanTilt' => 'Kann Neigung',
|
'CanTilt' => 'Kann Neigung',
|
||||||
|
|
|
@ -206,6 +206,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Can Move Relative',
|
'CanMoveRel' => 'Can Move Relative',
|
||||||
'CanPan' => 'Can Pan' ,
|
'CanPan' => 'Can Pan' ,
|
||||||
'CanReset' => 'Can Reset',
|
'CanReset' => 'Can Reset',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Can Set Presets',
|
'CanSetPresets' => 'Can Set Presets',
|
||||||
'CanSleep' => 'Can Sleep',
|
'CanSleep' => 'Can Sleep',
|
||||||
'CanTilt' => 'Can Tilt',
|
'CanTilt' => 'Can Tilt',
|
||||||
|
|
|
@ -216,6 +216,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Can Move Relative',
|
'CanMoveRel' => 'Can Move Relative',
|
||||||
'CanPan' => 'Can Pan' ,
|
'CanPan' => 'Can Pan' ,
|
||||||
'CanReset' => 'Can Reset',
|
'CanReset' => 'Can Reset',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Can Set Presets',
|
'CanSetPresets' => 'Can Set Presets',
|
||||||
'CanSleep' => 'Can Sleep',
|
'CanSleep' => 'Can Sleep',
|
||||||
'CanTilt' => 'Can Tilt',
|
'CanTilt' => 'Can Tilt',
|
||||||
|
@ -271,6 +272,7 @@ $SLANG = array(
|
||||||
'Debug' => 'Debug',
|
'Debug' => 'Debug',
|
||||||
'DefaultRate' => 'Default Rate',
|
'DefaultRate' => 'Default Rate',
|
||||||
'DefaultScale' => 'Default Scale',
|
'DefaultScale' => 'Default Scale',
|
||||||
|
'DefaultCodec' => 'Default Method For Live View',
|
||||||
'DefaultView' => 'Default View',
|
'DefaultView' => 'Default View',
|
||||||
'Deinterlacing' => 'Deinterlacing',
|
'Deinterlacing' => 'Deinterlacing',
|
||||||
'RTSPDescribe' => 'Use RTSP Response Media URL',
|
'RTSPDescribe' => 'Use RTSP Response Media URL',
|
||||||
|
@ -539,6 +541,7 @@ $SLANG = array(
|
||||||
'NewState' => 'New State',
|
'NewState' => 'New State',
|
||||||
'NewUser' => 'New User',
|
'NewUser' => 'New User',
|
||||||
'Next' => 'Next',
|
'Next' => 'Next',
|
||||||
|
'NextMonitor' => 'Next Monitor',
|
||||||
'NoDetectedCameras' => 'No Detected Cameras',
|
'NoDetectedCameras' => 'No Detected Cameras',
|
||||||
'NoDetectedProfiles' => 'No Detected Profiles',
|
'NoDetectedProfiles' => 'No Detected Profiles',
|
||||||
'NoFramesRecorded' => 'There are no frames recorded for this event',
|
'NoFramesRecorded' => 'There are no frames recorded for this event',
|
||||||
|
@ -591,12 +594,14 @@ $SLANG = array(
|
||||||
'PathToApi' => 'Path To Api',
|
'PathToApi' => 'Path To Api',
|
||||||
'Paths' => 'Paths',
|
'Paths' => 'Paths',
|
||||||
'Pause' => 'Pause',
|
'Pause' => 'Pause',
|
||||||
|
'PauseCycle' => 'Pause Cycle',
|
||||||
'PhoneBW' => 'Phone B/W',
|
'PhoneBW' => 'Phone B/W',
|
||||||
'Phone' => 'Phone',
|
'Phone' => 'Phone',
|
||||||
'PixelDiff' => 'Pixel Diff',
|
'PixelDiff' => 'Pixel Diff',
|
||||||
'Pixels' => 'pixels',
|
'Pixels' => 'pixels',
|
||||||
'PlayAll' => 'Play All',
|
'PlayAll' => 'Play All',
|
||||||
'Play' => 'Play',
|
'Play' => 'Play',
|
||||||
|
'PlayCycle' => 'Play Cycle',
|
||||||
'Plugins' => 'Plugins',
|
'Plugins' => 'Plugins',
|
||||||
'PleaseWait' => 'Please Wait',
|
'PleaseWait' => 'Please Wait',
|
||||||
'Point' => 'Point',
|
'Point' => 'Point',
|
||||||
|
@ -606,6 +611,7 @@ $SLANG = array(
|
||||||
'Preset' => 'Preset',
|
'Preset' => 'Preset',
|
||||||
'Presets' => 'Presets',
|
'Presets' => 'Presets',
|
||||||
'Prev' => 'Prev',
|
'Prev' => 'Prev',
|
||||||
|
'PreviousMonitor' => 'Previous Monitor',
|
||||||
'Privacy' => 'Privacy',
|
'Privacy' => 'Privacy',
|
||||||
'PrivacyAbout' => 'About',
|
'PrivacyAbout' => 'About',
|
||||||
'PrivacyAboutText' => 'Since 2002, ZoneMinder has been the premier free and open-source Video Management System (VMS) solution for Linux platforms. ZoneMinder is supported by the community and is managed by those who choose to volunteer their spare time to the project. The best way to improve ZoneMinder is to get involved.',
|
'PrivacyAboutText' => 'Since 2002, ZoneMinder has been the premier free and open-source Video Management System (VMS) solution for Linux platforms. ZoneMinder is supported by the community and is managed by those who choose to volunteer their spare time to the project. The best way to improve ZoneMinder is to get involved.',
|
||||||
|
@ -663,6 +669,7 @@ $SLANG = array(
|
||||||
'RunState' => 'Run State',
|
'RunState' => 'Run State',
|
||||||
'RunStats' => 'Run Stats Process',
|
'RunStats' => 'Run Stats Process',
|
||||||
'RunTrigger' => 'Run Trigger Process',
|
'RunTrigger' => 'Run Trigger Process',
|
||||||
|
'RunEventNotification' => 'Run Event Notification Process',
|
||||||
'SaveAs' => 'Save as',
|
'SaveAs' => 'Save as',
|
||||||
'SaveFilter' => 'Save Filter',
|
'SaveFilter' => 'Save Filter',
|
||||||
'SaveJPEGs' => 'Save JPEGs',
|
'SaveJPEGs' => 'Save JPEGs',
|
||||||
|
@ -682,6 +689,7 @@ $SLANG = array(
|
||||||
'Settings' => 'Settings',
|
'Settings' => 'Settings',
|
||||||
'ShowFilterWindow' => 'Show Filter Window',
|
'ShowFilterWindow' => 'Show Filter Window',
|
||||||
'ShowTimeline' => 'Show Timeline',
|
'ShowTimeline' => 'Show Timeline',
|
||||||
|
'Shutdown' => 'Shutdown',
|
||||||
'SignalCheckColour' => 'Signal Check Colour',
|
'SignalCheckColour' => 'Signal Check Colour',
|
||||||
'SignalCheckPoints' => 'Signal Check Points',
|
'SignalCheckPoints' => 'Signal Check Points',
|
||||||
'Size' => 'Size',
|
'Size' => 'Size',
|
||||||
|
@ -775,6 +783,7 @@ $SLANG = array(
|
||||||
'VersionRemindNever' => 'Don\'t remind about new versions',
|
'VersionRemindNever' => 'Don\'t remind about new versions',
|
||||||
'VersionRemindWeek' => 'Remind again in 1 week',
|
'VersionRemindWeek' => 'Remind again in 1 week',
|
||||||
'Version' => 'Version',
|
'Version' => 'Version',
|
||||||
|
'ViewMatches' => 'View Matches',
|
||||||
'VideoFormat' => 'Video Format',
|
'VideoFormat' => 'Video Format',
|
||||||
'VideoGenFailed' => 'Video Generation Failed!',
|
'VideoGenFailed' => 'Video Generation Failed!',
|
||||||
'VideoGenFiles' => 'Existing Video Files',
|
'VideoGenFiles' => 'Existing Video Files',
|
||||||
|
|
|
@ -156,6 +156,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Can Move Relative',
|
'CanMoveRel' => 'Can Move Relative',
|
||||||
'CanPan' => 'Can Pan' ,
|
'CanPan' => 'Can Pan' ,
|
||||||
'CanReset' => 'Can Reset',
|
'CanReset' => 'Can Reset',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Can Set Presets',
|
'CanSetPresets' => 'Can Set Presets',
|
||||||
'CanSleep' => 'Can Sleep',
|
'CanSleep' => 'Can Sleep',
|
||||||
'CanTilt' => 'Can Tilt',
|
'CanTilt' => 'Can Tilt',
|
||||||
|
|
|
@ -205,6 +205,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Puede moverse de forma relativa',
|
'CanMoveRel' => 'Puede moverse de forma relativa',
|
||||||
'CanPan' => 'Puede desplazarse' ,
|
'CanPan' => 'Puede desplazarse' ,
|
||||||
'CanReset' => 'Puede restablecerse',
|
'CanReset' => 'Puede restablecerse',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Puede fefinir programaciones',
|
'CanSetPresets' => 'Puede fefinir programaciones',
|
||||||
'CanSleep' => 'Puede dormirse',
|
'CanSleep' => 'Puede dormirse',
|
||||||
'CanTilt' => 'Puede inclinarse',
|
'CanTilt' => 'Puede inclinarse',
|
||||||
|
|
|
@ -212,6 +212,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Can Move Relative',
|
'CanMoveRel' => 'Can Move Relative',
|
||||||
'CanPan' => 'Can Pan' ,
|
'CanPan' => 'Can Pan' ,
|
||||||
'CanReset' => 'Can Reset',
|
'CanReset' => 'Can Reset',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Can Set Presets',
|
'CanSetPresets' => 'Can Set Presets',
|
||||||
'CanSleep' => 'Can Sleep',
|
'CanSleep' => 'Can Sleep',
|
||||||
'CanTilt' => 'Can Tilt',
|
'CanTilt' => 'Can Tilt',
|
||||||
|
|
|
@ -211,6 +211,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Relatif',
|
'CanMoveRel' => 'Relatif',
|
||||||
'CanPan' => 'Panoramique' ,
|
'CanPan' => 'Panoramique' ,
|
||||||
'CanReset' => 'RàZ',
|
'CanReset' => 'RàZ',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Stockage prépos.',
|
'CanSetPresets' => 'Stockage prépos.',
|
||||||
'CanSleep' => 'Veille',
|
'CanSleep' => 'Veille',
|
||||||
'CanTilt' => 'Inclinaison',
|
'CanTilt' => 'Inclinaison',
|
||||||
|
|
|
@ -205,6 +205,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'àôùø úæåæä éçñéú',
|
'CanMoveRel' => 'àôùø úæåæä éçñéú',
|
||||||
'CanPan' => 'Can Pan' ,
|
'CanPan' => 'Can Pan' ,
|
||||||
'CanReset' => 'àôùø àúçåì',
|
'CanReset' => 'àôùø àúçåì',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Can Set Presets',
|
'CanSetPresets' => 'Can Set Presets',
|
||||||
'CanSleep' => 'àôùø îöá ùéðä',
|
'CanSleep' => 'àôùø îöá ùéðä',
|
||||||
'CanTilt' => 'àôùø æòæåò',
|
'CanTilt' => 'àôùø æòæåò',
|
||||||
|
|
|
@ -248,6 +248,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Relatíven tud mozogni',
|
'CanMoveRel' => 'Relatíven tud mozogni',
|
||||||
'CanPan' => 'Tud jobb-bal mozgást' ,
|
'CanPan' => 'Tud jobb-bal mozgást' ,
|
||||||
'CanReset' => 'Tud alaphelyzetbe jönni',
|
'CanReset' => 'Tud alaphelyzetbe jönni',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Tud menteni profilokat',
|
'CanSetPresets' => 'Tud menteni profilokat',
|
||||||
'CanSleep' => 'Tud phihenő üzemmódot',
|
'CanSleep' => 'Tud phihenő üzemmódot',
|
||||||
'CanTilt' => 'Tud fel-le mozgást',
|
'CanTilt' => 'Tud fel-le mozgást',
|
||||||
|
|
|
@ -210,6 +210,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Puo\' Mov. Relativo',
|
'CanMoveRel' => 'Puo\' Mov. Relativo',
|
||||||
'CanPan' => 'Puo\' Pan' ,
|
'CanPan' => 'Puo\' Pan' ,
|
||||||
'CanReset' => 'Puo\' Reset',
|
'CanReset' => 'Puo\' Reset',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Puo\' impostare preset',
|
'CanSetPresets' => 'Puo\' impostare preset',
|
||||||
'CanSleep' => 'Puo\' andare in sleep',
|
'CanSleep' => 'Puo\' andare in sleep',
|
||||||
'CanTilt' => 'Puo\' Tilt',
|
'CanTilt' => 'Puo\' Tilt',
|
||||||
|
|
|
@ -206,6 +206,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Can Move Relative',
|
'CanMoveRel' => 'Can Move Relative',
|
||||||
'CanPan' => 'Can Pan' ,
|
'CanPan' => 'Can Pan' ,
|
||||||
'CanReset' => 'Can Reset',
|
'CanReset' => 'Can Reset',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Can Set Presets',
|
'CanSetPresets' => 'Can Set Presets',
|
||||||
'CanSleep' => 'Can Sleep',
|
'CanSleep' => 'Can Sleep',
|
||||||
'CanTilt' => 'Can Tilt',
|
'CanTilt' => 'Can Tilt',
|
||||||
|
|
|
@ -206,6 +206,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Can Move Relatief',
|
'CanMoveRel' => 'Can Move Relatief',
|
||||||
'CanPan' => 'Can Pan' ,
|
'CanPan' => 'Can Pan' ,
|
||||||
'CanReset' => 'Can Reset',
|
'CanReset' => 'Can Reset',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Can Set Presets',
|
'CanSetPresets' => 'Can Set Presets',
|
||||||
'CanSleep' => 'Can Sleep',
|
'CanSleep' => 'Can Sleep',
|
||||||
'CanTilt' => 'Can Tilt',
|
'CanTilt' => 'Can Tilt',
|
||||||
|
|
|
@ -220,6 +220,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Can Move Relative',
|
'CanMoveRel' => 'Can Move Relative',
|
||||||
'CanPan' => 'Can Pan' ,
|
'CanPan' => 'Can Pan' ,
|
||||||
'CanReset' => 'Can Reset',
|
'CanReset' => 'Can Reset',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Can Set Presets',
|
'CanSetPresets' => 'Can Set Presets',
|
||||||
'CanSleep' => 'Can Sleep',
|
'CanSleep' => 'Can Sleep',
|
||||||
'CanTilt' => 'Can Tilt',
|
'CanTilt' => 'Can Tilt',
|
||||||
|
|
|
@ -145,6 +145,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Can Move Relative',
|
'CanMoveRel' => 'Can Move Relative',
|
||||||
'CanPan' => 'Can Pan' ,
|
'CanPan' => 'Can Pan' ,
|
||||||
'CanReset' => 'Can Reset',
|
'CanReset' => 'Can Reset',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Can Set Presets',
|
'CanSetPresets' => 'Can Set Presets',
|
||||||
'CanSleep' => 'Can Sleep',
|
'CanSleep' => 'Can Sleep',
|
||||||
'CanTilt' => 'Can Tilt',
|
'CanTilt' => 'Can Tilt',
|
||||||
|
|
|
@ -176,6 +176,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Mişcare relativă',
|
'CanMoveRel' => 'Mişcare relativă',
|
||||||
'CanPan' => 'Rotativ' ,
|
'CanPan' => 'Rotativ' ,
|
||||||
'CanReset' => 'Can Reset',
|
'CanReset' => 'Can Reset',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Can Set Presets',
|
'CanSetPresets' => 'Can Set Presets',
|
||||||
'CanSleep' => 'Can Sleep',
|
'CanSleep' => 'Can Sleep',
|
||||||
'CanTilt' => 'Se poate înclina',
|
'CanTilt' => 'Se poate înclina',
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
// ZoneMinder Russian Translation by Borodin A.S.
|
// ZoneMinder Russian Translation by Borodin A.S.
|
||||||
// ZoneMinder Russian Translation updated by IDDQDesnik, 2017
|
// ZoneMinder Russian Translation updated by IDDQDesnik, 2017
|
||||||
|
// ZoneMinder Russian Translation updated by santos995, 2019
|
||||||
|
|
||||||
// Notes for Translators
|
// Notes for Translators
|
||||||
// 0. Get some credit, put your name in the line above (optional)
|
// 0. Get some credit, put your name in the line above (optional)
|
||||||
|
@ -76,11 +77,11 @@ $SLANG = array(
|
||||||
'32BitColour' => '32 битный цвет', // Added - 2011-06-15
|
'32BitColour' => '32 битный цвет', // Added - 2011-06-15
|
||||||
'8BitGrey' => '256 оттенков серого',
|
'8BitGrey' => '256 оттенков серого',
|
||||||
'Action' => 'Действие',
|
'Action' => 'Действие',
|
||||||
'Actual' => 'Actual', // Added - 2018-08-30
|
'Actual' => 'Актуальный', // Edited - 2019-03-25
|
||||||
'AddNewControl' => 'Добавить новый',
|
'AddNewControl' => 'Добавить новый',
|
||||||
'AddNewMonitor' => 'Добавить монитор',
|
'AddNewMonitor' => 'Добавить монитор',
|
||||||
'AddNewServer' => 'Add New Server', // Added - 2018-08-30
|
'AddNewServer' => 'Добавить новый сервер', // Edited - 2019-03-25
|
||||||
'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30
|
'AddNewStorage' => 'Добавить новое хранилище', // Edited - 2019-03-25
|
||||||
'AddNewUser' => 'Добавить пользователя',
|
'AddNewUser' => 'Добавить пользователя',
|
||||||
'AddNewZone' => 'Добавить зону',
|
'AddNewZone' => 'Добавить зону',
|
||||||
'Alarm' => 'Тревога',
|
'Alarm' => 'Тревога',
|
||||||
|
@ -110,24 +111,24 @@ $SLANG = array(
|
||||||
'AttrCause' => 'Причина',
|
'AttrCause' => 'Причина',
|
||||||
'AttrDiskBlocks' => 'Диск, блоки',
|
'AttrDiskBlocks' => 'Диск, блоки',
|
||||||
'AttrDiskPercent' => 'Диск, проценты',
|
'AttrDiskPercent' => 'Диск, проценты',
|
||||||
'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30
|
'AttrDiskSpace' => 'Дисковое пространство', // Edited - 2019-03-24
|
||||||
'AttrDuration' => 'Длительность',
|
'AttrDuration' => 'Длительность',
|
||||||
'AttrEndDate' => 'End Date', // Added - 2018-08-30
|
'AttrEndDate' => 'Дата окончания', // Edited - 2019-03-24
|
||||||
'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30
|
'AttrEndDateTime' => 'Дата/время окончания', // Edited - 2019-03-24
|
||||||
'AttrEndTime' => 'End Time', // Added - 2018-08-30
|
'AttrEndTime' => 'Время окончания', // Edited - 2019-03-24
|
||||||
'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30
|
'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30
|
||||||
'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30
|
'AttrFilterServer' => 'Фильтр для серверов запущен', // Edited - 2019-03-24
|
||||||
'AttrFrames' => 'Кол-во кадров',
|
'AttrFrames' => 'Кол-во кадров',
|
||||||
'AttrId' => 'ИД',
|
'AttrId' => 'ИД',
|
||||||
'AttrMaxScore' => 'Макс. оценка',
|
'AttrMaxScore' => 'Макс. оценка',
|
||||||
'AttrMonitorId' => 'ИД Монитора',
|
'AttrMonitorId' => 'ИД Монитора',
|
||||||
'AttrMonitorName' => 'Название Монитора',
|
'AttrMonitorName' => 'Название Монитора',
|
||||||
'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30
|
'AttrMonitorServer' => 'Монитор серверов запущен', // Edited - 2019-03-24
|
||||||
'AttrName' => 'Имя',
|
'AttrName' => 'Имя',
|
||||||
'AttrNotes' => 'Примечание',
|
'AttrNotes' => 'Примечание',
|
||||||
'AttrStartDate' => 'Start Date', // Added - 2018-08-30
|
'AttrStartDate' => 'Дата начала', // Edited - 2019-03-24
|
||||||
'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30
|
'AttrStartDateTime' => 'Дата/Время начала', // Edited - 2019-03-24
|
||||||
'AttrStartTime' => 'Start Time', // Added - 2018-08-30
|
'AttrStartTime' => 'Время начала', // Edited - 2019-03-24
|
||||||
'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30
|
'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30
|
||||||
'AttrStateId' => 'Run State', // Added - 2018-08-30
|
'AttrStateId' => 'Run State', // Added - 2018-08-30
|
||||||
'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30
|
'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30
|
||||||
|
@ -170,7 +171,7 @@ $SLANG = array(
|
||||||
'BadStreamReplayBuffer'=> 'Буфер потока повторного воспроизведения должен быть целочисленным и большим либо равным нулю',
|
'BadStreamReplayBuffer'=> 'Буфер потока повторного воспроизведения должен быть целочисленным и большим либо равным нулю',
|
||||||
'BadWarmupCount' => 'Кол-во кадров разогрева должно быть целочисленным и большим либо равным нулю',
|
'BadWarmupCount' => 'Кол-во кадров разогрева должно быть целочисленным и большим либо равным нулю',
|
||||||
'BadWebColour' => 'Цвет отметки должен быть правильным Web-цветом',
|
'BadWebColour' => 'Цвет отметки должен быть правильным Web-цветом',
|
||||||
'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30
|
'BadWebSitePath' => 'Пожалуйста, введите полной название сайта url, включая http:// или https:// префикс.', // Edited - 2019-03-24
|
||||||
'BadWidth' => 'Неправильная ширина',
|
'BadWidth' => 'Неправильная ширина',
|
||||||
'Bandwidth' => 'канал',
|
'Bandwidth' => 'канал',
|
||||||
'BandwidthHead' => 'канал', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing;
|
'BandwidthHead' => 'канал', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing;
|
||||||
|
@ -178,9 +179,9 @@ $SLANG = array(
|
||||||
'BlobSizes' => 'Размер объектов',
|
'BlobSizes' => 'Размер объектов',
|
||||||
'Blobs' => 'Кол-во объектов',
|
'Blobs' => 'Кол-во объектов',
|
||||||
'Brightness' => 'Яркость',
|
'Brightness' => 'Яркость',
|
||||||
'Buffer' => 'Buffer', // Added - 2015-04-18
|
'Buffer' => 'Буфер', // Edited - 2019-03-24
|
||||||
'Buffers' => 'Буферы',
|
'Buffers' => 'Буферы',
|
||||||
'CSSDescription' => 'Change the default css for this computer', // Added - 2015-04-18
|
'CSSDescription' => 'Изменить стандартный CSS для данного компьютера', // Edited - 2019-03-24
|
||||||
'CanAutoFocus' => 'Автофокус',
|
'CanAutoFocus' => 'Автофокус',
|
||||||
'CanAutoGain' => 'Автоусиление',
|
'CanAutoGain' => 'Автоусиление',
|
||||||
'CanAutoIris' => 'Автодиафрагма',
|
'CanAutoIris' => 'Автодиафрагма',
|
||||||
|
@ -206,6 +207,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Относительное перемещение',
|
'CanMoveRel' => 'Относительное перемещение',
|
||||||
'CanPan' => 'Панорама' ,
|
'CanPan' => 'Панорама' ,
|
||||||
'CanReset' => 'Сброс',
|
'CanReset' => 'Сброс',
|
||||||
|
'CanReboot' => 'Перезагрузка', // Added - 2019-03-24
|
||||||
'CanSetPresets' => 'Создание предустановок',
|
'CanSetPresets' => 'Создание предустановок',
|
||||||
'CanSleep' => 'Сон',
|
'CanSleep' => 'Сон',
|
||||||
'CanTilt' => 'Наклон',
|
'CanTilt' => 'Наклон',
|
||||||
|
@ -224,17 +226,17 @@ $SLANG = array(
|
||||||
'CaptureHeight' => 'Размер по Y',
|
'CaptureHeight' => 'Размер по Y',
|
||||||
'CaptureMethod' => 'Метод захвата', // Added - 2009-02-08
|
'CaptureMethod' => 'Метод захвата', // Added - 2009-02-08
|
||||||
'CapturePalette' => 'Режим захвата',
|
'CapturePalette' => 'Режим захвата',
|
||||||
'CaptureResolution' => 'Capture Resolution', // Added - 2015-04-18
|
'CaptureResolution' => 'Разрешение', // Edited - 2019-03-24
|
||||||
'CaptureWidth' => 'Размер по X',
|
'CaptureWidth' => 'Размер по X',
|
||||||
'Cause' => 'Причина',
|
'Cause' => 'Причина',
|
||||||
'CheckMethod' => 'Метод проверки тревоги',
|
'CheckMethod' => 'Метод проверки тревоги',
|
||||||
'ChooseDetectedCamera' => 'Выберите камеру', // Added - 2009-03-31
|
'ChooseDetectedCamera' => 'Выберите камеру', // Added - 2009-03-31
|
||||||
'ChooseFilter' => 'Выбрать фильтр',
|
'ChooseFilter' => 'Выбрать фильтр',
|
||||||
'ChooseLogFormat' => 'Choose a log format', // Added - 2011-06-17
|
'ChooseLogFormat' => 'Выбрать формат лога', // Edited - 2019-03-25
|
||||||
'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17
|
'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17
|
||||||
'ChoosePreset' => 'Выберите предустановку',
|
'ChoosePreset' => 'Выберите предустановку',
|
||||||
'Clear' => 'Очистить', // Added - 2011-06-16
|
'Clear' => 'Очистить', // Added - 2011-06-16
|
||||||
'CloneMonitor' => 'Clone', // Added - 2018-08-30
|
'CloneMonitor' => 'Клонировать', // Edited - 2019-03-25
|
||||||
'Close' => 'Закрыть',
|
'Close' => 'Закрыть',
|
||||||
'Colour' => 'Цвет',
|
'Colour' => 'Цвет',
|
||||||
'Command' => 'Command',
|
'Command' => 'Command',
|
||||||
|
@ -248,7 +250,7 @@ $SLANG = array(
|
||||||
'ConjOr' => 'или',
|
'ConjOr' => 'или',
|
||||||
'Console' => 'Сервер',
|
'Console' => 'Сервер',
|
||||||
'ContactAdmin' => 'Пожалуйста обратитесь к вашему администратору.',
|
'ContactAdmin' => 'Пожалуйста обратитесь к вашему администратору.',
|
||||||
'Continue' => 'Continue',
|
'Continue' => 'Продолжить', // Added - 2019-03-25
|
||||||
'Contrast' => 'Контраст',
|
'Contrast' => 'Контраст',
|
||||||
'Control' => 'Управление',
|
'Control' => 'Управление',
|
||||||
'ControlAddress' => 'Адрес устройства',
|
'ControlAddress' => 'Адрес устройства',
|
||||||
|
@ -262,12 +264,12 @@ $SLANG = array(
|
||||||
'CycleWatch' => 'Циклический просмотр',
|
'CycleWatch' => 'Циклический просмотр',
|
||||||
'DateTime' => 'Дата/Время', // Added - 2011-06-16
|
'DateTime' => 'Дата/Время', // Added - 2011-06-16
|
||||||
'Day' => 'День',
|
'Day' => 'День',
|
||||||
'Debug' => 'Debug',
|
'Debug' => 'Отладка', // Added - 2019-03-25
|
||||||
'DefaultRate' => 'Скорость по умолчанию',
|
'DefaultRate' => 'Скорость по умолчанию',
|
||||||
'DefaultScale' => 'Масштаб по умолчанию',
|
'DefaultScale' => 'Масштаб по умолчанию',
|
||||||
'DefaultView' => 'Вид по умолчанию',
|
'DefaultView' => 'Вид по умолчанию',
|
||||||
'Deinterlacing' => 'Устранение чересстрочности', // Added - 2015-04-18
|
'Deinterlacing' => 'Устранение чересстрочности', // Added - 2015-04-18
|
||||||
'Delay' => 'Delay', // Added - 2015-04-18
|
'Delay' => 'Задержка', // Edited - 2019-03-25
|
||||||
'Delete' => 'Удалить',
|
'Delete' => 'Удалить',
|
||||||
'DeleteAndNext' => 'Удалить & след.',
|
'DeleteAndNext' => 'Удалить & след.',
|
||||||
'DeleteAndPrev' => 'Удалить & пред.',
|
'DeleteAndPrev' => 'Удалить & пред.',
|
||||||
|
@ -275,18 +277,18 @@ $SLANG = array(
|
||||||
'Description' => 'Описание',
|
'Description' => 'Описание',
|
||||||
'DetectedCameras' => 'Найденные камеры', // Added - 2009-03-31
|
'DetectedCameras' => 'Найденные камеры', // Added - 2009-03-31
|
||||||
'DetectedProfiles' => 'Найденные профили', // Added - 2015-04-18
|
'DetectedProfiles' => 'Найденные профили', // Added - 2015-04-18
|
||||||
'Device' => 'Device', // Added - 2009-02-08
|
'Device' => 'Устройство', // Edited - 2019-03-25
|
||||||
'DeviceChannel' => 'Канал',
|
'DeviceChannel' => 'Канал',
|
||||||
'DeviceFormat' => 'Формат',
|
'DeviceFormat' => 'Формат',
|
||||||
'DeviceNumber' => 'Номер устройства',
|
'DeviceNumber' => 'Номер устройства',
|
||||||
'DevicePath' => 'Путь к устройству',
|
'DevicePath' => 'Путь к устройству',
|
||||||
'Devices' => 'Devices',
|
'Devices' => 'Устройства', // Edited - 2019-03-25
|
||||||
'Dimensions' => 'Размеры',
|
'Dimensions' => 'Размеры',
|
||||||
'DisableAlarms' => 'Запретить тревогу',
|
'DisableAlarms' => 'Запретить тревогу',
|
||||||
'Disk' => 'Диск',
|
'Disk' => 'Диск',
|
||||||
'Display' => 'Display', // Added - 2011-01-30
|
'Display' => 'Display', // Added - 2011-01-30
|
||||||
'Displaying' => 'Отображено', // Added - 2011-06-16
|
'Displaying' => 'Отображено', // Added - 2011-06-16
|
||||||
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
|
'DoNativeMotionDetection'=> 'Использовать встроенное обнаружение движения', // Edited - 2019-03-25
|
||||||
'Donate' => 'Поддержите проект',
|
'Donate' => 'Поддержите проект',
|
||||||
'DonateAlready' => 'Нет, я уже сделал пожертвование',
|
'DonateAlready' => 'Нет, я уже сделал пожертвование',
|
||||||
'DonateEnticement' => 'Вы какое-то время используете ZoneMinder и, надеемся, находите его полезным дополнением к вашей домашней или рабочей безопасности. Хотя ZoneMinder есть и будет оставаться свободным и бесплатным, он требует денег на разработку и поддержку. Если Вы хотите поддержать его будущее развитие и новые функции, пожалуйста сделайте пожертвование. Это, конечно, необязательно, но очень высоко ценится. Вы можете пожертвовать любую сумму.<br><br>Если Вы хотите сделать пожертвование, то выберите соответствующий вариант ниже или перейдите по адресу https://www.bountysource.com/teams/zoneminder в вашем браузере.<br><br>Спасибо за использование ZoneMinder, и не забывайте посетить форум на ZoneMinder.com для поддержки и пожеланий как сделать ZoneMinder еще лучше.',
|
'DonateEnticement' => 'Вы какое-то время используете ZoneMinder и, надеемся, находите его полезным дополнением к вашей домашней или рабочей безопасности. Хотя ZoneMinder есть и будет оставаться свободным и бесплатным, он требует денег на разработку и поддержку. Если Вы хотите поддержать его будущее развитие и новые функции, пожалуйста сделайте пожертвование. Это, конечно, необязательно, но очень высоко ценится. Вы можете пожертвовать любую сумму.<br><br>Если Вы хотите сделать пожертвование, то выберите соответствующий вариант ниже или перейдите по адресу https://www.bountysource.com/teams/zoneminder в вашем браузере.<br><br>Спасибо за использование ZoneMinder, и не забывайте посетить форум на ZoneMinder.com для поддержки и пожеланий как сделать ZoneMinder еще лучше.',
|
||||||
|
@ -297,11 +299,11 @@ $SLANG = array(
|
||||||
'DonateRemindWeek' => 'Нет, не сейчас, напомнить через неделю',
|
'DonateRemindWeek' => 'Нет, не сейчас, напомнить через неделю',
|
||||||
'DonateYes' => 'Да, я хотел бы сделать пожертвование',
|
'DonateYes' => 'Да, я хотел бы сделать пожертвование',
|
||||||
'Download' => 'Скачать',
|
'Download' => 'Скачать',
|
||||||
'DownloadVideo' => 'Download Video', // Added - 2018-08-30
|
'DownloadVideo' => 'Скачать видео', // Added - 2019-03-24
|
||||||
'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31
|
'DuplicateMonitorName' => 'Скопировать имя монитора', // Added - 2019-03-25
|
||||||
'Duration' => 'Длительность',
|
'Duration' => 'Длительность',
|
||||||
'Edit' => 'Редактирование',
|
'Edit' => 'Редактирование',
|
||||||
'EditLayout' => 'Edit Layout', // Added - 2018-08-30
|
'EditLayout' => 'Редактирование шаблона', // Added - 2019-03-25
|
||||||
'Email' => 'Email',
|
'Email' => 'Email',
|
||||||
'EnableAlarms' => 'Разрешить тревогу',
|
'EnableAlarms' => 'Разрешить тревогу',
|
||||||
'Enabled' => 'Включен',
|
'Enabled' => 'Включен',
|
||||||
|
@ -312,13 +314,13 @@ $SLANG = array(
|
||||||
'Etc' => 'и т.д.',
|
'Etc' => 'и т.д.',
|
||||||
'Event' => 'Событие',
|
'Event' => 'Событие',
|
||||||
'EventFilter' => 'Фильтр событий',
|
'EventFilter' => 'Фильтр событий',
|
||||||
'EventId' => 'Event Id',
|
'EventId' => 'Id события', // Added - 2019-03-25
|
||||||
'EventName' => 'Event Name',
|
'EventName' => 'Имя события', // Added - 2019-03-25
|
||||||
'EventPrefix' => 'Префикс события',
|
'EventPrefix' => 'Префикс события',
|
||||||
'Events' => 'События',
|
'Events' => 'События',
|
||||||
'Exclude' => 'Исключить',
|
'Exclude' => 'Исключить',
|
||||||
'Execute' => 'Выполнить',
|
'Execute' => 'Выполнить',
|
||||||
'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30
|
'Exif' => 'Включить EXIF информацию в изображение', // Added - 2019-03-24
|
||||||
'Export' => 'Экспорт',
|
'Export' => 'Экспорт',
|
||||||
'ExportDetails' => 'Экспортировать описание события',
|
'ExportDetails' => 'Экспортировать описание события',
|
||||||
'ExportFailed' => 'Ошибка экспорта',
|
'ExportFailed' => 'Ошибка экспорта',
|
||||||
|
@ -350,14 +352,14 @@ $SLANG = array(
|
||||||
'FilterMessageEvents' => 'Message details of all matches',
|
'FilterMessageEvents' => 'Message details of all matches',
|
||||||
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
|
'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30
|
||||||
'FilterPx' => 'Пкс фильтра',
|
'FilterPx' => 'Пкс фильтра',
|
||||||
'FilterUnset' => 'You must specify a filter width and height',
|
'FilterUnset' => 'Вы должны указать ширину и высоту фильтра', // Added - 2019-03-25
|
||||||
'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30
|
'FilterUpdateDiskSpace'=> 'Обновить используемое дисковое пространство', // Edited - 2019-03-25
|
||||||
'FilterUploadEvents' => 'Upload all matches',
|
'FilterUploadEvents' => 'Загрузить все совпадения', // Added - 2019-03-25
|
||||||
'FilterVideoEvents' => 'Create video for all matches',
|
'FilterVideoEvents' => 'Создать видео для всех совпадений', // Added - 2019-03-25
|
||||||
'Filters' => 'Фильтры',
|
'Filters' => 'Фильтры',
|
||||||
'First' => 'Первый',
|
'First' => 'Первый',
|
||||||
'FlippedHori' => 'Flipped Horizontally',
|
'FlippedHori' => 'Перевернутый горизонтально', // Added - 2019-03-25
|
||||||
'FlippedVert' => 'Flipped Vertically',
|
'FlippedVert' => 'Перевернутый вертикально', // Added - 2019-03-25
|
||||||
'FnMocord' => 'Mocord', // Added 2013.08.16.
|
'FnMocord' => 'Mocord', // Added 2013.08.16.
|
||||||
'FnModect' => 'Modect', // Added 2013.08.16.
|
'FnModect' => 'Modect', // Added 2013.08.16.
|
||||||
'FnMonitor' => 'Monitor', // Added 2013.08.16.
|
'FnMonitor' => 'Monitor', // Added 2013.08.16.
|
||||||
|
@ -376,7 +378,7 @@ $SLANG = array(
|
||||||
'Function' => 'Функция',
|
'Function' => 'Функция',
|
||||||
'Gain' => 'Gain',
|
'Gain' => 'Gain',
|
||||||
'General' => 'Основные',
|
'General' => 'Основные',
|
||||||
'GenerateDownload' => 'Generate Download', // Added - 2018-08-30
|
'GenerateDownload' => 'Сгенерировать загрузку', // Edited - 2019-03-25
|
||||||
'GenerateVideo' => 'Генерировать видео',
|
'GenerateVideo' => 'Генерировать видео',
|
||||||
'GeneratingVideo' => 'Генерируется видео',
|
'GeneratingVideo' => 'Генерируется видео',
|
||||||
'GoToZoneMinder' => 'Перейти на ZoneMinder.com',
|
'GoToZoneMinder' => 'Перейти на ZoneMinder.com',
|
||||||
|
@ -397,7 +399,7 @@ $SLANG = array(
|
||||||
'High' => 'широкий',
|
'High' => 'широкий',
|
||||||
'HighBW' => 'Широкий канал',
|
'HighBW' => 'Широкий канал',
|
||||||
'Home' => 'Домой',
|
'Home' => 'Домой',
|
||||||
'Hostname' => 'Hostname', // Added - 2018-08-30
|
'Hostname' => 'Имя хоста', // Edited - 2019-03-25
|
||||||
'Hour' => 'Час',
|
'Hour' => 'Час',
|
||||||
'Hue' => 'Оттенок',
|
'Hue' => 'Оттенок',
|
||||||
'Id' => 'ИД',
|
'Id' => 'ИД',
|
||||||
|
@ -405,13 +407,13 @@ $SLANG = array(
|
||||||
'Ignore' => 'Игнорировать',
|
'Ignore' => 'Игнорировать',
|
||||||
'Image' => 'Изображение',
|
'Image' => 'Изображение',
|
||||||
'ImageBufferSize' => 'Буфер изображений',
|
'ImageBufferSize' => 'Буфер изображений',
|
||||||
'Images' => 'Images',
|
'Images' => 'Изображения', // Added - 2019-03-25
|
||||||
'In' => 'In',
|
'In' => 'In',
|
||||||
'Include' => 'Включить',
|
'Include' => 'Включить',
|
||||||
'Inverted' => 'Инвертировать',
|
'Inverted' => 'Инвертировать',
|
||||||
'Iris' => 'Наклон',
|
'Iris' => 'Наклон',
|
||||||
'KeyString' => 'Key String',
|
'KeyString' => 'Key String',
|
||||||
'Label' => 'Label',
|
'Label' => 'Имя', // Added - 2019-03-25
|
||||||
'Language' => 'Язык',
|
'Language' => 'Язык',
|
||||||
'Last' => 'Последний',
|
'Last' => 'Последний',
|
||||||
'Layout' => 'Раскладка', // Added - 2009-02-08
|
'Layout' => 'Раскладка', // Added - 2009-02-08
|
||||||
|
@ -422,16 +424,16 @@ $SLANG = array(
|
||||||
'Line' => 'Строка', // Added - 2011-06-16
|
'Line' => 'Строка', // Added - 2011-06-16
|
||||||
'LinkedMonitors' => 'Привязанные мониторы',
|
'LinkedMonitors' => 'Привязанные мониторы',
|
||||||
'List' => 'Список',
|
'List' => 'Список',
|
||||||
'ListMatches' => 'List Matches', // Added - 2018-08-30
|
'ListMatches' => 'Список совпадений', // Edited - 2019-03-25
|
||||||
'Load' => 'Нагрузка',
|
'Load' => 'Нагрузка',
|
||||||
'Local' => 'Локальный',
|
'Local' => 'Локальный',
|
||||||
'Log' => 'Лог', // Added - 2011-06-16;
|
'Log' => 'Лог', // Added - 2011-06-16;
|
||||||
'LoggedInAs' => 'Пользователь',
|
'LoggedInAs' => 'Пользователь',
|
||||||
'Logging' => 'Logging', // Added - 2011-06-16
|
'Logging' => 'Логгирование', // Added - 2019-03-24
|
||||||
'LoggingIn' => 'Вход в систему',
|
'LoggingIn' => 'Вход в систему',
|
||||||
'Login' => 'Войти',
|
'Login' => 'Войти',
|
||||||
'Logout' => 'Выйти',
|
'Logout' => 'Выйти',
|
||||||
'Logs' => 'Logs', // Added - 2011-06-17
|
'Logs' => 'Логи', // Added - 2019-03-24
|
||||||
'Low' => 'узкий',
|
'Low' => 'узкий',
|
||||||
'LowBW' => 'Узкий канал',
|
'LowBW' => 'Узкий канал',
|
||||||
'Main' => 'Основные',
|
'Main' => 'Основные',
|
||||||
|
@ -439,7 +441,7 @@ $SLANG = array(
|
||||||
'Manual' => 'Manual',
|
'Manual' => 'Manual',
|
||||||
'Mark' => 'Метка',
|
'Mark' => 'Метка',
|
||||||
'Max' => 'Макс.',
|
'Max' => 'Макс.',
|
||||||
'MaxBandwidth' => 'Max Bandwidth',
|
'MaxBandwidth' => 'Макс. пропускная способность', // Added - 2019-03-25
|
||||||
'MaxBrScore' => 'Макс.<br/>оценка',
|
'MaxBrScore' => 'Макс.<br/>оценка',
|
||||||
'MaxFocusRange' => 'Макс. диап. фокуса',
|
'MaxFocusRange' => 'Макс. диап. фокуса',
|
||||||
'MaxFocusSpeed' => 'Макс. скор. фокуса',
|
'MaxFocusSpeed' => 'Макс. скор. фокуса',
|
||||||
|
@ -509,7 +511,7 @@ $SLANG = array(
|
||||||
'MonitorProbeIntro' => 'В этом списке показаны найденные аналоговые и сетевые камеры, как уже заведенные, так и доступные для выбора.<br/><br/>Выберите нужную из списка ниже.<br/><br/>Обратите внимание, что не все камеры могут быть найдены, и что выбор камеры может переписать настройки определенные для этого монитора.<br/><br/>', // Added - 2009-03-31
|
'MonitorProbeIntro' => 'В этом списке показаны найденные аналоговые и сетевые камеры, как уже заведенные, так и доступные для выбора.<br/><br/>Выберите нужную из списка ниже.<br/><br/>Обратите внимание, что не все камеры могут быть найдены, и что выбор камеры может переписать настройки определенные для этого монитора.<br/><br/>', // Added - 2009-03-31
|
||||||
'Monitors' => 'Мониторы',
|
'Monitors' => 'Мониторы',
|
||||||
'Montage' => 'Монтаж',
|
'Montage' => 'Монтаж',
|
||||||
'MontageReview' => 'Montage Review', // Added - 2018-08-30
|
'MontageReview' => 'Обзор монтажа', // Added - 2019-03-24
|
||||||
'Month' => 'Месяц',
|
'Month' => 'Месяц',
|
||||||
'More' => 'Еще', // Added - 2011-06-16
|
'More' => 'Еще', // Added - 2011-06-16
|
||||||
'MotionFrameSkip' => 'Кол-во пропуск. кадров движения',
|
'MotionFrameSkip' => 'Кол-во пропуск. кадров движения',
|
||||||
|
@ -529,16 +531,16 @@ $SLANG = array(
|
||||||
'Network' => 'Сеть',
|
'Network' => 'Сеть',
|
||||||
'New' => 'Нов.',
|
'New' => 'Нов.',
|
||||||
'NewGroup' => 'Новая группа',
|
'NewGroup' => 'Новая группа',
|
||||||
'NewLabel' => 'New Label',
|
'NewLabel' => 'Новое имя', // Added - 2019-03-25
|
||||||
'NewPassword' => 'Новый пароль',
|
'NewPassword' => 'Новый пароль',
|
||||||
'NewState' => 'Новое состояние',
|
'NewState' => 'Новое состояние',
|
||||||
'NewUser' => 'Новый пользователь',
|
'NewUser' => 'Новый пользователь',
|
||||||
'Next' => 'След.',
|
'Next' => 'След.',
|
||||||
'No' => 'Нет',
|
'No' => 'Нет',
|
||||||
'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31
|
'NoDetectedCameras' => 'Нет камер', // Added - 2019-03-24
|
||||||
'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30
|
'NoDetectedProfiles' => 'Нет профилей', // Added - 2019-03-24
|
||||||
'NoFramesRecorded' => 'Это событие не содержит кадров',
|
'NoFramesRecorded' => 'Это событие не содержит кадров',
|
||||||
'NoGroup' => 'No Group',
|
'NoGroup' => 'Нет группы', // Added - 2019-03-24
|
||||||
'NoSavedFilters' => 'нет сохраненных фильтров',
|
'NoSavedFilters' => 'нет сохраненных фильтров',
|
||||||
'NoStatisticsRecorded' => 'Статистика по этому событию/кадру не записана',
|
'NoStatisticsRecorded' => 'Статистика по этому событию/кадру не записана',
|
||||||
'None' => 'отсутствует',
|
'None' => 'отсутствует',
|
||||||
|
@ -546,8 +548,8 @@ $SLANG = array(
|
||||||
'Normal' => 'Нормальная',
|
'Normal' => 'Нормальная',
|
||||||
'Notes' => 'Примечание',
|
'Notes' => 'Примечание',
|
||||||
'NumPresets' => 'Кол-во предустановок',
|
'NumPresets' => 'Кол-во предустановок',
|
||||||
'Off' => 'Off',
|
'Off' => 'Выкл.', // Added - 2019-03-25
|
||||||
'On' => 'On',
|
'On' => 'Вкл.', // Added - 2019-03-25
|
||||||
'OnvifCredentialsIntro'=> 'Пожалуйста укажите имя пользователя и пароль для выбранной камеры.<br/><br/>Если пользователь для камеры не был создан, тогда будет создан новый с указанными данными.<br/><br/>', // Added - 2015-04-18
|
'OnvifCredentialsIntro'=> 'Пожалуйста укажите имя пользователя и пароль для выбранной камеры.<br/><br/>Если пользователь для камеры не был создан, тогда будет создан новый с указанными данными.<br/><br/>', // Added - 2015-04-18
|
||||||
'OnvifProbe' => 'ONVIF', // Added - 2015-04-18
|
'OnvifProbe' => 'ONVIF', // Added - 2015-04-18
|
||||||
'OnvifProbeIntro' => 'В этом списке показаны найденные ONVIF камеры, как уже заведенные, так и доступные для выбора.<br/><br/>Выберите нужную из списка ниже.<br/><br/>Обратите внимание, что не все камеры могут быть найдены, и что выбор камеры может переписать настройки определенные для этого монитора.<br/><br/>', // Added - 2015-04-18
|
'OnvifProbeIntro' => 'В этом списке показаны найденные ONVIF камеры, как уже заведенные, так и доступные для выбора.<br/><br/>Выберите нужную из списка ниже.<br/><br/>Обратите внимание, что не все камеры могут быть найдены, и что выбор камеры может переписать настройки определенные для этого монитора.<br/><br/>', // Added - 2015-04-18
|
||||||
|
@ -566,7 +568,7 @@ $SLANG = array(
|
||||||
'Open' => 'Открыть',
|
'Open' => 'Открыть',
|
||||||
'OptionHelp' => 'Справка',
|
'OptionHelp' => 'Справка',
|
||||||
'OptionRestartWarning' => 'Эти изменения подействуют только после перезапуска программы.',
|
'OptionRestartWarning' => 'Эти изменения подействуют только после перезапуска программы.',
|
||||||
'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30
|
'OptionalEncoderParam' => 'Необязательные параметры кодировщика', // Added - 2019-03-24
|
||||||
'Options' => 'Опции',
|
'Options' => 'Опции',
|
||||||
'OrEnterNewName' => 'или введите новое имя',
|
'OrEnterNewName' => 'или введите новое имя',
|
||||||
'Order' => 'Сортировка',
|
'Order' => 'Сортировка',
|
||||||
|
@ -591,7 +593,7 @@ $SLANG = array(
|
||||||
'Play' => 'Играть',
|
'Play' => 'Играть',
|
||||||
'PlayAll' => 'Воспр. все',
|
'PlayAll' => 'Воспр. все',
|
||||||
'PleaseWait' => 'Пожалуйста подождите',
|
'PleaseWait' => 'Пожалуйста подождите',
|
||||||
'Plugins' => 'Plugins',
|
'Plugins' => 'Плагины', // Edited - 2019-03-24
|
||||||
'Point' => 'Точка',
|
'Point' => 'Точка',
|
||||||
'PostEventImageBuffer' => 'Буфер после события',
|
'PostEventImageBuffer' => 'Буфер после события',
|
||||||
'PreEventImageBuffer' => 'Буфер до события',
|
'PreEventImageBuffer' => 'Буфер до события',
|
||||||
|
@ -604,13 +606,13 @@ $SLANG = array(
|
||||||
'ProfileProbeIntro' => 'В этом списке показаны существующие профили потока выбранной камеры.<br/><br/>Выберите нужный из списка ниже.<br/><br/>Обратите внимание, что ZoneMinder не может добавить дополнительный профиль, и что выбор профиля может переписать настройки определенные для этого монитора.<br/><br/>', // Added - 2015-04-18
|
'ProfileProbeIntro' => 'В этом списке показаны существующие профили потока выбранной камеры.<br/><br/>Выберите нужный из списка ниже.<br/><br/>Обратите внимание, что ZoneMinder не может добавить дополнительный профиль, и что выбор профиля может переписать настройки определенные для этого монитора.<br/><br/>', // Added - 2015-04-18
|
||||||
'Progress' => 'Прогресс', // Added - 2015-04-18
|
'Progress' => 'Прогресс', // Added - 2015-04-18
|
||||||
'Protocol' => 'Протокол',
|
'Protocol' => 'Протокол',
|
||||||
'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30
|
'RTSPDescribe' => 'Использовать RTSP URL для ответа', // Edited - 2019-03-25
|
||||||
'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30
|
'RTSPTransport' => 'Транспортный протокол RTSP', // Edited - 2019-03-25
|
||||||
'Rate' => 'Скорость',
|
'Rate' => 'Скорость',
|
||||||
'Real' => 'Реальная',
|
'Real' => 'Реальная',
|
||||||
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30
|
'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30
|
||||||
'Record' => 'Record',
|
'Record' => 'Record',
|
||||||
'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30
|
'RecordAudio' => 'Сохранять ли аудиопоток при сохранении события.', // Edited - 2019-03-25
|
||||||
'RefImageBlendPct' => 'Смешение опорного кадра, %',
|
'RefImageBlendPct' => 'Смешение опорного кадра, %',
|
||||||
'Refresh' => 'Обновить',
|
'Refresh' => 'Обновить',
|
||||||
'Remote' => 'Удаленный',
|
'Remote' => 'Удаленный',
|
||||||
|
@ -626,7 +628,7 @@ $SLANG = array(
|
||||||
'ReplayAll' => 'Все события',
|
'ReplayAll' => 'Все события',
|
||||||
'ReplayGapless' => 'События подряд',
|
'ReplayGapless' => 'События подряд',
|
||||||
'ReplaySingle' => 'Одно событие',
|
'ReplaySingle' => 'Одно событие',
|
||||||
'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30
|
'ReportEventAudit' => 'Отчёт о событиях аудита', // Edited - 2019-03-24
|
||||||
'Reset' => 'Сбросить',
|
'Reset' => 'Сбросить',
|
||||||
'ResetEventCounts' => 'Обнулить счетчик событий',
|
'ResetEventCounts' => 'Обнулить счетчик событий',
|
||||||
'Restart' => 'Перезапустить',
|
'Restart' => 'Перезапустить',
|
||||||
|
@ -638,14 +640,14 @@ $SLANG = array(
|
||||||
'Rewind' => 'Назад',
|
'Rewind' => 'Назад',
|
||||||
'RotateLeft' => 'Повернуть влево',
|
'RotateLeft' => 'Повернуть влево',
|
||||||
'RotateRight' => 'Повернуть вправо',
|
'RotateRight' => 'Повернуть вправо',
|
||||||
'RunLocalUpdate' => 'Please run zmupdate.pl to update', // Added - 2011-05-25
|
'RunLocalUpdate' => 'Запустите zmupdate.pl для обновления', // Edited - 2019-03-24
|
||||||
'RunMode' => 'Режим работы',
|
'RunMode' => 'Режим работы',
|
||||||
'RunState' => 'Состояние',
|
'RunState' => 'Состояние',
|
||||||
'Running' => 'Выполняется',
|
'Running' => 'Выполняется',
|
||||||
'Save' => 'Сохранить',
|
'Save' => 'Сохранить',
|
||||||
'SaveAs' => 'Сохранить как',
|
'SaveAs' => 'Сохранить как',
|
||||||
'SaveFilter' => 'Сохранить фильтр',
|
'SaveFilter' => 'Сохранить фильтр',
|
||||||
'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30
|
'SaveJPEGs' => 'Сохранить JPEG-и', // Edited - 2019-03-24
|
||||||
'Scale' => 'Масштаб',
|
'Scale' => 'Масштаб',
|
||||||
'Score' => 'Оценка',
|
'Score' => 'Оценка',
|
||||||
'Secs' => 'Сек.',
|
'Secs' => 'Сек.',
|
||||||
|
@ -653,46 +655,46 @@ $SLANG = array(
|
||||||
'Select' => 'Выбор',
|
'Select' => 'Выбор',
|
||||||
'SelectFormat' => 'Выберите формат', // Added - 2011-06-17
|
'SelectFormat' => 'Выберите формат', // Added - 2011-06-17
|
||||||
'SelectLog' => 'Выберите лог', // Added - 2011-06-17
|
'SelectLog' => 'Выберите лог', // Added - 2011-06-17
|
||||||
'SelectMonitors' => 'Select Monitors',
|
'SelectMonitors' => 'Выбрать Мониторы', // Edited - 2019-03-24
|
||||||
'SelfIntersecting' => 'Polygon edges must not intersect',
|
'SelfIntersecting' => 'Polygon edges must not intersect',
|
||||||
'Set' => 'Set',
|
'Set' => 'Установка', // Edited - 2019-03-24
|
||||||
'SetNewBandwidth' => 'Установка новой ширина канала',
|
'SetNewBandwidth' => 'Установка новой ширина канала',
|
||||||
'SetPreset' => 'Set Preset',
|
'SetPreset' => 'Установка пресета', // Edited - 2019-03-24
|
||||||
'Settings' => 'Настройки',
|
'Settings' => 'Настройки',
|
||||||
'ShowFilterWindow' => 'Показать окно фильтра',
|
'ShowFilterWindow' => 'Показать окно фильтра',
|
||||||
'ShowTimeline' => 'Показать график',
|
'ShowTimeline' => 'Показать график',
|
||||||
'SignalCheckColour' => 'Цвет проверки сигнала',
|
'SignalCheckColour' => 'Цвет проверки сигнала',
|
||||||
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
|
'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30
|
||||||
'Size' => 'Size',
|
'Size' => 'Размер', // Edited - 2019-03-24
|
||||||
'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30
|
'SkinDescription' => 'Смена стандартного скина для данного компьютера', // Edited - 2019-03-24
|
||||||
'Sleep' => 'Sleep',
|
'Sleep' => 'Sleep',
|
||||||
'SortAsc' => 'По возр.',
|
'SortAsc' => 'По возр.',
|
||||||
'SortBy' => 'Сортировать',
|
'SortBy' => 'Сортировать',
|
||||||
'SortDesc' => 'По убыв.',
|
'SortDesc' => 'По убыв.',
|
||||||
'Source' => 'Источник',
|
'Source' => 'Источник',
|
||||||
'SourceColours' => 'Source Colours', // Added - 2009-02-08
|
'SourceColours' => 'Цвета источника', // Edited - 2019-03-24
|
||||||
'SourcePath' => 'Путь к источнику', // Added - 2009-02-08
|
'SourcePath' => 'Путь к источнику', // Added - 2009-02-08
|
||||||
'SourceType' => 'Тип источника',
|
'SourceType' => 'Тип источника',
|
||||||
'Speed' => 'Speed',
|
'Speed' => 'Скорость', //Edited - 2019-03-24
|
||||||
'SpeedHigh' => 'High Speed',
|
'SpeedHigh' => 'Высокая скорость', //Edited - 2019-03-24
|
||||||
'SpeedLow' => 'Low Speed',
|
'SpeedLow' => 'Низкая скорость', //Edited - 2019-03-24
|
||||||
'SpeedMedium' => 'Medium Speed',
|
'SpeedMedium' => 'Средняя скорость',
|
||||||
'SpeedTurbo' => 'Turbo Speed',
|
'SpeedTurbo' => 'Максимальная скорость', // Edited - 2019-03-24
|
||||||
'Start' => 'Запустить',
|
'Start' => 'Запустить',
|
||||||
'State' => 'Состояние',
|
'State' => 'Состояние',
|
||||||
'Stats' => 'Статистика',
|
'Stats' => 'Статистика',
|
||||||
'Status' => 'Статус',
|
'Status' => 'Статус',
|
||||||
'StatusConnected' => 'Capturing', // Added - 2018-08-30
|
'StatusConnected' => 'Записывается', // Edited - 2019-03-25
|
||||||
'StatusNotRunning' => 'Not Running', // Added - 2018-08-30
|
'StatusNotRunning' => 'Не запущен', // Edited - 2019-03-25
|
||||||
'StatusRunning' => 'Not Capturing', // Added - 2018-08-30
|
'StatusRunning' => 'Не записывается', // Edited - 2019-03-25
|
||||||
'StatusUnknown' => 'Unknown', // Added - 2018-08-30
|
'StatusUnknown' => 'Неизвестно', // Edited - 2019-03-25
|
||||||
'Step' => 'Шаг',
|
'Step' => 'Шаг',
|
||||||
'StepBack' => 'Кадр назад',
|
'StepBack' => 'Кадр назад',
|
||||||
'StepForward' => 'Кадр вперед',
|
'StepForward' => 'Кадр вперед',
|
||||||
'StepLarge' => 'Large Step',
|
'StepLarge' => 'Большой шаг', // Added - 2019-03-25
|
||||||
'StepMedium' => 'Medium Step',
|
'StepMedium' => 'Средний шаг', // Added - 2019-03-25
|
||||||
'StepNone' => 'No Step',
|
'StepNone' => 'Без шагов', // Added - 2019-03-25
|
||||||
'StepSmall' => 'Small Step',
|
'StepSmall' => 'Малый шаг', // Added - 2019-03-25
|
||||||
'Stills' => 'Стоп-кадры',
|
'Stills' => 'Стоп-кадры',
|
||||||
'Stop' => 'Остановить',
|
'Stop' => 'Остановить',
|
||||||
'Stopped' => 'Остановлен',
|
'Stopped' => 'Остановлен',
|
||||||
|
@ -757,24 +759,24 @@ $SLANG = array(
|
||||||
'VersionRemindNever' => 'Не говорить о новых версиях',
|
'VersionRemindNever' => 'Не говорить о новых версиях',
|
||||||
'VersionRemindWeek' => 'Напомнить через неделю',
|
'VersionRemindWeek' => 'Напомнить через неделю',
|
||||||
'Video' => 'Видео',
|
'Video' => 'Видео',
|
||||||
'VideoFormat' => 'Video Format',
|
'VideoFormat' => 'Формат видео', // Edited - 2019-03-24
|
||||||
'VideoGenFailed' => 'Ошибка генерации видео!',
|
'VideoGenFailed' => 'Ошибка генерации видео!',
|
||||||
'VideoGenFiles' => 'Existing Video Files',
|
'VideoGenFiles' => 'Existing Video Files',
|
||||||
'VideoGenNoFiles' => 'No Video Files Found',
|
'VideoGenNoFiles' => 'Видео не найдено', // Edited - 2019-03-24
|
||||||
'VideoGenParms' => 'Параметры генерации видео',
|
'VideoGenParms' => 'Параметры генерации видео',
|
||||||
'VideoGenSucceeded' => 'Video Generation Succeeded!',
|
'VideoGenSucceeded' => 'Видео сгенерировано!', // Edited - 2019-03-24
|
||||||
'VideoSize' => 'Размер изображения',
|
'VideoSize' => 'Размер изображения',
|
||||||
'VideoWriter' => 'Video Writer', // Added - 2018-08-30
|
'VideoWriter' => 'Video Writer', // Added - 2018-08-30
|
||||||
'View' => 'Просмотр',
|
'View' => 'Просмотр',
|
||||||
'ViewAll' => 'Просм. все',
|
'ViewAll' => 'Просм. все',
|
||||||
'ViewEvent' => 'View Event',
|
'ViewEvent' => 'Просм. событие', // Edited - 2019-03-24
|
||||||
'ViewPaged' => 'Просм. постранично',
|
'ViewPaged' => 'Просм. постранично',
|
||||||
'Wake' => 'Wake',
|
'Wake' => 'Wake',
|
||||||
'WarmupFrames' => 'Кадры разогрева',
|
'WarmupFrames' => 'Кадры разогрева',
|
||||||
'Watch' => 'Watch',
|
'Watch' => 'Watch',
|
||||||
'Web' => 'Интерфейс',
|
'Web' => 'Интерфейс',
|
||||||
'WebColour' => 'Цвет отметки',
|
'WebColour' => 'Цвет отметки',
|
||||||
'WebSiteUrl' => 'Website URL', // Added - 2018-08-30
|
'WebSiteUrl' => 'URL сайта', // Edited - 2019-03-25
|
||||||
'Week' => 'Неделя',
|
'Week' => 'Неделя',
|
||||||
'White' => 'Бал. белого',
|
'White' => 'Бал. белого',
|
||||||
'WhiteBalance' => 'White Balance',
|
'WhiteBalance' => 'White Balance',
|
||||||
|
@ -797,7 +799,7 @@ $SLANG = array(
|
||||||
'ZoneMinMaxBlobs' => 'Мин/Макс кол-во объектов',
|
'ZoneMinMaxBlobs' => 'Мин/Макс кол-во объектов',
|
||||||
'ZoneMinMaxFiltArea' => 'Мин/Макс разм. фильтр. зоны ',
|
'ZoneMinMaxFiltArea' => 'Мин/Макс разм. фильтр. зоны ',
|
||||||
'ZoneMinMaxPixelThres' => 'Мин/Макс порог изм. пикс. (0-255)',
|
'ZoneMinMaxPixelThres' => 'Мин/Макс порог изм. пикс. (0-255)',
|
||||||
'ZoneMinderLog' => 'ZoneMinder Log', // Added - 2011-06-17
|
'ZoneMinderLog' => 'Лог ZoneMinder', // Edited - 2019-03-25
|
||||||
'ZoneOverloadFrames' => 'Кол-во игнор. кадров перегрузки',
|
'ZoneOverloadFrames' => 'Кол-во игнор. кадров перегрузки',
|
||||||
'Zones' => 'Зоны',
|
'Zones' => 'Зоны',
|
||||||
'Zoom' => 'Увеличение',
|
'Zoom' => 'Увеличение',
|
||||||
|
|
|
@ -206,6 +206,7 @@ $SLANG = array(
|
||||||
'CanMoveRel' => 'Har relativ förflyttning',
|
'CanMoveRel' => 'Har relativ förflyttning',
|
||||||
'CanPan' => 'Har panorering',
|
'CanPan' => 'Har panorering',
|
||||||
'CanReset' => 'Har återställning',
|
'CanReset' => 'Har återställning',
|
||||||
|
'CanReboot' => 'Can Reboot',
|
||||||
'CanSetPresets' => 'Har förinställningar',
|
'CanSetPresets' => 'Har förinställningar',
|
||||||
'CanSleep' => 'Kan vila',
|
'CanSleep' => 'Kan vila',
|
||||||
'CanTilt' => 'Kan tilta',
|
'CanTilt' => 'Kan tilta',
|
||||||
|
|
|
@ -115,7 +115,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.ptzControls .controlsPanel .pantiltPanel .pantiltControls .centerBtn {
|
.ptzControls .controlsPanel .pantiltPanel .pantiltControls .centerBtn {
|
||||||
background: url("../skins/classic/graphics/graphics/center.png") no-repeat 0 0;
|
background: url("../skins/classic/graphics/center.png") no-repeat 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ptzControls .controlsPanel .pantiltPanel .pantiltControls .rightBtn {
|
.ptzControls .controlsPanel .pantiltPanel .pantiltControls .rightBtn {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue