Merge branch 'master' of github.com:zoneminder/ZoneMinder
commit
780f4f9b9a
|
@ -68,6 +68,7 @@ CREATE TABLE `Controls` (
|
|||
`CanWake` tinyint(3) unsigned NOT NULL default '0',
|
||||
`CanSleep` 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',
|
||||
`CanAutoZoom` tinyint(3) unsigned NOT NULL default '0',
|
||||
`CanZoomAbs` tinyint(3) unsigned NOT NULL default '0',
|
||||
|
@ -498,6 +499,7 @@ CREATE TABLE `Monitors` (
|
|||
`ReturnDelay` smallint(5) unsigned,
|
||||
`DefaultRate` smallint(5) unsigned NOT NULL default '100',
|
||||
`DefaultScale` smallint(5) unsigned NOT NULL default '100',
|
||||
`DefaultCodec` enum('auto','MP4','MJPEG') NOT NULL default 'auto',
|
||||
`SignalCheckPoints` INT UNSIGNED NOT NULL default '0',
|
||||
`SignalCheckColour` varchar(32) NOT NULL default '#0000BE',
|
||||
`WebColour` varchar(32) NOT NULL default 'red',
|
||||
|
@ -750,46 +752,46 @@ insert into Filters values (NULL,'Update DiskSpace','{"terms":[{"attr":"DiskSpac
|
|||
--
|
||||
-- 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-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,'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,'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,'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,'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,'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,'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-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,'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 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 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 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,'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,'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,'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,'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,'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,'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 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,'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,'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,'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,'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,'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,'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,'HikVision','Local','',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,100,0,0,1,0,0,0,0,1,1,100,1,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Maginon Supra IPC','cURL','MaginonIPC',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,0,1,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Floureon 1080P','Ffmpeg','Floureon',0,0,0,1,0,0,0,1,1,18,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-423','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,64,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-411','Ffmpeg','Reolink',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Reolink RLC-420','Ffmpeg','Reolink',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
||||
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,'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,'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,'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,'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,'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,'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,'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,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,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,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,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,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,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,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,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,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,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,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,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,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,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, 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,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,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,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,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, 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, 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,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,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, 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,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,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,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,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,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,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,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,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,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,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,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,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,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
|
||||
|
|
|
@ -322,7 +322,7 @@ WHERE NOT EXISTS (
|
|||
--
|
||||
-- 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
|
||||
|
|
|
@ -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;
|
|
@ -23,7 +23,7 @@
|
|||
%global _hardened_build 1
|
||||
|
||||
Name: zoneminder
|
||||
Version: 1.33.0
|
||||
Version: 1.33.4
|
||||
Release: 1%{?dist}
|
||||
Summary: A camera monitoring and analysis tool
|
||||
Group: System Environment/Daemons
|
||||
|
@ -317,7 +317,7 @@ EOF
|
|||
|
||||
%files common
|
||||
%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
|
||||
# compared to the folder contents
|
||||
|
@ -352,6 +352,7 @@ EOF
|
|||
%{_bindir}/zmx10.pl
|
||||
%{_bindir}/zmonvif-probe.pl
|
||||
%{_bindir}/zmstats.pl
|
||||
%{_bindir}/zmrecover.pl
|
||||
|
||||
%{perl_vendorlib}/ZoneMinder*
|
||||
%{perl_vendorlib}/ONVIF*
|
||||
|
@ -409,6 +410,9 @@ EOF
|
|||
%dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload
|
||||
|
||||
%changelog
|
||||
* Sat Mar 30 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.4-1
|
||||
- Bump tp 1.33.4 Development
|
||||
|
||||
* Tue Dec 11 2018 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.0-1
|
||||
- Bump tp 1.33.0 Development
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ The list of available Mock config files are available here:
|
|||
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
|
||||
************
|
||||
|
@ -188,8 +188,8 @@ Now clone the ZoneMinder git repository from your home folder:
|
|||
::
|
||||
|
||||
cd
|
||||
git clone https://github.com/ZoneMinder/ZoneMinder
|
||||
cd ZoneMinder
|
||||
git clone https://github.com/ZoneMinder/zoneminder
|
||||
cd zoneminder
|
||||
|
||||
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
|
||||
|
||||
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:
|
||||
|
||||
::
|
||||
|
||||
cd ~/ZoneMinder
|
||||
cd ~/zoneminder
|
||||
git checkout -b mynewbranch
|
||||
|
||||
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 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:
|
||||
|
||||
::
|
||||
|
||||
cd ~/ZoneMinder
|
||||
git archive --prefix=ZoneMinder-1.31.1/ -o ~/rpmbuild/SOURCES/zoneminder-1.31.1.tar.gz HEAD
|
||||
cd ~/zoneminder
|
||||
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:
|
||||
|
||||
::
|
||||
|
||||
cd ~/ZoneMinder
|
||||
cd ~/zoneminder
|
||||
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:
|
||||
|
||||
::
|
||||
|
||||
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.
|
||||
|
|
|
@ -10,6 +10,7 @@ configure_file(zmdc.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" @ONLY)
|
|||
configure_file(zmfilter.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" @ONLY)
|
||||
configure_file(zmonvif-probe.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmonvif-probe.pl" @ONLY)
|
||||
configure_file(zmpkg.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" @ONLY)
|
||||
configure_file(zmrecover.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmrecover.pl" @ONLY)
|
||||
configure_file(zmtrack.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" @ONLY)
|
||||
configure_file(zmtrigger.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" @ONLY)
|
||||
configure_file(zmupdate.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" @ONLY)
|
||||
|
@ -35,7 +36,7 @@ FOREACH(PERLSCRIPT ${perlscripts})
|
|||
ENDFOREACH(PERLSCRIPT ${perlscripts})
|
||||
|
||||
# Install the perl scripts
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmonvif-probe.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtelemetry.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmstats.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmonvif-probe.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmrecover.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtelemetry.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmstats.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||
if(NOT ZM_NO_X10)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmx10.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||
endif(NOT ZM_NO_X10)
|
||||
|
|
|
@ -30,6 +30,7 @@ use warnings;
|
|||
|
||||
require Exporter;
|
||||
require ZoneMinder::Base;
|
||||
|
||||
use ZoneMinder::ConfigData qw(:all);
|
||||
|
||||
our @ISA = qw(Exporter ZoneMinder::Base);
|
||||
|
@ -57,20 +58,22 @@ our %EXPORT_TAGS = (
|
|||
push( @{$EXPORT_TAGS{config}}, @EXPORT_CONFIG );
|
||||
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 $VERSION = $ZoneMinder::Base::VERSION;
|
||||
|
||||
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_SUBDIR => "@ZM_CONFIG_SUBDIR@"; # Path to the ZoneMinder config subfolder
|
||||
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_SUBDIR => '@ZM_CONFIG_SUBDIR@'; # Path to the ZoneMinder config subfolder
|
||||
|
||||
use Carp;
|
||||
require ZoneMinder::Database;
|
||||
|
||||
# Load the config from the database into the symbol table
|
||||
BEGIN {
|
||||
require ZoneMinder::Database;
|
||||
|
||||
# Process name, value pairs from the main config file first
|
||||
my $config_file = ZM_CONFIG;
|
||||
|
@ -78,52 +81,25 @@ BEGIN {
|
|||
|
||||
# Search for user created config files. If one or more are found then
|
||||
# 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 ) {
|
||||
foreach my $filename ( glob ZM_CONFIG_SUBDIR."/*.conf" ) {
|
||||
foreach my $filename ( glob ZM_CONFIG_SUBDIR.'/*.conf' ) {
|
||||
process_configfile($filename);
|
||||
}
|
||||
} 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 $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 $dbh = ZoneMinder::Database::zmDbConnect();
|
||||
my $sql = 'SELECT Name,Value FROM Config';
|
||||
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 $sth = $dbh->prepare_cached($sql) or croak("Can't prepare '$sql': ".$dbh->errstr());
|
||||
my $res = $sth->execute() or croak("Can't execute: ".$sth->errstr());
|
||||
while( my $config = $sth->fetchrow_hashref() ) {
|
||||
$Config{$config->{Name}} = $config->{Value};
|
||||
}
|
||||
$sth->finish();
|
||||
#$dbh->disconnect();
|
||||
#
|
||||
|
||||
if ( ! $Config{ZM_SERVER_ID} ) {
|
||||
$Config{ZM_SERVER_ID} = undef;
|
||||
$sth = $dbh->prepare_cached( 'SELECT * FROM Servers WHERE Name=?' );
|
||||
|
@ -143,25 +119,30 @@ BEGIN {
|
|||
sub process_configfile {
|
||||
my $config_file = shift;
|
||||
|
||||
if ( -R $config_file ) {
|
||||
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/;
|
||||
$Config{$name} = $value;
|
||||
}
|
||||
close( $CONFIG );
|
||||
} else {
|
||||
print( STDERR "WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $config_file\n" );
|
||||
if ( ! -R $config_file ) {
|
||||
print(STDERR "WARNING: ZoneMinder configuration file found but is not readable. Check file permissions on $config_file\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
|
|
|
@ -3952,7 +3952,7 @@ our %options_hash = map { ( $_->{name}, $_ ) } @options;
|
|||
# This function should never need to be called explicitly, except if
|
||||
# this module is 'require'd rather than 'use'd. See zmconfgen.pl.
|
||||
sub initialiseConfig {
|
||||
return if ( $configInitialised );
|
||||
return if $configInitialised;
|
||||
|
||||
# Do some initial data munging to finish the data structures
|
||||
# Create option ids
|
||||
|
|
|
@ -39,6 +39,8 @@ sub AUTOLOAD
|
|||
my $class = ref($self) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$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}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
|
@ -46,9 +48,17 @@ sub AUTOLOAD
|
|||
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
|
||||
{
|
||||
#Debug("&open invoked by: " . (caller(1))[3]);
|
||||
my $self = shift;
|
||||
my $cgi = shift || '/cgi-bin/configManager.cgi?action=getConfig&name=Ptz';
|
||||
$self->loadMonitor();
|
||||
|
||||
# The Dahua camera firmware API supports the concept of having multiple
|
||||
|
@ -73,60 +83,55 @@ sub open
|
|||
}
|
||||
|
||||
use LWP::UserAgent;
|
||||
$self->{ua} = LWP::UserAgent->new;
|
||||
$self->{ua}->agent("ZoneMinder Control Agent/".$ZoneMinder::Base::ZM_VERSION);
|
||||
$self->{ua} = LWP::UserAgent->new(keep_alive => 1);
|
||||
$self->{ua}->agent("ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION);
|
||||
$self->{state} = 'closed';
|
||||
# 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);
|
||||
|
||||
# Detect REALM
|
||||
my $get_config_url = $PROTOCOL . $ADDRESS . "/cgi-bin/configManager.cgi?action=getConfig&name=Ptz";
|
||||
my $req = HTTP::Request->new(GET=>$get_config_url);
|
||||
my $url = $PROTOCOL . $ADDRESS . $cgi;
|
||||
my $req = HTTP::Request->new(GET=>$url);
|
||||
my $res = $self->{ua}->request($req);
|
||||
|
||||
if ($res->is_success) {
|
||||
$self->{state} = 'open';
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( $res->status_line() eq '401 Unauthorized' ) {
|
||||
my $headers = $res->headers();
|
||||
foreach my $k (keys %$headers) {
|
||||
Debug("Initial Header $k => $$headers{$k}");
|
||||
}
|
||||
|
||||
if ($$headers{'www-authenticate'}) {
|
||||
my ($auth, $tokens) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
|
||||
Debug("Tokens: " . $tokens);
|
||||
## FIXME: This is necessary because the Dahua spec does not match reality
|
||||
if ($tokens =~ /\w+="([^"]+)"/i) {
|
||||
if ($REALM ne $1) {
|
||||
$REALM = $1;
|
||||
Debug("Changing REALM to '" . $REALM . "'");
|
||||
$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);
|
||||
|
||||
if ($res->is_success()) {
|
||||
$self->{state} = 'open';
|
||||
return;
|
||||
Debug('Authentication succeeded...');
|
||||
return 1;
|
||||
}
|
||||
Debug('Authentication still failed after updating REALM' . $res->status_line);
|
||||
$headers = $res->headers();
|
||||
foreach my $k ( keys %$headers ) {
|
||||
Debug("Initial Header $k => $$headers{$k}");
|
||||
} # end foreach
|
||||
} else {
|
||||
Error('Authentication failed, not a REALM problem');
|
||||
} else { ## NOTE: Each of these else conditions is fatal as the command will not be
|
||||
## executed. No use going further.
|
||||
Fatal('Authentication failed: Check username and password.');
|
||||
}
|
||||
} else {
|
||||
Error('Failed to match realm in tokens');
|
||||
Fatal('Authentication failed: Incorrect realm.');
|
||||
} # end if
|
||||
} else {
|
||||
Error('No WWW-Authenticate Header');
|
||||
Fatal('Authentication failed: No www-authenticate header returned.');
|
||||
} # end if headers
|
||||
} # end if $res->status_line() eq '401 Unauthorized'
|
||||
}
|
||||
|
@ -146,45 +151,40 @@ sub printMsg
|
|||
Debug( $msg."[".$msg_len."]" );
|
||||
}
|
||||
|
||||
sub sendGetRequest {
|
||||
sub _sendGetRequest {
|
||||
my $self = 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 $req = HTTP::Request->new(GET=>$url);
|
||||
|
||||
my $req = HTTP::Request->new(GET => $url);
|
||||
my $res = $self->{ua}->request($req);
|
||||
|
||||
if ($res->is_success) {
|
||||
$result = !undef;
|
||||
return 1;
|
||||
} else {
|
||||
if ($res->status_line() eq '401 Unauthorized') {
|
||||
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($self->open($url_path)); # if we have to, open a new connection
|
||||
}
|
||||
return($result);
|
||||
}
|
||||
|
||||
sub sendPtzCommand
|
||||
sub _sendPtzCommand
|
||||
{
|
||||
my $self = shift;
|
||||
my $action = shift;
|
||||
my $command_code = shift;
|
||||
my $arg1 = shift;
|
||||
my $arg2 = shift;
|
||||
my $arg3 = shift;
|
||||
my $arg1 = shift || 0;
|
||||
my $arg2 = shift || 0;
|
||||
my $arg3 = shift || 0;
|
||||
my $arg4 = shift || 0;
|
||||
|
||||
my $channel = $self->{dahua_channel_number};
|
||||
|
||||
|
@ -194,10 +194,12 @@ sub sendPtzCommand
|
|||
$url_path .= "code=" . $command_code . "&";
|
||||
$url_path .= "arg1=" . $arg1 . "&";
|
||||
$url_path .= "arg2=" . $arg2 . "&";
|
||||
$url_path .= "arg3=" . $arg3;
|
||||
$self->sendGetRequest($url_path);
|
||||
$url_path .= "arg3=" . $arg3 . "&";
|
||||
$url_path .= "arg4=" . $arg4;
|
||||
return $self->_sendGetRequest($url_path);
|
||||
}
|
||||
sub sendMomentaryPtzCommand
|
||||
|
||||
sub _sendMomentaryPtzCommand
|
||||
{
|
||||
my $self = shift;
|
||||
my $command_code = shift;
|
||||
|
@ -206,92 +208,195 @@ sub sendMomentaryPtzCommand
|
|||
my $arg3 = 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;
|
||||
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
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Up Left");
|
||||
$self->sendMomentaryPtzCommand("LeftUp", 4, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("LeftUp", 4, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub moveRelUp
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Up");
|
||||
$self->sendMomentaryPtzCommand("Up", 0, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("Up", 0, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub moveRelUpRight
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Up Right");
|
||||
$self->sendMomentaryPtzCommand("RightUp", 0, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("RightUp", 0, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub moveRelLeft
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Left");
|
||||
$self->sendMomentaryPtzCommand("Left", 0, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("Left", 0, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub moveRelRight
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Right");
|
||||
$self->sendMomentaryPtzCommand("Right", 0, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("Right", 0, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub moveRelDownLeft
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Down Left");
|
||||
$self->sendMomentaryPtzCommand("LeftDown", 4, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("LeftDown", 4, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub moveRelDown
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Down");
|
||||
$self->sendMomentaryPtzCommand("Down", 0, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("Down", 0, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub moveRelDownRight
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Move Down Right");
|
||||
$self->sendMomentaryPtzCommand("RightDown", 4, 4, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("RightDown", 4, 4, 0, 500);
|
||||
}
|
||||
|
||||
sub zoomRelTele
|
||||
{
|
||||
my $self = shift;
|
||||
Debug("Zoom Relative Tele");
|
||||
$self->sendMomentaryPtzCommand("ZoomTele", 0, 0, 0, 500);
|
||||
$self->_sendMomentaryPtzCommand("ZoomTele", 0, 0, 0, 500);
|
||||
}
|
||||
|
||||
sub zoomRelWide
|
||||
{
|
||||
my $self = shift;
|
||||
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
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
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
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -308,8 +413,8 @@ sub presetSet
|
|||
my $control_preset_row = $sth->fetchrow_hashref();
|
||||
my $new_label_name = $control_preset_row->{'Label'};
|
||||
|
||||
$self->sendPtzCommand("start", "SetPreset", 0, $preset_id, 0);
|
||||
$self->sendPtzCommand("start", "SetPresetName", $preset_id, $new_label_name, 0);
|
||||
$self->_sendPtzCommand("start", "SetPreset", 0, $preset_id, 0);
|
||||
$self->_sendPtzCommand("start", "SetPresetName", $preset_id, $new_label_name, 0);
|
||||
}
|
||||
|
||||
sub presetGoto
|
||||
|
@ -318,12 +423,39 @@ sub presetGoto
|
|||
my $params = shift;
|
||||
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;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
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.
|
||||
|
||||
=head2 EXPORT
|
||||
|
||||
None by default.
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
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
|
||||
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
|
||||
|
|
|
@ -40,7 +40,7 @@ sub open
|
|||
|
||||
use LWP::UserAgent;
|
||||
$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';
|
||||
Debug( "sendCmd credentials control address:'".$ADDRESS
|
||||
."' realm:'" . $REALM
|
||||
|
|
|
@ -45,7 +45,7 @@ sub open {
|
|||
|
||||
use LWP::UserAgent;
|
||||
$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';
|
||||
# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string)
|
||||
Debug ( "sendCmd credentials control address:'".$ADDRESS
|
||||
|
@ -120,6 +120,7 @@ sub sendCmd {
|
|||
|
||||
Debug('sendCmd command: ' . $url);
|
||||
if ( $res->is_success ) {
|
||||
Debug($res->content);
|
||||
return !undef;
|
||||
}
|
||||
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."'");
|
||||
|
||||
if ( $res->is_success ) {
|
||||
Debug($res->content);
|
||||
return !undef;
|
||||
}
|
||||
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::Config qw(:all);
|
||||
|
||||
require ZoneMinder::Config;
|
||||
|
||||
our $dbh = undef;
|
||||
|
||||
|
@ -87,25 +88,25 @@ sub zmDbConnect {
|
|||
$socket = ';host='.$host.';port='.$portOrSocket;
|
||||
}
|
||||
} else {
|
||||
$socket = ';host='.$Config{ZM_DB_HOST};
|
||||
$socket = ';host='.$ZoneMinder::Config::Config{ZM_DB_HOST};
|
||||
}
|
||||
|
||||
my $sslOptions = '';
|
||||
if ( $Config{ZM_DB_SSL_CA_CERT} ) {
|
||||
$sslOptions = join(';','',
|
||||
if ( $ZoneMinder::Config::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}
|
||||
'mysql_ssl_ca_file='.$ZoneMinder::Config::Config{ZM_DB_SSL_CA_CERT},
|
||||
'mysql_ssl_client_key='.$ZoneMinder::Config::Config{ZM_DB_SSL_CLIENT_KEY},
|
||||
'mysql_ssl_client_cert='.$ZoneMinder::Config::Config{ZM_DB_SSL_CLIENT_CERT}
|
||||
);
|
||||
}
|
||||
|
||||
eval {
|
||||
$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} ) : '')
|
||||
, $Config{ZM_DB_USER}
|
||||
, $Config{ZM_DB_PASS}
|
||||
, $ZoneMinder::Config::Config{ZM_DB_USER}
|
||||
, $ZoneMinder::Config::Config{ZM_DB_PASS}
|
||||
);
|
||||
};
|
||||
if ( !$dbh or $@ ) {
|
||||
|
@ -124,7 +125,7 @@ sub zmDbConnect {
|
|||
} # end sub zmDbConnect
|
||||
|
||||
sub zmDbDisconnect {
|
||||
if ( defined( $dbh ) ) {
|
||||
if ( defined($dbh) ) {
|
||||
$dbh->disconnect() or Error('Error disconnecting db? ' . $dbh->errstr());
|
||||
$dbh = undef;
|
||||
}
|
||||
|
|
|
@ -31,12 +31,16 @@ use warnings;
|
|||
require ZoneMinder::Base;
|
||||
require ZoneMinder::Object;
|
||||
require ZoneMinder::Storage;
|
||||
require ZoneMinder::Frame;
|
||||
require Date::Manip;
|
||||
require File::Find;
|
||||
require File::Path;
|
||||
require File::Copy;
|
||||
require File::Basename;
|
||||
require Number::Bytes::Human;
|
||||
require Date::Parse;
|
||||
require POSIX;
|
||||
use Date::Format qw(time2str);
|
||||
|
||||
#our @ISA = qw(ZoneMinder::Object);
|
||||
use parent qw(ZoneMinder::Object);
|
||||
|
@ -50,9 +54,8 @@ use parent qw(ZoneMinder::Object);
|
|||
use ZoneMinder::Config qw(:all);
|
||||
use ZoneMinder::Logger qw(:all);
|
||||
use ZoneMinder::Database qw(:all);
|
||||
require Date::Parse;
|
||||
|
||||
use vars qw/ $table $primary_key %fields $serial @identified_by/;
|
||||
use vars qw/ $table $primary_key %fields $serial @identified_by %defaults/;
|
||||
$table = 'Events';
|
||||
@identified_by = ('Id');
|
||||
$serial = $primary_key = 'Id';
|
||||
|
@ -84,10 +87,21 @@ $serial = $primary_key = 'Id';
|
|||
StateId
|
||||
Orientation
|
||||
DiskSpace
|
||||
SaveJPEGs
|
||||
Scheme
|
||||
);
|
||||
%defaults = (
|
||||
Cause => q`'Unknown'`,
|
||||
DefaultVideo => q`''`,
|
||||
TotScore => '0',
|
||||
Archived => '0',
|
||||
Videoed => '0',
|
||||
Uploaded => '0',
|
||||
Emailed => '0',
|
||||
Messaged => '0',
|
||||
Executed => '0',
|
||||
);
|
||||
|
||||
use POSIX;
|
||||
|
||||
sub Time {
|
||||
if ( @_ > 1 ) {
|
||||
|
@ -101,56 +115,10 @@ sub Time {
|
|||
return $_[0]{Time};
|
||||
}
|
||||
|
||||
sub Name {
|
||||
if ( @_ > 1 ) {
|
||||
$_[0]{Name} = $_[1];
|
||||
}
|
||||
return $_[0]{Name};
|
||||
} # end sub Name
|
||||
|
||||
sub find {
|
||||
shift if $_[0] eq 'ZoneMinder::Event';
|
||||
my %sql_filters = @_;
|
||||
|
||||
my $sql = 'SELECT * FROM Events';
|
||||
my @sql_filters;
|
||||
my @sql_values;
|
||||
|
||||
if ( exists $sql_filters{Name} ) {
|
||||
push @sql_filters , ' Name = ? ';
|
||||
push @sql_values, $sql_filters{Name};
|
||||
}
|
||||
if ( exists $sql_filters{Id} ) {
|
||||
push @sql_filters , ' Id = ? ';
|
||||
push @sql_values, $sql_filters{Id};
|
||||
}
|
||||
|
||||
$sql .= ' WHERE ' . join(' AND ', @sql_filters ) if @sql_filters;
|
||||
$sql .= ' LIMIT ' . $sql_filters{limit} if $sql_filters{limit};
|
||||
|
||||
my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
|
||||
or Fatal( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
|
||||
my $res = $sth->execute( @sql_values )
|
||||
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
|
||||
|
||||
my @results;
|
||||
|
||||
while( my $db_filter = $sth->fetchrow_hashref() ) {
|
||||
my $filter = new ZoneMinder::Event( $$db_filter{Id}, $db_filter );
|
||||
push @results, $filter;
|
||||
} # end while
|
||||
$sth->finish();
|
||||
return @results;
|
||||
}
|
||||
|
||||
sub find_one {
|
||||
my @results = find(@_);
|
||||
return $results[0] if @results;
|
||||
}
|
||||
|
||||
sub getPath {
|
||||
return Path( @_ );
|
||||
}
|
||||
|
||||
sub Path {
|
||||
my $event = shift;
|
||||
|
||||
|
@ -170,6 +138,7 @@ sub Path {
|
|||
|
||||
sub Scheme {
|
||||
my $self = shift;
|
||||
|
||||
$$self{Scheme} = shift if @_;
|
||||
|
||||
if ( ! $$self{Scheme} ) {
|
||||
|
@ -186,16 +155,15 @@ sub Scheme {
|
|||
|
||||
sub RelativePath {
|
||||
my $event = shift;
|
||||
if ( @_ ) {
|
||||
$$event{RelativePath} = $_[0];
|
||||
}
|
||||
|
||||
$$event{RelativePath} = shift if @_;
|
||||
|
||||
if ( ! $$event{RelativePath} ) {
|
||||
if ( $$event{Scheme} eq 'Deep' ) {
|
||||
if ( $event->Time() ) {
|
||||
$$event{RelativePath} = join('/',
|
||||
$event->{MonitorId},
|
||||
strftime( '%y/%m/%d/%H/%M/%S',
|
||||
POSIX::strftime( '%y/%m/%d/%H/%M/%S',
|
||||
localtime($event->Time())
|
||||
),
|
||||
);
|
||||
|
@ -207,7 +175,7 @@ sub RelativePath {
|
|||
if ( $event->Time() ) {
|
||||
$$event{RelativePath} = join('/',
|
||||
$event->{MonitorId},
|
||||
strftime( '%Y-%m-%d', localtime($event->Time())),
|
||||
POSIX::strftime('%Y-%m-%d', localtime($event->Time())),
|
||||
$event->{Id},
|
||||
);
|
||||
} else {
|
||||
|
@ -227,16 +195,15 @@ sub RelativePath {
|
|||
|
||||
sub LinkPath {
|
||||
my $event = shift;
|
||||
if ( @_ ) {
|
||||
$$event{LinkPath} = $_[0];
|
||||
}
|
||||
|
||||
$$event{LinkPath} = shift if @_;
|
||||
|
||||
if ( ! $$event{LinkPath} ) {
|
||||
if ( $$event{Scheme} eq 'Deep' ) {
|
||||
if ( $event->Time() ) {
|
||||
$$event{LinkPath} = join('/',
|
||||
$event->{MonitorId},
|
||||
strftime( '%y/%m/%d',
|
||||
POSIX::strftime( '%y/%m/%d',
|
||||
localtime($event->Time())
|
||||
),
|
||||
'.'.$$event{Id}
|
||||
|
@ -483,9 +450,23 @@ sub delete_files {
|
|||
}
|
||||
} # end sub delete_files
|
||||
|
||||
sub StorageId {
|
||||
my $event = shift;
|
||||
if ( @_ ) {
|
||||
$$event{StorageId} = shift;
|
||||
delete $$event{Storage};
|
||||
delete $$event{Path};
|
||||
}
|
||||
return $$event{StorageId};
|
||||
}
|
||||
|
||||
sub Storage {
|
||||
if ( @_ > 1 ) {
|
||||
$_[0]{Storage} = $_[1];
|
||||
if ( $_[0]{Storage} ) {
|
||||
$_[0]{StorageId} = $_[0]{Storage}->Id();
|
||||
delete $_[0]{Path};
|
||||
}
|
||||
}
|
||||
if ( ! $_[0]{Storage} ) {
|
||||
$_[0]{Storage} = new ZoneMinder::Storage($_[0]{StorageId});
|
||||
|
@ -693,6 +674,92 @@ Debug("Done deleting files, returning");
|
|||
return $error;
|
||||
} # end sub MoveTo
|
||||
|
||||
# Assumes $path is absolute
|
||||
#
|
||||
sub recover_timestamps {
|
||||
my ( $Event, $path ) = @_;
|
||||
$path = $Event->Path() if ! $path;
|
||||
|
||||
if ( !opendir(DIR, $path) ) {
|
||||
Error("Can't open directory '$path': $!");
|
||||
return;
|
||||
}
|
||||
my @contents = readdir(DIR);
|
||||
Debug('Have ' . @contents . " files in $path");
|
||||
closedir(DIR);
|
||||
|
||||
my @mp4_files = grep( /^\d+\-video\.mp4$/, @contents);
|
||||
if ( @mp4_files ) {
|
||||
$$Event{DefaultVideo} = $mp4_files[0];
|
||||
}
|
||||
|
||||
my @analyse_jpgs = grep( /^\d+\-analyse\.jpg$/, @contents);
|
||||
if ( @analyse_jpgs ) {
|
||||
$$Event{Save_JPEGs} |= 2;
|
||||
}
|
||||
|
||||
my @capture_jpgs = grep( /^\d+\-capture\.jpg$/, @contents);
|
||||
if ( @capture_jpgs ) {
|
||||
$$Event{Frames} = scalar @capture_jpgs;
|
||||
$$Event{Save_JPEGs} |= 1;
|
||||
# can get start and end times from stat'ing first and last jpg
|
||||
@capture_jpgs = sort { $a cmp $b } @capture_jpgs;
|
||||
my $first_file = "$path/$capture_jpgs[0]";
|
||||
( $first_file ) = $first_file =~ /^(.*)$/;
|
||||
my $first_timestamp = (stat($first_file))[9];
|
||||
|
||||
my $last_file = "$path/$capture_jpgs[@capture_jpgs-1]";
|
||||
( $last_file ) = $last_file =~ /^(.*)$/;
|
||||
my $last_timestamp = (stat($last_file))[9];
|
||||
|
||||
my $duration = $last_timestamp - $first_timestamp;
|
||||
$Event->Length($duration);
|
||||
$Event->StartTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $first_timestamp) );
|
||||
$Event->EndTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $last_timestamp) );
|
||||
Debug("From capture Jpegs have duration $duration = $last_timestamp - $first_timestamp : $$Event{StartTime} to $$Event{EndTime}");
|
||||
$ZoneMinder::Database::dbh->begin_work();
|
||||
foreach my $jpg ( @capture_jpgs ) {
|
||||
my ( $id ) = $jpg =~ /^(\d+)\-capture\.jpg$/;
|
||||
|
||||
if ( ! ZoneMinder::Frame->find_one( EventId=>$$Event{Id}, FrameId=>$id ) ) {
|
||||
my $file = "$path/$jpg";
|
||||
( $file ) = $file =~ /^(.*)$/;
|
||||
my $timestamp = (stat($file))[9];
|
||||
my $Frame = new ZoneMinder::Frame();
|
||||
$Frame->save({
|
||||
EventId=>$$Event{Id}, FrameId=>$id,
|
||||
TimeStamp=>Date::Format::time2str('%Y-%m-%d %H:%M:%S',$timestamp),
|
||||
Delta => $timestamp - $first_timestamp,
|
||||
Type=>'Normal',
|
||||
Score=>0,
|
||||
});
|
||||
}
|
||||
}
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
} elsif ( @mp4_files ) {
|
||||
my $file = "$path/$mp4_files[0]";
|
||||
( $file ) = $file =~ /^(.*)$/;
|
||||
|
||||
my $first_timestamp = (stat($file))[9];
|
||||
my $output = `ffprobe $file 2>&1`;
|
||||
my ($duration) = $output =~ /Duration: [:\.0-9]+/gm;
|
||||
Debug("From mp4 have duration $duration, start: $first_timestamp");
|
||||
|
||||
my ( $h, $m, $s, $u );
|
||||
if ( $duration =~ m/(\d+):(\d+):(\d+)\.(\d+)/ ) {
|
||||
( $h, $m, $s, $u ) = ($1, $2, $3, $4 );
|
||||
Debug("( $h, $m, $s, $u ) from /^(\\d{2}):(\\d{2}):(\\d{2})\.(\\d+)/");
|
||||
}
|
||||
my $seconds = ($h*60*60)+($m*60)+$s;
|
||||
$Event->Length($seconds.'.'.$u);
|
||||
$Event->StartTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $first_timestamp) );
|
||||
$Event->EndTime( Date::Format::time2str('%Y-%m-%d %H:%M:%S', $first_timestamp+$seconds) );
|
||||
}
|
||||
if ( @mp4_files ) {
|
||||
$Event->DefaultVideo($mp4_files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ use warnings;
|
|||
|
||||
require ZoneMinder::Base;
|
||||
require Date::Manip;
|
||||
require POSIX;
|
||||
|
||||
use parent qw(ZoneMinder::Object);
|
||||
|
||||
|
@ -48,8 +49,6 @@ use ZoneMinder::Database qw(:all);
|
|||
require ZoneMinder::Storage;
|
||||
require ZoneMinder::Server;
|
||||
|
||||
use POSIX;
|
||||
|
||||
sub Name {
|
||||
if ( @_ > 1 ) {
|
||||
$_[0]{Name} = $_[1];
|
||||
|
@ -178,7 +177,7 @@ sub Sql {
|
|||
$self->{Sql} .= 'to_days( E.StartTime )';
|
||||
} elsif ( $term->{attr} eq 'Time' or $term->{attr} eq '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 )';
|
||||
|
||||
# EndTIme options
|
||||
|
@ -436,7 +435,7 @@ sub DateTimeToSQL {
|
|||
Error( "Unable to parse date string '$dt_str'\n" );
|
||||
return( undef );
|
||||
}
|
||||
return( strftime( "%Y-%m-%d %H:%M:%S", localtime( $dt_val ) ) );
|
||||
return( POSIX::strftime( "%Y-%m-%d %H:%M:%S", localtime( $dt_val ) ) );
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
# ==========================================================================
|
||||
#
|
||||
# ZoneMinder Monitor Module, $Date$, $Revision$
|
||||
# Copyright (C) 2001-2008 Philip Coombes
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ==========================================================================
|
||||
#
|
||||
# This module contains the common definitions and functions used by the rest
|
||||
# of the ZoneMinder scripts
|
||||
#
|
||||
package ZoneMinder::Frame;
|
||||
|
||||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
require ZoneMinder::Base;
|
||||
require ZoneMinder::Object;
|
||||
|
||||
use parent qw(ZoneMinder::Object);
|
||||
|
||||
use vars qw/ $table $primary_key %fields /;
|
||||
$table = 'Frames';
|
||||
$primary_key = 'Id';
|
||||
|
||||
%fields = (
|
||||
Id => 'Id',
|
||||
EventId => 'EventId',
|
||||
FrameId => 'FrameId',
|
||||
Type => 'Type',
|
||||
TimeStamp => 'TimeStamp',
|
||||
Delta => 'Delta',
|
||||
Score => 'Score',
|
||||
);
|
||||
|
||||
sub Event {
|
||||
return new ZoneMinder::Event( $_[0]{EventId} );
|
||||
} # end sub Event
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
ZoneMinder::Frame - Perl Class for Frames
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use ZoneMinder::Frame;
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Isaac Connor, E<lt>isaac@zoneminder.comE<gt>
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (C) 2001-2017 ZoneMinder LLC
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the same terms as Perl itself, either Perl version 5.8.3 or,
|
||||
at your option, any later version of Perl 5 you may have available.
|
||||
|
||||
|
||||
=cut
|
|
@ -1,6 +1,6 @@
|
|||
# ==========================================================================
|
||||
############################################################################
|
||||
#
|
||||
# ZoneMinder Logger Module, $Date$, $Revision$
|
||||
# ZoneMinder Logger Module
|
||||
# Copyright (C) 2001-2008 Philip Coombes
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
|
@ -17,7 +17,7 @@
|
|||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# ==========================================================================
|
||||
############################################################################
|
||||
#
|
||||
# This module contains the debug definitions and functions used by the rest
|
||||
# of the ZoneMinder scripts
|
||||
|
@ -81,17 +81,17 @@ our @EXPORT = qw();
|
|||
|
||||
our $VERSION = $ZoneMinder::Base::VERSION;
|
||||
|
||||
# ==========================================================================
|
||||
############################################################################
|
||||
#
|
||||
# Logger Facilities
|
||||
#
|
||||
# ==========================================================================
|
||||
############################################################################
|
||||
|
||||
use ZoneMinder::Config qw(:all);
|
||||
require ZoneMinder::Config;
|
||||
|
||||
use DBI;
|
||||
use Carp;
|
||||
use POSIX;
|
||||
require POSIX;
|
||||
use IO::Handle;
|
||||
use Data::Dumper;
|
||||
use Time::HiRes qw/gettimeofday/;
|
||||
|
@ -156,7 +156,7 @@ sub new {
|
|||
$this->{autoFlush} = 1;
|
||||
|
||||
( $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->{logFile} =~ /^([\w\.\/]+)$/;
|
||||
|
||||
|
@ -169,7 +169,7 @@ sub new {
|
|||
sub BEGIN {
|
||||
# Fake the config variables that are used in case they are not defined yet
|
||||
# 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 'refs';
|
||||
my %dbgConfig = (
|
||||
|
@ -221,17 +221,17 @@ sub initialise( @ ) {
|
|||
if ( defined($options{databaseLevel}) ) {
|
||||
$tempDatabaseLevel = $options{databaseLevel};
|
||||
} else {
|
||||
$tempDatabaseLevel = $Config{ZM_LOG_LEVEL_DATABASE};
|
||||
$tempDatabaseLevel = $ZoneMinder::Config::Config{ZM_LOG_LEVEL_DATABASE};
|
||||
}
|
||||
if ( defined($options{fileLevel}) ) {
|
||||
$tempFileLevel = $options{fileLevel};
|
||||
} else {
|
||||
$tempFileLevel = $Config{ZM_LOG_LEVEL_FILE};
|
||||
$tempFileLevel = $ZoneMinder::Config::Config{ZM_LOG_LEVEL_FILE};
|
||||
}
|
||||
if ( defined($options{syslogLevel}) ) {
|
||||
$tempSyslogLevel = $options{syslogLevel};
|
||||
} else {
|
||||
$tempSyslogLevel = $Config{ZM_LOG_LEVEL_SYSLOG};
|
||||
$tempSyslogLevel = $ZoneMinder::Config::Config{ZM_LOG_LEVEL_SYSLOG};
|
||||
}
|
||||
|
||||
if ( defined($ENV{LOG_PRINT}) ) {
|
||||
|
@ -245,19 +245,19 @@ sub initialise( @ ) {
|
|||
$tempFileLevel = $level if defined($level = $this->getTargettedEnv('LOG_LEVEL_FILE'));
|
||||
$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
|
||||
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}
|
||||
|| $target eq '_'.$this->{id}
|
||||
|| $target eq $this->{idRoot}
|
||||
|| $target eq '_'.$this->{idRoot}
|
||||
|| $target eq ''
|
||||
) {
|
||||
if ( $Config{ZM_LOG_DEBUG_LEVEL} > NOLOG ) {
|
||||
$tempLevel = $this->limit( $Config{ZM_LOG_DEBUG_LEVEL} );
|
||||
if ( $Config{ZM_LOG_DEBUG_FILE} ne '' ) {
|
||||
$tempLogFile = $Config{ZM_LOG_DEBUG_FILE};
|
||||
if ( $ZoneMinder::Config::Config{ZM_LOG_DEBUG_LEVEL} > NOLOG ) {
|
||||
$tempLevel = $this->limit( $ZoneMinder::Config::Config{ZM_LOG_DEBUG_LEVEL} );
|
||||
if ( $ZoneMinder::Config::Config{ZM_LOG_DEBUG_FILE} ne '' ) {
|
||||
$tempLogFile = $ZoneMinder::Config::Config{ZM_LOG_DEBUG_FILE};
|
||||
$tempFileLevel = $tempLevel;
|
||||
}
|
||||
}
|
||||
|
@ -501,8 +501,8 @@ sub openFile {
|
|||
if ( open($LOGFILE, '>>', $this->{logFile}) ) {
|
||||
$LOGFILE->autoflush() if $this->{autoFlush};
|
||||
|
||||
my $webUid = (getpwnam($Config{ZM_WEB_USER}))[2];
|
||||
my $webGid = (getgrnam($Config{ZM_WEB_GROUP}))[2];
|
||||
my $webUid = (getpwnam($ZoneMinder::Config::Config{ZM_WEB_USER}))[2];
|
||||
my $webGid = (getgrnam($ZoneMinder::Config::Config{ZM_WEB_GROUP}))[2];
|
||||
if ( $> == 0 ) {
|
||||
chown( $webUid, $webGid, $this->{logFile} )
|
||||
or Fatal("Can't change permissions on log file $$this{logFile}: $!");
|
||||
|
@ -535,7 +535,7 @@ sub logPrint {
|
|||
if ( $level <= $this->{fileLevel} or $level <= $this->{termLevel} ) {
|
||||
my $message = sprintf(
|
||||
'%s.%06d %s[%d].%s [%s:%d] [%s]'
|
||||
, strftime('%x %H:%M:%S', localtime($seconds))
|
||||
, POSIX::strftime('%x %H:%M:%S', localtime($seconds))
|
||||
, $microseconds
|
||||
, $this->{id}
|
||||
, $$
|
||||
|
@ -577,7 +577,7 @@ sub logPrint {
|
|||
my $res = $this->{sth}->execute(
|
||||
$seconds+($microseconds/1000000.0),
|
||||
$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,
|
||||
$codes{$level},
|
||||
|
|
|
@ -36,17 +36,6 @@ require ZoneMinder::Server;
|
|||
#our @ISA = qw(Exporter ZoneMinder::Base);
|
||||
use parent qw(ZoneMinder::Object);
|
||||
|
||||
# ==========================================================================
|
||||
#
|
||||
# General Utility Functions
|
||||
#
|
||||
# ==========================================================================
|
||||
|
||||
use ZoneMinder::Config qw(:all);
|
||||
use ZoneMinder::Logger qw(:all);
|
||||
use ZoneMinder::Database qw(:all);
|
||||
|
||||
use POSIX;
|
||||
use vars qw/ $table $primary_key /;
|
||||
$table = 'Monitors';
|
||||
$primary_key = 'Id';
|
||||
|
|
|
@ -27,6 +27,8 @@ package ZoneMinder::Object;
|
|||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
use Time::HiRes qw{ gettimeofday tv_interval };
|
||||
use Carp qw( cluck );
|
||||
|
||||
require ZoneMinder::Base;
|
||||
|
||||
|
@ -167,17 +169,6 @@ sub lock_and_load {
|
|||
} # end sub lock_and_load
|
||||
|
||||
|
||||
sub AUTOLOAD {
|
||||
my ( $self, $newvalue ) = @_;
|
||||
my $type = ref($_[0]);
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( @_ > 1 ) {
|
||||
return $_[0]{$name} = $_[1];
|
||||
}
|
||||
return $_[0]{$name};
|
||||
}
|
||||
|
||||
sub save {
|
||||
my ( $self, $data, $force_insert ) = @_;
|
||||
|
||||
|
@ -187,7 +178,12 @@ sub save {
|
|||
$log->error("No type in Object::save. self:$self from $caller:$line");
|
||||
}
|
||||
my $local_dbh = eval '$'.$type.'::dbh';
|
||||
$local_dbh = $ZoneMinder::Database::dbh if ! $local_dbh;
|
||||
if ( ! $local_dbh ) {
|
||||
$local_dbh = $ZoneMinder::Database::dbh;
|
||||
if ( $debug or DEBUG_ALL ) {
|
||||
$log->debug("Using global dbh");
|
||||
}
|
||||
}
|
||||
$self->set( $data ? $data : {} );
|
||||
if ( $debug or DEBUG_ALL ) {
|
||||
if ( $data ) {
|
||||
|
@ -196,7 +192,6 @@ sub save {
|
|||
}
|
||||
}
|
||||
}
|
||||
#$debug = 0;
|
||||
|
||||
my $table = eval '$'.$type.'::table';
|
||||
my $fields = eval '\%'.$type.'::fields';
|
||||
|
@ -297,6 +292,7 @@ $log->debug("No serial") if $debug;
|
|||
|
||||
if ( $need_serial ) {
|
||||
if ( $serial ) {
|
||||
$log->debug("Getting auto_increments");
|
||||
my $s = qq{SELECT `auto_increment` FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '$table'};
|
||||
@$self{@identified_by} = @sql{@$fields{@identified_by}} = $local_dbh->selectrow_array( $s );
|
||||
#@$self{@identified_by} = @sql{@$fields{@identified_by}} = $local_dbh->selectrow_array( q{SELECT nextval('} . $serial . q{')} );
|
||||
|
@ -345,7 +341,7 @@ $log->debug("No serial") if $debug;
|
|||
} # end if
|
||||
} # end if
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
||||
$self->load();
|
||||
#$self->load();
|
||||
#if ( $$fields{id} ) {
|
||||
#if ( ! $ZoneMinder::Object::cache{$type}{$$self{id}} ) {
|
||||
#$ZoneMinder::Object::cache{$type}{$$self{id}} = $self;
|
||||
|
@ -368,6 +364,7 @@ sub set {
|
|||
$log->warn("ZoneMinder::Object::set called on an object ($type) with no fields".$@);
|
||||
} # end if
|
||||
my %defaults = eval('%'.$type.'::defaults');
|
||||
|
||||
if ( ref $params ne 'HASH' ) {
|
||||
my ( $caller, undef, $line ) = caller;
|
||||
$log->error("$type -> set called with non-hash params from $caller $line");
|
||||
|
@ -456,7 +453,420 @@ sub transform {
|
|||
sub to_string {
|
||||
my $type = ref($_[0]);
|
||||
my $fields = eval '\%'.$type.'::fields';
|
||||
return $type . ': '. join(' ' , map { $_[0]{$_} ? "$_ => $_[0]{$_}" : () } sort { $a cmp $b } keys %$fields );
|
||||
if ( $fields and %{$fields} ) {
|
||||
return $type . ': '. join(' ', map { $_[0]{$_} ? "$_ => $_[0]{$_}" : () } sort { $a cmp $b } keys %$fields );
|
||||
}
|
||||
return $type . ': '. join(' ', map { $_ .' => '.(defined $_[0]{$_} ? $_[0]{$_} : 'undef') } sort { $a cmp $b } keys %{$_[0]} );
|
||||
}
|
||||
|
||||
# We make this a separate function so that we can use it to generate the sql statements for each value in an OR
|
||||
sub find_operators {
|
||||
my ( $field, $type, $operator, $value ) = @_;
|
||||
$log->debug("find_operators: field($field) type($type) op($operator) value($value)") if DEBUG_ALL;
|
||||
|
||||
my $add_placeholder = ( ! ( $field =~ /\?/ ) ) ? 1 : 0;
|
||||
|
||||
if ( sets::isin( $operator, [ '=', '!=', '<', '>', '<=', '>=', '<<=' ] ) ) {
|
||||
return ( $field.$type.' ' . $operator . ( $add_placeholder ? ' ?' : '' ), $value );
|
||||
} elsif ( $operator eq 'not' ) {
|
||||
return ( '( NOT ' . $field.$type.')', $value );
|
||||
} elsif ( sets::isin( $operator, [ '&&', '<@', '@>' ] ) ) {
|
||||
if ( ref $value eq 'ARRAY' ) {
|
||||
if ( $field =~ /^\(/ ) {
|
||||
return ( 'ARRAY('.$field.$type.') ' . $operator . ' ?', $value );
|
||||
} else {
|
||||
return ( $field.$type.' ' . $operator . ' ?', $value );
|
||||
} # emd of
|
||||
} else {
|
||||
return ( $field.$type.' ' . $operator . ' ?', [ $value ] );
|
||||
} # end if
|
||||
} elsif ( $operator eq 'exists' ) {
|
||||
return ( $value ? '' : 'NOT ' ) . 'EXISTS ' . $field.$type;
|
||||
} elsif ( sets::isin( $operator, [ 'in', 'not in' ] ) ) {
|
||||
if ( ref $value eq 'ARRAY' ) {
|
||||
return ( $field.$type.' ' . $operator . ' ('. join(',', map { '?' } @{$value} ) . ')', @{$value} );
|
||||
} else {
|
||||
return ( $field.$type.' ' . $operator . ' (?)', $value );
|
||||
} # end if
|
||||
} elsif ( $operator eq 'contains' ) {
|
||||
return ( '? IN '.$field.$type, $value );
|
||||
} elsif ( $operator eq 'does not contain' ) {
|
||||
return ( '? NOT IN '.$field.$type, $value );
|
||||
} elsif ( sets::isin( $operator, [ 'like','ilike' ] ) ) {
|
||||
return $field.'::text ' . $operator . ' ?', $value;
|
||||
} elsif ( $operator eq 'null_or_<=' ) {
|
||||
return '('.$field.$type.' IS NULL OR '.$field.$type.' <= ?)', $value;
|
||||
} elsif ( $operator eq 'is null or <=' ) {
|
||||
return '('.$field.$type.' IS NULL OR '.$field.$type.' <= ?)', $value;
|
||||
} elsif ( $operator eq 'null_or_>=' ) {
|
||||
return '('.$field.$type.' IS NULL OR '.$field.$type.' >= ?)', $value;
|
||||
} elsif ( $operator eq 'is null or >=' ) {
|
||||
return '('.$field.$type.' IS NULL OR '.$field.$type.' >= ?)', $value;
|
||||
} elsif ( $operator eq 'null_or_>' or $operator eq 'is null or >' ) {
|
||||
return '('.$field.$type.' IS NULL OR '.$field.$type.' > ?)', $value;
|
||||
} elsif ( $operator eq 'null_or_<' or $operator eq 'is null or <' ) {
|
||||
return '('.$field.$type.' IS NULL OR '.$field.$type.' < ?)', $value;
|
||||
} elsif ( $operator eq 'null_or_=' or $operator eq 'is null or =' ) {
|
||||
return '('.$field.$type.' IS NULL OR '.$field.$type.' = ?)', $value;
|
||||
} elsif ( $operator eq 'null or in' or $operator eq 'is null or in' ) {
|
||||
return '('.$field.$type.' IS NULL OR '.$field.$type.' IN ('.join(',', map { '?' } @{$value} ) . '))', @{$value};
|
||||
} elsif ( $operator eq 'null or not in' ) {
|
||||
return '('.$field.$type.' IS NULL OR '.$field.$type.' NOT IN ('.join(',', map { '?' } @{$value} ) . '))', @{$value};
|
||||
} elsif ( $operator eq 'exists' ) {
|
||||
return ( $value ? ' EXISTS ' : 'NOT EXISTS ' ).$field;
|
||||
} elsif ( $operator eq 'lc' ) {
|
||||
return 'lower('.$field.$type.') = ?', $value;
|
||||
} elsif ( $operator eq 'uc' ) {
|
||||
return 'upper('.$field.$type.') = ?', $value;
|
||||
} elsif ( $operator eq 'trunc' ) {
|
||||
return 'trunc('.$field.$type.') = ?', $value;
|
||||
} elsif ( $operator eq 'any' ) {
|
||||
if ( ref $value eq 'ARRAY' ) {
|
||||
return '(' . join(',', map { '?' } @{$value} ).") = ANY($field)", @{$value};
|
||||
} else {
|
||||
return "? = ANY($field)", $value;
|
||||
} # end if
|
||||
} elsif ( $operator eq 'not any' ) {
|
||||
if ( ref $value eq 'ARRAY' ) {
|
||||
return '(' . join(',', map { '?' } @{$value} ).") != ANY($field)", @{$value};
|
||||
} else {
|
||||
return "? != ANY($field)", $value;
|
||||
} # end if
|
||||
} elsif ( $operator eq 'is null' ) {
|
||||
if ( $value ) {
|
||||
return $field.$type. ' is null';
|
||||
} else {
|
||||
return $field.$type. ' is not null';
|
||||
} # end if
|
||||
} elsif ( $operator eq 'is not null' ) {
|
||||
if ( $value ) {
|
||||
return $field.$type. ' is not null';
|
||||
} else {
|
||||
return $field.$type. ' is null';
|
||||
} # end if
|
||||
} else {
|
||||
$log->warn("find_operators: op not found field($field) type($type) op($operator) value($value)");
|
||||
} # end if
|
||||
return;
|
||||
} # end sub find_operators
|
||||
|
||||
sub get_fields_values {
|
||||
my ( $object_type, $search, $param_keys ) = @_;
|
||||
|
||||
my @used_fields;
|
||||
my @where;
|
||||
my @values;
|
||||
no strict 'refs';
|
||||
|
||||
foreach my $k ( @$param_keys ) {
|
||||
if ( $k eq 'or' ) {
|
||||
my $or_ref = ref $$search{or};
|
||||
|
||||
if ( $or_ref eq 'HASH' ) {
|
||||
my @keys = keys %{$$search{or}};
|
||||
if ( @keys ) {
|
||||
my ( $where, $values, $used_fields ) = get_fields_values( $object_type, $$search{or}, \@keys );
|
||||
|
||||
push @where, '('.join(' OR ', @{$where} ).')';
|
||||
push @values, @{$values};
|
||||
} else {
|
||||
$log->error("No keys in or");
|
||||
}
|
||||
|
||||
} elsif ( $or_ref eq 'ARRAY' ) {
|
||||
my %s = @{$$search{or}};
|
||||
my ( $where, $values, $used_fields ) = get_fields_values( $object_type, \%s, [ keys %s ] );
|
||||
push @where, '('.join(' OR ', @{$where} ).')';
|
||||
push @values, @{$values};
|
||||
|
||||
} else {
|
||||
$log->error("Deprecated use of or $or_ref for $$search{or}");
|
||||
} # end if
|
||||
push @used_fields, $k;
|
||||
next;
|
||||
} elsif ( $k eq 'and' ) {
|
||||
my $and_ref = ref $$search{and};
|
||||
if ( $and_ref eq 'HASH' ) {
|
||||
my @keys = keys %{$$search{and}};
|
||||
if ( @keys ) {
|
||||
my ( $where, $values, $used_fields ) = get_fields_values( $object_type, $$search{and}, \@keys );
|
||||
|
||||
push @where, '('.join(' AND ', @{$where} ).')';
|
||||
push @values, @{$values};
|
||||
} else {
|
||||
$log->error("No keys in and");
|
||||
}
|
||||
} elsif ( $and_ref eq 'ARRAY' and @{$$search{and}} ) {
|
||||
my @sub_where;
|
||||
|
||||
for( my $p_index = 0; $p_index < @{$$search{and}}; $p_index += 2 ) {
|
||||
my %p = ( $$search{and}[$p_index], $$search{and}[$p_index+1] );
|
||||
|
||||
my ( $where, $values, $used_fields ) = get_fields_values( $object_type, \%p, [ keys %p ] );
|
||||
push @sub_where, @{$where};
|
||||
push @values, @{$values};
|
||||
}
|
||||
push @where, '('.join(' AND ', @sub_where ).')';
|
||||
} else {
|
||||
$log->error("incorrect ref of and $and_ref");
|
||||
}
|
||||
push @used_fields, $k;
|
||||
next;
|
||||
}
|
||||
my ( $field, $type, $function ) = $k =~ /^([_\+\w\-]+)(::\w+\[?\]?)?[\s_]*(.*)?$/;
|
||||
$type = '' if ! defined $type;
|
||||
$log->debug("$object_type param $field($type) func($function) " . ( ref $$search{$k} eq 'ARRAY' ? join(',',@{$$search{$k}}) : $$search{$k} ) ) if DEBUG_ALL;
|
||||
|
||||
foreach ( 'find_fields', 'fields' ) {
|
||||
my $fields = \%{$object_type.'::'.$_};
|
||||
if ( ! $fields ) {
|
||||
$log->debug("No $fields in $object_type") if DEBUG_ALL;
|
||||
next;
|
||||
} # end if
|
||||
|
||||
if ( ! $$fields{$field} ) {
|
||||
#$log->debug("No $field in $_ for $object_type") if DEBUG_ALL;
|
||||
next;
|
||||
} # end if
|
||||
|
||||
# This allows mainly for find_fields to reference multiple values, opinion in Project, value
|
||||
foreach my $db_field ( ref $$fields{$field} eq 'ARRAY' ? @{$$fields{$field}} : $$fields{$field} ) {
|
||||
if ( ! $function ) {
|
||||
$db_field .= $type;
|
||||
|
||||
if ( ref $$search{$k} eq 'ARRAY' ) {
|
||||
$log->debug("Have array for $k $$search{$k}") if DEBUG_ALL;
|
||||
|
||||
if ( ! ( $db_field =~ /\?/ ) ) {
|
||||
if ( @{$$search{$k}} != 1 ) {
|
||||
push @where, $db_field .' IN ('.join(',', map {'?'} @{$$search{$k}} ) . ')';
|
||||
} else {
|
||||
push @where, $db_field.'=?';
|
||||
} # end if
|
||||
} else {
|
||||
$log->debug("Have question ? for $k $$search{$k} $db_field") if DEBUG_ALL;
|
||||
|
||||
$db_field =~ s/=/IN/g;
|
||||
my $question_replacement = '('.join(',', map {'?'} @{$$search{$k}} ) . ')';
|
||||
$db_field =~ s/\?/$question_replacement/;
|
||||
push @where, $db_field;
|
||||
}
|
||||
push @values, @{$$search{$k}};
|
||||
} elsif ( ref $$search{$k} eq 'HASH' ) {
|
||||
foreach my $p_k ( keys %{$$search{$k}} ) {
|
||||
my $v = $$search{$k}{$p_k};
|
||||
if ( ref $v eq 'ARRAY' ) {
|
||||
push @where, $db_field.' IN ('.join(',', map {'?'} @{$v} ) . ')';
|
||||
push @values, $p_k, @{$v};
|
||||
} else {
|
||||
push @where, $db_field.'=?';
|
||||
push @values, $p_k, $v;
|
||||
} # end if
|
||||
} # end foreach p_k
|
||||
} elsif ( ! defined $$search{$k} ) {
|
||||
push @where, $db_field.' IS NULL';
|
||||
} else {
|
||||
if ( ! ( $db_field =~ /\?/ ) ) {
|
||||
push @where, $db_field .'=?';
|
||||
} else {
|
||||
push @where, $db_field;
|
||||
}
|
||||
push @values, $$search{$k};
|
||||
} # end if
|
||||
push @used_fields, $k;
|
||||
} else {
|
||||
#my @w =
|
||||
#ref $search{$k} eq 'ARRAY' ?
|
||||
#map { find_operators( $field, $type, $function, $_ ); } @{$search{$k}} :
|
||||
my ( $w, @v ) = find_operators( $db_field, $type, $function, $$search{$k} );
|
||||
if ( $w ) {
|
||||
#push @where, '(' . join(' OR ', @w ) . ')';
|
||||
push @where, $w;
|
||||
push @values, @v if @v;
|
||||
push @used_fields, $k;
|
||||
} # end if @w
|
||||
} # end if has function or not
|
||||
} # end foreach db_field
|
||||
} # end foreach find_field
|
||||
} # end foreach k
|
||||
return ( \@where, \@values, \@used_fields );
|
||||
}
|
||||
|
||||
sub find {
|
||||
no strict 'refs';
|
||||
my $object_type = shift;
|
||||
my $debug = ${$object_type.'::debug'};
|
||||
$debug = DEBUG_ALL if ! $debug;
|
||||
|
||||
my $starttime = [gettimeofday] if $debug;
|
||||
my $params;
|
||||
if ( @_ == 1 ) {
|
||||
$params = $_[0];
|
||||
if ( ref $params ne 'HASH' ) {
|
||||
$log->error("params $params was not a has");
|
||||
} # end if
|
||||
} else {
|
||||
$params = { @_ };
|
||||
} # end if
|
||||
|
||||
my $local_dbh = ${$object_type.'::dbh'};
|
||||
if ( $$params{dbh} ) {
|
||||
$local_dbh = $$params{dbh};
|
||||
delete $$params{dbh};
|
||||
} elsif ( ! $local_dbh ) {
|
||||
$local_dbh = $dbh if ! $local_dbh;
|
||||
} # end if
|
||||
|
||||
my $sql = find_sql( $object_type, $params);
|
||||
|
||||
my $do_cache = $$sql{columns} ne '*' ? 0 : 1;
|
||||
|
||||
#$log->debug( 'find prepare: ' . sprintf('%.4f', tv_interval($starttime)*1000) ." useconds") if $debug;
|
||||
my $data = $local_dbh->selectall_arrayref($$sql{sql}, { Slice => {} }, @{$$sql{values}});
|
||||
if ( ! $data ) {
|
||||
$log->error('Error ' . $local_dbh->errstr() . " loading $object_type ($$sql{sql}) (". join(',', map { ref $_ eq 'ARRAY' ? 'ARRAY('.join(',',@$_).')' : $_ } @{$$sql{values}} ) . ')' );
|
||||
return ();
|
||||
#} elsif ( ( ! @$data ) and $debug ) {
|
||||
#$log->debug("No $type ($sql) (@values) " );
|
||||
} elsif ( $debug ) {
|
||||
$log->debug("Loading Debug:$debug $object_type ($$sql{sql}) (".join(',', map { ref $_ eq 'ARRAY' ? join(',', @{$_}) : $_ } @{$$sql{values}}).') # of results:' . @$data . ' in ' . sprintf('%.4f', tv_interval($starttime)*1000) .' useconds' );
|
||||
} # end if
|
||||
|
||||
my $fields = \%{$object_type.'::fields'};
|
||||
my $primary_key = ${$object_type.'::primary_key'};
|
||||
if ( ! $primary_key ) {
|
||||
Error( 'NO primary_key for type ' . $object_type );
|
||||
return;
|
||||
} # end if
|
||||
if ( ! ($fields and keys %{$fields}) ) {
|
||||
return map { new($object_type, $$_{$primary_key}, $_ ) } @$data;
|
||||
} elsif ( $$fields{$primary_key} ) {
|
||||
return map { new($object_type, $_->{$$fields{$primary_key}}, $_) } @$data;
|
||||
} else {
|
||||
my @identified_by = eval '@'.$object_type.'::identified_by';
|
||||
if ( ! @identified_by ) {
|
||||
$log->debug("Multi key object $object_type but no identified by $fields") if $debug;
|
||||
} # end if
|
||||
return map { new($object_type, \@identified_by, $_, !$do_cache) } @$data;
|
||||
} # end if
|
||||
} # end sub find
|
||||
|
||||
sub find_one {
|
||||
my $object_type = shift;
|
||||
my $params;
|
||||
if ( @_ == 1 ) {
|
||||
$params = $_[0];
|
||||
} else {
|
||||
%{$params} = @_;
|
||||
} # end if
|
||||
$$params{limit}=1;
|
||||
my @Results = $object_type->find(%$params);
|
||||
my ( $caller, undef, $line ) = caller;
|
||||
$log->debug("returning to $caller:$line from find_one") if DEBUG_ALL;
|
||||
return $Results[0] if @Results;
|
||||
} # end sub find_one
|
||||
|
||||
sub find_sql {
|
||||
no strict 'refs';
|
||||
my $object_type = shift;
|
||||
|
||||
my $debug = ${$object_type.'::debug'};
|
||||
$debug = DEBUG_ALL if ! $debug;
|
||||
|
||||
my $params;
|
||||
if ( @_ == 1 ) {
|
||||
$params = $_[0];
|
||||
if ( ref $params ne 'HASH' ) {
|
||||
$log->error("params $params was not a has");
|
||||
} # end if
|
||||
} else {
|
||||
$params = { @_ };
|
||||
} # end if
|
||||
|
||||
my %sql = (
|
||||
( distinct => ( exists $$params{distinct} ? 1:0 ) ),
|
||||
( columns => ( exists $$params{columns} ? $$params{columns} : '*' ) ),
|
||||
( table => ( exists $$params{table} ? $$params{table} : ${$object_type.'::table'} )),
|
||||
'group by'=> $$params{'group by'},
|
||||
limit => $$params{limit},
|
||||
offset => $$params{offset},
|
||||
);
|
||||
if ( exists $$params{order} ) {
|
||||
$sql{order} = $$params{order};
|
||||
} else {
|
||||
my $order = eval '$'.$object_type.'::default_sort';
|
||||
#$log->debug("default sort: $object_type :: default_sort = $order") if DEBUG_ALL;
|
||||
$sql{order} = $order if $order;
|
||||
} # end if
|
||||
delete @$params{'distinct','columns','table','group by','limit','offset','order'};
|
||||
|
||||
my @where;
|
||||
my @values;
|
||||
if ( exists $$params{custom} ) {
|
||||
push @where, '(' . (shift @{$$params{custom}}) . ')';
|
||||
push @values, @{$$params{custom}};
|
||||
delete $$params{custom};
|
||||
} # end if
|
||||
|
||||
my @param_keys = keys %$params;
|
||||
|
||||
# no operators, just which fields are being searched on. Mostly just useful for detetion of the deleted field.
|
||||
my %used_fields;
|
||||
|
||||
# We use this search hash so that we can mash it up and leave the params hash alone
|
||||
my %search;
|
||||
@search{@param_keys} = @$params{@param_keys};
|
||||
|
||||
my ( $where, $values, $used_fields ) = get_fields_values( $object_type, \%search, \@param_keys );
|
||||
delete @search{@{$used_fields}};
|
||||
@used_fields{ @{$used_fields} } = @{$used_fields};
|
||||
push @where, @{$where};
|
||||
push @values, @{$values};
|
||||
|
||||
my $fields = \%{$object_type.'::fields'};
|
||||
|
||||
#optimise this
|
||||
if ( $$fields{deleted} and ! $used_fields{deleted} ) {
|
||||
push @where, 'deleted=?';
|
||||
push @values, 0;
|
||||
} # end if
|
||||
$sql{where} = \@where;
|
||||
$sql{values} = \@values;
|
||||
$sql{used_fields} = \%used_fields;
|
||||
|
||||
foreach my $k ( keys %search ) {
|
||||
$log->error("Extra parameters in $object_type ::find $k => $search{$k}");
|
||||
Carp::cluck("Extra parameters in $object_type ::find $k => $search{$k}");
|
||||
} # end foreach
|
||||
|
||||
$sql{sql} = join( ' ',
|
||||
( 'SELECT', ( $sql{distinct} ? ('DISTINCT') : () ) ),
|
||||
( $sql{columns}, 'FROM', $sql{table} ),
|
||||
( @{$sql{where}} ? ('WHERE', join(' AND ', @{$sql{where}})) : () ),
|
||||
( $sql{order} ? ( 'ORDER BY', $sql{order} ) : () ),
|
||||
( $sql{'group by'} ? ( 'GROUP BY', $sql{'group by'} ) : () ),
|
||||
( $sql{limit} ? ( 'LIMIT', $sql{limit}) : () ),
|
||||
( $sql{offset} ? ( 'OFFSET', $sql{offset} ) : () ),
|
||||
);
|
||||
#$log->debug("Loading Debug:$debug $object_type ($sql) (".join(',', map { ref $_ eq 'ARRAY' ? join(',', @{$_}) : $_ } @values).')' ) if $debug;
|
||||
return \%sql;
|
||||
} # end sub find_sql
|
||||
|
||||
sub AUTOLOAD {
|
||||
my $type = ref($_[0]);
|
||||
Carp::cluck("No type in autoload") if ! $type;
|
||||
if ( DEBUG_ALL ) {
|
||||
Carp::cluck("Using AUTOLOAD $AUTOLOAD");
|
||||
}
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( @_ > 1 ) {
|
||||
return $_[0]{$name} = $_[1];
|
||||
}
|
||||
return $_[0]{$name};
|
||||
}
|
||||
|
||||
sub DESTROY {
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -176,8 +176,10 @@ MAIN: while( $loop ) {
|
|||
} # end while can't connect to the db
|
||||
|
||||
my @Storage_Areas;
|
||||
my @all_Storage_Areas = ZoneMinder::Storage->find();
|
||||
|
||||
if ( defined $storage_id ) {
|
||||
@Storage_Areas = ZoneMinder::Storage->find( Id=>$storage_id );
|
||||
@Storage_Areas = map { $$_{Id} == $storage_id ? $_ : () } @all_Storage_Areas;
|
||||
if ( !@Storage_Areas ) {
|
||||
Error("No Storage Area found with Id $storage_id");
|
||||
Term();
|
||||
|
@ -403,27 +405,28 @@ MAIN: while( $loop ) {
|
|||
$$Event{RelativePath} = $event_dir;
|
||||
$Event->MonitorId( $monitor_dir );
|
||||
$Event->StorageId( $Storage->Id() );
|
||||
$Event->StartTime( POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime(time_of_youngest_file($$Event{Path})) ) );
|
||||
$Event->StartTime( POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime(time_of_youngest_file($Event->Path())) ) );
|
||||
} # end foreach event
|
||||
}
|
||||
|
||||
if ( ! $$Storage{Scheme} ) {
|
||||
Error("Storage Scheme not set on $$Storage{Name}");
|
||||
if ( ! chdir( $monitor_dir ) ) {
|
||||
Error( "Can't chdir directory '$$Storage{Path}/$monitor_dir': $!" );
|
||||
if ( ! chdir($monitor_dir) ) {
|
||||
Error("Can't chdir directory '$$Storage{Path}/$monitor_dir': $!");
|
||||
next;
|
||||
}
|
||||
if ( ! opendir( DIR, "." ) ) {
|
||||
Error( "Can't open directory '$$Storage{Path}/$monitor_dir': $!" );
|
||||
if ( ! opendir(DIR, '.') ) {
|
||||
Error("Can't open directory '$$Storage{Path}/$monitor_dir': $!");
|
||||
next;
|
||||
}
|
||||
my @temp_events = sort { $b <=> $a } grep { -d $_ && $_ =~ /^\d+$/ } readdir( DIR );
|
||||
closedir( DIR );
|
||||
my @temp_events = sort { $b <=> $a } grep { -d $_ && $_ =~ /^\d+$/ } readdir(DIR);
|
||||
closedir(DIR);
|
||||
my $count = 0;
|
||||
foreach my $event ( @temp_events ) {
|
||||
my $Event = $fs_events->{$event} = new ZoneMinder::Event();
|
||||
$$Event{Id} = $event;
|
||||
#$$Event{Path} = $event_path;
|
||||
$$Event{Scheme} = 'Shallow';
|
||||
$Event->MonitorId( $monitor_dir );
|
||||
$Event->StorageId( $Storage->Id() );
|
||||
} # end foreach event
|
||||
|
@ -528,7 +531,7 @@ MAIN: while( $loop ) {
|
|||
# If we found the monitor in the file system
|
||||
my $fs_events = $fs_monitors->{$db_monitor};
|
||||
|
||||
while ( my ( $db_event, $age ) = each( %$db_events ) ) {
|
||||
EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) {
|
||||
if ( ! ($fs_events and defined( $fs_events->{$db_event} ) ) ) {
|
||||
Debug("Don't have an fs event for $db_event");
|
||||
my $Event = ZoneMinder::Event->find_one( Id=>$db_event );
|
||||
|
@ -537,6 +540,21 @@ MAIN: while( $loop ) {
|
|||
next;
|
||||
}
|
||||
Debug("Event $db_event is not in fs. Should have been at ".$Event->Path());
|
||||
# Check for existence in other Storage Areas
|
||||
foreach my $Storage ( @all_Storage_Areas ) {
|
||||
next if $$Storage{Id} == $$Event{StorageId};
|
||||
|
||||
my $path = $Storage->Path().'/'.$Event->RelativePath();
|
||||
if ( -e $path ) {
|
||||
Info("Event $$Event{Id} found at $path instead of $$Event{Path}");
|
||||
if ( confirm('update', 'updating') ) {
|
||||
$Event->save({StorageId=>$$Storage{Id}});
|
||||
next EVENT;
|
||||
}
|
||||
} else {
|
||||
Debug("$$Event{Id} Not found at $path");
|
||||
}
|
||||
}
|
||||
if ( $Event->Archived() ) {
|
||||
Warning("Event $$Event{Id} is Archived. Taking no further action on it.");
|
||||
next;
|
||||
|
@ -563,7 +581,7 @@ MAIN: while( $loop ) {
|
|||
Debug("Database event $$Event{Id} apparently exists at " . $Event->Path() );
|
||||
} else {
|
||||
if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) {
|
||||
aud_print( "Database event '$db_monitor/$db_event' does not exist at " . $Event->Path().' in filesystem, deleting' );
|
||||
aud_print("Database event '$db_monitor/$db_event' does not exist at " . $Event->Path().' in filesystem, deleting');
|
||||
if ( confirm() ) {
|
||||
$Event->delete();
|
||||
$cleaned = 1;
|
||||
|
@ -573,12 +591,13 @@ MAIN: while( $loop ) {
|
|||
}
|
||||
} # end if exists in filesystem
|
||||
} else {
|
||||
Debug("Found fs event for $db_event, $age at " . $$fs_events{$db_event}->Path());
|
||||
my $Event = new ZoneMinder::Event( $db_event );
|
||||
if ( ! $Event->check_for_in_filesystem() ) {
|
||||
Debug("Found fs event for id $db_event, $age seconds old at " . $$fs_events{$db_event}->Path());
|
||||
my $Event = ZoneMinder::Event->find_one( Id=>$db_event );
|
||||
if ( $Event and ! $Event->check_for_in_filesystem() ) {
|
||||
Warning("Not found at " . $Event->Path() . ' was found at ' . $$fs_events{$db_event}->Path() );
|
||||
Warning($Event->to_string());
|
||||
Warning($$fs_events{$db_event}->to_string());
|
||||
$$Event{Scheme} = '' if ! defined $$Event{Scheme};
|
||||
if ( $$fs_events{$db_event}->Scheme() ne $Event->Scheme() ) {
|
||||
Info("Updating scheme on event $$Event{Id} from $$Event{Scheme} to $$fs_events{$db_event}{Scheme}");
|
||||
$Event->Scheme($$fs_events{$db_event}->Scheme());
|
||||
|
@ -747,7 +766,7 @@ FROM Frames WHERE EventId=?';
|
|||
$res = $selectUnclosedEventsSth->execute()
|
||||
or Fatal( "Can't execute: ".$selectUnclosedEventsSth->errstr() );
|
||||
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 ( ! ( $res = $selectFrameDataSth->execute($event->{Id}) ) ) {
|
||||
Error( "Can't execute: $selectFrameDataSql:".$selectFrameDataSth->errstr() );
|
||||
|
@ -773,13 +792,13 @@ FROM Frames WHERE EventId=?';
|
|||
$frame->{MaxScore},
|
||||
RECOVER_TEXT,
|
||||
$event->{Id}
|
||||
) or Error( "Can't execute: ".$updateUnclosedEventsSth->errstr() );
|
||||
) or Error( 'Can\'t execute: '.$updateUnclosedEventsSth->errstr() );
|
||||
} else {
|
||||
Error('SHOULD DELETE');
|
||||
} # end if has frame data
|
||||
}
|
||||
} # end while unclosed event
|
||||
Debug("Done closing open events.");
|
||||
Debug('Done closing open events.');
|
||||
|
||||
# Now delete any old image files
|
||||
if ( my @old_files = grep { -M > $max_image_age } <$image_path/*.{jpg,gif,wbmp}> ) {
|
||||
|
@ -962,10 +981,11 @@ sub deleteSwapImage {
|
|||
|
||||
# Deletes empty sub directories of the given path.
|
||||
# Does not delete the path if empty. Is not meant to be recursive.
|
||||
# Assumes absolute path
|
||||
sub delete_empty_subdirs {
|
||||
my $DIR;
|
||||
if ( !opendir($DIR, $_[0]) ) {
|
||||
Error("delete_empty_directories: Can't open directory '".getcwd()."/$_[0]': $!" );
|
||||
Error("delete_empty_subdirs: Can't open directory '/$_[0]': $!" );
|
||||
return;
|
||||
}
|
||||
my @contents = map { ( $_ eq '.' or $_ eq '..' ) ? () : $_ } readdir( $DIR );
|
||||
|
@ -978,23 +998,24 @@ sub delete_empty_subdirs {
|
|||
closedir($DIR);
|
||||
}
|
||||
|
||||
# Assumes absolute path
|
||||
sub delete_empty_directories {
|
||||
my $DIR;
|
||||
if ( !opendir($DIR, $_[0]) ) {
|
||||
Error("delete_empty_directories: Can't open directory '".getcwd()."/$_[0]': $!" );
|
||||
Error("delete_empty_directories: Can't open directory '/$_[0]': $!" );
|
||||
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) : '' ));
|
||||
my @dirs = map { -d $_[0].'/'.$_ ? $_ : () } @contents;
|
||||
if ( @dirs ) {
|
||||
Debug("Have " . @dirs . " dirs");
|
||||
Debug('Have ' . @dirs . " dirs in $_[0]");
|
||||
foreach ( @dirs ) {
|
||||
delete_empty_directories( $_[0].'/'.$_ );
|
||||
delete_empty_directories($_[0].'/'.$_);
|
||||
}
|
||||
#Reload, since we may now be empty
|
||||
rewinddir $DIR;
|
||||
@contents = map { ($_ eq '.' or $_ eq '..') ? () : $_ } readdir( $DIR );
|
||||
@contents = map { ($_ eq '.' or $_ eq '..') ? () : $_ } readdir($DIR);
|
||||
}
|
||||
closedir($DIR);
|
||||
if ( ! @contents ) {
|
||||
|
|
|
@ -30,7 +30,7 @@ use autouse 'Pod::Usage'=>qw(pod2usage);
|
|||
use POSIX qw/strftime EPIPE/;
|
||||
use Socket;
|
||||
#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_COMMAND_WAIT => 1800;
|
||||
|
@ -43,7 +43,7 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
|||
|
||||
logInit();
|
||||
|
||||
my $arg_string = join( " ", @ARGV );
|
||||
my $arg_string = join(' ', @ARGV);
|
||||
|
||||
my $id;
|
||||
my %options;
|
||||
|
@ -63,23 +63,47 @@ GetOptions(
|
|||
'autostop' =>\$options{autostop},
|
||||
) or pod2usage(-exitstatus => -1);
|
||||
|
||||
if ( !$id || !$options{command} ) {
|
||||
print( STDERR "Please give a valid monitor id and command\n" );
|
||||
if ( !$id ) {
|
||||
print(STDERR "Please give a valid monitor id\n");
|
||||
pod2usage(-exitstatus => -1);
|
||||
}
|
||||
|
||||
( $id ) = $id =~ /^(\w+)$/;
|
||||
|
||||
Debug("zmcontrol: arg string: $arg_string");
|
||||
|
||||
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)
|
||||
or Fatal("Can't open socket: $!");
|
||||
socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!");
|
||||
|
||||
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
|
||||
my $monitor = zmDbGetMonitorAndControl($id);
|
||||
if ( !$monitor ) {
|
||||
|
@ -113,97 +137,72 @@ if ( !$server_up ) {
|
|||
Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR");
|
||||
}
|
||||
|
||||
if ( my $cpid = fork() ) {
|
||||
logReinit();
|
||||
Info("Control server $id/$protocol starting at "
|
||||
.strftime('%y/%m/%d %H:%M:%S', localtime())
|
||||
);
|
||||
|
||||
# Parent process just sleep and fall through
|
||||
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);
|
||||
$0 = $0." --id=$id";
|
||||
|
||||
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 "
|
||||
.strftime('%y/%m/%d %H:%M:%S', localtime())
|
||||
);
|
||||
|
||||
$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: $!");
|
||||
# If we have a command when starting up, then do it.
|
||||
if ( $options{command} ) {
|
||||
my $command = $options{command};
|
||||
$control->$command(\%options);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
# 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);
|
||||
|
||||
|
@ -216,7 +215,7 @@ zmcontrol.pl - ZoneMinder control script
|
|||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
zmcontrol.pl --id {monitor_id} --command={command} [various options]
|
||||
zmcontrol.pl --id {monitor_id} [--command={command}] [various options]
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
|
|
|
@ -101,6 +101,7 @@ my @daemons = (
|
|||
'zmupdate.pl',
|
||||
'zmstats.pl',
|
||||
'zmtrack.pl',
|
||||
'zmcontrol.pl',
|
||||
'zmtelemetry.pl'
|
||||
);
|
||||
|
||||
|
|
|
@ -84,9 +84,7 @@ if ( !$command ) {
|
|||
}
|
||||
|
||||
# PP - Sane state check
|
||||
Debug("StartisActiveSSantiyCheck");
|
||||
isActiveSanityCheck();
|
||||
Debug("Done isActiveSSantiyCheck");
|
||||
|
||||
# Move to the right place
|
||||
chdir($Config{ZM_PATH_WEB})
|
||||
|
@ -222,14 +220,17 @@ if ( $command =~ /^(?:start|restart)$/ ) {
|
|||
runCommand("zmdc.pl start zma -m $monitor->{Id}");
|
||||
}
|
||||
if ( $Config{ZM_OPT_CONTROL} ) {
|
||||
if ( $monitor->{Controllable} && $monitor->{TrackMotion} ) {
|
||||
if ( $monitor->{Function} eq 'Modect' || $monitor->{Function} eq 'Mocord' ) {
|
||||
runCommand( "zmdc.pl start zmtrack.pl -m $monitor->{Id}" );
|
||||
} else {
|
||||
Warning(' Monitor is set to track motion, but does not have motion detection enabled.');
|
||||
} # end if Has motion enabled
|
||||
} # end if track motion
|
||||
} # end if ZM_OPT_CONTROL
|
||||
if ( $monitor->{Controllable} ) {
|
||||
runCommand("zmdc.pl start zmcontrol.pl --id $monitor->{Id}");
|
||||
if ( $monitor->{TrackMotion} ) {
|
||||
if ( $monitor->{Function} eq 'Modect' || $monitor->{Function} eq 'Mocord' ) {
|
||||
runCommand("zmdc.pl start zmtrack.pl -m $monitor->{Id}");
|
||||
} else {
|
||||
Warning('Monitor is set to track motion, but does not have motion detection enabled.');
|
||||
} # 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 foreach monitor
|
||||
$sth->finish();
|
||||
|
@ -253,15 +254,15 @@ if ( $command =~ /^(?:start|restart)$/ ) {
|
|||
}
|
||||
|
||||
if ( $Config{ZM_RUN_AUDIT} ) {
|
||||
if ( $Server and exists $$Server{'zmaudit'} and ! $$Server{'zmaudit'} ) {
|
||||
Debug("Not running zmaudit.pl because it is turned off for this server.");
|
||||
if ( $Server and exists $$Server{zmaudit} and ! $$Server{zmaudit} ) {
|
||||
Debug('Not running zmaudit.pl because it is turned off for this server.');
|
||||
} else {
|
||||
runCommand('zmdc.pl start zmaudit.pl -c');
|
||||
}
|
||||
}
|
||||
if ( $Config{ZM_OPT_TRIGGERS} ) {
|
||||
if ( $Server and exists $$Server{'zmtrigger'} and ! $$Server{'zmtrigger'} ) {
|
||||
Debug("Not running zmtrigger.pl because it is turned off for this server.");
|
||||
if ( $Server and exists $$Server{zmtrigger} and ! $$Server{zmtrigger} ) {
|
||||
Debug('Not running zmtrigger.pl because it is turned off for this server.');
|
||||
} else {
|
||||
runCommand('zmdc.pl start zmtrigger.pl');
|
||||
}
|
||||
|
@ -283,8 +284,8 @@ if ( $command =~ /^(?:start|restart)$/ ) {
|
|||
runCommand('zmdc.pl start zmeventnotification.pl');
|
||||
}
|
||||
}
|
||||
if ( $Server and exists $$Server{'zmstats'} and ! $$Server{'zmstats'} ) {
|
||||
Debug("Not running zmstats.pl because it is turned off for this server.");
|
||||
if ( $Server and exists $$Server{zmstats} and ! $$Server{zmstats} ) {
|
||||
Debug('Not running zmstats.pl because it is turned off for this server.');
|
||||
} else {
|
||||
runCommand('zmdc.pl start zmstats.pl');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,491 @@
|
|||
#!/usr/bin/perl -wT
|
||||
|
||||
use strict;
|
||||
use bytes;
|
||||
|
||||
# ==========================================================================
|
||||
#
|
||||
# These are the elements you can edit to suit your installation
|
||||
#
|
||||
# ==========================================================================
|
||||
|
||||
use constant RECOVER_TAG => '(r)'; # Tag to append to event name when recovered
|
||||
use constant RECOVER_TEXT => 'Recovered.'; # Text to append to event notes when recovered
|
||||
|
||||
# ==========================================================================
|
||||
#
|
||||
# You shouldn't need to change anything from here downwards
|
||||
#
|
||||
# ==========================================================================
|
||||
|
||||
@EXTRA_PERL_LIB@
|
||||
use ZoneMinder;
|
||||
use DBI;
|
||||
use POSIX;
|
||||
use File::Find;
|
||||
use Time::HiRes qw/gettimeofday/;
|
||||
use Getopt::Long;
|
||||
use autouse 'Pod::Usage'=>qw(pod2usage);
|
||||
|
||||
use constant ZM_RECOVER_PID => '@ZM_RUNDIR@/zmrecover.pid';
|
||||
|
||||
|
||||
$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin';
|
||||
$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL};
|
||||
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
||||
|
||||
my $report = 0;
|
||||
my $interactive = 1;
|
||||
my $monitor_id = 0;
|
||||
my $version;
|
||||
my $force = 0;
|
||||
my $server_id = undef;
|
||||
my $storage_id = undef;
|
||||
|
||||
logInit();
|
||||
|
||||
GetOptions(
|
||||
force =>\$force,
|
||||
'interactive=i' =>\$interactive,
|
||||
'monitor_id=i' =>\$monitor_id,
|
||||
report =>\$report,
|
||||
'server_id=i' =>\$server_id,
|
||||
'storage_id=i' =>\$storage_id,
|
||||
version =>\$version
|
||||
) or pod2usage(-exitstatus => -1);
|
||||
|
||||
if ( $version ) {
|
||||
print( ZoneMinder::Base::ZM_VERSION . "\n");
|
||||
exit(0);
|
||||
}
|
||||
if ( ($report + $interactive) > 1 ) {
|
||||
print( STDERR "Error, only one option may be specified\n" );
|
||||
pod2usage(-exitstatus => -1);
|
||||
}
|
||||
|
||||
if ( -e ZM_RECOVER_PID ) {
|
||||
local $/ = undef;
|
||||
open FILE, ZM_RECOVER_PID or die "Couldn't open file: $!";
|
||||
binmode FILE;
|
||||
my $pid = <FILE>;
|
||||
close FILE;
|
||||
if ( $force ) {
|
||||
Error("zmrecover.pl appears to already be running at pid $pid. Continuing." );
|
||||
} else {
|
||||
Fatal("zmrecover.pl appears to already be running at pid $pid. If not, please delete " .
|
||||
ZM_RECOVER_PID . " or use the --force command line option." );
|
||||
}
|
||||
} # end if ZM_RECOVER_PID exists
|
||||
|
||||
if ( open(my $PID, '>', ZM_RECOVER_PID) ) {
|
||||
print($PID $$);
|
||||
close($PID);
|
||||
} else {
|
||||
Error( "Can't open pid file at " . ZM_PID );
|
||||
}
|
||||
|
||||
sub HupHandler {
|
||||
Info('Received HUP, reloading');
|
||||
&ZoneMinder::Logger::logHupHandler();
|
||||
}
|
||||
sub TermHandler {
|
||||
Info('Received TERM, exiting');
|
||||
Term();
|
||||
}
|
||||
sub Term {
|
||||
unlink ZM_RECOVER_PID;
|
||||
exit(0);
|
||||
}
|
||||
$SIG{HUP} = \&HupHandler;
|
||||
$SIG{TERM} = \&TermHandler;
|
||||
$SIG{INT} = \&TermHandler;
|
||||
|
||||
my $dbh = zmDbConnect();
|
||||
if ( ! $dbh ) {
|
||||
Error('Unable to connect to database');
|
||||
Term();
|
||||
} # end if
|
||||
|
||||
$| = 1;
|
||||
|
||||
require ZoneMinder::Monitor;
|
||||
require ZoneMinder::Storage;
|
||||
require ZoneMinder::Event;
|
||||
|
||||
|
||||
my @Storage_Areas;
|
||||
if ( defined $storage_id ) {
|
||||
@Storage_Areas = ZoneMinder::Storage->find( Id=>$storage_id );
|
||||
if ( !@Storage_Areas ) {
|
||||
Error("No Storage Area found with Id $storage_id");
|
||||
Term();
|
||||
}
|
||||
Info("Recovering from Storage Area $Storage_Areas[0]{Id} $Storage_Areas[0]{Name} at $Storage_Areas[0]{Path}");
|
||||
} elsif ( $server_id ) {
|
||||
@Storage_Areas = ZoneMinder::Storage->find( ServerId => $server_id );
|
||||
if ( ! @Storage_Areas ) {
|
||||
Error("No Storage Area found with ServerId =" . $server_id);
|
||||
Term();
|
||||
}
|
||||
foreach my $Storage ( @Storage_Areas ) {
|
||||
Info('Recovering from ' . $Storage->Name() . ' at ' . $Storage->Path() . ' on ' . $Storage->Server()->Name() );
|
||||
}
|
||||
} else {
|
||||
@Storage_Areas = ZoneMinder::Storage->find();
|
||||
Info("Recovering from All Storage Areas");
|
||||
}
|
||||
|
||||
my @Monitors = ZoneMinder::Monitor->find();
|
||||
Debug("@Monitors");
|
||||
foreach my $Monitor ( @Monitors ) {
|
||||
Debug("Monitor " . $Monitor->to_string() )
|
||||
}
|
||||
my %Monitors = map { $$_{Id} => $_ } @Monitors;
|
||||
#ZoneMinder::Monitor->find(
|
||||
|
||||
# ($monitor_id ? ( Id=>$monitor_id ) : () ),
|
||||
|
||||
#);
|
||||
Debug("Found " . (keys %Monitors) . " monitors");
|
||||
foreach my $id ( keys %Monitors ) {
|
||||
Debug("Monitor $id $Monitors{$id}{Name}");
|
||||
}
|
||||
|
||||
foreach my $Storage ( @Storage_Areas ) {
|
||||
Debug('Checking events in ' . $Storage->Path() );
|
||||
if ( ! chdir($Storage->Path()) ) {
|
||||
Error('Unable to change dir to ' . $Storage->Path());
|
||||
next;
|
||||
} # end if
|
||||
|
||||
# Please note that this glob will take all files beginning with a digit.
|
||||
foreach my $monitor ( glob('[0-9]*') ) {
|
||||
if ( $monitor =~ /\D/ ) {
|
||||
Debug("Weird non digit characters in $monitor");
|
||||
next;
|
||||
}
|
||||
# De-taint
|
||||
( my $monitor_dir ) = ( $monitor =~ /^(\d+)$/ );
|
||||
if ( $monitor_id and ( $monitor_id != $monitor_dir ) ) {
|
||||
Debug("Skipping monitor $monitor_dir because we are only interested in monitor $monitor_id");
|
||||
next;
|
||||
}
|
||||
if ( ! $Monitors{$monitor_dir} ) {
|
||||
Warning("There is no monitor in the database for $$Storage{Path}/$monitor_dir. Skipping it.");
|
||||
next;
|
||||
}
|
||||
my $Monitor = $Monitors{$monitor_dir};
|
||||
|
||||
Debug("Found filesystem monitor '$monitor_dir'");
|
||||
|
||||
{
|
||||
my @day_dirs = glob("$monitor_dir/[0-9][0-9]/[0-9][0-9]/[0-9][0-9]");
|
||||
Debug(qq`Checking for Deep Events under $$Storage{Path} using glob("$monitor_dir/[0-9][0-9]/[0-9][0-9]/[0-9][0-9]") returned `. scalar @day_dirs . ' days with events');
|
||||
foreach my $day_dir ( @day_dirs ) {
|
||||
Debug("Checking day dir $day_dir");
|
||||
( $day_dir ) = ( $day_dir =~ /^(.*)$/ ); # De-taint
|
||||
if ( !chdir($day_dir) ) {
|
||||
Error("Can't chdir to '$$Storage{Path}/$day_dir': $!");
|
||||
next;
|
||||
}
|
||||
if ( ! opendir(DIR, '.') ) {
|
||||
Error("Can't open directory '$$Storage{Path}/$day_dir': $!");
|
||||
next;
|
||||
}
|
||||
my %event_ids_by_path;
|
||||
|
||||
my @event_links = sort { $b <=> $a } grep { -l $_ } readdir( DIR );
|
||||
Debug('Have ' . (scalar @event_links) . ' event links');
|
||||
closedir(DIR);
|
||||
|
||||
my $count = 0;
|
||||
foreach my $event_link ( @event_links ) {
|
||||
# Event links start with a period and consist of the digits of the event id.
|
||||
# Anything else is not an event link
|
||||
my ($event_id) = $event_link =~ /^\.(\d+)$/;
|
||||
if ( !$event_id ) {
|
||||
Warning("Non-event link found $event_link in $day_dir, skipping");
|
||||
next;
|
||||
}
|
||||
Debug("Checking link $event_link");
|
||||
#Event path is hour/minute/sec
|
||||
my $event_path = readlink($event_link);
|
||||
|
||||
if ( !($event_path and -e $event_path) ) {
|
||||
Warning("Event link $day_dir/$event_link does not point to valid target at $event_path");
|
||||
next;
|
||||
}
|
||||
if ( ! ZoneMinder::Event->find_one(Id=>$event_id) ) {
|
||||
Info("Event not found in db for event data found at $$Storage{Path}/$day_dir/$event_path with Id=$event_id");
|
||||
if ( confirm() ) {
|
||||
my $Event = new ZoneMinder::Event();
|
||||
$$Event{Id} = $event_id;
|
||||
$$Event{Path} = join('/', $Storage->Path(), $day_dir, $event_path);
|
||||
$$Event{RelativePath} = join('/', $day_dir, $event_path);
|
||||
$$Event{Scheme} = 'Deep';
|
||||
$$Event{Name} = "Event $event_id recovered";
|
||||
$Event->MonitorId( $monitor_dir );
|
||||
$Event->StorageId( $Storage->Id() );
|
||||
$Event->DiskSpace( undef );
|
||||
$Event->Width( $Monitor->Width() );
|
||||
$Event->Height( $Monitor->Height() );
|
||||
$Event->Orientation( $Monitor->Orientation() );
|
||||
$Event->recover_timestamps();
|
||||
if ( $$Event{StartTime} ) {
|
||||
$Event->save({}, 1);
|
||||
Info("Event resurrected as " . $Event->to_string() );
|
||||
} else {
|
||||
Warning("Unable to determine starttime. Not resurrecting this event.");
|
||||
}
|
||||
next;
|
||||
} # end if resurrection
|
||||
} # event path exists
|
||||
} # end foreach event_link
|
||||
|
||||
# Now check for events that have lost their link. We can determine event Id from .mp4
|
||||
|
||||
my @time_dirs = glob('[0-9][0-9]/[0-9][0-9]/[0-9][0-9]');
|
||||
foreach my $event_dir ( @time_dirs ) {
|
||||
Debug("Checking time dir $event_dir");
|
||||
( $event_dir ) = ( $event_dir =~ /^(.*)$/ ); # De-taint
|
||||
|
||||
my $event_id = undef;
|
||||
|
||||
if ( ! opendir(DIR, $event_dir) ) {
|
||||
Error("Can't open directory '$$Storage{Path}/$day_dir': $!");
|
||||
next;
|
||||
}
|
||||
my @contents = readdir( DIR );
|
||||
Debug('Have ' . @contents . " files in $day_dir/$event_dir");
|
||||
closedir(DIR);
|
||||
|
||||
my @mp4_files = grep( /^\d+\-video.mp4$/, @contents);
|
||||
foreach my $mp4_file ( @mp4_files ) {
|
||||
my ( $id ) = $mp4_file =~ /^([0-9]+)\-video\.mp4$/;
|
||||
if ( $id ) {
|
||||
$event_id = $id;
|
||||
Debug("Got event id from mp4 file $mp4_file => $event_id");
|
||||
last;
|
||||
}
|
||||
} # end foreach mp4
|
||||
|
||||
if ( ! $event_id ) {
|
||||
# Look for .id file
|
||||
my @hidden_files = grep( /^\.\d+$/, @contents);
|
||||
Debug('Have ' . @hidden_files . ' hidden files');
|
||||
if ( @hidden_files ) {
|
||||
( $event_id ) = $hidden_files[0] =~ /^.(\d+)$/;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $event_id and ! ZoneMinder::Event->find_one(Id=>$event_id) ) {
|
||||
Info("Event not found in db for event data found at $$Storage{Path}/$monitor_dir/$day_dir/$event_dir");
|
||||
if ( confirm() ) {
|
||||
my $Event = new ZoneMinder::Event();
|
||||
$$Event{Id} = $event_id;
|
||||
$$Event{Path} = join('/', $Storage->Path(), $day_dir, $event_dir);
|
||||
$$Event{RelativePath} = join('/', $day_dir, $event_dir);
|
||||
$$Event{Scheme} = 'Deep';
|
||||
$$Event{Name} = "Event $event_id recovered";
|
||||
$Event->MonitorId( $monitor_dir );
|
||||
$Event->Width( $Monitor->Width() );
|
||||
$Event->Height( $Monitor->Height() );
|
||||
$Event->Orientation( $Monitor->Orientation() );
|
||||
$Event->StorageId( $Storage->Id() );
|
||||
$Event->DiskSpace( undef );
|
||||
$Event->recover_timestamps();
|
||||
if ( $$Event{StartTime} ) {
|
||||
$Event->save({}, 1);
|
||||
Info("Event resurrected as " . $Event->to_string() );
|
||||
} else {
|
||||
Warning("Unable to determine starttime. Not resurrecting this event.");
|
||||
}
|
||||
next;
|
||||
}
|
||||
} # end if event found
|
||||
|
||||
# Search in db for given timestamp?
|
||||
|
||||
my ( undef, $year, $month, $day ) = split('/', $day_dir);
|
||||
$year += 2000;
|
||||
my ( $hour, $minute, $second ) = split('/', $event_dir);
|
||||
my $StartTime =sprintf('%.4d-%.2d-%.2d %.2d:%.2d:%.2d', $year, $month, $day, $hour, $minute, $second);
|
||||
my $Event = ZoneMinder::Event->find_one(
|
||||
MonitorId=>$monitor_dir,
|
||||
StartTime=>$StartTime,
|
||||
);
|
||||
if ( $Event ) {
|
||||
Debug("Found event matching starttime on monitor $monitor_dir at $StartTime: " . $Event->to_string());
|
||||
next;
|
||||
}
|
||||
|
||||
} # end foreach event_dir without link
|
||||
chdir( $Storage->Path() );
|
||||
} # end foreach day dir
|
||||
}
|
||||
|
||||
Debug("Checking for Medium Scheme Events under $$Storage{Path}/$monitor_dir");
|
||||
{
|
||||
my @event_dirs = glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*");
|
||||
Debug(qq`glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") returned ` . scalar @event_dirs . " entries." );
|
||||
foreach my $event_dir ( @event_dirs ) {
|
||||
if ( ! -d $event_dir ) {
|
||||
Debug("$event_dir is not a dir. Skipping");
|
||||
next;
|
||||
}
|
||||
my ( $date, $event_id ) = $event_dir =~ /^$monitor_dir\/(\d{4}\-\d{2}\-\d{2})\/(\d+)$/;
|
||||
if ( !$event_id ) {
|
||||
Debug("Unable to parse date/event_id from $event_dir");
|
||||
next;
|
||||
}
|
||||
|
||||
my $Event = ZoneMinder::Event->find_one(Id=>$event_id);
|
||||
if ( $Event ) {
|
||||
Debug('Found event in the db, moving on.');
|
||||
next;
|
||||
}
|
||||
$Event = new ZoneMinder::Event();
|
||||
$$Event{Id} = $event_id;
|
||||
$$Event{Path} = join('/', $Storage->Path(), $event_dir );
|
||||
Info("Have event $$Event{Id} at $$Event{Path}");
|
||||
if ( confirm() ) {
|
||||
$$Event{Scheme} = 'Medium';
|
||||
$$Event{RelativePath} = $event_dir;
|
||||
$$Event{Name} = "Event $event_id recovered";
|
||||
$Event->MonitorId( $monitor_dir );
|
||||
$Event->Width( $Monitor->Width() );
|
||||
$Event->Height( $Monitor->Height() );
|
||||
$Event->Orientation( $Monitor->Orientation() );
|
||||
$Event->StorageId( $Storage->Id() );
|
||||
$Event->recover_timestamps();
|
||||
if ( $$Event{StartTime} ) {
|
||||
$Event->save({}, 1);
|
||||
Info("Event resurrected as " . $Event->to_string() );
|
||||
} else {
|
||||
Warning("Unable to determine starttime. Not resurrecting this event.");
|
||||
}
|
||||
}
|
||||
} # end foreach event
|
||||
} # end search for Medium
|
||||
|
||||
# Shallow
|
||||
Debug("Checking for ShallowScheme Events under $$Storage{Path}/$monitor_dir");
|
||||
if ( ! chdir($monitor_dir) ) {
|
||||
Error("Can't chdir directory '$$Storage{Path}/$monitor_dir': $!");
|
||||
next;
|
||||
}
|
||||
if ( ! opendir(DIR, '.') ) {
|
||||
Error("Can't open directory '$$Storage{Path}/$monitor_dir': $!");
|
||||
next;
|
||||
}
|
||||
my @temp_events = sort { $b <=> $a } grep { -d $_ && $_ =~ /^\d+$/ } readdir( DIR );
|
||||
closedir(DIR);
|
||||
foreach my $event ( @temp_events ) {
|
||||
my $Event = ZoneMinder::Event->find_one(Id=>$event);
|
||||
if ( $Event ) {
|
||||
Debug("Found an event in db for $event");
|
||||
next;
|
||||
}
|
||||
$Event = new ZoneMinder::Event();
|
||||
$$Event{Id} = $event;
|
||||
$$Event{Path} = join('/', $Storage->Path(), $event );
|
||||
Info("Have event $$Event{Id} at $$Event{Path}");
|
||||
if ( confirm() ) {
|
||||
$$Event{Scheme} = 'Shallow';
|
||||
$$Event{Name} = "Event $event recovered";
|
||||
#$$Event{Path} = $event_path;
|
||||
$Event->MonitorId( $monitor_dir );
|
||||
$Event->Width( $Monitor->Width() );
|
||||
$Event->Height( $Monitor->Height() );
|
||||
$Event->Orientation( $Monitor->Orientation() );
|
||||
$Event->StorageId( $Storage->Id() );
|
||||
$Event->recover_timestamps();
|
||||
if ( $$Event{StartTime} ) {
|
||||
$Event->save({}, 1);
|
||||
Info("Event resurrected as " . $Event->to_string() );
|
||||
} else {
|
||||
Warning("Unable to determine starttime. Not resurrecting this event.");
|
||||
}
|
||||
}
|
||||
} # end foreach event
|
||||
chdir( $Storage->Path() );
|
||||
} # end foreach monitor
|
||||
|
||||
} # end foreach Storage Area
|
||||
|
||||
Term();
|
||||
|
||||
sub confirm {
|
||||
my $prompt = shift || 'resurrect';
|
||||
my $action = shift || 'resurrecting';
|
||||
|
||||
my $yesno = 0;
|
||||
if ( $report ) {
|
||||
print( "\n" );
|
||||
} elsif ( $interactive ) {
|
||||
print(", $prompt Y/n/q: ");
|
||||
my $char = <>;
|
||||
chomp( $char );
|
||||
if ( $char eq 'q' ) {
|
||||
Term();
|
||||
}
|
||||
if ( !$char ) {
|
||||
$char = 'y';
|
||||
}
|
||||
$yesno = ( $char =~ /[yY]/ );
|
||||
} else {
|
||||
Info($action);
|
||||
$yesno = 1;
|
||||
}
|
||||
return $yesno;
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
zmrecover.pl - ZoneMinder event file system and database recovery checker
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
zmrecover.pl [-r,-report|-i,-interactive]
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This script checks for consistency between the event filesystem and
|
||||
the database. If events are found in one and not the other they are
|
||||
deleted (optionally). Additionally any monitor event directories that
|
||||
do not correspond to a database monitor are similarly disposed of.
|
||||
However monitors in the database that don't have a directory are left
|
||||
alone as this is valid if they are newly created and have no events
|
||||
yet.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
-i, --interactive - Ask before applying any changes
|
||||
-m, --monitor_id - Only consider the given monitor
|
||||
-r, --report - Just report don't actually do anything
|
||||
-s, --storage_id - Specify a storage area to recover instead of all
|
||||
-v, --version - Print the installed version of ZoneMinder
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
ZoneMinder Recover Script
|
||||
Copyright (C) 2018 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.
|
||||
|
||||
=cut
|
|
@ -18,9 +18,7 @@ use constant START_DELAY => 30; # To give everything else time to start
|
|||
|
||||
@EXTRA_PERL_LIB@
|
||||
use ZoneMinder;
|
||||
use POSIX;
|
||||
use DBI;
|
||||
use autouse 'Data::Dumper'=>qw(Dumper);
|
||||
|
||||
$| = 1;
|
||||
|
||||
|
@ -31,15 +29,15 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
|
|||
logInit();
|
||||
logSetSignal();
|
||||
|
||||
Info("Stats Daemon starting in ".START_DELAY." seconds");
|
||||
Info('Stats Daemon starting in '.START_DELAY.' seconds');
|
||||
sleep(START_DELAY);
|
||||
|
||||
my $dbh = zmDbConnect();
|
||||
|
||||
while( 1 ) {
|
||||
while ( ! ( $dbh and $dbh->ping() ) ) {
|
||||
Info("Reconnecting to db");
|
||||
if ( ! ( $dbh = zmDbConnect() ) ) {
|
||||
Info('Reconnecting to db');
|
||||
if ( !($dbh = zmDbConnect()) ) {
|
||||
#What we do here is not that important, so just skip this interval
|
||||
sleep($Config{ZM_STATS_UPDATE_INTERVAL});
|
||||
}
|
||||
|
@ -53,7 +51,7 @@ while( 1 ) {
|
|||
sleep($Config{ZM_STATS_UPDATE_INTERVAL});
|
||||
} # end while (1)
|
||||
|
||||
Info("Stats Daemon exiting");
|
||||
Info('Stats Daemon exiting');
|
||||
exit();
|
||||
1;
|
||||
__END__
|
||||
|
|
|
@ -923,10 +923,10 @@ if ( $version ) {
|
|||
die( "Can't find upgrade from version '$version'" );
|
||||
}
|
||||
# Re-enable the privacy popup after each upgrade
|
||||
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 $res = $sth->execute( ) or die( "Can't execute: ".$sth->errstr() );
|
||||
$sth->finish();
|
||||
#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 $res = $sth->execute( ) or die( "Can't execute: ".$sth->errstr() );
|
||||
#$sth->finish();
|
||||
print( "\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n" );
|
||||
}
|
||||
zmDbDisconnect();
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY)
|
||||
|
||||
# Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc)
|
||||
set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_group.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_nvsocket.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp)
|
||||
set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_frame.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_group.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_nvsocket.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp)
|
||||
|
||||
# A fix for cmake recompiling the source files for every target.
|
||||
add_library(zm STATIC ${ZM_BIN_SRC_FILES})
|
||||
|
|
102
src/zm_event.cpp
102
src/zm_event.cpp
|
@ -193,6 +193,9 @@ Event::Event(
|
|||
|
||||
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 */
|
||||
|
||||
if ( monitor->GetOptVideoWriter() != 0 ) {
|
||||
|
@ -239,30 +242,26 @@ Event::~Event() {
|
|||
videowriter = NULL;
|
||||
}
|
||||
|
||||
// Should not be static because we are multi-threaded
|
||||
char sql[ZM_SQL_MED_BUFSIZ];
|
||||
struct DeltaTimeval delta_time;
|
||||
DELTA_TIMEVAL(delta_time, end_time, start_time, DT_PREC_2);
|
||||
Debug(2, "start_time:%d.%d end_time%d.%d", start_time.tv_sec, start_time.tv_usec, end_time.tv_sec, end_time.tv_usec);
|
||||
|
||||
if ( frames > last_db_frame ) {
|
||||
Debug(1, "Adding closing frame %d to DB", frames);
|
||||
snprintf(sql, sizeof(sql),
|
||||
"INSERT INTO Frames ( EventId, FrameId, TimeStamp, Delta ) VALUES ( %" PRIu64 ", %d, from_unixtime( %ld ), %s%ld.%02ld )",
|
||||
id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec);
|
||||
db_mutex.lock();
|
||||
if ( mysql_query(&dbconn, sql) ) {
|
||||
db_mutex.unlock();
|
||||
Error("Can't insert frame: %s", mysql_error(&dbconn));
|
||||
} else {
|
||||
db_mutex.unlock();
|
||||
Debug(1,"Success writing last frame");
|
||||
}
|
||||
frame_data.push(new Frame(id, frames, NORMAL, end_time, delta_time, 0));
|
||||
}
|
||||
if ( frame_data.size() )
|
||||
WriteDbFrames();
|
||||
|
||||
// Should not be static because we might be multi-threaded
|
||||
char sql[ZM_SQL_LGE_BUFSIZ];
|
||||
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,
|
||||
monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id );
|
||||
monitor->EventPrefix(), id, end_time.tv_sec,
|
||||
delta_time.positive?"":"-", delta_time.sec, delta_time.fsec,
|
||||
frames, alarm_frames,
|
||||
tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score,
|
||||
video_name, id );
|
||||
db_mutex.lock();
|
||||
while ( mysql_query(&dbconn, sql) && !zm_terminate ) {
|
||||
db_mutex.unlock();
|
||||
|
@ -378,7 +377,7 @@ void Event::updateNotes( const StringSetMap &newNoteSetMap ) {
|
|||
createNotes( notes );
|
||||
|
||||
Debug( 2, "Updating notes for event %d, '%s'", id, notes.c_str() );
|
||||
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||
static char sql[ZM_SQL_LGE_BUFSIZ];
|
||||
#if USE_PREPARED_SQL
|
||||
static MYSQL_STMT *stmt = 0;
|
||||
|
||||
|
@ -457,9 +456,9 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st
|
|||
|
||||
frames++;
|
||||
|
||||
static char event_file[PATH_MAX];
|
||||
snprintf(event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames);
|
||||
if ( monitor->GetOptSaveJPEGs() & 1 ) {
|
||||
static char event_file[PATH_MAX];
|
||||
snprintf(event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames);
|
||||
Debug(1, "Writing pre-capture frame %d", frames);
|
||||
WriteFrameImage(images[i], *(timestamps[i]), event_file);
|
||||
} else {
|
||||
|
@ -468,8 +467,6 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st
|
|||
// 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.
|
||||
if ( frames == 1 ) {
|
||||
char snapshot_file[PATH_MAX];
|
||||
snprintf(snapshot_file, sizeof(snapshot_file), "%s/snapshot.jpg", path);
|
||||
WriteFrameImage(images[i], *(timestamps[i]), snapshot_file);
|
||||
}
|
||||
}
|
||||
|
@ -506,6 +503,34 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st
|
|||
}
|
||||
}
|
||||
|
||||
void Event::WriteDbFrames() {
|
||||
static char sql[ZM_SQL_LGE_BUFSIZ];
|
||||
char * sql_ptr = (char *)&sql;
|
||||
sql_ptr += snprintf(sql, sizeof(sql),
|
||||
"INSERT INTO Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) VALUES "
|
||||
);
|
||||
while ( frame_data.size() ) {
|
||||
Frame *frame = frame_data.front();
|
||||
frame_data.pop();
|
||||
sql_ptr += snprintf(sql_ptr, sizeof(sql)-(sql_ptr-(char *)&sql), "( %" PRIu64 ", %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d ), ",
|
||||
id, frame->frame_id, frame_type_names[frame->type],
|
||||
frame->timestamp.tv_sec,
|
||||
frame->delta.positive?"":"-",
|
||||
frame->delta.sec,
|
||||
frame->delta.fsec,
|
||||
frame->score);
|
||||
delete frame;
|
||||
}
|
||||
*(sql_ptr-2) = '\0';
|
||||
db_mutex.lock();
|
||||
if ( mysql_query(&dbconn, sql) ) {
|
||||
db_mutex.unlock();
|
||||
Error("Can't insert frames: %s, sql was %s", mysql_error(&dbconn), sql);
|
||||
return;
|
||||
}
|
||||
db_mutex.unlock();
|
||||
} // end void Event::WriteDbFrames()
|
||||
|
||||
void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image) {
|
||||
if ( !timestamp.tv_sec ) {
|
||||
Debug(1, "Not adding new frame, zero timestamp");
|
||||
|
@ -514,31 +539,25 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
|
|||
|
||||
frames++;
|
||||
|
||||
static char event_file[PATH_MAX];
|
||||
snprintf(event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames);
|
||||
|
||||
if ( monitor->GetOptSaveJPEGs() & 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);
|
||||
if ( ! WriteFrameImage(image, timestamp, event_file) ) {
|
||||
Error("Failed to write frame image");
|
||||
}
|
||||
} else {
|
||||
//If this is the first frame, we should add a thumbnail to the event directory
|
||||
if ( frames == 1 || score > (int)max_score ) {
|
||||
char snapshot_file[PATH_MAX];
|
||||
snprintf(snapshot_file, sizeof(snapshot_file), "%s/snapshot.jpg", path);
|
||||
if ( (frames == 1) || (score > (int)max_score) ) {
|
||||
WriteFrameImage(image, timestamp, snapshot_file);
|
||||
}
|
||||
// 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) ) {
|
||||
alarm_frame_written = true;
|
||||
char alarm_file[PATH_MAX];
|
||||
snprintf(alarm_file, sizeof(alarm_file), "%s/alarm.jpg", path);
|
||||
WriteFrameImage(image, timestamp, alarm_file);
|
||||
}
|
||||
}
|
||||
if ( videowriter != NULL ) {
|
||||
Debug(3, "Writing video");
|
||||
WriteFrameVideo(image, timestamp, videowriter);
|
||||
}
|
||||
|
||||
|
@ -552,22 +571,14 @@ Debug(3, "Writing video");
|
|||
|
||||
bool db_frame = ( frame_type != BULK ) || (!frames) || ((frames%config.bulk_frame_interval)==0) ;
|
||||
if ( db_frame ) {
|
||||
|
||||
Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, Event::frame_type_names[frame_type] );
|
||||
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||
snprintf(sql, sizeof(sql),
|
||||
"INSERT INTO Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score )"
|
||||
" VALUES ( %" PRIu64 ", %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )",
|
||||
id, frames, frame_type_names[frame_type], timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score);
|
||||
db_mutex.lock();
|
||||
if ( mysql_query(&dbconn, sql) ) {
|
||||
Error("Can't insert frame: %s", mysql_error(&dbconn));
|
||||
Error("SQL was %s", sql);
|
||||
db_mutex.unlock();
|
||||
return;
|
||||
|
||||
frame_data.push(new Frame(id, frames, frame_type, timestamp, delta_time, score));
|
||||
if ( frame_data.size() > 20 ) {
|
||||
WriteDbFrames();
|
||||
Debug(1, "Adding 20 frames to DB");
|
||||
last_db_frame = frames;
|
||||
}
|
||||
db_mutex.unlock();
|
||||
last_db_frame = frames;
|
||||
|
||||
// We are writing a Bulk frame
|
||||
if ( frame_type == BULK ) {
|
||||
|
@ -590,7 +601,7 @@ Debug(3, "Writing video");
|
|||
db_mutex.lock();
|
||||
}
|
||||
db_mutex.unlock();
|
||||
}
|
||||
} // end if frame_type == BULK
|
||||
} // end if db_frame
|
||||
|
||||
end_time = timestamp;
|
||||
|
@ -605,6 +616,7 @@ Debug(3, "Writing video");
|
|||
|
||||
if ( alarm_image ) {
|
||||
if ( monitor->GetOptSaveJPEGs() & 2 ) {
|
||||
static char event_file[PATH_MAX];
|
||||
snprintf(event_file, sizeof(event_file), staticConfig.analyse_file_format, path, frames);
|
||||
Debug(1, "Writing analysis frame %d", frames);
|
||||
if ( ! WriteFrameImage(alarm_image, timestamp, event_file, true) ) {
|
||||
|
@ -612,7 +624,7 @@ Debug(3, "Writing video");
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end if frame_type == ALARM
|
||||
|
||||
/* This makes viewing the diagnostic images impossible because it keeps deleting them
|
||||
if ( config.record_diag_images ) {
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
|
||||
#include "zm.h"
|
||||
#include "zm_image.h"
|
||||
|
@ -45,7 +46,10 @@ class Monitor;
|
|||
class EventStream;
|
||||
|
||||
#define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored
|
||||
typedef uint64_t event_id_t;
|
||||
typedef enum { NORMAL=0, BULK, ALARM } FrameType;
|
||||
|
||||
#include "zm_frame.h"
|
||||
//
|
||||
// Class describing events, i.e. captured periods of activity.
|
||||
//
|
||||
|
@ -60,7 +64,6 @@ class Event {
|
|||
typedef std::map<std::string,StringSet> StringSetMap;
|
||||
|
||||
protected:
|
||||
typedef enum { NORMAL=0, BULK, ALARM } FrameType;
|
||||
static const char * frame_type_names[3];
|
||||
|
||||
struct PreAlarmData {
|
||||
|
@ -69,6 +72,7 @@ class Event {
|
|||
unsigned int score;
|
||||
Image *alarm_frame;
|
||||
};
|
||||
std::queue<Frame*> frame_data;
|
||||
|
||||
static int pre_alarm_count;
|
||||
static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES];
|
||||
|
@ -86,6 +90,8 @@ class Event {
|
|||
unsigned int tot_score;
|
||||
unsigned int max_score;
|
||||
char path[PATH_MAX];
|
||||
char snapshot_file[PATH_MAX];
|
||||
char alarm_file[PATH_MAX];
|
||||
VideoWriter* videowriter;
|
||||
FILE* timecodes_fd;
|
||||
char video_name[PATH_MAX];
|
||||
|
@ -125,6 +131,7 @@ class Event {
|
|||
|
||||
private:
|
||||
void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps );
|
||||
void WriteDbFrames();
|
||||
|
||||
public:
|
||||
static const char *getSubPath( struct tm *time ) {
|
||||
|
|
|
@ -71,8 +71,9 @@ bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) {
|
|||
|
||||
if ( event_time ) {
|
||||
curr_stream_time = event_time;
|
||||
curr_frame_id = 1;
|
||||
curr_frame_id = 1; // curr_frame_id is 1-based
|
||||
if ( event_time >= event_data->start_time ) {
|
||||
Debug(2, "event time is after event start");
|
||||
for (unsigned int i = 0; i < event_data->frame_count; i++ ) {
|
||||
//Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time );
|
||||
if ( event_data->frames[i].timestamp >= event_time ) {
|
||||
|
@ -108,7 +109,10 @@ bool EventStream::loadInitialEventData( uint64_t init_event_id, unsigned int ini
|
|||
bool EventStream::loadEventData(uint64_t event_id) {
|
||||
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||
|
||||
snprintf(sql, sizeof(sql), "SELECT MonitorId, StorageId, Frames, unix_timestamp( StartTime ) AS StartTimestamp, (SELECT max(Delta)-min(Delta) FROM Frames WHERE EventId=Events.Id) AS Duration, DefaultVideo, Scheme FROM Events WHERE Id = %" PRIu64, event_id);
|
||||
snprintf(sql, sizeof(sql),
|
||||
"SELECT MonitorId, StorageId, Frames, unix_timestamp( StartTime ) AS StartTimestamp, "
|
||||
"(SELECT max(Delta)-min(Delta) FROM Frames WHERE EventId=Events.Id) AS Duration, "
|
||||
"DefaultVideo, Scheme, SaveJPEGs FROM Events WHERE Id = %" PRIu64, event_id);
|
||||
|
||||
if ( mysql_query(&dbconn, sql) ) {
|
||||
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||
|
@ -150,6 +154,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
} else {
|
||||
event_data->scheme = Storage::SHALLOW;
|
||||
}
|
||||
event_data->SaveJPEGs = dbrow[7] == NULL ? 0 : atoi(dbrow[7]);
|
||||
mysql_free_result( result );
|
||||
|
||||
Storage * storage = new Storage(event_data->storage_id);
|
||||
|
@ -197,8 +202,9 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
delete storage; storage = NULL;
|
||||
|
||||
updateFrameRate((double)event_data->frame_count/event_data->duration);
|
||||
Debug(3,"fps set by frame_count(%d)/duration(%f)", event_data->frame_count, event_data->duration);
|
||||
|
||||
snprintf(sql, sizeof(sql), "SELECT FrameId, unix_timestamp( `TimeStamp` ), Delta FROM Frames WHERE EventId = %" PRIu64 " ORDER BY FrameId ASC", event_id);
|
||||
snprintf(sql, sizeof(sql), "SELECT FrameId, unix_timestamp(`TimeStamp`), Delta FROM Frames WHERE EventId = %" PRIu64 " ORDER BY FrameId ASC", event_id);
|
||||
if ( mysql_query(&dbconn, sql) ) {
|
||||
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||
exit(mysql_errno(&dbconn));
|
||||
|
@ -214,33 +220,50 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
|
||||
event_data->frames = new FrameData[event_data->frame_count];
|
||||
int last_id = 0;
|
||||
time_t timestamp, last_timestamp = event_data->start_time;
|
||||
double last_timestamp = event_data->start_time;
|
||||
double last_delta = 0.0;
|
||||
|
||||
while ( ( dbrow = mysql_fetch_row( result ) ) ) {
|
||||
int id = atoi(dbrow[0]);
|
||||
timestamp = atoi(dbrow[1]);
|
||||
//timestamp = atof(dbrow[1]);
|
||||
double delta = atof(dbrow[2]);
|
||||
int id_diff = id - last_id;
|
||||
double frame_delta = id_diff ? (delta-last_delta)/id_diff : 0;
|
||||
double frame_delta = id_diff ? (delta-last_delta)/id_diff : (delta-last_delta);
|
||||
// Fill in data between bulk frames
|
||||
if ( id_diff > 1 ) {
|
||||
for ( int i = last_id+1; i < id; i++ ) {
|
||||
event_data->frames[i-1].timestamp = (time_t)(last_timestamp + ((i-last_id)*frame_delta));
|
||||
event_data->frames[i-1].offset = (time_t)(event_data->frames[i-1].timestamp-event_data->start_time);
|
||||
// Delta is the time since last frame, no since beginning of Event
|
||||
event_data->frames[i-1].delta = frame_delta;
|
||||
event_data->frames[i-1].timestamp = last_timestamp + ((i-last_id)*frame_delta);
|
||||
event_data->frames[i-1].offset = event_data->frames[i-1].timestamp - event_data->start_time;
|
||||
event_data->frames[i-1].in_db = false;
|
||||
Debug(3,"Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)",
|
||||
i,
|
||||
event_data->frames[i-1].timestamp,
|
||||
event_data->frames[i-1].offset,
|
||||
event_data->frames[i-1].delta,
|
||||
event_data->frames[i-1].in_db
|
||||
);
|
||||
}
|
||||
}
|
||||
event_data->frames[id-1].timestamp = timestamp;
|
||||
event_data->frames[id-1].offset = (time_t)(event_data->frames[id-1].timestamp-event_data->start_time);
|
||||
event_data->frames[id-1].delta = id>1?frame_delta:0.0;
|
||||
event_data->frames[id-1].timestamp = event_data->start_time + delta;
|
||||
event_data->frames[id-1].offset = delta;
|
||||
event_data->frames[id-1].delta = frame_delta;
|
||||
event_data->frames[id-1].in_db = true;
|
||||
last_id = id;
|
||||
last_delta = delta;
|
||||
last_timestamp = timestamp;
|
||||
last_timestamp = event_data->frames[id-1].timestamp;
|
||||
Debug(4,"Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)",
|
||||
id,
|
||||
event_data->frames[id-1].timestamp,
|
||||
event_data->frames[id-1].offset,
|
||||
event_data->frames[id-1].delta,
|
||||
event_data->frames[id-1].in_db
|
||||
);
|
||||
}
|
||||
if ( mysql_errno( &dbconn ) ) {
|
||||
Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
Error("Can't fetch row: %s", mysql_error(&dbconn));
|
||||
exit(mysql_errno(&dbconn));
|
||||
}
|
||||
|
||||
mysql_free_result(result);
|
||||
|
@ -252,6 +275,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
if ( event_data->video_file[0] ) {
|
||||
char filepath[PATH_MAX];
|
||||
snprintf(filepath, sizeof(filepath), "%s/%s", event_data->path, event_data->video_file);
|
||||
Debug(1, "Loading video file from %s", filepath);
|
||||
ffmpeg_input = new FFmpeg_Input();
|
||||
if ( 0 > ffmpeg_input->Open( filepath ) ) {
|
||||
Warning("Unable to open ffmpeg_input %s/%s", event_data->path, event_data->video_file);
|
||||
|
@ -331,6 +355,7 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
|||
replay_rate = 50 * ZM_RATE_BASE;
|
||||
break;
|
||||
default :
|
||||
Debug(1,"Defaulting replay_rate to 2*ZM_RATE_BASE because it is %d", replay_rate);
|
||||
replay_rate = 2 * ZM_RATE_BASE;
|
||||
break;
|
||||
}
|
||||
|
@ -485,11 +510,12 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
|||
DataMsg status_msg;
|
||||
status_msg.msg_type = MSG_DATA_EVENT;
|
||||
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 ( errno != EAGAIN )
|
||||
{
|
||||
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
|
||||
|
@ -497,7 +523,7 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
|||
exit(0);
|
||||
|
||||
updateFrameRate((double)event_data->frame_count/event_data->duration);
|
||||
}
|
||||
} // void EventStream::processCommand(const CmdMsg *msg)
|
||||
|
||||
void EventStream::checkEventLoaded() {
|
||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
||||
|
@ -508,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);
|
||||
} else {
|
||||
// No event change required
|
||||
//Debug(3, "No event change required");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -579,14 +606,17 @@ bool EventStream::sendFrame( int delta_us ) {
|
|||
|
||||
// This needs to be abstracted. If we are saving jpgs, then load the capture file. If we are only saving analysis frames, then send that.
|
||||
// // This is also wrong, need to have this info stored in the event! FIXME
|
||||
if ( monitor->GetOptSaveJPEGs() & 1 ) {
|
||||
if ( event_data->SaveJPEGs & 1 ) {
|
||||
snprintf(filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id);
|
||||
} else if ( monitor->GetOptSaveJPEGs() & 2 ) {
|
||||
} else if ( event_data->SaveJPEGs & 2 ) {
|
||||
snprintf(filepath, sizeof(filepath), staticConfig.analyse_file_format, event_data->path, curr_frame_id);
|
||||
if ( stat(filepath, &filestat) < 0 ) {
|
||||
if ( stat(filepath, &filestat ) < 0 ) {
|
||||
Debug(1, "analyze file %s not found will try to stream from other", filepath);
|
||||
snprintf(filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id);
|
||||
filepath[0] = 0;
|
||||
if ( stat(filepath, &filestat ) < 0 ) {
|
||||
Debug(1, "capture file %s not found either", filepath);
|
||||
filepath[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} else if ( !ffmpeg_input ) {
|
||||
|
@ -645,10 +675,12 @@ Debug(1, "Loading image");
|
|||
} else if ( ffmpeg_input ) {
|
||||
// Get the frame from the mp4 input
|
||||
Debug(1,"Getting frame from ffmpeg");
|
||||
AVFrame *frame = ffmpeg_input->get_frame(ffmpeg_input->get_video_stream_id());
|
||||
AVFrame *frame;
|
||||
FrameData *frame_data = &event_data->frames[curr_frame_id-1];
|
||||
frame = ffmpeg_input->get_frame( ffmpeg_input->get_video_stream_id(), frame_data->offset );
|
||||
if ( frame ) {
|
||||
image = new Image(frame);
|
||||
av_frame_free(&frame);
|
||||
//av_frame_free(&frame);
|
||||
} else {
|
||||
Error("Failed getting a frame.");
|
||||
return false;
|
||||
|
@ -754,24 +786,31 @@ void EventStream::runStream() {
|
|||
|
||||
Debug(3, "frame rate is: (%f)", (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 ) {
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
unsigned int delta_us = 0;
|
||||
int delta_us = 0;
|
||||
send_frame = false;
|
||||
|
||||
if ( connkey ) {
|
||||
// commands may set send_frame to true
|
||||
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.
|
||||
if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) {
|
||||
touch(sock_path_lock);
|
||||
last_comm_update = now;
|
||||
}
|
||||
} else {
|
||||
Debug(1, "Not checking command queue");
|
||||
}
|
||||
|
||||
if ( step != 0 )
|
||||
|
@ -787,6 +826,7 @@ void EventStream::runStream() {
|
|||
//Info( "cfid:%d", curr_frame_id );
|
||||
//Info( "fdt:%d", frame_data->timestamp );
|
||||
if ( !paused ) {
|
||||
Debug(3,"Not paused");
|
||||
bool in_event = true;
|
||||
double time_to_event = 0;
|
||||
if ( replay_rate > 0 ) {
|
||||
|
@ -809,6 +849,7 @@ void EventStream::runStream() {
|
|||
}
|
||||
//else
|
||||
//{
|
||||
Debug(2,"Sleeping because paused");
|
||||
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 += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000);
|
||||
|
@ -817,17 +858,23 @@ void EventStream::runStream() {
|
|||
} // end if !in_event
|
||||
|
||||
// Figure out if we should send this frame
|
||||
|
||||
Debug(3,"cur_frame_id (%d-1) mod frame_mod(%d)",curr_frame_id, frame_mod);
|
||||
// If we are streaming and this frame is due to be sent
|
||||
if ( ((curr_frame_id-1)%frame_mod) == 0 ) {
|
||||
// frame mod defaults to 1 and if we are going faster than max_fps will get multiplied by 2
|
||||
// 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) ) {
|
||||
delta_us = (unsigned int)(frame_data->delta * 1000000);
|
||||
Debug(3,"frame delta %uus ", delta_us);
|
||||
// if effective > base we should speed up frame delivery
|
||||
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);
|
||||
// but must not exceed 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;
|
||||
}
|
||||
} else if ( step != 0 ) {
|
||||
Debug(2,"Paused with step");
|
||||
// We are paused and are just stepping forward or backward one frame
|
||||
step = 0;
|
||||
send_frame = true;
|
||||
|
@ -838,28 +885,76 @@ void EventStream::runStream() {
|
|||
// Send keepalive
|
||||
Debug(2, "Sending keepalive frame");
|
||||
send_frame = true;
|
||||
//} else {
|
||||
//Debug(2, "Not Sending keepalive frame");
|
||||
}
|
||||
} // end if streaming stepping or doing nothing
|
||||
|
||||
if ( send_frame )
|
||||
if ( send_frame ) {
|
||||
//Debug(3,"sending frame");
|
||||
if ( !sendFrame(delta_us) )
|
||||
zm_terminate = true;
|
||||
//} else {
|
||||
//Debug(3,"Not sending frame");
|
||||
}
|
||||
|
||||
curr_stream_time = frame_data->timestamp;
|
||||
|
||||
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) ) {
|
||||
Debug(2, "Have mode==MODE_SINGLE and at end of event, looping back to start");
|
||||
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 ) {
|
||||
Debug(3, "dUs: %d", delta_us);
|
||||
if ( delta_us )
|
||||
if ( delta_us > 0 ) {
|
||||
Debug( 3, "dUs: %d", delta_us );
|
||||
usleep(delta_us);
|
||||
Debug(3, "Done sleeping: %d usec", delta_us);
|
||||
}
|
||||
}
|
||||
} 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))));
|
||||
} else {
|
||||
//Error("Not sleeping!");
|
||||
usleep(100000);
|
||||
}
|
||||
}
|
||||
} // end while ! zm_terminate
|
||||
#if HAVE_LIBAVCODEC
|
||||
|
|
|
@ -47,8 +47,8 @@ class EventStream : public StreamBase {
|
|||
protected:
|
||||
struct FrameData {
|
||||
//unsigned long id;
|
||||
time_t timestamp;
|
||||
time_t offset;
|
||||
double timestamp;
|
||||
double offset;
|
||||
double delta;
|
||||
bool in_db;
|
||||
};
|
||||
|
@ -65,6 +65,7 @@ class EventStream : public StreamBase {
|
|||
FrameData *frames;
|
||||
char video_file[PATH_MAX];
|
||||
Storage::Schemes scheme;
|
||||
int SaveJPEGs;
|
||||
};
|
||||
|
||||
protected:
|
||||
|
@ -78,6 +79,7 @@ class EventStream : public StreamBase {
|
|||
int curr_frame_id;
|
||||
double curr_stream_time;
|
||||
bool send_frame;
|
||||
struct timeval start; // clock time when started the event
|
||||
|
||||
EventData *event_data;
|
||||
FFmpeg_Input *ffmpeg_input;
|
||||
|
@ -94,6 +96,7 @@ class EventStream : public StreamBase {
|
|||
public:
|
||||
EventStream() {
|
||||
mode = DEFAULT_MODE;
|
||||
replay_rate = DEFAULT_RATE;
|
||||
|
||||
forceEventChange = false;
|
||||
|
||||
|
@ -108,7 +111,6 @@ class EventStream : public StreamBase {
|
|||
input_codec = 0;
|
||||
|
||||
ffmpeg_input = NULL;
|
||||
|
||||
}
|
||||
void setStreamStart( uint64_t init_event_id, unsigned int init_frame_id );
|
||||
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() {
|
||||
static bool bInit = false;
|
||||
|
||||
if ( !bInit ) {
|
||||
if ( logDebugging() )
|
||||
|
@ -77,12 +78,20 @@ void FFMPEGInit() {
|
|||
av_log_set_callback(log_libav_callback);
|
||||
else
|
||||
Info("Not enabling ffmpeg logs, as LOG_FFMPEG is disabled in options");
|
||||
#if LIBAVFORMAT_VERSION_CHECK(58, 9, 0, 64, 0)
|
||||
#else
|
||||
av_register_all();
|
||||
#endif
|
||||
avformat_network_init();
|
||||
bInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
void FFMPEGDeInit() {
|
||||
avformat_network_deinit();
|
||||
bInit = false;
|
||||
}
|
||||
|
||||
#if HAVE_LIBAVUTIL
|
||||
enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder) {
|
||||
enum _AVPIXELFORMAT pf;
|
||||
|
@ -275,6 +284,26 @@ static void zm_log_fps(double d, const char *postfix) {
|
|||
}
|
||||
}
|
||||
|
||||
void zm_dump_frame(const AVFrame *frame,const char *text) {
|
||||
Debug(1, "%s: format %d %s sample_rate %" PRIu32 " nb_samples %d channels %d"
|
||||
" duration %" PRId64
|
||||
" layout %d pts %" PRId64,
|
||||
text,
|
||||
frame->format,
|
||||
av_get_sample_fmt_name((AVSampleFormat)frame->format),
|
||||
frame->sample_rate,
|
||||
frame->nb_samples,
|
||||
#if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100)
|
||||
frame->channels,
|
||||
frame->pkt_duration,
|
||||
#else
|
||||
0, 0,
|
||||
#endif
|
||||
frame->channel_layout,
|
||||
frame->pts
|
||||
);
|
||||
}
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
void zm_dump_codecpar ( const AVCodecParameters *par ) {
|
||||
Debug(1, "Dumping codecpar codec_type(%d) codec_id(%d) codec_tag(%d) width(%d) height(%d) bit_rate(%d) format(%d = %s)",
|
||||
|
@ -313,6 +342,11 @@ void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output)
|
|||
int flags = (is_output ? ic->oformat->flags : ic->iformat->flags);
|
||||
AVStream *st = ic->streams[i];
|
||||
AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0);
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
AVCodecParameters *codec = st->codecpar;
|
||||
#else
|
||||
AVCodecContext *codec = st->codec;
|
||||
#endif
|
||||
|
||||
Debug(1, " Stream #%d:%d", index, i);
|
||||
|
||||
|
@ -321,23 +355,20 @@ void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output)
|
|||
if (flags & AVFMT_SHOW_IDS)
|
||||
Debug(1, "ids [0x%x]", st->id);
|
||||
if (lang)
|
||||
Debug(1, "lang:%s", lang->value);
|
||||
Debug(1, "frames:%d, stream timebase: %d/%d codec timebase: %d/%d",
|
||||
st->codec_info_nb_frames, st->time_base.num, st->time_base.den,
|
||||
Debug(1, "language (%s)", lang->value);
|
||||
Debug(1, "frames:%d, frame_size:%d stream timebase: %d/%d codec timebase: %d/%d",
|
||||
st->codec_info_nb_frames, codec->frame_size, st->time_base.num, st->time_base.den,
|
||||
st->codec->time_base.num, st->codec->time_base.den
|
||||
);
|
||||
avcodec_string(buf, sizeof(buf), st->codec, is_output);
|
||||
Debug(1, "codec: %s", buf);
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
AVCodecParameters *codec = st->codecpar;
|
||||
#else
|
||||
AVCodecContext *codec = st->codec;
|
||||
#endif
|
||||
|
||||
if (st->sample_aspect_ratio.num && // default
|
||||
av_cmp_q(st->sample_aspect_ratio, codec->sample_aspect_ratio)) {
|
||||
if ( st->sample_aspect_ratio.num && // default
|
||||
av_cmp_q(st->sample_aspect_ratio, codec->sample_aspect_ratio)
|
||||
) {
|
||||
AVRational display_aspect_ratio;
|
||||
av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
|
||||
av_reduce(&display_aspect_ratio.num,
|
||||
&display_aspect_ratio.den,
|
||||
codec->width * (int64_t)st->sample_aspect_ratio.num,
|
||||
codec->height * (int64_t)st->sample_aspect_ratio.den,
|
||||
1024 * 1024);
|
||||
|
@ -346,7 +377,7 @@ void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output)
|
|||
display_aspect_ratio.num, display_aspect_ratio.den);
|
||||
}
|
||||
|
||||
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
if ( st->codec->codec_type == AVMEDIA_TYPE_VIDEO ) {
|
||||
int fps = st->avg_frame_rate.den && st->avg_frame_rate.num;
|
||||
int tbn = st->time_base.den && st->time_base.num;
|
||||
int tbc = st->codec->time_base.den && st->codec->time_base.num;
|
||||
|
@ -403,6 +434,10 @@ unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src ) {
|
|||
av_new_packet(dst,src->size);
|
||||
memcpy(dst->data, src->data, src->size);
|
||||
dst->flags = src->flags;
|
||||
dst->pts = src->pts;
|
||||
dst->dts = src->dts;
|
||||
dst->duration = src->duration;
|
||||
dst->stream_index = src->stream_index;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -422,6 +457,14 @@ bool is_video_stream( AVStream * stream ) {
|
|||
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 ) {
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
|
@ -438,6 +481,15 @@ bool is_audio_stream( AVStream * stream ) {
|
|||
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 ret;
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
|
@ -472,21 +524,31 @@ int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet
|
|||
# else
|
||||
int frameComplete = 0;
|
||||
while ( !frameComplete ) {
|
||||
if ( (ret = zm_avcodec_decode_video( context, frame, &frameComplete, &packet )) < 0 ) {
|
||||
Error( "Unable to decode frame at frame: %s, continuing",
|
||||
av_make_error_string(ret).c_str() );
|
||||
if ( is_video_context(context) ) {
|
||||
ret = zm_avcodec_decode_video(context, frame, &frameComplete, &packet);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
} // end while !frameComplete
|
||||
#endif
|
||||
return 1;
|
||||
} // end int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet )
|
||||
void dumpPacket(AVPacket *pkt, const char *text) {
|
||||
|
||||
void dumpPacket(AVStream *stream, AVPacket *pkt, const char *text) {
|
||||
char b[10240];
|
||||
|
||||
double pts_time = (double)av_rescale_q(pkt->pts,
|
||||
stream->time_base,
|
||||
AV_TIME_BASE_Q
|
||||
) / AV_TIME_BASE;
|
||||
|
||||
snprintf(b, sizeof(b),
|
||||
" pts: %" PRId64 ", dts: %" PRId64
|
||||
", data: %p, size: %d, stream_index: %d, flags: %04x, keyframe(%d) pos: %" PRId64
|
||||
" pts: %" PRId64 "=%f, dts: %" PRId64
|
||||
", size: %d, stream_index: %d, flags: %04x, keyframe(%d) pos: %" PRId64
|
||||
", duration: %"
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
PRId64
|
||||
|
@ -495,8 +557,8 @@ void dumpPacket(AVPacket *pkt, const char *text) {
|
|||
#endif
|
||||
"\n",
|
||||
pkt->pts,
|
||||
pts_time,
|
||||
pkt->dts,
|
||||
pkt->data,
|
||||
pkt->size,
|
||||
pkt->stream_index,
|
||||
pkt->flags,
|
||||
|
|
|
@ -199,6 +199,7 @@ extern "C" {
|
|||
|
||||
/* A single function to initialize ffmpeg, to avoid multiple initializations */
|
||||
void FFMPEGInit();
|
||||
void FFMPEGDeInit();
|
||||
|
||||
#if HAVE_LIBAVUTIL
|
||||
enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder);
|
||||
|
@ -294,10 +295,11 @@ static av_always_inline av_const int64_t av_clip64_c(int64_t a, int64_t amin, in
|
|||
#endif
|
||||
|
||||
void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output);
|
||||
void zm_dump_codec ( const AVCodecContext *codec );
|
||||
void zm_dump_codec(const AVCodecContext *codec);
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
void zm_dump_codecpar ( const AVCodecParameters *par );
|
||||
void zm_dump_codecpar(const AVCodecParameters *par);
|
||||
#endif
|
||||
void zm_dump_frame(const AVFrame *frame, const char *text="Frame");
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100)
|
||||
#define zm_av_packet_unref( packet ) av_packet_unref( packet )
|
||||
|
@ -324,8 +326,11 @@ void zm_dump_codecpar ( const AVCodecParameters *par );
|
|||
|
||||
int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt);
|
||||
|
||||
bool is_video_stream( AVStream * stream );
|
||||
bool is_audio_stream( AVStream * stream );
|
||||
bool is_video_stream(AVStream *);
|
||||
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 );
|
||||
void dumpPacket(AVPacket *,const char *text="DEBUG");
|
||||
void dumpPacket(AVStream *, AVPacket *,const char *text="");
|
||||
#endif // ZM_FFMPEG_H
|
||||
|
|
|
@ -82,8 +82,35 @@ static AVPixelFormat get_format(AVCodecContext *avctx, const enum AVPixelFormat
|
|||
}
|
||||
#endif
|
||||
|
||||
FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
|
||||
Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ),
|
||||
FfmpegCamera::FfmpegCamera(
|
||||
int p_id,
|
||||
const std::string &p_path,
|
||||
const std::string &p_method,
|
||||
const std::string &p_options,
|
||||
int p_width,
|
||||
int p_height,
|
||||
int p_colours,
|
||||
int p_brightness,
|
||||
int p_contrast,
|
||||
int p_hue,
|
||||
int p_colour,
|
||||
bool p_capture,
|
||||
bool p_record_audio
|
||||
) :
|
||||
Camera(
|
||||
p_id,
|
||||
FFMPEG_SRC,
|
||||
p_width,
|
||||
p_height,
|
||||
p_colours,
|
||||
ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours),
|
||||
p_brightness,
|
||||
p_contrast,
|
||||
p_hue,
|
||||
p_colour,
|
||||
p_capture,
|
||||
p_record_audio
|
||||
),
|
||||
mPath( p_path ),
|
||||
mMethod( p_method ),
|
||||
mOptions( p_options )
|
||||
|
@ -113,6 +140,8 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
|
|||
videoStore = NULL;
|
||||
video_last_pts = 0;
|
||||
have_video_keyframe = false;
|
||||
packetqueue = NULL;
|
||||
error_count = 0;
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
mConvertContext = NULL;
|
||||
|
@ -134,16 +163,12 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
|
|||
|
||||
FfmpegCamera::~FfmpegCamera() {
|
||||
|
||||
if ( videoStore ) {
|
||||
delete videoStore;
|
||||
videoStore = NULL;
|
||||
}
|
||||
Close();
|
||||
|
||||
if ( capture ) {
|
||||
Terminate();
|
||||
}
|
||||
avformat_network_deinit();
|
||||
FFMPEGDeInit();
|
||||
}
|
||||
|
||||
void FfmpegCamera::Initialise() {
|
||||
|
@ -155,12 +180,12 @@ void FfmpegCamera::Terminate() {
|
|||
|
||||
int FfmpegCamera::PrimeCapture() {
|
||||
if ( mCanCapture ) {
|
||||
Info( "Priming capture from %s, CLosing", mPath.c_str() );
|
||||
Info("Priming capture from %s, Closing", mPath.c_str());
|
||||
Close();
|
||||
}
|
||||
mVideoStreamId = -1;
|
||||
mAudioStreamId = -1;
|
||||
Info( "Priming capture from %s", mPath.c_str() );
|
||||
Info("Priming capture from %s", mPath.c_str());
|
||||
|
||||
return OpenFfmpeg();
|
||||
}
|
||||
|
@ -170,7 +195,7 @@ int FfmpegCamera::PreCapture() {
|
|||
if ( ! mCanCapture )
|
||||
return OpenFfmpeg();
|
||||
// Nothing to do here
|
||||
return( 0 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FfmpegCamera::Capture( Image &image ) {
|
||||
|
@ -310,6 +335,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
int ret;
|
||||
|
||||
have_video_keyframe = false;
|
||||
error_count = 0;
|
||||
|
||||
// Open the input, not necessarily a file
|
||||
#if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0)
|
||||
|
@ -336,6 +362,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
} else {
|
||||
Warning("Unknown method (%s)", method.c_str() );
|
||||
}
|
||||
//#av_dict_set(&opts, "timeout", "10000000", 0); // in microseconds.
|
||||
|
||||
if ( ret < 0 ) {
|
||||
Warning("Could not set rtsp_transport method '%s'\n", method.c_str());
|
||||
|
@ -437,6 +464,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
|
||||
Debug(3, "Found video stream at index %d", mVideoStreamId);
|
||||
Debug(3, "Found audio stream at index %d", mAudioStreamId);
|
||||
packetqueue = new zm_packetqueue( mVideoStreamId > mAudioStreamId ? mVideoStreamId : mAudioStreamId );
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
//mVideoCodecContext = avcodec_alloc_context3(NULL);
|
||||
|
@ -537,15 +565,23 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
Debug(1, "HWACCEL not in use");
|
||||
}
|
||||
if ( mAudioStreamId >= 0 ) {
|
||||
if ( (mAudioCodec = avcodec_find_decoder(
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
mAudioCodecContext = avcodec_alloc_context3( NULL );
|
||||
avcodec_parameters_to_context( mAudioCodecContext, mFormatContext->streams[mAudioStreamId]->codecpar );
|
||||
mFormatContext->streams[mAudioStreamId]->codecpar->codec_id
|
||||
#else
|
||||
mAudioCodecContext = mFormatContext->streams[mAudioStreamId]->codec;
|
||||
mFormatContext->streams[mAudioStreamId]->codec->codec_id
|
||||
#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());
|
||||
} 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");
|
||||
zm_dump_stream_format(mFormatContext, mAudioStreamId, 0, 0);
|
||||
// Open the codec
|
||||
|
@ -556,7 +592,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
Debug ( 1, "Calling avcodec_open2" );
|
||||
if ( avcodec_open2(mAudioCodecContext, mAudioCodec, 0) < 0 ) {
|
||||
#endif
|
||||
Error( "Unable to open codec for video stream from %s", mPath.c_str() );
|
||||
Error( "Unable to open codec for audio stream from %s", mPath.c_str() );
|
||||
return -1;
|
||||
}
|
||||
Debug(2, "Opened audio codec");
|
||||
|
@ -647,6 +683,11 @@ int FfmpegCamera::Close() {
|
|||
}
|
||||
#endif
|
||||
|
||||
if ( videoStore ) {
|
||||
delete videoStore;
|
||||
videoStore = NULL;
|
||||
}
|
||||
|
||||
if ( mVideoCodecContext ) {
|
||||
avcodec_close(mVideoCodecContext);
|
||||
Debug(1,"After codec close");
|
||||
|
@ -672,9 +713,9 @@ int FfmpegCamera::Close() {
|
|||
mFormatContext = NULL;
|
||||
}
|
||||
|
||||
if ( videoStore ) {
|
||||
delete videoStore;
|
||||
videoStore = NULL;
|
||||
if ( packetqueue ) {
|
||||
delete packetqueue;
|
||||
packetqueue = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -708,19 +749,33 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
|
|||
return -1;
|
||||
}
|
||||
|
||||
if ( (packet.pts != AV_NOPTS_VALUE) && (packet.pts < -100000) ) {
|
||||
// Ignore packets that have crazy negative pts. They aren't supposed to happen.
|
||||
Warning("Ignore packet because pts %" PRId64 " is massively negative. Error count is %d", packet.pts, error_count);
|
||||
dumpPacket(mFormatContext->streams[packet.stream_index], &packet,"Ignored packet");
|
||||
if ( error_count > 100 ) {
|
||||
Error("Bad packet count over 100, going to close and re-open stream");
|
||||
return -1;
|
||||
}
|
||||
error_count += 1;
|
||||
continue;
|
||||
}
|
||||
// If we get a good frame, decrease the error count.. We could zero it...
|
||||
if ( error_count > 0 ) error_count -= 1;
|
||||
|
||||
int keyframe = packet.flags & AV_PKT_FLAG_KEY;
|
||||
bytes += packet.size;
|
||||
dumpPacket(&packet);
|
||||
dumpPacket(mFormatContext->streams[packet.stream_index], &packet, "Captured Packet");
|
||||
|
||||
//Video recording
|
||||
// Video recording
|
||||
if ( recording.tv_sec ) {
|
||||
|
||||
uint32_t last_event_id = monitor->GetLastEventId() ;
|
||||
uint32_t last_event_id = monitor->GetLastEventId();
|
||||
uint32_t video_writer_event_id = monitor->GetVideoWriterEventId();
|
||||
|
||||
if ( last_event_id != video_writer_event_id ) {
|
||||
Debug(2, "Have change of event. last_event(%d), our current (%d)",
|
||||
last_event_id, video_writer_event_id);
|
||||
last_event_id, video_writer_event_id);
|
||||
|
||||
if ( videoStore ) {
|
||||
Info("Re-starting video storage module");
|
||||
|
@ -787,63 +842,71 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
|
|||
ZMPacket *queued_packet;
|
||||
|
||||
// Clear all packets that predate the moment when the recording began
|
||||
packetqueue.clear_unwanted_packets( &recording, mVideoStreamId );
|
||||
packetqueue->clear_unwanted_packets( &recording, mVideoStreamId );
|
||||
|
||||
while ( ( queued_packet = packetqueue.popPacket() ) ) {
|
||||
while ( ( queued_packet = packetqueue->popPacket() ) ) {
|
||||
AVPacket *avp = queued_packet->av_packet();
|
||||
|
||||
packet_count += 1;
|
||||
//Write the packet to our video store
|
||||
Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, packetqueue.size() );
|
||||
Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)",
|
||||
avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, packetqueue->size());
|
||||
if ( avp->stream_index == mVideoStreamId ) {
|
||||
ret = videoStore->writeVideoFramePacket( avp );
|
||||
have_video_keyframe = true;
|
||||
} else if ( avp->stream_index == mAudioStreamId ) {
|
||||
ret = videoStore->writeAudioFramePacket( avp );
|
||||
} else {
|
||||
Warning("Unknown stream id in queued packet (%d)", avp->stream_index );
|
||||
Warning("Unknown stream id in queued packet (%d)", avp->stream_index);
|
||||
ret = -1;
|
||||
}
|
||||
if ( ret < 0 ) {
|
||||
//Less than zero and we skipped a frame
|
||||
// Less than zero and we skipped a frame
|
||||
}
|
||||
delete queued_packet;
|
||||
} // end while packets in the packetqueue
|
||||
Debug(2, "Wrote %d queued packets", packet_count );
|
||||
Debug(2, "Wrote %d queued packets", packet_count);
|
||||
}
|
||||
} // end if ! was recording
|
||||
|
||||
} else {
|
||||
// Not recording
|
||||
|
||||
if ( videoStore ) {
|
||||
Info("Deleting videoStore instance");
|
||||
Debug(1,"Deleting videoStore instance");
|
||||
delete videoStore;
|
||||
videoStore = NULL;
|
||||
have_video_keyframe = false;
|
||||
monitor->SetVideoWriterEventId(0);
|
||||
}
|
||||
|
||||
// Buffer video packets, since we are not recording.
|
||||
// All audio packets are keyframes, so only if it's a video keyframe
|
||||
if ( packet.stream_index == mVideoStreamId ) {
|
||||
if ( keyframe ) {
|
||||
Debug(3, "Clearing queue");
|
||||
packetqueue.clearQueue(monitor->GetPreEventCount(), mVideoStreamId);
|
||||
packetqueue.queuePacket(&packet);
|
||||
} else if ( packetqueue.size() ) {
|
||||
// it's a keyframe or we already have something in the queue
|
||||
packetqueue.queuePacket(&packet);
|
||||
}
|
||||
} else if ( packet.stream_index == mAudioStreamId ) {
|
||||
// The following lines should ensure that the queue always begins with a video keyframe
|
||||
//Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", record_audio, packetqueue.size() );
|
||||
if ( record_audio && packetqueue.size() ) {
|
||||
// if it's audio, and we are doing audio, and there is already something in the queue
|
||||
packetqueue.queuePacket(&packet);
|
||||
}
|
||||
}
|
||||
} // end if recording or not
|
||||
|
||||
// Buffer video packets, since we are not recording.
|
||||
// All audio packets are keyframes, so only if it's a video keyframe
|
||||
if ( packet.stream_index == mVideoStreamId ) {
|
||||
if ( keyframe ) {
|
||||
Debug(3, "Clearing queue");
|
||||
if ( packetqueue->packet_count(mVideoStreamId) >= monitor->GetImageBufferCount() ) {
|
||||
Warning("ImageBufferCount %d is too small. Needs to be at least %d. Either increase it or decrease time between keyframes",
|
||||
monitor->GetImageBufferCount(),
|
||||
packetqueue->packet_count(mVideoStreamId)+1 );
|
||||
}
|
||||
|
||||
packetqueue->clearQueue(monitor->GetPreEventCount(), mVideoStreamId);
|
||||
packetqueue->queuePacket(&packet);
|
||||
} else if ( packetqueue->size() ) {
|
||||
// it's a keyframe or we already have something in the queue
|
||||
packetqueue->queuePacket(&packet);
|
||||
}
|
||||
} else if ( packet.stream_index == mAudioStreamId ) {
|
||||
// The following lines should ensure that the queue always begins with a video keyframe
|
||||
//Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", record_audio, packetqueue.size() );
|
||||
if ( record_audio && packetqueue->size() ) {
|
||||
// if it's audio, and we are doing audio, and there is already something in the queue
|
||||
packetqueue->queuePacket(&packet);
|
||||
}
|
||||
} // end if packet type
|
||||
|
||||
if ( packet.stream_index == mVideoStreamId ) {
|
||||
// only do decode if we have had a keyframe, should save a few cycles.
|
||||
if ( have_video_keyframe || keyframe ) {
|
||||
|
@ -980,7 +1043,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
|
|||
|
||||
int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
|
||||
//FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
|
||||
|
||||
//Debug(4, "FfmpegInterruptCallback");
|
||||
return zm_terminate;
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ class FfmpegCamera : public Camera {
|
|||
#endif // HAVE_LIBAVFORMAT
|
||||
|
||||
VideoStore *videoStore;
|
||||
zm_packetqueue packetqueue;
|
||||
zm_packetqueue *packetqueue;
|
||||
bool have_video_keyframe;
|
||||
|
||||
#if HAVE_LIBSWSCALE
|
||||
|
@ -87,6 +87,7 @@ class FfmpegCamera : public Camera {
|
|||
#endif
|
||||
|
||||
int64_t startTime;
|
||||
int error_count;
|
||||
|
||||
public:
|
||||
FfmpegCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio );
|
||||
|
|
|
@ -10,6 +10,7 @@ FFmpeg_Input::FFmpeg_Input() {
|
|||
av_register_all();
|
||||
avcodec_register_all();
|
||||
streams = NULL;
|
||||
frame = NULL;
|
||||
}
|
||||
|
||||
FFmpeg_Input::~FFmpeg_Input() {
|
||||
|
@ -34,13 +35,16 @@ int FFmpeg_Input::Open( const char *filepath ) {
|
|||
|
||||
/** Get information on the input file (number of streams etc.). */
|
||||
if ( (error = avformat_find_stream_info(input_format_context, NULL)) < 0 ) {
|
||||
Error( "Could not open find stream info (error '%s')\n",
|
||||
av_make_error_string(error).c_str() );
|
||||
Error(
|
||||
"Could not open find stream info (error '%s')",
|
||||
av_make_error_string(error).c_str()
|
||||
);
|
||||
avformat_close_input(&input_format_context);
|
||||
return error;
|
||||
}
|
||||
|
||||
streams = new stream[input_format_context->nb_streams];
|
||||
Debug(2,"Have %d streams", input_format_context->nb_streams);
|
||||
|
||||
for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) {
|
||||
if ( is_video_stream( input_format_context->streams[i] ) ) {
|
||||
|
@ -53,10 +57,13 @@ int FFmpeg_Input::Open( const char *filepath ) {
|
|||
}
|
||||
} else if ( is_audio_stream( input_format_context->streams[i] ) ) {
|
||||
if ( audio_stream_id == -1 ) {
|
||||
Debug(2,"Audio stream is %d", i);
|
||||
audio_stream_id = i;
|
||||
} else {
|
||||
Warning( "Have another audio stream." );
|
||||
}
|
||||
} else {
|
||||
Warning("Unknown stream type");
|
||||
}
|
||||
|
||||
streams[i].frame_count = 0;
|
||||
|
@ -95,16 +102,15 @@ int FFmpeg_Input::Open( const char *filepath ) {
|
|||
} // end int FFmpeg_Input::Open( const char * filepath )
|
||||
|
||||
AVFrame *FFmpeg_Input::get_frame( int stream_id ) {
|
||||
Debug(1, "Getting frame from stream %d", stream_id );
|
||||
Debug(1, "Getting frame from stream %d", stream_id);
|
||||
|
||||
int frameComplete = false;
|
||||
AVPacket packet;
|
||||
av_init_packet( &packet );
|
||||
AVFrame *frame = zm_av_frame_alloc();
|
||||
av_init_packet(&packet);
|
||||
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||
|
||||
while ( !frameComplete ) {
|
||||
int ret = av_read_frame( input_format_context, &packet );
|
||||
int ret = av_read_frame(input_format_context, &packet);
|
||||
if ( ret < 0 ) {
|
||||
av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||
if (
|
||||
|
@ -119,22 +125,22 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id ) {
|
|||
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf );
|
||||
return NULL;
|
||||
}
|
||||
dumpPacket(input_format_context->streams[packet.stream_index], &packet, "Received packet");
|
||||
|
||||
if ( (stream_id < 0 ) || ( packet.stream_index == stream_id ) ) {
|
||||
if ( (stream_id < 0) || (packet.stream_index == stream_id) ) {
|
||||
Debug(3,"Packet is for our stream (%d)", packet.stream_index );
|
||||
|
||||
AVCodecContext *context = streams[packet.stream_index].context;
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
ret = avcodec_send_packet( context, &packet );
|
||||
if ( ret < 0 ) {
|
||||
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 );
|
||||
zm_av_packet_unref( &packet );
|
||||
continue;
|
||||
} else {
|
||||
Debug(1, "Success getting a packet");
|
||||
}
|
||||
ret = avcodec_send_packet(context, &packet);
|
||||
if ( ret < 0 ) {
|
||||
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);
|
||||
zm_av_packet_unref(&packet);
|
||||
continue;
|
||||
}
|
||||
|
||||
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||
if ( hwaccel ) {
|
||||
|
@ -149,17 +155,24 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id ) {
|
|||
if (ret < 0) {
|
||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||
Error( "Unable to transfer frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
|
||||
zm_av_packet_unref( &packet );
|
||||
zm_av_packet_unref(&packet);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
Debug(1,"Getting a frame?");
|
||||
ret = avcodec_receive_frame( context, frame );
|
||||
if ( frame ) {
|
||||
av_frame_free(&frame);
|
||||
frame = zm_av_frame_alloc();
|
||||
} else {
|
||||
frame = zm_av_frame_alloc();
|
||||
}
|
||||
//Debug(1,"Getting frame %d", streams[packet.stream_index].frame_count);
|
||||
ret = avcodec_receive_frame(context, frame);
|
||||
if ( ret < 0 ) {
|
||||
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 );
|
||||
zm_av_packet_unref( &packet );
|
||||
av_frame_free(&frame);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -169,19 +182,76 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id ) {
|
|||
|
||||
frameComplete = 1;
|
||||
# else
|
||||
if ( frame ) {
|
||||
av_frame_free(&frame);
|
||||
frame = zm_av_frame_alloc();
|
||||
} else {
|
||||
frame = zm_av_frame_alloc();
|
||||
}
|
||||
ret = zm_avcodec_decode_video(context, frame, &frameComplete, &packet);
|
||||
if ( ret < 0 ) {
|
||||
av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||
Error( "Unable to decode frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
|
||||
zm_av_packet_unref( &packet );
|
||||
av_frame_free(&frame);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
} // end if it's the right stream
|
||||
|
||||
zm_av_packet_unref( &packet );
|
||||
zm_av_packet_unref(&packet);
|
||||
|
||||
} // end while ! frameComplete
|
||||
return frame;
|
||||
|
||||
} // end AVFrame *FFmpeg_Input::get_frame
|
||||
|
||||
AVFrame *FFmpeg_Input::get_frame( int stream_id, double at ) {
|
||||
Debug(1, "Getting frame from stream %d at %f", stream_id, at);
|
||||
|
||||
int64_t seek_target = (int64_t)(at * AV_TIME_BASE);
|
||||
Debug(1, "Getting frame from stream %d at seektarget: %" PRId64, stream_id, seek_target);
|
||||
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);
|
||||
|
||||
int ret;
|
||||
|
||||
if ( !frame ) {
|
||||
// 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, 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);
|
||||
} // 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);
|
||||
|
||||
} // end AVFrame *FFmpeg_Input::get_frame( int stream_id, struct timeval at)
|
||||
|
|
|
@ -22,6 +22,7 @@ class FFmpeg_Input {
|
|||
int Open( const char *filename );
|
||||
int Close();
|
||||
AVFrame *get_frame( int stream_id=-1 );
|
||||
AVFrame *get_frame( int stream_id, double at );
|
||||
int get_video_stream_id() {
|
||||
return video_stream_id;
|
||||
}
|
||||
|
@ -40,6 +41,7 @@ class FFmpeg_Input {
|
|||
int video_stream_id;
|
||||
int audio_stream_id;
|
||||
AVFormatContext *input_format_context;
|
||||
AVFrame *frame;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
#include "zm_frame.h"
|
||||
|
||||
Frame::Frame(
|
||||
event_id_t p_event_id,
|
||||
int p_frame_id,
|
||||
FrameType p_type,
|
||||
struct timeval p_timestamp,
|
||||
struct DeltaTimeval p_delta,
|
||||
int p_score
|
||||
) :
|
||||
event_id(p_event_id),
|
||||
frame_id(p_frame_id),
|
||||
type(p_type),
|
||||
timestamp(p_timestamp),
|
||||
delta(p_delta),
|
||||
score(p_score)
|
||||
{
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// ZoneMinder Frame Class Interfaces, $Date$, $Revision$
|
||||
// Copyright (C) 2001-2008 Philip Coombes
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
|
||||
#ifndef ZM_FRAME_H
|
||||
#define ZM_FRAME_H
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
class Frame;
|
||||
|
||||
#include "zm_event.h"
|
||||
#include "zm_time.h"
|
||||
|
||||
//
|
||||
// This describes a frame record
|
||||
//
|
||||
class Frame {
|
||||
|
||||
public:
|
||||
Frame(
|
||||
event_id_t p_event_id,
|
||||
int p_frame_id,
|
||||
FrameType p_type,
|
||||
struct timeval p_timestamp,
|
||||
struct DeltaTimeval p_delta,
|
||||
int p_score
|
||||
);
|
||||
|
||||
event_id_t event_id;
|
||||
int frame_id;
|
||||
FrameType type;
|
||||
struct timeval timestamp;
|
||||
struct DeltaTimeval delta;
|
||||
int score;
|
||||
|
||||
};
|
||||
|
||||
#endif // ZM_FRAME_H
|
|
@ -57,9 +57,9 @@ static void subtractTime( struct timeval * const tp1, struct timeval * const tp2
|
|||
|
||||
void Logger::usrHandler( int sig ) {
|
||||
Logger *logger = fetch();
|
||||
if ( sig == SIGUSR1 ) {
|
||||
if ( sig == SIGUSR1 )
|
||||
logger->level(logger->level()+1);
|
||||
} else if ( sig == SIGUSR2 )
|
||||
else if ( sig == SIGUSR2 )
|
||||
logger->level(logger->level()-1);
|
||||
Info("Logger - Level changed to %d", logger->level());
|
||||
}
|
||||
|
|
|
@ -412,7 +412,7 @@ Monitor::Monitor(
|
|||
+ (image_buffer_count*camera->ImageSize())
|
||||
+ 64; /* Padding used to permit aligning the images buffer to 64 byte boundary */
|
||||
|
||||
Debug(1, "mem.size SharedData=%d TriggerData=%d VideoStoreData=%d total=%d",
|
||||
Debug(1, "mem.size SharedData=%d TriggerData=%d VideoStoreData=%d total=%" PRId64,
|
||||
sizeof(SharedData), sizeof(TriggerData), sizeof(VideoStoreData), mem_size);
|
||||
mem_ptr = NULL;
|
||||
|
||||
|
@ -824,6 +824,7 @@ unsigned int Monitor::GetLastWriteIndex() const {
|
|||
}
|
||||
|
||||
uint64_t Monitor::GetLastEventId() const {
|
||||
#if 0
|
||||
Debug(2, "mem_ptr(%x), State(%d) last_read_index(%d) last_read_time(%d) last_event(%" PRIu64 ")",
|
||||
mem_ptr,
|
||||
shared_data->state,
|
||||
|
@ -831,6 +832,7 @@ uint64_t Monitor::GetLastEventId() const {
|
|||
shared_data->last_read_time,
|
||||
shared_data->last_event
|
||||
);
|
||||
#endif
|
||||
return shared_data->last_event;
|
||||
}
|
||||
|
||||
|
@ -1349,8 +1351,6 @@ bool Monitor::Analyse() {
|
|||
auto_resume_time = 0;
|
||||
}
|
||||
|
||||
static int last_section_mod = 0;
|
||||
|
||||
if ( Enabled() ) {
|
||||
bool signal = shared_data->signal;
|
||||
bool signal_change = (signal != last_signal);
|
||||
|
@ -1386,7 +1386,6 @@ bool Monitor::Analyse() {
|
|||
if ( event && !signal ) {
|
||||
Info( "%s: %03d - Closing event %" PRIu64 ", signal loss", name, image_count, event->Id() );
|
||||
closeEvent();
|
||||
last_section_mod = 0;
|
||||
}
|
||||
if ( !event ) {
|
||||
if ( cause.length() )
|
||||
|
@ -1402,7 +1401,7 @@ bool Monitor::Analyse() {
|
|||
|
||||
} else if ( signal && Active() && (function == MODECT || function == MOCORD) ) {
|
||||
Event::StringSet zoneSet;
|
||||
if ( !(image_count % (motion_frame_skip+1) ) ) {
|
||||
if ( (!motion_frame_skip) || !(image_count % (motion_frame_skip+1) ) ) {
|
||||
// Get new score.
|
||||
int new_motion_score = DetectMotion(*snap_image, zoneSet);
|
||||
|
||||
|
@ -1455,31 +1454,16 @@ bool Monitor::Analyse() {
|
|||
//TODO: What happens is the event closes and sets recording to false then recording to true again so quickly that our capture daemon never picks it up. Maybe need a refresh flag?
|
||||
if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) {
|
||||
if ( event ) {
|
||||
Debug(3, "Detected new event at (%d.%d)", timestamp->tv_sec, timestamp->tv_usec);
|
||||
Debug(3, "Have signal and recording with open event at (%d.%d)", timestamp->tv_sec, timestamp->tv_usec);
|
||||
|
||||
if ( section_length && ( timestamp->tv_sec >= section_length ) ) {
|
||||
// TODO: Wouldn't this be clearer if we just did something like if now - event->start > section_length ?
|
||||
int section_mod = timestamp->tv_sec % section_length;
|
||||
Debug(3,
|
||||
"Section length (%d) Last Section Mod(%d), new section mod(%d)",
|
||||
section_length, last_section_mod, section_mod
|
||||
if ( section_length && ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length ) ) {
|
||||
Info( "%s: %03d - Closing event %" PRIu64 ", section end forced %d - %d = %d >= %d",
|
||||
name, image_count, event->Id(),
|
||||
timestamp->tv_sec, video_store_data->recording.tv_sec,
|
||||
timestamp->tv_sec - video_store_data->recording.tv_sec,
|
||||
section_length
|
||||
);
|
||||
if ( section_mod < last_section_mod ) {
|
||||
//if ( state == IDLE || state == TAPE || event_close_mode == CLOSE_TIME ) {
|
||||
//if ( state == TAPE ) {
|
||||
//shared_data->state = state = IDLE;
|
||||
//Info( "%s: %03d - Closing event %d, section end", name, image_count, event->Id() )
|
||||
//} else {
|
||||
Info( "%s: %03d - Closing event %" PRIu64 ", section end forced ", name, image_count, event->Id() );
|
||||
//}
|
||||
closeEvent();
|
||||
last_section_mod = 0;
|
||||
//} else {
|
||||
//Debug( 2, "Time to close event, but state (%d) is not IDLE or TAPE and event_close_mode is not CLOSE_TIME (%d)", state, event_close_mode );
|
||||
//}
|
||||
} else {
|
||||
last_section_mod = section_mod;
|
||||
}
|
||||
closeEvent();
|
||||
} // end if section_length
|
||||
} // end if event
|
||||
|
||||
|
@ -1549,7 +1533,7 @@ bool Monitor::Analyse() {
|
|||
}
|
||||
} // end if false or config.overlap_timed_events
|
||||
} // end if ! event
|
||||
}
|
||||
} // end if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) {
|
||||
if ( score ) {
|
||||
if ( state == IDLE || state == TAPE || state == PREALARM ) {
|
||||
if ( (!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count) ) {
|
||||
|
@ -1716,6 +1700,19 @@ Error("Creating new event when one exists");
|
|||
}
|
||||
if ( event && noteSetMap.size() > 0 )
|
||||
event->updateNotes( noteSetMap );
|
||||
|
||||
if ( section_length
|
||||
&& ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length )
|
||||
&& ! (image_count % fps_report_interval)
|
||||
) {
|
||||
Warning( "%s: %03d - event %" PRIu64 ", has exceeded desired section length. %d - %d = %d >= %d",
|
||||
name, image_count, event->Id(),
|
||||
timestamp->tv_sec, video_store_data->recording.tv_sec,
|
||||
timestamp->tv_sec - video_store_data->recording.tv_sec,
|
||||
section_length
|
||||
);
|
||||
}
|
||||
|
||||
} else if ( state == ALERT ) {
|
||||
event->AddFrame( snap_image, *timestamp );
|
||||
if ( noteSetMap.size() > 0 )
|
||||
|
@ -1727,7 +1724,7 @@ Error("Creating new event when one exists");
|
|||
//Warning("In state TAPE,
|
||||
//video_store_data->recording = event->StartTime();
|
||||
//}
|
||||
if ( !(image_count%(frame_skip+1)) ) {
|
||||
if ( (!frame_skip) || !(image_count%(frame_skip+1)) ) {
|
||||
if ( config.bulk_frame_interval > 1 ) {
|
||||
event->AddFrame( snap_image, *timestamp, (event->Frames()<pre_event_count?0:-1) );
|
||||
} else {
|
||||
|
@ -1739,11 +1736,10 @@ Error("Creating new event when one exists");
|
|||
}
|
||||
} else {
|
||||
if ( event ) {
|
||||
Info( "%s: %03d - Closing event %" PRIu64 ", trigger off", name, image_count, event->Id() );
|
||||
Info("%s: %03d - Closing event %" PRIu64 ", trigger off", name, image_count, event->Id());
|
||||
closeEvent();
|
||||
}
|
||||
shared_data->state = state = IDLE;
|
||||
last_section_mod = 0;
|
||||
trigger_data->trigger_state = TRIGGER_CANCEL;
|
||||
} // end if ( trigger_data->trigger_state != TRIGGER_OFF )
|
||||
|
||||
|
@ -1770,7 +1766,7 @@ Error("Creating new event when one exists");
|
|||
|
||||
image_count++;
|
||||
|
||||
return( true );
|
||||
return true;
|
||||
}
|
||||
|
||||
void Monitor::Reload() {
|
||||
|
@ -2481,7 +2477,7 @@ int Monitor::Capture() {
|
|||
// If we are too fast, we get div by zero. This seems to happen in the case of audio packets.
|
||||
if ( now != last_fps_time ) {
|
||||
// # of images per interval / the amount of time it took
|
||||
double new_fps = double(fps_report_interval)/(now-last_fps_time);
|
||||
double new_fps = double(image_count%fps_report_interval?image_count:fps_report_interval)/(now-last_fps_time);
|
||||
unsigned int new_camera_bytes = camera->Bytes();
|
||||
unsigned int new_capture_bandwidth = (new_camera_bytes - last_camera_bytes)/(now-last_fps_time);
|
||||
last_camera_bytes = new_camera_bytes;
|
||||
|
|
|
@ -457,6 +457,7 @@ public:
|
|||
void SetVideoWriterEventId( unsigned long long p_event_id ) { video_store_data->current_event = p_event_id; }
|
||||
|
||||
unsigned int GetPreEventCount() const { return pre_event_count; };
|
||||
int GetImageBufferCount() const { return image_buffer_count; };
|
||||
State GetState() const;
|
||||
int GetImage( int index=-1, int scale=100 );
|
||||
Snapshot *getSnapshot() const;
|
||||
|
|
|
@ -21,27 +21,31 @@
|
|||
#include "zm_ffmpeg.h"
|
||||
#include <sys/time.h>
|
||||
|
||||
#define VIDEO_QUEUESIZE 200
|
||||
#define AUDIO_QUEUESIZE 50
|
||||
|
||||
zm_packetqueue::zm_packetqueue(){
|
||||
|
||||
zm_packetqueue::zm_packetqueue( int p_max_stream_id ) {
|
||||
max_stream_id = p_max_stream_id;
|
||||
packet_counts = new int[max_stream_id+1];
|
||||
for ( int i=0; i <= max_stream_id; ++i )
|
||||
packet_counts[i] = 0;
|
||||
}
|
||||
|
||||
zm_packetqueue::~zm_packetqueue() {
|
||||
clearQueue();
|
||||
delete[] packet_counts;
|
||||
packet_counts = NULL;
|
||||
}
|
||||
|
||||
bool zm_packetqueue::queuePacket( ZMPacket* zm_packet ) {
|
||||
pktQueue.push_back( zm_packet );
|
||||
|
||||
bool zm_packetqueue::queuePacket(ZMPacket* zm_packet) {
|
||||
pktQueue.push_back(zm_packet);
|
||||
packet_counts[zm_packet->packet.stream_index] += 1;
|
||||
return true;
|
||||
}
|
||||
bool zm_packetqueue::queuePacket( AVPacket* av_packet ) {
|
||||
|
||||
bool zm_packetqueue::queuePacket(AVPacket* av_packet) {
|
||||
|
||||
ZMPacket *zm_packet = new ZMPacket( av_packet );
|
||||
ZMPacket *zm_packet = new ZMPacket(av_packet);
|
||||
|
||||
pktQueue.push_back( zm_packet );
|
||||
pktQueue.push_back(zm_packet);
|
||||
packet_counts[zm_packet->packet.stream_index] += 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -53,13 +57,14 @@ ZMPacket* zm_packetqueue::popPacket( ) {
|
|||
|
||||
ZMPacket *packet = pktQueue.front();
|
||||
pktQueue.pop_front();
|
||||
packet_counts[packet->packet.stream_index] -= 1;
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream_id ) {
|
||||
unsigned int zm_packetqueue::clearQueue(unsigned int frames_to_keep, int stream_id) {
|
||||
|
||||
Debug(3, "Clearing all but %d frames, queue has %d", frames_to_keep, pktQueue.size() );
|
||||
Debug(3, "Clearing all but %d frames, queue has %d", frames_to_keep, pktQueue.size());
|
||||
frames_to_keep += 1;
|
||||
|
||||
if ( pktQueue.empty() ) {
|
||||
|
@ -74,7 +79,8 @@ unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream
|
|||
ZMPacket *zm_packet = *it;
|
||||
AVPacket *av_packet = &(zm_packet->packet);
|
||||
|
||||
Debug(4, "Looking at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
|
||||
Debug(4, "Looking at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)",
|
||||
av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
|
||||
|
||||
// Want frames_to_keep video keyframes. Otherwise, we may not have enough
|
||||
if ( ( av_packet->stream_index == stream_id) ) {
|
||||
|
@ -83,21 +89,23 @@ unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream
|
|||
}
|
||||
}
|
||||
|
||||
// Make sure we start on a keyframe
|
||||
// Make sure we start on a keyframe
|
||||
for ( ; it != pktQueue.rend(); ++it ) {
|
||||
ZMPacket *zm_packet = *it;
|
||||
AVPacket *av_packet = &(zm_packet->packet);
|
||||
|
||||
Debug(5, "Looking for keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
|
||||
Debug(5, "Looking for keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)",
|
||||
av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
|
||||
|
||||
// Want frames_to_keep video keyframes. Otherwise, we may not have enough
|
||||
if ( ( av_packet->stream_index == stream_id) && ( av_packet->flags & AV_PKT_FLAG_KEY ) ) {
|
||||
Debug(4, "Found keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
|
||||
Debug(4, "Found keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)",
|
||||
av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( frames_to_keep ) {
|
||||
Debug(3, "Hit end of queue, still need (%d) video frames", frames_to_keep );
|
||||
Debug(3, "Hit end of queue, still need (%d) video frames", frames_to_keep);
|
||||
}
|
||||
if ( it != pktQueue.rend() ) {
|
||||
// We want to keep this packet, so advance to the next
|
||||
|
@ -105,43 +113,51 @@ unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream
|
|||
}
|
||||
unsigned int delete_count = 0;
|
||||
while ( it != pktQueue.rend() ) {
|
||||
Debug(4, "Deleting a packet from the front, count is (%d)", delete_count );
|
||||
Debug(4, "Deleting a packet from the front, count is (%d)", delete_count);
|
||||
|
||||
packet = pktQueue.front();
|
||||
pktQueue.pop_front();
|
||||
packet_counts[packet->packet.stream_index] -= 1;
|
||||
delete packet;
|
||||
|
||||
delete_count += 1;
|
||||
}
|
||||
Debug(3, "Deleted (%d) packets", delete_count );
|
||||
packet = NULL; // tidy up for valgrind
|
||||
Debug(3, "Deleted %d packets, %d remaining", delete_count, pktQueue.size());
|
||||
return delete_count;
|
||||
} // end unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream_id )
|
||||
|
||||
void zm_packetqueue::clearQueue() {
|
||||
ZMPacket *packet = NULL;
|
||||
while(!pktQueue.empty()) {
|
||||
int delete_count = 0;
|
||||
while ( !pktQueue.empty() ) {
|
||||
packet = pktQueue.front();
|
||||
packet_counts[packet->packet.stream_index] -= 1;
|
||||
pktQueue.pop_front();
|
||||
delete packet;
|
||||
delete_count += 1;
|
||||
}
|
||||
Debug(3, "Deleted (%d) packets", delete_count );
|
||||
}
|
||||
|
||||
unsigned int zm_packetqueue::size() {
|
||||
return pktQueue.size();
|
||||
}
|
||||
|
||||
int zm_packetqueue::packet_count( int stream_id ) {
|
||||
return packet_counts[stream_id];
|
||||
} // end int zm_packetqueue::packet_count( int stream_id )
|
||||
|
||||
void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVideoStreamId ) {
|
||||
// Need to find the keyframe <= recording_started. Can get rid of audio packets.
|
||||
if ( pktQueue.empty() ) {
|
||||
if ( pktQueue.empty() )
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1 - find keyframe < recording_started.
|
||||
// Step 2 - pop packets until we get to the packet in step 2
|
||||
std::list<ZMPacket *>::reverse_iterator it;
|
||||
|
||||
Debug(3, "Looking for keyframe after start recording stream id (%d)", mVideoStreamId );
|
||||
Debug(3, "Looking for keyframe after start recording stream id (%d)", mVideoStreamId);
|
||||
for ( it = pktQueue.rbegin(); it != pktQueue.rend(); ++ it ) {
|
||||
ZMPacket *zm_packet = *it;
|
||||
AVPacket *av_packet = &(zm_packet->packet);
|
||||
|
@ -150,9 +166,12 @@ void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVi
|
|||
&&
|
||||
( av_packet->stream_index == mVideoStreamId )
|
||||
&&
|
||||
timercmp( &(zm_packet->timestamp), recording_started, < )
|
||||
timercmp( &(zm_packet->timestamp), recording_started, <= )
|
||||
) {
|
||||
Debug(3, "Found keyframe before start with stream index (%d) with keyframe (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ) );
|
||||
Debug(3, "Found keyframe before start with stream index %d at %d.%d",
|
||||
av_packet->stream_index,
|
||||
zm_packet->timestamp.tv_sec,
|
||||
zm_packet->timestamp.tv_usec );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -171,19 +190,23 @@ void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVi
|
|||
|
||||
unsigned int deleted_frames = 0;
|
||||
ZMPacket *packet = NULL;
|
||||
while ( distance( it, pktQueue.rend() ) > 1 ) {
|
||||
while ( distance(it, pktQueue.rend()) > 1 ) {
|
||||
//while ( pktQueue.rend() != it ) {
|
||||
packet = pktQueue.front();
|
||||
pktQueue.pop_front();
|
||||
packet_counts[packet->packet.stream_index] -= 1;
|
||||
delete packet;
|
||||
deleted_frames += 1;
|
||||
}
|
||||
packet = NULL; // tidy up for valgrind
|
||||
|
||||
zm_packet = pktQueue.front();
|
||||
av_packet = &(zm_packet->packet);
|
||||
if ( ( ! ( av_packet->flags & AV_PKT_FLAG_KEY ) ) || ( av_packet->stream_index != mVideoStreamId ) ) {
|
||||
Error( "Done looking for keyframe. Deleted %d frames. Remaining frames in queue: %d stream of head packet is (%d), keyframe (%d), distance(%d), packets(%d)", deleted_frames, pktQueue.size(), av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), distance( it, pktQueue.rend() ), pktQueue.size() );
|
||||
Error( "Done looking for keyframe. Deleted %d frames. Remaining frames in queue: %d stream of head packet is (%d), keyframe (%d), distance(%d), packets(%d)",
|
||||
deleted_frames, pktQueue.size(), av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), distance( it, pktQueue.rend() ), pktQueue.size() );
|
||||
} else {
|
||||
Debug(1, "Done looking for keyframe. Deleted %d frames. Remaining frames in queue: %d stream of head packet is (%d), keyframe (%d), distance(%d), packets(%d)", deleted_frames, pktQueue.size(), av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), distance( it, pktQueue.rend() ), pktQueue.size() );
|
||||
Debug(1, "Done looking for keyframe. Deleted %d frames. Remaining frames in queue: %d stream of head packet is (%d), keyframe (%d), distance(%d), packets(%d)",
|
||||
deleted_frames, pktQueue.size(), av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), distance( it, pktQueue.rend() ), pktQueue.size() );
|
||||
}
|
||||
}
|
||||
} // end void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVideoStreamId )
|
||||
|
|
|
@ -29,23 +29,25 @@
|
|||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
|
||||
class zm_packetqueue {
|
||||
public:
|
||||
zm_packetqueue();
|
||||
zm_packetqueue(int max_stream_id);
|
||||
virtual ~zm_packetqueue();
|
||||
bool queuePacket( AVPacket* packet, struct timeval *timestamp );
|
||||
bool queuePacket( ZMPacket* packet );
|
||||
bool queuePacket( AVPacket* packet );
|
||||
ZMPacket * popPacket( );
|
||||
bool queuePacket(AVPacket* packet, struct timeval *timestamp);
|
||||
bool queuePacket(ZMPacket* packet);
|
||||
bool queuePacket(AVPacket* packet);
|
||||
ZMPacket * popPacket();
|
||||
bool popVideoPacket(ZMPacket* packet);
|
||||
bool popAudioPacket(ZMPacket* packet);
|
||||
unsigned int clearQueue( unsigned int video_frames_to_keep, int stream_id );
|
||||
void clearQueue( );
|
||||
unsigned int clearQueue(unsigned int video_frames_to_keep, int stream_id);
|
||||
void clearQueue();
|
||||
unsigned int size();
|
||||
void clear_unwanted_packets( timeval *recording, int mVideoStreamId );
|
||||
void clear_unwanted_packets(timeval *recording, int mVideoStreamId);
|
||||
int packet_count(int stream_id);
|
||||
private:
|
||||
std::list<ZMPacket *> pktQueue;
|
||||
int max_stream_id;
|
||||
int *packet_counts; /* packet count for each stream_id, to keep track of how many video vs audio packets are in the queue */
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ RemoteCamera::RemoteCamera(
|
|||
|
||||
RemoteCamera::~RemoteCamera() {
|
||||
if ( hp != NULL ) {
|
||||
freeaddrinfo(hp);
|
||||
freeaddrinfo(hp);
|
||||
hp = NULL;
|
||||
}
|
||||
if ( mAuthenticator ) {
|
||||
|
@ -68,8 +68,8 @@ void RemoteCamera::Initialise() {
|
|||
if( host.empty() )
|
||||
Fatal( "No host specified for remote camera" );
|
||||
|
||||
if( port.empty() )
|
||||
Fatal( "No port specified for remote camera" );
|
||||
if ( port.empty() )
|
||||
Fatal("No port specified for remote camera");
|
||||
|
||||
//if( path.empty() )
|
||||
//Fatal( "No path specified for remote camera" );
|
||||
|
@ -99,6 +99,12 @@ void RemoteCamera::Initialise() {
|
|||
if ( ret != 0 ) {
|
||||
Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) );
|
||||
}
|
||||
struct addrinfo *p = NULL;
|
||||
int addr_count = 0;
|
||||
for ( p = hp; p != NULL; p = p->ai_next ) {
|
||||
addr_count++;
|
||||
}
|
||||
Debug(1, "%d addresses returned", addr_count);
|
||||
}
|
||||
|
||||
int RemoteCamera::Read( int fd, char *buf, int size ) {
|
||||
|
|
|
@ -35,6 +35,14 @@
|
|||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_LIBPCRE
|
||||
static RegExpr *header_expr = 0;
|
||||
static RegExpr *status_expr = 0;
|
||||
static RegExpr *connection_expr = 0;
|
||||
static RegExpr *content_length_expr = 0;
|
||||
static RegExpr *content_type_expr = 0;
|
||||
#endif
|
||||
|
||||
RemoteCameraHttp::RemoteCameraHttp(
|
||||
unsigned int p_monitor_id,
|
||||
const std::string &p_method,
|
||||
|
@ -72,30 +80,25 @@ RemoteCameraHttp::RemoteCameraHttp(
|
|||
|
||||
if ( p_method == "simple" )
|
||||
method = SIMPLE;
|
||||
else if ( p_method == "regexp" )
|
||||
else if ( p_method == "regexp" ) {
|
||||
method = REGEXP;
|
||||
else
|
||||
} else
|
||||
Fatal( "Unrecognised method '%s' when creating HTTP camera %d", p_method.c_str(), monitor_id );
|
||||
if ( capture )
|
||||
{
|
||||
if ( capture ) {
|
||||
Initialise();
|
||||
}
|
||||
}
|
||||
|
||||
RemoteCameraHttp::~RemoteCameraHttp()
|
||||
{
|
||||
if ( capture )
|
||||
{
|
||||
RemoteCameraHttp::~RemoteCameraHttp() {
|
||||
if ( capture ) {
|
||||
Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteCameraHttp::Initialise()
|
||||
{
|
||||
void RemoteCameraHttp::Initialise() {
|
||||
RemoteCamera::Initialise();
|
||||
|
||||
if ( request.empty() )
|
||||
{
|
||||
if ( request.empty() ) {
|
||||
request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version );
|
||||
request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION );
|
||||
request += stringtf( "Host: %s\r\n", host.c_str());
|
||||
|
@ -107,8 +110,7 @@ void RemoteCameraHttp::Initialise()
|
|||
Debug( 2, "Request: %s", request.c_str() );
|
||||
}
|
||||
|
||||
if ( !timeout.tv_sec )
|
||||
{
|
||||
if ( !timeout.tv_sec ) {
|
||||
timeout.tv_sec = config.http_timeout/1000;
|
||||
timeout.tv_usec = (config.http_timeout%1000)*1000;
|
||||
}
|
||||
|
@ -120,10 +122,25 @@ void RemoteCameraHttp::Initialise()
|
|||
mode = SINGLE_IMAGE;
|
||||
format = UNDEF;
|
||||
state = HEADER;
|
||||
}
|
||||
|
||||
#if HAVE_LIBPCRE
|
||||
if ( method == REGEXP ) {
|
||||
if ( !header_expr )
|
||||
header_expr = new RegExpr("^(.+?\r?\n\r?\n)", PCRE_DOTALL);
|
||||
if ( !status_expr )
|
||||
status_expr = new RegExpr("^HTTP/(1\\.[01]) +([0-9]+) +(.+?)\r?\n", PCRE_CASELESS);
|
||||
if ( !connection_expr )
|
||||
connection_expr = new RegExpr("Connection: ?(.+?)\r?\n", PCRE_CASELESS);
|
||||
if ( !content_length_expr )
|
||||
content_length_expr = new RegExpr("Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS);
|
||||
if ( !content_type_expr )
|
||||
content_type_expr = new RegExpr("Content-type: ?(.+?)(?:; ?boundary=\x22?(.+?)\x22?)?\r?\n", PCRE_CASELESS);
|
||||
}
|
||||
#endif
|
||||
} // end void RemoteCameraHttp::Initialise()
|
||||
|
||||
int RemoteCameraHttp::Connect() {
|
||||
struct addrinfo *p;
|
||||
struct addrinfo *p = NULL;
|
||||
|
||||
for ( p = hp; p != NULL; p = p->ai_next ) {
|
||||
sd = socket( p->ai_family, p->ai_socktype, p->ai_protocol );
|
||||
|
@ -157,27 +174,24 @@ int RemoteCameraHttp::Connect() {
|
|||
return sd;
|
||||
} // end int RemoteCameraHttp::Connect()
|
||||
|
||||
int RemoteCameraHttp::Disconnect()
|
||||
{
|
||||
close( sd );
|
||||
int RemoteCameraHttp::Disconnect() {
|
||||
close(sd);
|
||||
sd = -1;
|
||||
Debug( 3, "Disconnected from host" );
|
||||
return( 0 );
|
||||
Debug(3, "Disconnected from host");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RemoteCameraHttp::SendRequest()
|
||||
{
|
||||
Debug( 2, "Sending request: %s", request.c_str() );
|
||||
if ( write( sd, request.data(), request.length() ) < 0 )
|
||||
{
|
||||
Error( "Can't write: %s", strerror(errno) );
|
||||
int RemoteCameraHttp::SendRequest() {
|
||||
Debug(2, "Sending request: %s", request.c_str());
|
||||
if ( write(sd, request.data(), request.length()) < 0 ) {
|
||||
Error("Can't write: %s", strerror(errno));
|
||||
Disconnect();
|
||||
return( -1 );
|
||||
return -1;
|
||||
}
|
||||
format = UNDEF;
|
||||
state = HEADER;
|
||||
Debug( 3, "Request sent" );
|
||||
return( 0 );
|
||||
Debug(3, "Request sent");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return codes are as follows:
|
||||
|
@ -273,17 +287,15 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) {
|
|||
total_bytes_to_read -= bytes_read;
|
||||
} while ( total_bytes_to_read );
|
||||
|
||||
Debug( 4, buffer );
|
||||
Debug(4, buffer);
|
||||
|
||||
return( total_bytes_read );
|
||||
return total_bytes_read;
|
||||
}
|
||||
|
||||
int RemoteCameraHttp::GetResponse()
|
||||
{
|
||||
int RemoteCameraHttp::GetResponse() {
|
||||
int buffer_len;
|
||||
#if HAVE_LIBPCRE
|
||||
if ( method == REGEXP )
|
||||
{
|
||||
if ( method == REGEXP ) {
|
||||
const char *header = 0;
|
||||
int header_len = 0;
|
||||
const char *http_version = 0;
|
||||
|
@ -298,36 +310,23 @@ int RemoteCameraHttp::GetResponse()
|
|||
//int subcontent_length = 0;
|
||||
//const char *subcontent_type = "";
|
||||
|
||||
while ( true )
|
||||
{
|
||||
switch( state )
|
||||
{
|
||||
while ( !zm_terminate ) {
|
||||
switch( state ) {
|
||||
case HEADER :
|
||||
{
|
||||
static RegExpr *header_expr = 0;
|
||||
static RegExpr *status_expr = 0;
|
||||
static RegExpr *connection_expr = 0;
|
||||
static RegExpr *content_length_expr = 0;
|
||||
static RegExpr *content_type_expr = 0;
|
||||
|
||||
while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) {
|
||||
Debug(4, "Timeout waiting for REGEXP HEADER");
|
||||
}
|
||||
if ( buffer_len < 0 ) {
|
||||
Error( "Unable to read header data" );
|
||||
return( -1 );
|
||||
Error("Unable to read header data");
|
||||
return -1;
|
||||
}
|
||||
bytes += buffer_len;
|
||||
if ( !header_expr )
|
||||
header_expr = new RegExpr( "^(.+?\r?\n\r?\n)", PCRE_DOTALL );
|
||||
if ( header_expr->Match( (char*)buffer, buffer.size() ) == 2 )
|
||||
{
|
||||
if ( header_expr->Match( (char*)buffer, buffer.size() ) == 2 ) {
|
||||
header = header_expr->MatchString( 1 );
|
||||
header_len = header_expr->MatchLength( 1 );
|
||||
Debug( 4, "Captured header (%d bytes):\n'%s'", header_len, header );
|
||||
Debug(4, "Captured header (%d bytes):\n'%s'", header_len, header);
|
||||
|
||||
if ( !status_expr )
|
||||
status_expr = new RegExpr( "^HTTP/(1\\.[01]) +([0-9]+) +(.+?)\r?\n", PCRE_CASELESS );
|
||||
if ( status_expr->Match( header, header_len ) < 4 )
|
||||
{
|
||||
Error( "Unable to extract HTTP status from header" );
|
||||
|
@ -366,24 +365,18 @@ int RemoteCameraHttp::GetResponse()
|
|||
}
|
||||
Debug( 3, "Got status '%d' (%s), http version %s", status_code, status_mesg, http_version );
|
||||
|
||||
if ( !connection_expr )
|
||||
connection_expr = new RegExpr( "Connection: ?(.+?)\r?\n", PCRE_CASELESS );
|
||||
if ( connection_expr->Match( header, header_len ) == 2 )
|
||||
{
|
||||
connection_type = connection_expr->MatchString( 1 );
|
||||
Debug( 3, "Got connection '%s'", connection_type );
|
||||
}
|
||||
|
||||
if ( !content_length_expr )
|
||||
content_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS );
|
||||
if ( content_length_expr->Match( header, header_len ) == 2 )
|
||||
{
|
||||
content_length = atoi( content_length_expr->MatchString( 1 ) );
|
||||
Debug( 3, "Got content length '%d'", content_length );
|
||||
}
|
||||
|
||||
if ( !content_type_expr )
|
||||
content_type_expr = new RegExpr( "Content-type: ?(.+?)(?:; ?boundary=\x22?(.+?)\x22?)?\r?\n", PCRE_CASELESS );
|
||||
if ( content_type_expr->Match( header, header_len ) >= 2 )
|
||||
{
|
||||
content_type = content_type_expr->MatchString( 1 );
|
||||
|
@ -567,17 +560,14 @@ int RemoteCameraHttp::GetResponse()
|
|||
}
|
||||
}
|
||||
}
|
||||
if ( mode == SINGLE_IMAGE )
|
||||
{
|
||||
if ( mode == SINGLE_IMAGE ) {
|
||||
state = HEADER;
|
||||
Disconnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
state = SUBHEADER;
|
||||
}
|
||||
Debug( 3, "Returning %d (%d) bytes of captured content", content_length, buffer.size() );
|
||||
return( content_length );
|
||||
return content_length;
|
||||
}
|
||||
case HEADERCONT :
|
||||
case SUBHEADERCONT :
|
||||
|
@ -587,8 +577,7 @@ int RemoteCameraHttp::GetResponse()
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
} else
|
||||
#endif // HAVE_LIBPCRE
|
||||
{
|
||||
static const char *http_match = "HTTP/";
|
||||
|
@ -641,7 +630,7 @@ int RemoteCameraHttp::GetResponse()
|
|||
static char content_boundary[64];
|
||||
static int content_boundary_len;
|
||||
|
||||
while ( true ) {
|
||||
while ( !zm_terminate ) {
|
||||
switch( state ) {
|
||||
case HEADER :
|
||||
{
|
||||
|
@ -664,11 +653,11 @@ int RemoteCameraHttp::GetResponse()
|
|||
case HEADERCONT :
|
||||
{
|
||||
while ( !( buffer_len = ReadData(buffer) ) && !zm_terminate ) {
|
||||
Debug(4, "Timeout waiting for HEADERCONT");
|
||||
Debug(1, "Timeout waiting for HEADERCONT");
|
||||
}
|
||||
if ( buffer_len < 0 ) {
|
||||
Error( "Unable to read header" );
|
||||
return( -1 );
|
||||
Error("Unable to read header");
|
||||
return -1;
|
||||
}
|
||||
bytes += buffer_len;
|
||||
|
||||
|
@ -678,9 +667,10 @@ int RemoteCameraHttp::GetResponse()
|
|||
bool all_headers = false;
|
||||
|
||||
while( true ) {
|
||||
int crlf_len = memspn( header_ptr, "\r\n", header_len );
|
||||
int crlf_len = memspn(header_ptr, "\r\n", header_len);
|
||||
if ( n_headers ) {
|
||||
if ( (crlf_len == 2 && !strncmp( header_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( header_ptr, "\r\n\r\n", crlf_len )) ) {
|
||||
Debug(3, "Have double linefeed, done headers");
|
||||
*header_ptr = '\0';
|
||||
header_ptr += crlf_len;
|
||||
header_len -= buffer.consume( header_ptr-(char *)buffer );
|
||||
|
@ -740,7 +730,7 @@ int RemoteCameraHttp::GetResponse()
|
|||
start_ptr = http_header;
|
||||
end_ptr = start_ptr+strspn( start_ptr, "10." );
|
||||
|
||||
// FIXME WHy are we memsetting every time? Can we not do it once?
|
||||
// FIXME Why are we memsetting every time? Can we not do it once?
|
||||
memset( http_version, 0, sizeof(http_version) );
|
||||
strncpy( http_version, start_ptr, end_ptr-start_ptr );
|
||||
|
||||
|
@ -824,7 +814,7 @@ int RemoteCameraHttp::GetResponse()
|
|||
strcpy( content_type, start_ptr );
|
||||
Debug( 3, "Got content type '%s'", content_type );
|
||||
}
|
||||
}
|
||||
} // end if content_type_header
|
||||
|
||||
if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) {
|
||||
// Single image
|
||||
|
@ -859,10 +849,10 @@ int RemoteCameraHttp::GetResponse()
|
|||
return( -1 );
|
||||
}
|
||||
} else {
|
||||
Debug( 3, "Unable to extract entire header from stream, continuing" );
|
||||
Debug(3, "Unable to extract entire header from stream, continuing");
|
||||
state = HEADERCONT;
|
||||
//return( -1 );
|
||||
}
|
||||
} // end if all_headers
|
||||
break;
|
||||
}
|
||||
case SUBHEADER :
|
||||
|
|
|
@ -58,7 +58,7 @@ public:
|
|||
int Capture( Image &image );
|
||||
int PostCapture();
|
||||
int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) {return 0;};
|
||||
int Close() { return 0; };
|
||||
int Close() { Disconnect(); return 0; };
|
||||
};
|
||||
|
||||
#endif // ZM_REMOTE_CAMERA_HTTP_H
|
||||
|
|
|
@ -338,7 +338,11 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
|
|||
{
|
||||
AVFormatContext *formatContext = avformat_alloc_context();
|
||||
|
||||
strncpy( formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) );
|
||||
#if (LIBAVFORMAT_VERSION_CHECK(58, 12, 0, 0, 100))
|
||||
formatContext->url = av_strdup(mUrl.c_str());
|
||||
#else
|
||||
strncpy(formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename));
|
||||
#endif
|
||||
/*
|
||||
if ( mName.length() )
|
||||
strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) );
|
||||
|
|
|
@ -62,16 +62,16 @@ Storage::Storage( unsigned int p_id ) {
|
|||
|
||||
if ( p_id ) {
|
||||
char sql[ZM_SQL_SML_BUFSIZ];
|
||||
snprintf( sql, sizeof(sql), "SELECT Id, Name, Path, Type, Scheme from Storage WHERE Id=%d", p_id );
|
||||
snprintf(sql, sizeof(sql), "SELECT Id, Name, Path, Type, Scheme FROM Storage WHERE Id=%d", p_id);
|
||||
Debug(2,"Loading Storage for %d using %s", p_id, sql );
|
||||
zmDbRow dbrow;
|
||||
if ( ! dbrow.fetch( sql ) ) {
|
||||
Error( "Unable to load storage area for id %d: %s", p_id, mysql_error( &dbconn ) );
|
||||
if ( !dbrow.fetch(sql) ) {
|
||||
Error("Unable to load storage area for id %d: %s", p_id, mysql_error(&dbconn));
|
||||
} else {
|
||||
unsigned int index = 0;
|
||||
id = atoi( dbrow[index++] );
|
||||
strncpy( name, dbrow[index++], sizeof(name)-1 );
|
||||
strncpy( path, dbrow[index++], sizeof(path)-1 );
|
||||
id = atoi(dbrow[index++]);
|
||||
strncpy(name, dbrow[index++], sizeof(name)-1);
|
||||
strncpy(path, dbrow[index++], sizeof(path)-1);
|
||||
type_str = std::string(dbrow[index++]);
|
||||
scheme_str = std::string(dbrow[index++]);
|
||||
if ( scheme_str == "Deep" ) {
|
||||
|
@ -81,17 +81,17 @@ Storage::Storage( unsigned int p_id ) {
|
|||
} else {
|
||||
scheme = SHALLOW;
|
||||
}
|
||||
Debug( 1, "Loaded Storage area %d '%s'", id, this->Name() );
|
||||
Debug(1, "Loaded Storage area %d '%s'", id, this->Name());
|
||||
}
|
||||
}
|
||||
if ( ! id ) {
|
||||
if ( !id ) {
|
||||
if ( staticConfig.DIR_EVENTS[0] != '/' ) {
|
||||
// not using an absolute path. Make it one by appending ZM_PATH_WEB
|
||||
snprintf( path, sizeof (path), "%s/%s", staticConfig.PATH_WEB.c_str( ), staticConfig.DIR_EVENTS.c_str() );
|
||||
snprintf(path, sizeof (path), "%s/%s", staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str());
|
||||
} else {
|
||||
strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1 );
|
||||
strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1);
|
||||
}
|
||||
Debug(1,"No id passed to Storage constructor. Using default path %s instead", path );
|
||||
Debug(1,"No id passed to Storage constructor. Using default path %s instead", path);
|
||||
strcpy(name, "Default");
|
||||
scheme = MEDIUM;
|
||||
scheme_str = "Medium";
|
||||
|
|
|
@ -32,8 +32,8 @@
|
|||
StreamBase::~StreamBase() {
|
||||
#if HAVE_LIBAVCODEC
|
||||
if ( vid_stream ) {
|
||||
delete vid_stream;
|
||||
vid_stream = NULL;
|
||||
delete vid_stream;
|
||||
vid_stream = NULL;
|
||||
}
|
||||
#endif
|
||||
closeComms();
|
||||
|
@ -54,29 +54,34 @@ bool StreamBase::loadMonitor(int monitor_id) {
|
|||
|
||||
bool StreamBase::checkInitialised() {
|
||||
if ( !monitor ) {
|
||||
Fatal( "Cannot stream, not initialised" );
|
||||
Fatal("Cannot stream, not initialised");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void StreamBase::updateFrameRate(double fps) {
|
||||
base_fps = fps;
|
||||
frame_mod = 1;
|
||||
if ( !fps ) {
|
||||
Debug(1, "Zero fps in updateFrameRate. Setting frame_mod=1 and effective_fps=0.0");
|
||||
if ( (fps < 0) || !fps || isinf(fps) ) {
|
||||
Debug(1, "Zero or negative fps %f in updateFrameRate. Setting frame_mod=1 and effective_fps=0.0", fps);
|
||||
effective_fps = 0.0;
|
||||
base_fps = 0.0;
|
||||
return;
|
||||
}
|
||||
base_fps = fps;
|
||||
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?
|
||||
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;
|
||||
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() {
|
||||
if ( sd >= 0 ) {
|
||||
|
@ -279,7 +284,7 @@ void StreamBase::openComms() {
|
|||
}
|
||||
}
|
||||
|
||||
unsigned int length = snprintf(
|
||||
unsigned int length = snprintf(
|
||||
sock_path_lock,
|
||||
sizeof(sock_path_lock),
|
||||
"%s/zms-%06d.lock",
|
||||
|
@ -291,11 +296,23 @@ void StreamBase::openComms() {
|
|||
}
|
||||
Debug(1, "Trying to open the lock on %s", sock_path_lock);
|
||||
|
||||
// Under systemd, we get chrooted to something like /tmp/systemd-apache-blh/ so the dir may not exist.
|
||||
if ( mkdir(staticConfig.PATH_SOCKS.c_str(), 0755) ) {
|
||||
if ( errno != EEXIST ) {
|
||||
Error("Can't mkdir %s: %s", staticConfig.PATH_SOCKS.c_str(), strerror(errno));
|
||||
return;
|
||||
} else {
|
||||
Debug(3, "SOCKS dir %s already exists", staticConfig.PATH_SOCKS.c_str() );
|
||||
}
|
||||
} else {
|
||||
Debug(3, "Success making SOCKS dir %s", staticConfig.PATH_SOCKS.c_str() );
|
||||
}
|
||||
|
||||
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
|
||||
if ( lock_fd <= 0 ) {
|
||||
Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno));
|
||||
lock_fd = 0;
|
||||
} else if ( flock(lock_fd, LOCK_EX) != 0 ) {
|
||||
} else if ( flock(lock_fd, LOCK_EX) != 0 ) {
|
||||
Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno));
|
||||
close(lock_fd);
|
||||
lock_fd = 0;
|
||||
|
@ -306,8 +323,8 @@ void StreamBase::openComms() {
|
|||
sd = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if ( sd < 0 ) {
|
||||
Fatal("Can't create socket: %s", strerror(errno));
|
||||
} else {
|
||||
Debug(1, "Have socket %d", sd);
|
||||
} else {
|
||||
Debug(3, "Have socket %d", sd);
|
||||
}
|
||||
|
||||
length = snprintf(
|
||||
|
@ -329,7 +346,7 @@ void StreamBase::openComms() {
|
|||
|
||||
strncpy(loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path));
|
||||
loc_addr.sun_family = AF_UNIX;
|
||||
Debug(3, "Binding to %s", loc_sock_path);
|
||||
Debug(3, "Binding to %s", loc_sock_path);
|
||||
if ( ::bind(sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)+1) < 0 ) {
|
||||
Fatal("Can't bind: %s", strerror(errno));
|
||||
}
|
||||
|
@ -340,7 +357,7 @@ void StreamBase::openComms() {
|
|||
|
||||
gettimeofday(&last_comm_update, NULL);
|
||||
} // end if connKey > 0
|
||||
Debug(2, "comms open");
|
||||
Debug(3, "comms open at %s", loc_sock_path);
|
||||
} // end void StreamBase::openComms()
|
||||
|
||||
void StreamBase::closeComms() {
|
||||
|
|
|
@ -160,6 +160,7 @@ public:
|
|||
scale = DEFAULT_SCALE;
|
||||
}
|
||||
void setStreamReplayRate( int p_rate ) {
|
||||
Debug(2,"Setting replay_rate %d", p_rate);
|
||||
replay_rate = p_rate;
|
||||
}
|
||||
void setStreamMaxFPS( double p_maxfps ) {
|
||||
|
|
|
@ -248,15 +248,19 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) {
|
|||
|
||||
//Function to check Username length
|
||||
bool checkUser ( const char *username) {
|
||||
if ( strlen(username) > 32) {
|
||||
if ( ! username )
|
||||
return false;
|
||||
}
|
||||
if ( strlen(username) > 32 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
//Function to check password length
|
||||
bool checkPass (const char *password) {
|
||||
if ( strlen(password) > 64) {
|
||||
if ( !password )
|
||||
return false;
|
||||
}
|
||||
if ( strlen(password) > 64 )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -376,7 +376,10 @@ int X264MP4Writer::x264config() {
|
|||
x264params.b_annexb = 0;
|
||||
|
||||
/* TODO: Setup error handler */
|
||||
// x264params.i_log_level = X264_LOG_DEBUG;
|
||||
if ( logDebugging() )
|
||||
x264params.i_log_level = X264_LOG_DEBUG;
|
||||
else
|
||||
x264params.i_log_level = X264_LOG_NONE;
|
||||
|
||||
/* Process user parameters (excluding preset, tune and profile) */
|
||||
for ( unsigned int i = 0; i < user_params.size(); i++ ) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -20,14 +20,16 @@ extern "C" {
|
|||
class VideoStore {
|
||||
private:
|
||||
|
||||
AVOutputFormat *out_format;
|
||||
AVFormatContext *oc;
|
||||
AVStream *video_out_stream;
|
||||
AVStream *audio_out_stream;
|
||||
AVCodecContext *video_out_ctx;
|
||||
AVOutputFormat *out_format;
|
||||
AVFormatContext *oc;
|
||||
|
||||
AVStream *video_in_stream;
|
||||
AVStream *audio_in_stream;
|
||||
AVCodec *video_out_codec;
|
||||
AVCodecContext *video_out_ctx;
|
||||
AVStream *video_out_stream;
|
||||
|
||||
AVStream *video_in_stream;
|
||||
|
||||
AVStream *audio_in_stream;
|
||||
|
||||
// Move this into the object so that we aren't constantly allocating/deallocating it on the stack
|
||||
AVPacket opkt;
|
||||
|
@ -41,6 +43,7 @@ private:
|
|||
int ret;
|
||||
|
||||
// The following are used when encoding the audio stream to AAC
|
||||
AVStream *audio_out_stream;
|
||||
AVCodec *audio_out_codec;
|
||||
AVCodecContext *audio_out_ctx;
|
||||
#ifdef HAVE_LIBSWRESAMPLE
|
||||
|
@ -62,6 +65,11 @@ private:
|
|||
int64_t audio_last_pts;
|
||||
int64_t audio_last_dts;
|
||||
|
||||
int64_t video_first_pts;
|
||||
int64_t video_first_dts;
|
||||
int64_t audio_first_pts;
|
||||
int64_t audio_first_dts;
|
||||
|
||||
// These are for out, should start at zero. We assume they do not wrap because we just aren't going to save files that big.
|
||||
int64_t video_next_pts;
|
||||
int64_t video_next_dts;
|
||||
|
@ -69,6 +77,7 @@ private:
|
|||
int64_t audio_next_dts;
|
||||
|
||||
bool setup_resampler();
|
||||
int resample_audio();
|
||||
|
||||
public:
|
||||
VideoStore(
|
||||
|
|
|
@ -276,7 +276,7 @@ int main(int argc, char *argv[]) {
|
|||
struct timeval now;
|
||||
struct DeltaTimeval delta_time;
|
||||
while ( !zm_terminate ) {
|
||||
sigprocmask(SIG_BLOCK, &block_set, 0);
|
||||
//sigprocmask(SIG_BLOCK, &block_set, 0);
|
||||
for ( int i = 0; i < n_monitors; i++ ) {
|
||||
long min_delay = MAXINT;
|
||||
|
||||
|
@ -333,7 +333,7 @@ int main(int argc, char *argv[]) {
|
|||
} // end if next_delay <= min_delay || next_delays[i] <= 0 )
|
||||
|
||||
} // end foreach n_monitors
|
||||
sigprocmask(SIG_UNBLOCK, &block_set, 0);
|
||||
//sigprocmask(SIG_UNBLOCK, &block_set, 0);
|
||||
if ( zm_reload ) {
|
||||
for ( int i = 0; i < n_monitors; i++ ) {
|
||||
monitors[i]->Reload();
|
||||
|
@ -344,6 +344,7 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
if ( result < 0 ) {
|
||||
// Failure, try reconnecting
|
||||
sleep(1);
|
||||
break;
|
||||
}
|
||||
} // end while ! zm_terminate
|
||||
|
|
|
@ -85,6 +85,9 @@ int main( int argc, const char *argv[] ) {
|
|||
}
|
||||
|
||||
zmLoadConfig();
|
||||
char log_id_string[32] = "zms";
|
||||
logInit( log_id_string );
|
||||
Debug(1,"rate %d", rate);
|
||||
|
||||
const char *query = getenv("QUERY_STRING");
|
||||
if ( query ) {
|
||||
|
@ -95,7 +98,7 @@ int main( int argc, const char *argv[] ) {
|
|||
char *q_ptr = temp_query;
|
||||
char *parms[16]; // Shouldn't be more than this
|
||||
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++;
|
||||
q_ptr = NULL;
|
||||
}
|
||||
|
@ -130,6 +133,7 @@ int main( int argc, const char *argv[] ) {
|
|||
scale = atoi( value );
|
||||
} else if ( !strcmp( name, "rate" ) ) {
|
||||
rate = atoi( value );
|
||||
Debug(2,"Setting rate to %d from %s", rate, value);
|
||||
} else if ( !strcmp( name, "maxfps" ) ) {
|
||||
maxfps = atof( value );
|
||||
} else if ( !strcmp( name, "bitrate" ) ) {
|
||||
|
@ -163,9 +167,10 @@ int main( int argc, const char *argv[] ) {
|
|||
Debug(1, "Unknown parameter passed to zms %s=%s", name, value);
|
||||
} // end if possible parameter names
|
||||
} // end foreach parm
|
||||
} else {
|
||||
Fatal("No query string.");
|
||||
} // end if query
|
||||
|
||||
char log_id_string[32] = "zms";
|
||||
if ( monitor_id ) {
|
||||
snprintf(log_id_string, sizeof(log_id_string), "zms_m%d", monitor_id);
|
||||
} else {
|
||||
|
|
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 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // end getopt loop
|
||||
|
||||
if ( optind < argc ) {
|
||||
fprintf(stderr, "Extraneous options, ");
|
||||
|
@ -425,50 +425,44 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
if ( config.opt_use_auth ) {
|
||||
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 ) {
|
||||
fprintf(stderr, "Error, username must be supplied\n");
|
||||
Error("Username must be supplied");
|
||||
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)) {
|
||||
fprintf(stderr, "Error, username greater than allowed 32 characters\n");
|
||||
Error("Username greater than allowed 32 characters");
|
||||
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);
|
||||
}
|
||||
//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 ) {
|
||||
user = zmLoadUser(username, password);
|
||||
if ( username && password ) {
|
||||
if ( !checkUser(username)) {
|
||||
Error("username greater than allowed 32 characters");
|
||||
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 ) {
|
||||
fprintf(stderr, "Error, unable to authenticate user\n");
|
||||
return exit_zmu(-1);
|
||||
Error("Unable to authenticate user");
|
||||
exit_zmu(-1);
|
||||
}
|
||||
if ( !ValidateAccess(user, mon_id, function) ) {
|
||||
fprintf(stderr, "Error, insufficient privileges for requested action\n");
|
||||
Error("Insufficient privileges for requested action");
|
||||
exit_zmu(-1);
|
||||
}
|
||||
} // end if auth
|
||||
|
@ -503,201 +497,201 @@ int main(int argc, char *argv[]) {
|
|||
if ( verbose ) {
|
||||
char timestamp_str[64] = "None";
|
||||
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 )
|
||||
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
|
||||
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 {
|
||||
if ( have_output ) printf( "%c", separator );
|
||||
printf( "%ld.%02ld", timestamp.tv_sec, timestamp.tv_usec/10000 );
|
||||
if ( have_output ) printf("%c", separator);
|
||||
printf("%ld.%02ld", timestamp.tv_sec, timestamp.tv_usec/10000);
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
if ( function & ZMU_READ_IDX ) {
|
||||
if ( verbose )
|
||||
printf( "Last read index: %d\n", monitor->GetLastReadIndex() );
|
||||
printf("Last read index: %d\n", monitor->GetLastReadIndex());
|
||||
else {
|
||||
if ( have_output ) printf( "%c", separator );
|
||||
printf( "%d", monitor->GetLastReadIndex() );
|
||||
if ( have_output ) printf("%c", separator);
|
||||
printf("%d", monitor->GetLastReadIndex());
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
if ( function & ZMU_WRITE_IDX ) {
|
||||
if ( verbose )
|
||||
printf( "Last write index: %d\n", monitor->GetLastWriteIndex() );
|
||||
else {
|
||||
if ( have_output ) printf( "%c", separator );
|
||||
printf( "%d", monitor->GetLastWriteIndex() );
|
||||
if ( verbose ) {
|
||||
printf("Last write index: %d\n", monitor->GetLastWriteIndex());
|
||||
} else {
|
||||
if ( have_output ) printf("%c", separator);
|
||||
printf("%d", monitor->GetLastWriteIndex());
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
if ( function & ZMU_EVENT ) {
|
||||
if ( verbose )
|
||||
printf( "Last event id: %" PRIu64 "\n", monitor->GetLastEventId() );
|
||||
else {
|
||||
if ( have_output ) printf( "%c", separator );
|
||||
printf( "%" PRIu64, monitor->GetLastEventId() );
|
||||
if ( verbose ) {
|
||||
printf("Last event id: %" PRIu64 "\n", monitor->GetLastEventId());
|
||||
} else {
|
||||
if ( have_output ) printf("%c", separator);
|
||||
printf("%" PRIu64, monitor->GetLastEventId());
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
if ( function & ZMU_FPS ) {
|
||||
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 {
|
||||
if ( have_output ) printf( "%c", separator );
|
||||
printf( "%.2f", monitor->GetFPS() );
|
||||
if ( have_output ) printf("%c", separator);
|
||||
printf("%.2f", monitor->GetFPS());
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
if ( function & ZMU_IMAGE ) {
|
||||
if ( verbose ) {
|
||||
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
|
||||
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 )
|
||||
printf( ", scaling by %d%%", scale );
|
||||
printf( "\n" );
|
||||
printf(", scaling by %d%%", scale);
|
||||
printf("\n");
|
||||
}
|
||||
monitor->GetImage( image_idx, scale>0?scale:100 );
|
||||
monitor->GetImage(image_idx, scale>0?scale:100);
|
||||
}
|
||||
if ( function & ZMU_ZONES ) {
|
||||
if ( verbose )
|
||||
printf( "Dumping zone image to Zones%d.jpg\n", monitor->Id() );
|
||||
monitor->DumpZoneImage( zoneString );
|
||||
printf("Dumping zone image to Zones%d.jpg\n", monitor->Id());
|
||||
monitor->DumpZoneImage(zoneString);
|
||||
}
|
||||
if ( function & ZMU_ALARM ) {
|
||||
if ( verbose )
|
||||
printf( "Forcing alarm on\n" );
|
||||
monitor->ForceAlarmOn( config.forced_alarm_score, "Forced Web" );
|
||||
printf("Forcing alarm on\n");
|
||||
monitor->ForceAlarmOn(config.forced_alarm_score, "Forced Web");
|
||||
while ( monitor->GetState() != Monitor::ALARM ) {
|
||||
// Wait for monitor to notice.
|
||||
usleep(1000);
|
||||
}
|
||||
printf( "Alarmed event id: %" PRIu64 "\n", monitor->GetLastEventId() );
|
||||
printf("Alarmed event id: %" PRIu64 "\n", monitor->GetLastEventId());
|
||||
}
|
||||
if ( function & ZMU_NOALARM ) {
|
||||
if ( verbose )
|
||||
printf( "Forcing alarm off\n" );
|
||||
printf("Forcing alarm off\n");
|
||||
monitor->ForceAlarmOff();
|
||||
}
|
||||
if ( function & ZMU_CANCEL ) {
|
||||
if ( verbose )
|
||||
printf( "Cancelling forced alarm on/off\n" );
|
||||
printf("Cancelling forced alarm on/off\n");
|
||||
monitor->CancelForced();
|
||||
}
|
||||
if ( function & ZMU_RELOAD ) {
|
||||
if ( verbose )
|
||||
printf( "Reloading monitor settings\n" );
|
||||
printf("Reloading monitor settings\n");
|
||||
monitor->actionReload();
|
||||
}
|
||||
if ( function & ZMU_ENABLE ) {
|
||||
if ( verbose )
|
||||
printf( "Enabling event generation\n" );
|
||||
printf("Enabling event generation\n");
|
||||
monitor->actionEnable();
|
||||
}
|
||||
if ( function & ZMU_DISABLE ) {
|
||||
if ( verbose )
|
||||
printf( "Disabling event generation\n" );
|
||||
printf("Disabling event generation\n");
|
||||
monitor->actionDisable();
|
||||
}
|
||||
if ( function & ZMU_SUSPEND ) {
|
||||
if ( verbose )
|
||||
printf( "Suspending event generation\n" );
|
||||
printf("Suspending event generation\n");
|
||||
monitor->actionSuspend();
|
||||
}
|
||||
if ( function & ZMU_RESUME ) {
|
||||
if ( verbose )
|
||||
printf( "Resuming event generation\n" );
|
||||
printf("Resuming event generation\n");
|
||||
monitor->actionResume();
|
||||
}
|
||||
if ( function & ZMU_QUERY ) {
|
||||
char monString[16382] = "";
|
||||
monitor->DumpSettings( monString, verbose );
|
||||
printf( "%s\n", monString );
|
||||
monitor->DumpSettings(monString, verbose);
|
||||
printf("%s\n", monString);
|
||||
}
|
||||
if ( function & ZMU_BRIGHTNESS ) {
|
||||
if ( verbose ) {
|
||||
if ( brightness >= 0 )
|
||||
printf( "New brightness: %d\n", monitor->actionBrightness( brightness ) );
|
||||
printf("New brightness: %d\n", monitor->actionBrightness(brightness));
|
||||
else
|
||||
printf( "Current brightness: %d\n", monitor->actionBrightness() );
|
||||
printf("Current brightness: %d\n", monitor->actionBrightness());
|
||||
} else {
|
||||
if ( have_output ) printf( "%c", separator );
|
||||
if ( have_output ) printf("%c", separator);
|
||||
if ( brightness >= 0 )
|
||||
printf( "%d", monitor->actionBrightness( brightness ) );
|
||||
printf("%d", monitor->actionBrightness(brightness));
|
||||
else
|
||||
printf( "%d", monitor->actionBrightness() );
|
||||
printf("%d", monitor->actionBrightness());
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
if ( function & ZMU_CONTRAST ) {
|
||||
if ( verbose ) {
|
||||
if ( contrast >= 0 )
|
||||
printf( "New brightness: %d\n", monitor->actionContrast( contrast ) );
|
||||
printf("New brightness: %d\n", monitor->actionContrast(contrast));
|
||||
else
|
||||
printf( "Current contrast: %d\n", monitor->actionContrast() );
|
||||
printf("Current contrast: %d\n", monitor->actionContrast());
|
||||
} else {
|
||||
if ( have_output ) printf( "%c", separator );
|
||||
if ( have_output ) printf("%c", separator);
|
||||
if ( contrast >= 0 )
|
||||
printf( "%d", monitor->actionContrast( contrast ) );
|
||||
printf("%d", monitor->actionContrast(contrast));
|
||||
else
|
||||
printf( "%d", monitor->actionContrast() );
|
||||
printf("%d", monitor->actionContrast());
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
if ( function & ZMU_HUE ) {
|
||||
if ( verbose ) {
|
||||
if ( hue >= 0 )
|
||||
printf( "New hue: %d\n", monitor->actionHue( hue ) );
|
||||
printf("New hue: %d\n", monitor->actionHue(hue));
|
||||
else
|
||||
printf( "Current hue: %d\n", monitor->actionHue() );
|
||||
printf("Current hue: %d\n", monitor->actionHue());
|
||||
} else {
|
||||
if ( have_output ) printf( "%c", separator );
|
||||
if ( have_output ) printf("%c", separator);
|
||||
if ( hue >= 0 )
|
||||
printf( "%d", monitor->actionHue( hue ) );
|
||||
printf("%d", monitor->actionHue(hue));
|
||||
else
|
||||
printf( "%d", monitor->actionHue() );
|
||||
printf("%d", monitor->actionHue());
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
if ( function & ZMU_COLOUR ) {
|
||||
if ( verbose ) {
|
||||
if ( colour >= 0 )
|
||||
printf( "New colour: %d\n", monitor->actionColour( colour ) );
|
||||
printf("New colour: %d\n", monitor->actionColour(colour));
|
||||
else
|
||||
printf( "Current colour: %d\n", monitor->actionColour() );
|
||||
printf("Current colour: %d\n", monitor->actionColour());
|
||||
} else {
|
||||
if ( have_output ) printf( "%c", separator );
|
||||
if ( have_output ) printf("%c", separator);
|
||||
if ( colour >= 0 )
|
||||
printf( "%d", monitor->actionColour( colour ) );
|
||||
printf("%d", monitor->actionColour(colour));
|
||||
else
|
||||
printf( "%d", monitor->actionColour() );
|
||||
printf("%d", monitor->actionColour());
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
if ( have_output ) {
|
||||
printf( "\n" );
|
||||
printf("\n");
|
||||
}
|
||||
if ( !function ) {
|
||||
Usage();
|
||||
}
|
||||
delete monitor;
|
||||
} else {
|
||||
fprintf(stderr, "Error, invalid monitor id %d\n", mon_id);
|
||||
Error("Invalid monitor id %d", mon_id);
|
||||
exit_zmu(-1);
|
||||
}
|
||||
} else {
|
||||
if ( function & ZMU_QUERY ) {
|
||||
#if ZM_HAS_V4L
|
||||
char vidString[0x10000] = "";
|
||||
bool ok = LocalCamera::GetCurrentSettings( device, vidString, v4lVersion, verbose );
|
||||
printf( "%s", vidString );
|
||||
exit_zmu( ok?0:-1 );
|
||||
bool ok = LocalCamera::GetCurrentSettings(device, vidString, v4lVersion, verbose);
|
||||
printf("%s", vidString);
|
||||
exit_zmu(ok ? 0 : -1);
|
||||
#else // ZM_HAS_V4L
|
||||
fprintf( stderr, "Error, video4linux is required for device querying\n" );
|
||||
exit_zmu( -1 );
|
||||
Error("Video4linux is required for device querying");
|
||||
exit_zmu(-1);
|
||||
#endif // ZM_HAS_V4L
|
||||
}
|
||||
|
||||
|
@ -708,25 +702,25 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
sql += " order by Id asc";
|
||||
|
||||
if ( mysql_query( &dbconn, sql.c_str() ) ) {
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit_zmu( mysql_errno( &dbconn ) );
|
||||
if ( mysql_query(&dbconn, sql.c_str()) ) {
|
||||
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||
exit_zmu(mysql_errno(&dbconn));
|
||||
}
|
||||
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
MYSQL_RES *result = mysql_store_result(&dbconn);
|
||||
if ( !result ) {
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit_zmu( mysql_errno( &dbconn ) );
|
||||
Error("Can't use query result: %s", mysql_error(&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" );
|
||||
for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) {
|
||||
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++ ) {
|
||||
int mon_id = atoi(dbrow[0]);
|
||||
int function = atoi(dbrow[1]);
|
||||
if ( !user || user->canAccess( mon_id ) ) {
|
||||
if ( !user || user->canAccess(mon_id) ) {
|
||||
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() ) {
|
||||
struct timeval tv = monitor->GetTimestamp();
|
||||
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 {
|
||||
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,
|
||||
function,
|
||||
0,
|
||||
|
@ -755,11 +749,11 @@ int main(int argc, char *argv[]) {
|
|||
0,
|
||||
0.0
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
mysql_free_result( result );
|
||||
}
|
||||
} // end if function filter
|
||||
} // endif !user || canAccess(mon_id)
|
||||
} // end foreach row
|
||||
mysql_free_result(result);
|
||||
} // end if function && ZMU_LIST
|
||||
}
|
||||
delete user;
|
||||
|
||||
|
|
|
@ -124,7 +124,11 @@ PPA="";
|
|||
if [ "$RELEASE" != "" ]; then
|
||||
# We need to use our official tarball for the original source, so grab it and overwrite our generated one.
|
||||
IFS='.' read -r -a VERSION <<< "$RELEASE"
|
||||
PPA="ppa:iconnor/zoneminder-${VERSION[0]}.${VERSION[1]}"
|
||||
if [ "${VERSION[0]}.${VERSION[1]}" == "1.30" ]; then
|
||||
PPA="ppa:iconnor/zoneminder-stable"
|
||||
else
|
||||
PPA="ppa:iconnor/zoneminder-${VERSION[0]}.${VERSION[1]}"
|
||||
fi;
|
||||
else
|
||||
if [ "$BRANCH" == "" ]; then
|
||||
PPA="ppa:iconnor/zoneminder-master";
|
||||
|
|
|
@ -1,136 +1,151 @@
|
|||
<?php
|
||||
error_reporting(0);
|
||||
|
||||
if ( empty($_REQUEST['id']) && empty($_REQUEST['eids']) ) {
|
||||
ajaxError( 'No event id(s) supplied' );
|
||||
ajaxError('No event id(s) supplied');
|
||||
}
|
||||
|
||||
if ( canView( 'Events' ) ) {
|
||||
if ( canView('Events') ) {
|
||||
switch ( $_REQUEST['action'] ) {
|
||||
case 'video' :
|
||||
{
|
||||
if ( empty($_REQUEST['videoFormat']) ) {
|
||||
ajaxError( 'Video Generation Failure, no format given' );
|
||||
} elseif ( empty($_REQUEST['rate']) ) {
|
||||
ajaxError( 'Video Generation Failure, no rate given' );
|
||||
} elseif ( empty($_REQUEST['scale']) ) {
|
||||
ajaxError( 'Video Generation Failure, no scale given' );
|
||||
} 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();
|
||||
if ( !($event = dbFetchOne( $sql, NULL, array( $_REQUEST['id'] ) )) )
|
||||
ajaxError( 'Video Generation Failure, Unable to load event' );
|
||||
else {
|
||||
if ( $videoFile = createVideo( $event, $_REQUEST['videoFormat'], $_REQUEST['rate'], $_REQUEST['scale'], !empty($_REQUEST['overwrite']) ) )
|
||||
ajaxResponse( array( 'response'=>$videoFile ) );
|
||||
else
|
||||
ajaxError( 'Video Generation Failed' );
|
||||
}
|
||||
}
|
||||
$ok = true;
|
||||
break;
|
||||
case 'video' :
|
||||
if ( empty($_REQUEST['videoFormat']) ) {
|
||||
ajaxError('Video Generation Failure, no format given');
|
||||
} elseif ( empty($_REQUEST['rate']) ) {
|
||||
ajaxError('Video Generation Failure, no rate given');
|
||||
} elseif ( empty($_REQUEST['scale']) ) {
|
||||
ajaxError('Video Generation Failure, no scale given');
|
||||
} 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();
|
||||
if ( !($event = dbFetchOne($sql, NULL, array( $_REQUEST['id']))) ) {
|
||||
ajaxError('Video Generation Failure, Unable to load event');
|
||||
} else {
|
||||
if ( $videoFile = createVideo($event, $_REQUEST['videoFormat'], $_REQUEST['rate'], $_REQUEST['scale'], !empty($_REQUEST['overwrite'])) )
|
||||
ajaxResponse(array('response'=>$videoFile));
|
||||
else
|
||||
ajaxError('Video Generation Failed');
|
||||
}
|
||||
case 'deleteVideo' :
|
||||
{
|
||||
unlink( $videoFiles[$_REQUEST['id']] );
|
||||
unset( $videoFiles[$_REQUEST['id']] );
|
||||
ajaxResponse();
|
||||
break;
|
||||
}
|
||||
case 'export' :
|
||||
{
|
||||
require_once( ZM_SKIN_PATH.'/includes/export_functions.php' );
|
||||
}
|
||||
$ok = true;
|
||||
break;
|
||||
case 'deleteVideo' :
|
||||
unlink( $videoFiles[$_REQUEST['id']] );
|
||||
unset( $videoFiles[$_REQUEST['id']] );
|
||||
ajaxResponse();
|
||||
break;
|
||||
case 'export' :
|
||||
require_once(ZM_SKIN_PATH.'/includes/export_functions.php');
|
||||
|
||||
# We use session vars in here, so we need to restart the session because we stopped it in index.php to improve concurrency.
|
||||
session_start();
|
||||
# We use session vars in here, so we need to restart the session
|
||||
# because we stopped it in index.php to improve concurrency.
|
||||
zm_session_start();
|
||||
|
||||
if ( !empty($_REQUEST['exportDetail']) )
|
||||
$exportDetail = $_SESSION['export']['detail'] = $_REQUEST['exportDetail'];
|
||||
else
|
||||
$exportDetail = false;
|
||||
if ( !empty($_REQUEST['exportFrames']) )
|
||||
$exportFrames = $_SESSION['export']['frames'] = $_REQUEST['exportFrames'];
|
||||
else
|
||||
$exportFrames = false;
|
||||
if ( !empty($_REQUEST['exportImages']) )
|
||||
$exportImages = $_SESSION['export']['images'] = $_REQUEST['exportImages'];
|
||||
else
|
||||
$exportImages = false;
|
||||
if ( !empty($_REQUEST['exportVideo']) )
|
||||
$exportVideo = $_SESSION['export']['video'] = $_REQUEST['exportVideo'];
|
||||
else
|
||||
$exportVideo = false;
|
||||
if ( !empty($_REQUEST['exportMisc']) )
|
||||
$exportMisc = $_SESSION['export']['misc'] = $_REQUEST['exportMisc'];
|
||||
else
|
||||
$exportMisc = false;
|
||||
if ( !empty($_REQUEST['exportFormat']) )
|
||||
$exportFormat = $_SESSION['export']['format'] = $_REQUEST['exportFormat'];
|
||||
else
|
||||
$exportFormat = '';
|
||||
if ( !empty($_REQUEST['exportDetail']) )
|
||||
$exportDetail = $_SESSION['export']['detail'] = $_REQUEST['exportDetail'];
|
||||
else
|
||||
$exportDetail = false;
|
||||
|
||||
session_write_close();
|
||||
if ( !empty($_REQUEST['exportFrames']) )
|
||||
$exportFrames = $_SESSION['export']['frames'] = $_REQUEST['exportFrames'];
|
||||
else
|
||||
$exportFrames = false;
|
||||
|
||||
$exportIds = !empty($_REQUEST['eids'])?$_REQUEST['eids']:$_REQUEST['id'];
|
||||
if ( $exportFile = exportEvents( $exportIds, $exportDetail, $exportFrames, $exportImages, $exportVideo, $exportMisc, $exportFormat ) )
|
||||
ajaxResponse( array( 'exportFormat'=>$exportFormat ) );
|
||||
else
|
||||
ajaxError( 'Export Failed' );
|
||||
break;
|
||||
}
|
||||
case 'download' :
|
||||
{
|
||||
require_once( ZM_SKIN_PATH.'/includes/export_functions.php' );
|
||||
$exportVideo = 1;
|
||||
$exportFormat = $_REQUEST['exportFormat'];
|
||||
$exportStructure = 'flat';
|
||||
$exportIds = !empty($_REQUEST['eids'])?$_REQUEST['eids']:$_REQUEST['id'];
|
||||
if ( $exportFile = exportEvents( $exportIds, false, false, false, $exportVideo, false, $exportFormat, $exportStructure ) )
|
||||
ajaxResponse( array( 'exportFormat'=>$exportFormat ) );
|
||||
else
|
||||
ajaxError( 'Export Failed' );
|
||||
break;
|
||||
}
|
||||
if ( !empty($_REQUEST['exportImages']) )
|
||||
$exportImages = $_SESSION['export']['images'] = $_REQUEST['exportImages'];
|
||||
else
|
||||
$exportImages = false;
|
||||
|
||||
if ( !empty($_REQUEST['exportVideo']) )
|
||||
$exportVideo = $_SESSION['export']['video'] = $_REQUEST['exportVideo'];
|
||||
else
|
||||
$exportVideo = false;
|
||||
|
||||
if ( !empty($_REQUEST['exportMisc']) )
|
||||
$exportMisc = $_SESSION['export']['misc'] = $_REQUEST['exportMisc'];
|
||||
else
|
||||
$exportMisc = false;
|
||||
|
||||
if ( !empty($_REQUEST['exportFormat']) )
|
||||
$exportFormat = $_SESSION['export']['format'] = $_REQUEST['exportFormat'];
|
||||
else
|
||||
$exportFormat = '';
|
||||
|
||||
if ( !empty($_REQUEST['exportCompress']) )
|
||||
$exportCompress = $_SESSION['export']['compress'] = $_REQUEST['exportCompress'];
|
||||
else
|
||||
$exportCompress = false;
|
||||
|
||||
session_write_close();
|
||||
|
||||
$exportIds = !empty($_REQUEST['eids']) ? $_REQUEST['eids'] : $_REQUEST['id'];
|
||||
if ( $exportFile = exportEvents(
|
||||
$exportIds,
|
||||
(isset($_REQUEST['connkey'])?$_REQUEST['connkey']:''),
|
||||
$exportDetail,
|
||||
$exportFrames,
|
||||
$exportImages,
|
||||
$exportVideo,
|
||||
$exportMisc,
|
||||
$exportFormat,
|
||||
$exportCompress
|
||||
) )
|
||||
ajaxResponse(array('exportFile'=>$exportFile));
|
||||
else
|
||||
ajaxError('Export Failed');
|
||||
break;
|
||||
case 'download' :
|
||||
require_once(ZM_SKIN_PATH.'/includes/export_functions.php');
|
||||
$exportVideo = 1;
|
||||
$exportFormat = $_REQUEST['exportFormat'];
|
||||
$exportStructure = 'flat';
|
||||
$exportIds = !empty($_REQUEST['eids'])?$_REQUEST['eids']:$_REQUEST['id'];
|
||||
if ( $exportFile = exportEvents(
|
||||
$exportIds,
|
||||
(isset($_REQUEST['connkey'])?$_REQUEST['connkey']:''),
|
||||
false,false, false,
|
||||
$exportVideo, false, $exportFormat, $exportStructure ) )
|
||||
ajaxResponse(array('exportFile'=>$exportFile));
|
||||
else
|
||||
ajaxError('Export Failed');
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // end if canView('Events')
|
||||
|
||||
if ( canEdit( 'Events' ) ) {
|
||||
if ( canEdit('Events') ) {
|
||||
switch ( $_REQUEST['action'] ) {
|
||||
case 'rename' :
|
||||
{
|
||||
if ( !empty($_REQUEST['eventName']) )
|
||||
dbQuery( 'UPDATE Events SET Name = ? WHERE Id = ?', array( $_REQUEST['eventName'], $_REQUEST['id'] ) );
|
||||
else
|
||||
ajaxError( 'No new event name supplied' );
|
||||
ajaxResponse( array( 'refreshEvent'=>true, 'refreshParent'=>true ) );
|
||||
break;
|
||||
}
|
||||
case 'eventdetail' :
|
||||
{
|
||||
dbQuery( 'UPDATE Events SET Cause = ?, Notes = ? WHERE Id = ?', array( $_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['id'] ) );
|
||||
ajaxResponse( array( 'refreshEvent'=>true, 'refreshParent'=>true ) );
|
||||
break;
|
||||
}
|
||||
case 'archive' :
|
||||
case 'unarchive' :
|
||||
{
|
||||
$archiveVal = ($_REQUEST['action'] == 'archive')?1:0;
|
||||
dbQuery( 'UPDATE Events SET Archived = ? WHERE Id = ?', array( $archiveVal, $_REQUEST['id']) );
|
||||
ajaxResponse( array( 'refreshEvent'=>true, 'refreshParent'=>false ) );
|
||||
break;
|
||||
}
|
||||
case 'delete' :
|
||||
{
|
||||
$Event = new ZM\Event( $_REQUEST['id'] );
|
||||
if ( ! $Event->Id() ) {
|
||||
ajaxResponse( array( 'refreshEvent'=>false, 'refreshParent'=>true, 'message'=> 'Event not found.' ) );
|
||||
} else {
|
||||
$Event->delete();
|
||||
ajaxResponse( array( 'refreshEvent'=>false, 'refreshParent'=>true ) );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ajaxError( 'Unrecognised action or insufficient permissions' );
|
||||
case 'rename' :
|
||||
if ( !empty($_REQUEST['eventName']) )
|
||||
dbQuery('UPDATE Events SET Name = ? WHERE Id = ?', array($_REQUEST['eventName'], $_REQUEST['id']));
|
||||
else
|
||||
ajaxError('No new event name supplied');
|
||||
ajaxResponse(array('refreshEvent'=>true, 'refreshParent'=>true));
|
||||
break;
|
||||
case 'eventdetail' :
|
||||
dbQuery(
|
||||
'UPDATE Events SET Cause = ?, Notes = ? WHERE Id = ?',
|
||||
array($_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['id'])
|
||||
);
|
||||
ajaxResponse(array('refreshEvent'=>true, 'refreshParent'=>true));
|
||||
break;
|
||||
case 'archive' :
|
||||
case 'unarchive' :
|
||||
$archiveVal = ($_REQUEST['action'] == 'archive')?1:0;
|
||||
dbQuery(
|
||||
'UPDATE Events SET Archived = ? WHERE Id = ?',
|
||||
array($archiveVal, $_REQUEST['id'])
|
||||
);
|
||||
ajaxResponse(array('refreshEvent'=>true, 'refreshParent'=>false));
|
||||
break;
|
||||
case 'delete' :
|
||||
$Event = new Event($_REQUEST['id']);
|
||||
if ( ! $Event->Id() ) {
|
||||
ajaxResponse(array('refreshEvent'=>false, 'refreshParent'=>true, 'message'=> 'Event not found.'));
|
||||
} else {
|
||||
$Event->delete();
|
||||
ajaxResponse(array('refreshEvent'=>false, 'refreshParent'=>true));
|
||||
}
|
||||
break;
|
||||
} // end switch action
|
||||
} // end if canEdit('Events')
|
||||
|
||||
ajaxError('Unrecognised action or insufficient permissions');
|
||||
?>
|
||||
|
|
|
@ -3,13 +3,15 @@
|
|||
|
||||
$start_time = time();
|
||||
|
||||
define( 'MSG_TIMEOUT', ZM_WEB_AJAX_TIMEOUT/2 );
|
||||
define( 'MSG_DATA_SIZE', 4+256 );
|
||||
define('MSG_TIMEOUT', ZM_WEB_AJAX_TIMEOUT/2);
|
||||
define('MSG_DATA_SIZE', 4+256);
|
||||
|
||||
if ( !($_REQUEST['connkey'] && $_REQUEST['command']) ) {
|
||||
ajaxError( "Unexpected received message type '$type'" );
|
||||
ajaxError("Unexpected received message type '$type'");
|
||||
}
|
||||
|
||||
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
|
||||
$key = ftok(ZM_PATH_SOCKS.'/zms-'.sprintf('%06d',$_REQUEST['connkey']).'s.sock', 'Z');
|
||||
$semaphore = sem_get($key,1);
|
||||
|
@ -23,7 +25,7 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
|||
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.
|
||||
}
|
||||
if ( ! socket_bind( $socket, $localSocketFile ) ) {
|
||||
if ( !socket_bind( $socket, $localSocketFile ) ) {
|
||||
ajaxError("socket_bind( $localSocketFile ) failed: ".socket_strerror(socket_last_error()) );
|
||||
}
|
||||
|
||||
|
@ -49,18 +51,20 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
|||
$msg = pack( 'lcN', MSG_CMD, $_REQUEST['command'], $_REQUEST['offset'] );
|
||||
break;
|
||||
default :
|
||||
ZM\Logger::Debug('Sending command ' . $_REQUEST['command']);
|
||||
$msg = pack( 'lc', MSG_CMD, $_REQUEST['command'] );
|
||||
break;
|
||||
}
|
||||
|
||||
$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
|
||||
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...
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -88,43 +92,35 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
|||
ajaxError( "Socket closed $remSockFile" );
|
||||
} else if ( $numSockets == 0 ) {
|
||||
ZM\Error( "Timed out waiting for msg $remSockFile" );
|
||||
socket_Set_nonblock($socket);
|
||||
#ajaxError( "Timed out waiting for msg $remSockFile" );
|
||||
socket_set_nonblock($socket);
|
||||
#ajaxError("Timed out waiting for msg $remSockFile");
|
||||
} else if ( $numSockets > 0 ) {
|
||||
if ( count($rSockets) != 1 ) {
|
||||
ZM\Error( 'Bogus return from select, '.count($rSockets).' sockets available' );
|
||||
ajaxError( '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');
|
||||
}
|
||||
}
|
||||
|
||||
switch( $nbytes = @socket_recvfrom( $socket, $msg, MSG_DATA_SIZE, 0, $remSockFile ) ) {
|
||||
case -1 :
|
||||
{
|
||||
ajaxError( "socket_recvfrom( $remSockFile ) failed: ".socket_strerror(socket_last_error()) );
|
||||
ajaxError("socket_recvfrom( $remSockFile ) failed: ".socket_strerror(socket_last_error()));
|
||||
break;
|
||||
}
|
||||
case 0 :
|
||||
{
|
||||
ajaxError( 'No data to read from socket' );
|
||||
ajaxError('No data to read from socket');
|
||||
break;
|
||||
}
|
||||
default :
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$data = unpack( 'ltype', $msg );
|
||||
$data = unpack('ltype', $msg);
|
||||
switch ( $data['type'] ) {
|
||||
case MSG_DATA_WATCH :
|
||||
{
|
||||
$data = unpack( "ltype/imonitor/istate/dfps/ilevel/irate/ddelay/izoom/Cdelayed/Cpaused/Cenabled/Cforced", $msg );
|
||||
ZM\Logger::Debug("FPS: " . $data['fps'] );
|
||||
$data = unpack('ltype/imonitor/istate/dfps/ilevel/irate/ddelay/izoom/Cdelayed/Cpaused/Cenabled/Cforced', $msg);
|
||||
ZM\Logger::Debug('FPS: ' . $data['fps']);
|
||||
$data['fps'] = round( $data['fps'], 2 );
|
||||
ZM\Logger::Debug("FPS: " . $data['fps'] );
|
||||
ZM\Logger::Debug('FPS: ' . $data['fps'] );
|
||||
$data['rate'] /= RATE_BASE;
|
||||
$data['delay'] = round( $data['delay'], 2 );
|
||||
$data['zoom'] = round( $data['zoom']/SCALE_BASE, 1 );
|
||||
|
@ -137,11 +133,14 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
|||
}
|
||||
ajaxResponse( array( 'status'=>$data ) );
|
||||
break;
|
||||
}
|
||||
case MSG_DATA_EVENT :
|
||||
{
|
||||
$data = unpack( "ltype/Pevent/iprogress/irate/izoom/Cpaused", $msg );
|
||||
//$data['progress'] = sprintf( "%.2f", $data['progress'] );
|
||||
if ( version_compare( phpversion(), '5.6.0', '<') ) {
|
||||
ZM\Logger::Debug('Using old unpack methods to handle 64bit event id');
|
||||
$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['zoom'] = round( $data['zoom']/SCALE_BASE, 1 );
|
||||
if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == 'hashed' ) {
|
||||
|
@ -151,13 +150,10 @@ if ( sem_acquire($semaphore,1) !== false ) {
|
|||
$data['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||
}
|
||||
}
|
||||
ajaxResponse( array( 'status'=>$data ) );
|
||||
ajaxResponse(array('status'=>$data));
|
||||
break;
|
||||
}
|
||||
default :
|
||||
{
|
||||
ajaxError( "Unexpected received message type '$type'" );
|
||||
}
|
||||
ajaxError("Unexpected received message type '$type'");
|
||||
}
|
||||
sem_release($semaphore);
|
||||
} else {
|
||||
|
|
|
@ -1,14 +1,88 @@
|
|||
<?php
|
||||
App::uses('Component', 'Controller');
|
||||
|
||||
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
|
||||
// that are passed in
|
||||
public function buildFilter($namedParams) {
|
||||
if ($namedParams) {
|
||||
$conditions = array();
|
||||
|
||||
$conditions = array();
|
||||
if ($namedParams) {
|
||||
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
|
||||
// Otherwise, we add it right into the $conditions array
|
||||
if (is_array($value)) {
|
||||
|
@ -18,10 +92,10 @@ class FilterComponent extends Component {
|
|||
array_push($array, $term);
|
||||
}
|
||||
|
||||
$query = array($attribute => $array);
|
||||
$query = array($lhs => $array);
|
||||
array_push($conditions, $query);
|
||||
} else {
|
||||
$conditions[$attribute] = $value;
|
||||
$conditions[$lhs] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,9 +44,8 @@ class EventsController extends AppController {
|
|||
}
|
||||
|
||||
if ( $this->request->params['named'] ) {
|
||||
//$this->FilterComponent = $this->Components->load('Filter');
|
||||
//$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
|
||||
$conditions = $this->request->params['named'];
|
||||
$this->FilterComponent = $this->Components->load('Filter');
|
||||
$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
|
||||
} else {
|
||||
$conditions = array();
|
||||
}
|
||||
|
@ -143,7 +142,7 @@ class EventsController extends AppController {
|
|||
'event' => $event,
|
||||
'_serialize' => array('event')
|
||||
));
|
||||
}
|
||||
} // end function view
|
||||
|
||||
|
||||
/**
|
||||
|
@ -234,18 +233,34 @@ class EventsController extends AppController {
|
|||
|
||||
public function search() {
|
||||
$this->Event->recursive = -1;
|
||||
// Unmodified conditions to pass to find()
|
||||
$find_conditions = array();
|
||||
// Conditions to be filtered by buildFilter
|
||||
$conditions = array();
|
||||
|
||||
foreach ($this->params['named'] as $param_name => $value) {
|
||||
// Transform params into mysql
|
||||
if ( preg_match('/interval/i', $value, $matches) ) {
|
||||
$condition = array("$param_name >= (date_sub(now(), $value))");
|
||||
// Transform params into conditions
|
||||
if ( preg_match('/^\s?interval\s?/i', $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 {
|
||||
$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(
|
||||
'conditions' => $conditions
|
||||
));
|
||||
|
@ -261,18 +276,32 @@ class EventsController extends AppController {
|
|||
// consoleEvents/1 hour/AlarmFrames >=: 1/AlarmFrames <=: 20.json
|
||||
|
||||
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;
|
||||
$results = array();
|
||||
$this->FilterComponent = $this->Components->load('Filter');
|
||||
$conditions = $this->FilterComponent->buildFilter($conditions);
|
||||
array_push($conditions, array("StartTime >= DATE_SUB(NOW(), INTERVAL $expr $unit)"));
|
||||
|
||||
$moreconditions = '';
|
||||
foreach ($this->request->params['named'] as $name => $param) {
|
||||
$moreconditions = $moreconditions . ' AND '.$name.$param;
|
||||
}
|
||||
|
||||
$query = $this->Event->query("SELECT MonitorId, COUNT(*) AS Count FROM Events WHERE (StartTime >= (DATE_SUB(NOW(), interval $interval)) $moreconditions) GROUP BY MonitorId;");
|
||||
$query = $this->Event->find('all', array(
|
||||
'fields' => array(
|
||||
'MonitorId',
|
||||
'COUNT(*) AS Count',
|
||||
),
|
||||
'conditions' => $conditions,
|
||||
'group' => 'MonitorId',
|
||||
));
|
||||
|
||||
foreach ($query as $result) {
|
||||
$results[$result['Events']['MonitorId']] = $result[0]['Count'];
|
||||
$results[$result['Event']['MonitorId']] = $result[0]['Count'];
|
||||
}
|
||||
|
||||
$this->set(array(
|
||||
|
|
|
@ -40,8 +40,7 @@ class MonitorsController extends AppController {
|
|||
|
||||
if ( $this->request->params['named'] ) {
|
||||
$this->FilterComponent = $this->Components->load('Filter');
|
||||
//$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
|
||||
$conditions = $this->request->params['named'];
|
||||
$conditions = $this->FilterComponent->buildFilter($this->request->params['named']);
|
||||
} else {
|
||||
$conditions = array();
|
||||
}
|
||||
|
@ -182,6 +181,9 @@ class MonitorsController extends AppController {
|
|||
($Monitor['ServerId']==ZM_SERVER_ID)
|
||||
)
|
||||
) {
|
||||
if ( !defined('ZM_SERVER_ID')) {
|
||||
ZM\Logger::Debug("Not defined ZM_SERVER_ID");
|
||||
}
|
||||
$this->daemonControl($this->Monitor->id, 'start');
|
||||
}
|
||||
} else {
|
||||
|
@ -315,6 +317,10 @@ class MonitorsController extends AppController {
|
|||
throw new NotFoundException(__('Invalid monitor'));
|
||||
}
|
||||
|
||||
if (preg_match('/^[a-z]+$/i', $daemon) !== 1) {
|
||||
throw new BadRequestException(__('Invalid command'));
|
||||
}
|
||||
|
||||
$monitor = $this->Monitor->find('first', array(
|
||||
'fields' => array('Id', 'Type', 'Device'),
|
||||
'conditions' => array('Id' => $id)
|
||||
|
@ -349,26 +355,29 @@ class MonitorsController extends AppController {
|
|||
));
|
||||
}
|
||||
|
||||
public function daemonControl($id, $command, $monitor=null, $daemon=null) {
|
||||
public function daemonControl($id, $command, $daemon=null) {
|
||||
|
||||
// Need to see if it is local or remote
|
||||
$monitor = $this->Monitor->find('first', array(
|
||||
'fields' => array('Type', 'Function', 'Device'),
|
||||
'conditions' => array('Id' => $id)
|
||||
));
|
||||
$monitor = $monitor['Monitor'];
|
||||
|
||||
$daemons = array();
|
||||
|
||||
if ( !$monitor ) {
|
||||
// Need to see if it is local or remote
|
||||
$monitor = $this->Monitor->find('first', array(
|
||||
'fields' => array('Type', 'Function', 'Device'),
|
||||
'conditions' => array('Id' => $id)
|
||||
));
|
||||
$monitor = $monitor['Monitor'];
|
||||
}
|
||||
|
||||
if ( $monitor['Function'] == 'Monitor' ) {
|
||||
array_push($daemons, 'zmc');
|
||||
if ( ! $daemon ) {
|
||||
if ( $monitor['Function'] == 'Monitor' ) {
|
||||
array_push($daemons, 'zmc');
|
||||
} else {
|
||||
array_push($daemons, 'zmc', 'zma');
|
||||
}
|
||||
} else {
|
||||
array_push($daemons, 'zmc', 'zma');
|
||||
array_push($daemons, $daemon);
|
||||
}
|
||||
|
||||
$zm_path_bin = Configure::read('ZM_PATH_BIN');
|
||||
|
||||
$status_text = '';
|
||||
foreach ( $daemons as $daemon ) {
|
||||
$args = '';
|
||||
if ( $daemon == 'zmc' and $monitor['Type'] == 'Local' ) {
|
||||
|
@ -378,7 +387,14 @@ class MonitorsController extends AppController {
|
|||
}
|
||||
|
||||
$shellcmd = escapeshellcmd("$zm_path_bin/zmdc.pl $command $daemon $args");
|
||||
$status = exec( $shellcmd );
|
||||
ZM\Logger::Debug("Command $shellcmd");
|
||||
$status = exec($shellcmd);
|
||||
$status_text .= $status."\n";
|
||||
}
|
||||
}
|
||||
$this->set(array(
|
||||
'status' => 'ok',
|
||||
'statustext' => $status_text,
|
||||
'_serialize' => array('status','statustext'),
|
||||
));
|
||||
} // end function daemonControl
|
||||
} // end class MonitorsController
|
||||
|
|
|
@ -18,7 +18,7 @@ class ZonesController extends AppController {
|
|||
parent::beforeFilter();
|
||||
|
||||
global $user;
|
||||
$canView = (!$user) || $user['Monitors'] != 'None';
|
||||
$canView = (!$user) || ($user['Monitors'] != 'None');
|
||||
if ( !$canView ) {
|
||||
throw new UnauthorizedException(__('Insufficient Privileges'));
|
||||
return;
|
||||
|
|
|
@ -103,17 +103,17 @@ class Event extends AppModel {
|
|||
);
|
||||
|
||||
public function Relative_Path() {
|
||||
$Event = new ZM\Event($this->data);
|
||||
$Event = new ZM\Event($this->id);
|
||||
return $Event->Relative_Path();
|
||||
} // end function Relative_Path()
|
||||
|
||||
public function Path() {
|
||||
$Event = new ZM\Event($this->data);
|
||||
public function Path() {
|
||||
$Event = new ZM\Event($this->id);
|
||||
return $Event->Path();
|
||||
}
|
||||
|
||||
public function Link_Path() {
|
||||
$Event = new ZM\Event($this->data);
|
||||
$Event = new ZM\Event($this->id);
|
||||
return $Event->Link_Path();
|
||||
}
|
||||
|
||||
|
@ -123,23 +123,25 @@ class Event extends AppModel {
|
|||
$storage = $this->Storage->findById($event['StorageId']);
|
||||
|
||||
if ( $event['DefaultVideo'] ) {
|
||||
if ( file_exists($storage['Storage']['Path'].'/'.$this->Relative_Path().'/'.$event['DefaultVideo']) ) {
|
||||
if ( file_exists($this->Path().'/'.$event['DefaultVideo']) ) {
|
||||
return 1;
|
||||
} else {
|
||||
ZM\Logger::Debug("FIle does not exist at " . $storage['Storage']['Path'].'/'.$this->Relative_Path().'/'.$event['DefaultVideo'] );
|
||||
ZM\Logger::Debug('File does not exist at ' . $this->Path().'/'.$event['DefaultVideo'] );
|
||||
}
|
||||
} else {
|
||||
ZM\Logger::Debug("No DefaultVideo in Event" . $this->Event);
|
||||
ZM\Logger::Debug('No DefaultVideo in Event' . $this->Event);
|
||||
return 0;
|
||||
}
|
||||
} // end function fileExists($event)
|
||||
|
||||
public function fileSize($event) {
|
||||
$storage = $this->Storage->findById($event['StorageId']);
|
||||
return filesize($storage['Storage']['Path'].'/'.$this->Relative_Path($event).'/'.$event['DefaultVideo']);
|
||||
return filesize($this->Path().'/'.$event['DefaultVideo']);
|
||||
}
|
||||
|
||||
public function beforeDelete($cascade=true) {
|
||||
$Event = new ZM\Event($this->data);
|
||||
return $Event->delete();
|
||||
$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 +1 @@
|
|||
Subproject commit 0bd63fb464957080ead342db58ca9e01532cf1ef
|
||||
Subproject commit c3976f1478c681b0bbc132ec3a3e82c3984eeed5
|
|
@ -0,0 +1 @@
|
|||
echo json_encode($status_text);
|
|
@ -0,0 +1,2 @@
|
|||
$xml = Xml::fromArray(array('response' => $status_text));
|
||||
echo $xml->asXML();
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
require_once( 'database.php' );
|
||||
require_once( 'Server.php' );
|
||||
namespace ZM;
|
||||
|
||||
require_once('database.php');
|
||||
|
||||
class Control {
|
||||
|
||||
|
@ -13,6 +14,7 @@ private $defaults = array(
|
|||
'CanMoveCon' => 0,
|
||||
'CanPan' => 0,
|
||||
'CanReset' => 0,
|
||||
'CanReboot' => 0,
|
||||
'CanSleep' => 0,
|
||||
'CanWake' => 0,
|
||||
'MinPanRange' => NULL,
|
||||
|
|
|
@ -43,9 +43,9 @@ class Event {
|
|||
$row = NULL;
|
||||
if ( $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 ) {
|
||||
Error('Unable to load Event record for Id=' . $IdOrRow );
|
||||
Error('Unable to load Event record for Id=' . $IdOrRow);
|
||||
}
|
||||
} elseif ( is_array($IdOrRow) ) {
|
||||
$row = $IdOrRow;
|
||||
|
@ -53,8 +53,7 @@ class Event {
|
|||
$backTrace = debug_backtrace();
|
||||
$file = $backTrace[1]['file'];
|
||||
$line = $backTrace[1]['line'];
|
||||
Error("Unknown argument passed to Event Constructor from $file:$line)");
|
||||
Error("Unknown argument passed to Event Constructor ($IdOrRow)");
|
||||
Error("Unknown argument passed to Event Constructor from $file:$line) Id was $IdOrRow");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -95,11 +94,11 @@ class Event {
|
|||
return new Monitor();
|
||||
}
|
||||
|
||||
public function __call( $fn, array $args){
|
||||
if ( count( $args ) ) {
|
||||
public function __call($fn, array $args){
|
||||
if ( count($args) ) {
|
||||
$this->{$fn} = $args[0];
|
||||
}
|
||||
if ( array_key_exists( $fn, $this ) ) {
|
||||
if ( array_key_exists($fn, $this) ) {
|
||||
return $this->{$fn};
|
||||
|
||||
$backTrace = debug_backtrace();
|
||||
|
@ -109,7 +108,7 @@ class Event {
|
|||
$file = $backTrace[1]['file'];
|
||||
$line = $backTrace[1]['line'];
|
||||
Warning("Unknown function call Event->$fn from $file:$line");
|
||||
Warning(print_r( $this, true ));
|
||||
Warning(print_r($this, true));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,18 +121,23 @@ class Event {
|
|||
|
||||
public function Path() {
|
||||
$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() {
|
||||
$event_path = '';
|
||||
|
||||
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' ) {
|
||||
$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 {
|
||||
$event_path = $this->{'MonitorId'} .'/'.$this->{'Id'};
|
||||
$event_path = $this->{'MonitorId'}.'/'.$this->{'Id'};
|
||||
}
|
||||
|
||||
return $event_path;
|
||||
|
@ -141,24 +145,26 @@ class Event {
|
|||
|
||||
public function Link_Path() {
|
||||
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');
|
||||
return '';
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
# This wouldn't work with foreign keys
|
||||
dbQuery( 'DELETE FROM Events WHERE Id = ?', array($this->{'Id'}) );
|
||||
if ( ! $this->{'Id'} ) {
|
||||
Error('Event delete on event with empty Id');
|
||||
return;
|
||||
}
|
||||
if ( !ZM_OPT_FAST_DELETE ) {
|
||||
dbQuery( 'DELETE FROM Stats WHERE EventId = ?', array($this->{'Id'}) );
|
||||
dbQuery( 'DELETE FROM Frames WHERE EventId = ?', array($this->{'Id'}) );
|
||||
dbQuery('DELETE FROM Stats WHERE EventId = ?', array($this->{'Id'}));
|
||||
dbQuery('DELETE FROM Frames WHERE EventId = ?', array($this->{'Id'}));
|
||||
if ( $this->{'Scheme'} == 'Deep' ) {
|
||||
|
||||
# Assumption: All events have a start time
|
||||
$start_date = date_parse( $this->{'StartTime'} );
|
||||
$start_date = date_parse($this->{'StartTime'});
|
||||
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;
|
||||
}
|
||||
$start_date['year'] = $start_date['year'] % 100;
|
||||
|
@ -166,37 +172,42 @@ class Event {
|
|||
# So this is because ZM creates a link under the day pointing to the time that the event happened.
|
||||
$link_path = $this->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;
|
||||
}
|
||||
|
||||
$Storage = $this->Storage();
|
||||
$eventlink_path = $Storage->Path().'/'.$link_path;
|
||||
|
||||
if ( $id_files = glob( $eventlink_path ) ) {
|
||||
if ( $id_files = glob($eventlink_path) ) {
|
||||
if ( ! $eventPath = readlink($id_files[0]) ) {
|
||||
Error("Unable to read link at $id_files[0]");
|
||||
return;
|
||||
}
|
||||
# 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] );
|
||||
deletePath( $eventPath );
|
||||
deletePath( $id_files[0] );
|
||||
$pathParts = explode( '/', $eventPath );
|
||||
$eventPath = preg_replace('/\.'.$this->{'Id'}.'$/', $eventPath, $id_files[0]);
|
||||
deletePath($eventPath);
|
||||
deletePath($id_files[0]);
|
||||
$pathParts = explode('/', $eventPath);
|
||||
for ( $i = count($pathParts)-1; $i >= 2; $i-- ) {
|
||||
$deletePath = join( '/', array_slice( $pathParts, 0, $i ) );
|
||||
if ( !glob( $deletePath."/*" ) ) {
|
||||
deletePath( $deletePath );
|
||||
$deletePath = join('/', array_slice($pathParts, 0, $i));
|
||||
if ( !glob($deletePath.'/*') ) {
|
||||
deletePath($deletePath);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Warning( "Found no event files under $eventlink_path" );
|
||||
Warning("Found no event files under $eventlink_path");
|
||||
} # end if found files
|
||||
} else {
|
||||
$eventPath = $this->Path();
|
||||
deletePath( $eventPath );
|
||||
if ( ! $eventPath ) {
|
||||
Error('No event Path in Event delete. Not deleting');
|
||||
return;
|
||||
}
|
||||
deletePath($eventPath);
|
||||
} # USE_DEEP_STORAGE OR NOT
|
||||
} # ! ZM_OPT_FAST_DELETE
|
||||
dbQuery('DELETE FROM Events WHERE Id = ?', array($this->{'Id'}));
|
||||
} # end Event->delete
|
||||
|
||||
public function getStreamSrc( $args=array(), $querySep='&' ) {
|
||||
|
@ -415,31 +426,31 @@ class Event {
|
|||
|
||||
$captPath = $eventPath.'/'.$captImage;
|
||||
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>";
|
||||
|
||||
$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;
|
||||
|
||||
//echo "AI:$analImage, AP:$analPath, TAP:$analPath<br>";
|
||||
|
||||
$alarmFrame = $frame['Type']=='Alarm';
|
||||
|
||||
$hasAnalImage = $alarmFrame && file_exists( $analPath ) && filesize( $analPath );
|
||||
$hasAnalImage = $alarmFrame && file_exists($analPath) && filesize($analPath);
|
||||
$isAnalImage = $hasAnalImage && !$captureOnly;
|
||||
|
||||
if ( !ZM_WEB_SCALE_THUMBS || $scale >= SCALE_BASE || !function_exists( 'imagecreatefromjpeg' ) ) {
|
||||
$imagePath = $thumbPath = $isAnalImage?$analPath:$captPath;
|
||||
if ( !ZM_WEB_SCALE_THUMBS || $scale >= SCALE_BASE || !function_exists('imagecreatefromjpeg') ) {
|
||||
$imagePath = $thumbPath = $isAnalImage ? $analPath : $captPath;
|
||||
$imageFile = $imagePath;
|
||||
$thumbFile = $thumbPath;
|
||||
} else {
|
||||
if ( version_compare( phpversion(), '4.3.10', '>=') )
|
||||
$fraction = sprintf( '%.3F', $scale/SCALE_BASE );
|
||||
$fraction = sprintf('%.3F', $scale/SCALE_BASE);
|
||||
else
|
||||
$fraction = sprintf( '%.3f', $scale/SCALE_BASE );
|
||||
$scale = (int)round( $scale );
|
||||
$fraction = sprintf('%.3f', $scale/SCALE_BASE);
|
||||
$scale = (int)round($scale);
|
||||
|
||||
$thumbCaptPath = preg_replace( '/\.jpg$/', "-$scale.jpg", $captPath );
|
||||
$thumbAnalPath = preg_replace( '/\.jpg$/', "-$scale.jpg", $analPath );
|
||||
|
@ -569,8 +580,8 @@ class Event {
|
|||
if ( file_exists( $this->Path().'/'.$this->DefaultVideo() ) ) {
|
||||
return true;
|
||||
}
|
||||
$Storage= $this->Storage();
|
||||
$Server = $Storage->ServerId() ? $Storage->Server() : $this->Monitor()->Server();
|
||||
$Storage= $this->Storage();
|
||||
$Server = $Storage->ServerId() ? $Storage->Server() : $this->Monitor()->Server();
|
||||
if ( $Server->Id() != ZM_SERVER_ID ) {
|
||||
|
||||
$url = $Server->UrlToApi() . '/events/'.$this->{'Id'}.'.json';
|
||||
|
@ -637,14 +648,14 @@ class Event {
|
|||
'content' => ''
|
||||
)
|
||||
);
|
||||
$context = stream_context_create($options);
|
||||
$context = stream_context_create($options);
|
||||
try {
|
||||
$result = file_get_contents($url, false, $context);
|
||||
if ($result === FALSE) { /* Handle error */
|
||||
if ( $result === FALSE ) { /* Handle error */
|
||||
Error("Error restarting zmc using $url");
|
||||
}
|
||||
$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'];
|
||||
} catch ( Exception $e ) {
|
||||
Error("Except $e thrown trying to get event data");
|
||||
|
|
|
@ -214,11 +214,13 @@ public $defaults = array(
|
|||
$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");
|
||||
$data = array();
|
||||
if ( defined('ZM_ENABLE_CSRF_MAGIC') )
|
||||
$data['__csrf_magic'] = csrf_get_secret();
|
||||
if ( defined('ZM_ENABLE_CSRF_MAGIC') ) {
|
||||
require_once( 'includes/csrf/csrf-magic.php' );
|
||||
$data['__csrf_magic'] = csrf_get_tokens();
|
||||
}
|
||||
|
||||
// use key 'http' even if you send the request to https://...
|
||||
$options = array(
|
||||
|
|
|
@ -99,6 +99,7 @@ private $defaults = array(
|
|||
'ArchivedEventDiskSpace' => null,
|
||||
'ZoneCount' => 0,
|
||||
'Refresh' => null,
|
||||
'DefaultCodec' => 'auto',
|
||||
);
|
||||
private $status_fields = array(
|
||||
'AnalysisFPS' => null,
|
||||
|
@ -112,6 +113,7 @@ private $control_fields = array(
|
|||
'CanWake' => '0',
|
||||
'CanSleep' => '0',
|
||||
'CanReset' => '0',
|
||||
'CanReboot' => '0',
|
||||
'CanZoom' => '0',
|
||||
'CanAutoZoom' => '0',
|
||||
'CanZoomAbs' => '0',
|
||||
|
@ -306,7 +308,7 @@ private $control_fields = array(
|
|||
$args['rand'] = time();
|
||||
}
|
||||
|
||||
$streamSrc .= '?'.http_build_query($args,'', $querySep);
|
||||
$streamSrc .= '?'.http_build_query($args, '', $querySep);
|
||||
|
||||
return $streamSrc;
|
||||
} // end function getStreamSrc
|
||||
|
@ -465,7 +467,7 @@ private $control_fields = array(
|
|||
} else if ( $this->ServerId() ) {
|
||||
$Server = $this->Server();
|
||||
|
||||
$url = $Server->UrlToApi().'/monitors/'.$this->{'Id'}.'.json';
|
||||
$url = $Server->UrlToApi().'/monitors/daemonControl/'.$this->{'Id'}.'/'.$mode.'/zmc.json';
|
||||
if ( ZM_OPT_USE_AUTH ) {
|
||||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||
$url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS );
|
||||
|
@ -477,17 +479,8 @@ private $control_fields = array(
|
|||
}
|
||||
}
|
||||
Logger::Debug("sending command to $url");
|
||||
$data = array('Monitor[Function]' => $this->{'Function'} );
|
||||
|
||||
// use key 'http' even if you send the request to https://...
|
||||
$options = array(
|
||||
'http' => array(
|
||||
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
|
||||
'method' => 'POST',
|
||||
'content' => http_build_query($data)
|
||||
)
|
||||
);
|
||||
$context = stream_context_create($options);
|
||||
$context = stream_context_create();
|
||||
try {
|
||||
$result = file_get_contents($url, false, $context);
|
||||
if ($result === FALSE) { /* Handle error */
|
||||
|
@ -523,6 +516,33 @@ private $control_fields = array(
|
|||
daemonControl( 'reload', 'zma', '-m '.$this->{'Id'} );
|
||||
}
|
||||
}
|
||||
} else if ( $this->ServerId() ) {
|
||||
$Server = $this->Server();
|
||||
|
||||
$url = ZM_BASE_PROTOCOL . '://'.$Server->Hostname().'/zm/api/monitors/daemonControl/'.$this->{'Id'}.'/'.$mode.'/zma.json';
|
||||
if ( ZM_OPT_USE_AUTH ) {
|
||||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||
$url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS );
|
||||
} elseif ( ZM_AUTH_RELAY == 'plain' ) {
|
||||
$url = '?user='.$_SESSION['username'];
|
||||
$url = '?pass='.$_SESSION['password'];
|
||||
} elseif ( ZM_AUTH_RELAY == 'none' ) {
|
||||
$url = '?user='.$_SESSION['username'];
|
||||
}
|
||||
}
|
||||
Logger::Debug("sending command to $url");
|
||||
|
||||
$context = stream_context_create();
|
||||
try {
|
||||
$result = file_get_contents($url, false, $context);
|
||||
if ($result === FALSE) { /* Handle error */
|
||||
Error("Error restarting zma using $url");
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
Error("Except $e thrown trying to restart zma");
|
||||
}
|
||||
} else {
|
||||
Error("Server not assigned to Monitor in a multi-server setup. Please assign a server to the Monitor.");
|
||||
} // end if we are on the recording server
|
||||
} // end public function zmaControl
|
||||
|
||||
|
|
|
@ -24,5 +24,6 @@ if ( $action == 'bandwidth' && isset($_REQUEST['newBandwidth']) ) {
|
|||
setcookie('zmBandwidth', validStr($_REQUEST['newBandwidth']), time()+3600*24*30*12*10);
|
||||
$refreshParent = true;
|
||||
$view = 'none';
|
||||
$closePopup = true;
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
//
|
||||
|
||||
|
||||
if ( $action == 'login' && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == 'remote' || isset($_REQUEST['password']) ) ) {
|
||||
if ( ('login' == $action) && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == 'remote' || isset($_REQUEST['password']) ) ) {
|
||||
|
||||
$refreshParent = true;
|
||||
// User login is automatically performed in includes/auth.php So we don't need to perform a login here,
|
||||
|
|
|
@ -86,10 +86,15 @@ if ( $action == 'monitor' ) {
|
|||
unlink($OldStorage->Path().'/'.$saferOldName);
|
||||
|
||||
$NewStorage = new ZM\Storage($_REQUEST['newMonitor']['StorageId']);
|
||||
if ( ! file_exists($NewStorage->Path().'/'.$mid) )
|
||||
mkdir($NewStorage->Path().'/'.$mid, 0755);
|
||||
if ( !file_exists($NewStorage->Path().'/'.$mid) ) {
|
||||
if ( !mkdir($NewStorage->Path().'/'.$mid, 0755) ) {
|
||||
Error('Unable to mkdir ' . $NewStorage->Path().'/'.$mid);
|
||||
}
|
||||
}
|
||||
$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']) ) {
|
||||
$newW = $_REQUEST['newMonitor']['Width'];
|
||||
|
|
|
@ -29,10 +29,10 @@ if ( isset($_REQUEST['object']) ) {
|
|||
if ( $action == 'Save' ) {
|
||||
$Layout = null;
|
||||
if ( $_REQUEST['Name'] != '' ) {
|
||||
$Layout = new MontageLayout();
|
||||
$Layout = new ZM\MontageLayout();
|
||||
$Layout->Name($_REQUEST['Name']);
|
||||
} else {
|
||||
$Layout = new MontageLayout($_REQUEST['zmMontageLayout']);
|
||||
$Layout = new ZM\MontageLayout($_REQUEST['zmMontageLayout']);
|
||||
}
|
||||
$Layout->Positions($_REQUEST['Positions']);
|
||||
$Layout->save();
|
||||
|
|
|
@ -211,7 +211,7 @@ global $user;
|
|||
if ( ZM_OPT_USE_AUTH ) {
|
||||
$close_session = 0;
|
||||
if ( !is_session_started() ) {
|
||||
session_start();
|
||||
zm_session_start();
|
||||
$close_session = 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -735,29 +735,29 @@ function buildControlCommand( $monitor ) {
|
|||
return( $ctrlCommand );
|
||||
}
|
||||
|
||||
function sendControlCommand($mid,$command) {
|
||||
function sendControlCommand($mid, $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 ) {
|
||||
Fatal( 'socket_create() failed: '.socket_strerror($socket) );
|
||||
Fatal('socket_create() failed: '.socket_strerror($socket));
|
||||
}
|
||||
$sockFile = ZM_PATH_SOCKS.'/zmcontrol-'.$mid.'.sock';
|
||||
if ( @socket_connect( $socket, $sockFile ) ) {
|
||||
if ( @socket_connect($socket, $sockFile) ) {
|
||||
$options = array();
|
||||
foreach ( explode( ' ', $command ) as $option ) {
|
||||
if ( preg_match( '/--([^=]+)(?:=(.+))?/', $option, $matches ) ) {
|
||||
foreach ( explode(' ', $command) as $option ) {
|
||||
if ( preg_match('/--([^=]+)(?:=(.+))?/', $option, $matches) ) {
|
||||
$options[$matches[1]] = $matches[2]?$matches[2]:1;
|
||||
}
|
||||
}
|
||||
$optionString = jsonEncode( $options );
|
||||
if ( !socket_write( $socket, $optionString ) ) {
|
||||
Fatal( "Can't write to control socket: ".socket_strerror(socket_last_error($socket)) );
|
||||
$optionString = jsonEncode($options);
|
||||
if ( !socket_write($socket, $optionString) ) {
|
||||
Fatal("Can't write to control socket: ".socket_strerror(socket_last_error($socket)));
|
||||
}
|
||||
socket_close( $socket );
|
||||
socket_close($socket);
|
||||
} else if ( $command != 'quit' ) {
|
||||
$command .= ' --id='.$mid;
|
||||
|
||||
// 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
|
||||
// a bug in ActiveXObject prevents us from properly testing for it.
|
||||
CsrfMagic = function(real) {
|
||||
// try to make it ourselves, if you didn't pass it
|
||||
if (!real) try { real = new XMLHttpRequest; } 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('Msxml2.XMLHTTP.4.0'); } catch (e) {;}
|
||||
this.csrf = real;
|
||||
// properties
|
||||
var csrfMagic = this;
|
||||
real.onreadystatechange = function() {
|
||||
csrfMagic._updateProps();
|
||||
return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null;
|
||||
};
|
||||
// try to make it ourselves, if you didn't pass it
|
||||
if (!real) try {real = new XMLHttpRequest;} 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('Msxml2.XMLHTTP.4.0');} catch (e) {;}
|
||||
this.csrf = real;
|
||||
// properties
|
||||
var csrfMagic = this;
|
||||
real.onreadystatechange = function() {
|
||||
csrfMagic._updateProps();
|
||||
}
|
||||
return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null;
|
||||
};
|
||||
csrfMagic._updateProps();
|
||||
};
|
||||
|
||||
CsrfMagic.prototype = {
|
||||
|
||||
open: function(method, url, async, username, password) {
|
||||
if (method == 'POST') this.csrf_isPost = true;
|
||||
// deal with Opera bug, thanks jQuery
|
||||
if (username) return this.csrf_open(method, url, async, username, password);
|
||||
else return this.csrf_open(method, url, async);
|
||||
},
|
||||
csrf_open: function(method, url, async, username, password) {
|
||||
if (username) return this.csrf.open(method, url, async, username, password);
|
||||
else return this.csrf.open(method, url, async);
|
||||
},
|
||||
open: function(method, url, async, username, password) {
|
||||
if (method == 'POST') this.csrf_isPost = true;
|
||||
// deal with Opera bug, thanks jQuery
|
||||
if (username) return this.csrf_open(method, url, async, username, password);
|
||||
else return this.csrf_open(method, url, async);
|
||||
},
|
||||
csrf_open: function(method, url, async, username, password) {
|
||||
if (username) return this.csrf.open(method, url, async, username, password);
|
||||
else return this.csrf.open(method, url, async);
|
||||
},
|
||||
|
||||
send: function(data) {
|
||||
if (!this.csrf_isPost) return this.csrf_send(data);
|
||||
prepend = csrfMagicName + '=' + csrfMagicToken + '&';
|
||||
send: function(data) {
|
||||
if (!this.csrf_isPost) return this.csrf_send(data);
|
||||
prepend = csrfMagicName + '=' + csrfMagicToken + '&';
|
||||
// XXX: Removed to eliminate 'Refused to set unsafe header "Content-length" ' errors in modern browsers
|
||||
// if (this.csrf_purportedLength === undefined) {
|
||||
// this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length);
|
||||
// delete this.csrf_purportedLength;
|
||||
// }
|
||||
delete this.csrf_isPost;
|
||||
return this.csrf_send(prepend + data);
|
||||
},
|
||||
csrf_send: function(data) {
|
||||
return this.csrf.send(data);
|
||||
},
|
||||
delete this.csrf_isPost;
|
||||
return this.csrf_send(prepend + data);
|
||||
},
|
||||
csrf_send: function(data) {
|
||||
return this.csrf.send(data);
|
||||
},
|
||||
|
||||
setRequestHeader: function(header, value) {
|
||||
// We have to auto-set this at the end, since we don't know how long the
|
||||
// nonce is when added to the data.
|
||||
if (this.csrf_isPost && header == "Content-length") {
|
||||
this.csrf_purportedLength = value;
|
||||
return;
|
||||
}
|
||||
return this.csrf_setRequestHeader(header, value);
|
||||
},
|
||||
csrf_setRequestHeader: function(header, value) {
|
||||
return this.csrf.setRequestHeader(header, value);
|
||||
},
|
||||
setRequestHeader: function(header, value) {
|
||||
// We have to auto-set this at the end, since we don't know how long the
|
||||
// nonce is when added to the data.
|
||||
if (this.csrf_isPost && header == "Content-length") {
|
||||
this.csrf_purportedLength = value;
|
||||
return;
|
||||
}
|
||||
return this.csrf_setRequestHeader(header, value);
|
||||
},
|
||||
csrf_setRequestHeader: function(header, value) {
|
||||
return this.csrf.setRequestHeader(header, value);
|
||||
},
|
||||
|
||||
abort: function() {
|
||||
return this.csrf.abort();
|
||||
},
|
||||
getAllResponseHeaders: function() {
|
||||
return this.csrf.getAllResponseHeaders();
|
||||
},
|
||||
getResponseHeader: function(header) {
|
||||
return this.csrf.getResponseHeader(header);
|
||||
} // ,
|
||||
}
|
||||
abort: function() {
|
||||
return this.csrf.abort();
|
||||
},
|
||||
getAllResponseHeaders: function() {
|
||||
return this.csrf.getAllResponseHeaders();
|
||||
},
|
||||
getResponseHeader: function(header) {
|
||||
return this.csrf.getResponseHeader(header);
|
||||
} // ,
|
||||
};
|
||||
|
||||
// proprietary
|
||||
CsrfMagic.prototype._updateProps = function() {
|
||||
this.readyState = this.csrf.readyState;
|
||||
if (this.readyState == 4) {
|
||||
this.responseText = this.csrf.responseText;
|
||||
this.responseXML = this.csrf.responseXML;
|
||||
this.status = this.csrf.status;
|
||||
this.statusText = this.csrf.statusText;
|
||||
}
|
||||
}
|
||||
this.readyState = this.csrf.readyState;
|
||||
if (this.readyState == 4) {
|
||||
this.responseText = this.csrf.responseText;
|
||||
this.responseXML = this.csrf.responseXML;
|
||||
this.status = this.csrf.status;
|
||||
this.statusText = this.csrf.statusText;
|
||||
}
|
||||
};
|
||||
|
||||
CsrfMagic.process = function(base) {
|
||||
if(typeof base == 'object') {
|
||||
base[csrfMagicName] = csrfMagicToken;
|
||||
return base;
|
||||
}
|
||||
var prepend = csrfMagicName + '=' + csrfMagicToken;
|
||||
if (base) return prepend + '&' + base;
|
||||
return prepend;
|
||||
}
|
||||
if ( typeof base == 'object' ) {
|
||||
base[csrfMagicName] = csrfMagicToken;
|
||||
return base;
|
||||
}
|
||||
var prepend = csrfMagicName + '=' + csrfMagicToken;
|
||||
if ( base ) return prepend + '&' + base;
|
||||
return prepend;
|
||||
};
|
||||
|
||||
// callback function for when everything on the page has loaded
|
||||
CsrfMagic.end = function() {
|
||||
// This rewrites forms AGAIN, so in case buffering didn't work this
|
||||
// certainly will.
|
||||
forms = document.getElementsByTagName('form');
|
||||
for (var i = 0; i < forms.length; i++) {
|
||||
form = forms[i];
|
||||
if (form.method.toUpperCase() !== 'POST') continue;
|
||||
if (form.elements[csrfMagicName]) continue;
|
||||
var input = document.createElement('input');
|
||||
input.setAttribute('name', csrfMagicName);
|
||||
input.setAttribute('value', csrfMagicToken);
|
||||
input.setAttribute('type', 'hidden');
|
||||
form.appendChild(input);
|
||||
}
|
||||
}
|
||||
// This rewrites forms AGAIN, so in case buffering didn't work this
|
||||
// certainly will.
|
||||
forms = document.getElementsByTagName('form');
|
||||
for (var i = 0; i < forms.length; i++) {
|
||||
form = forms[i];
|
||||
if (form.method.toUpperCase() !== 'POST') continue;
|
||||
if (form.elements[csrfMagicName]) continue;
|
||||
var input = document.createElement('input');
|
||||
input.setAttribute('name', csrfMagicName);
|
||||
input.setAttribute('value', csrfMagicToken);
|
||||
input.setAttribute('type', 'hidden');
|
||||
form.appendChild(input);
|
||||
}
|
||||
};
|
||||
|
||||
// Sets things up for Mozilla/Opera/nice browsers
|
||||
// We very specifically match against Internet Explorer, since they haven't
|
||||
// implemented prototypes correctly yet.
|
||||
if (window.XMLHttpRequest && window.XMLHttpRequest.prototype && '\v' != 'v') {
|
||||
var x = XMLHttpRequest.prototype;
|
||||
var c = CsrfMagic.prototype;
|
||||
if ( window.XMLHttpRequest && window.XMLHttpRequest.prototype && '\v' != 'v' ) {
|
||||
var x = XMLHttpRequest.prototype;
|
||||
var c = CsrfMagic.prototype;
|
||||
|
||||
// Save the original functions
|
||||
x.csrf_open = x.open;
|
||||
x.csrf_send = x.send;
|
||||
x.csrf_setRequestHeader = x.setRequestHeader;
|
||||
// Save the original functions
|
||||
x.csrf_open = x.open;
|
||||
x.csrf_send = x.send;
|
||||
x.csrf_setRequestHeader = x.setRequestHeader;
|
||||
|
||||
// Notice that CsrfMagic is itself an instantiatable object, but only
|
||||
// open, send and setRequestHeader are necessary as decorators.
|
||||
x.open = c.open;
|
||||
x.send = c.send;
|
||||
x.setRequestHeader = c.setRequestHeader;
|
||||
// Notice that CsrfMagic is itself an instantiatable object, but only
|
||||
// open, send and setRequestHeader are necessary as decorators.
|
||||
x.open = c.open;
|
||||
x.send = c.send;
|
||||
x.setRequestHeader = c.setRequestHeader;
|
||||
} else {
|
||||
// The only way we can do this is by modifying a library you have been
|
||||
// using. We support YUI, script.aculo.us, prototype, MooTools,
|
||||
// jQuery, Ext and Dojo.
|
||||
if (window.jQuery) {
|
||||
// jQuery didn't implement a new XMLHttpRequest function, so we have
|
||||
// to do this the hard way.
|
||||
jQuery.csrf_ajax = jQuery.ajax;
|
||||
jQuery.ajax = function( s ) {
|
||||
if (s.type && s.type.toUpperCase() == 'POST') {
|
||||
s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
|
||||
if ( s.data && s.processData && typeof s.data != "string" ) {
|
||||
s.data = jQuery.param(s.data);
|
||||
}
|
||||
s.data = CsrfMagic.process(s.data);
|
||||
}
|
||||
return jQuery.csrf_ajax( s );
|
||||
// The only way we can do this is by modifying a library you have been
|
||||
// using. We support YUI, script.aculo.us, prototype, MooTools,
|
||||
// jQuery, Ext and Dojo.
|
||||
if ( window.jQuery ) {
|
||||
// jQuery didn't implement a new XMLHttpRequest function, so we have
|
||||
// to do this the hard way.
|
||||
jQuery.csrf_ajax = jQuery.ajax;
|
||||
jQuery.ajax = function( s ) {
|
||||
if (s.type && s.type.toUpperCase() == 'POST') {
|
||||
s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
|
||||
if ( s.data && s.processData && typeof s.data != "string" ) {
|
||||
s.data = jQuery.param(s.data);
|
||||
}
|
||||
}
|
||||
if (window.Prototype) {
|
||||
// This works for script.aculo.us too
|
||||
Ajax.csrf_getTransport = Ajax.getTransport;
|
||||
Ajax.getTransport = function() {
|
||||
return new CsrfMagic(Ajax.csrf_getTransport());
|
||||
}
|
||||
}
|
||||
if (window.MooTools) {
|
||||
Browser.csrf_Request = Browser.Request;
|
||||
Browser.Request = function () {
|
||||
return new CsrfMagic(Browser.csrf_Request());
|
||||
}
|
||||
}
|
||||
if (window.YAHOO) {
|
||||
// old YUI API
|
||||
YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject;
|
||||
YAHOO.util.Connect.createXhrObject = function (transaction) {
|
||||
obj = YAHOO.util.Connect.csrf_createXhrObject(transaction);
|
||||
obj.conn = new CsrfMagic(obj.conn);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
if (window.Ext) {
|
||||
// Ext can use other js libraries as loaders, so it has to come last
|
||||
// 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;
|
||||
Ext.lib.Ajax.createXhrObject = function (transaction) {
|
||||
obj = Ext.lib.Ajax.csrf_createXhrObject(transaction);
|
||||
obj.conn = new CsrfMagic(obj.conn);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
s.data = CsrfMagic.process(s.data);
|
||||
}
|
||||
return jQuery.csrf_ajax(s);
|
||||
};
|
||||
}
|
||||
if ( window.Prototype ) {
|
||||
// This works for script.aculo.us too
|
||||
Ajax.csrf_getTransport = Ajax.getTransport;
|
||||
Ajax.getTransport = function() {
|
||||
return new CsrfMagic(Ajax.csrf_getTransport());
|
||||
};
|
||||
}
|
||||
if ( window.MooTools ) {
|
||||
Browser.csrf_Request = Browser.Request;
|
||||
Browser.Request = function() {
|
||||
return new CsrfMagic(Browser.csrf_Request());
|
||||
};
|
||||
}
|
||||
if ( window.YAHOO ) {
|
||||
// old YUI API
|
||||
YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject;
|
||||
YAHOO.util.Connect.createXhrObject = function(transaction) {
|
||||
obj = YAHOO.util.Connect.csrf_createXhrObject(transaction);
|
||||
obj.conn = new CsrfMagic(obj.conn);
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
if ( window.Ext ) {
|
||||
// Ext can use other js libraries as loaders, so it has to come last
|
||||
// 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;
|
||||
Ext.lib.Ajax.createXhrObject = function(transaction) {
|
||||
obj = Ext.lib.Ajax.csrf_createXhrObject(transaction);
|
||||
obj.conn = new CsrfMagic(obj.conn);
|
||||
return obj;
|
||||
};
|
||||
}
|
||||
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());
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -211,6 +211,7 @@ break;
|
|||
}
|
||||
$ok = true;
|
||||
} while (false);
|
||||
|
||||
if ($fatal && !$ok) {
|
||||
$callback = $GLOBALS['csrf']['callback'];
|
||||
if (trim($tokens, 'A..Za..z0..9:;,') !== '') $tokens = 'hidden';
|
||||
|
@ -295,6 +296,7 @@ function csrf_callback($tokens) {
|
|||
// 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 "<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>
|
||||
";
|
||||
|
|
|
@ -283,7 +283,7 @@ function outputImageStream( $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='' ) {
|
||||
if ( canStreamIframe() ) {
|
||||
return '<iframe id="'.$id.'" src="'.$src.'" alt="'. validHtmlStr($title) .'" '.($width? ' width="'. validInt($width).'"' : '').($height?' height="'.validInt($height).'"' : '' ).'/>';
|
||||
|
@ -1106,22 +1106,32 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') {
|
|||
$StorageArea = 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) ) {
|
||||
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']);
|
||||
$filter['sql'] .= ' '.$terms[$i]['cnj'].' ';
|
||||
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][cnj]\" value=\"".htmlspecialchars($terms[$i]['cnj'])."\"/>\n";
|
||||
|
||||
$term = $terms[$i];
|
||||
|
||||
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'] ) {
|
||||
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][obr]").'='.urlencode($terms[$i]['obr']);
|
||||
$filter['sql'] .= ' '.str_repeat('(', $terms[$i]['obr']).' ';
|
||||
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][obr]\" value=\"".htmlspecialchars($terms[$i]['obr'])."\"/>\n";
|
||||
if ( isset($term['obr']) && (string)(int)$term['obr'] == $term['obr'] ) {
|
||||
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][obr]").'='.urlencode($term['obr']);
|
||||
$filter['sql'] .= ' '.str_repeat('(', $term['obr']).' ';
|
||||
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][obr]\" value=\"".htmlspecialchars($term['obr'])."\"/>\n";
|
||||
}
|
||||
if ( isset($terms[$i]['attr']) ) {
|
||||
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][attr]").'='.urlencode($terms[$i]['attr']);
|
||||
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][attr]\" value=\"".htmlspecialchars($terms[$i]['attr'])."\"/>\n";
|
||||
switch ( $terms[$i]['attr'] ) {
|
||||
if ( isset($term['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($term['attr'])."\"/>\n";
|
||||
switch ( $term['attr'] ) {
|
||||
case 'MonitorName':
|
||||
$filter['sql'] .= 'M.Name';
|
||||
break;
|
||||
|
@ -1189,14 +1199,14 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') {
|
|||
case 'Notes':
|
||||
case 'StateId':
|
||||
case 'Archived':
|
||||
$filter['sql'] .= 'E.'.$terms[$i]['attr'];
|
||||
$filter['sql'] .= 'E.'.$term['attr'];
|
||||
break;
|
||||
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
|
||||
if ( ! $StorageArea ) {
|
||||
for ( $j = 0; $j < count($terms); $j++ ) {
|
||||
if ( isset($terms[$j]['attr']) and $terms[$j]['attr'] == 'StorageId' and isset($terms[$j]['val']) ) {
|
||||
$StorageArea = new ZM\Storage($terms[$j]['val']);
|
||||
$StorageArea = ZM\Storage::find_one(array('Id'=>$terms[$j]['val']));
|
||||
break;
|
||||
}
|
||||
} // end foreach remaining term
|
||||
|
@ -1209,8 +1219,8 @@ 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
|
||||
if ( ! $StorageArea ) {
|
||||
for ( $j = $i; $j < count($terms); $j++ ) {
|
||||
if ( isset($terms[$i]['attr']) and $terms[$i]['attr'] == 'StorageId' and isset($terms[$j]['val']) ) {
|
||||
$StorageArea = new ZM\Storage($terms[$i]['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']));
|
||||
}
|
||||
} // end foreach remaining term
|
||||
} // end no StorageArea found yet
|
||||
|
@ -1221,8 +1231,8 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') {
|
|||
break;
|
||||
}
|
||||
$valueList = array();
|
||||
foreach ( preg_split( '/["\'\s]*?,["\'\s]*?/', preg_replace( '/^["\']+?(.+)["\']+?$/', '$1', $terms[$i]['val'] ) ) as $value ) {
|
||||
switch ( $terms[$i]['attr'] ) {
|
||||
foreach ( preg_split( '/["\'\s]*?,["\'\s]*?/', preg_replace( '/^["\']+?(.+)["\']+?$/', '$1', $term['val'] ) ) as $value ) {
|
||||
switch ( $term['attr'] ) {
|
||||
case 'MonitorName':
|
||||
case 'Name':
|
||||
case 'Cause':
|
||||
|
@ -1242,7 +1252,7 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') {
|
|||
}
|
||||
break;
|
||||
case 'StorageId':
|
||||
$StorageArea = new ZM\Storage( $value );
|
||||
$StorageArea = ZM\Storage::find_one(array('Id'=>$value));
|
||||
if ( $value != 'NULL' )
|
||||
$value = dbEscape($value);
|
||||
break;
|
||||
|
@ -1270,16 +1280,16 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') {
|
|||
break;
|
||||
}
|
||||
$valueList[] = $value;
|
||||
}
|
||||
} // end foreach value
|
||||
|
||||
switch ( $terms[$i]['op'] ) {
|
||||
switch ( $term['op'] ) {
|
||||
case '=' :
|
||||
case '!=' :
|
||||
case '>=' :
|
||||
case '>' :
|
||||
case '<' :
|
||||
case '<=' :
|
||||
$filter['sql'] .= ' '.$terms[$i]['op'].' '. $value;
|
||||
$filter['sql'] .= ' '.$term['op'].' '. $value;
|
||||
break;
|
||||
case '=~' :
|
||||
$filter['sql'] .= ' regexp '.$value;
|
||||
|
@ -1307,28 +1317,39 @@ function parseFilter(&$filter, $saveToSession=false, $querySep='&') {
|
|||
$filter['sql'] .= " IS NOT $value";
|
||||
break;
|
||||
default:
|
||||
ZM\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['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][op]\" value=\"".htmlspecialchars($terms[$i]['op'])."\"/>\n";
|
||||
if ( isset($terms[$i]['val']) ) {
|
||||
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][val]").'='.urlencode($terms[$i]['val']);
|
||||
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][val]\" value=\"".htmlspecialchars($terms[$i]['val'])."\"/>\n";
|
||||
}
|
||||
} // end foreach term
|
||||
if ( isset($terms[$i]['cbr']) && (string)(int)$terms[$i]['cbr'] == $terms[$i]['cbr'] ) {
|
||||
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][cbr]").'='.urlencode($terms[$i]['cbr']);
|
||||
$filter['sql'] .= ' '.str_repeat( ')', $terms[$i]['cbr'] ).' ';
|
||||
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][cbr]\" value=\"".htmlspecialchars($terms[$i]['cbr'])."\"/>\n";
|
||||
$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($term['op'])."\"/>\n";
|
||||
if ( isset($term['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($term['val'])."\"/>\n";
|
||||
}
|
||||
} // end if ( isset($term['attr']) )
|
||||
if ( isset($term['cbr']) && (string)(int)$term['cbr'] == $term['cbr'] ) {
|
||||
$filter['query'] .= $querySep.urlencode("filter[Query][terms][$i][cbr]").'='.urlencode($term['cbr']);
|
||||
$filter['sql'] .= ' '.str_repeat( ')', $term['cbr'] ).' ';
|
||||
$filter['fields'] .= "<input type=\"hidden\" name=\"filter[Query][terms][$i][cbr]\" value=\"".htmlspecialchars($term['cbr'])."\"/>\n";
|
||||
}
|
||||
}
|
||||
} // end foreach term
|
||||
if ( $filter['sql'] )
|
||||
$filter['sql'] = ' and ( '.$filter['sql'].' )';
|
||||
if ( $saveToSession ) {
|
||||
$_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.
|
||||
|
@ -2235,10 +2256,9 @@ function validHtmlStr( $input ) {
|
|||
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 ) ) {
|
||||
//Warning("Scale to " . $options['scale'] );
|
||||
if ( isset($options['scale']) and $options['scale'] and ($options['scale'] != 100) ) {
|
||||
$options['width'] = reScale($monitor->Width(), $options['scale']).'px';
|
||||
$options['height'] = reScale($monitor->Height(), $options['scale']).'px';
|
||||
} else {
|
||||
|
@ -2271,10 +2291,10 @@ function getStreamHTML( $monitor, $options = array() ) {
|
|||
//FIXME, the width and height of the image need to be scaled.
|
||||
} else if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
|
||||
$streamSrc = $monitor->getStreamSrc( array(
|
||||
'mode'=>'mpeg',
|
||||
'scale'=>(isset($options['scale'])?$options['scale']:100),
|
||||
'bitrate'=>ZM_WEB_VIDEO_BITRATE,
|
||||
'maxfps'=>ZM_WEB_VIDEO_MAXFPS,
|
||||
'mode' => 'mpeg',
|
||||
'scale' => (isset($options['scale'])?$options['scale']:100),
|
||||
'bitrate'=> ZM_WEB_VIDEO_BITRATE,
|
||||
'maxfps' => ZM_WEB_VIDEO_MAXFPS,
|
||||
'format' => ZM_MPEG_LIVE_FORMAT
|
||||
) );
|
||||
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.' );
|
||||
}
|
||||
$options['mode'] = 'single';
|
||||
$streamSrc = $monitor->getStreamSrc( $options );
|
||||
$streamSrc = $monitor->getStreamSrc($options);
|
||||
return getImageStill( 'liveStream'.$monitor->Id(), $streamSrc, $options['width'], $options['height'], $monitor->Name());
|
||||
}
|
||||
} // end function getStreamHTML
|
||||
|
@ -2382,7 +2402,6 @@ function unparse_url($parsed_url, $substitutions = array() ) {
|
|||
// PP - POST request handler for PHP which does not need extensions
|
||||
// credit: http://wezfurlong.org/blog/2006/nov/http-post-from-php-without-curl/
|
||||
|
||||
|
||||
function do_request($method, $url, $data=array(), $optional_headers = null) {
|
||||
global $php_errormsg;
|
||||
|
||||
|
@ -2426,7 +2445,7 @@ function do_post_request($url, $data, $optional_headers = null) {
|
|||
}
|
||||
|
||||
// The following works around php not being built with semaphore functions.
|
||||
if (!function_exists('sem_get')) {
|
||||
if ( !function_exists('sem_get') ) {
|
||||
function sem_get($key) {
|
||||
return fopen(__FILE__ . '.sem.' . $key, 'w+');
|
||||
}
|
||||
|
@ -2438,7 +2457,7 @@ if (!function_exists('sem_get')) {
|
|||
}
|
||||
}
|
||||
|
||||
if( !function_exists('ftok') ) {
|
||||
if ( !function_exists('ftok') ) {
|
||||
function ftok($filename = "", $proj = "") {
|
||||
if ( empty($filename) || !file_exists($filename) ) {
|
||||
return -1;
|
||||
|
|
|
@ -21,7 +21,8 @@ function zm_session_start() {
|
|||
|
||||
session_start();
|
||||
// Do not allow to use expired session ID
|
||||
if (!empty($_SESSION['last_time']) && $_SESSION['last_time'] < time() - 180) {
|
||||
if ( !empty($_SESSION['last_time']) && ($_SESSION['last_time'] < (time() - 180)) ) {
|
||||
ZM\Info('Destroying session due to timeout. ');
|
||||
session_destroy();
|
||||
session_start();
|
||||
}
|
||||
|
@ -32,8 +33,13 @@ function zm_session_regenerate_id() {
|
|||
if ( session_status() != PHP_SESSION_ACTIVE ) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
// Set deleted timestamp. Session data must not be deleted immediately for reasons.
|
||||
$_SESSION['last_time'] = time();
|
||||
// Finish session
|
||||
session_write_close();
|
||||
|
||||
session_start();
|
||||
session_regenerate_id();
|
||||
unset($_SESSION['last_time']);
|
||||
} // function zm_session_regenerate_id()
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
namespace ZM;
|
||||
|
||||
error_reporting(E_ALL);
|
||||
|
||||
|
@ -72,7 +71,7 @@ define('ZM_BASE_URL', '');
|
|||
|
||||
require_once('includes/functions.php');
|
||||
if ( $_SERVER['REQUEST_METHOD'] == 'OPTIONS' ) {
|
||||
Logger::Debug("OPTIONS Method, only doing CORS");
|
||||
ZM\Logger::Debug("OPTIONS Method, only doing CORS");
|
||||
# Add Cross domain access headers
|
||||
CORSHeaders();
|
||||
return;
|
||||
|
@ -159,7 +158,7 @@ CORSHeaders();
|
|||
|
||||
// Check for valid content dirs
|
||||
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
|
||||
|
@ -181,7 +180,7 @@ if ( isset($_REQUEST['request']) )
|
|||
require_once('includes/auth.php');
|
||||
|
||||
foreach ( getSkinIncludes('skin.php') as $includeFile ) {
|
||||
#Logger::Debug("including $includeFile");
|
||||
#ZM\Logger::Debug("including $includeFile");
|
||||
require_once $includeFile;
|
||||
}
|
||||
|
||||
|
@ -194,7 +193,7 @@ isset($view) || $view = NULL;
|
|||
isset($request) || $request = NULL;
|
||||
isset($action) || $action = NULL;
|
||||
|
||||
Logger::Debug("View: $view Request: $request Action: $action User: " . ( isset($user) ? $user['Username'] : 'none' ));
|
||||
ZM\Logger::Debug("View: $view Request: $request Action: $action User: " . ( isset($user) ? $user['Username'] : 'none' ));
|
||||
if (
|
||||
ZM_ENABLE_CSRF_MAGIC &&
|
||||
( $action != 'login' ) &&
|
||||
|
@ -205,17 +204,17 @@ if (
|
|||
( $view != 'archive' )
|
||||
) {
|
||||
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();
|
||||
}
|
||||
|
||||
# Need to include actions because it does auth
|
||||
if ( $action ) {
|
||||
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');
|
||||
} else {
|
||||
Warning("No includes/actions/$view.php for action $action");
|
||||
ZM\Warning("No includes/actions/$view.php for action $action");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,19 +226,20 @@ if ( ZM_OPT_USE_AUTH and !isset($user) and ($view != 'login') ) {
|
|||
header('HTTP/1.1 401 Unauthorized');
|
||||
exit;
|
||||
}
|
||||
Logger::Debug('Redirecting to login');
|
||||
ZM\Logger::Debug('Redirecting to login');
|
||||
$view = 'none';
|
||||
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=login';
|
||||
$request = null;
|
||||
} else if ( ZM_SHOW_PRIVACY && ($view != 'privacy') && ($view != 'options') && (!$request) && canEdit('System') ) {
|
||||
$view = 'none';
|
||||
$redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=privacy';
|
||||
$request = null;
|
||||
}
|
||||
|
||||
CSPHeaders($view, $cspNonce);
|
||||
|
||||
if ( $redirect ) {
|
||||
Logger::Debug("Redirecting to $redirect");
|
||||
ZM\Logger::Debug("Redirecting to $redirect");
|
||||
header('Location: '.$redirect);
|
||||
return;
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ if ( $redirect ) {
|
|||
if ( $request ) {
|
||||
foreach ( getSkinIncludes('ajax/'.$request.'.php', true, true) as $includeFile ) {
|
||||
if ( !file_exists($includeFile) )
|
||||
Fatal("Request '$request' does not exist");
|
||||
ZM\Fatal("Request '$request' does not exist");
|
||||
require_once $includeFile;
|
||||
}
|
||||
return;
|
||||
|
@ -256,7 +256,7 @@ if ( $request ) {
|
|||
if ( $includeFiles = getSkinIncludes('views/'.$view.'.php', true, true) ) {
|
||||
foreach ( $includeFiles as $includeFile ) {
|
||||
if ( !file_exists($includeFile) )
|
||||
Fatal("View '$view' does not exist");
|
||||
ZM\Fatal("View '$view' does not exist");
|
||||
require_once $includeFile;
|
||||
}
|
||||
// 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',
|
||||
'CanPan' => 'Podržava Pomak' ,
|
||||
'CanReset' => 'PodržavaReset',
|
||||
'CanReboot' => 'Can Reboot',
|
||||
'CanSetPresets' => 'Podržava presetove',
|
||||
'CanSleep' => 'Podržava Sleep',
|
||||
'CanTilt' => 'Podržava nagib',
|
||||
|
|
|
@ -209,6 +209,7 @@ $SLANG = array(
|
|||
'CanMoveRel' => 'Can Move Relative',
|
||||
'CanPan' => 'Can Pan' ,
|
||||
'CanReset' => 'Can Reset',
|
||||
'CanReboot' => 'Can Reboot',
|
||||
'CanSetPresets' => 'Can Set Presets',
|
||||
'CanSleep' => 'Can Sleep',
|
||||
'CanTilt' => 'Can Tilt',
|
||||
|
|
|
@ -205,6 +205,7 @@ $SLANG = array(
|
|||
'CanMoveRel' => '可以相对移动',
|
||||
'CanPan' => '可以平移' ,
|
||||
'CanReset' => '可以复位',
|
||||
'CanReboot' => 'Can Reboot',
|
||||
'CanSetPresets' => '可以进行预设',
|
||||
'CanSleep' => '可以休眠',
|
||||
'CanTilt' => '可以倾斜',
|
||||
|
|
|
@ -205,6 +205,7 @@ $SLANG = array(
|
|||
'CanMoveRel' => 'Umí relativní pohyb',
|
||||
'CanPan' => 'Umí otáčení',
|
||||
'CanReset' => 'Umí reset',
|
||||
'CanReboot' => 'Can Reboot',
|
||||
'CanSetPresets' => 'Umí navolit předvolby',
|
||||
'CanSleep' => 'Může spát',
|
||||
'CanTilt' => 'Umí náklon',
|
||||
|
|
|
@ -207,6 +207,7 @@ $SLANG = array(
|
|||
'CanMoveRel' => 'Kann relative Bewegung',
|
||||
'CanPan' => 'Kann Pan' ,
|
||||
'CanReset' => 'Kann Reset',
|
||||
'CanReboot' => 'Can Reboot',
|
||||
'CanSetPresets' => 'Kann Voreinstellungen setzen',
|
||||
'CanSleep' => 'Kann Sleep',
|
||||
'CanTilt' => 'Kann Neigung',
|
||||
|
|
|
@ -206,6 +206,7 @@ $SLANG = array(
|
|||
'CanMoveRel' => 'Can Move Relative',
|
||||
'CanPan' => 'Can Pan' ,
|
||||
'CanReset' => 'Can Reset',
|
||||
'CanReboot' => 'Can Reboot',
|
||||
'CanSetPresets' => 'Can Set Presets',
|
||||
'CanSleep' => 'Can Sleep',
|
||||
'CanTilt' => 'Can Tilt',
|
||||
|
|
|
@ -216,6 +216,7 @@ $SLANG = array(
|
|||
'CanMoveRel' => 'Can Move Relative',
|
||||
'CanPan' => 'Can Pan' ,
|
||||
'CanReset' => 'Can Reset',
|
||||
'CanReboot' => 'Can Reboot',
|
||||
'CanSetPresets' => 'Can Set Presets',
|
||||
'CanSleep' => 'Can Sleep',
|
||||
'CanTilt' => 'Can Tilt',
|
||||
|
@ -271,6 +272,7 @@ $SLANG = array(
|
|||
'Debug' => 'Debug',
|
||||
'DefaultRate' => 'Default Rate',
|
||||
'DefaultScale' => 'Default Scale',
|
||||
'DefaultCodec' => 'Default Method For Live View',
|
||||
'DefaultView' => 'Default View',
|
||||
'Deinterlacing' => 'Deinterlacing',
|
||||
'RTSPDescribe' => 'Use RTSP Response Media URL',
|
||||
|
@ -324,7 +326,9 @@ $SLANG = array(
|
|||
'Events' => 'Events',
|
||||
'Exclude' => 'Exclude',
|
||||
'Execute' => 'Execute',
|
||||
'ExportCompress' => 'Use Compression',
|
||||
'ExportDetails' => 'Export Event Details',
|
||||
'ExportMatches' => 'Export Matches',
|
||||
'Exif' => 'Embed EXIF data into image',
|
||||
'Export' => 'Export',
|
||||
'DownloadVideo' => 'Download Video',
|
||||
|
|
|
@ -156,6 +156,7 @@ $SLANG = array(
|
|||
'CanMoveRel' => 'Can Move Relative',
|
||||
'CanPan' => 'Can Pan' ,
|
||||
'CanReset' => 'Can Reset',
|
||||
'CanReboot' => 'Can Reboot',
|
||||
'CanSetPresets' => 'Can Set Presets',
|
||||
'CanSleep' => 'Can Sleep',
|
||||
'CanTilt' => 'Can Tilt',
|
||||
|
|
|
@ -205,6 +205,7 @@ $SLANG = array(
|
|||
'CanMoveRel' => 'Puede moverse de forma relativa',
|
||||
'CanPan' => 'Puede desplazarse' ,
|
||||
'CanReset' => 'Puede restablecerse',
|
||||
'CanReboot' => 'Can Reboot',
|
||||
'CanSetPresets' => 'Puede fefinir programaciones',
|
||||
'CanSleep' => 'Puede dormirse',
|
||||
'CanTilt' => 'Puede inclinarse',
|
||||
|
|
|
@ -212,6 +212,7 @@ $SLANG = array(
|
|||
'CanMoveRel' => 'Can Move Relative',
|
||||
'CanPan' => 'Can Pan' ,
|
||||
'CanReset' => 'Can Reset',
|
||||
'CanReboot' => 'Can Reboot',
|
||||
'CanSetPresets' => 'Can Set Presets',
|
||||
'CanSleep' => 'Can Sleep',
|
||||
'CanTilt' => 'Can Tilt',
|
||||
|
|
|
@ -211,6 +211,7 @@ $SLANG = array(
|
|||
'CanMoveRel' => 'Relatif',
|
||||
'CanPan' => 'Panoramique' ,
|
||||
'CanReset' => 'RàZ',
|
||||
'CanReboot' => 'Can Reboot',
|
||||
'CanSetPresets' => 'Stockage prépos.',
|
||||
'CanSleep' => 'Veille',
|
||||
'CanTilt' => 'Inclinaison',
|
||||
|
|
|
@ -205,6 +205,7 @@ $SLANG = array(
|
|||
'CanMoveRel' => 'àôùø úæåæä éçñéú',
|
||||
'CanPan' => 'Can Pan' ,
|
||||
'CanReset' => 'àôùø àúçåì',
|
||||
'CanReboot' => 'Can Reboot',
|
||||
'CanSetPresets' => 'Can Set Presets',
|
||||
'CanSleep' => 'àôùø îöá ùéðä',
|
||||
'CanTilt' => 'àôùø æòæåò',
|
||||
|
|
|
@ -248,6 +248,7 @@ $SLANG = array(
|
|||
'CanMoveRel' => 'Relatíven tud mozogni',
|
||||
'CanPan' => 'Tud jobb-bal mozgást' ,
|
||||
'CanReset' => 'Tud alaphelyzetbe jönni',
|
||||
'CanReboot' => 'Can Reboot',
|
||||
'CanSetPresets' => 'Tud menteni profilokat',
|
||||
'CanSleep' => 'Tud phihenő üzemmódot',
|
||||
'CanTilt' => 'Tud fel-le mozgást',
|
||||
|
|
|
@ -210,6 +210,7 @@ $SLANG = array(
|
|||
'CanMoveRel' => 'Puo\' Mov. Relativo',
|
||||
'CanPan' => 'Puo\' Pan' ,
|
||||
'CanReset' => 'Puo\' Reset',
|
||||
'CanReboot' => 'Can Reboot',
|
||||
'CanSetPresets' => 'Puo\' impostare preset',
|
||||
'CanSleep' => 'Puo\' andare in sleep',
|
||||
'CanTilt' => 'Puo\' Tilt',
|
||||
|
|
|
@ -206,6 +206,7 @@ $SLANG = array(
|
|||
'CanMoveRel' => 'Can Move Relative',
|
||||
'CanPan' => 'Can Pan' ,
|
||||
'CanReset' => 'Can Reset',
|
||||
'CanReboot' => 'Can Reboot',
|
||||
'CanSetPresets' => 'Can Set Presets',
|
||||
'CanSleep' => 'Can Sleep',
|
||||
'CanTilt' => 'Can Tilt',
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue