Merge branch 'storageareas' of github.com:connortechnology/ZoneMinder into storageareas
commit
50070a9a7c
|
@ -4,7 +4,7 @@ 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,
|
||||
HourEvents = GREATEST(COALESCE(HourEvents,1)-1,0),
|
||||
HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END;
|
||||
|
@ -62,7 +62,7 @@ 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,
|
||||
WeekEvents = GREATEST(COALESCE(WeekEvents,1)-1,0),
|
||||
WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END;
|
||||
|
@ -90,7 +90,7 @@ 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,
|
||||
MonthEvents = GREATEST(COALESCE(MonthEvents,1)-1,0),
|
||||
MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END;
|
||||
|
|
|
@ -789,7 +789,7 @@ INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0
|
|||
INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'PSIA','Remote','PSIA',0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,-100,100,0,0,1,0,0,0,0,1,-100,100,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Dahua','Remote','Dahua',0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Dahua','Ffmpeg','Dahua',0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'FOSCAMR2C','Libvlc','FOSCAMR2C',1,1,1,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,0,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,0,0);
|
||||
INSERT INTO `Controls` VALUES (NULL,'Amcrest HTTP API','Ffmpeg','Amcrest_HTTP',0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,5);
|
||||
|
||||
|
|
|
@ -0,0 +1,283 @@
|
|||
|
||||
delimiter //
|
||||
DROP TRIGGER IF EXISTS Events_Hour_delete_trigger//
|
||||
CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour
|
||||
FOR EACH ROW BEGIN
|
||||
UPDATE Monitors SET
|
||||
HourEvents = GREATEST(COALESCE(HourEvents,1)-1,0),
|
||||
HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END;
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS Events_Hour_update_trigger//
|
||||
|
||||
CREATE TRIGGER Events_Hour_update_trigger AFTER UPDATE ON Events_Hour
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( diff ) THEN
|
||||
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
|
||||
UPDATE Monitors SET HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId;
|
||||
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||
ELSE
|
||||
UPDATE Monitors SET HourEventDiskSpace=COALESCE(HourEventDiskSpace,0)+diff WHERE Monitors.Id=NEW.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
END;
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS Events_Day_delete_trigger//
|
||||
CREATE TRIGGER Events_Day_delete_trigger BEFORE DELETE ON Events_Day
|
||||
FOR EACH ROW BEGIN
|
||||
UPDATE Monitors SET
|
||||
DayEvents = GREATEST(COALESCE(DayEvents,1)-1,0),
|
||||
DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END;
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS Events_Day_update_trigger;
|
||||
CREATE TRIGGER Events_Day_update_trigger AFTER UPDATE ON Events_Day
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( diff ) THEN
|
||||
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
|
||||
UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId;
|
||||
UPDATE Monitors SET DayEventDiskSpace=COALESCE(DayEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||
ELSE
|
||||
UPDATE Monitors SET DayEventDiskSpace=GREATEST(COALESCE(DayEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
END;
|
||||
//
|
||||
|
||||
|
||||
DROP TRIGGER IF EXISTS Events_Week_delete_trigger//
|
||||
CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week
|
||||
FOR EACH ROW BEGIN
|
||||
UPDATE Monitors SET
|
||||
WeekEvents = GREATEST(COALESCE(WeekEvents,1)-1,0),
|
||||
WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END;
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS Events_Week_update_trigger;
|
||||
CREATE TRIGGER Events_Week_update_trigger AFTER UPDATE ON Events_Week
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( diff ) THEN
|
||||
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
|
||||
UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Monitors.Id=OLD.MonitorId;
|
||||
UPDATE Monitors SET WeekEventDiskSpace=COALESCE(WeekEventDiskSpace,0)+COALESCE(NEW.DiskSpace,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||
ELSE
|
||||
UPDATE Monitors SET WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
END;
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS Events_Month_delete_trigger//
|
||||
CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month
|
||||
FOR EACH ROW BEGIN
|
||||
UPDATE Monitors SET
|
||||
MonthEvents = GREATEST(COALESCE(MonthEvents,1)-1,0),
|
||||
MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END;
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS Events_Month_update_trigger;
|
||||
CREATE TRIGGER Events_Month_update_trigger AFTER UPDATE ON Events_Month
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( diff ) THEN
|
||||
IF ( NEW.MonitorID != OLD.MonitorID ) THEN
|
||||
UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace),0) WHERE Monitors.Id=OLD.MonitorId;
|
||||
UPDATE Monitors SET MonthEventDiskSpace=COALESCE(MonthEventDiskSpace,0)+COALESCE(NEW.DiskSpace) WHERE Monitors.Id=NEW.MonitorId;
|
||||
ELSE
|
||||
UPDATE Monitors SET MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)+diff,0) WHERE Monitors.Id=NEW.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
END;
|
||||
//
|
||||
|
||||
drop procedure if exists update_storage_stats//
|
||||
|
||||
drop trigger if exists event_update_trigger//
|
||||
|
||||
CREATE TRIGGER event_update_trigger AFTER UPDATE ON Events
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
declare diff BIGINT default 0;
|
||||
|
||||
set diff = COALESCE(NEW.DiskSpace,0) - COALESCE(OLD.DiskSpace,0);
|
||||
IF ( NEW.StorageId = OLD.StorageID ) THEN
|
||||
IF ( diff ) THEN
|
||||
UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) + diff,0) WHERE Id = OLD.StorageId;
|
||||
END IF;
|
||||
ELSE
|
||||
IF ( NEW.DiskSpace ) THEN
|
||||
UPDATE Storage SET DiskSpace = COALESCE(DiskSpace,0) + NEW.DiskSpace WHERE Id = NEW.StorageId;
|
||||
END IF;
|
||||
IF ( OLD.DiskSpace ) THEN
|
||||
UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - OLD.DiskSpace,0) WHERE Id = OLD.StorageId;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
UPDATE Events_Hour SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
UPDATE Events_Day SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
UPDATE Events_Week SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
UPDATE Events_Month SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
|
||||
IF ( NEW.Archived != OLD.Archived ) THEN
|
||||
IF ( NEW.Archived ) THEN
|
||||
INSERT INTO Events_Archived (EventId,MonitorId,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.DiskSpace);
|
||||
UPDATE Monitors SET ArchivedEvents = COALESCE(ArchivedEvents,0)+1, ArchivedEventDiskSpace = COALESCE(ArchivedEventDiskSpace,0) + COALESCE(NEW.DiskSpace,0) WHERE Id=NEW.MonitorId;
|
||||
ELSEIF ( OLD.Archived ) THEN
|
||||
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
|
||||
UPDATE Monitors
|
||||
SET
|
||||
ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,0)-1,0),
|
||||
ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
ELSE
|
||||
IF ( OLD.DiskSpace != NEW.DiskSpace ) THEN
|
||||
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
UPDATE Monitors SET
|
||||
ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END IF;
|
||||
END IF;
|
||||
ELSEIF ( NEW.Archived AND diff ) THEN
|
||||
UPDATE Events_Archived SET DiskSpace=NEW.DiskSpace WHERE EventId=NEW.Id;
|
||||
END IF;
|
||||
|
||||
IF ( diff ) THEN
|
||||
UPDATE Monitors
|
||||
SET
|
||||
TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0) + COALESCE(NEW.DiskSpace,0),0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END IF;
|
||||
|
||||
END;
|
||||
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS event_insert_trigger//
|
||||
|
||||
/* The assumption is that when an Event is inserted, it has no size yet, so don't bother updating the DiskSpace, just the count.
|
||||
* The DiskSpace will get update in the Event Update Trigger
|
||||
*/
|
||||
CREATE TRIGGER event_insert_trigger AFTER INSERT ON Events
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
|
||||
INSERT INTO Events_Hour (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
|
||||
INSERT INTO Events_Day (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
|
||||
INSERT INTO Events_Week (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
|
||||
INSERT INTO Events_Month (EventId,MonitorId,StartTime,DiskSpace) VALUES (NEW.Id,NEW.MonitorId,NEW.StartTime,0);
|
||||
UPDATE Monitors SET
|
||||
HourEvents = COALESCE(HourEvents,0)+1,
|
||||
DayEvents = COALESCE(DayEvents,0)+1,
|
||||
WeekEvents = COALESCE(WeekEvents,0)+1,
|
||||
MonthEvents = COALESCE(MonthEvents,0)+1,
|
||||
TotalEvents = COALESCE(TotalEvents,0)+1
|
||||
WHERE Id=NEW.MonitorId;
|
||||
END;
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS event_delete_trigger//
|
||||
|
||||
CREATE TRIGGER event_delete_trigger BEFORE DELETE ON Events
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF ( OLD.DiskSpace ) THEN
|
||||
UPDATE Storage SET DiskSpace = GREATEST(COALESCE(DiskSpace,0) - COALESCE(OLD.DiskSpace,0),0) WHERE Id = OLD.StorageId;
|
||||
END IF;
|
||||
DELETE FROM Events_Hour WHERE EventId=OLD.Id;
|
||||
DELETE FROM Events_Day WHERE EventId=OLD.Id;
|
||||
DELETE FROM Events_Week WHERE EventId=OLD.Id;
|
||||
DELETE FROM Events_Month WHERE EventId=OLD.Id;
|
||||
IF ( OLD.Archived ) THEN
|
||||
DELETE FROM Events_Archived WHERE EventId=OLD.Id;
|
||||
UPDATE Monitors SET
|
||||
ArchivedEvents = GREATEST(COALESCE(ArchivedEvents,1) - 1,0),
|
||||
ArchivedEventDiskSpace = GREATEST(COALESCE(ArchivedEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0),
|
||||
TotalEvents = GREATEST(COALESCE(TotalEvents,1) - 1,0),
|
||||
TotalEventDiskSpace = GREATEST(COALESCE(TotalEventDiskSpace,0) - COALESCE(OLD.DiskSpace,0),0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
ELSE
|
||||
UPDATE Monitors SET
|
||||
TotalEvents = GREATEST(COALESCE(TotalEvents,1)-1,0),
|
||||
TotalEventDiskSpace=GREATEST(COALESCE(TotalEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
|
||||
WHERE Id=OLD.MonitorId;
|
||||
END IF;
|
||||
END;
|
||||
|
||||
//
|
||||
|
||||
DROP TRIGGER IF EXISTS Zone_Insert_Trigger//
|
||||
CREATE TRIGGER Zone_Insert_Trigger AFTER INSERT ON Zones
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=NEW.MonitorId) WHERE Id=NEW.MonitorID;
|
||||
END
|
||||
//
|
||||
DROP TRIGGER IF EXISTS Zone_Delete_Trigger//
|
||||
CREATE TRIGGER Zone_Delete_Trigger AFTER DELETE ON Zones
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE Monitors SET ZoneCount=(SELECT COUNT(*) FROM Zones WHERE MonitorId=OLD.MonitorId) WHERE Id=OLD.MonitorID;
|
||||
END
|
||||
//
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
REPLACE INTO Events_Hour SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 hour);
|
||||
REPLACE INTO Events_Day SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 day);
|
||||
REPLACE INTO Events_Week SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 week);
|
||||
REPLACE INTO Events_Month SELECT Id,MonitorId,StartTime,DiskSpace FROM Events WHERE StartTime > DATE_SUB(NOW(), INTERVAL 1 month);
|
||||
REPLACE INTO Events_Archived SELECT Id,MonitorId,DiskSpace FROM Events WHERE Archived=1;
|
||||
|
||||
UPDATE Monitors INNER JOIN (
|
||||
SELECT MonitorId,
|
||||
COUNT(Id) AS TotalEvents,
|
||||
SUM(DiskSpace) AS TotalEventDiskSpace,
|
||||
SUM(IF(Archived,1,0)) AS ArchivedEvents,
|
||||
SUM(IF(Archived,DiskSpace,0)) AS ArchivedEventDiskSpace,
|
||||
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),1,0)) AS HourEvents,
|
||||
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 hour),DiskSpace,0)) AS HourEventDiskSpace,
|
||||
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),1,0)) AS DayEvents,
|
||||
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 day),DiskSpace,0)) AS DayEventDiskSpace,
|
||||
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),1,0)) AS WeekEvents,
|
||||
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 week),DiskSpace,0)) AS WeekEventDiskSpace,
|
||||
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),1,0)) AS MonthEvents,
|
||||
SUM(IF(StartTime > DATE_SUB(NOW(), INTERVAL 1 month),DiskSpace,0)) AS MonthEventDiskSpace
|
||||
FROM Events GROUP BY MonitorId
|
||||
) AS E ON E.MonitorId=Monitors.Id SET
|
||||
Monitors.TotalEvents = E.TotalEvents,
|
||||
Monitors.TotalEventDiskSpace = E.TotalEventDiskSpace,
|
||||
Monitors.ArchivedEvents = E.ArchivedEvents,
|
||||
Monitors.ArchivedEventDiskSpace = E.ArchivedEventDiskSpace,
|
||||
Monitors.HourEvents = E.HourEvents,
|
||||
Monitors.HourEventDiskSpace = E.HourEventDiskSpace,
|
||||
Monitors.DayEvents = E.DayEvents,
|
||||
Monitors.DayEventDiskSpace = E.DayEventDiskSpace,
|
||||
Monitors.WeekEvents = E.WeekEvents,
|
||||
Monitors.WeekEventDiskSpace = E.WeekEventDiskSpace,
|
||||
Monitors.MonthEvents = E.MonthEvents,
|
||||
Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace;
|
||||
|
|
@ -13,7 +13,7 @@ Type=forking
|
|||
ExecStart=/usr/bin/zmpkg.pl start
|
||||
ExecReload=/usr/bin/zmpkg.pl restart
|
||||
ExecStop=/usr/bin/zmpkg.pl stop
|
||||
PIDFile=/var/run/zm/zm.pid
|
||||
PIDFile=/run/zm/zm.pid
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment=TZ=:/etc/localtime
|
||||
|
|
|
@ -65,7 +65,7 @@ Because ZoneMinder's package repository provides a secure connection through HTT
|
|||
Finally, download the GPG key for ZoneMinder's repository:
|
||||
::
|
||||
|
||||
sudo wget -O - https://zmrepo.zoneminder.com/debian/archive-keyring.gpg | sudo apt-key add -
|
||||
wget -O - https://zmrepo.zoneminder.com/debian/archive-keyring.gpg | sudo apt-key add -
|
||||
|
||||
|
||||
**Step 5:** Install ZoneMinder
|
||||
|
|
|
@ -11,4 +11,4 @@ A fast video interface core, a user-friendly and comprehensive PHP based web int
|
|||
|
||||
The core of ZoneMinder is the capture and analysis of images and a highly configurable set of parameters that eliminate false positives whilst ensuring minimum loss of footage. For example, you can define a set of 'zones' for each camera of varying sensitivity and functionality. This eliminates zones that you don't wish to track or define areas that will alarm if various thresholds are exceeded in conjunction with other zones.
|
||||
|
||||
ZoneMinder is free under GPL License, but if you do find it useful, then please feel free to visit http://www.zoneminder.com/donate.html and help us fund our future improvements.
|
||||
ZoneMinder is free under GPL License, but if you do find it useful, then please feel free to visit https://zoneminder.com/donate/ and help us fund our future improvements.
|
||||
|
|
|
@ -381,6 +381,22 @@ sub focusRelFar
|
|||
Debug("focusRelFar response: " . $response);
|
||||
}
|
||||
|
||||
sub irisRelOpen
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
my $response = $self->_sendPtzCommand("start", "IrisLarge", 0, 1, 0, 0);
|
||||
Debug("irisRelOpen response: " . $response);
|
||||
}
|
||||
|
||||
sub irisRelClose
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
my $response = $self->_sendPtzCommand("start", "IrisSmall", 0, 1, 0, 0);
|
||||
Debug("irisRelClose response: " . $response);
|
||||
}
|
||||
|
||||
sub moveStop
|
||||
{
|
||||
my $self = shift;
|
||||
|
@ -592,6 +608,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|||
We pass in a 1 for that as it does not seem to matter what number (0-8) is
|
||||
provided, the camera focus behaves the same.
|
||||
|
||||
=head2 irisRel<Large/Small>
|
||||
|
||||
This set of methods invoke realtive iris size in the direction indicated by
|
||||
the <Large/Small> portion of their name. They accept no arguments.
|
||||
|
||||
NOTE:
|
||||
|
||||
This only just does work. The Dahua API specifies "multiples" as the input.
|
||||
We pass in a 1 for that as it does not seem to matter what number (0-8) is
|
||||
provided, the camera iris behaves the same.
|
||||
|
||||
=head2 moveStop
|
||||
|
||||
This method attempts to stop the camera. The problem is that if continuous
|
||||
|
|
|
@ -28,6 +28,9 @@ package ZoneMinder::Control::Netcat;
|
|||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
use MIME::Base64;
|
||||
use Digest::SHA;
|
||||
use DateTime;
|
||||
|
||||
require ZoneMinder::Base;
|
||||
require ZoneMinder::Control;
|
||||
|
@ -36,6 +39,8 @@ our @ISA = qw(ZoneMinder::Control);
|
|||
|
||||
our %CamParams = ();
|
||||
|
||||
our ($profileToken, $address, $port, %identity);
|
||||
|
||||
# ==========================================================================
|
||||
#
|
||||
# Netcat IP Control Protocol
|
||||
|
@ -50,7 +55,6 @@ our %CamParams = ();
|
|||
#
|
||||
#
|
||||
# Possible future improvements (for anyone to improve upon):
|
||||
# - Onvif authentication
|
||||
# - Build the SOAP commands at runtime rather than use templates
|
||||
# - Implement previously mentioned advanced features
|
||||
#
|
||||
|
@ -58,9 +62,10 @@ our %CamParams = ();
|
|||
# more dependencies to ZoneMinder is always a concern.
|
||||
#
|
||||
# On ControlAddress use the format :
|
||||
# ADDRESS:PORT
|
||||
# [USERNAME:PASSWORD@]ADDRESS:PORT
|
||||
# eg : 10.1.2.1:8899
|
||||
# 10.0.100.1:8899
|
||||
# username:password@10.0.100.1:8899
|
||||
#
|
||||
# Use port 8899 for the Netcat camera
|
||||
#
|
||||
|
@ -79,6 +84,11 @@ sub open {
|
|||
|
||||
$self->loadMonitor();
|
||||
|
||||
$profileToken = $self->{Monitor}->{ControlDevice};
|
||||
if ($profileToken eq '') { $profileToken = '000'; }
|
||||
|
||||
parseControlAddress($self->{Monitor}->{ControlAddress});
|
||||
|
||||
use LWP::UserAgent;
|
||||
$self->{ua} = LWP::UserAgent->new;
|
||||
$self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
|
||||
|
@ -86,6 +96,39 @@ sub open {
|
|||
$self->{state} = 'open';
|
||||
}
|
||||
|
||||
sub parseControlAddress
|
||||
{
|
||||
my $controlAddress = shift;
|
||||
my ($usernamepassword, $addressport) = split /@/, $controlAddress;
|
||||
if ( !defined $addressport ) {
|
||||
# If value of "Control address" does not consist of two parts, then only address is given
|
||||
$addressport = $usernamepassword;
|
||||
} else {
|
||||
my ($username , $password) = split /:/, $usernamepassword;
|
||||
%identity = (username => "$username", password => "$password");
|
||||
}
|
||||
($address, $port) = split /:/, $addressport;
|
||||
}
|
||||
|
||||
sub digestBase64
|
||||
{
|
||||
my ($nonce, $date, $password) = @_;
|
||||
my $shaGenerator = Digest::SHA->new(1);
|
||||
$shaGenerator->add($nonce . $date . $password);
|
||||
return encode_base64($shaGenerator->digest, "");
|
||||
}
|
||||
|
||||
sub authentificationHeader
|
||||
{
|
||||
my ($username, $password) = @_;
|
||||
my $nonce;
|
||||
$nonce .= chr(int(rand(254))) for (0 .. 20);
|
||||
my $nonceBase64 = encode_base64($nonce, "");
|
||||
my $currentDate = DateTime->now()->iso8601().'Z';
|
||||
|
||||
return '<s:Header><Security s:mustUnderstand="1" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><UsernameToken xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><Username>' . $username . '</Username><Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">' . digestBase64($nonce, $currentDate, $password) . '</Password><Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">' . $nonceBase64 . '</Nonce><Created xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">' . $currentDate . '</Created></UsernameToken></Security></s:Header>';
|
||||
}
|
||||
|
||||
sub printMsg {
|
||||
my $self = shift;
|
||||
my $msg = shift;
|
||||
|
@ -103,10 +146,10 @@ sub sendCmd {
|
|||
|
||||
printMsg($cmd, 'Tx');
|
||||
|
||||
my $server_endpoint = 'http://'.$self->{Monitor}->{ControlAddress}.'/'.$cmd;
|
||||
my $server_endpoint = 'http://' . $address . ':' . $port . "/$cmd";
|
||||
my $req = HTTP::Request->new(POST => $server_endpoint);
|
||||
$req->header('content-type' => $content_type);
|
||||
$req->header('Host' => $self->{Monitor}->{ControlAddress});
|
||||
$req->header('Host' => $address . ':' . $port);
|
||||
$req->header('content-length' => length($msg));
|
||||
$req->header('accept-encoding' => 'gzip, deflate');
|
||||
$req->header('connection' => 'Close');
|
||||
|
@ -125,10 +168,10 @@ sub sendCmd {
|
|||
sub getCamParams {
|
||||
my $self = shift;
|
||||
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $server_endpoint = 'http://' . $address . ':' . $port . '/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' => $self->{Monitor}->{ControlAddress});
|
||||
$req->header('Host' => $address . ':' . $port);
|
||||
$req->header('content-length' => length($msg));
|
||||
$req->header('accept-encoding' => 'gzip, deflate');
|
||||
$req->header('connection' => 'Close');
|
||||
|
@ -160,7 +203,7 @@ sub autoStop {
|
|||
if ( $autostop ) {
|
||||
Debug('Auto Stop');
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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>' . $profileToken . '</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);
|
||||
|
@ -172,7 +215,7 @@ sub reset {
|
|||
Debug('Camera Reset');
|
||||
my $self = shift;
|
||||
my $cmd = '';
|
||||
my $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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);
|
||||
}
|
||||
|
@ -182,7 +225,7 @@ sub moveConUp {
|
|||
Debug('Move Up');
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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>' . $profileToken . '</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});
|
||||
|
@ -193,7 +236,7 @@ sub moveConDown {
|
|||
Debug('Move Down');
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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>' . $profileToken . '</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});
|
||||
|
@ -204,7 +247,7 @@ sub moveConLeft {
|
|||
Debug('Move Left');
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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>' . $profileToken . '</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});
|
||||
|
@ -215,7 +258,7 @@ sub moveConRight {
|
|||
Debug('Move Right');
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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>' . $profileToken . '</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});
|
||||
|
@ -226,7 +269,7 @@ sub zoomConTele {
|
|||
Debug('Zoom Tele');
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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>' . $profileToken . '</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});
|
||||
|
@ -237,7 +280,7 @@ sub zoomConWide {
|
|||
Debug('Zoom Wide');
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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>' . $profileToken . '</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});
|
||||
|
@ -249,7 +292,7 @@ sub moveConUpRight {
|
|||
Debug('Move Diagonally Up Right');
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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>' . $profileToken . '</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});
|
||||
|
@ -261,7 +304,7 @@ sub moveConDownRight {
|
|||
Debug('Move Diagonally Down Right');
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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>' . $profileToken . '</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});
|
||||
|
@ -273,7 +316,7 @@ sub moveConUpLeft {
|
|||
Debug('Move Diagonally Up Left');
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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>' . $profileToken . '</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});
|
||||
|
@ -285,7 +328,7 @@ sub moveConDownLeft {
|
|||
Debug('Move Diagonally Down Left');
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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>' . $profileToken . '</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});
|
||||
|
@ -296,7 +339,7 @@ sub moveStop {
|
|||
Debug('Move Stop');
|
||||
my $self = shift;
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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>' . $profileToken . '</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);
|
||||
}
|
||||
|
@ -308,7 +351,7 @@ sub presetSet {
|
|||
my $preset = $self->getParam($params, 'preset');
|
||||
Debug("Set Preset $preset");
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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>' . $profileToken . '</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);
|
||||
}
|
||||
|
@ -320,7 +363,7 @@ sub presetGoto {
|
|||
my $preset = $self->getParam($params, 'preset');
|
||||
Debug("Goto Preset $preset");
|
||||
my $cmd = 'onvif/PTZ';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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>'.$preset.'</PresetToken></GotoPreset></s:Body></s:Envelope>';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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>' . $profileToken . '</ProfileToken><PresetToken>'.$preset.'</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 );
|
||||
}
|
||||
|
@ -362,7 +405,7 @@ sub irisAbsOpen {
|
|||
$CamParams{Brightness} = $max if ($CamParams{Brightness} > $max);
|
||||
|
||||
my $cmd = 'onvif/imaging';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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 );
|
||||
}
|
||||
|
@ -381,7 +424,7 @@ sub irisAbsClose
|
|||
$CamParams{Brightness} = $min if ($CamParams{Brightness} < $min);
|
||||
|
||||
my $cmd = 'onvif/imaging';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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 );
|
||||
}
|
||||
|
@ -399,7 +442,7 @@ sub whiteAbsIn {
|
|||
$CamParams{Contrast} = $max if ($CamParams{Contrast} > $max);
|
||||
|
||||
my $cmd = 'onvif/imaging';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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"';
|
||||
}
|
||||
|
||||
|
@ -416,7 +459,7 @@ sub whiteAbsOut {
|
|||
$CamParams{Contrast} = $min if ($CamParams{Contrast} < $min);
|
||||
|
||||
my $cmd = 'onvif/imaging';
|
||||
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"><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 $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<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"';
|
||||
}
|
||||
|
||||
|
|
|
@ -571,3 +571,27 @@ void dumpPacket(AVStream *stream, AVPacket *pkt, const char *text) {
|
|||
pkt->duration);
|
||||
Debug(2, "%s:%d:%s: %s", __FILE__, __LINE__, text, b);
|
||||
}
|
||||
|
||||
void dumpPacket(AVPacket *pkt, const char *text) {
|
||||
char b[10240];
|
||||
|
||||
snprintf(b, sizeof(b),
|
||||
" pts: %" PRId64 ", dts: %" PRId64
|
||||
", size: %d, stream_index: %d, flags: %04x, keyframe(%d) pos: %" PRId64
|
||||
", duration: %"
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
PRId64
|
||||
#else
|
||||
"d"
|
||||
#endif
|
||||
"\n",
|
||||
pkt->pts,
|
||||
pkt->dts,
|
||||
pkt->size,
|
||||
pkt->stream_index,
|
||||
pkt->flags,
|
||||
pkt->flags & AV_PKT_FLAG_KEY,
|
||||
pkt->pos,
|
||||
pkt->duration);
|
||||
Debug(2, "%s:%d:%s: %s", __FILE__, __LINE__, text, b);
|
||||
}
|
||||
|
|
|
@ -333,4 +333,5 @@ bool is_audio_context(AVCodec *);
|
|||
|
||||
int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet );
|
||||
void dumpPacket(AVStream *, AVPacket *,const char *text="");
|
||||
void dumpPacket(AVPacket *,const char *text="");
|
||||
#endif // ZM_FFMPEG_H
|
||||
|
|
|
@ -947,9 +947,15 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
|
|||
if ( ret < 0 ) {
|
||||
av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||
Warning("Unable to receive frame %d: %s, continuing", frameCount, errbuf);
|
||||
error_count += 1;
|
||||
if ( error_count > 100 ) {
|
||||
Error("Error count over 100, going to close and re-open stream");
|
||||
return -1;
|
||||
}
|
||||
zm_av_packet_unref(&packet);
|
||||
continue;
|
||||
}
|
||||
if ( error_count > 0 ) error_count --;
|
||||
|
||||
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||
}
|
||||
|
|
|
@ -1548,9 +1548,19 @@ bool Monitor::Analyse() {
|
|||
if ( score ) {
|
||||
if ( state == IDLE || state == TAPE || state == PREALARM ) {
|
||||
if ( (!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count) ) {
|
||||
Info("%s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u",
|
||||
name, image_count, Event::PreAlarmCount(), alarm_frame_count);
|
||||
shared_data->state = state = ALARM;
|
||||
// lets construct alarm cause. It will contain cause + names of zones alarmed
|
||||
std::string alarm_cause="";
|
||||
for ( int i=0; i < n_zones; i++) {
|
||||
if (zones[i]->Alarmed()) {
|
||||
alarm_cause = alarm_cause+ ","+ std::string(zones[i]->Label());
|
||||
}
|
||||
}
|
||||
if (!alarm_cause.empty()) alarm_cause[0]=' ';
|
||||
alarm_cause = cause+alarm_cause;
|
||||
strncpy(shared_data->alarm_cause,alarm_cause.c_str(), sizeof(shared_data->alarm_cause)-1);
|
||||
Info("%s: %03d - Gone into alarm state PreAlarmCount: %u > AlarmFrameCount:%u Cause:%s",
|
||||
name, image_count, Event::PreAlarmCount(), alarm_frame_count, shared_data->alarm_cause);
|
||||
if ( signal_change || (function != MOCORD && state != ALERT) ) {
|
||||
int pre_index;
|
||||
int pre_event_images = pre_event_count;
|
||||
|
@ -1596,18 +1606,6 @@ bool Monitor::Analyse() {
|
|||
} // end if analysis_fps && pre_event_count
|
||||
|
||||
shared_data->last_event = event->Id();
|
||||
// lets construct alarm cause. It will contain cause + names of zones alarmed
|
||||
std::string alarm_cause = "";
|
||||
for ( int i=0; i < n_zones; i++ ) {
|
||||
if ( zones[i]->Alarmed() ) {
|
||||
alarm_cause += std::string(zones[i]->Label());
|
||||
if ( i < n_zones-1 ) {
|
||||
alarm_cause += ",";
|
||||
}
|
||||
}
|
||||
}
|
||||
alarm_cause = cause+" "+alarm_cause;
|
||||
strncpy(shared_data->alarm_cause,alarm_cause.c_str(), sizeof(shared_data->alarm_cause)-1);
|
||||
//set up video store data
|
||||
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
|
||||
video_store_data->recording = event->StartTime();
|
||||
|
@ -1729,6 +1727,8 @@ bool Monitor::Analyse() {
|
|||
timestamp->tv_sec - video_store_data->recording.tv_sec,
|
||||
section_length
|
||||
);
|
||||
closeEvent();
|
||||
event = new Event(this, *timestamp, cause, noteSetMap);
|
||||
}
|
||||
} // end if event
|
||||
|
||||
|
|
|
@ -394,10 +394,15 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) {
|
|||
img_buffer_size = send_image->Size();
|
||||
break;
|
||||
case STREAM_ZIP :
|
||||
#if HAVE_ZLIB_H
|
||||
fputs("Content-Type: image/x-rgbz\r\n",stdout);
|
||||
unsigned long zip_buffer_size;
|
||||
send_image->Zip(img_buffer, &zip_buffer_size);
|
||||
img_buffer_size = zip_buffer_size;
|
||||
#else
|
||||
Error("zlib is required for zipped images. Falling back to raw image");
|
||||
type = STREAM_RAW;
|
||||
#endif // HAVE_ZLIB_H
|
||||
break;
|
||||
default :
|
||||
Error("Unexpected frame type %d", type);
|
||||
|
@ -794,6 +799,8 @@ void MonitorStream::SingleImageRaw( int scale ) {
|
|||
fwrite( snap_image->Buffer(), snap_image->Size(), 1, stdout );
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_ZLIB_H
|
||||
void MonitorStream::SingleImageZip( int scale ) {
|
||||
unsigned long img_buffer_size = 0;
|
||||
static Bytef img_buffer[ZM_MAX_IMAGE_SIZE];
|
||||
|
@ -816,3 +823,4 @@ void MonitorStream::SingleImageZip( int scale ) {
|
|||
fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" );
|
||||
fwrite( img_buffer, img_buffer_size, 1, stdout );
|
||||
}
|
||||
#endif // HAVE_ZLIB_H
|
||||
|
|
|
@ -55,7 +55,9 @@ class MonitorStream : public StreamBase {
|
|||
void processCommand( const CmdMsg *msg );
|
||||
void SingleImage( int scale=100 );
|
||||
void SingleImageRaw( int scale=100 );
|
||||
#ifdef HAVE_ZLIB_H
|
||||
void SingleImageZip( int scale=100 );
|
||||
#endif
|
||||
|
||||
public:
|
||||
MonitorStream() :
|
||||
|
|
|
@ -35,20 +35,76 @@ zm_packetqueue::~zm_packetqueue() {
|
|||
}
|
||||
|
||||
bool zm_packetqueue::queuePacket(ZMPacket* zm_packet) {
|
||||
|
||||
if (
|
||||
( zm_packet->packet.dts == AV_NOPTS_VALUE )
|
||||
||
|
||||
( packet_counts[zm_packet->packet.stream_index] <= 0 )
|
||||
) {
|
||||
Debug(2,"Inserting packet with dts %" PRId64 " because queue is empty or invalid dts", zm_packet->packet.dts);
|
||||
// No dts value, can't so much with it
|
||||
pktQueue.push_back(zm_packet);
|
||||
packet_counts[zm_packet->packet.stream_index] += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool zm_packetqueue::queuePacket(AVPacket* av_packet) {
|
||||
std::list<ZMPacket *>::reverse_iterator it = pktQueue.rbegin();
|
||||
|
||||
ZMPacket *zm_packet = new ZMPacket(av_packet);
|
||||
// Scan through the queue looking for a packet for our stream with a dts <= ours.
|
||||
while ( it != pktQueue.rend() ) {
|
||||
AVPacket *av_packet = &((*it)->packet);
|
||||
|
||||
Debug(2, "Looking at packet with stream index (%d) with dts %" PRId64,
|
||||
av_packet->stream_index, av_packet->dts);
|
||||
if (
|
||||
( av_packet->stream_index == zm_packet->packet.stream_index )
|
||||
&&
|
||||
( av_packet->dts != AV_NOPTS_VALUE )
|
||||
&&
|
||||
( av_packet->dts <= zm_packet->packet.dts)
|
||||
) {
|
||||
Debug(2, "break packet with stream index (%d) with dts %" PRId64,
|
||||
(*it)->packet.stream_index, (*it)->packet.dts);
|
||||
break;
|
||||
}
|
||||
it++;
|
||||
} // end while not the end of the queue
|
||||
|
||||
if ( it != pktQueue.rend() ) {
|
||||
Debug(2, "Found packet with stream index (%d) with dts %" PRId64,
|
||||
(*it)->packet.stream_index, (*it)->packet.dts);
|
||||
//it --;
|
||||
//Debug(2, "Found packet with stream index (%d) with dts %" PRId64,
|
||||
//(*it)->packet.stream_index, (*it)->packet.dts);
|
||||
if ( it == pktQueue.rbegin() ) {
|
||||
Debug(2,"Inserting packet with dts %" PRId64 " at end", zm_packet->packet.dts);
|
||||
// No dts value, can't so much with it
|
||||
pktQueue.push_back(zm_packet);
|
||||
packet_counts[zm_packet->packet.stream_index] += 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
// Convert to a forward iterator so that we can insert at end
|
||||
std::list<ZMPacket *>::iterator f_it = it.base();
|
||||
|
||||
Debug(2, "Insert packet with stream index (%d) with dts %" PRId64 " for dts %" PRId64,
|
||||
(*f_it)->packet.stream_index, (*f_it)->packet.dts, zm_packet->packet.dts);
|
||||
|
||||
pktQueue.insert(f_it, zm_packet);
|
||||
|
||||
packet_counts[zm_packet->packet.stream_index] += 1;
|
||||
return true;
|
||||
}
|
||||
Debug(1,"Unable to insert packet for stream %d with dts %" PRId64 " into queue.",
|
||||
zm_packet->packet.stream_index, zm_packet->packet.dts);
|
||||
pktQueue.push_back(zm_packet);
|
||||
packet_counts[zm_packet->packet.stream_index] += 1;
|
||||
return true;
|
||||
} // end bool zm_packetqueue::queuePacket(ZMPacket* zm_packet)
|
||||
|
||||
bool zm_packetqueue::queuePacket(AVPacket* av_packet) {
|
||||
ZMPacket *zm_packet = new ZMPacket(av_packet);
|
||||
return queuePacket(zm_packet);
|
||||
}
|
||||
|
||||
ZMPacket* zm_packetqueue::popPacket( ) {
|
||||
if ( pktQueue.empty() ) {
|
||||
|
|
|
@ -28,7 +28,22 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
RemoteCameraRtsp::RemoteCameraRtsp( unsigned int p_monitor_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
|
||||
RemoteCameraRtsp::RemoteCameraRtsp(
|
||||
unsigned int p_monitor_id,
|
||||
const std::string &p_method,
|
||||
const std::string &p_host,
|
||||
const std::string &p_port,
|
||||
const std::string &p_path,
|
||||
int p_width,
|
||||
int p_height,
|
||||
bool p_rtsp_describe,
|
||||
int p_colours,
|
||||
int p_brightness,
|
||||
int p_contrast,
|
||||
int p_hue,
|
||||
int p_colour,
|
||||
bool p_capture,
|
||||
bool p_record_audio ) :
|
||||
RemoteCamera( p_monitor_id, "rtsp", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ),
|
||||
rtsp_describe( p_rtsp_describe ),
|
||||
rtspThread( 0 )
|
||||
|
@ -169,7 +184,7 @@ int RemoteCameraRtsp::PrimeCapture() {
|
|||
} else {
|
||||
Debug(2, "Have another video stream." );
|
||||
}
|
||||
}
|
||||
} else
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO )
|
||||
#else
|
||||
|
@ -181,6 +196,8 @@ int RemoteCameraRtsp::PrimeCapture() {
|
|||
} else {
|
||||
Debug(2, "Have another audio stream." );
|
||||
}
|
||||
} else {
|
||||
Debug(1, "Have unknown codec type in stream %d : %d", i, mFormatContext->streams[i]->codec->codec_type);
|
||||
}
|
||||
} // end foreach stream
|
||||
|
||||
|
|
|
@ -34,8 +34,7 @@
|
|||
// accessed over a network connection using rtsp protocol
|
||||
// (Real Time Streaming Protocol)
|
||||
//
|
||||
class RemoteCameraRtsp : public RemoteCamera
|
||||
{
|
||||
class RemoteCameraRtsp : public RemoteCamera {
|
||||
protected:
|
||||
struct sockaddr_in rtsp_sa;
|
||||
struct sockaddr_in rtcp_sa;
|
||||
|
|
|
@ -28,12 +28,12 @@
|
|||
|
||||
#include <errno.h>
|
||||
|
||||
RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ) : mRtspThread( rtspThread ), mRtpSource( rtpSource ), mStop( false )
|
||||
RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource )
|
||||
: mRtspThread( rtspThread ), mRtpSource( rtpSource ), mStop( false )
|
||||
{
|
||||
}
|
||||
|
||||
int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
|
||||
{
|
||||
int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) {
|
||||
const RtcpPacket *rtcpPacket;
|
||||
rtcpPacket = (RtcpPacket *)packet;
|
||||
|
||||
|
@ -48,33 +48,24 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
|
|||
int pt = rtcpPacket->header.pt;
|
||||
int len = ntohs(rtcpPacket->header.lenN);
|
||||
|
||||
Debug( 5, "RTCP Ver: %d", ver );
|
||||
Debug( 5, "RTCP Count: %d", count );
|
||||
Debug( 5, "RTCP Pt: %d", pt );
|
||||
Debug( 5, "RTCP len: %d", len );
|
||||
Debug( 5, "RTCP Ver: %d Count: %d Pt: %d len: %d", ver, count, pt, len);
|
||||
|
||||
switch( pt )
|
||||
{
|
||||
switch( pt ) {
|
||||
case RTCP_SR :
|
||||
{
|
||||
uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN);
|
||||
|
||||
Debug( 5, "RTCP Got SR (%x)", ssrc );
|
||||
if ( mRtpSource.getSsrc() )
|
||||
{
|
||||
if ( ssrc != mRtpSource.getSsrc() )
|
||||
{
|
||||
if ( mRtpSource.getSsrc() ) {
|
||||
if ( ssrc != mRtpSource.getSsrc() ) {
|
||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
else if ( ssrc )
|
||||
{
|
||||
} else if ( ssrc ) {
|
||||
mRtpSource.setSsrc( ssrc );
|
||||
}
|
||||
|
||||
if ( len > 1 )
|
||||
{
|
||||
if ( len > 1 ) {
|
||||
//printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts );
|
||||
uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN);
|
||||
uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN);
|
||||
|
@ -89,25 +80,21 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
|
|||
case RTCP_SDES :
|
||||
{
|
||||
ssize_t contentLen = packetLen - sizeof(rtcpPacket->header);
|
||||
while ( contentLen )
|
||||
{
|
||||
while ( contentLen ) {
|
||||
Debug( 5, "RTCP CL: %zd", contentLen );
|
||||
uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN);
|
||||
|
||||
Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count );
|
||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
|
||||
{
|
||||
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) {
|
||||
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item;
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
for ( int i = 0; i < count; i++ ) {
|
||||
RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr;
|
||||
Debug( 5, "RTCP Item length %d", item->len );
|
||||
switch( item->type )
|
||||
{
|
||||
switch( item->type ) {
|
||||
case RTCP_SDES_CNAME :
|
||||
{
|
||||
std::string cname( item->data, item->len );
|
||||
|
@ -123,10 +110,8 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
|
|||
case RTCP_SDES_NOTE :
|
||||
case RTCP_SDES_PRIV :
|
||||
default :
|
||||
{
|
||||
Error( "Received unexpected SDES item type %d, ignoring", item->type );
|
||||
return( -1 );
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
int paddedLen = 4+2+item->len+1; // Add null byte
|
||||
paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4
|
||||
|
@ -134,39 +119,30 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
|
|||
sdesPtr += paddedLen;
|
||||
contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0;
|
||||
}
|
||||
}
|
||||
} // end whiel contentLen
|
||||
break;
|
||||
}
|
||||
case RTCP_BYE :
|
||||
{
|
||||
Debug(5, "RTCP Got BYE");
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
case RTCP_APP :
|
||||
{
|
||||
// Ignoring as per RFC 3550
|
||||
Debug(5, "Received RTCP_APP packet, ignoring.");
|
||||
break;
|
||||
}
|
||||
case RTCP_RR :
|
||||
{
|
||||
Error("Received RTCP_RR packet.");
|
||||
return( -1 );
|
||||
}
|
||||
return -1;
|
||||
default :
|
||||
{
|
||||
// Ignore unknown packet types. Some cameras do this by design.
|
||||
Debug(5, "Received unexpected packet type %d, ignoring", pt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
consumed = sizeof(uint32_t)*(len+1);
|
||||
return( consumed );
|
||||
return consumed;
|
||||
}
|
||||
|
||||
int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
|
||||
{
|
||||
int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen ) {
|
||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]);
|
||||
|
@ -180,11 +156,13 @@ int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
|
|||
|
||||
mRtpSource.updateRtcpStats();
|
||||
|
||||
Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 );
|
||||
Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() );
|
||||
Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() );
|
||||
Debug( 5, "Jitter = %d", mRtpSource.getJitter() );
|
||||
Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() );
|
||||
Debug(5, "Ssrc = %d Ssrc_1 = %d Last Seq = %d Jitter = %d Last SR = %d",
|
||||
mRtspThread.getSsrc()+1,
|
||||
mRtpSource.getSsrc(),
|
||||
mRtpSource.getMaxSeq(),
|
||||
mRtpSource.getJitter(),
|
||||
mRtpSource.getLastSrTimestamp()
|
||||
);
|
||||
|
||||
rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1);
|
||||
rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc());
|
||||
|
@ -195,11 +173,10 @@ int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
|
|||
rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp());
|
||||
rtcpPacket->body.rr.rr[0].dlsrN = 0;
|
||||
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
}
|
||||
return wordLen*sizeof(uint32_t);
|
||||
} // end RtpCtrlThread::generateRr
|
||||
|
||||
int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen )
|
||||
{
|
||||
int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen ) {
|
||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||
|
||||
const std::string &cname = mRtpSource.getCname();
|
||||
|
@ -218,11 +195,10 @@ int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen
|
|||
rtcpPacket->body.sdes.item[0].len = cname.size();
|
||||
memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() );
|
||||
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
}
|
||||
return wordLen*sizeof(uint32_t);
|
||||
} // end RtpCtrlThread::generateSdes
|
||||
|
||||
int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen )
|
||||
{
|
||||
int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen ) {
|
||||
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
|
||||
|
||||
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]);
|
||||
|
@ -236,11 +212,10 @@ int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen )
|
|||
|
||||
rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc());
|
||||
|
||||
return( wordLen*sizeof(uint32_t) );
|
||||
}
|
||||
return wordLen*sizeof(uint32_t);
|
||||
} // end RtpCtrolThread::generateBye
|
||||
|
||||
int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes )
|
||||
{
|
||||
int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes ) {
|
||||
unsigned char *bufferPtr = buffer;
|
||||
|
||||
// u_int32 len; /* length of compound RTCP packet in words */
|
||||
|
@ -259,33 +234,28 @@ int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes )
|
|||
// /* something wrong with packet format */
|
||||
// }
|
||||
|
||||
while ( nBytes > 0 )
|
||||
{
|
||||
while ( nBytes > 0 ) {
|
||||
int consumed = recvPacket( bufferPtr, nBytes );
|
||||
if ( consumed <= 0 )
|
||||
break;
|
||||
bufferPtr += consumed;
|
||||
nBytes -= consumed;
|
||||
}
|
||||
return( nBytes );
|
||||
return nBytes;
|
||||
}
|
||||
|
||||
int RtpCtrlThread::run()
|
||||
{
|
||||
int RtpCtrlThread::run() {
|
||||
Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() );
|
||||
SockAddrInet localAddr, remoteAddr;
|
||||
|
||||
bool sendReports;
|
||||
UdpInetSocket rtpCtrlServer;
|
||||
if ( mRtpSource.getLocalHost() != "" )
|
||||
{
|
||||
if ( mRtpSource.getLocalHost() != "" ) {
|
||||
if ( !rtpCtrlServer.bind( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ) )
|
||||
Fatal( "Failed to bind RTCP server" );
|
||||
sendReports = false;
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
if ( !rtpCtrlServer.bind( mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0", mRtpSource.getLocalCtrlPort() ) )
|
||||
Fatal( "Failed to bind RTCP server" );
|
||||
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
|
||||
|
@ -309,15 +279,15 @@ int RtpCtrlThread::run()
|
|||
|
||||
time_t now = time(NULL);
|
||||
Select::CommsList readable = select.getReadable();
|
||||
if ( readable.size() == 0 )
|
||||
{
|
||||
if ( readable.size() == 0 ) {
|
||||
if ( ! timeout ) {
|
||||
// With this code here, we will send an SDES and RR packet every 10 seconds
|
||||
ssize_t nBytes;
|
||||
unsigned char *bufferPtr = buffer;
|
||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
|
||||
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d",
|
||||
bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
|
||||
if ( (nBytes = rtpCtrlServer.send(buffer, bufferPtr-buffer)) < 0 )
|
||||
Error("Unable to send: %s", strerror(errno));
|
||||
timeout = true;
|
||||
|
@ -332,19 +302,15 @@ int RtpCtrlThread::run()
|
|||
timeout = false;
|
||||
last_receive = time(NULL);
|
||||
}
|
||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter )
|
||||
{
|
||||
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) )
|
||||
{
|
||||
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter ) {
|
||||
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) ) {
|
||||
ssize_t nBytes = socket->recv( buffer, sizeof(buffer) );
|
||||
Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() );
|
||||
|
||||
if ( nBytes )
|
||||
{
|
||||
if ( nBytes ) {
|
||||
recvPackets( buffer, nBytes );
|
||||
|
||||
if ( sendReports )
|
||||
{
|
||||
if ( sendReports ) {
|
||||
unsigned char *bufferPtr = buffer;
|
||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
|
@ -358,16 +324,14 @@ int RtpCtrlThread::run()
|
|||
mStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Panic("Barfed");
|
||||
}
|
||||
}
|
||||
} // end if socket
|
||||
} // end foeach comms iterator
|
||||
}
|
||||
rtpCtrlServer.close();
|
||||
mRtspThread.stop();
|
||||
return( 0 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
|
|
@ -34,13 +34,11 @@
|
|||
class RtspThread;
|
||||
class RtpSource;
|
||||
|
||||
class RtpCtrlThread : public Thread
|
||||
{
|
||||
class RtpCtrlThread : public Thread {
|
||||
friend class RtspThread;
|
||||
|
||||
private:
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
RTCP_SR = 200,
|
||||
RTCP_RR = 201,
|
||||
RTCP_SDES = 202,
|
||||
|
@ -48,8 +46,7 @@ private:
|
|||
RTCP_APP = 204
|
||||
} RtcpType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
typedef enum {
|
||||
RTCP_SDES_END = 0,
|
||||
RTCP_SDES_CNAME = 1,
|
||||
RTCP_SDES_NAME = 2,
|
||||
|
@ -61,8 +58,7 @@ private:
|
|||
RTCP_SDES_PRIV = 8
|
||||
} RtcpSdesType;
|
||||
|
||||
struct RtcpCommonHeader
|
||||
{
|
||||
struct RtcpCommonHeader {
|
||||
uint8_t count:5; // varies by packet type
|
||||
uint8_t p:1; // padding flag
|
||||
uint8_t version:2; // protocol version
|
||||
|
@ -71,8 +67,7 @@ private:
|
|||
};
|
||||
|
||||
// Reception report block
|
||||
struct RtcpRr
|
||||
{
|
||||
struct RtcpRr {
|
||||
uint32_t ssrcN; // data source being reported
|
||||
int32_t lost:24; // cumul. no. pkts lost (signed!)
|
||||
uint32_t fraction:8; // fraction lost since last SR/RR
|
||||
|
@ -83,22 +78,18 @@ private:
|
|||
};
|
||||
|
||||
// SDES item
|
||||
struct RtcpSdesItem
|
||||
{
|
||||
struct RtcpSdesItem {
|
||||
uint8_t type; // type of item (rtcp_sdes_type_t)
|
||||
uint8_t len; // length of item (in octets)
|
||||
char data[]; // text, not null-terminated
|
||||
};
|
||||
|
||||
// RTCP packet
|
||||
struct RtcpPacket
|
||||
{
|
||||
struct RtcpPacket {
|
||||
RtcpCommonHeader header; // common header
|
||||
union
|
||||
{
|
||||
union {
|
||||
// Sender Report (SR)
|
||||
struct Sr
|
||||
{
|
||||
struct Sr {
|
||||
uint32_t ssrcN; // sender generating this report, network order
|
||||
uint32_t ntpSecN; // NTP timestamp, network order
|
||||
uint32_t ntpFracN;
|
||||
|
@ -109,22 +100,19 @@ private:
|
|||
} sr;
|
||||
|
||||
// Reception Report (RR)
|
||||
struct Rr
|
||||
{
|
||||
struct Rr {
|
||||
uint32_t ssrcN; // receiver generating this report
|
||||
RtcpRr rr[]; // variable-length list
|
||||
} rr;
|
||||
|
||||
// source description (SDES)
|
||||
struct Sdes
|
||||
{
|
||||
struct Sdes {
|
||||
uint32_t srcN; // first SSRC/CSRC
|
||||
RtcpSdesItem item[]; // list of SDES items
|
||||
} sdes;
|
||||
|
||||
// BYE
|
||||
struct
|
||||
{
|
||||
struct {
|
||||
uint32_t srcN[]; // list of sources
|
||||
// can't express trailing text for reason (what does this mean? it's not even english!)
|
||||
} bye;
|
||||
|
@ -148,8 +136,7 @@ private:
|
|||
public:
|
||||
RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource );
|
||||
|
||||
void stop()
|
||||
{
|
||||
void stop() {
|
||||
mStop = true;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -26,7 +26,17 @@
|
|||
|
||||
#if HAVE_LIBAVCODEC
|
||||
|
||||
RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ) :
|
||||
RtpSource::RtpSource(
|
||||
int id,
|
||||
const std::string &localHost,
|
||||
int localPortBase,
|
||||
const std::string &remoteHost,
|
||||
int remotePortBase,
|
||||
uint32_t ssrc,
|
||||
uint16_t seq,
|
||||
uint32_t rtpClock,
|
||||
uint32_t rtpTime,
|
||||
_AVCODECID codecId ) :
|
||||
mId( id ),
|
||||
mSsrc( ssrc ),
|
||||
mLocalHost( localHost ),
|
||||
|
@ -66,11 +76,10 @@ RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, c
|
|||
mLastSrTimeRtp = 0;
|
||||
|
||||
if ( mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4 )
|
||||
Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." );
|
||||
Warning("The device is using a codec (%d) that may not be supported. Do not be surprised if things don't work.", mCodecId);
|
||||
}
|
||||
|
||||
void RtpSource::init( uint16_t seq )
|
||||
{
|
||||
void RtpSource::init( uint16_t seq ) {
|
||||
Debug(3, "Initialising sequence");
|
||||
mBaseSeq = seq;
|
||||
mMaxSeq = seq;
|
||||
|
@ -84,77 +93,58 @@ void RtpSource::init( uint16_t seq )
|
|||
mTransit = 0;
|
||||
}
|
||||
|
||||
bool RtpSource::updateSeq( uint16_t seq )
|
||||
{
|
||||
bool RtpSource::updateSeq( uint16_t seq ) {
|
||||
uint16_t uDelta = seq - mMaxSeq;
|
||||
|
||||
// Source is not valid until MIN_SEQUENTIAL packets with
|
||||
// sequential sequence numbers have been received.
|
||||
Debug( 5, "Seq: %d", seq );
|
||||
|
||||
if ( mProbation)
|
||||
{
|
||||
if ( mProbation) {
|
||||
// packet is in sequence
|
||||
if ( seq == mMaxSeq + 1)
|
||||
{
|
||||
if ( seq == mMaxSeq + 1) {
|
||||
Debug( 3, "Sequence in probation %d, in sequence", mProbation );
|
||||
mProbation--;
|
||||
mMaxSeq = seq;
|
||||
if ( mProbation == 0 )
|
||||
{
|
||||
if ( mProbation == 0 ) {
|
||||
init( seq );
|
||||
mReceivedPackets++;
|
||||
return( true );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Warning( "Sequence in probation %d, out of sequence", mProbation );
|
||||
mProbation = MIN_SEQUENTIAL - 1;
|
||||
mMaxSeq = seq;
|
||||
return( false );
|
||||
}
|
||||
return( true );
|
||||
}
|
||||
else if ( uDelta < MAX_DROPOUT )
|
||||
{
|
||||
if ( uDelta == 1 )
|
||||
{
|
||||
} else if ( uDelta < MAX_DROPOUT ) {
|
||||
if ( uDelta == 1 ) {
|
||||
Debug( 4, "Packet in sequence, gap %d", uDelta );
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Warning( "Packet in sequence, gap %d", uDelta );
|
||||
}
|
||||
|
||||
// in order, with permissible gap
|
||||
if ( seq < mMaxSeq )
|
||||
{
|
||||
if ( seq < mMaxSeq ) {
|
||||
// Sequence number wrapped - count another 64K cycle.
|
||||
mCycles += RTP_SEQ_MOD;
|
||||
}
|
||||
mMaxSeq = seq;
|
||||
}
|
||||
else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER )
|
||||
{
|
||||
} else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER ) {
|
||||
Warning( "Packet out of sequence, gap %d", uDelta );
|
||||
// the sequence number made a very large jump
|
||||
if ( seq == mBadSeq )
|
||||
{
|
||||
if ( seq == mBadSeq ) {
|
||||
Debug( 3, "Restarting sequence" );
|
||||
// Two sequential packets -- assume that the other side
|
||||
// restarted without telling us so just re-sync
|
||||
// (i.e., pretend this was the first packet).
|
||||
init( seq );
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1);
|
||||
return( false );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Warning( "Packet duplicate or reordered, gap %d", uDelta );
|
||||
// duplicate or reordered packet
|
||||
return( false );
|
||||
|
@ -163,10 +153,8 @@ bool RtpSource::updateSeq( uint16_t seq )
|
|||
return( uDelta==1?true:false );
|
||||
}
|
||||
|
||||
void RtpSource::updateJitter( const RtpDataHeader *header )
|
||||
{
|
||||
if ( mRtpFactor > 0 )
|
||||
{
|
||||
void RtpSource::updateJitter( const RtpDataHeader *header ) {
|
||||
if ( mRtpFactor > 0 ) {
|
||||
Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) );
|
||||
uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor );
|
||||
Debug( 5, "Local RTP time = %x", localTimeRtp );
|
||||
|
@ -174,8 +162,7 @@ void RtpSource::updateJitter( const RtpDataHeader *header )
|
|||
uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN);
|
||||
Debug( 5, "Packet transit RTP time = %x", packetTransit );
|
||||
|
||||
if ( mTransit > 0 )
|
||||
{
|
||||
if ( mTransit > 0 ) {
|
||||
// Jitter
|
||||
int d = packetTransit - mTransit;
|
||||
Debug( 5, "Jitter D = %d", d );
|
||||
|
@ -185,28 +172,22 @@ void RtpSource::updateJitter( const RtpDataHeader *header )
|
|||
mJitter += d - ((mJitter + 8) >> 4);
|
||||
}
|
||||
mTransit = packetTransit;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
mJitter = 0;
|
||||
}
|
||||
Debug( 5, "RTP Jitter: %d", mJitter );
|
||||
}
|
||||
|
||||
void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime )
|
||||
{
|
||||
void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ) {
|
||||
struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) );
|
||||
|
||||
Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime );
|
||||
|
||||
if ( mBaseTimeNtp.tv_sec == 0 )
|
||||
{
|
||||
if ( mBaseTimeNtp.tv_sec == 0 ) {
|
||||
mBaseTimeReal = tvNow();
|
||||
mBaseTimeNtp = ntpTime;
|
||||
mBaseTimeRtp = rtpTime;
|
||||
}
|
||||
else if ( !mRtpClock )
|
||||
{
|
||||
} else if ( !mRtpClock ) {
|
||||
Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime );
|
||||
Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime );
|
||||
|
||||
|
@ -227,8 +208,7 @@ void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint
|
|||
mLastSrTimeRtp = rtpTime;
|
||||
}
|
||||
|
||||
void RtpSource::updateRtcpStats()
|
||||
{
|
||||
void RtpSource::updateRtcpStats() {
|
||||
uint32_t extendedMax = mCycles + mMaxSeq;
|
||||
mExpectedPackets = extendedMax - mBaseSeq + 1;
|
||||
|
||||
|
@ -255,8 +235,7 @@ void RtpSource::updateRtcpStats()
|
|||
Debug( 5, "Lost fraction = %d", mLostFraction );
|
||||
}
|
||||
|
||||
bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
|
||||
{
|
||||
bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) {
|
||||
const RtpDataHeader *rtpHeader;
|
||||
rtpHeader = (RtpDataHeader *)packet;
|
||||
int rtpHeaderSize = 12 + rtpHeader->cc * 4;
|
||||
|
@ -269,39 +248,29 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
|
|||
// that there is no marker bit by changing the number of bits in the payload type field.
|
||||
bool thisM = rtpHeader->m || h264FragmentEnd;
|
||||
|
||||
if ( updateSeq( ntohs(rtpHeader->seqN) ) )
|
||||
{
|
||||
if ( updateSeq( ntohs(rtpHeader->seqN) ) ) {
|
||||
Hexdump( 4, packet+rtpHeaderSize, 16 );
|
||||
|
||||
if ( mFrameGood )
|
||||
{
|
||||
if ( mFrameGood ) {
|
||||
int extraHeader = 0;
|
||||
|
||||
if( mCodecId == AV_CODEC_ID_H264 )
|
||||
{
|
||||
if ( mCodecId == AV_CODEC_ID_H264 ) {
|
||||
int nalType = (packet[rtpHeaderSize] & 0x1f);
|
||||
Debug( 3, "Have H264 frame: nal type is %d", nalType );
|
||||
|
||||
switch (nalType)
|
||||
{
|
||||
switch (nalType) {
|
||||
case 24: // STAP-A
|
||||
{
|
||||
extraHeader = 2;
|
||||
break;
|
||||
}
|
||||
case 25: // STAP-B
|
||||
case 26: // MTAP-16
|
||||
case 27: // MTAP-24
|
||||
{
|
||||
extraHeader = 3;
|
||||
break;
|
||||
}
|
||||
// FU-A and FU-B
|
||||
case 28: case 29:
|
||||
{
|
||||
// Is this NAL the first NAL in fragmentation sequence
|
||||
if ( packet[rtpHeaderSize+1] & 0x80 )
|
||||
{
|
||||
if ( packet[rtpHeaderSize+1] & 0x80 ) {
|
||||
// Now we will form new header of frame
|
||||
mFrame.append( "\x0\x0\x1\x0", 4 );
|
||||
// Reconstruct NAL header from FU headers
|
||||
|
@ -311,17 +280,14 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
|
|||
|
||||
extraHeader = 2;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Debug(3, "Unhandled nalType %d", nalType );
|
||||
}
|
||||
}
|
||||
|
||||
// Append NAL frame start code
|
||||
if ( !mFrame.size() )
|
||||
mFrame.append( "\x0\x0\x1", 3 );
|
||||
}
|
||||
} // end if H264
|
||||
mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader );
|
||||
} else {
|
||||
Debug( 3, "NOT H264 frame: type is %d", mCodecId );
|
||||
|
@ -329,16 +295,13 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
|
|||
|
||||
Hexdump( 4, mFrame.head(), 16 );
|
||||
|
||||
if ( thisM )
|
||||
{
|
||||
if ( mFrameGood )
|
||||
{
|
||||
if ( thisM ) {
|
||||
if ( mFrameGood ) {
|
||||
Debug( 3, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() );
|
||||
|
||||
mFrameProcessed.setValueImmediate( false );
|
||||
mFrameReady.updateValueSignal( true );
|
||||
if ( !mFrameProcessed.getValueImmediate() )
|
||||
{
|
||||
if ( !mFrameProcessed.getValueImmediate() ) {
|
||||
// What is the point of this for loop? Is it just me, or will it call getUpdatedValue once or twice? Could it not be better written as
|
||||
// if ( ! mFrameProcessed.getUpdatedValue( 1 ) && mFrameProcessed.getUpdatedValue( 1 ) ) return false;
|
||||
|
||||
|
@ -347,45 +310,34 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
|
|||
return( false );
|
||||
}
|
||||
mFrameCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() );
|
||||
}
|
||||
mFrame.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( mFrame.size() )
|
||||
{
|
||||
} else {
|
||||
if ( mFrame.size() ) {
|
||||
Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() );
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Warning( "Discarding frame %d", mFrameCount );
|
||||
}
|
||||
mFrameGood = false;
|
||||
mFrame.clear();
|
||||
}
|
||||
if ( thisM )
|
||||
{
|
||||
if ( thisM ) {
|
||||
mFrameGood = true;
|
||||
prevM = true;
|
||||
}
|
||||
else
|
||||
} else
|
||||
prevM = false;
|
||||
|
||||
updateJitter( rtpHeader );
|
||||
|
||||
return( true );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RtpSource::getFrame( Buffer &buffer )
|
||||
{
|
||||
bool RtpSource::getFrame( Buffer &buffer ) {
|
||||
Debug( 3, "Getting frame" );
|
||||
if ( !mFrameReady.getValueImmediate() )
|
||||
{
|
||||
if ( !mFrameReady.getValueImmediate() ) {
|
||||
// Allow for a couple of spurious returns
|
||||
for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ )
|
||||
if ( count > 1 )
|
||||
|
@ -395,7 +347,7 @@ bool RtpSource::getFrame( Buffer &buffer )
|
|||
mFrameReady.setValueImmediate( false );
|
||||
mFrameProcessed.updateValueSignal( true );
|
||||
Debug( 4, "Copied %d bytes", buffer.size() );
|
||||
return( true );
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
|
|
105
src/zm_rtsp.cpp
105
src/zm_rtsp.cpp
|
@ -50,15 +50,15 @@ bool RtspThread::sendCommand( std::string message ) {
|
|||
Debug(2, "Sending encoded RTSP message: %s", message.c_str());
|
||||
if ( mRtspSocket2.send(message.c_str(), message.size()) != (int)message.length() ) {
|
||||
Error("Unable to send message '%s': %s", message.c_str(), strerror(errno));
|
||||
return( false );
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ( mRtspSocket.send(message.c_str(), message.size()) != (int)message.length() ) {
|
||||
Error("Unable to send message '%s': %s", message.c_str(), strerror(errno));
|
||||
return( false );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return( true );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RtspThread::recvResponse( std::string &response ) {
|
||||
|
@ -76,23 +76,24 @@ bool RtspThread::recvResponse( std::string &response ) {
|
|||
if ( response.size() )
|
||||
Hexdump( Logger::ERROR, response.data(), min(response.size(),16) );
|
||||
}
|
||||
return( false );
|
||||
return false;
|
||||
}
|
||||
if ( respCode == 401 ) {
|
||||
Debug( 2, "Got 401 access denied response code, check WWW-Authenticate header and retry");
|
||||
mAuthenticator->checkAuthResponse(response);
|
||||
mNeedAuth = true;
|
||||
return( false );
|
||||
return false;
|
||||
} else if ( respCode != 200 ) {
|
||||
Error("Unexpected response code %d, text is '%s'", respCode, respText);
|
||||
return( false );
|
||||
}
|
||||
return( true );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} // end RtspThread::recResponse
|
||||
|
||||
int RtspThread::requestPorts() {
|
||||
if ( !smMinDataPort ) {
|
||||
char sql[ZM_SQL_SML_BUFSIZ];
|
||||
//FIXME Why not load specifically by Id? This will get ineffeicient with a lot of monitors
|
||||
strncpy( sql, "select Id from Monitors where Function != 'None' and Type = 'Remote' and Protocol = 'rtsp' and Method = 'rtpUni' order by Id asc", sizeof(sql) );
|
||||
if ( mysql_query( &dbconn, sql ) ) {
|
||||
Error( "Can't run query: %s", mysql_error( &dbconn ) );
|
||||
|
@ -129,11 +130,11 @@ int RtspThread::requestPorts() {
|
|||
PortSet::const_iterator iter = smAssignedPorts.find(i);
|
||||
if ( iter == smAssignedPorts.end() ) {
|
||||
smAssignedPorts.insert(i);
|
||||
return( i );
|
||||
return i;
|
||||
}
|
||||
}
|
||||
Panic("Can assign RTP port, no ports left in pool");
|
||||
return( -1 );
|
||||
return -1;
|
||||
}
|
||||
|
||||
void RtspThread::releasePorts( int port ) {
|
||||
|
@ -141,7 +142,15 @@ void RtspThread::releasePorts( int port ) {
|
|||
smAssignedPorts.erase(port);
|
||||
}
|
||||
|
||||
RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe) :
|
||||
RtspThread::RtspThread(
|
||||
int id,
|
||||
RtspMethod method,
|
||||
const std::string &protocol,
|
||||
const std::string &host,
|
||||
const std::string &port,
|
||||
const std::string &path,
|
||||
const std::string &auth,
|
||||
bool rtsp_describe) :
|
||||
mId( id ),
|
||||
mMethod( method ),
|
||||
mProtocol( protocol ),
|
||||
|
@ -241,11 +250,11 @@ int RtspThread::run() {
|
|||
Debug( 2, "Sending HTTP message: %s", message.c_str() );
|
||||
if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) {
|
||||
Error("Unable to send message '%s': %s", message.c_str(), strerror(errno));
|
||||
return( -1 );
|
||||
return -1;
|
||||
}
|
||||
if ( mRtspSocket.recv( response ) < 0 ) {
|
||||
Error("Recv failed; %s", strerror(errno));
|
||||
return( -1 );
|
||||
return -1;
|
||||
}
|
||||
|
||||
Debug(2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size());
|
||||
|
@ -259,7 +268,7 @@ int RtspThread::run() {
|
|||
if ( response.size() )
|
||||
Hexdump( Logger::ERROR, response.data(), min(response.size(),16) );
|
||||
}
|
||||
return( -1 );
|
||||
return -1;
|
||||
}
|
||||
// If Server requests authentication, check WWW-Authenticate header and fill required fields
|
||||
// for requested authentication method
|
||||
|
@ -277,7 +286,7 @@ int RtspThread::run() {
|
|||
|
||||
if ( respCode != 200 ) {
|
||||
Error("Unexpected response code %d, text is '%s'", respCode, respText);
|
||||
return( -1 );
|
||||
return -1;
|
||||
}
|
||||
|
||||
message = "POST "+mPath+" HTTP/1.0\r\n";
|
||||
|
@ -300,18 +309,18 @@ int RtspThread::run() {
|
|||
// Request supported RTSP commands by the server
|
||||
message = "OPTIONS "+mUrl+" RTSP/1.0\r\n";
|
||||
if ( !sendCommand( message ) )
|
||||
return( -1 );
|
||||
return -1;
|
||||
|
||||
// A negative return here may indicate auth failure, but we will have setup the auth mechanisms so we need to retry.
|
||||
if ( !recvResponse(response) ) {
|
||||
if ( mNeedAuth ) {
|
||||
Debug( 2, "Resending OPTIONS due to possible auth requirement" );
|
||||
if ( !sendCommand(message) )
|
||||
return( -1 );
|
||||
return -1;
|
||||
if ( !recvResponse(response) )
|
||||
return( -1 );
|
||||
return -1;
|
||||
} else {
|
||||
return( -1 );
|
||||
return -1;
|
||||
}
|
||||
} // end if failed response maybe due to auth
|
||||
|
||||
|
@ -332,6 +341,7 @@ int RtspThread::run() {
|
|||
if (mNeedAuth)
|
||||
authTried = true;
|
||||
sendCommand(message);
|
||||
// FIXME WHy sleep 1?
|
||||
sleep(1);
|
||||
res = recvResponse(response);
|
||||
if ( !res && respCode==401 )
|
||||
|
@ -341,7 +351,7 @@ int RtspThread::run() {
|
|||
const std::string endOfHeaders = "\r\n\r\n";
|
||||
size_t sdpStart = response.find(endOfHeaders);
|
||||
if ( sdpStart == std::string::npos )
|
||||
return( -1 );
|
||||
return -1;
|
||||
|
||||
if ( mRtspDescribe ) {
|
||||
std::string DescHeader = response.substr(0, sdpStart);
|
||||
|
@ -355,8 +365,8 @@ int RtspThread::run() {
|
|||
Info("Received new Content-Base in DESCRIBE response header. Updated device Url to: '%s'", mUrl.c_str() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end foreach line
|
||||
} // end if mRtspDescribe
|
||||
|
||||
sdpStart += endOfHeaders.length();
|
||||
|
||||
|
@ -368,7 +378,7 @@ int RtspThread::run() {
|
|||
mFormatContext = mSessDesc->generateFormatContext();
|
||||
} catch( const Exception &e ) {
|
||||
Error( e.getMessage().c_str() );
|
||||
return( -1 );
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -672,51 +682,36 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
|
|||
Hexdump( 4, (char *)buffer, 16 );
|
||||
rtpDataThread.recvPacket( buffer+4, len );
|
||||
Debug( 4, "Received" );
|
||||
}
|
||||
else if ( channel == remoteChannels[1] )
|
||||
{
|
||||
} else if ( channel == remoteChannels[1] ) {
|
||||
// len = ntohs( *((unsigned short *)(buffer+2)) );
|
||||
// Debug( 4, "Got %d bytes on control channel %d", nBytes, channel );
|
||||
Debug( 4, "Got %d bytes on control channel %d, packet length is %d", buffer.size(), channel, len );
|
||||
Hexdump( 4, (char *)buffer, 16 );
|
||||
rtpCtrlThread.recvPackets( buffer+4, len );
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Error( "Unexpected channel selector %d in RTSP interleaved data", buffer[1] );
|
||||
buffer.clear();
|
||||
break;
|
||||
}
|
||||
buffer.consume( len+4 );
|
||||
nBytes -= len+4;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 )
|
||||
{
|
||||
} else {
|
||||
if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 ) {
|
||||
Debug( 4, "Got keepalive response '%s'", (char *)buffer );
|
||||
//buffer.consume( keepaliveResponse.size() );
|
||||
if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) )
|
||||
{
|
||||
if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) {
|
||||
int discardBytes = charPtr-(char *)buffer;
|
||||
buffer -= discardBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) )
|
||||
{
|
||||
} else {
|
||||
if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) {
|
||||
int discardBytes = charPtr-(char *)buffer;
|
||||
Warning( "Unexpected format RTSP interleaved data, resyncing by %d bytes", discardBytes );
|
||||
Hexdump( -1, (char *)buffer, discardBytes );
|
||||
buffer -= discardBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
Warning( "Unexpected format RTSP interleaved data, dumping %d bytes", buffer.size() );
|
||||
Hexdump( -1, (char *)buffer, 32 );
|
||||
buffer.clear();
|
||||
|
@ -764,13 +759,11 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
|
|||
rtpDataThread.start();
|
||||
rtpCtrlThread.start();
|
||||
|
||||
while( !mStop )
|
||||
{
|
||||
while ( !mStop ) {
|
||||
// Send a keepalive message if the server supports this feature and we are close to the timeout expiration
|
||||
if ( sendKeepalive && (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) )
|
||||
{
|
||||
if ( sendKeepalive && (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) ) {
|
||||
if ( !sendCommand( message ) )
|
||||
return( -1 );
|
||||
return -1;
|
||||
lastKeepalive = time(NULL);
|
||||
}
|
||||
usleep(100000);
|
||||
|
@ -784,9 +777,9 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
|
|||
#endif
|
||||
message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
|
||||
if ( !sendCommand(message) )
|
||||
return( -1 );
|
||||
return -1;
|
||||
if ( !recvResponse(response) )
|
||||
return( -1 );
|
||||
return -1;
|
||||
|
||||
rtpDataThread.stop();
|
||||
rtpCtrlThread.stop();
|
||||
|
@ -801,13 +794,11 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
|
|||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Panic("Got unexpected method %d", mMethod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
|
|
@ -136,7 +136,12 @@ SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) :
|
|||
mValue = atoi(tokens[1].c_str());
|
||||
}
|
||||
|
||||
SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) :
|
||||
SessionDescriptor::MediaDescriptor::MediaDescriptor(
|
||||
const std::string &type,
|
||||
int port,
|
||||
int numPorts,
|
||||
const std::string &transport,
|
||||
int payloadType ) :
|
||||
mType( type ),
|
||||
mPort( port ),
|
||||
mNumPorts( numPorts ),
|
||||
|
@ -170,8 +175,7 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
|
|||
throw Exception("Invalid SDP format at '"+line+"'");
|
||||
|
||||
line.erase(0, 2);
|
||||
switch( sdpType )
|
||||
{
|
||||
switch( sdpType ) {
|
||||
case 'v' :
|
||||
mVersion = line;
|
||||
break;
|
||||
|
@ -204,19 +208,13 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
|
|||
mAttributes.push_back( line );
|
||||
StringVector tokens = split( line, ":", 2 );
|
||||
std::string attrName = tokens[0];
|
||||
if ( currMedia )
|
||||
{
|
||||
if ( attrName == "control" )
|
||||
{
|
||||
if ( currMedia ) {
|
||||
if ( attrName == "control" ) {
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
currMedia->setControlUrl( tokens[1] );
|
||||
}
|
||||
else if ( attrName == "range" )
|
||||
{
|
||||
}
|
||||
else if ( attrName == "rtpmap" )
|
||||
{
|
||||
} else if ( attrName == "range" ) {
|
||||
} else if ( attrName == "rtpmap" ) {
|
||||
// a=rtpmap:96 MP4V-ES/90000
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
||||
|
@ -226,53 +224,46 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
|
|||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||
std::string payloadDesc = attrTokens[1];
|
||||
//currMedia->setPayloadType( payloadType );
|
||||
if ( attrTokens.size() > 1 )
|
||||
{
|
||||
if ( attrTokens.size() > 1 ) {
|
||||
StringVector payloadTokens = split( attrTokens[1], "/" );
|
||||
std::string payloadDesc = payloadTokens[0];
|
||||
int payloadClock = atoi(payloadTokens[1].c_str());
|
||||
currMedia->setPayloadDesc( payloadDesc );
|
||||
currMedia->setClock( payloadClock );
|
||||
}
|
||||
}
|
||||
else if ( attrName == "framesize" )
|
||||
{
|
||||
} else if ( attrName == "framesize" ) {
|
||||
// a=framesize:96 320-240
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception("Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'");
|
||||
StringVector attrTokens = split(tokens[1], " ");
|
||||
int payloadType = atoi(attrTokens[0].c_str());
|
||||
if ( payloadType != currMedia->getPayloadType() )
|
||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||
throw Exception( stringtf("Payload type mismatch, expected %d, got %d in '%s'",
|
||||
currMedia->getPayloadType(), payloadType, line.c_str()));
|
||||
//currMedia->setPayloadType( payloadType );
|
||||
StringVector sizeTokens = split(attrTokens[1], "-");
|
||||
int width = atoi(sizeTokens[0].c_str());
|
||||
int height = atoi(sizeTokens[1].c_str());
|
||||
currMedia->setFrameSize(width, height);
|
||||
}
|
||||
else if ( attrName == "framerate" )
|
||||
{
|
||||
} else if ( attrName == "framerate" ) {
|
||||
// a=framerate:5.0
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception("Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'");
|
||||
double frameRate = atof(tokens[1].c_str());
|
||||
currMedia->setFrameRate(frameRate);
|
||||
}
|
||||
else if ( attrName == "fmtp" )
|
||||
{
|
||||
} else if ( attrName == "fmtp" ) {
|
||||
// a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F
|
||||
if ( tokens.size() < 2 )
|
||||
throw Exception("Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'");
|
||||
StringVector attrTokens = split(tokens[1], " ", 2);
|
||||
int payloadType = atoi(attrTokens[0].c_str());
|
||||
if ( payloadType != currMedia->getPayloadType() )
|
||||
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
||||
throw Exception(stringtf("Payload type mismatch, expected %d, got %d in '%s'",
|
||||
currMedia->getPayloadType(), payloadType, line.c_str()));
|
||||
//currMedia->setPayloadType( payloadType );
|
||||
if ( attrTokens.size() > 1 )
|
||||
{
|
||||
if ( attrTokens.size() > 1 ) {
|
||||
StringVector attr2Tokens = split( attrTokens[1], "; " );
|
||||
for ( unsigned int i = 0; i < attr2Tokens.size(); i++ )
|
||||
{
|
||||
for ( unsigned int i = 0; i < attr2Tokens.size(); i++ ) {
|
||||
StringVector attr3Tokens = split( attr2Tokens[i], "=" );
|
||||
//Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() );
|
||||
if ( attr3Tokens[0] == "profile-level-id" ) {
|
||||
|
@ -292,7 +283,7 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
|
|||
} else if ( attrName == "mpeg4-esid" ) {
|
||||
// a=mpeg4-esid:201
|
||||
} else {
|
||||
Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() )
|
||||
Debug(3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str());
|
||||
}
|
||||
} else {
|
||||
Debug(3, "Ignoring general SDP attribute '%s'", line.c_str());
|
||||
|
@ -320,12 +311,11 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
|
|||
mMediaList.push_back(currMedia);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end switch
|
||||
} // end foreach line
|
||||
}
|
||||
|
||||
SessionDescriptor::~SessionDescriptor()
|
||||
{
|
||||
SessionDescriptor::~SessionDescriptor() {
|
||||
if ( mConnInfo )
|
||||
delete mConnInfo;
|
||||
if ( mBandInfo )
|
||||
|
@ -334,8 +324,7 @@ SessionDescriptor::~SessionDescriptor()
|
|||
delete mMediaList[i];
|
||||
}
|
||||
|
||||
AVFormatContext *SessionDescriptor::generateFormatContext() const
|
||||
{
|
||||
AVFormatContext *SessionDescriptor::generateFormatContext() const {
|
||||
AVFormatContext *formatContext = avformat_alloc_context();
|
||||
|
||||
#if (LIBAVFORMAT_VERSION_CHECK(58, 12, 0, 0, 100))
|
||||
|
@ -362,26 +351,31 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
|
|||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||
AVCodecContext *codec_context = avcodec_alloc_context3(NULL);
|
||||
avcodec_parameters_to_context(codec_context, stream->codecpar);
|
||||
stream->codec = codec_context;
|
||||
#else
|
||||
AVCodecContext *codec_context = stream->codec;
|
||||
#endif
|
||||
|
||||
Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
||||
std::string type = mediaDesc->getType();
|
||||
Debug(1, "Looking for codec for %s payload type %d / %s",
|
||||
type.c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str());
|
||||
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
||||
if ( mediaDesc->getType() == "video" )
|
||||
if ( type == "video" )
|
||||
codec_context->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
else if ( mediaDesc->getType() == "audio" )
|
||||
else if ( type == "audio" )
|
||||
codec_context->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||
else if ( mediaDesc->getType() == "application" )
|
||||
else if ( type == "application" )
|
||||
codec_context->codec_type = AVMEDIA_TYPE_DATA;
|
||||
#else
|
||||
if ( mediaDesc->getType() == "video" )
|
||||
if ( type == "video" )
|
||||
codec_context->codec_type = CODEC_TYPE_VIDEO;
|
||||
else if ( mediaDesc->getType() == "audio" )
|
||||
else if ( type == "audio" )
|
||||
codec_context->codec_type = CODEC_TYPE_AUDIO;
|
||||
else if ( mediaDesc->getType() == "application" )
|
||||
else if ( type == "application" )
|
||||
codec_context->codec_type = CODEC_TYPE_DATA;
|
||||
#endif
|
||||
else
|
||||
Warning("Unknown media_type %s", type.c_str());
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
std::string codec_name;
|
||||
|
@ -394,7 +388,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
|
|||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
codec_name = std::string(smStaticPayloads[i].payloadName);
|
||||
#else
|
||||
strncpy( codec_context->codec_name, smStaticPayloads[i].payloadName, sizeof(codec_context->codec_name) );;
|
||||
strncpy(codec_context->codec_name, smStaticPayloads[i].payloadName, sizeof(codec_context->codec_name));
|
||||
#endif
|
||||
codec_context->codec_type = smStaticPayloads[i].codecType;
|
||||
codec_context->codec_id = smStaticPayloads[i].codecId;
|
||||
|
@ -410,7 +404,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
|
|||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
codec_name = std::string(smStaticPayloads[i].payloadName);
|
||||
#else
|
||||
strncpy( codec_context->codec_name, smDynamicPayloads[i].payloadName, sizeof(codec_context->codec_name) );;
|
||||
strncpy(codec_context->codec_name, smDynamicPayloads[i].payloadName, sizeof(codec_context->codec_name));
|
||||
#endif
|
||||
codec_context->codec_type = smDynamicPayloads[i].codecType;
|
||||
codec_context->codec_id = smDynamicPayloads[i].codecId;
|
||||
|
@ -418,7 +412,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
|
|||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} /// end if static or dynamic
|
||||
|
||||
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
||||
if ( codec_name.empty() )
|
||||
|
@ -426,7 +420,8 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
|
|||
if ( !stream->codec->codec_name[0] )
|
||||
#endif
|
||||
{
|
||||
Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
||||
Warning( "Can't find payload details for %s payload type %d, name %s",
|
||||
mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
||||
//return( 0 );
|
||||
}
|
||||
if ( mediaDesc->getWidth() )
|
||||
|
@ -497,7 +492,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
|
|||
}
|
||||
}
|
||||
|
||||
return( formatContext );
|
||||
return formatContext;
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBAVFORMAT
|
||||
|
|
|
@ -1174,7 +1174,7 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) {
|
|||
} // end int VideoStore::writeAudioFramePacket(AVPacket *ipkt)
|
||||
|
||||
int VideoStore::resample_audio() {
|
||||
// Resample the in into the audioSampleBuffer until we process the whole
|
||||
// Resample the in_frame into the audioSampleBuffer until we process the whole
|
||||
// decoded data. Note: pts does not survive resampling or converting
|
||||
#if defined(HAVE_LIBSWRESAMPLE) || defined(HAVE_LIBAVRESAMPLE)
|
||||
#if defined(HAVE_LIBSWRESAMPLE)
|
||||
|
@ -1194,8 +1194,8 @@ int VideoStore::resample_audio() {
|
|||
}
|
||||
/** Store the new samples in the FIFO buffer. */
|
||||
ret = av_audio_fifo_write(fifo, (void **)out_frame->data, out_frame->nb_samples);
|
||||
if ( ret < in_frame->nb_samples ) {
|
||||
Error("Could not write data to FIFO on %d written", ret);
|
||||
if ( ret < out_frame->nb_samples ) {
|
||||
Error("Could not write data to FIFO on %d written, expecting %d", ret, out_frame->nb_samples);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,9 @@ if ( $action == 'monitor' ) {
|
|||
|
||||
$columns = getTableColumns('Monitors');
|
||||
$changes = getFormChanges($monitor, $_REQUEST['newMonitor'], $types, $columns);
|
||||
ZM\Logger::Debug("Columns:". print_r($columns,true));
|
||||
ZM\Logger::Debug("Changes:". print_r($changes,true));
|
||||
ZM\Logger::Debug("newMonitor:". print_r($_REQUEST['newMonitor'],true));
|
||||
|
||||
if ( count($changes) ) {
|
||||
if ( $mid ) {
|
||||
|
@ -88,12 +91,12 @@ if ( $action == 'monitor' ) {
|
|||
$NewStorage = new ZM\Storage($_REQUEST['newMonitor']['StorageId']);
|
||||
if ( !file_exists($NewStorage->Path().'/'.$mid) ) {
|
||||
if ( !mkdir($NewStorage->Path().'/'.$mid, 0755) ) {
|
||||
Error('Unable to mkdir ' . $NewStorage->Path().'/'.$mid);
|
||||
ZM\Error('Unable to mkdir ' . $NewStorage->Path().'/'.$mid);
|
||||
}
|
||||
}
|
||||
$saferNewName = basename($_REQUEST['newMonitor']['Name']);
|
||||
if ( !symlink($NewStorage->Path().'/'.$mid, $NewStorage->Path().'/'.$saferNewName) ) {
|
||||
Warning('Unable to symlink ' . $NewStorage->Path().'/'.$mid . ' to ' . $NewStorage->Path().'/'.$saferNewName);
|
||||
ZM\Warning('Unable to symlink ' . $NewStorage->Path().'/'.$mid . ' to ' . $NewStorage->Path().'/'.$saferNewName);
|
||||
}
|
||||
}
|
||||
if ( isset($changes['Width']) || isset($changes['Height']) ) {
|
||||
|
|
|
@ -93,7 +93,7 @@ if ( isset($_GET['skin']) ) {
|
|||
$skins = array_map('basename', glob('skins/*', GLOB_ONLYDIR));
|
||||
|
||||
if ( ! in_array($skin, $skins) ) {
|
||||
Error("Invalid skin '$skin' setting to " . $skins[0]);
|
||||
ZM\Error("Invalid skin '$skin' setting to " . $skins[0]);
|
||||
$skin = $skins[0];
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ if ( isset($_GET['css']) ) {
|
|||
|
||||
$css_skins = array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR));
|
||||
if ( !in_array($css, $css_skins) ) {
|
||||
Error("Invalid skin css '$css' setting to " . $css_skins[0]);
|
||||
ZM\Error("Invalid skin css '$css' setting to " . $css_skins[0]);
|
||||
$css = $css_skins[0];
|
||||
}
|
||||
|
||||
|
|
|
@ -294,7 +294,7 @@ $SLANG = array(
|
|||
'Display' => 'Prikaz',
|
||||
'Displaying' => 'Prikazujem',
|
||||
'DonateAlready' => 'Ne, već sam napravio donaciju.',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'Donate' => 'Molimo donirajte',
|
||||
'DonateRemindDay' => 'Ne još, podsjetime za 1 dan',
|
||||
'DonateRemindHour' => 'Ne još, podsjetime za 1 sat',
|
||||
|
|
|
@ -293,7 +293,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
|
||||
'Donate' => 'Please Donate',
|
||||
'DonateAlready' => 'No, I\'ve already donated',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateRemindDay' => 'Not yet, remind again in 1 day',
|
||||
'DonateRemindHour' => 'Not yet, remind again in 1 hour',
|
||||
'DonateRemindMonth' => 'Not yet, remind again in 1 month',
|
||||
|
|
|
@ -289,7 +289,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
|
||||
'Donate' => '请捐款',
|
||||
'DonateAlready' => '不,我已经捐赠过了',
|
||||
'DonateEnticement' => '迄今,您已经运行ZoneMinder有一阵子了,希望它能够有助于增强您家或者办公区域的安全。尽管ZoneMinder是,并将保持免费和开源,该项目依然在研发和支持中投入了资金和精力。如果您愿意支持今后的开发和新功能,那么请考虑为该项目捐款。捐款不是必须的,任何数量的捐赠,我们都很感谢。<br/><br/>如果您愿意捐款,请选择下列选项,或者访问 http://www.zoneminder.com/donate.html 捐赠主页。<br/><br/>感谢您使用ZoneMinder,并且不要忘记访问访问ZoneMinder.com的论坛以获得支持或建议,这可以提升您的ZoneMinder的体验。',
|
||||
'DonateEnticement' => '迄今,您已经运行ZoneMinder有一阵子了,希望它能够有助于增强您家或者办公区域的安全。尽管ZoneMinder是,并将保持免费和开源,该项目依然在研发和支持中投入了资金和精力。如果您愿意支持今后的开发和新功能,那么请考虑为该项目捐款。捐款不是必须的,任何数量的捐赠,我们都很感谢。<br/><br/>如果您愿意捐款,请选择下列选项,或者访问 https://zoneminder.com/donate/ 捐赠主页。<br/><br/>感谢您使用ZoneMinder,并且不要忘记访问访问ZoneMinder.com的论坛以获得支持或建议,这可以提升您的ZoneMinder的体验。',
|
||||
'DonateRemindDay' => '现在不,1天内再次提醒我',
|
||||
'DonateRemindHour' => '现在不,1小时内再次提醒我',
|
||||
'DonateRemindMonth' => '现在不,1个月内再次提醒我',
|
||||
|
|
|
@ -289,7 +289,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
|
||||
'Donate' => 'Prosím podpořte',
|
||||
'DonateAlready' => 'Ne, už jsem podpořil',
|
||||
'DonateEnticement' => 'Již nějakou dobu používáte software ZoneMinder k ochraně svého majetku a předpokládám, že jej shledáváte užitečným. Přestože je ZoneMinder, znovu připomínám, zdarma a volně šířený software, stojí jeho vývoj a podpora nějaké peníze. Pokud byste chtěl/a podpořit budoucí vývoj a nové možnosti softwaru, prosím zvažte darování finanční pomoci. Darování je, samozřejmě, dobrovolné, ale zato velmi ceněné můžete přispět jakou částkou chcete.<br><br>Pokud máte zájem podpořit náš tým, prosím, vyberte níže uvedenou možnost, nebo navštivte http://www.zoneminder.com/donate.html.<br><br>Děkuji Vám že jste si vybral/a software ZoneMinder a nezapomeňte navštívit fórum na ZoneMinder.com pro podporu a návrhy jak udělat ZoneMinder ještě lepším než je dnes.',
|
||||
'DonateEnticement' => 'Již nějakou dobu používáte software ZoneMinder k ochraně svého majetku a předpokládám, že jej shledáváte užitečným. Přestože je ZoneMinder, znovu připomínám, zdarma a volně šířený software, stojí jeho vývoj a podpora nějaké peníze. Pokud byste chtěl/a podpořit budoucí vývoj a nové možnosti softwaru, prosím zvažte darování finanční pomoci. Darování je, samozřejmě, dobrovolné, ale zato velmi ceněné můžete přispět jakou částkou chcete.<br><br>Pokud máte zájem podpořit náš tým, prosím, vyberte níže uvedenou možnost, nebo navštivte https://zoneminder.com/donate/.<br><br>Děkuji Vám že jste si vybral/a software ZoneMinder a nezapomeňte navštívit fórum na ZoneMinder.com pro podporu a návrhy jak udělat ZoneMinder ještě lepším než je dnes.',
|
||||
'DonateRemindDay' => 'Nyní ne, připomenout za 1 den',
|
||||
'DonateRemindHour' => 'Nyní ne, připomenout za hodinu',
|
||||
'DonateRemindMonth' => 'Nyní ne, připomenout za měsíc',
|
||||
|
|
|
@ -291,7 +291,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
|
||||
'Donate' => 'Bitte spenden Sie.',
|
||||
'DonateAlready' => 'Nein, ich habe schon gespendet',
|
||||
'DonateEnticement' => 'Sie benutzen ZoneMinder nun schon eine Weile und es ist hoffentlich eine nützliche Applikation zur Verbesserung Ihrer Heim- oder Arbeitssicherheit. Obwohl ZoneMinder eine freie Open-Source-Software ist und bleiben wird, entstehen Kosten bei der Entwicklung und dem Support.<br><br>Falls Sie ZoneMinder für Weiterentwicklung in der Zukunft unterstützen möchten, denken Sie bitte über eine Spende für das Projekt unter der Webadresse http://www.zoneminder.com/donate.html oder über nachfolgend stehende Option nach. Spenden sind, wie der Name schon sagt, immer freiwillig. Dem Projekt helfen kleine genauso wie größere Spenden sehr weiter und ein herzlicher Dank ist jedem Spender sicher.<br><br>Vielen Dank dafür, dass sie ZoneMinder benutzen. Vergessen Sie nicht die Foren unter ZoneMinder.com, um Support zu erhalten und Ihre Erfahrung mit ZoneMinder zu verbessern!',
|
||||
'DonateEnticement' => 'Sie benutzen ZoneMinder nun schon eine Weile und es ist hoffentlich eine nützliche Applikation zur Verbesserung Ihrer Heim- oder Arbeitssicherheit. Obwohl ZoneMinder eine freie Open-Source-Software ist und bleiben wird, entstehen Kosten bei der Entwicklung und dem Support.<br><br>Falls Sie ZoneMinder für Weiterentwicklung in der Zukunft unterstützen möchten, denken Sie bitte über eine Spende für das Projekt unter der Webadresse https://zoneminder.com/donate/ oder über nachfolgend stehende Option nach. Spenden sind, wie der Name schon sagt, immer freiwillig. Dem Projekt helfen kleine genauso wie größere Spenden sehr weiter und ein herzlicher Dank ist jedem Spender sicher.<br><br>Vielen Dank dafür, dass sie ZoneMinder benutzen. Vergessen Sie nicht die Foren unter ZoneMinder.com, um Support zu erhalten und Ihre Erfahrung mit ZoneMinder zu verbessern!',
|
||||
'DonateRemindDay' => 'Noch nicht, erinnere mich in einem Tag noch mal.',
|
||||
'DonateRemindHour' => 'Noch nicht, erinnere mich in einer Stunde noch mal.',
|
||||
'DonateRemindMonth' => 'Noch nicht, erinnere mich in einem Monat noch mal.',
|
||||
|
|
|
@ -290,7 +290,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
|
||||
'Donate' => 'Venligst Donér',
|
||||
'DonateAlready' => 'Nej, jeg har allerede doneret',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateRemindDay' => 'Ikke endnu, påmind igen on 1 dag',
|
||||
'DonateRemindHour' => 'Ikke endnu, påmind igen on 1 time',
|
||||
'DonateRemindMonth' => 'Ikke endnu, påmind igen on 1 måned',
|
||||
|
|
|
@ -296,7 +296,7 @@ $SLANG = array(
|
|||
'Display' => 'Display',
|
||||
'Displaying' => 'Displaying',
|
||||
'DonateAlready' => 'No, I\'ve already donated',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'Donate' => 'Please Donate',
|
||||
'DonateRemindDay' => 'Not yet, remind again in 1 day',
|
||||
'DonateRemindHour' => 'Not yet, remind again in 1 hour',
|
||||
|
|
|
@ -240,7 +240,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
|
||||
'Donate' => 'Please Donate',
|
||||
'DonateAlready' => 'No, I\'ve already donated',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateRemindDay' => 'Not yet, remind again in 1 day',
|
||||
'DonateRemindHour' => 'Not yet, remind again in 1 hour',
|
||||
'DonateRemindMonth' => 'Not yet, remind again in 1 month',
|
||||
|
|
|
@ -289,7 +289,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18
|
||||
'Donate' => 'Por favor, done',
|
||||
'DonateAlready' => 'No, ya he donado',
|
||||
'DonateEnticement' => 'Ha estado ejecutando ZoneMinder por un tiempo y con suerte le resultará un útil complemento para su seguridad en hogar y trabajo. Aunque ZoneMinder es, y será, libre y de código abierto, cuesta dinero desarrollarlo y mantenerlo. Si quiere ayudar a mantener un futuro desarrollo y nuevas funciones entonces considere hacer un donativo por favor. Donar es, por supuesto, opcional pero muy apreciado y puede donar tanto como desee sin importar la cantidad.<br/><br/>Si desea hacer una donación por favor seleccione la opción de debajo o vaya a http://www.zoneminder.com/donate.html en su navegador.<br/><br/>Muchas gracias por usar ZoneMinder y no se olvide de vistar los foros en ZoneMinder.com para obtener soporte o hacer sugerencias sobre cómo mejorar su experiencia con ZoneMinder aún más.',
|
||||
'DonateEnticement' => 'Ha estado ejecutando ZoneMinder por un tiempo y con suerte le resultará un útil complemento para su seguridad en hogar y trabajo. Aunque ZoneMinder es, y será, libre y de código abierto, cuesta dinero desarrollarlo y mantenerlo. Si quiere ayudar a mantener un futuro desarrollo y nuevas funciones entonces considere hacer un donativo por favor. Donar es, por supuesto, opcional pero muy apreciado y puede donar tanto como desee sin importar la cantidad.<br/><br/>Si desea hacer una donación por favor seleccione la opción de debajo o vaya a https://zoneminder.com/donate/ en su navegador.<br/><br/>Muchas gracias por usar ZoneMinder y no se olvide de vistar los foros en ZoneMinder.com para obtener soporte o hacer sugerencias sobre cómo mejorar su experiencia con ZoneMinder aún más.',
|
||||
'DonateRemindDay' => 'Aún no, recordarme de nuevo en 1 día',
|
||||
'DonateRemindHour' => 'Aún no, recordarme de nuevo en 1 hora',
|
||||
'DonateRemindMonth' => 'Aún no, recordarme de nuevo en 1 mes',
|
||||
|
|
|
@ -296,7 +296,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18
|
||||
'Donate' => 'Palun Anneta',
|
||||
'DonateAlready' => 'EI, Ma olen juba annetanud',
|
||||
'DonateEnticement' => 'Sa oled juba kasutanud ZoneMinderit juba mõnda aega. Nüüd kus sa oled leidnud, et see on kasulik lisa sinu kodule või sinu töökohale. Kuigi ZoneMinder on, jääb alatiseks, vabaks ja avatud lähtekoodiks, siiski selle arendamiseks kulub aega ja raha. Kui sa soovid meid aidata, siis toeta meid tuleviku arendusteks ja uute lisade loomiseks. Palun mõelge annetuse peale. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateEnticement' => 'Sa oled juba kasutanud ZoneMinderit juba mõnda aega. Nüüd kus sa oled leidnud, et see on kasulik lisa sinu kodule või sinu töökohale. Kuigi ZoneMinder on, jääb alatiseks, vabaks ja avatud lähtekoodiks, siiski selle arendamiseks kulub aega ja raha. Kui sa soovid meid aidata, siis toeta meid tuleviku arendusteks ja uute lisade loomiseks. Palun mõelge annetuse peale. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br/><br/>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br/><br/>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateRemindDay' => 'Ei veel, tuleta meelde ühe päeva pärast',
|
||||
'DonateRemindHour' => 'Ei veel, tuleta meelde ühe tunni pärast',
|
||||
'DonateRemindMonth' => 'Ei veel, tuleta meelde ühe kuu pärast',
|
||||
|
|
|
@ -295,7 +295,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Réaliser détection native',
|
||||
'Donate' => 'Veuillez faire un don',
|
||||
'DonateAlready' => 'Non, j\'ai déjà donné',
|
||||
'DonateEnticement' => 'Vous utilisez ZoneMinder depuis quelque temps et nous espérons que vous trouvez cette solution utile. Bien que ZoneMinder est, et restera, une solution libre et ouverte (open source), son développement et son maintien nécessitent des moyens financiers. Si vous voulez aider au développement et à l\'ajout de fonctionnalités, veuillez considérer la possibilité d\'effectuer un don. Les dons sont bien sûr optionnels mais grandement appréciés et vous pouvez donner le montant que vous désirez.<br><br>Si vous voulez effectuer un don, veuillez sélectionner l\'option ci-dessous ou veuillez vous rendre sur http://www.zoneminder.com/donate.html à l\'aide de votre navigateur internet.<br><br>Merci d\'utiliser ZoneMinder et n\'oubliez pas de visiter les forums sur ZoneMinder.com pour le support ou des suggestions pour rendre votre expérience de ZoneMinder encore meilleure.',
|
||||
'DonateEnticement' => 'Vous utilisez ZoneMinder depuis quelque temps et nous espérons que vous trouvez cette solution utile. Bien que ZoneMinder est, et restera, une solution libre et ouverte (open source), son développement et son maintien nécessitent des moyens financiers. Si vous voulez aider au développement et à l\'ajout de fonctionnalités, veuillez considérer la possibilité d\'effectuer un don. Les dons sont bien sûr optionnels mais grandement appréciés et vous pouvez donner le montant que vous désirez.<br><br>Si vous voulez effectuer un don, veuillez sélectionner l\'option ci-dessous ou veuillez vous rendre sur https://zoneminder.com/donate/ à l\'aide de votre navigateur internet.<br><br>Merci d\'utiliser ZoneMinder et n\'oubliez pas de visiter les forums sur ZoneMinder.com pour le support ou des suggestions pour rendre votre expérience de ZoneMinder encore meilleure.',
|
||||
'DonateRemindDay' => 'Pas encore, me rappeler dans 1 jour',
|
||||
'DonateRemindHour' => 'Pas encore, me rappeler dans 1 heure',
|
||||
'DonateRemindMonth' => 'Pas encore, me rappeler dans 1 mois',
|
||||
|
|
|
@ -289,7 +289,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
|
||||
'Donate' => 'úøåí áá÷ùä',
|
||||
'DonateAlready' => 'ìà, úøîúé ëáø',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateRemindDay' => 'òãééï ìà, äæëø ìà áòåã éåí àçã',
|
||||
'DonateRemindHour' => 'òãééï ìà, äæëø ìé áòåã ùòä àçú',
|
||||
'DonateRemindMonth' => 'òãééï ìà, äæëø ìé áòåã çåãù àçã',
|
||||
|
|
|
@ -332,7 +332,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18
|
||||
'Donate' => 'Kérem támogasson',
|
||||
'DonateAlready' => 'Nem, én már támogattam',
|
||||
'DonateEnticement' => 'Ön már jó ideje használja a ZoneMindert, és reméljük hasznos eszköznek tartja háza vagy munkahelye biztonságában. Bár a ZoneMinder egy szabad, nyílt forráskódú termék és az is marad, a fejlesztése pénzbe kerül. Ha van lehetősége támogatni a jövőbeni fejlesztéseket és az új funkciókat kérem, tegye meg. A támogatás teljesen önkéntes, de nagyon megbecsült és mértéke is tetszőleges.<br><br>Ha támogatni szertne, kérem, válasszon az alábbi lehetőségekből vagy látogassa meg a http://www.zoneminder.com/donate.html oldalt.<br><br>Köszönjük, hogy használja a ZoneMinder-t és ne felejtse el meglátogatni a fórumokat a ZoneMinder.com oldalon támogatásért és ötletekért, hogy a jövőben is még jobban ki tudja használni a ZoneMinder lehetőségeit.',
|
||||
'DonateEnticement' => 'Ön már jó ideje használja a ZoneMindert, és reméljük hasznos eszköznek tartja háza vagy munkahelye biztonságában. Bár a ZoneMinder egy szabad, nyílt forráskódú termék és az is marad, a fejlesztése pénzbe kerül. Ha van lehetősége támogatni a jövőbeni fejlesztéseket és az új funkciókat kérem, tegye meg. A támogatás teljesen önkéntes, de nagyon megbecsült és mértéke is tetszőleges.<br><br>Ha támogatni szertne, kérem, válasszon az alábbi lehetőségekből vagy látogassa meg a https://zoneminder.com/donate/ oldalt.<br><br>Köszönjük, hogy használja a ZoneMinder-t és ne felejtse el meglátogatni a fórumokat a ZoneMinder.com oldalon támogatásért és ötletekért, hogy a jövőben is még jobban ki tudja használni a ZoneMinder lehetőségeit.',
|
||||
'DonateRemindDay' => 'Nem most, figyelmeztessen egy nap múlva',
|
||||
'DonateRemindHour' => 'Nem most, figyelmeztessen egy óra múlva',
|
||||
'DonateRemindMonth' => 'Nem most, figyelmeztessen egy hónap múlva',
|
||||
|
|
|
@ -294,7 +294,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
|
||||
'Donate' => 'Donate,per favore',
|
||||
'DonateAlready' => 'No, ho gia donato... ',
|
||||
'DonateEnticement' => 'Stai usando ZoneMinder da un po\' di tempo e spero che tu lo stia trovando utile per la sicurezza di casa tua o del tuo posto di lavoro..Anche se ZoneMinder e\' distribuito liberamente come software libero,costa soldi sia svilupparlo che supportarlo. Se preferisci che questo software continui ad avere supporto e sviluppo in futuro allora considera l\idea di fare una piccola donazione. Donare e\' ovviamente opzionale, ma apprezzato e puoi donare quanto vuoi,quel poco o tanto che tu desideri.<br><br>Se hai voglia per cortesia seleziona l\'opzione sotto o punta il tuo browser a http://www.zoneminder.com/donate.html .<br><br>Grazie per usare ZoneMinder e non dimenticare di visitare il forum in ZoneMinder.com se cerchi supporto o hai suggerimenti riguardo a come rendere migliore Zoneminder.',
|
||||
'DonateEnticement' => 'Stai usando ZoneMinder da un po\' di tempo e spero che tu lo stia trovando utile per la sicurezza di casa tua o del tuo posto di lavoro..Anche se ZoneMinder e\' distribuito liberamente come software libero,costa soldi sia svilupparlo che supportarlo. Se preferisci che questo software continui ad avere supporto e sviluppo in futuro allora considera l\idea di fare una piccola donazione. Donare e\' ovviamente opzionale, ma apprezzato e puoi donare quanto vuoi,quel poco o tanto che tu desideri.<br><br>Se hai voglia per cortesia seleziona l\'opzione sotto o punta il tuo browser a https://zoneminder.com/donate/ .<br><br>Grazie per usare ZoneMinder e non dimenticare di visitare il forum in ZoneMinder.com se cerchi supporto o hai suggerimenti riguardo a come rendere migliore Zoneminder.',
|
||||
'DonateRemindDay' => 'Non ancora, ricordamelo ancora tra 1 giorno',
|
||||
'DonateRemindHour' => 'Non ancora, ricordamelo ancora tra 1 ora',
|
||||
'DonateRemindMonth' => 'Non ancora, ricordamelo ancora tra 1 mese',
|
||||
|
|
|
@ -290,7 +290,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
|
||||
'Donate' => 'Please Donate',
|
||||
'DonateAlready' => 'No, I\'ve already donated',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateRemindDay' => 'Not yet, remind again in 1 day',
|
||||
'DonateRemindHour' => 'Not yet, remind again in 1 hour',
|
||||
'DonateRemindMonth' => 'Not yet, remind again in 1 month',
|
||||
|
|
|
@ -290,7 +290,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18
|
||||
'Donate' => 'Geef a.u.b. een donatie',
|
||||
'DonateAlready' => 'Nee, ik heb al gedoneerd',
|
||||
'DonateEnticement' => 'U gebruikt ZoneMinder nu voor een geruime tijd, hopelijk vindt u het een nuttige toevoeging voor uw huis- of werkplekbeveiliging. Natuurlijk is en blijft ZoneMinder gratis en open source software, maar het kost geld om te ontwikkelen, ondersteunen, en te onderhouden. Wij vragen u dan ook om er over na te denken een donatie te doen om zo de ontwikkeling van ZoneMinder te ondersteunen. Natuurlijk bent u hier vrij in, en elke donatie hoe klein dan ook wordt erg gewaardeerd. <br><br> Als u wilt doneren geef dat hieronder dan aan of ga naar http://www.zoneminder.com/donate.html in uw browser.<br><br>Bedankt voor het gebruiken van ZoneMinder en vergeet niet om ons forum op ZoneMinder.com te bezoeken voor ondersteuning of suggesties waarmee uw ZoneMinder beleving nog beter wordt.',
|
||||
'DonateEnticement' => 'U gebruikt ZoneMinder nu voor een geruime tijd, hopelijk vindt u het een nuttige toevoeging voor uw huis- of werkplekbeveiliging. Natuurlijk is en blijft ZoneMinder gratis en open source software, maar het kost geld om te ontwikkelen, ondersteunen, en te onderhouden. Wij vragen u dan ook om er over na te denken een donatie te doen om zo de ontwikkeling van ZoneMinder te ondersteunen. Natuurlijk bent u hier vrij in, en elke donatie hoe klein dan ook wordt erg gewaardeerd. <br><br> Als u wilt doneren geef dat hieronder dan aan of ga naar https://zoneminder.com/donate/ in uw browser.<br><br>Bedankt voor het gebruiken van ZoneMinder en vergeet niet om ons forum op ZoneMinder.com te bezoeken voor ondersteuning of suggesties waarmee uw ZoneMinder beleving nog beter wordt.',
|
||||
'DonateRemindDay' => 'Nu niet, herinner mij over 1 dag hieraan',
|
||||
'DonateRemindHour' => 'Nu niet, herinner mij over een uur hieraan',
|
||||
'DonateRemindMonth' => 'Nu niet, herinner mij over een maand hieraan',
|
||||
|
|
|
@ -304,7 +304,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
|
||||
'Donate' => 'Please Donate',
|
||||
'DonateAlready' => 'No, I\'ve already donated',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateRemindDay' => 'Not yet, remind again in 1 day',
|
||||
'DonateRemindHour' => 'Not yet, remind again in 1 hour',
|
||||
'DonateRemindMonth' => 'Not yet, remind again in 1 month',
|
||||
|
|
|
@ -229,7 +229,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
|
||||
'Donate' => 'Please Donate',
|
||||
'DonateAlready' => 'No, I\'ve already donated',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateRemindDay' => 'Not yet, remind again in 1 day',
|
||||
'DonateRemindHour' => 'Not yet, remind again in 1 hour',
|
||||
'DonateRemindMonth' => 'Not yet, remind again in 1 month',
|
||||
|
|
|
@ -260,7 +260,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
|
||||
'Donate' => 'Please Donate',
|
||||
'DonateAlready' => 'No, I\'ve already donated',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.<br><br>If you would like to donate please select the option below or go to https://zoneminder.com/donate/ in your browser.<br><br>Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.',
|
||||
'DonateRemindDay' => 'Not yet, remind again in 1 day',
|
||||
'DonateRemindHour' => 'Not yet, remind again in 1 hour',
|
||||
'DonateRemindMonth' => 'Not yet, remind again in 1 month',
|
||||
|
|
|
@ -290,7 +290,7 @@ $SLANG = array(
|
|||
'DoNativeMotionDetection'=> 'Do Native Motion Detection',
|
||||
'Donate' => 'Var vänlig och donera',
|
||||
'DonateAlready' => 'Nej, Jag har redan donerat',
|
||||
'DonateEnticement' => 'Du har kört ZoneMinder ett tag nu och förhoppningsvis har du sett att det fungerar bra hemma eller på ditt företag. Även om ZoneMinder är, och kommer att vara, fri programvara och öppen kallkod, så kostar det pengar att utveckla och underhålla. Om du vill hjälpa till med framtida utveckling och nya funktioner så var vanlig och bidrag med en slant. Bidragen är naturligtvis en option men mycket uppskattade och du kan bidra med precis hur mycket du vill.<br><br>Om du vill ge ett bidrag väljer du nedan eller surfar till http://www.zoneminder.com/donate.html.<br><br>Tack för att du använder ZoneMinder, glöm inte att besöka forumen på ZoneMinder.com för support och förslag om hur du får din ZoneMinder att fungera lite bättre.',
|
||||
'DonateEnticement' => 'Du har kört ZoneMinder ett tag nu och förhoppningsvis har du sett att det fungerar bra hemma eller på ditt företag. Även om ZoneMinder är, och kommer att vara, fri programvara och öppen kallkod, så kostar det pengar att utveckla och underhålla. Om du vill hjälpa till med framtida utveckling och nya funktioner så var vanlig och bidrag med en slant. Bidragen är naturligtvis en option men mycket uppskattade och du kan bidra med precis hur mycket du vill.<br><br>Om du vill ge ett bidrag väljer du nedan eller surfar till https://zoneminder.com/donate/.<br><br>Tack för att du använder ZoneMinder, glöm inte att besöka forumen på ZoneMinder.com för support och förslag om hur du får din ZoneMinder att fungera lite bättre.',
|
||||
'DonateRemindDay' => 'Inte än, påminn om 1 dag',
|
||||
'DonateRemindHour' => 'Inte än, påminn om en 1 timme',
|
||||
'DonateRemindMonth' => 'Inte än, påminn om 1 månad',
|
||||
|
|
|
@ -173,12 +173,12 @@ echo output_link_if_exists( array(
|
|||
<script src="<?php echo cache_bust('skins/classic/js/base.js') ?>"></script>
|
||||
<?php } ?>
|
||||
<script src="<?php echo cache_bust($skinJsFile) ?>"></script>
|
||||
<script src="js/logger.js"></script>
|
||||
<script src="<?php echo cache_bust('js/logger.js')?>"></script>
|
||||
<?php
|
||||
if ($basename == 'watch' or $basename == 'log' ) {
|
||||
// This is used in the log popup for the export function. Not sure if it's used anywhere else
|
||||
?>
|
||||
<script src="js/overlay.js"></script>
|
||||
<script src="<?php echo cache_bust('js/overlay.js') ?>"></script>
|
||||
<?php } ?>
|
||||
<?php
|
||||
if ( $viewJsFile ) {
|
||||
|
|
|
@ -64,6 +64,15 @@ function initPage() {
|
|||
return false;
|
||||
};
|
||||
});
|
||||
|
||||
// Disable form submit on enter
|
||||
$j('#contentForm input').on('keyup keypress', function(e) {
|
||||
var keyCode = e.keyCode || e.which;
|
||||
if (keyCode === 13) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} // end function initPage()
|
||||
|
||||
window.addEventListener('DOMContentLoaded', initPage);
|
||||
|
|
|
@ -139,7 +139,7 @@ if ( ZM_OPT_X10 && empty($x10Monitor) ) {
|
|||
}
|
||||
|
||||
function fourcc($a, $b, $c, $d) {
|
||||
return( ord($a) | (ord($b) << 8) | (ord($c) << 16) | (ord($d) << 24) );
|
||||
return ord($a) | (ord($b) << 8) | (ord($c) << 16) | (ord($d) << 24);
|
||||
}
|
||||
|
||||
if ( isset($_REQUEST['newMonitor']) ) {
|
||||
|
@ -171,7 +171,8 @@ if ( !empty($_REQUEST['preset']) ) {
|
|||
$monitor->$name = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
} # end if preset
|
||||
|
||||
if ( !empty($_REQUEST['probe']) ) {
|
||||
$probe = json_decode(base64_decode($_REQUEST['probe']));
|
||||
foreach ( $probe as $name=>$value ) {
|
||||
|
@ -187,7 +188,7 @@ if ( !empty($_REQUEST['probe']) ) {
|
|||
elseif ( $monitor->Format() == 'NTSC' )
|
||||
$monitor->Format( 0x0000b000 );
|
||||
}
|
||||
}
|
||||
} # end if apply probe settings
|
||||
|
||||
$sourceTypes = array(
|
||||
'Local' => translate('Local'),
|
||||
|
@ -458,7 +459,7 @@ $codecs = array(
|
|||
'MJPEG' => translate('MJPEG'),
|
||||
);
|
||||
|
||||
xhtmlHeaders(__FILE__, translate('Monitor')." - ".validHtmlStr($monitor->Name()) );
|
||||
xhtmlHeaders(__FILE__, translate('Monitor').' - '.validHtmlStr($monitor->Name()));
|
||||
getBodyTopHTML();
|
||||
?>
|
||||
<div id="page">
|
||||
|
|
|
@ -46,7 +46,7 @@ if ( isset($_REQUEST['scale']) )
|
|||
else
|
||||
$scale = reScale(SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE);
|
||||
|
||||
$Event = new Event($event['Id']);
|
||||
$Event = new ZM\Event($event['Id']);
|
||||
$eventPath = $Event->Path();
|
||||
|
||||
$videoFormats = array();
|
||||
|
|
Loading…
Reference in New Issue