Merge branch 'storageareas' into zma_to_thread
This commit is contained in:
commit
03d3f5e665
12
.travis.yml
12
.travis.yml
|
@ -23,7 +23,17 @@ addons:
|
|||
- curl
|
||||
- sshfs
|
||||
- sed
|
||||
- binfmt-support
|
||||
- qemu
|
||||
- qemu-user-static
|
||||
install:
|
||||
- update-binfmts --enable qemu-arm
|
||||
|
||||
env:
|
||||
global:
|
||||
- DEB_BUILD_OPTIONS="parallel=4"
|
||||
- DEBUILD_LINTIAN="no"
|
||||
- SMPFLAGS="-j4"
|
||||
matrix:
|
||||
- OS=el DIST=6
|
||||
- OS=el DIST=6 ARCH=i386 DOCKER_REPO=knnniggett/packpack
|
||||
|
@ -34,6 +44,8 @@ env:
|
|||
- OS=ubuntu DIST=xenial
|
||||
- OS=ubuntu DIST=trusty ARCH=i386
|
||||
- OS=ubuntu DIST=xenial ARCH=i386
|
||||
- OS=raspbian DIST=stretch ARCH=armhf DOCKER_REPO=knnniggett/packpack
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
services:
|
||||
|
|
|
@ -58,6 +58,7 @@ if(NOT HOST_OS)
|
|||
"ZoneMinder was unable to deterimine the host OS. Please report this. Value of CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
|
||||
endif(NOT HOST_OS)
|
||||
|
||||
set (CMAKE_CXX_STANDARD 11)
|
||||
# Default CLFAGS and CXXFLAGS:
|
||||
set(CMAKE_C_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2")
|
||||
|
|
|
@ -11,3 +11,6 @@ install(FILES ${dbfileslist} DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db
|
|||
|
||||
# install zm_create.sql
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm_create.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
|
||||
|
||||
# install triggers.sql
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/triggers.sql" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/db")
|
||||
|
|
|
@ -228,37 +228,6 @@ CREATE TABLE `Events_Hour` (
|
|||
KEY `Events_Hour_StartTime_idx` (`StartTime`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
delimiter //
|
||||
DROP TRIGGER IF EXISTS Events_Hour_delete_trigger//
|
||||
CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour
|
||||
FOR EACH ROW BEGIN
|
||||
UPDATE Monitors SET
|
||||
HourEvents = COALESCE(HourEvents,1)-1,
|
||||
HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END;
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS Events_Hour_update_trigger//
|
||||
|
||||
CREATE TRIGGER Events_Hour_update_trigger AFTER UPDATE ON Events_Hour
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( diff ) THEN
|
||||
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
|
||||
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
|
||||
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)-COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||
ELSE
|
||||
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
END;
|
||||
//
|
||||
DELIMITER ;
|
||||
|
||||
DROP TABLE IF EXISTS `Events_Day`;
|
||||
CREATE TABLE `Events_Day` (
|
||||
`EventId` int(10) unsigned NOT NULL,
|
||||
|
@ -270,37 +239,6 @@ CREATE TABLE `Events_Day` (
|
|||
KEY `Events_Day_StartTime_idx` (`StartTime`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
delimiter //
|
||||
DROP TRIGGER IF EXISTS Events_Day_delete_trigger//
|
||||
CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day
|
||||
FOR EACH ROW BEGIN
|
||||
UPDATE Monitors SET
|
||||
DayEvents = COALESCE(DayEvents,1)-1,
|
||||
DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END;
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS Events_Day_update_trigger;
|
||||
CREATE TRIGGER Events_Day_update_trigger AFTER UPDATE ON Events_Day
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( diff ) THEN
|
||||
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
|
||||
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
|
||||
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||
ELSE
|
||||
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
END;
|
||||
//
|
||||
|
||||
|
||||
DELIMITER ;
|
||||
DROP TABLE IF EXISTS `Events_Week`;
|
||||
CREATE TABLE `Events_Week` (
|
||||
`EventId` int(10) unsigned NOT NULL,
|
||||
|
@ -312,37 +250,6 @@ CREATE TABLE `Events_Week` (
|
|||
KEY `Events_Week_StartTime_idx` (`StartTime`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
delimiter //
|
||||
DROP TRIGGER IF EXISTS Events_Week_delete_trigger//
|
||||
CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week
|
||||
FOR EACH ROW BEGIN
|
||||
UPDATE Monitors SET
|
||||
WeekEvents = COALESCE(WeekEvents,1)-1,
|
||||
WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END;
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS Events_Week_update_trigger;
|
||||
CREATE TRIGGER Events_Week_update_trigger AFTER UPDATE ON Events_Week
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( diff ) THEN
|
||||
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
|
||||
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0) WHERE Monitors.Id=OLD.MonitorId;
|
||||
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||
ELSE
|
||||
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
END;
|
||||
//
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
DROP TABLE IF EXISTS `Events_Month`;
|
||||
CREATE TABLE `Events_Month` (
|
||||
`EventId` int(10) unsigned NOT NULL,
|
||||
|
@ -354,38 +261,6 @@ CREATE TABLE `Events_Month` (
|
|||
KEY `Events_Month_StartTime_idx` (`StartTime`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
delimiter //
|
||||
DROP TRIGGER IF EXISTS Events_Month_delete_trigger//
|
||||
CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month
|
||||
FOR EACH ROW BEGIN
|
||||
UPDATE Monitors SET
|
||||
MonthEvents = COALESCE(MonthEvents,1)-1,
|
||||
MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END;
|
||||
//
|
||||
|
||||
|
||||
DROP TRIGGER IF EXISTS Events_Month_update_trigger;
|
||||
CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( diff ) THEN
|
||||
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
|
||||
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace) WHERE Monitors.Id=OLD.MonitorId;
|
||||
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId;
|
||||
ELSE
|
||||
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
END;
|
||||
//
|
||||
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
DROP TABLE IF EXISTS `Events_Archived`;
|
||||
CREATE TABLE `Events_Archived` (
|
||||
|
@ -396,135 +271,6 @@ CREATE TABLE `Events_Archived` (
|
|||
KEY `Events_Archived_MonitorId_idx` (`MonitorId`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
|
||||
drop procedure if exists update_storage_stats;
|
||||
|
||||
delimiter //
|
||||
|
||||
create procedure update_storage_stats(IN StorageId smallint(5), IN space BIGINT)
|
||||
|
||||
sql security invoker
|
||||
|
||||
deterministic
|
||||
|
||||
begin
|
||||
|
||||
update Storage set DiskSpace = COALESCE(DiskSpace,0) + COALESCE(space,0) where Id = StorageId;
|
||||
|
||||
end;
|
||||
|
||||
//
|
||||
|
||||
drop trigger if exists event_update_trigger//
|
||||
|
||||
CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( NEW.StorageId = OLD.StorageID ) THEN
|
||||
IF ( diff ) THEN
|
||||
call update_storage_stats(OLD.StorageId, diff);
|
||||
END IF;
|
||||
ELSE
|
||||
IF ( NEW.DiskSpace ) THEN
|
||||
call update_storage_stats(NEW.StorageId, NEW.DiskSpace);
|
||||
END IF;
|
||||
IF ( OLD.DiskSpace ) THEN
|
||||
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
|
||||
IF ( NEW.Archived != OLD.Archived ) THEN
|
||||
IF ( NEW.Archived ) THEN
|
||||
INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace);
|
||||
UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId;
|
||||
ELSEIF ( OLD.Archived ) THEN
|
||||
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
|
||||
UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)-1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) WHERE Id=OLD.MonitorId;
|
||||
ELSE
|
||||
IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
|
||||
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
UPDATE Monitors SET
|
||||
ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
ELSE IF ( NEW.Archived AND diff ) THEN
|
||||
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
END IF;
|
||||
|
||||
IF ( diff ) THEN
|
||||
UPDATE Monitors SET TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=OLD.MonitorId;
|
||||
END IF;
|
||||
|
||||
END;
|
||||
|
||||
//
|
||||
|
||||
delimiter ;
|
||||
|
||||
DROP TRIGGER IF EXISTS event_insert_trigger;
|
||||
|
||||
delimiter //
|
||||
/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count.
|
||||
* The DiskSpace will get update in the Event Update Trigger
|
||||
*/
|
||||
CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
|
||||
INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
|
||||
INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
|
||||
INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
|
||||
INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
|
||||
UPDATE Monitors SET
|
||||
HourEvents = COALESCE(DayEvents,0)+1,
|
||||
DayEvents = COALESCE(DayEvents,0)+1,
|
||||
WeekEvents = COALESCE(DayEvents,0)+1,
|
||||
MonthEvents = COALESCE(DayEvents,0)+1,
|
||||
TotalEvents = COALESCE(TotalEvents,0)+1
|
||||
WHERE Id=NEW.MonitorId;
|
||||
END;
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS event_delete_trigger//
|
||||
|
||||
CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF ( OLD.DiskSpace ) THEN
|
||||
call update_storage_stats(OLD.StorageId, -OLD.DiskSpace);
|
||||
END IF;
|
||||
DELETE FROM Events_Hour WHERE EventId=OLD.Id;
|
||||
DELETE FROM Events_Day WHERE EventId=OLD.Id;
|
||||
DELETE FROM Events_Week WHERE EventId=OLD.Id;
|
||||
DELETE FROM Events_Month WHERE EventId=OLD.Id;
|
||||
IF ( OLD.Archived ) THEN
|
||||
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
|
||||
UPDATE Monitors SET
|
||||
ArchivedEvents = COALESCE(ArchivedEvents,1) - 1,
|
||||
ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),
|
||||
TotalEvents = COALESCE(TotalEvents,1) - 1,
|
||||
TotalEventDiskSpace = COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
ELSE
|
||||
UPDATE Monitors SET
|
||||
TotalEvents = COALESCE(TotalEvents,1)-1,
|
||||
TotalEventDiskSpace=COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END IF;
|
||||
END;
|
||||
|
||||
//
|
||||
|
||||
delimiter ;
|
||||
--
|
||||
-- Table structure for table `Filters`
|
||||
--
|
||||
|
@ -950,24 +696,6 @@ CREATE TABLE `Zones` (
|
|||
KEY `MonitorId` (`MonitorId`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
DELIMITER //
|
||||
DROP TRIGGER IF EXISTS Zone_Insert_Trigger//
|
||||
CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID;
|
||||
END
|
||||
//
|
||||
DROP TRIGGER IF EXISTS Zone_Delete_Trigger//
|
||||
CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID;
|
||||
END
|
||||
//
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
DROP TABLE IF EXISTS `Storage`;
|
||||
CREATE TABLE `Storage` (
|
||||
`Id` smallint(5) unsigned NOT NULL auto_increment,
|
||||
|
@ -1041,6 +769,9 @@ INSERT INTO `Controls` VALUES (NULL,'Keekoon','Remote','Keekoon', 0, 0, 0, 0, 0,
|
|||
INSERT INTO `Controls` VALUES (NULL,'HikVision','Local','',0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,0,1,1,0,0,0,0,1,1,100,0,0,1,0,0,0,0,1,1,100,1,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'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);
|
||||
|
||||
--
|
||||
-- Add some monitor preset values
|
||||
|
@ -1139,7 +870,7 @@ CREATE TABLE Maps (
|
|||
PRIMARY KEY (`Id`)
|
||||
);
|
||||
|
||||
DROP TABLE IF EXISTS MontageLayout;
|
||||
DROP TABLE IF EXISTS MontageLayouts;
|
||||
|
||||
CREATE TABLE MontageLayouts (
|
||||
`Id` int(10) unsigned NOT NULL auto_increment,
|
||||
|
|
|
@ -5,6 +5,9 @@ set -e
|
|||
if [ "$1" = "configure" ]; then
|
||||
|
||||
. /etc/zm/zm.conf
|
||||
for i in /etc/zm/conf.d/*.conf; do
|
||||
. $i
|
||||
done;
|
||||
|
||||
# The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group.
|
||||
chown www-data:root /var/log/zm
|
||||
|
|
|
@ -5,6 +5,10 @@ set -e
|
|||
if [ "$1" = "configure" ]; then
|
||||
|
||||
. /etc/zm/zm.conf
|
||||
for i in /etc/zm/conf.d/*.conf; do
|
||||
. $i
|
||||
done;
|
||||
|
||||
|
||||
# The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group
|
||||
chown www-data:root /var/log/zm
|
||||
|
|
|
@ -16,7 +16,7 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa
|
|||
,libcurl4-gnutls-dev
|
||||
,libgnutls-openssl-dev
|
||||
,libjpeg8-dev | libjpeg9-dev | libjpeg62-turbo-dev
|
||||
,libmysqlclient-dev | libmariadbclient-dev
|
||||
,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev
|
||||
,libpcre3-dev
|
||||
,libpolkit-gobject-1-dev
|
||||
,libv4l-dev (>= 0.8.3) [!hurd-any]
|
||||
|
|
|
@ -5,6 +5,9 @@ set -e
|
|||
if [ "$1" = "configure" ]; then
|
||||
|
||||
. /etc/zm/zm.conf
|
||||
for i in /etc/zm/conf.d/*.conf; do
|
||||
. $i
|
||||
done;
|
||||
|
||||
# The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group
|
||||
chown www-data:root /var/log/zm
|
||||
|
|
|
@ -44,7 +44,7 @@ Clone the ZoneMinder project if you have not done so already.
|
|||
|
||||
::
|
||||
|
||||
git clone https://ZoneMinder/ZoneMinder
|
||||
git clone https://github.com/ZoneMinder/ZoneMinder
|
||||
cd ZoneMinder
|
||||
|
||||
Alternatively, if you have already cloned the repo and wish to update it, do the following.
|
||||
|
|
|
@ -0,0 +1,630 @@
|
|||
# ==========================================================================
|
||||
#
|
||||
# ZoneMinder Reolink IP Control Protocol Module, Date: 2016-01-19
|
||||
# Converted for use with Reolink IP Camera by Chris Swertfeger
|
||||
# Copyright (C) 2016 Chris Swertfeger
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# ==========================================================================
|
||||
#
|
||||
# This module contains the first implementation of the Reolink IP camera control
|
||||
# protocol
|
||||
#
|
||||
package ZoneMinder::Control::Reolink;
|
||||
|
||||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
require ZoneMinder::Base;
|
||||
require ZoneMinder::Control;
|
||||
|
||||
our @ISA = qw(ZoneMinder::Control);
|
||||
|
||||
our %CamParams = ();
|
||||
|
||||
# ==========================================================================
|
||||
#
|
||||
# Reolink IP Control Protocol
|
||||
# This script sends ONVIF compliant commands and may work with other cameras
|
||||
# that require authentication
|
||||
#
|
||||
# The script was developed against a RLC-423 and RLC-420.
|
||||
#
|
||||
# Basic preset functions are supported, but more advanced features, which make
|
||||
# use of abnormally high preset numbers (ir lamp control, tours, pan speed, etc)
|
||||
# may or may not work.
|
||||
#
|
||||
#
|
||||
# On ControlAddress use the format :
|
||||
# USERNAME:PASSWORD@ADDRESS:PORT
|
||||
# eg : admin:pass@10.1.2.1:8899
|
||||
# admin:password@10.0.100.1:8899
|
||||
#
|
||||
# Use port 8000 by default for Reolink cameras
|
||||
#
|
||||
# Make sure and place a value in the Auto Stop Timeout field.
|
||||
# Recommend starting with a value of 1 second, and adjust accordingly.
|
||||
#
|
||||
# ==========================================================================
|
||||
|
||||
use ZoneMinder::Logger qw(:all);
|
||||
use ZoneMinder::Config qw(:all);
|
||||
|
||||
use Time::HiRes qw( usleep );
|
||||
|
||||
use MIME::Base64;
|
||||
use Digest::SHA;
|
||||
use DateTime;
|
||||
|
||||
my ($username,$password,$host,$port);
|
||||
|
||||
sub new
|
||||
{
|
||||
|
||||
my $class = shift;
|
||||
my $id = shift;
|
||||
my $self = ZoneMinder::Control->new( $id );
|
||||
my $logindetails = "";
|
||||
bless( $self, $class );
|
||||
srand( time() );
|
||||
return $self;
|
||||
}
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
sub AUTOLOAD
|
||||
{
|
||||
my $self = shift;
|
||||
my $class = ref( ) || croak( "$self not object" );
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://;
|
||||
if ( exists($self->{$name}) )
|
||||
{
|
||||
return( $self->{$name} );
|
||||
}
|
||||
Fatal( "Can't access $name member of object of class $class" );
|
||||
}
|
||||
|
||||
sub open
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
$self->loadMonitor();
|
||||
#
|
||||
# Extract the username/password host/port from ControlAddress
|
||||
#
|
||||
if( $self->{Monitor}{ControlAddress} =~ /^([^:]+):([^@]+)@(.+)/ )
|
||||
{ # user:pass@host...
|
||||
$username = $1;
|
||||
$password = $2;
|
||||
$host = $3;
|
||||
}
|
||||
elsif( $self->{Monitor}{ControlAddress} =~ /^([^@]+)@(.+)/ )
|
||||
{ # user@host...
|
||||
$username = $1;
|
||||
$host = $2;
|
||||
}
|
||||
else { # Just a host
|
||||
$host = $self->{Monitor}{ControlAddress};
|
||||
}
|
||||
# Check if it is a host and port or just a host
|
||||
if( $host =~ /([^:]+):(.+)/ )
|
||||
{
|
||||
$host = $1;
|
||||
$port = $2;
|
||||
}
|
||||
else
|
||||
{
|
||||
$port = 80;
|
||||
}
|
||||
|
||||
use LWP::UserAgent;
|
||||
$self->{ua} = LWP::UserAgent->new;
|
||||
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
|
||||
|
||||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub close
|
||||
{
|
||||
my $self = shift;
|
||||
$self->{state} = 'closed';
|
||||
}
|
||||
|
||||
sub printMsg
|
||||
{
|
||||
my $self = shift;
|
||||
my $msg = shift;
|
||||
my $msg_len = length($msg);
|
||||
|
||||
Debug( $msg."[".$msg_len."]" );
|
||||
}
|
||||
|
||||
sub sendCmd
|
||||
{
|
||||
my $self = shift;
|
||||
my $cmd = shift;
|
||||
my $msg = shift;
|
||||
my $content_type = shift;
|
||||
my $result = undef;
|
||||
|
||||
printMsg( $cmd, "Tx" );
|
||||
|
||||
my $server_endpoint = "http://".$host.":".$port."/$cmd";
|
||||
my $req = HTTP::Request->new( POST => $server_endpoint );
|
||||
$req->header('content-type' => $content_type);
|
||||
$req->header('Host' => $host.":".$port);
|
||||
$req->header('content-length' => length($msg));
|
||||
$req->header('accept-encoding' => 'gzip, deflate');
|
||||
$req->header('connection' => 'close');
|
||||
$req->content($msg);
|
||||
|
||||
my $res = $self->{ua}->request($req);
|
||||
|
||||
if ( $res->is_success ) {
|
||||
$result = !undef;
|
||||
} else {
|
||||
Error( "After sending PTZ command, camera returned the following error:'".$res->status_line()."'" );
|
||||
}
|
||||
return( $result );
|
||||
}
|
||||
|
||||
sub getCamParams
|
||||
{
|
||||
my $self = shift;
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken></GetImagingSettings></s:Body></s:Envelope>';
|
||||
my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/onvif/imaging";
|
||||
my $req = HTTP::Request->new( POST => $server_endpoint );
|
||||
$req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"');
|
||||
$req->header('Host' => $host.":".$port);
|
||||
$req->header('content-length' => length($msg));
|
||||
$req->header('accept-encoding' => 'gzip, deflate');
|
||||
$req->header('connection' => 'Close');
|
||||
$req->content($msg);
|
||||
|
||||
my $res = $self->{ua}->request($req);
|
||||
|
||||
if ( $res->is_success ) {
|
||||
# We should really use an xml or soap library to parse the xml tags
|
||||
my $content = $res->decoded_content;
|
||||
|
||||
if ($content =~ /.*<tt:(Brightness)>(.+)<\/tt:Brightness>.*/) {
|
||||
$CamParams{$1} = $2;
|
||||
}
|
||||
if ($content =~ /.*<tt:(Contrast)>(.+)<\/tt:Contrast>.*/) {
|
||||
$CamParams{$1} = $2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error( "Unable to retrieve camera image settings:'".$res->status_line()."'" );
|
||||
}
|
||||
}
|
||||
|
||||
#autoStop
|
||||
#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab
|
||||
sub autoStop
|
||||
{
|
||||
my $self = shift;
|
||||
my $autostop = shift;
|
||||
|
||||
if( $autostop ) {
|
||||
Debug( "Auto Stop" );
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
usleep( $autostop );
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
}
|
||||
}
|
||||
|
||||
# Reset the Camera
|
||||
sub reset
|
||||
{
|
||||
Debug( "Camera Reset" );
|
||||
my $self = shift;
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $cmd = "";
|
||||
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SystemReboot xmlns="http://www.onvif.org/ver10/device/wsdl"/></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
}
|
||||
|
||||
#Up Arrow
|
||||
sub moveConUp
|
||||
{
|
||||
Debug( "Move Up" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Down Arrow
|
||||
sub moveConDown
|
||||
{
|
||||
Debug( "Move Down" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Left Arrow
|
||||
sub moveConLeft
|
||||
{
|
||||
Debug( "Move Left" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Right Arrow
|
||||
sub moveConRight
|
||||
{
|
||||
Debug( "Move Right" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.49" y="0" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Zoom In
|
||||
sub zoomConTele
|
||||
{
|
||||
Debug( "Zoom Tele" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Zoom Out
|
||||
sub zoomConWide
|
||||
{
|
||||
Debug( "Zoom Wide" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><Zoom x="-0.49" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Diagonally Up Right Arrow
|
||||
#This camera does not have builtin diagonal commands so we emulate them
|
||||
sub moveConUpRight
|
||||
{
|
||||
Debug( "Move Diagonally Up Right" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Diagonally Down Right Arrow
|
||||
#This camera does not have builtin diagonal commands so we emulate them
|
||||
sub moveConDownRight
|
||||
{
|
||||
Debug( "Move Diagonally Down Right" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Diagonally Up Left Arrow
|
||||
#This camera does not have builtin diagonal commands so we emulate them
|
||||
sub moveConUpLeft
|
||||
{
|
||||
Debug( "Move Diagonally Up Left" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Diagonally Down Left Arrow
|
||||
#This camera does not have builtin diagonal commands so we emulate them
|
||||
sub moveConDownLeft
|
||||
{
|
||||
Debug( "Move Diagonally Down Left" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><Velocity><PanTilt x="-0.5" y="-0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
$self->autoStop( $self->{Monitor}->{AutoStopTimeout} );
|
||||
}
|
||||
|
||||
#Stop
|
||||
sub moveStop
|
||||
{
|
||||
Debug( "Move Stop" );
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PanTilt>true</PanTilt><Zoom>false</Zoom></Stop></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
}
|
||||
|
||||
#Set Camera Preset
|
||||
sub presetSet
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam( $params, 'preset' );
|
||||
Debug( "Set Preset $preset" );
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetPreset xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PresetToken>'.$preset.'</PresetToken></SetPreset></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/SetPreset"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
}
|
||||
|
||||
#Recall Camera Preset
|
||||
sub presetGoto
|
||||
{
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam( $params, 'preset' );
|
||||
my $num = sprintf("%03d", $preset);
|
||||
$num=~ tr/ /0/;
|
||||
Debug( "Goto Preset $preset" );
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><GotoPreset xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>000</ProfileToken><PresetToken>'.$num.'</PresetToken></GotoPreset></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
}
|
||||
|
||||
#Horizontal Patrol
|
||||
#To be determined if this camera supports this feature
|
||||
sub horizontalPatrol
|
||||
{
|
||||
Debug( "Horizontal Patrol" );
|
||||
my $self = shift;
|
||||
my $cmd = '';
|
||||
my $msg ='';
|
||||
my $content_type = '';
|
||||
# $self->sendCmd( $cmd, $msg, $content_type );
|
||||
Error( "PTZ Command not implemented in control script." );
|
||||
}
|
||||
|
||||
#Horizontal Patrol Stop
|
||||
#To be determined if this camera supports this feature
|
||||
sub horizontalPatrolStop
|
||||
{
|
||||
Debug( "Horizontal Patrol Stop" );
|
||||
my $self = shift;
|
||||
my $cmd = '';
|
||||
my $msg ='';
|
||||
my $content_type = '';
|
||||
# $self->sendCmd( $cmd, $msg, $content_type );
|
||||
Error( "PTZ Command not implemented in control script." );
|
||||
}
|
||||
|
||||
# Increase Brightness
|
||||
sub irisAbsOpen
|
||||
{
|
||||
Debug( "Iris $CamParams{'Brightness'}" );
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->getCamParams() unless($CamParams{'Brightness'});
|
||||
my $step = $self->getParam( $params, 'step' );
|
||||
my $max = 100;
|
||||
|
||||
$CamParams{'Brightness'} += $step;
|
||||
$CamParams{'Brightness'} = $max if ($CamParams{'Brightness'} > $max);
|
||||
|
||||
my $cmd = 'onvif/imaging';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Brightness'}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
}
|
||||
|
||||
# Decrease Brightness
|
||||
sub irisAbsClose
|
||||
{
|
||||
Debug( "Iris $CamParams{'Brightness'}" );
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->getCamParams() unless($CamParams{'brightness'});
|
||||
my $step = $self->getParam( $params, 'step' );
|
||||
my $min = 0;
|
||||
|
||||
$CamParams{'Brightness'} -= $step;
|
||||
$CamParams{'Brightness'} = $min if ($CamParams{'Brightness'} < $min);
|
||||
|
||||
my $cmd = 'onvif/imaging';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Brightness xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Brightness'}.'</Brightness></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
|
||||
$self->sendCmd( $cmd, $msg, $content_type );
|
||||
}
|
||||
|
||||
# Increase Contrast
|
||||
sub whiteAbsIn
|
||||
{
|
||||
Debug( "Iris $CamParams{'Contrast'}" );
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->getCamParams() unless($CamParams{'Contrast'});
|
||||
my $step = $self->getParam( $params, 'step' );
|
||||
my $max = 100;
|
||||
|
||||
$CamParams{'Contrast'} += $step;
|
||||
$CamParams{'Contrast'} = $max if ($CamParams{'Contrast'} > $max);
|
||||
|
||||
my $cmd = 'onvif/imaging';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Contrast'}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
|
||||
}
|
||||
|
||||
# Decrease Contrast
|
||||
sub whiteAbsOut
|
||||
{
|
||||
Debug( "Iris $CamParams{'Contrast'}" );
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
$self->getCamParams() unless($CamParams{'Contrast'});
|
||||
my $step = $self->getParam( $params, 'step' );
|
||||
my $min = 0;
|
||||
|
||||
$CamParams{'Contrast'} -= $step;
|
||||
$CamParams{'Contrast'} = $min if ($CamParams{'Contrast'} < $min);
|
||||
|
||||
my $cmd = 'onvif/imaging';
|
||||
my $nonce;
|
||||
for (0..20){$nonce .= chr(int(rand(254)));}
|
||||
my $mydate = DateTime->now()->iso8601().'Z';
|
||||
my $sha = Digest::SHA->new(1);
|
||||
$sha->add($nonce.$mydate.$password);
|
||||
my $digest = encode_base64($sha->digest,"");
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken><Username>'.$username.'</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$digest.'</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.encode_base64($nonce,"").'</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">'.$mydate.'</Created></UsernameToken></Security></s:Header><s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><SetImagingSettings xmlns="http://www.onvif.org/ver20/imaging/wsdl"><VideoSourceToken>000</VideoSourceToken><ImagingSettings><Contrast xmlns="http://www.onvif.org/ver10/schema">'.$CamParams{'Contrast'}.'</Contrast></ImagingSettings><ForcePersistence>true</ForcePersistence></SetImagingSettings></s:Body></s:Envelope>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
@ -97,7 +97,8 @@ Event::Event(
|
|||
db_mutex.lock();
|
||||
if ( mysql_query( &dbconn, sql ) ) {
|
||||
Error( "Can't insert event: %s. sql was (%s)", mysql_error( &dbconn ), sql );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
db_mutex.unlock();
|
||||
return;
|
||||
}
|
||||
id = mysql_insert_id( &dbconn );
|
||||
db_mutex.unlock();
|
||||
|
@ -217,6 +218,16 @@ Event::Event(
|
|||
} // Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent )
|
||||
|
||||
Event::~Event() {
|
||||
|
||||
// We cose the videowriter first, because it might take some time, and we don't want to lock the db if we can avoid it
|
||||
|
||||
/* Close the video file */
|
||||
if ( videoStore ) {
|
||||
delete videoStore;
|
||||
videoStore = NULL;
|
||||
}
|
||||
|
||||
|
||||
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||
struct DeltaTimeval delta_time;
|
||||
DELTA_TIMEVAL(delta_time, end_time, start_time, DT_PREC_2);
|
||||
|
@ -225,25 +236,23 @@ Event::~Event() {
|
|||
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 ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld )",
|
||||
"INSERT INTO Frames ( EventId, FrameId, TimeStamp, Delta ) VALUES ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld )",
|
||||
id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec );
|
||||
db_mutex.lock();
|
||||
if ( mysql_query( &dbconn, sql ) ) {
|
||||
Error( "Can't insert frame: %s", mysql_error( &dbconn ) );
|
||||
} else {
|
||||
Debug(1,"Success writing last frame");
|
||||
}
|
||||
db_mutex.unlock();
|
||||
}
|
||||
|
||||
/* Close the video file */
|
||||
if ( videoStore ) {
|
||||
delete videoStore;
|
||||
videoStore = NULL;
|
||||
}
|
||||
|
||||
snprintf( sql, sizeof(sql), "UPDATE Events SET Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, video_name, id );
|
||||
db_mutex.lock();
|
||||
if ( mysql_query( &dbconn, sql ) ) {
|
||||
Error( "Can't update event: %s", mysql_error( &dbconn ) );
|
||||
} else {
|
||||
Debug(1,"Success updating event");
|
||||
}
|
||||
db_mutex.unlock();
|
||||
} // ~Event
|
||||
|
@ -380,27 +389,27 @@ void Event::updateNotes( const StringSetMap &newNoteSetMap ) {
|
|||
#else
|
||||
static char escapedNotes[ZM_SQL_MED_BUFSIZ];
|
||||
|
||||
mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() );
|
||||
mysql_real_escape_string(&dbconn, escapedNotes, notes.c_str(), notes.length());
|
||||
|
||||
snprintf(sql, sizeof(sql), "UPDATE Events SET Notes = '%s' WHERE Id = %d", escapedNotes, id);
|
||||
db_mutex.lock();
|
||||
snprintf( sql, sizeof(sql), "UPDATE Events SET Notes = '%s' WHERE Id = %d", escapedNotes, id );
|
||||
if ( mysql_query( &dbconn, sql ) ) {
|
||||
Error( "Can't insert event: %s", mysql_error( &dbconn ) );
|
||||
if ( mysql_query(&dbconn, sql) ) {
|
||||
Error("Can't insert event: %s", mysql_error(&dbconn));
|
||||
}
|
||||
db_mutex.unlock();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Event::AddFrames( int n_frames, Image **images, struct timeval **timestamps ) {
|
||||
void Event::AddFrames(int n_frames, Image **images, struct timeval **timestamps) {
|
||||
for (int i = 0; i < n_frames; i += ZM_SQL_BATCH_SIZE) {
|
||||
AddFramesInternal(n_frames, i, images, timestamps);
|
||||
}
|
||||
}
|
||||
|
||||
void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ) {
|
||||
void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, struct timeval **timestamps) {
|
||||
static char sql[ZM_SQL_LGE_BUFSIZ];
|
||||
strncpy( sql, "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ", sizeof(sql) );
|
||||
strncpy(sql, "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ", sizeof(sql));
|
||||
int frameCount = 0;
|
||||
for ( int i = start_frame; i < n_frames && i - start_frame < ZM_SQL_BATCH_SIZE; i++ ) {
|
||||
if ( timestamps[i]->tv_sec <= 0 ) {
|
||||
|
|
|
@ -88,43 +88,48 @@ bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) {
|
|||
}
|
||||
|
||||
bool EventStream::loadInitialEventData( int init_event_id, unsigned int init_frame_id ) {
|
||||
loadEventData( init_event_id );
|
||||
loadEventData(init_event_id);
|
||||
|
||||
if ( init_frame_id ) {
|
||||
curr_stream_time = event_data->frames[init_frame_id-1].timestamp;
|
||||
curr_frame_id = init_frame_id;
|
||||
if ( init_frame_id >= event_data->frame_count ) {
|
||||
Error("Invalid frame id specified. %d > %d", init_frame_id, event_data->frame_count );
|
||||
curr_stream_time = event_data->start_time;
|
||||
} else {
|
||||
curr_stream_time = event_data->frames[init_frame_id-1].timestamp;
|
||||
curr_frame_id = init_frame_id;
|
||||
}
|
||||
} else {
|
||||
curr_stream_time = event_data->start_time;
|
||||
}
|
||||
|
||||
return( true );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EventStream::loadEventData( int event_id ) {
|
||||
bool EventStream::loadEventData(int 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 = %d", event_id );
|
||||
snprintf(sql, sizeof(sql), "SELECT MonitorId, StorageId, Frames, unix_timestamp( StartTime ) AS StartTimestamp, (SELECT max(Delta)-min(Delta) FROM Frames WHERE EventId=Events.Id) AS Duration, DefaultVideo, Scheme FROM Events WHERE Id = %d", event_id);
|
||||
|
||||
if ( mysql_query( &dbconn, sql ) ) {
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
if ( mysql_query(&dbconn, sql) ) {
|
||||
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||
exit(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( mysql_errno( &dbconn ) );
|
||||
Error("Can't use query result: %s", mysql_error(&dbconn));
|
||||
exit(mysql_errno(&dbconn));
|
||||
}
|
||||
|
||||
if ( !mysql_num_rows( result ) ) {
|
||||
Fatal( "Unable to load event %d, not found in DB", event_id );
|
||||
if ( !mysql_num_rows(result) ) {
|
||||
Fatal("Unable to load event %d, not found in DB", event_id);
|
||||
}
|
||||
|
||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||
MYSQL_ROW dbrow = mysql_fetch_row(result);
|
||||
|
||||
if ( mysql_errno( &dbconn ) ) {
|
||||
Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
if ( mysql_errno(&dbconn) ) {
|
||||
Error("Can't fetch row: %s", mysql_error(&dbconn));
|
||||
exit(mysql_errno(&dbconn));
|
||||
}
|
||||
|
||||
delete event_data;
|
||||
|
@ -885,3 +890,17 @@ void EventStream::runStream() {
|
|||
|
||||
closeComms();
|
||||
}
|
||||
void EventStream::setStreamStart( int init_event_id, unsigned int init_frame_id=0 ) {
|
||||
loadInitialEventData( init_event_id, init_frame_id );
|
||||
if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) {
|
||||
Fatal( "Unable to load monitor id %d for streaming", event_data->monitor_id );
|
||||
return;
|
||||
}
|
||||
}
|
||||
void EventStream::setStreamStart( int monitor_id, time_t event_time ) {
|
||||
loadInitialEventData( monitor_id, event_time );
|
||||
if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) {
|
||||
Fatal( "Unable to load monitor id %d for streaming", monitor_id );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,20 +110,8 @@ class EventStream : public StreamBase {
|
|||
ffmpeg_input = NULL;
|
||||
|
||||
}
|
||||
void setStreamStart( int init_event_id, unsigned int init_frame_id=0 ) {
|
||||
loadInitialEventData( init_event_id, init_frame_id );
|
||||
if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) {
|
||||
Fatal( "Unable to load monitor id %d for streaming", event_data->monitor_id );
|
||||
return;
|
||||
}
|
||||
}
|
||||
void setStreamStart( int monitor_id, time_t event_time ) {
|
||||
loadInitialEventData( monitor_id, event_time );
|
||||
if ( !(monitor = Monitor::Load( event_data->monitor_id, false, Monitor::QUERY )) ) {
|
||||
Fatal( "Unable to load monitor id %d for streaming", monitor_id );
|
||||
return;
|
||||
}
|
||||
}
|
||||
void setStreamStart( int init_event_id, unsigned int init_frame_id );
|
||||
void setStreamStart( int monitor_id, time_t event_time );
|
||||
void setStreamMode( StreamMode p_mode ) {
|
||||
mode = p_mode;
|
||||
}
|
||||
|
|
|
@ -131,7 +131,15 @@ int FfmpegCamera::PrimeCapture() {
|
|||
}
|
||||
|
||||
int FfmpegCamera::PreCapture() {
|
||||
<<<<<<< HEAD
|
||||
return 0;
|
||||
=======
|
||||
// If Reopen was called, then ffmpeg is closed and we need to reopen it.
|
||||
if ( ! mCanCapture )
|
||||
return OpenFfmpeg();
|
||||
// Nothing to do here
|
||||
return( 0 );
|
||||
>>>>>>> storageareas
|
||||
}
|
||||
|
||||
int FfmpegCamera::Capture( ZMPacket &zm_packet ) {
|
||||
|
@ -346,13 +354,20 @@ int FfmpegCamera::OpenFfmpeg() {
|
|||
}
|
||||
} // end if h264
|
||||
#endif
|
||||
if ( mVideoCodecContext->codec_id == AV_CODEC_ID_H264 ) {
|
||||
if ( (mVideoCodec = avcodec_find_decoder_by_name("h264_mmal")) == NULL ) {
|
||||
Debug(1, "Failed to find decoder (h264_mmal)" );
|
||||
} else {
|
||||
Debug(1, "Success finding decoder (h264_mmal)" );
|
||||
}
|
||||
}
|
||||
|
||||
if ( (!mVideoCodec) and ( (mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id)) == NULL ) ) {
|
||||
// Try and get the codec from the codec context
|
||||
Error("Can't find codec for video stream from %s", mPath.c_str());
|
||||
return -1;
|
||||
} else {
|
||||
Debug(1, "Video Found decoder");
|
||||
Debug(1, "Video Found decoder %s", mVideoCodec->name);
|
||||
zm_dump_stream_format(mFormatContext, mVideoStreamId, 0, 0);
|
||||
|
||||
// Open the codec
|
||||
|
@ -468,6 +483,301 @@ int FfmpegCamera::CloseFfmpeg() {
|
|||
}
|
||||
|
||||
return 0;
|
||||
<<<<<<< HEAD
|
||||
} // end int FfmpegCamera::CloseFfmpeg()
|
||||
=======
|
||||
} // end FfmpegCamera::Close
|
||||
|
||||
//Function to handle capture and store
|
||||
int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event_file ) {
|
||||
if ( ! mCanCapture ) {
|
||||
return -1;
|
||||
}
|
||||
int ret;
|
||||
static char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||
|
||||
int frameComplete = false;
|
||||
while ( ! frameComplete ) {
|
||||
av_init_packet( &packet );
|
||||
|
||||
ret = av_read_frame( mFormatContext, &packet );
|
||||
if ( ret < 0 ) {
|
||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf );
|
||||
return -1;
|
||||
}
|
||||
|
||||
int keyframe = packet.flags & AV_PKT_FLAG_KEY;
|
||||
dumpPacket(&packet);
|
||||
|
||||
//Video recording
|
||||
if ( recording.tv_sec ) {
|
||||
|
||||
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 );
|
||||
|
||||
if ( videoStore ) {
|
||||
Info("Re-starting video storage module");
|
||||
|
||||
// I don't know if this is important or not... but I figure we might as well write this last packet out to the store before closing it.
|
||||
// Also don't know how much it matters for audio.
|
||||
if ( packet.stream_index == mVideoStreamId ) {
|
||||
//Write the packet to our video store
|
||||
int ret = videoStore->writeVideoFramePacket( &packet );
|
||||
if ( ret < 0 ) { //Less than zero and we skipped a frame
|
||||
Warning("Error writing last packet to videostore.");
|
||||
}
|
||||
} // end if video
|
||||
|
||||
delete videoStore;
|
||||
videoStore = NULL;
|
||||
have_video_keyframe = false;
|
||||
|
||||
monitor->SetVideoWriterEventId( 0 );
|
||||
} // end if videoStore
|
||||
} // end if end of recording
|
||||
|
||||
if ( last_event_id and ! videoStore ) {
|
||||
//Instantiate the video storage module
|
||||
|
||||
if ( record_audio ) {
|
||||
if ( mAudioStreamId == -1 ) {
|
||||
Debug(3, "Record Audio on but no audio stream found");
|
||||
videoStore = new VideoStore((const char *) event_file, "mp4",
|
||||
mFormatContext->streams[mVideoStreamId],
|
||||
NULL,
|
||||
startTime,
|
||||
this->getMonitor());
|
||||
|
||||
} else {
|
||||
Debug(3, "Video module initiated with audio stream");
|
||||
videoStore = new VideoStore((const char *) event_file, "mp4",
|
||||
mFormatContext->streams[mVideoStreamId],
|
||||
mFormatContext->streams[mAudioStreamId],
|
||||
startTime,
|
||||
this->getMonitor());
|
||||
}
|
||||
} else {
|
||||
if ( mAudioStreamId >= 0 ) {
|
||||
Debug(3, "Record_audio is false so exclude audio stream");
|
||||
}
|
||||
videoStore = new VideoStore((const char *) event_file, "mp4",
|
||||
mFormatContext->streams[mVideoStreamId],
|
||||
NULL,
|
||||
startTime,
|
||||
this->getMonitor());
|
||||
} // end if record_audio
|
||||
|
||||
if ( ! videoStore->open() ) {
|
||||
delete videoStore;
|
||||
videoStore = NULL;
|
||||
|
||||
} else {
|
||||
monitor->SetVideoWriterEventId( last_event_id );
|
||||
|
||||
// Need to write out all the frames from the last keyframe?
|
||||
// No... need to write out all frames from when the event began. Due to PreEventFrames, this could be more than since the last keyframe.
|
||||
unsigned int packet_count = 0;
|
||||
ZMPacket *queued_packet;
|
||||
|
||||
// Clear all packets that predate the moment when the recording began
|
||||
packetqueue.clear_unwanted_packets( &recording, mVideoStreamId );
|
||||
|
||||
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() );
|
||||
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 );
|
||||
ret = -1;
|
||||
}
|
||||
if ( ret < 0 ) {
|
||||
//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 );
|
||||
}
|
||||
} // end if ! was recording
|
||||
|
||||
} else {
|
||||
// Not recording
|
||||
if ( videoStore ) {
|
||||
Info("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
|
||||
|
||||
if ( packet.stream_index == mVideoStreamId ) {
|
||||
// only do decode if we have had a keyframe, should save a few cycles.
|
||||
if ( have_video_keyframe || keyframe ) {
|
||||
|
||||
if ( videoStore ) {
|
||||
|
||||
//Write the packet to our video store
|
||||
int ret = videoStore->writeVideoFramePacket( &packet );
|
||||
if ( ret < 0 ) { //Less than zero and we skipped a frame
|
||||
zm_av_packet_unref( &packet );
|
||||
return 0;
|
||||
}
|
||||
have_video_keyframe = true;
|
||||
}
|
||||
} // end if keyframe or have_video_keyframe
|
||||
|
||||
Debug(4, "about to decode video" );
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
ret = avcodec_send_packet( mVideoCodecContext, &packet );
|
||||
if ( ret < 0 ) {
|
||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||
Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
|
||||
zm_av_packet_unref( &packet );
|
||||
continue;
|
||||
}
|
||||
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||
if ( hwaccel ) {
|
||||
ret = avcodec_receive_frame( mVideoCodecContext, hwFrame );
|
||||
if ( ret < 0 ) {
|
||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||
Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
|
||||
zm_av_packet_unref( &packet );
|
||||
continue;
|
||||
}
|
||||
ret = av_hwframe_transfer_data(mRawFrame, hwFrame, 0);
|
||||
if (ret < 0) {
|
||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||
Error( "Unable to transfer frame at frame %d: %s, continuing", frameCount, errbuf );
|
||||
zm_av_packet_unref( &packet );
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
ret = avcodec_receive_frame( mVideoCodecContext, mRawFrame );
|
||||
if ( ret < 0 ) {
|
||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||
Warning( "Unable to receive frame %d: %s, continuing", frameCount, errbuf );
|
||||
zm_av_packet_unref( &packet );
|
||||
continue;
|
||||
}
|
||||
|
||||
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||
}
|
||||
#endif
|
||||
|
||||
frameComplete = 1;
|
||||
# else
|
||||
ret = zm_avcodec_decode_video( mVideoCodecContext, mRawFrame, &frameComplete, &packet );
|
||||
if ( ret < 0 ) {
|
||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||
Error( "Unable to decode frame at frame %d: %s, continuing", frameCount, errbuf );
|
||||
zm_av_packet_unref( &packet );
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( frameComplete ) {
|
||||
Debug( 4, "Got frame %d", frameCount );
|
||||
|
||||
uint8_t* directbuffer;
|
||||
|
||||
/* Request a writeable buffer of the target image */
|
||||
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||
if ( directbuffer == NULL ) {
|
||||
Error("Failed requesting writeable buffer for the captured image.");
|
||||
zm_av_packet_unref( &packet );
|
||||
return (-1);
|
||||
}
|
||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||
av_image_fill_arrays(mFrame->data, mFrame->linesize, directbuffer, imagePixFormat, width, height, 1);
|
||||
#else
|
||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
|
||||
#endif
|
||||
|
||||
|
||||
if (sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize,
|
||||
0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0) {
|
||||
Error("Unable to convert raw format %u to target format %u at frame %d",
|
||||
mVideoCodecContext->pix_fmt, imagePixFormat, frameCount);
|
||||
return -1;
|
||||
}
|
||||
|
||||
frameCount++;
|
||||
} else {
|
||||
Debug( 3, "Not framecomplete after av_read_frame" );
|
||||
} // end if frameComplete
|
||||
} else if ( packet.stream_index == mAudioStreamId ) { //FIXME best way to copy all other streams
|
||||
frameComplete = 1;
|
||||
if ( videoStore ) {
|
||||
if ( record_audio ) {
|
||||
if ( have_video_keyframe ) {
|
||||
Debug(3, "Recording audio packet streamindex(%d) packetstreamindex(%d)", mAudioStreamId, packet.stream_index );
|
||||
//Write the packet to our video store
|
||||
//FIXME no relevance of last key frame
|
||||
int ret = videoStore->writeAudioFramePacket( &packet );
|
||||
if ( ret < 0 ) {//Less than zero and we skipped a frame
|
||||
Warning("Failure to write audio packet.");
|
||||
zm_av_packet_unref( &packet );
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
Debug(3, "Not recording audio yet because we don't have a video keyframe yet");
|
||||
}
|
||||
} else {
|
||||
Debug(4, "Not doing recording of audio packet" );
|
||||
}
|
||||
} else {
|
||||
Debug(4, "Have audio packet, but not recording atm" );
|
||||
}
|
||||
zm_av_packet_unref( &packet );
|
||||
return 0;
|
||||
} else {
|
||||
#if LIBAVUTIL_VERSION_CHECK(56, 23, 0, 23, 0)
|
||||
Debug( 3, "Some other stream index %d, %s", packet.stream_index, av_get_media_type_string( mFormatContext->streams[packet.stream_index]->codecpar->codec_type) );
|
||||
#else
|
||||
Debug( 3, "Some other stream index %d", packet.stream_index );
|
||||
#endif
|
||||
} // end if is video or audio or something else
|
||||
|
||||
// the packet contents are ref counted... when queuing, we allocate another packet and reference it with that one, so we should always need to unref here, which should not affect the queued version.
|
||||
zm_av_packet_unref( &packet );
|
||||
} // end while ! frameComplete
|
||||
return frameCount;
|
||||
} // end FfmpegCamera::CaptureAndRecord
|
||||
|
||||
|
||||
>>>>>>> storageareas
|
||||
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
|
|
@ -143,13 +143,69 @@ AVFrame *FFmpeg_Input::get_frame( int stream_id, int frame_number ) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
if ( ( stream_id < 0 ) || ( packet.stream_index != stream_id ) ) {
|
||||
Warning("Packet is not for our stream (%d)", packet.stream_index);
|
||||
return NULL;
|
||||
=======
|
||||
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");
|
||||
}
|
||||
|
||||
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||
if ( hwaccel ) {
|
||||
ret = avcodec_receive_frame( context, hwFrame );
|
||||
if ( ret < 0 ) {
|
||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||
Error( "Unable to receive frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
|
||||
zm_av_packet_unref( &packet );
|
||||
continue;
|
||||
}
|
||||
ret = av_hwframe_transfer_data(frame, hwFrame, 0);
|
||||
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 );
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
Debug(1,"Getting a frame?");
|
||||
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 );
|
||||
continue;
|
||||
}
|
||||
|
||||
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||
>>>>>>> storageareas
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
if ( ! zm_receive_frame( streams[packet.stream_index].context, frame, packet ) ) {
|
||||
Error("Unable to get frame %d, continuing", streams[packet.stream_index].frame_count);
|
||||
=======
|
||||
frameComplete = 1;
|
||||
# else
|
||||
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 );
|
||||
>>>>>>> storageareas
|
||||
zm_av_packet_unref( &packet );
|
||||
continue;
|
||||
} else {
|
||||
|
|
|
@ -326,7 +326,8 @@ Monitor::Monitor(
|
|||
zones( p_zones ),
|
||||
timestamps( 0 ),
|
||||
images( 0 ),
|
||||
privacy_bitmask( NULL )
|
||||
privacy_bitmask( NULL ),
|
||||
event_delete_thread(NULL)
|
||||
{
|
||||
strncpy( name, p_name, sizeof(name)-1 );
|
||||
|
||||
|
@ -477,7 +478,6 @@ Monitor::Monitor(
|
|||
adaptive_skip = true;
|
||||
|
||||
ReloadLinkedMonitors( p_linked_monitors );
|
||||
videoStore = NULL;
|
||||
} // Monitor::Monitor
|
||||
|
||||
bool Monitor::connect() {
|
||||
|
@ -578,12 +578,13 @@ bool Monitor::connect() {
|
|||
} // Monitor::connect
|
||||
|
||||
Monitor::~Monitor() {
|
||||
if ( videoStore ) {
|
||||
delete videoStore;
|
||||
videoStore = NULL;
|
||||
|
||||
if ( event_delete_thread ) {
|
||||
event_delete_thread->join();
|
||||
delete event_delete_thread;
|
||||
event_delete_thread = NULL;
|
||||
}
|
||||
delete packetqueue;
|
||||
packetqueue = NULL;
|
||||
|
||||
|
||||
if ( timestamps ) {
|
||||
delete[] timestamps;
|
||||
|
@ -593,13 +594,18 @@ Monitor::~Monitor() {
|
|||
delete[] images;
|
||||
images = 0;
|
||||
}
|
||||
|
||||
delete packetqueue;
|
||||
packetqueue = NULL;
|
||||
|
||||
if ( privacy_bitmask ) {
|
||||
delete[] privacy_bitmask;
|
||||
privacy_bitmask = NULL;
|
||||
}
|
||||
|
||||
if ( mem_ptr ) {
|
||||
if ( event ) {
|
||||
Info( "%s: image_count:%d - Closing event %d, shutting down", name, image_count, event->Id() );
|
||||
Info("%s: image_count:%d - Closing event %d, shutting down", name, image_count, event->Id() );
|
||||
closeEvent();
|
||||
}
|
||||
|
||||
|
@ -607,7 +613,7 @@ Monitor::~Monitor() {
|
|||
delete next_buffer.image;
|
||||
}
|
||||
#if 1
|
||||
for ( int i = 0; i < image_buffer_count; i++ ) {
|
||||
for ( int i=0; i < image_buffer_count; i++ ) {
|
||||
delete image_buffer[i].image;
|
||||
}
|
||||
#endif
|
||||
|
@ -620,15 +626,15 @@ Monitor::~Monitor() {
|
|||
shared_data->last_read_time = 0;
|
||||
} else if ( purpose == CAPTURE ) {
|
||||
shared_data->valid = false;
|
||||
memset( mem_ptr, 0, mem_size );
|
||||
memset(mem_ptr, 0, mem_size);
|
||||
}
|
||||
|
||||
#if ZM_MEM_MAPPED
|
||||
if ( msync( mem_ptr, mem_size, MS_SYNC ) < 0 )
|
||||
Error( "Can't msync: %s", strerror(errno) );
|
||||
if ( munmap( mem_ptr, mem_size ) < 0 )
|
||||
Fatal( "Can't munmap: %s", strerror(errno) );
|
||||
close( map_fd );
|
||||
if ( msync(mem_ptr, mem_size, MS_SYNC) < 0 )
|
||||
Error("Can't msync: %s", strerror(errno));
|
||||
if ( munmap(mem_ptr, mem_size) < 0 )
|
||||
Fatal("Can't munmap: %s", strerror(errno));
|
||||
close(map_fd);
|
||||
|
||||
if ( purpose == CAPTURE ) {
|
||||
// How about we store this in the object on instantiation so that we don't have to do this again.
|
||||
|
@ -641,20 +647,20 @@ Monitor::~Monitor() {
|
|||
}
|
||||
#else // ZM_MEM_MAPPED
|
||||
struct shmid_ds shm_data;
|
||||
if ( shmctl( shm_id, IPC_STAT, &shm_data ) < 0 ) {
|
||||
Error( "Can't shmctl: %s", strerror(errno) );
|
||||
exit( -1 );
|
||||
if ( shmctl(shm_id, IPC_STAT, &shm_data) < 0 ) {
|
||||
Error("Can't shmctl: %s", strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
if ( shm_data.shm_nattch <= 1 ) {
|
||||
if ( shmctl( shm_id, IPC_RMID, 0 ) < 0 ) {
|
||||
Error( "Can't shmctl: %s", strerror(errno) );
|
||||
exit( -1 );
|
||||
if ( shmctl(shm_id, IPC_RMID, 0) < 0 ) {
|
||||
Error("Can't shmctl: %s", strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
#endif // ZM_MEM_MAPPED
|
||||
} // end if mem_ptr
|
||||
|
||||
for ( int i = 0; i < n_zones; i++ ) {
|
||||
for ( int i=0; i < n_zones; i++ ) {
|
||||
delete zones[i];
|
||||
}
|
||||
delete[] zones;
|
||||
|
@ -663,29 +669,29 @@ Monitor::~Monitor() {
|
|||
delete storage;
|
||||
}
|
||||
|
||||
void Monitor::AddZones( int p_n_zones, Zone *p_zones[] ) {
|
||||
for ( int i = 0; i < n_zones; i++ )
|
||||
void Monitor::AddZones(int p_n_zones, Zone *p_zones[]) {
|
||||
for ( int i=0; i < n_zones; i++ )
|
||||
delete zones[i];
|
||||
delete[] zones;
|
||||
n_zones = p_n_zones;
|
||||
zones = p_zones;
|
||||
}
|
||||
|
||||
void Monitor::AddPrivacyBitmask( Zone *p_zones[] ) {
|
||||
void Monitor::AddPrivacyBitmask(Zone *p_zones[]) {
|
||||
if ( privacy_bitmask ) {
|
||||
delete[] privacy_bitmask;
|
||||
privacy_bitmask = NULL;
|
||||
}
|
||||
Image *privacy_image = NULL;
|
||||
|
||||
for ( int i = 0; i < n_zones; i++ ) {
|
||||
for ( int i=0; i < n_zones; i++ ) {
|
||||
if ( p_zones[i]->IsPrivacy() ) {
|
||||
if ( !privacy_image ) {
|
||||
privacy_image = new Image( width, height, 1, ZM_SUBPIX_ORDER_NONE);
|
||||
privacy_image = new Image(width, height, 1, ZM_SUBPIX_ORDER_NONE);
|
||||
privacy_image->Clear();
|
||||
}
|
||||
privacy_image->Fill( 0xff, p_zones[i]->GetPolygon() );
|
||||
privacy_image->Outline( 0xff, p_zones[i]->GetPolygon() );
|
||||
privacy_image->Fill(0xff, p_zones[i]->GetPolygon());
|
||||
privacy_image->Outline(0xff, p_zones[i]->GetPolygon());
|
||||
}
|
||||
} // end foreach zone
|
||||
if ( privacy_image )
|
||||
|
@ -693,7 +699,7 @@ void Monitor::AddPrivacyBitmask( Zone *p_zones[] ) {
|
|||
}
|
||||
|
||||
Monitor::State Monitor::GetState() const {
|
||||
return( (State)shared_data->state );
|
||||
return (State)shared_data->state;
|
||||
}
|
||||
|
||||
int Monitor::GetImage( int index, int scale ) {
|
||||
|
@ -1571,50 +1577,50 @@ bool Monitor::Analyse() {
|
|||
} // end Monitor::Analyze
|
||||
|
||||
void Monitor::Reload() {
|
||||
Debug( 1, "Reloading monitor %s", name );
|
||||
Debug(1, "Reloading monitor %s", name);
|
||||
|
||||
if ( event )
|
||||
Info( "%s: %03d - Closing event %d, reloading", name, image_count, event->Id() );
|
||||
Info("%s: %03d - Closing event %d, reloading", name, image_count, event->Id());
|
||||
|
||||
closeEvent();
|
||||
|
||||
static char sql[ZM_SQL_MED_BUFSIZ];
|
||||
// This seems to have fallen out of date.
|
||||
snprintf( sql, sizeof(sql), "select Function+0, Enabled, LinkedMonitors, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = '%d'", id );
|
||||
snprintf(sql, sizeof(sql), "select Function+0, Enabled, LinkedMonitors, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = '%d'", id);
|
||||
|
||||
db_mutex.lock();
|
||||
if ( mysql_query( &dbconn, sql ) ) {
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
if ( mysql_query(&dbconn, sql) ) {
|
||||
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||
db_mutex.unlock();
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
db_mutex.unlock();
|
||||
if ( !result ) {
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
}
|
||||
int n_monitors = mysql_num_rows( result );
|
||||
if ( n_monitors != 1 ) {
|
||||
Error( "Bogus number of monitors, %d, returned. Can't reload", n_monitors );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) {
|
||||
MYSQL_RES *result = mysql_store_result(&dbconn);
|
||||
db_mutex.unlock();
|
||||
if ( !result ) {
|
||||
Error("Can't use query result: %s. Can't reload", mysql_error(&dbconn));
|
||||
return;
|
||||
}
|
||||
int n_monitors = mysql_num_rows(result);
|
||||
if ( n_monitors != 1 ) {
|
||||
Error("Bogus number of monitors, %d, returned. Can't reload", n_monitors);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( MYSQL_ROW dbrow = mysql_fetch_row(result) ) {
|
||||
int index = 0;
|
||||
function = (Function)atoi(dbrow[index++]);
|
||||
enabled = atoi(dbrow[index++]);
|
||||
const char *p_linked_monitors = dbrow[index++];
|
||||
|
||||
if ( dbrow[index] ) {
|
||||
strncpy( event_prefix, dbrow[index++], sizeof(event_prefix)-1 );
|
||||
strncpy(event_prefix, dbrow[index++], sizeof(event_prefix)-1);
|
||||
} else {
|
||||
event_prefix[0] = 0;
|
||||
index++;
|
||||
}
|
||||
if ( dbrow[index] ) {
|
||||
strncpy( label_format, dbrow[index++], sizeof(label_format)-1 );
|
||||
strncpy(label_format, dbrow[index++], sizeof(label_format)-1);
|
||||
} else {
|
||||
label_format[0] = 0;
|
||||
index++;
|
||||
|
@ -1638,7 +1644,6 @@ void Monitor::Reload() {
|
|||
alarm_ref_blend_perc = atoi(dbrow[index++]);
|
||||
track_motion = atoi(dbrow[index++]);
|
||||
|
||||
|
||||
if ( dbrow[index][0] == '#' )
|
||||
signal_check_colour = strtol(dbrow[index]+1,0,16);
|
||||
else
|
||||
|
@ -1651,32 +1656,31 @@ void Monitor::Reload() {
|
|||
shared_data->active = true;
|
||||
ready_count = image_count+warmup_count;
|
||||
|
||||
ReloadLinkedMonitors( p_linked_monitors );
|
||||
ReloadLinkedMonitors(p_linked_monitors);
|
||||
}
|
||||
if ( mysql_errno( &dbconn ) ) {
|
||||
Error( "Can't fetch row: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
if ( mysql_errno(&dbconn) ) {
|
||||
Error("Can't fetch row: %s", mysql_error(&dbconn));
|
||||
}
|
||||
mysql_free_result( result );
|
||||
mysql_free_result(result);
|
||||
|
||||
ReloadZones();
|
||||
}
|
||||
} // end void Monitor::Reload()
|
||||
|
||||
void Monitor::ReloadZones() {
|
||||
Debug( 1, "Reloading zones for monitor %s", name );
|
||||
for( int i = 0; i < n_zones; i++ ) {
|
||||
Debug(1, "Reloading zones for monitor %s", name);
|
||||
for( int i=0; i < n_zones; i++ ) {
|
||||
delete zones[i];
|
||||
}
|
||||
delete[] zones;
|
||||
zones = 0;
|
||||
n_zones = Zone::Load( this, zones );
|
||||
n_zones = Zone::Load(this, zones);
|
||||
//DumpZoneImage();
|
||||
}
|
||||
} // end void Monitor::ReloadZones()
|
||||
|
||||
void Monitor::ReloadLinkedMonitors( const char *p_linked_monitors ) {
|
||||
Debug( 1, "Reloading linked monitors for monitor %s, '%s'", name, p_linked_monitors );
|
||||
void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
|
||||
Debug(1, "Reloading linked monitors for monitor %s, '%s'", name, p_linked_monitors);
|
||||
if ( n_linked_monitors ) {
|
||||
for( int i = 0; i < n_linked_monitors; i++ ) {
|
||||
for( int i=0; i < n_linked_monitors; i++ ) {
|
||||
delete linked_monitors[i];
|
||||
}
|
||||
delete[] linked_monitors;
|
||||
|
@ -1688,6 +1692,7 @@ void Monitor::ReloadLinkedMonitors( const char *p_linked_monitors ) {
|
|||
int n_link_ids = 0;
|
||||
unsigned int link_ids[256];
|
||||
|
||||
// This nasty code picks out strings of digits from p_linked_monitors and tries to load them.
|
||||
char link_id_str[8];
|
||||
char *dest_ptr = link_id_str;
|
||||
const char *src_ptr = p_linked_monitors;
|
||||
|
@ -1729,35 +1734,35 @@ void Monitor::ReloadLinkedMonitors( const char *p_linked_monitors ) {
|
|||
linked_monitors = new MonitorLink *[n_link_ids];
|
||||
int count = 0;
|
||||
for ( int i = 0; i < n_link_ids; i++ ) {
|
||||
Debug( 1, "Checking linked monitor %d", link_ids[i] );
|
||||
Debug(1, "Checking linked monitor %d", link_ids[i]);
|
||||
|
||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
||||
snprintf( sql, sizeof(sql), "select Id, Name from Monitors where Id = %d and Function != 'None' and Function != 'Monitor' and Enabled = 1", link_ids[i] );
|
||||
snprintf(sql, sizeof(sql), "SELECT Id, Name FROM Monitors WHERE Id = %d AND Function != 'None' AND Function != 'Monitor' AND Enabled = 1", link_ids[i] );
|
||||
db_mutex.lock();
|
||||
if ( mysql_query( &dbconn, sql ) ) {
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
if ( mysql_query(&dbconn, sql) ) {
|
||||
Error("Can't run query: %s", mysql_error(&dbconn));
|
||||
db_mutex.unlock();
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
MYSQL_RES *result = mysql_store_result( &dbconn );
|
||||
MYSQL_RES *result = mysql_store_result(&dbconn);
|
||||
db_mutex.unlock();
|
||||
if ( !result ) {
|
||||
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
|
||||
exit( mysql_errno( &dbconn ) );
|
||||
Error("Can't use query result: %s", mysql_error(&dbconn));
|
||||
continue;
|
||||
}
|
||||
int n_monitors = mysql_num_rows( result );
|
||||
int n_monitors = mysql_num_rows(result);
|
||||
if ( n_monitors == 1 ) {
|
||||
MYSQL_ROW dbrow = mysql_fetch_row( result );
|
||||
Debug( 1, "Linking to monitor %d", link_ids[i] );
|
||||
linked_monitors[count++] = new MonitorLink( link_ids[i], dbrow[1] );
|
||||
MYSQL_ROW dbrow = mysql_fetch_row(result);
|
||||
Debug(1, "Linking to monitor %d", link_ids[i]);
|
||||
linked_monitors[count++] = new MonitorLink(link_ids[i], dbrow[1]);
|
||||
} else {
|
||||
Warning( "Can't link to monitor %d, invalid id, function or not enabled", link_ids[i] );
|
||||
Warning("Can't link to monitor %d, invalid id, function or not enabled", link_ids[i]);
|
||||
}
|
||||
mysql_free_result( result );
|
||||
}
|
||||
mysql_free_result(result);
|
||||
} // end foreach n_link_id
|
||||
n_linked_monitors = count;
|
||||
}
|
||||
} // end if n_link_ids > 0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3048,12 +3053,22 @@ bool Monitor::closeEvent() {
|
|||
if ( function == RECORD || function == MOCORD ) {
|
||||
gettimeofday( &(event->EndTime()), NULL );
|
||||
}
|
||||
delete event;
|
||||
if ( event_delete_thread ) {
|
||||
event_delete_thread->join();
|
||||
delete event_delete_thread;
|
||||
event_delete_thread = NULL;
|
||||
}
|
||||
event_delete_thread = new std::thread([](Event *event) {
|
||||
Event * e = event;
|
||||
event = NULL;
|
||||
delete e;
|
||||
e = NULL;
|
||||
}, event );
|
||||
video_store_data->recording = (struct timeval){0};
|
||||
event = 0;
|
||||
return( true );
|
||||
event = NULL;
|
||||
return true;
|
||||
}
|
||||
return( false );
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &zoneSet ) {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
#include "zm.h"
|
||||
#include "zm_coord.h"
|
||||
|
@ -336,6 +337,7 @@ protected:
|
|||
Image **images;
|
||||
|
||||
const unsigned char *privacy_bitmask;
|
||||
std::thread *event_delete_thread; // Used to close events, but continue processing.
|
||||
|
||||
int n_linked_monitors;
|
||||
MonitorLink **linked_monitors;
|
||||
|
|
|
@ -88,12 +88,11 @@ bool StreamBase::checkCommandQueue() {
|
|||
//Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes );
|
||||
//}
|
||||
else {
|
||||
Debug(2, "Message length is (%d)", nbytes );
|
||||
processCommand( &msg );
|
||||
return( true );
|
||||
}
|
||||
} else {
|
||||
Error("sd is < 0");
|
||||
Warning("No sd in checkCommandQueue, comms not open?");
|
||||
}
|
||||
return( false );
|
||||
}
|
||||
|
|
|
@ -211,6 +211,7 @@ int X264MP4Writer::Open() {
|
|||
int X264MP4Writer::Close() {
|
||||
/* Flush all pending frames */
|
||||
for ( int i = (x264_encoder_delayed_frames(x264enc) + 1); i > 0; i-- ) {
|
||||
Debug(1,"Encoding delayed frame");
|
||||
x264encodeloop(true);
|
||||
}
|
||||
|
||||
|
@ -220,6 +221,7 @@ int X264MP4Writer::Close() {
|
|||
/* Close MP4 handle */
|
||||
MP4Close(mp4h);
|
||||
|
||||
Debug(1,"Optimising");
|
||||
/* Required for proper HTTP streaming */
|
||||
MP4Optimize((path + ".incomplete").c_str(), path.c_str());
|
||||
|
||||
|
@ -228,7 +230,7 @@ int X264MP4Writer::Close() {
|
|||
|
||||
bOpen = false;
|
||||
|
||||
Debug(7, "Video closed. Total frames: %d", frame_count);
|
||||
Debug(1, "Video closed. Total frames: %d", frame_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -413,7 +415,7 @@ void X264MP4Writer::x264encodeloop(bool bFlush) {
|
|||
}
|
||||
|
||||
if ( frame_size > 0 || bFlush ) {
|
||||
Debug(8, "x264 Frame: %d PTS: %d DTS: %d Size: %d\n",
|
||||
Debug(1, "x264 Frame: %d PTS: %d DTS: %d Size: %d\n",
|
||||
frame_count, x264picout.i_pts, x264picout.i_dts, frame_size);
|
||||
|
||||
/* Handle the previous frame */
|
||||
|
@ -490,7 +492,7 @@ void X264MP4Writer::x264encodeloop(bool bFlush) {
|
|||
}
|
||||
|
||||
} else if ( frame_size == 0 ) {
|
||||
Debug(7, "x264 encode returned zero. Delayed frames: %d",
|
||||
Debug(1, "x264 encode returned zero. Delayed frames: %d",
|
||||
x264_encoder_delayed_frames(x264enc));
|
||||
} else {
|
||||
Error("x264 encode failed: %d", frame_size);
|
||||
|
|
|
@ -283,10 +283,10 @@ int main( int argc, const char *argv[] ) {
|
|||
stream.setStreamMode( replay );
|
||||
stream.setStreamQueue( connkey );
|
||||
if ( monitor_id && event_time ) {
|
||||
stream.setStreamStart( monitor_id, event_time );
|
||||
stream.setStreamStart(monitor_id, event_time);
|
||||
} else {
|
||||
Debug(3, "Setting stream start to frame (%d)", frame_id);
|
||||
stream.setStreamStart( event_id, frame_id );
|
||||
stream.setStreamStart(event_id, frame_id);
|
||||
}
|
||||
if ( mode == ZMS_JPEG ) {
|
||||
stream.setStreamType( EventStream::STREAM_JPEG );
|
||||
|
|
|
@ -295,7 +295,7 @@ if [ "${TRAVIS_EVENT_TYPE}" == "cron" ] || [ "${TRAVIS}" != "true" ]; then
|
|||
execpackpack
|
||||
|
||||
# Steps common to Debian based distros
|
||||
elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ]; then
|
||||
elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbian" ]; then
|
||||
echo "Begin ${OS} ${DIST} build..."
|
||||
|
||||
setdebpkgname
|
||||
|
|
|
@ -0,0 +1,230 @@
|
|||
<?php
|
||||
require_once( 'database.php' );
|
||||
require_once( 'Server.php' );
|
||||
|
||||
class Control {
|
||||
|
||||
private $defaults = array(
|
||||
'CanMove' => 0,
|
||||
'CanMoveDiag' => 0,
|
||||
'CanMoveMap' => 0,
|
||||
'CanMoveAbs' => 0,
|
||||
'CanMoveRel' => 0,
|
||||
'CanMoveCon' => 0,
|
||||
'CanPan' => 0,
|
||||
'MinPanRange' => NULL,
|
||||
'MaxPanRange' => NULL,
|
||||
'MinPanStep' => NULL,
|
||||
'MaxPanStep' => NULL,
|
||||
'HasPanSpeed' => 0,
|
||||
'MinPanSpeed' => NULL,
|
||||
'MaxPanSpeed' => NULL,
|
||||
'HasTurboPan' => 0,
|
||||
'TurboPanSpeed' => NULL,
|
||||
'CanTilt' => 0,
|
||||
'MinTiltRange' => NULL,
|
||||
'MaxTiltRange' => NULL,
|
||||
'MinTiltStep' => NULL,
|
||||
'MaxTiltStep' => NULL,
|
||||
'HasTiltSpeed' => 0,
|
||||
'MinTiltSpeed' => NULL,
|
||||
'MaxTiltSpeed' => NULL,
|
||||
'HasTurboTilt' => 0,
|
||||
'TurboTiltSpeed' => NULL,
|
||||
'CanZoom' => 0,
|
||||
'CanZoomAbs' => 0,
|
||||
'CanZoomRel' => 0,
|
||||
'CanZoomCon' => 0,
|
||||
'MinZoomRange' => NULL,
|
||||
'MaxZoomRange' => NULL,
|
||||
'MinZoomStep' => NULL,
|
||||
'MaxZoomStep' => NULL,
|
||||
'HasZoomSpeed' => 0,
|
||||
'MinZoomSpeed' => NULL,
|
||||
'MaxZoomSpeed' => NULL,
|
||||
'CanFocus' => 0,
|
||||
'CanAutoFocus' => 0,
|
||||
'CanFocusAbs' => 0,
|
||||
'CanFocusRel' => 0,
|
||||
'CanFocusCon' => 0,
|
||||
'MinFocusRange' => NULL,
|
||||
'MaxFocusRange' => NULL,
|
||||
'MinFocusStep' => NULL,
|
||||
'MaxFocusStep' => NULL,
|
||||
'HasFocusSpeed' => 0,
|
||||
'MinFocusSpeed' => NULL,
|
||||
'MaxFocusSpeed' => NULL,
|
||||
'CanIris' => 0,
|
||||
'CanAutoIris' => 0,
|
||||
'CanIrisAbs' => 0,
|
||||
'CanIrisRel' => 0,
|
||||
'CanIrisCon' => 0,
|
||||
'MinIrisRange' => NULL,
|
||||
'MaxIrisRange' => NULL,
|
||||
'MinIrisStep' => NULL,
|
||||
'MaxIrisStep' => NULL,
|
||||
'HasIrisSpeed' => 0,
|
||||
'MinIrisSpeed' => NULL,
|
||||
'MaxIrisSpeed' => NULL,
|
||||
'CanGain' => 0,
|
||||
'CanAutoGain' => 0,
|
||||
'CanGainAbs' => 0,
|
||||
'CanGainRel' => 0,
|
||||
'CanGainCon' => 0,
|
||||
'MinGainRange' => NULL,
|
||||
'MaxGainRange' => NULL,
|
||||
'MinGainStep' => NULL,
|
||||
'MaxGainStep' => NULL,
|
||||
'HasGainSpeed' => 0,
|
||||
'MinGainSpeed' => NULL,
|
||||
'MaxGainSpeed' => NULL,
|
||||
'CanWhite' => 0,
|
||||
'CanAutoWhite' => 0,
|
||||
'CanWhiteAbs' => 0,
|
||||
'CanWhiteRel' => 0,
|
||||
'CanWhiteCon' => 0,
|
||||
'MinWhiteRange' => NULL,
|
||||
'MaxWhiteRange' => NULL,
|
||||
'MinWhiteStep' => NULL,
|
||||
'MaxWhiteStep' => NULL,
|
||||
'HasWhiteSpeed' => 0,
|
||||
'MinWhiteSpeed' => NULL,
|
||||
'MaxWhiteSpeed' => NULL,
|
||||
'HasPresets' => 0,
|
||||
'NumPresets' => 0,
|
||||
'HasHomePreset' => 0,
|
||||
'CanSetPresets' => 0,
|
||||
'Name' => 'New',
|
||||
'Type' => 'Local',
|
||||
'Protocol' => NULL
|
||||
);
|
||||
|
||||
public function __construct( $IdOrRow = NULL ) {
|
||||
if ( $IdOrRow ) {
|
||||
$row = NULL;
|
||||
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
|
||||
$row = dbFetchOne( 'SELECT * FROM Control WHERE Id=?', NULL, array( $IdOrRow ) );
|
||||
if ( ! $row ) {
|
||||
Error("Unable to load Control record for Id=" . $IdOrRow );
|
||||
}
|
||||
} elseif ( is_array( $IdOrRow ) ) {
|
||||
$row = $IdOrRow;
|
||||
} else {
|
||||
Error("Unknown argument passed to Control Constructor ($IdOrRow)");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $row ) {
|
||||
foreach ($row as $k => $v) {
|
||||
$this->{$k} = $v;
|
||||
}
|
||||
} else {
|
||||
Error('No row for Control ' . $IdOrRow );
|
||||
}
|
||||
} # end if isset($IdOrRow)
|
||||
} // end function __construct
|
||||
|
||||
public function __call($fn, array $args){
|
||||
if ( count($args) ) {
|
||||
$this->{$fn} = $args[0];
|
||||
}
|
||||
if ( array_key_exists($fn, $this) ) {
|
||||
return $this->{$fn};
|
||||
#array_unshift($args, $this);
|
||||
#call_user_func_array( $this->{$fn}, $args);
|
||||
} else {
|
||||
if ( array_key_exists($fn, $this->control_fields) ) {
|
||||
return $this->control_fields{$fn};
|
||||
} else if ( array_key_exists( $fn, $this->defaults ) ) {
|
||||
return $this->defaults{$fn};
|
||||
} else {
|
||||
$backTrace = debug_backtrace();
|
||||
$file = $backTrace[1]['file'];
|
||||
$line = $backTrace[1]['line'];
|
||||
Warning( "Unknown function call Control->$fn from $file:$line" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function set( $data ) {
|
||||
foreach ($data as $k => $v) {
|
||||
if ( is_array( $v ) ) {
|
||||
# perhaps should turn into a comma-separated string
|
||||
$this->{$k} = implode(',',$v);
|
||||
} else if ( is_string( $v ) ) {
|
||||
$this->{$k} = trim( $v );
|
||||
} else if ( is_integer( $v ) ) {
|
||||
$this->{$k} = $v;
|
||||
} else if ( is_bool( $v ) ) {
|
||||
$this->{$k} = $v;
|
||||
} else {
|
||||
Error( "Unknown type $k => $v of var " . gettype( $v ) );
|
||||
$this->{$k} = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
public static function find_all( $parameters = null, $options = null ) {
|
||||
$filters = array();
|
||||
$sql = 'SELECT * FROM Controls ';
|
||||
$values = array();
|
||||
|
||||
if ( $parameters ) {
|
||||
$fields = array();
|
||||
$sql .= 'WHERE ';
|
||||
foreach ( $parameters as $field => $value ) {
|
||||
if ( $value == null ) {
|
||||
$fields[] = $field.' IS NULL';
|
||||
} else if ( is_array( $value ) ) {
|
||||
$func = function(){return '?';};
|
||||
$fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')';
|
||||
$values += $value;
|
||||
|
||||
} else {
|
||||
$fields[] = $field.'=?';
|
||||
$values[] = $value;
|
||||
}
|
||||
}
|
||||
$sql .= implode(' AND ', $fields );
|
||||
}
|
||||
if ( $options and isset($options['order']) ) {
|
||||
$sql .= ' ORDER BY ' . $options['order'];
|
||||
}
|
||||
$result = dbQuery($sql, $values);
|
||||
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Control');
|
||||
foreach ( $results as $row => $obj ) {
|
||||
$filters[] = $obj;
|
||||
}
|
||||
return $filters;
|
||||
}
|
||||
|
||||
public function save( $new_values = null ) {
|
||||
|
||||
if ( $new_values ) {
|
||||
foreach ( $new_values as $k=>$v ) {
|
||||
$this->{$k} = $v;
|
||||
}
|
||||
}
|
||||
// Set default values
|
||||
foreach ( $this->defaults as $k=>$v ) {
|
||||
if ( ( ! array_key_exists( $k, $this ) ) or ( $this->{$k} == '' ) ) {
|
||||
$this->{$k} = $v;
|
||||
}
|
||||
}
|
||||
|
||||
$fields = array_keys( $this->defaults );
|
||||
|
||||
if ( array_key_exists( 'Id', $this ) ) {
|
||||
$sql = 'UPDATE Controls SET '.implode(', ', array_map( function($field) {return $field.'=?';}, $fields ) ) . ' WHERE Id=?';
|
||||
$values = array_map( function($field){return $this->{$field};}, $fields );
|
||||
$values[] = $this->{'Id'};
|
||||
dbQuery( $sql, $values );
|
||||
} else {
|
||||
$sql = 'INSERT INTO Controls SET '.implode(', ', array_map( function($field) {return $field.'=?';}, $fields ) ) . '';
|
||||
$values = array_map( function($field){return $this->{$field};}, $fields );
|
||||
dbQuery( $sql, $values );
|
||||
$this->{'Id'} = dbInsertId();
|
||||
}
|
||||
} // end function save
|
||||
|
||||
} // end class Control
|
||||
?>
|
|
@ -168,15 +168,22 @@ class Event {
|
|||
} else {
|
||||
$streamSrc .= $_SERVER['HTTP_HOST'];
|
||||
}
|
||||
$streamSrc .= ( ZM_BASE_PATH != '/' ? ZM_BASE_PATH : '' ).'/index.php?view=view_video&eid='.$this->{'Id'};
|
||||
return $streamSrc;
|
||||
$streamSrc .= ( ZM_BASE_PATH != '/' ? ZM_BASE_PATH : '' ).'/index.php';
|
||||
$args['eid'] = $this->{'Id'};
|
||||
$args['view'] = 'view_video';
|
||||
} else {
|
||||
$streamSrc = ZM_BASE_URL.ZM_PATH_ZMS;
|
||||
|
||||
$args['source'] = 'event';
|
||||
$args['event'] = $this->{'Id'};
|
||||
if ( ( (!isset($args['mode'])) or ( $args['mode'] != 'single' ) ) && !empty($GLOBALS['connkey']) ) {
|
||||
$args['connkey'] = $GLOBALS['connkey'];
|
||||
}
|
||||
if ( ZM_RAND_STREAM ) {
|
||||
$args['rand'] = time();
|
||||
}
|
||||
}
|
||||
|
||||
$streamSrc = ZM_BASE_URL.ZM_PATH_ZMS;
|
||||
|
||||
$args['source'] = 'event';
|
||||
$args['event'] = $this->{'Id'};
|
||||
|
||||
if ( ZM_OPT_USE_AUTH ) {
|
||||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||
$args['auth'] = generateAuthHash( ZM_AUTH_HASH_IPS );
|
||||
|
@ -187,16 +194,10 @@ class Event {
|
|||
$args['user'] = $_SESSION['username'];
|
||||
}
|
||||
}
|
||||
if ( ( (!isset($args['mode'])) or ( $args['mode'] != 'single' ) ) && !empty($GLOBALS['connkey']) ) {
|
||||
$args['connkey'] = $GLOBALS['connkey'];
|
||||
}
|
||||
if ( ZM_RAND_STREAM ) {
|
||||
$args['rand'] = time();
|
||||
}
|
||||
|
||||
$streamSrc .= '?'.http_build_query( $args,'', $querySep );
|
||||
|
||||
return( $streamSrc );
|
||||
return $streamSrc;
|
||||
} // end function getStreamSrc
|
||||
|
||||
function DiskSpace( $new='' ) {
|
||||
|
|
|
@ -135,7 +135,7 @@ public $defaults = array(
|
|||
return $this->{'MonitorIds'};
|
||||
}
|
||||
|
||||
public static function get_group_dropdown() {
|
||||
public static function get_group_dropdown( ) {
|
||||
|
||||
session_start();
|
||||
$selected_group_id = 0;
|
||||
|
@ -148,13 +148,23 @@ public $defaults = array(
|
|||
}
|
||||
session_write_close();
|
||||
|
||||
return htmlSelect( 'Group[]', Group::get_dropdown_options(), isset($_SESSION['Group'])?$_SESSION['Group']:null, array(
|
||||
'onchange' => 'this.form.submit();',
|
||||
'class'=>'chosen',
|
||||
'multiple'=>'multiple',
|
||||
'data-placeholder'=>'All',
|
||||
) );
|
||||
|
||||
} # end public static function get_group_dropdown
|
||||
|
||||
public static function get_dropdown_options() {
|
||||
$Groups = array();
|
||||
foreach ( Group::find_all( ) as $Group ) {
|
||||
$Groups[$Group->Id()] = $Group;
|
||||
}
|
||||
|
||||
# This array is indexed by parent_id
|
||||
global $children;
|
||||
global $children;
|
||||
$children = array();
|
||||
|
||||
foreach ( $Groups as $id=>$Group ) {
|
||||
|
@ -181,16 +191,10 @@ global $children;
|
|||
$group_options += get_options( $Group );
|
||||
}
|
||||
}
|
||||
return htmlSelect( 'Group[]', $group_options, isset($_SESSION['Group'])?$_SESSION['Group']:null, array(
|
||||
'onchange' => 'this.form.submit();',
|
||||
'class'=>'chosen',
|
||||
'multiple'=>'multiple',
|
||||
'data-placeholder'=>'All',
|
||||
) );
|
||||
return $group_options;
|
||||
}
|
||||
|
||||
} # end public static function get_group_dropdown
|
||||
|
||||
public static function get_group_dropdowns() {
|
||||
public static function get_group_dropdowns( $selected = null ) {
|
||||
# This will end up with the group_id of the deepest selection
|
||||
$group_id = 0;
|
||||
$depth = 0;
|
||||
|
@ -205,6 +209,7 @@ global $children;
|
|||
break;
|
||||
|
||||
$parent_group_ids = array();
|
||||
if ( ! $selected ) {
|
||||
$selected_group_id = 0;
|
||||
if ( isset($_REQUEST['group'.$depth]) ) {
|
||||
$selected_group_id = $group_id = $_SESSION['group'.$depth] = $_REQUEST['group'.$depth];
|
||||
|
@ -213,6 +218,9 @@ global $children;
|
|||
} else if ( isset($_REQUEST['filtering']) ) {
|
||||
unset($_SESSION['group'.$depth]);
|
||||
}
|
||||
} else {
|
||||
$selected_group_id = $selected;
|
||||
}
|
||||
|
||||
foreach ( $Groups as $Group ) {
|
||||
if ( ! isset( $groups[$depth] ) ) {
|
||||
|
@ -280,6 +288,23 @@ $group_options[$Group->Id()] = str_repeat( ' ', $depth ) . $Group->Name();
|
|||
return $monitor_id;
|
||||
}
|
||||
|
||||
public function Parent( ) {
|
||||
if ( $this->{'ParentId'} ) {
|
||||
return new Group($this->{'ParentId'});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function Parents() {
|
||||
$Parents = array();
|
||||
$Parent = $this->Parent();
|
||||
while( $Parent ) {
|
||||
array_unshift($Parents, $Parent);
|
||||
$Parent = $Parent->Parent();
|
||||
}
|
||||
return $Parents;
|
||||
}
|
||||
|
||||
} # end class Group
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,9 @@ private $defaults = array(
|
|||
'OutputContainer' => 'auto',
|
||||
'ZoneCount' => 0,
|
||||
'Triggers' => null,
|
||||
'Type' => 'Ffmpeg',
|
||||
'MaxFPS' => null,
|
||||
'AlarmMaxFPS' => null,
|
||||
);
|
||||
private $status_fields = array(
|
||||
'AnalysisFPS' => null,
|
||||
|
@ -395,5 +398,15 @@ Logger::Debug("sending command to $url");
|
|||
}
|
||||
} // end if we are on the recording server
|
||||
}
|
||||
public function GroupIds( ) {
|
||||
if ( !array_key_exists('GroupIds', $this) ) {
|
||||
if ( array_key_exists('Id', $this) and $this->{'Id'} ) {
|
||||
$this->{'GroupIds'} = dbFetchAll( 'SELECT GroupId FROM Groups_Monitors WHERE MonitorId=?', 'GroupId', array($this->{'Id'}) );
|
||||
} else {
|
||||
$this0->{'GroupIds'} = array();
|
||||
}
|
||||
}
|
||||
return $this->{'GroupIds'};
|
||||
}
|
||||
} // end class Monitor
|
||||
?>
|
||||
|
|
|
@ -271,36 +271,14 @@ if ( !empty($_REQUEST['mid']) && canView( 'Control', $_REQUEST['mid'] ) ) {
|
|||
}
|
||||
|
||||
// Control capability actions, require control edit permissions
|
||||
if ( canEdit( 'Control' ) ) {
|
||||
if ( canEdit('Control') ) {
|
||||
if ( $action == 'controlcap' ) {
|
||||
if ( !empty($_REQUEST['cid']) ) {
|
||||
$control = dbFetchOne( 'SELECT * FROM Controls WHERE Id = ?', NULL, array($_REQUEST['cid']) );
|
||||
} else {
|
||||
$control = array();
|
||||
}
|
||||
require_once( 'Control.php' );
|
||||
$Control = new Control( !empty($_REQUEST['cid']) ? $_REQUEST['cid'] : null );
|
||||
|
||||
// Define a field type for anything that's not simple text equivalent
|
||||
$types = array(
|
||||
// Empty
|
||||
);
|
||||
|
||||
$columns = getTableColumns( 'Controls' );
|
||||
foreach ( $columns as $name=>$type ) {
|
||||
if ( preg_match( '/^(Can|Has)/', $name ) ) {
|
||||
$types[$name] = 'toggle';
|
||||
}
|
||||
}
|
||||
$changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns );
|
||||
|
||||
if ( count( $changes ) ) {
|
||||
if ( !empty($_REQUEST['cid']) ) {
|
||||
dbQuery( 'update Controls set '.implode( ', ', $changes ).' where Id = ?', array($_REQUEST['cid']) );
|
||||
} else {
|
||||
dbQuery( 'insert into Controls set '.implode( ', ', $changes ) );
|
||||
//$_REQUEST['cid'] = dbInsertId();
|
||||
}
|
||||
$refreshParent = true;
|
||||
}
|
||||
//$changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns );
|
||||
$Control->save( $_REQUEST['newControl'] );
|
||||
$refreshParent = true;
|
||||
$view = 'none';
|
||||
} elseif ( $action == 'delete' ) {
|
||||
if ( isset($_REQUEST['markCids']) ) {
|
||||
|
@ -310,8 +288,8 @@ if ( canEdit( 'Control' ) ) {
|
|||
$refreshParent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end if action
|
||||
} // end if canEdit Controls
|
||||
|
||||
if ( isset($_REQUEST['object']) and $_REQUEST['object'] == 'Monitor' ) {
|
||||
if ( $action == 'save' ) {
|
||||
|
@ -471,7 +449,7 @@ if ( canEdit( 'Monitors' ) ) {
|
|||
$mid = 0;
|
||||
if ( !empty($_REQUEST['mid']) ) {
|
||||
$mid = validInt($_REQUEST['mid']);
|
||||
$monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id = ?', NULL, array($mid) );
|
||||
$monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) );
|
||||
|
||||
if ( ZM_OPT_X10 ) {
|
||||
$x10Monitor = dbFetchOne( 'SELECT * FROM TriggersX10 WHERE MonitorId=?', NULL, array($mid) );
|
||||
|
@ -484,6 +462,7 @@ if ( canEdit( 'Monitors' ) ) {
|
|||
$x10Monitor = array();
|
||||
}
|
||||
}
|
||||
$Monitor = new Monitor( $monitor );
|
||||
|
||||
// Define a field type for anything that's not simple text equivalent
|
||||
$types = array(
|
||||
|
@ -519,6 +498,7 @@ if ( canEdit( 'Monitors' ) ) {
|
|||
zmaControl( $monitor, 'stop' );
|
||||
zmcControl( $monitor, 'stop' );
|
||||
dbQuery( 'UPDATE Monitors SET '.implode( ', ', $changes ).' WHERE Id=?', array($mid) );
|
||||
// Groups will be added below
|
||||
if ( isset($changes['Name']) or isset($changes['StorageId']) ) {
|
||||
$OldStorage = new Storage( $monitor['StorageId'] );
|
||||
$saferOldName = basename( $monitor['Name'] );
|
||||
|
@ -578,16 +558,25 @@ if ( canEdit( 'Monitors' ) ) {
|
|||
mkdir( $Storage->Path().'/'.$mid, 0755 );
|
||||
$saferName = basename($_REQUEST['newMonitor']['Name']);
|
||||
symlink( $mid, $Storage->Path().'/'.$saferName );
|
||||
if ( isset($_COOKIE['zmGroup']) ) {
|
||||
dbQuery( 'INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($_COOKIE['zmGroup'],$mid) );
|
||||
}
|
||||
|
||||
} else {
|
||||
Error("Error saving new Monitor.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Error("Users with Monitors restrictions cannot create new monitors.");
|
||||
return;
|
||||
}
|
||||
if ( count($_POST['newMonitor']['GroupIds']) != count($Monitor->GroupIds()) or array_diff($_POST['newMonitor']['GroupIds'], $Monitor->GroupIds() ) ) {
|
||||
if ( $Monitor->Id() )
|
||||
dbQuery('DELETE FROM Groups_Monitors WHERE MonitorId=?', null, array($Mid));
|
||||
|
||||
if ( isset($_POST['newMonitor']['GroupIds']) ) {
|
||||
foreach ( $_POST['newMonitor']['GroupIds'] as $group_id ) {
|
||||
dbQuery( 'INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid) );
|
||||
}
|
||||
}
|
||||
} // end if there has been a change of groups
|
||||
$restart = true;
|
||||
} # end if count(changes)
|
||||
|
||||
|
|
|
@ -553,20 +553,19 @@ function htmlSelect( $name, $contents, $values, $behaviours=false ) {
|
|||
}
|
||||
}
|
||||
|
||||
$html = "<select name=\"$name\" id=\"$name\"$behaviourText>";
|
||||
return "<select name=\"$name\" id=\"$name\"$behaviourText>".htmlOptions( $contents, $values ).'</select>';
|
||||
}
|
||||
|
||||
function htmlOptions( $contents, $values ) {
|
||||
$html = '';
|
||||
foreach ( $contents as $value=>$text ) {
|
||||
if ( is_array( $text ) )
|
||||
$text = $text['Name'];
|
||||
else if ( is_object( $text ) )
|
||||
$text = $text->Name();
|
||||
//for ( $i = 0; $i < count($contents); $i +=2 ) {
|
||||
//$value = $contents[$i];
|
||||
//$text = $contents[$i+1];
|
||||
$selected = is_array( $values ) ? in_array( $value, $values ) : !strcmp($value, $values);
|
||||
//Warning("Selected is $selected from $value and $values");
|
||||
$html .= "<option value=\"$value\"".($selected?" selected=\"selected\"":'').">$text</option>";
|
||||
}
|
||||
$html .= '</select>';
|
||||
return $html;
|
||||
}
|
||||
|
||||
|
@ -634,6 +633,7 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) {
|
|||
|
||||
if ( !isset($types[$key]) )
|
||||
$types[$key] = false;
|
||||
|
||||
switch( $types[$key] ) {
|
||||
case 'set' :
|
||||
{
|
||||
|
@ -695,6 +695,16 @@ function getFormChanges( $values, $newValues, $types=false, $columns=false ) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case 'toggle' :
|
||||
if ( (!isset($values[$key])) or $values[$key] != $value ) {
|
||||
if ( empty($value) ) {
|
||||
$changes[$key] = "$key = 0";
|
||||
} else {
|
||||
$changes[$key] = "$key = 0";
|
||||
//$changes[$key] = $key . ' = '.dbEscape(trim($value));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default :
|
||||
{
|
||||
if ( !isset($values[$key]) || ($values[$key] != $value) ) {
|
||||
|
|
|
@ -180,6 +180,7 @@ xhtmlHeaders( __FILE__, translate('Console') );
|
|||
<?php
|
||||
for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
|
||||
$monitor = $displayMonitors[$monitor_i];
|
||||
$Monitor = new Monitor($monitor);
|
||||
?>
|
||||
<tr id="<?php echo 'monitor_id-'.$monitor['Id'] ?>" title="<?php echo $monitor['Id'] ?>">
|
||||
<?php
|
||||
|
@ -209,7 +210,16 @@ $stream_available = canView('Stream') && $monitor['CaptureFPS'] && $monitor['Fun
|
|||
<?php
|
||||
}
|
||||
?>
|
||||
<td class="colName"><a <?php echo ($stream_available ? 'href="?view=watch&mid='.$monitor['Id'].'">' : '>') . $monitor['Name'] ?></a><br/><?php echo $monitor['Status'] ?></td>
|
||||
<td class="colName">
|
||||
<a <?php echo ($stream_available ? 'href="?view=watch&mid='.$monitor['Id'].'">' : '>') . $monitor['Name'] ?></a><br/>
|
||||
<?php echo $monitor['Status'] ?><br/>
|
||||
<?php echo implode('<br/>',
|
||||
array_map(function($group_id){
|
||||
$Group = new Group($group_id);
|
||||
return implode(' > ', array_map(function($Group){ return $Group->Name(); }, $Group->Parents()));
|
||||
}, $Monitor->GroupIds() ) );
|
||||
?>
|
||||
</td>
|
||||
<td class="colFunction">
|
||||
<?php echo makePopupLink( '?view=function&mid='.$monitor['Id'], 'zmFunction', 'function', '<span class="'.$fclass.'">'.translate('Fn'.$monitor['Function']).( empty($monitor['Enabled']) ? ', disabled' : '' ) .'</span>', canEdit( 'Monitors' ) ) ?><br/>
|
||||
<?php
|
||||
|
|
|
@ -38,42 +38,95 @@ function evaluateLoadTimes() {
|
|||
else if (avgFrac >= 0.2) currentDisplayInterval = (currentDisplayInterval * 1.50).toFixed(1);
|
||||
else if (avgFrac >= 0.1) currentDisplayInterval = (currentDisplayInterval * 2.00).toFixed(1);
|
||||
else currentDisplayInterval = (currentDisplayInterval * 2.50).toFixed(1);
|
||||
currentDisplayInterval = Math.min(Math.max(currentDisplayInterval, 30), 10000); // limit this from about 30fps to .1 fps
|
||||
// limit this from about 40fps to .1 fps
|
||||
currentDisplayInterval = Math.min(Math.max(currentDisplayInterval, 40), 10000);
|
||||
imageLoadTimesEvaluated=0;
|
||||
setSpeed(speedIndex);
|
||||
$('fps').innerHTML="Display refresh rate is " + (1000 / currentDisplayInterval).toFixed(1) + " per second, avgFrac=" + avgFrac.toFixed(3) + ".";
|
||||
} // end evaluateLoadTimes()
|
||||
|
||||
function getFrame( monId, time ) {
|
||||
var Frame = null;
|
||||
for ( var event_id in events ) {
|
||||
// Search for the event matching this time. Would be more efficient if we had events indexed by monitor
|
||||
Event = events[event_id];
|
||||
if ( Event.MonitorId != monId || Event.StartTimeSecs > time || Event.EndTimeSecs < time )
|
||||
continue;
|
||||
|
||||
var duration = Event.EndTimeSecs - Event.StartTimeSecs;
|
||||
var frame = parseInt((time - Event.StartTimeSecs)/(duration)*Object.keys(Event.FramesById).length)+1;
|
||||
// Need to get frame by time, not some fun calc that assumes frames have the same mlength.
|
||||
// Frames are not sorted.
|
||||
for ( var frame_id in Event.FramesById ) {
|
||||
if ( 0 ) {
|
||||
if ( frame == 0 ) {
|
||||
console.log("Found frame for time " + time );
|
||||
console.log(Frame);
|
||||
Frame = Event.FramesById[frame_id];
|
||||
break;
|
||||
}
|
||||
frame --;
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
Event.FramesById[frame_id].TimeStampSecs == time
|
||||
|| (
|
||||
Event.FramesById[frame_id].TimeStampSecs < time
|
||||
&& (
|
||||
(!Event.FramesById[frame_id].NextTimeStampSecs)
|
||||
||
|
||||
(Event.FramesById[frame_id].NextTimeStampSecs > time)
|
||||
)
|
||||
)
|
||||
) {
|
||||
Frame = Event.FramesById[frame_id];
|
||||
break;
|
||||
}
|
||||
} // end foreach frame in the event.
|
||||
if ( ! Frame ) {
|
||||
console.log("Difn't find frame for " + time );
|
||||
return null;
|
||||
}
|
||||
} // end foreach event
|
||||
return Frame;
|
||||
}
|
||||
|
||||
// time is seconds since epoch
|
||||
function getImageSource( monId, time ) {
|
||||
if ( liveMode == 1 ) {
|
||||
var new_url = monitorImageObject[monId].src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
|
||||
var new_url = monitorImageObject[monId].src.replace(
|
||||
/rand=\d+/i,
|
||||
'rand='+Math.floor((Math.random() * 1000000) )
|
||||
);
|
||||
if ( auth_hash ) {
|
||||
// update auth hash
|
||||
new_url = new_url.replace(/auth=[a-z0-9]+/i, 'auth='+auth_hash);
|
||||
}
|
||||
return new_url;
|
||||
}
|
||||
var Frame = getFrame(monId, time);
|
||||
if ( Frame ) {
|
||||
Event = events[Frame.EventId];
|
||||
|
||||
for ( var i=0, eIdlength = eId.length; i < eIdlength; i++ ) {
|
||||
// Search for the event matching this time. Would be more efficient if we had events indexed by monitor
|
||||
if ( eMonId[i] == monId && time >= eStartSecs[i] && time <= eEndSecs[i] ) {
|
||||
var duration = eEndSecs[i]-eStartSecs[i];
|
||||
var frame = parseInt((time - eStartSecs[i])/(duration)*eventFrames[i])+1;
|
||||
var storage = Storage[eStorageId[i]];
|
||||
if ( storage.ServerId ) {
|
||||
var server = Servers[storage.ServerId];
|
||||
if ( server ) {
|
||||
//console.log( server.Hostname + " for event " + eId[i] );
|
||||
return location.protocol + '//' + server.Hostname + '/index.php?view=image&eid=' + eId[i] + '&fid='+frame + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height;
|
||||
} else {
|
||||
console.log("No server found for " + storage.ServerId );
|
||||
}
|
||||
var storage = Storage[Event.StorageId];
|
||||
if ( storage.ServerId ) {
|
||||
var server = Servers[storage.ServerId];
|
||||
if ( server ) {
|
||||
//console.log( server.Hostname + " for event " + eId[i] );
|
||||
return location.protocol + '//' + server.Hostname +
|
||||
//'/cgi-bin/zms?mode=jpeg&replay=single&event=' + event_id +
|
||||
//'&frame='+Frame.FrameId +
|
||||
'/index.php?view=image&eid=' + event_id + '&fid='+Frame.FrameId +
|
||||
"&width=" + monitorCanvasObj[monId].width +
|
||||
"&height=" + monitorCanvasObj[monId].height;
|
||||
} else {
|
||||
console.log("No server found for " + storage.ServerId );
|
||||
}
|
||||
//console.log("No storage found for " + eStorageId[i] );
|
||||
return "index.php?view=image&eid=" + eId[i] + '&fid='+frame + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height;
|
||||
}
|
||||
} // end for
|
||||
//console.log("No storage found for " + eStorageId[i] );
|
||||
return '/index.php?view=image&eid=' + Frame.EventId + '&fid='+Frame.FrameId + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height;
|
||||
return "/cgi-bin/zms?mode=jpeg&replay=single&event=" + Frame.EventId + '&frame='+Frame.FrameId + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height;
|
||||
} // end found Frame
|
||||
return "no data";
|
||||
}
|
||||
|
||||
|
@ -294,7 +347,7 @@ function drawGraph() {
|
|||
|
||||
canvas.height = cHeight;
|
||||
|
||||
if ( eId.length == 0 ) {
|
||||
if ( Object.keys(events).length == 0 ) {
|
||||
ctx.globalAlpha=1;
|
||||
ctx.font= "40px Georgia";
|
||||
ctx.fillStyle="white";
|
||||
|
@ -308,24 +361,32 @@ function drawGraph() {
|
|||
|
||||
// first fill in the bars for the events (not alarms)
|
||||
|
||||
for(var i=0; i<eId.length; i++) { // Display all we loaded
|
||||
for ( var event_id in events ) {
|
||||
var Event = events[event_id];
|
||||
|
||||
var x1=parseInt( (eStartSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth) ; // round low end down
|
||||
var x2=parseInt( (eEndSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ) ; // round high end up to be sure consecutive ones connect
|
||||
ctx.fillStyle=monitorColour[eMonId[i]];
|
||||
// round low end down
|
||||
var x1 = parseInt((Event.StartTimeSecs - minTimeSecs) / rangeTimeSecs * cWidth);
|
||||
var x2 = parseInt((Event.EndTimeSecs - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ) ; // round high end up to be sure consecutive ones connect
|
||||
ctx.fillStyle = monitorColour[Event.MonitorId];
|
||||
ctx.globalAlpha = 0.2; // light color for background
|
||||
ctx.clearRect(x1,monitorIndex[eMonId[i]]*rowHeight,x2-x1,rowHeight); // Erase any overlap so it doesn't look artificially darker
|
||||
ctx.fillRect (x1,monitorIndex[eMonId[i]]*rowHeight,x2-x1,rowHeight);
|
||||
}
|
||||
for(var i=0; (i<fScore.length) && (maxScore>0); i++) {
|
||||
// Now put in scored frames (if any)
|
||||
var x1=parseInt( (fTimeFromSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth) ; // round low end down
|
||||
var x2=parseInt( (fTimeToSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ) ; // round up
|
||||
if(x2-x1 < 2) x2=x1+2; // So it is visible make them all at least this number of seconds wide
|
||||
ctx.fillStyle=monitorColour[fMonId[i]];
|
||||
ctx.globalAlpha = 0.4 + 0.6 * (1 - fScore[i]/maxScore); // Background is scaled but even lowest is twice as dark as the background
|
||||
ctx.fillRect(x1,monitorIndex[fMonId[i]]*rowHeight,x2-x1,rowHeight);
|
||||
}
|
||||
ctx.clearRect(x1,monitorIndex[Event.MonitorId]*rowHeight,x2-x1,rowHeight); // Erase any overlap so it doesn't look artificially darker
|
||||
ctx.fillRect(x1,monitorIndex[Event.MonitorId]*rowHeight,x2-x1,rowHeight);
|
||||
|
||||
for ( var frame_id in Event.FramesById ) {
|
||||
var Frame = Event.FramesById[frame_id];
|
||||
if ( ! Frame.Score )
|
||||
continue;
|
||||
|
||||
// Now put in scored frames (if any)
|
||||
var x1=parseInt( (Frame.TimeStampSecs - minTimeSecs) / rangeTimeSecs * cWidth) ; // round low end down
|
||||
var x2=parseInt( (Frame.TimeStampSecs - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ) ; // round up
|
||||
if(x2-x1 < 2) x2=x1+2; // So it is visible make them all at least this number of seconds wide
|
||||
ctx.fillStyle=monitorColour[Event.MonitorId];
|
||||
ctx.globalAlpha = 0.4 + 0.6 * (1 - Frame.Score/maxScore); // Background is scaled but even lowest is twice as dark as the background
|
||||
ctx.fillRect(x1,monitorIndex[Event.MonitorId]*rowHeight,x2-x1,rowHeight);
|
||||
} // end foreach frame
|
||||
} // end foreach Event
|
||||
|
||||
for(var i=0; i<numMonitors; i++) {
|
||||
// Note that this may be a sparse array
|
||||
ctx.font= parseInt(rowHeight * timeLabelsFractOfRow).toString() + "px Georgia";
|
||||
|
@ -343,7 +404,8 @@ function redrawScreen() {
|
|||
// if we are not in live view switch to history -- this has to come before fit in case we re-establish the timeline
|
||||
$('DateTimeDiv').style.display="none";
|
||||
$('SpeedDiv').style.display="none";
|
||||
$('timelinediv').style.display="none";
|
||||
var timelinediv= $('timelinediv');
|
||||
if ( timelinediv ) timelinediv.style.display="none";
|
||||
$('liveButton').innerHTML="History";
|
||||
$('zoomin').style.display="none";
|
||||
$('zoomout').style.display="none";
|
||||
|
@ -368,6 +430,7 @@ function redrawScreen() {
|
|||
$('panright').style.display="inline";
|
||||
$('panright').style.display="inline-flex";
|
||||
if ($('downloadVideo')) $('downloadVideo').style.display="inline";
|
||||
drawGraph();
|
||||
}
|
||||
|
||||
if ( fitMode == 1 ) {
|
||||
|
@ -391,7 +454,6 @@ function redrawScreen() {
|
|||
$('fit').innerHTML="Fit";
|
||||
setScale(currentScale);
|
||||
}
|
||||
drawGraph();
|
||||
outputUpdate(currentTimeSecs);
|
||||
timerFire(); // force a fire in case it's not timing
|
||||
}
|
||||
|
@ -709,28 +771,15 @@ function showOneMonitor(monId) {
|
|||
url="?view=watch&mid=" + monId.toString();
|
||||
createPopup(url, 'zmWatch', 'watch', monitorWidth[monId], monitorHeight[monId] );
|
||||
} else {
|
||||
for ( var i=0, len=eId.length; i<len; i++ ) {
|
||||
if ( eMonId[i] != monId )
|
||||
continue;
|
||||
|
||||
if ( currentTimeSecs >= eStartSecs[i] && currentTimeSecs <= eEndSecs[i] ) {
|
||||
url="?view=event&eid=" + eId[i] + '&fid=' + parseInt(Math.max(1, Math.min(eventFrames[i], eventFrames[i] * (currentTimeSecs - eStartSecs[i]) / (eEndSecs[i] - eStartSecs[i] + 1) ) ));
|
||||
break;
|
||||
} else if ( currentTimeSecs <= eStartSecs[i] ) {
|
||||
if ( i ) {
|
||||
// Didn't find an exact event, so go with the one before.
|
||||
url="?view=event&eid=" + eId[i] + '&fid=' + parseInt(Math.max(1, Math.min(eventFrames[i], eventFrames[i] * (currentTimeSecs - eStartSecs[i]) / (eEndSecs[i] - eStartSecs[i] + 1) ) ));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( url ) {
|
||||
createPopup(url, 'zmEvent', 'event', monitorWidth[monId], monitorHeight[monId]);
|
||||
var Frame = getFrame( monId, currentTimeSecs );
|
||||
if ( Frame ) {
|
||||
url="?view=event&eid=" + Frame.EventId + '&fid=' +Frame.FrameId;
|
||||
createPopup(url, 'zmEvent', 'event', monitorWidth[monId], monitorHeight[monId]);
|
||||
} else {
|
||||
url="?view=watch&mid=" + monId.toString();
|
||||
createPopup(url, 'zmWatch', 'watch', monitorWidth[monId], monitorHeight[monId] );
|
||||
}
|
||||
}
|
||||
} // end if live/events
|
||||
}
|
||||
|
||||
function zoom(monId,scale) {
|
||||
|
@ -787,8 +836,6 @@ function changeDateTime(e) {
|
|||
// >>>>>>>>> Initialization that runs on window load by being at the bottom
|
||||
|
||||
function initPage() {
|
||||
canvas = $("timeline");
|
||||
ctx = canvas.getContext('2d');
|
||||
for ( var i = 0, len = monitorPtr.length; i < len; i += 1 ) {
|
||||
var monId = monitorPtr[i];
|
||||
if ( ! monId ) continue;
|
||||
|
@ -804,7 +851,11 @@ function initPage() {
|
|||
loadImage2Monitor( monId, monitorImageURL[monId] );
|
||||
}
|
||||
}
|
||||
drawGraph();
|
||||
if ( !liveMode ) {
|
||||
canvas = $("timeline");
|
||||
ctx = canvas.getContext('2d');
|
||||
drawGraph();
|
||||
}
|
||||
setSpeed(speedIndex);
|
||||
//setFit(fitMode); // will redraw
|
||||
//setLive(liveMode); // will redraw
|
||||
|
|
|
@ -9,10 +9,14 @@ echo $offset . '; // ' . floor($offset / 3600) . ' hours ';
|
|||
var currentScale=<?php echo $defaultScale?>;
|
||||
var liveMode=<?php echo $liveMode?>;
|
||||
var fitMode=<?php echo $fitMode?>;
|
||||
var currentSpeed=<?php echo $speeds[$speedIndex]?>; // slider scale, which is only for replay and relative to real time
|
||||
|
||||
// slider scale, which is only for replay and relative to real time
|
||||
var currentSpeed=<?php echo $speeds[$speedIndex]?>;
|
||||
var speedIndex=<?php echo $speedIndex?>;
|
||||
|
||||
// will be set based on performance, this is the display interval in milliseconds for history, and fps for live, and dynamically determined (in ms)
|
||||
// will be set based on performance, this is the display interval in milliseconds
|
||||
// for history, and fps for live, and dynamically determined (in ms)
|
||||
|
||||
var currentDisplayInterval=<?php echo $initialDisplayInterval?>;
|
||||
var playSecsperInterval=1; // How many seconds of recorded image we play per refresh determined by speed (replay rate) and display interval; (default=1 if coming from live)
|
||||
var timerInterval; // milliseconds between interrupts
|
||||
|
@ -21,12 +25,6 @@ var freeTimeLastIntervals=[]; // Percentage of current interval used in loadi
|
|||
var imageLoadTimesEvaluated=0; // running count
|
||||
var imageLoadTimesNeeded=15; // and how many we need
|
||||
var timeLabelsFractOfRow = 0.9;
|
||||
var eMonId = [];
|
||||
var eId = [];
|
||||
var eStorageId = [];
|
||||
var eStartSecs = [];
|
||||
var eEndSecs = [];
|
||||
var eventFrames = []; // this is going to presume all frames equal durationlength
|
||||
|
||||
<?php
|
||||
|
||||
|
@ -41,6 +39,7 @@ if ( ! $minTimeSecs )
|
|||
|
||||
$index = 0;
|
||||
$anyAlarms = false;
|
||||
$maxScore=0;
|
||||
|
||||
if ( ! $liveMode ) {
|
||||
$result = dbQuery( $eventsSql );
|
||||
|
@ -49,27 +48,53 @@ if ( ! $liveMode ) {
|
|||
return;
|
||||
}
|
||||
|
||||
$EventsById = array();
|
||||
|
||||
while( $event = $result->fetch( PDO::FETCH_ASSOC ) ) {
|
||||
$event_id = $event['Id'];
|
||||
$EventsById[$event_id] = $event;
|
||||
}
|
||||
|
||||
$StartTimeSecs = strtotime($event['StartTime']);
|
||||
$EndTimeSecs = strtotime($event['EndTime']);
|
||||
$next_frames = array();
|
||||
|
||||
if ( $minTimeSecs > $StartTimeSecs ) $minTimeSecs = $StartTimeSecs;
|
||||
if ( $result = dbQuery($frameSql) ) {
|
||||
$next_frame = null;
|
||||
while( $frame = $result->fetch(PDO::FETCH_ASSOC) ) {
|
||||
$event_id = $frame['EventId'];
|
||||
$event = &$EventsById[$event_id];
|
||||
|
||||
$frame['TimeStampSecs'] = $event['StartTimeSecs'] + $frame['Delta'];
|
||||
if ( !isset($event['FramesById']) ) {
|
||||
$event['FramesById'] = array();
|
||||
$frame['NextTimeStampSecs'] = 0;
|
||||
} else {
|
||||
$frame['NextTimeStampSecs'] = $next_frames[$frame['EventId']]['TimeStampSecs'];;
|
||||
}
|
||||
$event['FramesById'] += array( $frame['Id']=>$frame );
|
||||
$next_frames[$frame['EventId']] = $frame;
|
||||
}
|
||||
}
|
||||
|
||||
echo "var events = {\n";
|
||||
foreach ( $EventsById as $event_id=>$event ) {
|
||||
|
||||
$StartTimeSecs = $event['StartTimeSecs'];
|
||||
$EndTimeSecs = $event['EndTimeSecs'];
|
||||
|
||||
if ( $minTimeSecs > $StartTimeSecs ) $minTimeSecs = $StartTimeSecs;
|
||||
if ( $maxTimeSecs < $EndTimeSecs ) $maxTimeSecs = $EndTimeSecs;
|
||||
echo "
|
||||
eMonId[$index]=" . $event['MonitorId'] . ";
|
||||
eStorageId[$index]=".$event['StorageId'] . ";
|
||||
eId[$index]=" . $event['Id'] . ";
|
||||
eStartSecs[$index]=" . $StartTimeSecs . ";
|
||||
eEndSecs[$index]=" . $EndTimeSecs . ";
|
||||
eventFrames[$index]=" . $event['Frames'] . ";
|
||||
|
||||
";
|
||||
$event_json = json_encode($event, JSON_PRETTY_PRINT);
|
||||
echo " $event_id : $event_json,\n";
|
||||
|
||||
$index = $index + 1;
|
||||
if ( $event['MaxScore'] > 0 )
|
||||
if ( $event['MaxScore'] > 0 ) {
|
||||
if ( $event['MaxScore'] > $maxScore )
|
||||
$maxScore = $event['MaxScore'];
|
||||
$anyAlarms = true;
|
||||
}
|
||||
}
|
||||
echo " };\n";
|
||||
|
||||
// if there is no data set the min/max to the passed in values
|
||||
if ( $index == 0 ) {
|
||||
|
@ -93,60 +118,6 @@ eventFrames[$index]=" . $event['Frames'] . ";
|
|||
$maxTimeSecs = strtotime($maxTime);
|
||||
}
|
||||
|
||||
// If we had any alarms in those events, this builds the list of all alarm frames, but consolidated down to (nearly) contiguous segments
|
||||
// comparison in else governs how aggressively it consolidates
|
||||
|
||||
echo "
|
||||
var fMonId = [];
|
||||
var fTimeFromSecs = [];
|
||||
var fTimeToSecs = [];
|
||||
var fScore = [];
|
||||
";
|
||||
$maxScore=0;
|
||||
$index=0;
|
||||
$mId=-1;
|
||||
$fromSecs=-1;
|
||||
$toSecs=-1;
|
||||
$maxScore=-1;
|
||||
|
||||
if ( $anyAlarms && $result = dbQuery( $frameSql ) ) {
|
||||
|
||||
while( $frame = $result->fetch( PDO::FETCH_ASSOC ) ) {
|
||||
if ( $mId < 0 ) {
|
||||
$mId = $frame['MonitorId'];
|
||||
$fromSecs = $frame['TimeStampSecs'];
|
||||
$toSecs = $frame['TimeStampSecs'];
|
||||
$maxScore = $frame['Score'];
|
||||
} else if ( $mId != $frame['MonitorId'] || $frame['TimeStampSecs'] - $toSecs > 10 ) {
|
||||
// dump this one start a new
|
||||
$index++;
|
||||
echo "
|
||||
|
||||
fMonId[$index]= $mId;
|
||||
fTimeFromSecs[$index]= $fromSecs;
|
||||
fTimeToSecs[$index]= $toSecs;
|
||||
fScore[$index]= $maxScore;
|
||||
";
|
||||
$mId = $frame['MonitorId'];
|
||||
$fromSecs = $frame['TimeStampSecs'];
|
||||
$toSecs = $frame['TimeStampSecs'];
|
||||
$maxScore = $frame['Score'];
|
||||
} else {
|
||||
// just add this one on
|
||||
$toSecs = $frame['TimeStampSecs'];
|
||||
if ( $maxScore < $frame['Score'] ) $maxScore = $frame['Score'];
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( $mId > 0 ) {
|
||||
echo "
|
||||
fMonId[$index]= $mId;
|
||||
fTimeFromSecs[$index]= $fromSecs;
|
||||
fTimeToSecs[$index]= $toSecs;
|
||||
fScore[$index]= $maxScore;
|
||||
";
|
||||
}
|
||||
|
||||
echo "var maxScore=$maxScore;\n"; // used to skip frame load if we find no alarms.
|
||||
} // end if initialmodeislive
|
||||
|
||||
|
|
|
@ -89,12 +89,12 @@ if ( ! $monitor ) {
|
|||
'Pass' => '',
|
||||
'Colours' => 4,
|
||||
'Palette' => 0,
|
||||
'Width' => '1280',
|
||||
'Height' => '962',
|
||||
'Width' => '',
|
||||
'Height' => '',
|
||||
'Orientation' => '0',
|
||||
'Deinterlacing' => 0,
|
||||
'RTSPDescribe' => 0,
|
||||
'SaveJPEGs' => '4',
|
||||
'SaveJPEGs' => '0',
|
||||
'VideoWriter' => '1',
|
||||
'EncoderParameters' => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n",
|
||||
'RecordAudio' => '0',
|
||||
|
@ -102,9 +102,9 @@ if ( ! $monitor ) {
|
|||
'LabelX' => 0,
|
||||
'LabelY' => 0,
|
||||
'LabelSize' => 1,
|
||||
'ImageBufferCount' => 40,
|
||||
'WarmupCount' => 1,
|
||||
'PreEventCount' => 1,
|
||||
'ImageBufferCount' => 20,
|
||||
'WarmupCount' => 0,
|
||||
'PreEventCount' => 0,
|
||||
'PostEventCount' => 5,
|
||||
'StreamReplayBuffer' => 0,
|
||||
'AlarmFrameCount' => 1,
|
||||
|
@ -539,7 +539,7 @@ foreach ( $tabs as $name=>$value ) {
|
|||
?>
|
||||
</ul>
|
||||
<div class="clear"></div>
|
||||
<form name="contentForm" id="contentForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>" onsubmit="return validateForm( this )">
|
||||
<form name="contentForm" id="contentForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>" onsubmit="if(validateForm(this)){$j('#contentButtons').hide();return true;}else{return false;};">
|
||||
<input type="hidden" name="view" value="<?php echo $view ?>"/>
|
||||
<input type="hidden" name="tab" value="<?php echo $tab ?>"/>
|
||||
<input type="hidden" name="action" value="monitor"/>
|
||||
|
@ -731,7 +731,7 @@ switch ( $tab ) {
|
|||
<tr>
|
||||
<td><?php echo translate('LinkedMonitors') ?></td>
|
||||
<td>
|
||||
<select name="monitorIds" size="4" multiple="multiple" onchange="updateLinkedMonitors( this )">
|
||||
<select name="monitorIds" class="chosen" multiple="multiple" onchange="updateLinkedMonitors( this )">
|
||||
<?php
|
||||
$monitors = dbFetchAll( 'select Id,Name from Monitors order by Sequence asc' );
|
||||
if ( $monitor->LinkedMonitors() )
|
||||
|
@ -749,6 +749,9 @@ switch ( $tab ) {
|
|||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td><?php echo translate('Groups'); ?></td><td><select name="newMonitor[GroupIds][]" multiple="multiple" class="chosen"><?php
|
||||
echo htmlOptions(Group::get_dropdown_options( ), $monitor->GroupIds() );
|
||||
?></td></tr>
|
||||
<tr><td><?php echo translate('AnalysisFPS') ?></td><td><input type="text" name="newMonitor[AnalysisFPSLimit]" value="<?php echo validHtmlStr($monitor->AnalysisFPSLimit()) ?>" size="6"/></td></tr>
|
||||
<?php
|
||||
if ( $monitor->Type() != 'Local' && $monitor->Type() != 'File' && $monitor->Type() != 'NVSocket' ) {
|
||||
|
@ -1034,11 +1037,14 @@ if ( $monitor->Type() == 'Local' ) {
|
|||
</tbody>
|
||||
</table>
|
||||
<div id="contentButtons">
|
||||
<input type="submit" value="<?php echo translate('Save') ?>"<?php if ( !canEdit( 'Monitors' ) ) { ?> disabled="disabled"<?php } ?>/>
|
||||
<input type="button" value="<?php echo translate('Cancel') ?>" onclick="closeWindow()"/>
|
||||
<button type="submit" value="Save"<?php echo canEdit('Monitors') ? '' : ' disabled="disabled"' ?>><?php echo translate('Save') ?></button>
|
||||
<button onclick="closeWindow()"><?php echo translate('Cancel') ?></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script type="text/javascript">
|
||||
$j('.chosen').chosen();
|
||||
</script>
|
||||
</html>
|
||||
|
|
|
@ -94,14 +94,14 @@ if (isset($_REQUEST['minTime']) && isset($_REQUEST['maxTime']) && count($display
|
|||
// if the bulk record has not been written - to be able to include more current frames reduce bulk frame sizes (event size can be large)
|
||||
// Note we round up just a bit on the end time as otherwise you get gaps, like 59.78 to 00 in the next second, which can give blank frames when moved through slowly.
|
||||
|
||||
$eventsSql = '
|
||||
SELECT E.Id,E.Name,E.StorageId,E.StartTime AS StartTime,
|
||||
CASE WHEN E.EndTime IS NULL THEN (SELECT NOW()) ELSE E.EndTime END AS EndTime,
|
||||
E.Length,
|
||||
CASE WHEN E.Frames IS NULL THEN (SELECT COUNT(*) FROM Frames F WHERE F.EventId=E.Id) ELSE E.Frames END AS Frames,
|
||||
E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId
|
||||
$eventsSql = 'SELECT
|
||||
E.Id,E.Name,E.StorageId,
|
||||
E.StartTime AS StartTime,UNIX_TIMESTAMP(E.StartTime) AS StartTimeSecs,
|
||||
CASE WHEN E.EndTime IS NULL THEN (SELECT NOW()) ELSE E.EndTime END AS EndTime,
|
||||
UNIX_TIMESTAMP(EndTime) AS EndTimeSecs,
|
||||
E.Length, E.Frames, E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId
|
||||
FROM Events AS E
|
||||
WHERE NOT isnull(E.Frames)
|
||||
WHERE 1 > 0
|
||||
';
|
||||
|
||||
// select E.Id,E.Name,UNIX_TIMESTAMP(E.StartTime) as StartTimeSecs,UNIX_TIMESTAMP(max(DATE_ADD(E.StartTime, Interval Delta+0.5 Second))) as CalcEndTimeSecs, E.Length,max(F.FrameId) as Frames,E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId
|
||||
|
@ -112,10 +112,9 @@ $eventsSql = '
|
|||
|
||||
// Note that the delta value seems more accurate than the time stamp for some reason.
|
||||
$frameSql = '
|
||||
SELECT E.Id AS eId, E.MonitorId, UNIX_TIMESTAMP(DATE_ADD(E.StartTime, Interval Delta Second)) AS TimeStampSecs, max(F.Score) AS Score
|
||||
FROM Events AS E
|
||||
INNER JOIN Frames AS F ON (F.EventId = E.Id)
|
||||
WHERE F.Score>0
|
||||
SELECT Id, FrameId, EventId, TimeStamp, UNIX_TIMESTAMP(TimeStamp) AS TimeStampSecs, Score, Delta
|
||||
FROM Frames
|
||||
WHERE EventId IN (SELECT E.Id FROM Events AS E WHERE 1>0
|
||||
';
|
||||
|
||||
// This program only calls itself with the time range involved -- it does all monitors (the user can see, in the called group) all the time
|
||||
|
@ -183,7 +182,7 @@ if ( isset($_REQUEST['current']) )
|
|||
|
||||
$liveMode = 1; // default to live
|
||||
if ( isset($_REQUEST['live']) && $_REQUEST['live']=='0' )
|
||||
$liveMode=0;
|
||||
$liveMode = 0;
|
||||
|
||||
$initialDisplayInterval = 1000;
|
||||
if ( isset($_REQUEST['displayinterval']) )
|
||||
|
@ -196,10 +195,14 @@ if ( isset($minTime) && isset($maxTime) ) {
|
|||
$maxTimeSecs = strtotime($maxTime);
|
||||
Logger::Debug("Min/max time secs: $minTimeSecs $maxTimeSecs");
|
||||
$eventsSql .= " AND EndTime > '" . $minTime . "' AND StartTime < '" . $maxTime . "'";
|
||||
$frameSql .= " AND TimeStamp > '" . $minTime . "' AND TimeStamp < '" . $maxTime . "'";
|
||||
$frameSql .= " AND EndTime > '" . $minTime . "' AND StartTime < '" . $maxTime . "'";
|
||||
$frameSql .= ") AND TimeStamp > '" . $minTime . "' AND TimeStamp < '" . $maxTime . "'";
|
||||
}
|
||||
$frameSql .= ' GROUP BY E.Id, E.MonitorId, F.TimeStamp, F.Delta ORDER BY E.MonitorId, F.TimeStamp ASC';
|
||||
#$frameSql .= ' GROUP BY E.Id, E.MonitorId, F.TimeStamp, F.Delta ORDER BY E.MonitorId, F.TimeStamp ASC';
|
||||
#$frameSql .= ' GROUP BY E.Id, E.MonitorId, F.TimeStamp, F.Delta ORDER BY E.MonitorId, F.TimeStamp ASC';
|
||||
$eventsSql .= ' ORDER BY E.Id ASC';
|
||||
// DESC is intentional. We process them in reverse order so that we can point each frame to the next one in time.
|
||||
$frameSql .= ' ORDER BY Id DESC';
|
||||
|
||||
$monitors = array();
|
||||
foreach( $displayMonitors as $row ) {
|
||||
|
|
|
@ -223,11 +223,17 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
|
|||
<tr>
|
||||
<td class="colName"><?php echo makePopupLink( '?view=server&id='.$row['Id'], 'zmServer', 'server', validHtmlStr($row['Name']), $canEdit ) ?></td>
|
||||
<td class="colHostname"><?php echo makePopupLink( '?view=server&id='.$row['Id'], 'zmServer', 'server', validHtmlStr($row['Hostname']), $canEdit ) ?></td>
|
||||
<td class="colStatus"><?php echo makePopupLink( '?view=server&id='.$row['Id'], 'zmServer', 'server', validHtmlStr($row['Status']), $canEdit ) ?></td>
|
||||
<td class="colStatus
|
||||
<?php if ( $row['Status'] == 'NotRunning' ) { echo 'danger'; } ?>
|
||||
"><?php echo makePopupLink( '?view=server&id='.$row['Id'], 'zmServer', 'server', validHtmlStr($row['Status']), $canEdit ) ?></td>
|
||||
<td class="colMonitorCount"><?php echo makePopupLink( '?view=server&id='.$row['Id'], 'zmServer', 'server', validHtmlStr($row['MonitorCount']), $canEdit ) ?></td>
|
||||
<td class="colCpuLoad"><?php echo makePopupLink( '?view=server&id='.$row['Id'], 'zmServer', 'server',$row['CpuLoad'], $canEdit ) ?></td>
|
||||
<td class="colMemory"><?php echo makePopupLink( '?view=server&id='.$row['Id'], 'zmServer', 'server', human_filesize($row['FreeMem']) . ' / ' . human_filesize($row['TotalMem']), $canEdit ) ?></td>
|
||||
<td class="colSwap"><?php echo makePopupLink( '?view=server&id='.$row['Id'], 'zmServer', 'server', human_filesize($row['FreeSwap']) . ' / ' . human_filesize($row['TotalSwap']) , $canEdit ) ?></td>
|
||||
<td class="colCpuLoad
|
||||
<?php if ( $row['CpuLoad'] > 5 ) { echo 'danger'; } ?>
|
||||
"><?php echo makePopupLink( '?view=server&id='.$row['Id'], 'zmServer', 'server',$row['CpuLoad'], $canEdit ) ?></td>
|
||||
<td class="colMemory
|
||||
<?php if ( $row['FreeMem']/$row['TotalMem'] < .1 ) { echo 'danger'; } ?>"><?php echo makePopupLink( '?view=server&id='.$row['Id'], 'zmServer', 'server', human_filesize($row['FreeMem']) . ' / ' . human_filesize($row['TotalMem']), $canEdit ) ?></td>
|
||||
<td class="colSwap
|
||||
<?php if ( $row['FreeSwap']/$row['TotalSwap'] < .1 ) { echo 'danger'; } ?>"><?php echo makePopupLink( '?view=server&id='.$row['Id'], 'zmServer', 'server', human_filesize($row['FreeSwap']) . ' / ' . human_filesize($row['TotalSwap']) , $canEdit ) ?></td>
|
||||
<td class="colStats"><?php echo makePopupLink( '?view=server&id='.$row['Id'], 'zmServer', 'server', $row['zmstats'] ? 'yes' : 'no', $canEdit ) ?></td>
|
||||
<td class="colAudit"><?php echo makePopupLink( '?view=server&id='.$row['Id'], 'zmServer', 'server', $row['zmaudit'] ? 'yes' : 'no', $canEdit ) ?></td>
|
||||
<td class="colTrigger"><?php echo makePopupLink( '?view=server&id='.$row['Id'], 'zmServer', 'server', $row['zmtrigger'] ? 'yes' : 'no', $canEdit ) ?></td>
|
||||
|
|
Loading…
Reference in New Issue