Merge branch 'storageareas' into zma_to_thread

This commit is contained in:
Isaac Connor 2020-02-22 17:58:30 -05:00
commit bca4108396
56 changed files with 1077 additions and 701 deletions

View File

@ -33,7 +33,6 @@ install:
env: env:
- SMPFLAGS=-j4 OS=el DIST=7 - SMPFLAGS=-j4 OS=el DIST=7
- SMPFLAGS=-j4 OS=fedora DIST=27 DOCKER_REPO=knnniggett/packpack
- SMPFLAGS=-j4 OS=fedora DIST=28 DOCKER_REPO=knnniggett/packpack - SMPFLAGS=-j4 OS=fedora DIST=28 DOCKER_REPO=knnniggett/packpack
- SMPFLAGS=-j4 OS=fedora DIST=29 DOCKER_REPO=knnniggett/packpack - SMPFLAGS=-j4 OS=fedora DIST=29 DOCKER_REPO=knnniggett/packpack
- SMPFLAGS=-j4 OS=ubuntu DIST=trusty - SMPFLAGS=-j4 OS=ubuntu DIST=trusty

View File

@ -4,7 +4,7 @@ DROP TRIGGER IF EXISTS Events_Hour_delete_trigger//
CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour
FOR EACH ROW BEGIN FOR EACH ROW BEGIN
UPDATE Monitors SET UPDATE Monitors SET
HourEvents = COALESCE(HourEvents,1)-1, HourEvents = GREATEST(COALESCE(HourEvents,1)-1,0),
HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
WHERE Id=OLD.MonitorId; WHERE Id=OLD.MonitorId;
END; END;
@ -62,7 +62,7 @@ DROP TRIGGER IF EXISTS Events_Week_delete_trigger//
CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week
FOR EACH ROW BEGIN FOR EACH ROW BEGIN
UPDATE Monitors SET 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) WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
WHERE Id=OLD.MonitorId; WHERE Id=OLD.MonitorId;
END; END;
@ -90,7 +90,7 @@ DROP TRIGGER IF EXISTS Events_Month_delete_trigger//
CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month
FOR EACH ROW BEGIN FOR EACH ROW BEGIN
UPDATE Monitors SET 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) MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0)
WHERE Id=OLD.MonitorId; WHERE Id=OLD.MonitorId;
END; END;

View File

@ -790,7 +790,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,'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,'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,'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,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,'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); 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);

283
db/zm_update-1.33.8.sql Normal file
View File

@ -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;

View File

@ -23,7 +23,7 @@
%global _hardened_build 1 %global _hardened_build 1
Name: zoneminder Name: zoneminder
Version: 1.33.6 Version: 1.33.8
Release: 1%{?dist} Release: 1%{?dist}
Summary: A camera monitoring and analysis tool Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons Group: System Environment/Daemons
@ -73,6 +73,7 @@ BuildRequires: libcurl-devel
BuildRequires: libv4l-devel BuildRequires: libv4l-devel
BuildRequires: desktop-file-utils BuildRequires: desktop-file-utils
BuildRequires: gzip BuildRequires: gzip
BuildRequires: zlib-devel
# ZoneMinder looks for and records the location of the ffmpeg binary during build # ZoneMinder looks for and records the location of the ffmpeg binary during build
BuildRequires: ffmpeg BuildRequires: ffmpeg
@ -410,6 +411,9 @@ EOF
%dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload %dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload
%changelog %changelog
* Tue Apr 30 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.8-1
- Bump to 1.33.8 Development
* Sun Apr 07 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.6-1 * Sun Apr 07 2019 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.33.6-1
- Bump to 1.33.6 Development - Bump to 1.33.6 Development

View File

@ -13,7 +13,7 @@ Type=forking
ExecStart=/usr/bin/zmpkg.pl start ExecStart=/usr/bin/zmpkg.pl start
ExecReload=/usr/bin/zmpkg.pl restart ExecReload=/usr/bin/zmpkg.pl restart
ExecStop=/usr/bin/zmpkg.pl stop ExecStop=/usr/bin/zmpkg.pl stop
PIDFile=/var/run/zm/zm.pid PIDFile=/run/zm/zm.pid
Restart=always Restart=always
RestartSec=10 RestartSec=10
Environment=TZ=:/etc/localtime Environment=TZ=:/etc/localtime

View File

@ -65,7 +65,7 @@ Because ZoneMinder's package repository provides a secure connection through HTT
Finally, download the GPG key for ZoneMinder's repository: 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 **Step 5:** Install ZoneMinder

View File

@ -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. 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.

View File

@ -197,8 +197,8 @@ our @options = (
name => 'ZM_BANDWIDTH_DEFAULT', name => 'ZM_BANDWIDTH_DEFAULT',
default => 'high', default => 'high',
description => 'Default setting for bandwidth profile used by web interface', description => 'Default setting for bandwidth profile used by web interface',
help => q`The classic skin for ZoneMinder has different help => q`The classic skin for ZoneMinder has different
profiles to use for low medium or high bandwidth connections. profiles to use for low, medium, or high bandwidth connections.
`, `,
type => $types{string}, type => $types{string},
category => 'system', category => 'system',
@ -559,7 +559,7 @@ our @options = (
higher quality setting than the ordinary file setting. If set higher quality setting than the ordinary file setting. If set
to a lower value then it is ignored. Thus leaving it at the to a lower value then it is ignored. Thus leaving it at the
default of 0 effectively means to use the regular file quality default of 0 effectively means to use the regular file quality
setting for all saved images. This is to prevent acccidentally setting for all saved images. This is to prevent accidentally
saving important images at a worse quality setting. saving important images at a worse quality setting.
`, `,
type => $types{integer}, type => $types{integer},
@ -683,7 +683,7 @@ our @options = (
Internet Explorer that don't natively support this format. If Internet Explorer that don't natively support this format. If
you use this browser it is highly recommended to install this you use this browser it is highly recommended to install this
from the [cambozola project site](http://www.charliemouse.com/code/cambozola/). from the [cambozola project site](http://www.charliemouse.com/code/cambozola/).
However, if it is not installed still images at a lower refresh rate can However, if it is not installed still images at a lower refresh rate can
still be viewed. still be viewed.
`, `,
type => $types{boolean}, type => $types{boolean},
@ -938,10 +938,10 @@ our @options = (
help => q` help => q`
Due to browsers only wanting to open 6 connections, if you have more Due to browsers only wanting to open 6 connections, if you have more
than 6 monitors, you can have trouble viewing more than 6. This setting than 6 monitors, you can have trouble viewing more than 6. This setting
specified the beginning of a port range that will be used to contact ZM specified the beginning of a port range that will be used to contact ZM
on. Each monitor will use this value plus the Monitor Id to stream on. Each monitor will use this value plus the Monitor Id to stream
content. So a value of 2000 here will cause a stream for Monitor 1 to content. So a value of 2000 here will cause a stream for Monitor 1 to
hit port 2001. Please ensure that you configure apache appropriately hit port 2001. Please ensure that you configure apache appropriately
to respond on these ports.`, to respond on these ports.`,
type => $types{integer}, type => $types{integer},
category => 'network', category => 'network',
@ -1077,12 +1077,12 @@ our @options = (
default => '0', default => '0',
description => 'Save logging output to the system log', description => 'Save logging output to the system log',
help => q` help => q`
ZoneMinder logging is now more more integrated between ZoneMinder logging is now more integrated between
components and allows you to specify the destination for components and allows you to specify the destination for
logging output and the individual levels for each. This option logging output and the individual levels for each. This option
lets you control the level of logging output that goes to the lets you control the level of logging output that goes to the
system log. ZoneMinder binaries have always logged to the system log. ZoneMinder binaries have always logged to the
system log but now scripts and web logging is also included. To system log but script and web logging is now included. To
preserve the previous behaviour you should ensure this value is preserve the previous behaviour you should ensure this value is
set to Info or Warning. This option controls the maximum level set to Info or Warning. This option controls the maximum level
of logging that will be written, so Info includes Warnings and of logging that will be written, so Info includes Warnings and
@ -1104,7 +1104,7 @@ our @options = (
default => '-5', default => '-5',
description => 'Save logging output to component files', description => 'Save logging output to component files',
help => q` help => q`
ZoneMinder logging is now more more integrated between ZoneMinder logging is now more integrated between
components and allows you to specify the destination for components and allows you to specify the destination for
logging output and the individual levels for each. This option logging output and the individual levels for each. This option
lets you control the level of logging output that goes to lets you control the level of logging output that goes to
@ -1134,7 +1134,7 @@ our @options = (
default => '-5', default => '-5',
description => 'Save logging output to the weblog', description => 'Save logging output to the weblog',
help => q` help => q`
ZoneMinder logging is now more more integrated between ZoneMinder logging is now more integrated between
components and allows you to specify the destination for components and allows you to specify the destination for
logging output and the individual levels for each. This option logging output and the individual levels for each. This option
lets you control the level of logging output from the web lets you control the level of logging output from the web
@ -1161,7 +1161,7 @@ our @options = (
default => '0', default => '0',
description => 'Save logging output to the database', description => 'Save logging output to the database',
help => q` help => q`
ZoneMinder logging is now more more integrated between ZoneMinder logging is now more integrated between
components and allows you to specify the destination for components and allows you to specify the destination for
logging output and the individual levels for each. This option logging output and the individual levels for each. This option
lets you control the level of logging output that is written to lets you control the level of logging output that is written to
@ -1216,7 +1216,7 @@ our @options = (
help => q` help => q`
When enabled (default is on), this option will log FFMPEG messages. When enabled (default is on), this option will log FFMPEG messages.
FFMPEG messages can be useful when debugging streaming issues. However, FFMPEG messages can be useful when debugging streaming issues. However,
depending on your distro and FFMPEG version, this may also result in depending on your distro and FFMPEG version, this may also result in
more logs than you'd typically like to see. If all your streams are working more logs than you'd typically like to see. If all your streams are working
well, you may choose to turn this off. well, you may choose to turn this off.
`, `,
@ -1231,7 +1231,7 @@ our @options = (
ZoneMinder components usually support debug logging available ZoneMinder components usually support debug logging available
to help with diagnosing problems. Binary components have to help with diagnosing problems. Binary components have
several levels of debug whereas more other components have only several levels of debug whereas more other components have only
one. Normally this is disabled to minimise performance one. Normally this is disabled to minimize performance
penalties and avoid filling logs too quickly. This option lets penalties and avoid filling logs too quickly. This option lets
you switch on other options that allow you to configure you switch on other options that allow you to configure
additional debug information to be output. Components will pick additional debug information to be output. Components will pick
@ -1492,8 +1492,8 @@ our @options = (
default => 'ZoneMinder', default => 'ZoneMinder',
description => 'The title displayed wherever the site references itself.', description => 'The title displayed wherever the site references itself.',
help => q` help => q`
If you want the site to identify as something other than ZoneMinder, change this here. If you want the site to identify as something other than ZoneMinder, change this here.
It can be used to more accurately identify this installation from others. It can be used to more accurately identify this installation from others.
`, `,
type => $types{string}, type => $types{string},
category => 'web', category => 'web',
@ -1516,8 +1516,8 @@ our @options = (
default => 'http://zoneminder.com', default => 'http://zoneminder.com',
description => 'The url used in the home/logo area of the navigation bar.', description => 'The url used in the home/logo area of the navigation bar.',
help => q` help => q`
By default this takes you to the zoneminder.com website, By default this takes you to the zoneminder.com website,
but perhaps you would prefer it to take you somewhere else. but perhaps you would prefer it to take you somewhere else.
`, `,
type => $types{string}, type => $types{string},
category => 'web', category => 'web',
@ -1527,7 +1527,7 @@ our @options = (
default => 'ZoneMinder', default => 'ZoneMinder',
description => 'The content of the home button.', description => 'The content of the home button.',
help => q` help => q`
You may wish to set this to empty if you are using css to put a background image on it. You may wish to set this to empty if you are using css to put a background image on it.
`, `,
type => $types{string}, type => $types{string},
category => 'web', category => 'web',
@ -1550,7 +1550,8 @@ our @options = (
name => 'ZM_WEB_EVENT_DISK_SPACE', name => 'ZM_WEB_EVENT_DISK_SPACE',
default => 'no', default => 'no',
description => 'Whether to show disk space used by each event.', description => 'Whether to show disk space used by each event.',
help => q`Adds another column to the listing of events help => q`
Adds another column to the listing of events
showing the disk space used by the event. This will impart a small showing the disk space used by the event. This will impart a small
overhead as it will call du on the event directory. In practice overhead as it will call du on the event directory. In practice
this overhead is fairly small but may be noticeable on IO-constrained this overhead is fairly small but may be noticeable on IO-constrained
@ -1567,7 +1568,7 @@ our @options = (
Traditionally the main ZoneMinder web console window has Traditionally the main ZoneMinder web console window has
resized itself to shrink to a size small enough to list only resized itself to shrink to a size small enough to list only
the monitors that are actually present. This is intended to the monitors that are actually present. This is intended to
make the window more unobtrusize but may not be to everyones make the window more unobtrusize but may not be to everyone's
tastes, especially if opened in a tab in browsers which support tastes, especially if opened in a tab in browsers which support
this kind if layout. Switch this option off to have the console this kind if layout. Switch this option off to have the console
window size left to the users preference window size left to the users preference
@ -2117,7 +2118,7 @@ our @options = (
a remote ftp server. This option indicates that ftp transfers a remote ftp server. This option indicates that ftp transfers
should be done in passive mode. This uses a single connection should be done in passive mode. This uses a single connection
for all ftp activity and, whilst slower than active transfers, for all ftp activity and, whilst slower than active transfers,
is more robust and likely to work from behind filewalls. This is more robust and likely to work from behind firewalls. This
option is ignored for SFTP transfers. option is ignored for SFTP transfers.
`, `,
requires => [ { name => 'ZM_OPT_UPLOAD', value => 'yes' } ], requires => [ { name => 'ZM_OPT_UPLOAD', value => 'yes' } ],
@ -2643,7 +2644,7 @@ our @options = (
help => q` help => q`
As event images are captured they are stored to the filesystem As event images are captured they are stored to the filesystem
with a numerical index. By default this index has three digits with a numerical index. By default this index has three digits
so the numbers start 001, 002 etc. This works works for most so the numbers start 001, 002 etc. This works for most
scenarios as events with more than 999 frames are rarely scenarios as events with more than 999 frames are rarely
captured. However if you have extremely long events and use captured. However if you have extremely long events and use
external applications then you may wish to increase this to external applications then you may wish to increase this to
@ -2740,7 +2741,7 @@ our @options = (
it to the ZoneMinder development team. This data will be used to it to the ZoneMinder development team. This data will be used to
determine things like who and where our customers are, how big their determine things like who and where our customers are, how big their
systems are, the underlying hardware and operating system, etc. systems are, the underlying hardware and operating system, etc.
This is being done for the sole purpoase of creating a better This is being done for the sole purpose of creating a better
product for our target audience. This script is intended to be product for our target audience. This script is intended to be
completely transparent to the end user, and can be disabled from completely transparent to the end user, and can be disabled from
the web console under Options. For more details on what information the web console under Options. For more details on what information
@ -2764,7 +2765,7 @@ our @options = (
{ {
name => 'ZM_TELEMETRY_LAST_UPLOAD', name => 'ZM_TELEMETRY_LAST_UPLOAD',
default => '', default => '',
description => 'When the last ZoneMinder telemetry upload ocurred', description => 'When the last ZoneMinder telemetry upload occurred',
help => '', help => '',
type => $types{integer}, type => $types{integer},
readonly => 1, readonly => 1,
@ -2822,7 +2823,7 @@ our @options = (
default => 'javascript', default => 'javascript',
description => 'What method windows should use to refresh themselves', description => 'What method windows should use to refresh themselves',
help => q` help => q`
Many windows in Javascript need to refresh themselves to keep Many windows in ZoneMinder need to refresh themselves to keep
their information current. This option determines what method their information current. This option determines what method
they should use to do this. Choosing 'javascript' means that they should use to do this. Choosing 'javascript' means that
each window will have a short JavaScript statement in with a each window will have a short JavaScript statement in with a
@ -2956,7 +2957,7 @@ our @options = (
some indication of the type of content. However this is not a some indication of the type of content. However this is not a
standard part of HTML. The official method is to use OBJECT standard part of HTML. The official method is to use OBJECT
tags which are able to give more information allowing the tags which are able to give more information allowing the
correct media viewers etc to be loaded. However these are less correct media viewers etc. to be loaded. However these are less
widely supported and content may be specifically tailored to a widely supported and content may be specifically tailored to a
particular platform or player. This option controls whether particular platform or player. This option controls whether
media content is enclosed in EMBED tags only or whether, where media content is enclosed in EMBED tags only or whether, where
@ -2980,7 +2981,7 @@ our @options = (
browsers. When this condition occurs, ZoneMinder will write a browsers. When this condition occurs, ZoneMinder will write a
warning to the log file. To get around this, one can install a browser warning to the log file. To get around this, one can install a browser
plugin or extension to ignore X-Frame headers, and then the page will plugin or extension to ignore X-Frame headers, and then the page will
display properly. Once the plugin or extenstion has ben installed, display properly. Once the plugin or extension has ben installed,
the end user may choose to turn this warning off. the end user may choose to turn this warning off.
`, `,
type => $types{boolean}, type => $types{boolean},
@ -3094,7 +3095,7 @@ our @options = (
description => 'How often (in seconds) the event listing is refreshed in the watch window', description => 'How often (in seconds) the event listing is refreshed in the watch window',
help => q` help => q`
The monitor window is actually made from several frames. The The monitor window is actually made from several frames. The
lower framme contains a listing of the last few events for easy lower frame contains a listing of the last few events for easy
access. This option determines how often this is refreshed. access. This option determines how often this is refreshed.
`, `,
type => $types{integer}, type => $types{integer},
@ -3224,12 +3225,12 @@ our @options = (
{ {
name => 'ZM_WEB_H_SCALE_THUMBS', name => 'ZM_WEB_H_SCALE_THUMBS',
default => 'no', default => 'no',
description => 'Scale thumbnails in events, bandwidth versus cpu in rescaling', description => 'Scale thumbnails in events, bandwidth versus CPU in rescaling',
help => q` help => q`
If unset, this option sends the whole image to the browser If unset, this option sends the whole image to the browser
which resizes it in the window. If set the image is scaled down which resizes it in the window. If set the image is scaled down
on the server before sending a reduced size image to the on the server before sending a reduced size image to the
browser to conserve bandwidth at the cost of cpu on the server. browser to conserve bandwidth at the cost of CPU on the server.
Note that ZM can only perform the resizing if the appropriate Note that ZM can only perform the resizing if the appropriate
PHP graphics functionality is installed. This is usually PHP graphics functionality is installed. This is usually
available in the php-gd package. available in the php-gd package.
@ -3263,7 +3264,7 @@ our @options = (
help => q` help => q`
When viewing events an event navigation panel and progress bar When viewing events an event navigation panel and progress bar
is shown below the event itself. This allows you to jump to is shown below the event itself. This allows you to jump to
specific points in the event, but can can also dynamically specific points in the event, but can also dynamically
update to display the current progress of the event replay update to display the current progress of the event replay
itself. This progress is calculated from the actual event itself. This progress is calculated from the actual event
duration and is not directly linked to the replay itself, so on duration and is not directly linked to the replay itself, so on
@ -3367,7 +3368,7 @@ our @options = (
description => 'How often (in seconds) the event listing is refreshed in the watch window', description => 'How often (in seconds) the event listing is refreshed in the watch window',
help => q` help => q`
The monitor window is actually made from several frames. The The monitor window is actually made from several frames. The
lower framme contains a listing of the last few events for easy lower frame contains a listing of the last few events for easy
access. This option determines how often this is refreshed. access. This option determines how often this is refreshed.
`, `,
type => $types{integer}, type => $types{integer},
@ -3497,12 +3498,12 @@ our @options = (
{ {
name => 'ZM_WEB_M_SCALE_THUMBS', name => 'ZM_WEB_M_SCALE_THUMBS',
default => 'yes', default => 'yes',
description => 'Scale thumbnails in events, bandwidth versus cpu in rescaling', description => 'Scale thumbnails in events, bandwidth versus CPU in rescaling',
help => q` help => q`
If unset, this option sends the whole image to the browser If unset, this option sends the whole image to the browser
which resizes it in the window. If set the image is scaled down which resizes it in the window. If set the image is scaled down
on the server before sending a reduced size image to the on the server before sending a reduced size image to the
browser to conserve bandwidth at the cost of cpu on the server. browser to conserve bandwidth at the cost of CPU on the server.
Note that ZM can only perform the resizing if the appropriate Note that ZM can only perform the resizing if the appropriate
PHP graphics functionality is installed. This is usually PHP graphics functionality is installed. This is usually
available in the php-gd package. available in the php-gd package.
@ -3536,7 +3537,7 @@ our @options = (
help => q` help => q`
When viewing events an event navigation panel and progress bar When viewing events an event navigation panel and progress bar
is shown below the event itself. This allows you to jump to is shown below the event itself. This allows you to jump to
specific points in the event, but can can also dynamically specific points in the event, but can also dynamically
update to display the current progress of the event replay update to display the current progress of the event replay
itself. This progress is calculated from the actual event itself. This progress is calculated from the actual event
duration and is not directly linked to the replay itself, so on duration and is not directly linked to the replay itself, so on
@ -3640,7 +3641,7 @@ our @options = (
description => 'How often (in seconds) the event listing is refreshed in the watch window', description => 'How often (in seconds) the event listing is refreshed in the watch window',
help => q` help => q`
The monitor window is actually made from several frames. The The monitor window is actually made from several frames. The
lower framme contains a listing of the last few events for easy lower frame contains a listing of the last few events for easy
access. This option determines how often this is refreshed. access. This option determines how often this is refreshed.
`, `,
type => $types{integer}, type => $types{integer},
@ -3769,12 +3770,12 @@ our @options = (
{ {
name => 'ZM_WEB_L_SCALE_THUMBS', name => 'ZM_WEB_L_SCALE_THUMBS',
default => 'yes', default => 'yes',
description => 'Scale thumbnails in events, bandwidth versus cpu in rescaling', description => 'Scale thumbnails in events, bandwidth versus CPU in rescaling',
help => q` help => q`
If unset, this option sends the whole image to the browser If unset, this option sends the whole image to the browser
which resizes it in the window. If set the image is scaled down which resizes it in the window. If set the image is scaled down
on the server before sending a reduced size image to the on the server before sending a reduced size image to the
browser to conserve bandwidth at the cost of cpu on the server. browser to conserve bandwidth at the cost of CPU on the server.
Note that ZM can only perform the resizing if the appropriate Note that ZM can only perform the resizing if the appropriate
PHP graphics functionality is installed. This is usually PHP graphics functionality is installed. This is usually
available in the php-gd package. available in the php-gd package.
@ -3808,7 +3809,7 @@ our @options = (
help => q` help => q`
When viewing events an event navigation panel and progress bar When viewing events an event navigation panel and progress bar
is shown below the event itself. This allows you to jump to is shown below the event itself. This allows you to jump to
specific points in the event, but can can also dynamically specific points in the event, but can also dynamically
update to display the current progress of the event replay update to display the current progress of the event replay
itself. This progress is calculated from the actual event itself. This progress is calculated from the actual event
duration and is not directly linked to the replay itself, so on duration and is not directly linked to the replay itself, so on
@ -4000,7 +4001,7 @@ saveConfigToDB();
The ZoneMinder:ConfigData module contains the master definition of the The ZoneMinder:ConfigData module contains the master definition of the
ZoneMinder configuration options as well as helper methods. This module is ZoneMinder configuration options as well as helper methods. This module is
intended for specialist confguration management and would not normally be intended for specialist configuration management and would not normally be
used by end users. used by end users.
The configuration held in this module, which was previously in zmconfig.pl, The configuration held in this module, which was previously in zmconfig.pl,

View File

@ -189,7 +189,7 @@ sub moveAbs ## Up, Down, Left, Right, etc. ??? Doesn't make sense here...
my $tilt_degrees = shift || 0; my $tilt_degrees = shift || 0;
my $speed = shift || 1; my $speed = shift || 1;
Debug( "Move ABS" ); Debug( "Move ABS" );
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan_degress.'&arg2='.$tilt_degrees.'&arg3=0&arg4='.$speed ); $self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan_degrees.'&arg2='.$tilt_degrees.'&arg3=0&arg4='.$speed );
} }
sub moveConUp sub moveConUp

View File

@ -381,6 +381,22 @@ sub focusRelFar
Debug("focusRelFar response: " . $response); 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 sub moveStop
{ {
my $self = shift; 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 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. 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 =head2 moveStop
This method attempts to stop the camera. The problem is that if continuous This method attempts to stop the camera. The problem is that if continuous

View File

@ -28,6 +28,9 @@ package ZoneMinder::Control::Netcat;
use 5.006; use 5.006;
use strict; use strict;
use warnings; use warnings;
use MIME::Base64;
use Digest::SHA;
use DateTime;
require ZoneMinder::Base; require ZoneMinder::Base;
require ZoneMinder::Control; require ZoneMinder::Control;
@ -36,6 +39,8 @@ our @ISA = qw(ZoneMinder::Control);
our %CamParams = (); our %CamParams = ();
our ($profileToken, $address, $port, %identity);
# ========================================================================== # ==========================================================================
# #
# Netcat IP Control Protocol # Netcat IP Control Protocol
@ -50,7 +55,6 @@ our %CamParams = ();
# #
# #
# Possible future improvements (for anyone to improve upon): # Possible future improvements (for anyone to improve upon):
# - Onvif authentication
# - Build the SOAP commands at runtime rather than use templates # - Build the SOAP commands at runtime rather than use templates
# - Implement previously mentioned advanced features # - Implement previously mentioned advanced features
# #
@ -58,9 +62,10 @@ our %CamParams = ();
# more dependencies to ZoneMinder is always a concern. # more dependencies to ZoneMinder is always a concern.
# #
# On ControlAddress use the format : # On ControlAddress use the format :
# ADDRESS:PORT # [USERNAME:PASSWORD@]ADDRESS:PORT
# eg : 10.1.2.1:8899 # eg : 10.1.2.1:8899
# 10.0.100.1:8899 # 10.0.100.1:8899
# username:password@10.0.100.1:8899
# #
# Use port 8899 for the Netcat camera # Use port 8899 for the Netcat camera
# #
@ -79,6 +84,11 @@ sub open {
$self->loadMonitor(); $self->loadMonitor();
$profileToken = $self->{Monitor}->{ControlDevice};
if ($profileToken eq '') { $profileToken = '000'; }
parseControlAddress($self->{Monitor}->{ControlAddress});
use LWP::UserAgent; use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new; $self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION); $self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION);
@ -86,6 +96,39 @@ sub open {
$self->{state} = '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 { sub printMsg {
my $self = shift; my $self = shift;
my $msg = shift; my $msg = shift;
@ -103,10 +146,10 @@ sub sendCmd {
printMsg($cmd, 'Tx'); 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); my $req = HTTP::Request->new(POST => $server_endpoint);
$req->header('content-type' => $content_type); $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('content-length' => length($msg));
$req->header('accept-encoding' => 'gzip, deflate'); $req->header('accept-encoding' => 'gzip, deflate');
$req->header('connection' => 'Close'); $req->header('connection' => 'Close');
@ -125,10 +168,10 @@ sub sendCmd {
sub getCamParams { sub getCamParams {
my $self = shift; 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 $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); 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('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('content-length' => length($msg));
$req->header('accept-encoding' => 'gzip, deflate'); $req->header('accept-encoding' => 'gzip, deflate');
$req->header('connection' => 'Close'); $req->header('connection' => 'Close');
@ -160,7 +203,7 @@ sub autoStop {
if ( $autostop ) { if ( $autostop ) {
Debug('Auto Stop'); Debug('Auto Stop');
my $cmd = 'onvif/PTZ'; 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"'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
usleep($autostop); usleep($autostop);
$self->sendCmd($cmd, $msg, $content_type); $self->sendCmd($cmd, $msg, $content_type);
@ -172,7 +215,7 @@ sub reset {
Debug('Camera Reset'); Debug('Camera Reset');
my $self = shift; my $self = shift;
my $cmd = ''; 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"'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"';
$self->sendCmd($cmd, $msg, $content_type); $self->sendCmd($cmd, $msg, $content_type);
} }
@ -182,7 +225,7 @@ sub moveConUp {
Debug('Move Up'); Debug('Move Up');
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; 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"'; 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->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout}); $self->autoStop($self->{Monitor}->{AutoStopTimeout});
@ -193,7 +236,7 @@ sub moveConDown {
Debug('Move Down'); Debug('Move Down');
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; 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"'; 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->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout}); $self->autoStop($self->{Monitor}->{AutoStopTimeout});
@ -204,7 +247,7 @@ sub moveConLeft {
Debug('Move Left'); Debug('Move Left');
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; 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"'; 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->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout}); $self->autoStop($self->{Monitor}->{AutoStopTimeout});
@ -215,7 +258,7 @@ sub moveConRight {
Debug('Move Right'); Debug('Move Right');
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; 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"'; 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->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout}); $self->autoStop($self->{Monitor}->{AutoStopTimeout});
@ -226,7 +269,7 @@ sub zoomConTele {
Debug('Zoom Tele'); Debug('Zoom Tele');
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; 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"'; 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->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout}); $self->autoStop($self->{Monitor}->{AutoStopTimeout});
@ -237,7 +280,7 @@ sub zoomConWide {
Debug('Zoom Wide'); Debug('Zoom Wide');
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; 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"'; 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->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout}); $self->autoStop($self->{Monitor}->{AutoStopTimeout});
@ -249,7 +292,7 @@ sub moveConUpRight {
Debug('Move Diagonally Up Right'); Debug('Move Diagonally Up Right');
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; 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"'; 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->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout}); $self->autoStop($self->{Monitor}->{AutoStopTimeout});
@ -261,7 +304,7 @@ sub moveConDownRight {
Debug('Move Diagonally Down Right'); Debug('Move Diagonally Down Right');
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; 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"'; 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->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout}); $self->autoStop($self->{Monitor}->{AutoStopTimeout});
@ -273,7 +316,7 @@ sub moveConUpLeft {
Debug('Move Diagonally Up Left'); Debug('Move Diagonally Up Left');
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; 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"'; 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->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout}); $self->autoStop($self->{Monitor}->{AutoStopTimeout});
@ -285,7 +328,7 @@ sub moveConDownLeft {
Debug('Move Diagonally Down Left'); Debug('Move Diagonally Down Left');
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; 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"'; 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->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout}); $self->autoStop($self->{Monitor}->{AutoStopTimeout});
@ -296,7 +339,7 @@ sub moveStop {
Debug('Move Stop'); Debug('Move Stop');
my $self = shift; my $self = shift;
my $cmd = 'onvif/PTZ'; 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"'; 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->sendCmd($cmd, $msg, $content_type);
} }
@ -308,7 +351,7 @@ sub presetSet {
my $preset = $self->getParam($params, 'preset'); my $preset = $self->getParam($params, 'preset');
Debug("Set Preset $preset"); Debug("Set Preset $preset");
my $cmd = 'onvif/PTZ'; 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"'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/SetPreset"';
$self->sendCmd($cmd, $msg, $content_type); $self->sendCmd($cmd, $msg, $content_type);
} }
@ -320,7 +363,7 @@ sub presetGoto {
my $preset = $self->getParam($params, 'preset'); my $preset = $self->getParam($params, 'preset');
Debug("Goto Preset $preset"); Debug("Goto Preset $preset");
my $cmd = 'onvif/PTZ'; 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"'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"';
$self->sendCmd( $cmd, $msg, $content_type ); $self->sendCmd( $cmd, $msg, $content_type );
} }
@ -362,7 +405,7 @@ sub irisAbsOpen {
$CamParams{Brightness} = $max if ($CamParams{Brightness} > $max); $CamParams{Brightness} = $max if ($CamParams{Brightness} > $max);
my $cmd = 'onvif/imaging'; 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"'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
$self->sendCmd( $cmd, $msg, $content_type ); $self->sendCmd( $cmd, $msg, $content_type );
} }
@ -381,7 +424,7 @@ sub irisAbsClose
$CamParams{Brightness} = $min if ($CamParams{Brightness} < $min); $CamParams{Brightness} = $min if ($CamParams{Brightness} < $min);
my $cmd = 'onvif/imaging'; 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"'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
$self->sendCmd( $cmd, $msg, $content_type ); $self->sendCmd( $cmd, $msg, $content_type );
} }
@ -399,7 +442,7 @@ sub whiteAbsIn {
$CamParams{Contrast} = $max if ($CamParams{Contrast} > $max); $CamParams{Contrast} = $max if ($CamParams{Contrast} > $max);
my $cmd = 'onvif/imaging'; 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"'; 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); $CamParams{Contrast} = $min if ($CamParams{Contrast} < $min);
my $cmd = 'onvif/imaging'; 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"'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"';
} }

View File

@ -488,21 +488,21 @@ MAIN: while( $loop ) {
my $monitor_links; my $monitor_links;
foreach my $link ( glob('*') ) { foreach my $link ( glob('*') ) {
next if ( !-l $link ); next if !-l $link;
next if ( -e $link ); next if -e $link;
aud_print( "Filesystem monitor link '$link' does not point to valid monitor directory" ); aud_print("Filesystem monitor link '$link' does not point to valid monitor directory");
if ( confirm() ) { if ( confirm() ) {
( $link ) = ( $link =~ /^(.*)$/ ); # De-taint ( $link ) = ( $link =~ /^(.*)$/ ); # De-taint
my $command = qq`rm "$link"`; my $command = qq`rm "$link"`;
executeShellCommand( $command ); executeShellCommand($command);
$cleaned = 1; $cleaned = 1;
} }
} } # end foreach monitor link
} # end foreach Storage Area } # end foreach Storage Area
if ( $cleaned ) { if ( $cleaned ) {
Debug("Events were deleted, starting again."); Debug('Events were deleted, starting again.');
redo MAIN; redo MAIN;
} }
@ -559,8 +559,8 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) {
Warning("Event $$Event{Id} is Archived. Taking no further action on it."); Warning("Event $$Event{Id} is Archived. Taking no further action on it.");
next; next;
} }
if ( ! $Event->StartTime() ) { if ( !$Event->StartTime() ) {
Info("Event $$Event{Id} has no start time."); aud_print("Event $$Event{Id} has no start time.");
if ( confirm() ) { if ( confirm() ) {
$Event->delete(); $Event->delete();
$cleaned = 1; $cleaned = 1;
@ -569,7 +569,7 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) {
} }
if ( ! $Event->EndTime() ) { if ( ! $Event->EndTime() ) {
if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) { if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) {
Info("Event $$Event{Id} has no end time and is $age seconds old. deleting it."); aud_print("Event $$Event{Id} has no end time and is $age seconds old. Deleting it.");
if ( confirm() ) { if ( confirm() ) {
$Event->delete(); $Event->delete();
$cleaned = 1; $cleaned = 1;
@ -578,7 +578,7 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) {
} }
} }
if ( $Event->check_for_in_filesystem() ) { if ( $Event->check_for_in_filesystem() ) {
Debug("Database event $$Event{Id} apparently exists at " . $Event->Path() ); Debug("Database event $$Event{Id} apparently exists at " . $Event->Path());
} else { } else {
if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) { if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) {
aud_print("Database event '$db_monitor/$db_event' does not exist at " . $Event->Path().' in filesystem, deleting'); aud_print("Database event '$db_monitor/$db_event' does not exist at " . $Event->Path().' in filesystem, deleting');
@ -587,14 +587,14 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) {
$cleaned = 1; $cleaned = 1;
} }
} else { } else {
aud_print( "Database event '".$Event->Path()." monitor:$db_monitor event:$db_event' does not exist in filesystem but too young to delete age: $age > MIN $Config{ZM_AUDIT_MIN_AGE}.\n" ); aud_print("Database event '".$Event->Path()." monitor:$db_monitor event:$db_event' does not exist in filesystem but too young to delete age: $age > MIN $Config{ZM_AUDIT_MIN_AGE}.");
} }
} # end if exists in filesystem } # end if exists in filesystem
} else { } else {
Debug("Found fs event for id $db_event, $age seconds old at " . $$fs_events{$db_event}->Path()); Debug("Found fs event for id $db_event, $age seconds old at " . $$fs_events{$db_event}->Path());
my $Event = ZoneMinder::Event->find_one( Id=>$db_event ); my $Event = ZoneMinder::Event->find_one( Id=>$db_event );
if ( $Event and ! $Event->check_for_in_filesystem() ) { if ( $Event and ! $Event->check_for_in_filesystem() ) {
Warning("Not found at " . $Event->Path() . ' was found at ' . $$fs_events{$db_event}->Path() ); Warning('Not found at ' . $Event->Path() . ' was found at ' . $$fs_events{$db_event}->Path());
Warning($Event->to_string()); Warning($Event->to_string());
Warning($$fs_events{$db_event}->to_string()); Warning($$fs_events{$db_event}->to_string());
$$Event{Scheme} = '' if ! defined $$Event{Scheme}; $$Event{Scheme} = '' if ! defined $$Event{Scheme};
@ -622,7 +622,7 @@ EVENT: while ( my ( $db_event, $age ) = each( %$db_events ) ) {
} # foreach db_event } # foreach db_event
} # end foreach db_monitor } # end foreach db_monitor
if ( $cleaned ) { if ( $cleaned ) {
Debug("Have done some cleaning, restarting."); Debug('Have done some cleaning, restarting.');
redo MAIN; redo MAIN;
} }
@ -904,25 +904,25 @@ FROM Frames WHERE EventId=?';
Monitors.MonthEvents = E.MonthEvents, Monitors.MonthEvents = E.MonthEvents,
Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace Monitors.MonthEventDiskSpace = E.MonthEventDiskSpace
`; `;
my $eventcounts_hour_sth = $dbh->prepare_cached( $eventcounts_hour_sql ); my $eventcounts_hour_sth = $dbh->prepare_cached($eventcounts_hour_sql);
my $eventcounts_day_sth = $dbh->prepare_cached( $eventcounts_day_sql ); my $eventcounts_day_sth = $dbh->prepare_cached($eventcounts_day_sql);
my $eventcounts_week_sth = $dbh->prepare_cached( $eventcounts_week_sql ); my $eventcounts_week_sth = $dbh->prepare_cached($eventcounts_week_sql);
my $eventcounts_month_sth = $dbh->prepare_cached( $eventcounts_month_sql ); my $eventcounts_month_sth = $dbh->prepare_cached($eventcounts_month_sql);
$eventcounts_hour_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() ); $eventcounts_hour_sth->execute() or Error("Can't execute: ".$eventcounts_sth->errstr());
$eventcounts_day_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() ); $eventcounts_day_sth->execute() or Error("Can't execute: ".$eventcounts_sth->errstr());
$eventcounts_week_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() ); $eventcounts_week_sth->execute() or Error("Can't execute: ".$eventcounts_sth->errstr());
$eventcounts_month_sth->execute( ) or Error( "Can't execute: ".$eventcounts_sth->errstr() ); $eventcounts_month_sth->execute() or Error("Can't execute: ".$eventcounts_sth->errstr());
sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} ) if $continuous; sleep($Config{ZM_AUDIT_CHECK_INTERVAL}) if $continuous;
}; };
Term(); Term();
sub aud_print { sub aud_print {
my $string = shift; my $string = shift;
if ( ! $continuous ) { if ( !$continuous ) {
print( $string ); print($string);
} else { } else {
Info( $string ); Info($string);
} }
} }
@ -932,13 +932,13 @@ sub confirm {
my $yesno = 0; my $yesno = 0;
if ( $report ) { if ( $report ) {
print( "\n" ); print("\n");
} elsif ( $interactive ) { } elsif ( $interactive ) {
print( ", $prompt Y/n/q: " ); print(", $prompt Y/n/q: ");
my $char = <>; my $char = <>;
chomp( $char ); chomp($char);
if ( $char eq 'q' ) { if ( $char eq 'q' ) {
exit( 0 ); exit(0);
} }
if ( !$char ) { if ( !$char ) {
$char = 'y'; $char = 'y';
@ -946,13 +946,13 @@ sub confirm {
$yesno = ( $char =~ /[yY]/ ); $yesno = ( $char =~ /[yY]/ );
} else { } else {
if ( !$continuous ) { if ( !$continuous ) {
print( ", $action\n" ); print(", $action\n");
} else { } else {
Info( $action ); Info($action);
} }
$yesno = 1; $yesno = 1;
} }
return( $yesno ); return $yesno;
} }
sub deleteSwapImage { sub deleteSwapImage {

View File

@ -546,7 +546,7 @@ sub uploadArchFile {
} }
if ( $archError ) { if ( $archError ) {
return( 0 ); return 0;
} else { } else {
if ( $Config{ZM_UPLOAD_PROTOCOL} eq 'ftp' ) { if ( $Config{ZM_UPLOAD_PROTOCOL} eq 'ftp' ) {
Info('Uploading to '.$Config{ZM_UPLOAD_HOST}.' using FTP'); Info('Uploading to '.$Config{ZM_UPLOAD_HOST}.' using FTP');
@ -680,47 +680,58 @@ sub substituteTags {
if ( $first_alarm_frame ) { if ( $first_alarm_frame ) {
$text =~ s/%EPI1%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$first_alarm_frame->{FrameId}/g; $text =~ s/%EPI1%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$first_alarm_frame->{FrameId}/g;
$text =~ s/%EPIM%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$max_alarm_frame->{FrameId}/g; $text =~ s/%EPIM%/$url?view=frame&mid=$Event->{MonitorId}&eid=$Event->{Id}&fid=$max_alarm_frame->{FrameId}/g;
if ( $attachments_ref && $text =~ s/%EI1%//g ) { if ( $attachments_ref ) {
my $path = generateImage($Event, $first_alarm_frame); if ( $text =~ s/%EI1%//g ) {
if ( -e $path ) { my $path = generateImage($Event, $first_alarm_frame);
push @$attachments_ref, { type=>'image/jpeg', path=>$path }; if ( -e $path ) {
push @$attachments_ref, { type=>'image/jpeg', path=>$path };
}
} }
}
if ( $attachments_ref && ( $text =~ s/%EIM%//g ) ) { if ( $text =~ s/%EIM%//g ) {
# Don't attach the same image twice # Don't attach the same image twice
if ( !@$attachments_ref if ( !@$attachments_ref
|| ( $first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} ) || ( $first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} )
) { ) {
my $path = generateImage($Event, $max_alarm_frame); my $path = generateImage($Event, $max_alarm_frame);
if ( -e $path ) {
push @$attachments_ref, { type=>'image/jpeg', path=>$path };
} else {
Warning("No image for EIM");
}
}
}
if ( $text =~ s/%EI1A%//g ) {
my $path = generateImage($Event, $first_alarm_frame, 'analyse');
if ( -e $path ) { if ( -e $path ) {
push @$attachments_ref, { type=>'image/jpeg', path=>$path }; push @$attachments_ref, { type=>'image/jpeg', path=>$path };
} else { } else {
Warning("No image for EIM"); Warning("No image for EI1A");
} }
} }
} if ( $text =~ s/%EIMA%//g ) {
if ( $attachments_ref && $text =~ s/%EI1A%//g ) { # Don't attach the same image twice
my $path = generateImage($Event, $first_alarm_frame, 'analyse'); if ( !@$attachments_ref
if ( -e $path ) {
push @$attachments_ref, { type=>'image/jpeg', path=>$path };
} else {
Warning("No image for EI1A");
}
}
if ( $attachments_ref && $text =~ s/%EIMA%//g ) {
# Don't attach the same image twice
if ( !@$attachments_ref
|| ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId}) || ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId})
) { ) {
my $path = generateImage($Event, $max_alarm_frame, 'analyse'); my $path = generateImage($Event, $max_alarm_frame, 'analyse');
if ( -e $path ) {
push @$attachments_ref, { type=>'image/jpeg', path=>$path };
} else {
Warning('No image for EIMA');
}
}
}
if ( $text =~ s/%EIMOD%//g ) {
my $path = $Event->Path().'/objdetect.jpg';
if ( -e $path ) { if ( -e $path ) {
push @$attachments_ref, { type=>'image/jpeg', path=>$path }; push @$attachments_ref, { type=>'image/jpeg', path=>$path };
} else { } else {
Warning('No image for EIMA'); Warning('No image for EIMOD at ' . $path);
} }
} }
}
} # end if attachments_ref
} # end if $first_alarm_frame } # end if $first_alarm_frame
if ( $attachments_ref ) { if ( $attachments_ref ) {
@ -732,7 +743,7 @@ sub substituteTags {
if ( !$format ) { if ( !$format ) {
return undef; return undef;
} }
push( @$attachments_ref, { type=>"video/$format", path=>$path } ); push @$attachments_ref, { type=>"video/$format", path=>$path };
} }
} }
if ( $text =~ s/%EVM%//g ) { if ( $text =~ s/%EVM%//g ) {
@ -806,7 +817,7 @@ sub sendEmail {
$ssmtp_location = qx('which ssmtp'); $ssmtp_location = qx('which ssmtp');
} }
if ( !$ssmtp_location ) { if ( !$ssmtp_location ) {
Debug('Unable to find ssmtp, trying MIME::Lite->send'); Warning('Unable to find ssmtp, trying MIME::Lite->send');
MIME::Lite->send('smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60); MIME::Lite->send('smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60);
$mail->send(); $mail->send();
} else { } else {

View File

@ -583,3 +583,26 @@ void zm_free_codec( AVCodecContext **ctx ) {
} // end if } // end if
} }
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);
}

View File

@ -339,6 +339,7 @@ bool is_audio_context(AVCodec *);
int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet ); int zm_receive_frame( AVCodecContext *context, AVFrame *frame, AVPacket &packet );
void dumpPacket(AVStream *, AVPacket *,const char *text=""); void dumpPacket(AVStream *, AVPacket *,const char *text="");
void dumpPacket(AVPacket *,const char *text="");
#ifndef HAVE_LIBSWRESAMPLE #ifndef HAVE_LIBSWRESAMPLE
#ifdef HAVE_LIBAVRESAMPLE #ifdef HAVE_LIBAVRESAMPLE
#define av_opt_set_channel_layout(ctx, setting, value, 0) av_opt_set_int(ctx, setting, value, 0); #define av_opt_set_channel_layout(ctx, setting, value, 0) av_opt_set_int(ctx, setting, value, 0);

View File

@ -1074,16 +1074,16 @@ Debug(3, "GetImage");
ZMPacket *snap = &image_buffer[index]; ZMPacket *snap = &image_buffer[index];
Image *snap_image = snap->image; Image *snap_image = snap->image;
alarm_image.Assign( *snap_image ); alarm_image.Assign(*snap_image);
//write_image.Assign( *snap_image ); //write_image.Assign( *snap_image );
if ( scale != ZM_SCALE_BASE ) { if ( scale != ZM_SCALE_BASE ) {
alarm_image.Scale( scale ); alarm_image.Scale(scale);
} }
if ( !config.timestamp_on_capture ) { if ( !config.timestamp_on_capture ) {
TimestampImage( &alarm_image, snap->timestamp ); TimestampImage(&alarm_image, snap->timestamp);
} }
image = &alarm_image; image = &alarm_image;
} else { } else {
@ -1091,10 +1091,10 @@ Debug(3, "GetImage");
} }
static char filename[PATH_MAX]; static char filename[PATH_MAX];
snprintf( filename, sizeof(filename), "Monitor%d.jpg", id ); snprintf(filename, sizeof(filename), "Monitor%d.jpg", id);
image->WriteJpeg(filename); image->WriteJpeg(filename);
} else { } else {
Error( "Unable to generate image, no images in buffer" ); Error("Unable to generate image, no images in buffer");
} }
return 0; return 0;
} }
@ -1769,8 +1769,8 @@ bool Monitor::Analyse() {
cause += ", "; cause += ", ";
cause += LINKED_CAUSE; cause += LINKED_CAUSE;
} }
noteSet.insert(linked_monitors[i]->Name()); } else {
score += 50; linked_monitors[i]->connect();
} }
} // end foreach linked_monitor } // end foreach linked_monitor
if ( noteSet.size() > 0 ) if ( noteSet.size() > 0 )
@ -1908,9 +1908,8 @@ bool Monitor::Analyse() {
} }
} else if ( config.record_event_stats && state == ALARM ) { } else if ( config.record_event_stats && state == ALARM ) {
for ( int i = 0; i < n_zones; i++ ) { for ( int i = 0; i < n_zones; i++ ) {
if ( zones[i]->Alarmed() ) { if ( zones[i]->Alarmed() )
zones[i]->RecordStats( event ); zones[i]->RecordStats(event);
}
} // end foreach zone } // end foreach zone
} // analsys_images or record stats } // analsys_images or record stats

View File

@ -396,10 +396,15 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) {
img_buffer_size = send_image->Size(); img_buffer_size = send_image->Size();
break; break;
case STREAM_ZIP : case STREAM_ZIP :
#if HAVE_ZLIB_H
fputs("Content-Type: image/x-rgbz\r\n",stdout); fputs("Content-Type: image/x-rgbz\r\n",stdout);
unsigned long zip_buffer_size; unsigned long zip_buffer_size;
send_image->Zip(img_buffer, &zip_buffer_size); send_image->Zip(img_buffer, &zip_buffer_size);
img_buffer_size = zip_buffer_size; img_buffer_size = zip_buffer_size;
#else
Error("zlib is required for zipped images. Falling back to raw image");
type = STREAM_RAW;
#endif // HAVE_ZLIB_H
break; break;
default : default :
Error("Unexpected frame type %d", type); Error("Unexpected frame type %d", type);
@ -802,6 +807,8 @@ void MonitorStream::SingleImageRaw( int scale ) {
fwrite( snap_image->Buffer(), snap_image->Size(), 1, stdout ); fwrite( snap_image->Buffer(), snap_image->Size(), 1, stdout );
} }
#ifdef HAVE_ZLIB_H
void MonitorStream::SingleImageZip( int scale ) { void MonitorStream::SingleImageZip( int scale ) {
unsigned long img_buffer_size = 0; unsigned long img_buffer_size = 0;
static Bytef img_buffer[ZM_MAX_IMAGE_SIZE]; static Bytef img_buffer[ZM_MAX_IMAGE_SIZE];
@ -824,3 +831,4 @@ void MonitorStream::SingleImageZip( int scale ) {
fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" ); fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" );
fwrite( img_buffer, img_buffer_size, 1, stdout ); fwrite( img_buffer, img_buffer_size, 1, stdout );
} }
#endif // HAVE_ZLIB_H

View File

@ -55,7 +55,9 @@ class MonitorStream : public StreamBase {
void processCommand( const CmdMsg *msg ); void processCommand( const CmdMsg *msg );
void SingleImage( int scale=100 ); void SingleImage( int scale=100 );
void SingleImageRaw( int scale=100 ); void SingleImageRaw( int scale=100 );
#ifdef HAVE_ZLIB_H
void SingleImageZip( int scale=100 ); void SingleImageZip( int scale=100 );
#endif
public: public:
MonitorStream() : MonitorStream() :

View File

@ -28,7 +28,22 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.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 ), 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 ), rtsp_describe( p_rtsp_describe ),
rtspThread( 0 ) rtspThread( 0 )
@ -175,7 +190,7 @@ int RemoteCameraRtsp::PrimeCapture() {
} else { } else {
Debug(2, "Have another video stream." ); 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 (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
if ( codec_context->codec_type == AVMEDIA_TYPE_AUDIO ) if ( codec_context->codec_type == AVMEDIA_TYPE_AUDIO )
#else #else
@ -187,6 +202,8 @@ int RemoteCameraRtsp::PrimeCapture() {
} else { } else {
Debug(2, "Have another audio stream." ); 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 } // end foreach stream

View File

@ -28,12 +28,12 @@
#include <errno.h> #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; const RtcpPacket *rtcpPacket;
rtcpPacket = (RtcpPacket *)packet; rtcpPacket = (RtcpPacket *)packet;
@ -48,33 +48,24 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
int pt = rtcpPacket->header.pt; int pt = rtcpPacket->header.pt;
int len = ntohs(rtcpPacket->header.lenN); int len = ntohs(rtcpPacket->header.lenN);
Debug( 5, "RTCP Ver: %d", ver ); Debug( 5, "RTCP Ver: %d Count: %d Pt: %d len: %d", ver, count, pt, len);
Debug( 5, "RTCP Count: %d", count );
Debug( 5, "RTCP Pt: %d", pt );
Debug( 5, "RTCP len: %d", len );
switch( pt ) switch( pt ) {
{
case RTCP_SR : case RTCP_SR :
{ {
uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN); uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN);
Debug( 5, "RTCP Got SR (%x)", ssrc ); Debug( 5, "RTCP Got SR (%x)", ssrc );
if ( mRtpSource.getSsrc() ) if ( mRtpSource.getSsrc() ) {
{ if ( ssrc != mRtpSource.getSsrc() ) {
if ( ssrc != mRtpSource.getSsrc() )
{
Warning( "Discarding packet for unrecognised ssrc %x", ssrc ); Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
return( -1 ); return( -1 );
} }
} } else if ( ssrc ) {
else if ( ssrc )
{
mRtpSource.setSsrc( ssrc ); mRtpSource.setSsrc( ssrc );
} }
if ( len > 1 ) if ( len > 1 ) {
{
//printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts ); //printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts );
uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN); uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN);
uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN); 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 : case RTCP_SDES :
{ {
ssize_t contentLen = packetLen - sizeof(rtcpPacket->header); ssize_t contentLen = packetLen - sizeof(rtcpPacket->header);
while ( contentLen ) while ( contentLen ) {
{
Debug( 5, "RTCP CL: %zd", contentLen ); Debug( 5, "RTCP CL: %zd", contentLen );
uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN); uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN);
Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count ); 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 ); Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
return( -1 ); return( -1 );
} }
unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item; 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; RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr;
Debug( 5, "RTCP Item length %d", item->len ); Debug( 5, "RTCP Item length %d", item->len );
switch( item->type ) switch( item->type ) {
{
case RTCP_SDES_CNAME : case RTCP_SDES_CNAME :
{ {
std::string cname( item->data, item->len ); std::string cname( item->data, item->len );
@ -123,50 +110,39 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
case RTCP_SDES_NOTE : case RTCP_SDES_NOTE :
case RTCP_SDES_PRIV : case RTCP_SDES_PRIV :
default : default :
{
Error( "Received unexpected SDES item type %d, ignoring", item->type ); Error( "Received unexpected SDES item type %d, ignoring", item->type );
return( -1 ); return -1;
}
} }
int paddedLen = 4+2+item->len+1; // Add null byte int paddedLen = 4+2+item->len+1; // Add null byte
paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4 paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4
Debug( 5, "RTCP PL:%d", paddedLen ); Debug(5, "RTCP PL:%d", paddedLen);
sdesPtr += paddedLen; sdesPtr += paddedLen;
contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0; contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0;
} }
} } // end whiel contentLen
break; break;
} }
case RTCP_BYE : case RTCP_BYE :
{ Debug(5, "RTCP Got BYE");
Debug( 5, "RTCP Got BYE" );
mStop = true; mStop = true;
break; break;
}
case RTCP_APP : case RTCP_APP :
{
// Ignoring as per RFC 3550 // Ignoring as per RFC 3550
Debug( 5, "Received RTCP_APP packet, ignoring."); Debug(5, "Received RTCP_APP packet, ignoring.");
break; break;
}
case RTCP_RR : case RTCP_RR :
{ Error("Received RTCP_RR packet.");
Error( "Received RTCP_RR packet." ); return -1;
return( -1 );
}
default : default :
{
// Ignore unknown packet types. Some cameras do this by design. // Ignore unknown packet types. Some cameras do this by design.
Debug( 5, "Received unexpected packet type %d, ignoring", pt ); Debug(5, "Received unexpected packet type %d, ignoring", pt);
break; break;
}
} }
consumed = sizeof(uint32_t)*(len+1); 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; RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]); 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(); mRtpSource.updateRtcpStats();
Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 ); Debug(5, "Ssrc = %d Ssrc_1 = %d Last Seq = %d Jitter = %d Last SR = %d",
Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() ); mRtspThread.getSsrc()+1,
Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() ); mRtpSource.getSsrc(),
Debug( 5, "Jitter = %d", mRtpSource.getJitter() ); mRtpSource.getMaxSeq(),
Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() ); mRtpSource.getJitter(),
mRtpSource.getLastSrTimestamp()
);
rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1); rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1);
rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc()); 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].lsrN = htonl(mRtpSource.getLastSrTimestamp());
rtcpPacket->body.rr.rr[0].dlsrN = 0; 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; RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
const std::string &cname = mRtpSource.getCname(); 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(); rtcpPacket->body.sdes.item[0].len = cname.size();
memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), 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; RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]); 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()); 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; unsigned char *bufferPtr = buffer;
// u_int32 len; /* length of compound RTCP packet in words */ // 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 */ // /* something wrong with packet format */
// } // }
while ( nBytes > 0 ) while ( nBytes > 0 ) {
{
int consumed = recvPacket( bufferPtr, nBytes ); int consumed = recvPacket( bufferPtr, nBytes );
if ( consumed <= 0 ) if ( consumed <= 0 )
break; break;
bufferPtr += consumed; bufferPtr += consumed;
nBytes -= 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() ); Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() );
SockAddrInet localAddr, remoteAddr; SockAddrInet localAddr, remoteAddr;
bool sendReports; bool sendReports;
UdpInetSocket rtpCtrlServer; UdpInetSocket rtpCtrlServer;
if ( mRtpSource.getLocalHost() != "" ) if ( mRtpSource.getLocalHost() != "" ) {
{
if ( !rtpCtrlServer.bind( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ) ) if ( !rtpCtrlServer.bind( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ) )
Fatal( "Failed to bind RTCP server" ); Fatal( "Failed to bind RTCP server" );
sendReports = false; sendReports = false;
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); 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() ) ) if ( !rtpCtrlServer.bind( mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0", mRtpSource.getLocalCtrlPort() ) )
Fatal( "Failed to bind RTCP server" ); Fatal( "Failed to bind RTCP server" );
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
@ -309,17 +279,17 @@ int RtpCtrlThread::run()
time_t now = time(NULL); time_t now = time(NULL);
Select::CommsList readable = select.getReadable(); Select::CommsList readable = select.getReadable();
if ( readable.size() == 0 ) if ( readable.size() == 0 ) {
{
if ( ! timeout ) { if ( ! timeout ) {
// With this code here, we will send an SDES and RR packet every 10 seconds // With this code here, we will send an SDES and RR packet every 10 seconds
ssize_t nBytes; ssize_t nBytes;
unsigned char *bufferPtr = buffer; unsigned char *bufferPtr = buffer;
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
bufferPtr += generateSdes( 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",
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 ) bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
Error( "Unable to send: %s", strerror( errno ) ); if ( (nBytes = rtpCtrlServer.send(buffer, bufferPtr-buffer)) < 0 )
Error("Unable to send: %s", strerror(errno));
timeout = true; timeout = true;
continue; continue;
} else { } else {
@ -332,25 +302,21 @@ int RtpCtrlThread::run()
timeout = false; timeout = false;
last_receive = time(NULL); last_receive = time(NULL);
} }
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter ) for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter ) {
{ if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) ) {
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) )
{
ssize_t nBytes = socket->recv( buffer, sizeof(buffer) ); ssize_t nBytes = socket->recv( buffer, sizeof(buffer) );
Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() ); Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() );
if ( nBytes ) if ( nBytes ) {
{
recvPackets( buffer, nBytes ); recvPackets( buffer, nBytes );
if ( sendReports ) if ( sendReports ) {
{
unsigned char *bufferPtr = buffer; unsigned char *bufferPtr = buffer;
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() ); Debug(3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc());
if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 ) if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 )
Error( "Unable to send: %s", strerror( errno ) ); Error("Unable to send: %s", strerror(errno));
//Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() ); //Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() );
} }
} else { } else {
@ -358,16 +324,14 @@ int RtpCtrlThread::run()
mStop = true; mStop = true;
break; break;
} }
} } else {
else Panic("Barfed");
{ } // end if socket
Panic( "Barfed" ); } // end foeach comms iterator
}
}
} }
rtpCtrlServer.close(); rtpCtrlServer.close();
mRtspThread.stop(); mRtspThread.stop();
return( 0 ); return 0;
} }
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT

View File

@ -34,13 +34,11 @@
class RtspThread; class RtspThread;
class RtpSource; class RtpSource;
class RtpCtrlThread : public Thread class RtpCtrlThread : public Thread {
{
friend class RtspThread; friend class RtspThread;
private: private:
typedef enum typedef enum {
{
RTCP_SR = 200, RTCP_SR = 200,
RTCP_RR = 201, RTCP_RR = 201,
RTCP_SDES = 202, RTCP_SDES = 202,
@ -48,8 +46,7 @@ private:
RTCP_APP = 204 RTCP_APP = 204
} RtcpType; } RtcpType;
typedef enum typedef enum {
{
RTCP_SDES_END = 0, RTCP_SDES_END = 0,
RTCP_SDES_CNAME = 1, RTCP_SDES_CNAME = 1,
RTCP_SDES_NAME = 2, RTCP_SDES_NAME = 2,
@ -61,8 +58,7 @@ private:
RTCP_SDES_PRIV = 8 RTCP_SDES_PRIV = 8
} RtcpSdesType; } RtcpSdesType;
struct RtcpCommonHeader struct RtcpCommonHeader {
{
uint8_t count:5; // varies by packet type uint8_t count:5; // varies by packet type
uint8_t p:1; // padding flag uint8_t p:1; // padding flag
uint8_t version:2; // protocol version uint8_t version:2; // protocol version
@ -71,8 +67,7 @@ private:
}; };
// Reception report block // Reception report block
struct RtcpRr struct RtcpRr {
{
uint32_t ssrcN; // data source being reported uint32_t ssrcN; // data source being reported
int32_t lost:24; // cumul. no. pkts lost (signed!) int32_t lost:24; // cumul. no. pkts lost (signed!)
uint32_t fraction:8; // fraction lost since last SR/RR uint32_t fraction:8; // fraction lost since last SR/RR
@ -83,22 +78,18 @@ private:
}; };
// SDES item // SDES item
struct RtcpSdesItem struct RtcpSdesItem {
{
uint8_t type; // type of item (rtcp_sdes_type_t) uint8_t type; // type of item (rtcp_sdes_type_t)
uint8_t len; // length of item (in octets) uint8_t len; // length of item (in octets)
char data[]; // text, not null-terminated char data[]; // text, not null-terminated
}; };
// RTCP packet // RTCP packet
struct RtcpPacket struct RtcpPacket {
{
RtcpCommonHeader header; // common header RtcpCommonHeader header; // common header
union union {
{
// Sender Report (SR) // Sender Report (SR)
struct Sr struct Sr {
{
uint32_t ssrcN; // sender generating this report, network order uint32_t ssrcN; // sender generating this report, network order
uint32_t ntpSecN; // NTP timestamp, network order uint32_t ntpSecN; // NTP timestamp, network order
uint32_t ntpFracN; uint32_t ntpFracN;
@ -109,22 +100,19 @@ private:
} sr; } sr;
// Reception Report (RR) // Reception Report (RR)
struct Rr struct Rr {
{
uint32_t ssrcN; // receiver generating this report uint32_t ssrcN; // receiver generating this report
RtcpRr rr[]; // variable-length list RtcpRr rr[]; // variable-length list
} rr; } rr;
// source description (SDES) // source description (SDES)
struct Sdes struct Sdes {
{
uint32_t srcN; // first SSRC/CSRC uint32_t srcN; // first SSRC/CSRC
RtcpSdesItem item[]; // list of SDES items RtcpSdesItem item[]; // list of SDES items
} sdes; } sdes;
// BYE // BYE
struct struct {
{
uint32_t srcN[]; // list of sources uint32_t srcN[]; // list of sources
// can't express trailing text for reason (what does this mean? it's not even english!) // can't express trailing text for reason (what does this mean? it's not even english!)
} bye; } bye;
@ -148,8 +136,7 @@ private:
public: public:
RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ); RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource );
void stop() void stop() {
{
mStop = true; mStop = true;
} }
}; };

View File

@ -26,7 +26,17 @@
#if HAVE_LIBAVCODEC #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 ), mId( id ),
mSsrc( ssrc ), mSsrc( ssrc ),
mLocalHost( localHost ), mLocalHost( localHost ),
@ -65,13 +75,12 @@ RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, c
mLastSrTimeNtp = tvZero(); mLastSrTimeNtp = tvZero();
mLastSrTimeRtp = 0; mLastSrTimeRtp = 0;
if(mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4) 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");
Debug( 3, "Initialising sequence" );
mBaseSeq = seq; mBaseSeq = seq;
mMaxSeq = seq; mMaxSeq = seq;
mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false
@ -84,77 +93,58 @@ void RtpSource::init( uint16_t seq )
mTransit = 0; mTransit = 0;
} }
bool RtpSource::updateSeq( uint16_t seq ) bool RtpSource::updateSeq( uint16_t seq ) {
{
uint16_t uDelta = seq - mMaxSeq; uint16_t uDelta = seq - mMaxSeq;
// Source is not valid until MIN_SEQUENTIAL packets with // Source is not valid until MIN_SEQUENTIAL packets with
// sequential sequence numbers have been received. // sequential sequence numbers have been received.
Debug( 5, "Seq: %d", seq ); Debug( 5, "Seq: %d", seq );
if ( mProbation) if ( mProbation) {
{
// packet is in sequence // packet is in sequence
if ( seq == mMaxSeq + 1) if ( seq == mMaxSeq + 1) {
{
Debug( 3, "Sequence in probation %d, in sequence", mProbation ); Debug( 3, "Sequence in probation %d, in sequence", mProbation );
mProbation--; mProbation--;
mMaxSeq = seq; mMaxSeq = seq;
if ( mProbation == 0 ) if ( mProbation == 0 ) {
{
init( seq ); init( seq );
mReceivedPackets++; mReceivedPackets++;
return( true ); return( true );
} }
} } else {
else
{
Warning( "Sequence in probation %d, out of sequence", mProbation ); Warning( "Sequence in probation %d, out of sequence", mProbation );
mProbation = MIN_SEQUENTIAL - 1; mProbation = MIN_SEQUENTIAL - 1;
mMaxSeq = seq; mMaxSeq = seq;
return( false ); return( false );
} }
return( true ); return( true );
} } else if ( uDelta < MAX_DROPOUT ) {
else if ( uDelta < MAX_DROPOUT ) if ( uDelta == 1 ) {
{
if ( uDelta == 1 )
{
Debug( 4, "Packet in sequence, gap %d", uDelta ); Debug( 4, "Packet in sequence, gap %d", uDelta );
} } else {
else
{
Warning( "Packet in sequence, gap %d", uDelta ); Warning( "Packet in sequence, gap %d", uDelta );
} }
// in order, with permissible gap // in order, with permissible gap
if ( seq < mMaxSeq ) if ( seq < mMaxSeq ) {
{
// Sequence number wrapped - count another 64K cycle. // Sequence number wrapped - count another 64K cycle.
mCycles += RTP_SEQ_MOD; mCycles += RTP_SEQ_MOD;
} }
mMaxSeq = seq; 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 ); Warning( "Packet out of sequence, gap %d", uDelta );
// the sequence number made a very large jump // the sequence number made a very large jump
if ( seq == mBadSeq ) if ( seq == mBadSeq ) {
{
Debug( 3, "Restarting sequence" ); Debug( 3, "Restarting sequence" );
// Two sequential packets -- assume that the other side // Two sequential packets -- assume that the other side
// restarted without telling us so just re-sync // restarted without telling us so just re-sync
// (i.e., pretend this was the first packet). // (i.e., pretend this was the first packet).
init( seq ); init( seq );
} } else {
else
{
mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1); mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1);
return( false ); return( false );
} }
} } else {
else
{
Warning( "Packet duplicate or reordered, gap %d", uDelta ); Warning( "Packet duplicate or reordered, gap %d", uDelta );
// duplicate or reordered packet // duplicate or reordered packet
return( false ); return( false );
@ -163,10 +153,8 @@ bool RtpSource::updateSeq( uint16_t seq )
return( uDelta==1?true:false ); return( uDelta==1?true:false );
} }
void RtpSource::updateJitter( const RtpDataHeader *header ) void RtpSource::updateJitter( const RtpDataHeader *header ) {
{ if ( mRtpFactor > 0 ) {
if ( mRtpFactor > 0 )
{
Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) ); Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) );
uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor ); uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor );
Debug( 5, "Local RTP time = %x", localTimeRtp ); Debug( 5, "Local RTP time = %x", localTimeRtp );
@ -174,8 +162,7 @@ void RtpSource::updateJitter( const RtpDataHeader *header )
uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN); uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN);
Debug( 5, "Packet transit RTP time = %x", packetTransit ); Debug( 5, "Packet transit RTP time = %x", packetTransit );
if ( mTransit > 0 ) if ( mTransit > 0 ) {
{
// Jitter // Jitter
int d = packetTransit - mTransit; int d = packetTransit - mTransit;
Debug( 5, "Jitter D = %d", d ); Debug( 5, "Jitter D = %d", d );
@ -185,28 +172,22 @@ void RtpSource::updateJitter( const RtpDataHeader *header )
mJitter += d - ((mJitter + 8) >> 4); mJitter += d - ((mJitter + 8) >> 4);
} }
mTransit = packetTransit; mTransit = packetTransit;
} } else {
else
{
mJitter = 0; mJitter = 0;
} }
Debug( 5, "RTP Jitter: %d", mJitter ); 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)) ); 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 ); 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(); mBaseTimeReal = tvNow();
mBaseTimeNtp = ntpTime; mBaseTimeNtp = ntpTime;
mBaseTimeRtp = rtpTime; mBaseTimeRtp = rtpTime;
} } else if ( !mRtpClock ) {
else if ( !mRtpClock )
{
Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime ); 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 ); 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; mLastSrTimeRtp = rtpTime;
} }
void RtpSource::updateRtcpStats() void RtpSource::updateRtcpStats() {
{
uint32_t extendedMax = mCycles + mMaxSeq; uint32_t extendedMax = mCycles + mMaxSeq;
mExpectedPackets = extendedMax - mBaseSeq + 1; mExpectedPackets = extendedMax - mBaseSeq + 1;
@ -255,8 +235,7 @@ void RtpSource::updateRtcpStats()
Debug( 5, "Lost fraction = %d", mLostFraction ); 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; const RtpDataHeader *rtpHeader;
rtpHeader = (RtpDataHeader *)packet; rtpHeader = (RtpDataHeader *)packet;
int rtpHeaderSize = 12 + rtpHeader->cc * 4; 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. // that there is no marker bit by changing the number of bits in the payload type field.
bool thisM = rtpHeader->m || h264FragmentEnd; bool thisM = rtpHeader->m || h264FragmentEnd;
if ( updateSeq( ntohs(rtpHeader->seqN) ) ) if ( updateSeq( ntohs(rtpHeader->seqN) ) ) {
{
Hexdump( 4, packet+rtpHeaderSize, 16 ); Hexdump( 4, packet+rtpHeaderSize, 16 );
if ( mFrameGood ) if ( mFrameGood ) {
{
int extraHeader = 0; int extraHeader = 0;
if( mCodecId == AV_CODEC_ID_H264 ) if ( mCodecId == AV_CODEC_ID_H264 ) {
{
int nalType = (packet[rtpHeaderSize] & 0x1f); int nalType = (packet[rtpHeaderSize] & 0x1f);
Debug( 3, "Have H264 frame: nal type is %d", nalType ); Debug( 3, "Have H264 frame: nal type is %d", nalType );
switch (nalType) switch (nalType) {
{
case 24: // STAP-A case 24: // STAP-A
{
extraHeader = 2; extraHeader = 2;
break; break;
}
case 25: // STAP-B case 25: // STAP-B
case 26: // MTAP-16 case 26: // MTAP-16
case 27: // MTAP-24 case 27: // MTAP-24
{
extraHeader = 3; extraHeader = 3;
break; break;
}
// FU-A and FU-B // FU-A and FU-B
case 28: case 29: case 28: case 29:
{
// Is this NAL the first NAL in fragmentation sequence // 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 // Now we will form new header of frame
mFrame.append( "\x0\x0\x1\x0", 4 ); mFrame.append( "\x0\x0\x1\x0", 4 );
// Reconstruct NAL header from FU headers // Reconstruct NAL header from FU headers
@ -311,17 +280,14 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
extraHeader = 2; extraHeader = 2;
break; break;
}
default: default:
{
Debug(3, "Unhandled nalType %d", nalType ); Debug(3, "Unhandled nalType %d", nalType );
}
} }
// Append NAL frame start code // Append NAL frame start code
if ( !mFrame.size() ) if ( !mFrame.size() )
mFrame.append( "\x0\x0\x1", 3 ); mFrame.append( "\x0\x0\x1", 3 );
} } // end if H264
mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader ); mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader );
} else { } else {
Debug( 3, "NOT H264 frame: type is %d", mCodecId ); 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 ); Hexdump( 4, mFrame.head(), 16 );
if ( thisM ) if ( thisM ) {
{ if ( mFrameGood ) {
if ( mFrameGood )
{
Debug( 3, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() ); Debug( 3, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() );
mFrameProcessed.setValueImmediate( false ); mFrameProcessed.setValueImmediate( false );
mFrameReady.updateValueSignal( true ); 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 // 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; // 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 ); return( false );
} }
mFrameCount++; mFrameCount++;
} } else {
else
{
Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() ); Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() );
} }
mFrame.clear(); mFrame.clear();
} }
} } else {
else if ( mFrame.size() ) {
{
if ( mFrame.size() )
{
Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() ); Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() );
} } else {
else
{
Warning( "Discarding frame %d", mFrameCount ); Warning( "Discarding frame %d", mFrameCount );
} }
mFrameGood = false; mFrameGood = false;
mFrame.clear(); mFrame.clear();
} }
if ( thisM ) if ( thisM ) {
{
mFrameGood = true; mFrameGood = true;
prevM = true; prevM = true;
} } else
else
prevM = false; prevM = false;
updateJitter( rtpHeader ); updateJitter( rtpHeader );
return( true ); return true;
} }
bool RtpSource::getFrame( Buffer &buffer ) bool RtpSource::getFrame( Buffer &buffer ) {
{
Debug( 3, "Getting frame" ); Debug( 3, "Getting frame" );
if ( !mFrameReady.getValueImmediate() ) if ( !mFrameReady.getValueImmediate() ) {
{
// Allow for a couple of spurious returns // Allow for a couple of spurious returns
for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ ) for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ )
if ( count > 1 ) if ( count > 1 )
@ -395,7 +347,7 @@ bool RtpSource::getFrame( Buffer &buffer )
mFrameReady.setValueImmediate( false ); mFrameReady.setValueImmediate( false );
mFrameProcessed.updateValueSignal( true ); mFrameProcessed.updateValueSignal( true );
Debug( 4, "Copied %d bytes", buffer.size() ); Debug( 4, "Copied %d bytes", buffer.size() );
return( true ); return true;
} }
#endif // HAVE_LIBAVCODEC #endif // HAVE_LIBAVCODEC

View File

@ -46,53 +46,54 @@ bool RtspThread::sendCommand( std::string message ) {
message += stringtf( "CSeq: %d\r\n\r\n", ++mSeq ); message += stringtf( "CSeq: %d\r\n\r\n", ++mSeq );
Debug( 2, "Sending RTSP message: %s", message.c_str() ); Debug( 2, "Sending RTSP message: %s", message.c_str() );
if ( mMethod == RTP_RTSP_HTTP ) { if ( mMethod == RTP_RTSP_HTTP ) {
message = base64Encode( message ); message = base64Encode(message);
Debug( 2, "Sending encoded RTSP message: %s", message.c_str() ); Debug(2, "Sending encoded RTSP message: %s", message.c_str());
if ( mRtspSocket2.send( message.c_str(), message.size() ) != (int)message.length() ) { if ( mRtspSocket2.send(message.c_str(), message.size()) != (int)message.length() ) {
Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); Error("Unable to send message '%s': %s", message.c_str(), strerror(errno));
return( false ); return false;
} }
} else { } else {
if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) { if ( mRtspSocket.send(message.c_str(), message.size()) != (int)message.length() ) {
Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); 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 ) { bool RtspThread::recvResponse( std::string &response ) {
if ( mRtspSocket.recv( response ) < 0 ) if ( mRtspSocket.recv( response ) < 0 )
Error( "Recv failed; %s", strerror(errno) ); Error("Recv failed; %s", strerror(errno));
Debug( 2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size() ); Debug(2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size());
float respVer = 0; float respVer = 0;
respCode = -1; respCode = -1;
char respText[ZM_NETWORK_BUFSIZ]; char respText[ZM_NETWORK_BUFSIZ];
if ( sscanf( response.c_str(), "RTSP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 ) { if ( sscanf(response.c_str(), "RTSP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText) != 3 ) {
if ( isalnum(response[0]) ) { if ( isalnum(response[0]) ) {
Error( "Response parse failure in '%s'", response.c_str() ); Error("Response parse failure in '%s'", response.c_str());
} else { } else {
Error( "Response parse failure, %zd bytes follow", response.size() ); Error("Response parse failure, %zd bytes follow", response.size());
if ( response.size() ) if ( response.size() )
Hexdump( Logger::ERROR, response.data(), min(response.size(),16) ); Hexdump( Logger::ERROR, response.data(), min(response.size(),16) );
} }
return( false ); return false;
} }
if ( respCode == 401) { if ( respCode == 401 ) {
Debug( 2, "Got 401 access denied response code, check WWW-Authenticate header and retry"); Debug( 2, "Got 401 access denied response code, check WWW-Authenticate header and retry");
mAuthenticator->checkAuthResponse(response); mAuthenticator->checkAuthResponse(response);
mNeedAuth = true; mNeedAuth = true;
return( false ); return false;
} else if ( respCode != 200 ) { } else if ( respCode != 200 ) {
Error( "Unexpected response code %d, text is '%s'", respCode, respText ); Error("Unexpected response code %d, text is '%s'", respCode, respText);
return( false ); return false;
} }
return( true ); return true;
} } // end RtspThread::recResponse
int RtspThread::requestPorts() { int RtspThread::requestPorts() {
if ( !smMinDataPort ) { if ( !smMinDataPort ) {
char sql[ZM_SQL_SML_BUFSIZ]; 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) ); 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 ) ) { if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) ); Error( "Can't run query: %s", mysql_error( &dbconn ) );
@ -107,7 +108,7 @@ int RtspThread::requestPorts() {
int nMonitors = mysql_num_rows( result ); int nMonitors = mysql_num_rows( result );
int position = 0; int position = 0;
if ( nMonitors ) { if ( nMonitors ) {
for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) { for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
int id = atoi(dbrow[0]); int id = atoi(dbrow[0]);
if ( mId == id ) { if ( mId == id ) {
position = i; position = i;
@ -126,22 +127,30 @@ int RtspThread::requestPorts() {
Debug( 2, "Assigned RTP port range is %d-%d", smMinDataPort, smMaxDataPort ); Debug( 2, "Assigned RTP port range is %d-%d", smMinDataPort, smMaxDataPort );
} }
for ( int i = smMinDataPort; i <= smMaxDataPort; i++ ) { for ( int i = smMinDataPort; i <= smMaxDataPort; i++ ) {
PortSet::const_iterator iter = smAssignedPorts.find( i ); PortSet::const_iterator iter = smAssignedPorts.find(i);
if ( iter == smAssignedPorts.end() ) { if ( iter == smAssignedPorts.end() ) {
smAssignedPorts.insert( i ); smAssignedPorts.insert(i);
return( i ); return i;
} }
} }
Panic( "Can assign RTP port, no ports left in pool" ); Panic("Can assign RTP port, no ports left in pool");
return( -1 ); return -1;
} }
void RtspThread::releasePorts( int port ) { void RtspThread::releasePorts( int port ) {
if ( port > 0 ) if ( port > 0 )
smAssignedPorts.erase( 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 ), mId( id ),
mMethod( method ), mMethod( method ),
mProtocol( protocol ), mProtocol( protocol ),
@ -168,10 +177,10 @@ RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol,
mSsrc = rand(); mSsrc = rand();
Debug( 2, "RTSP Local SSRC is %x", mSsrc ); Debug(2, "RTSP Local SSRC is %x", mSsrc);
if ( mMethod == RTP_RTSP_HTTP ) if ( mMethod == RTP_RTSP_HTTP )
mHttpSession = stringtf( "%d", rand() ); mHttpSession = stringtf("%d", rand());
mNeedAuth = false; mNeedAuth = false;
StringVector parts = split(auth,":"); StringVector parts = split(auth,":");
@ -216,8 +225,8 @@ int RtspThread::run() {
bool authTried = false; bool authTried = false;
if ( mMethod == RTP_RTSP_HTTP ) { if ( mMethod == RTP_RTSP_HTTP ) {
if ( !mRtspSocket2.connect( mHost.c_str(), mPort.c_str() ) ) if ( !mRtspSocket2.connect(mHost.c_str(), mPort.c_str()) )
Fatal( "Unable to connect auxiliary RTSP/HTTP socket" ); Fatal("Unable to connect auxiliary RTSP/HTTP socket");
//Select select( 0.25 ); //Select select( 0.25 );
//select.addReader( &mRtspSocket2 ); //select.addReader( &mRtspSocket2 );
//while ( select.wait() ) //while ( select.wait() )
@ -240,15 +249,15 @@ int RtspThread::run() {
message += "\r\n"; message += "\r\n";
Debug( 2, "Sending HTTP message: %s", message.c_str() ); Debug( 2, "Sending HTTP message: %s", message.c_str() );
if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) { if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) {
Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); Error("Unable to send message '%s': %s", message.c_str(), strerror(errno));
return( -1 ); return -1;
} }
if ( mRtspSocket.recv( response ) < 0 ) { if ( mRtspSocket.recv( response ) < 0 ) {
Error( "Recv failed; %s", strerror(errno) ); Error("Recv failed; %s", strerror(errno));
return( -1 ); return -1;
} }
Debug( 2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size() ); Debug(2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size());
float respVer = 0; float respVer = 0;
respCode = -1; respCode = -1;
if ( sscanf( response.c_str(), "HTTP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 ) { if ( sscanf( response.c_str(), "HTTP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 ) {
@ -259,25 +268,25 @@ int RtspThread::run() {
if ( response.size() ) if ( response.size() )
Hexdump( Logger::ERROR, response.data(), min(response.size(),16) ); 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 // If Server requests authentication, check WWW-Authenticate header and fill required fields
// for requested authentication method // for requested authentication method
if (respCode == 401 && !authTried) { if ( respCode == 401 && !authTried ) {
mNeedAuth = true; mNeedAuth = true;
mAuthenticator->checkAuthResponse(response); mAuthenticator->checkAuthResponse(response);
Debug(2, "Processed 401 response"); Debug(2, "Processed 401 response");
mRtspSocket.close(); mRtspSocket.close();
if ( !mRtspSocket.connect( mHost.c_str(), mPort.c_str() ) ) if ( !mRtspSocket.connect(mHost.c_str(), mPort.c_str()) )
Fatal( "Unable to reconnect RTSP socket" ); Fatal("Unable to reconnect RTSP socket");
Debug(2, "connection should be reopened now"); Debug(2, "connection should be reopened now");
} }
} while (respCode == 401 && !authTried); } while (respCode == 401 && !authTried);
if ( respCode != 200 ) { if ( respCode != 200 ) {
Error( "Unexpected response code %d, text is '%s'", respCode, respText ); Error("Unexpected response code %d, text is '%s'", respCode, respText);
return( -1 ); return -1;
} }
message = "POST "+mPath+" HTTP/1.0\r\n"; message = "POST "+mPath+" HTTP/1.0\r\n";
@ -300,25 +309,25 @@ int RtspThread::run() {
// Request supported RTSP commands by the server // Request supported RTSP commands by the server
message = "OPTIONS "+mUrl+" RTSP/1.0\r\n"; message = "OPTIONS "+mUrl+" RTSP/1.0\r\n";
if ( !sendCommand( message ) ) 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. // 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 ( !recvResponse(response) ) {
if ( mNeedAuth ) { if ( mNeedAuth ) {
Debug( 2, "Resending OPTIONS due to possible auth requirement" ); Debug( 2, "Resending OPTIONS due to possible auth requirement" );
if ( !sendCommand( message ) ) if ( !sendCommand(message) )
return( -1 ); return -1;
if ( !recvResponse( response ) ) if ( !recvResponse(response) )
return( -1 ); return -1;
} else { } else {
return( -1 ); return -1;
} }
} // end if failed response maybe due to auth } // end if failed response maybe due to auth
char publicLine[256] = ""; char publicLine[256] = "";
StringVector lines = split( response, "\r\n" ); StringVector lines = split(response, "\r\n");
for ( size_t i = 0; i < lines.size(); i++ ) for ( size_t i = 0; i < lines.size(); i++ )
sscanf( lines[i].c_str(), "Public: %[^\r\n]\r\n", publicLine ); sscanf(lines[i].c_str(), "Public: %[^\r\n]\r\n", publicLine);
// Check if the server supports the GET_PARAMETER command // Check if the server supports the GET_PARAMETER command
// If yes, it is likely that the server will request this command as a keepalive message // If yes, it is likely that the server will request this command as a keepalive message
@ -331,44 +340,45 @@ int RtspThread::run() {
do { do {
if (mNeedAuth) if (mNeedAuth)
authTried = true; authTried = true;
sendCommand( message ); sendCommand(message);
sleep( 1 ); // FIXME WHy sleep 1?
res = recvResponse( response ); sleep(1);
if (!res && respCode==401) res = recvResponse(response);
if ( !res && respCode==401 )
mNeedAuth = true; mNeedAuth = true;
} while (!res && respCode==401 && !authTried); } while (!res && respCode==401 && !authTried);
const std::string endOfHeaders = "\r\n\r\n"; const std::string endOfHeaders = "\r\n\r\n";
size_t sdpStart = response.find( endOfHeaders ); size_t sdpStart = response.find(endOfHeaders);
if( sdpStart == std::string::npos ) if ( sdpStart == std::string::npos )
return( -1 ); return -1;
if ( mRtspDescribe ) { if ( mRtspDescribe ) {
std::string DescHeader = response.substr( 0,sdpStart ); std::string DescHeader = response.substr(0, sdpStart);
Debug( 1, "Processing DESCRIBE response header '%s'", DescHeader.c_str() ); Debug(1, "Processing DESCRIBE response header '%s'", DescHeader.c_str());
lines = split( DescHeader, "\r\n" ); lines = split(DescHeader, "\r\n");
for ( size_t i = 0; i < lines.size(); i++ ) { for ( size_t i = 0; i < lines.size(); i++ ) {
// If the device sends us a url value for Content-Base in the response header, we should use that instead // If the device sends us a url value for Content-Base in the response header, we should use that instead
if ( ( lines[i].size() > 13 ) && ( lines[i].substr( 0, 13 ) == "Content-Base:" ) ) { if ( ( lines[i].size() > 13 ) && ( lines[i].substr( 0, 13 ) == "Content-Base:" ) ) {
mUrl = trimSpaces( lines[i].substr( 13 ) ); mUrl = trimSpaces( lines[i].substr( 13 ) );
Info("Received new Content-Base in DESCRIBE response header. Updated device Url to: '%s'", mUrl.c_str() ); Info("Received new Content-Base in DESCRIBE response header. Updated device Url to: '%s'", mUrl.c_str() );
break; break;
}
} }
} } // end foreach line
} // end if mRtspDescribe
sdpStart += endOfHeaders.length(); sdpStart += endOfHeaders.length();
std::string sdp = response.substr( sdpStart ); std::string sdp = response.substr(sdpStart);
Debug( 1, "Processing SDP '%s'", sdp.c_str() ); Debug(1, "Processing SDP '%s'", sdp.c_str());
try { try {
mSessDesc = new SessionDescriptor( mUrl, sdp ); mSessDesc = new SessionDescriptor( mUrl, sdp );
mFormatContext = mSessDesc->generateFormatContext(); mFormatContext = mSessDesc->generateFormatContext();
} catch( const Exception &e ) { } catch( const Exception &e ) {
Error( e.getMessage().c_str() ); Error( e.getMessage().c_str() );
return( -1 ); return -1;
} }
#if 0 #if 0
@ -672,51 +682,36 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
Hexdump( 4, (char *)buffer, 16 ); Hexdump( 4, (char *)buffer, 16 );
rtpDataThread.recvPacket( buffer+4, len ); rtpDataThread.recvPacket( buffer+4, len );
Debug( 4, "Received" ); Debug( 4, "Received" );
} } else if ( channel == remoteChannels[1] ) {
else if ( channel == remoteChannels[1] )
{
// len = ntohs( *((unsigned short *)(buffer+2)) ); // 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", nBytes, channel );
Debug( 4, "Got %d bytes on control channel %d, packet length is %d", buffer.size(), channel, len ); Debug( 4, "Got %d bytes on control channel %d, packet length is %d", buffer.size(), channel, len );
Hexdump( 4, (char *)buffer, 16 ); Hexdump( 4, (char *)buffer, 16 );
rtpCtrlThread.recvPackets( buffer+4, len ); rtpCtrlThread.recvPackets( buffer+4, len );
} } else {
else
{
Error( "Unexpected channel selector %d in RTSP interleaved data", buffer[1] ); Error( "Unexpected channel selector %d in RTSP interleaved data", buffer[1] );
buffer.clear(); buffer.clear();
break; break;
} }
buffer.consume( len+4 ); buffer.consume( len+4 );
nBytes -= len+4; nBytes -= len+4;
} } else {
else if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 ) {
{
if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 )
{
Debug( 4, "Got keepalive response '%s'", (char *)buffer ); Debug( 4, "Got keepalive response '%s'", (char *)buffer );
//buffer.consume( keepaliveResponse.size() ); //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; int discardBytes = charPtr-(char *)buffer;
buffer -= discardBytes; buffer -= discardBytes;
} } else {
else
{
buffer.clear(); buffer.clear();
} }
} } else {
else if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) {
{
if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) )
{
int discardBytes = charPtr-(char *)buffer; int discardBytes = charPtr-(char *)buffer;
Warning( "Unexpected format RTSP interleaved data, resyncing by %d bytes", discardBytes ); Warning( "Unexpected format RTSP interleaved data, resyncing by %d bytes", discardBytes );
Hexdump( -1, (char *)buffer, discardBytes ); Hexdump( -1, (char *)buffer, discardBytes );
buffer -= discardBytes; buffer -= discardBytes;
} } else {
else
{
Warning( "Unexpected format RTSP interleaved data, dumping %d bytes", buffer.size() ); Warning( "Unexpected format RTSP interleaved data, dumping %d bytes", buffer.size() );
Hexdump( -1, (char *)buffer, 32 ); Hexdump( -1, (char *)buffer, 32 );
buffer.clear(); buffer.clear();
@ -764,16 +759,14 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
rtpDataThread.start(); rtpDataThread.start();
rtpCtrlThread.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 // 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 ) ) if ( !sendCommand( message ) )
return( -1 ); return -1;
lastKeepalive = time(NULL); lastKeepalive = time(NULL);
} }
usleep( 100000 ); usleep(100000);
} }
#if 0 #if 0
message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
@ -783,10 +776,10 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
return( -1 ); return( -1 );
#endif #endif
message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
if ( !sendCommand( message ) ) if ( !sendCommand(message) )
return( -1 ); return -1;
if ( !recvResponse( response ) ) if ( !recvResponse(response) )
return( -1 ); return -1;
rtpDataThread.stop(); rtpDataThread.stop();
rtpCtrlThread.stop(); rtpCtrlThread.stop();
@ -801,13 +794,11 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
break; break;
} }
default: default:
{ Panic("Got unexpected method %d", mMethod);
Panic( "Got unexpected method %d", mMethod );
break; break;
}
} }
return( 0 ); return 0;
} }
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT

View File

@ -26,17 +26,17 @@
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) #if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = { SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
{ 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 }, { 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 },
{ 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 }, { 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 },
{ 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 }, { 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 },
{ 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 }, { 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 },
{ 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 }, { 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 },
{ 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 }, { 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 },
{ 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 }, { 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 },
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 }, { 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 },
{ 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
@ -45,36 +45,36 @@ SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
{ 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
{ 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 }, { 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
{ 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 }, { 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 },
{ 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 }, { 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
{ 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 }, { 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 },
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 }, { 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 },
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 }, { 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 },
{ 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 }, { 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 },
{ 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 }, { 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 },
{ -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 } { -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 }
}; };
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = { SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
{ "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 }, { "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
{ "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC }, { "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
{ "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, { "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
{ "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB }, { "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB },
{ "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE } { "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE }
}; };
#else #else
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = { SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
{ 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 }, { 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 },
{ 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 }, { 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 },
{ 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 }, { 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 },
{ 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 }, { 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 },
{ 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 }, { 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 },
{ 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 }, { 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 },
{ 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 }, { 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 },
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 }, { 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 },
{ 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
@ -83,7 +83,7 @@ SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
{ 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
{ 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 }, { 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
{ 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 }, { 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 },
{ 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 }, { 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
{ 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 }, { 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 },
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 }, { 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 },
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 }, { 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 },
@ -105,7 +105,7 @@ SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) :
mTtl( 16 ), mTtl( 16 ),
mNoAddresses( 0 ) mNoAddresses( 0 )
{ {
StringVector tokens = split( connInfo, " " ); StringVector tokens = split(connInfo, " ");
if ( tokens.size() < 3 ) if ( tokens.size() < 3 )
throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" ); throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" );
mNetworkType = tokens[0]; mNetworkType = tokens[0];
@ -136,7 +136,12 @@ SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) :
mValue = atoi(tokens[1].c_str()); 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 ), mType( type ),
mPort( port ), mPort( port ),
mNumPorts( numPorts ), mNumPorts( numPorts ),
@ -164,14 +169,13 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
if ( line.empty() ) if ( line.empty() )
break; break;
Debug( 3, "Processing SDP line '%s'", line.c_str() ); Debug(3, "Processing SDP line '%s'", line.c_str());
const char sdpType = line[0]; const char sdpType = line[0];
if ( line[1] != '=' ) if ( line[1] != '=' )
throw Exception( "Invalid SDP format at '"+line+"'" ); throw Exception("Invalid SDP format at '"+line+"'");
line.erase( 0, 2 ); line.erase(0, 2);
switch( sdpType ) switch( sdpType ) {
{
case 'v' : case 'v' :
mVersion = line; mVersion = line;
break; break;
@ -204,19 +208,13 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
mAttributes.push_back( line ); mAttributes.push_back( line );
StringVector tokens = split( line, ":", 2 ); StringVector tokens = split( line, ":", 2 );
std::string attrName = tokens[0]; std::string attrName = tokens[0];
if ( currMedia ) if ( currMedia ) {
{ if ( attrName == "control" ) {
if ( attrName == "control" )
{
if ( tokens.size() < 2 ) if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" ); throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
currMedia->setControlUrl( tokens[1] ); currMedia->setControlUrl( tokens[1] );
} } else if ( attrName == "range" ) {
else if ( attrName == "range" ) } else if ( attrName == "rtpmap" ) {
{
}
else if ( attrName == "rtpmap" )
{
// a=rtpmap:96 MP4V-ES/90000 // a=rtpmap:96 MP4V-ES/90000
if ( tokens.size() < 2 ) if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" ); 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() ) ); throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
std::string payloadDesc = attrTokens[1]; std::string payloadDesc = attrTokens[1];
//currMedia->setPayloadType( payloadType ); //currMedia->setPayloadType( payloadType );
if ( attrTokens.size() > 1 ) if ( attrTokens.size() > 1 ) {
{
StringVector payloadTokens = split( attrTokens[1], "/" ); StringVector payloadTokens = split( attrTokens[1], "/" );
std::string payloadDesc = payloadTokens[0]; std::string payloadDesc = payloadTokens[0];
int payloadClock = atoi(payloadTokens[1].c_str()); int payloadClock = atoi(payloadTokens[1].c_str());
currMedia->setPayloadDesc( payloadDesc ); currMedia->setPayloadDesc( payloadDesc );
currMedia->setClock( payloadClock ); currMedia->setClock( payloadClock );
} }
} } else if ( attrName == "framesize" ) {
else if ( attrName == "framesize" )
{
// a=framesize:96 320-240 // a=framesize:96 320-240
if ( tokens.size() < 2 ) if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" ); throw Exception("Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'");
StringVector attrTokens = split( tokens[1], " " ); StringVector attrTokens = split(tokens[1], " ");
int payloadType = atoi(attrTokens[0].c_str()); int payloadType = atoi(attrTokens[0].c_str());
if ( payloadType != currMedia->getPayloadType() ) 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 ); //currMedia->setPayloadType( payloadType );
StringVector sizeTokens = split( attrTokens[1], "-" ); StringVector sizeTokens = split(attrTokens[1], "-");
int width = atoi(sizeTokens[0].c_str()); int width = atoi(sizeTokens[0].c_str());
int height = atoi(sizeTokens[1].c_str()); int height = atoi(sizeTokens[1].c_str());
currMedia->setFrameSize( width, height ); currMedia->setFrameSize(width, height);
} } else if ( attrName == "framerate" ) {
else if ( attrName == "framerate" )
{
// a=framerate:5.0 // a=framerate:5.0
if ( tokens.size() < 2 ) if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" ); throw Exception("Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'");
double frameRate = atof(tokens[1].c_str()); double frameRate = atof(tokens[1].c_str());
currMedia->setFrameRate( frameRate ); currMedia->setFrameRate(frameRate);
} } else if ( attrName == "fmtp" ) {
else if ( attrName == "fmtp" )
{
// a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F // a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F
if ( tokens.size() < 2 ) if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" ); throw Exception("Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'");
StringVector attrTokens = split( tokens[1], " ", 2 ); StringVector attrTokens = split(tokens[1], " ", 2);
int payloadType = atoi(attrTokens[0].c_str()); int payloadType = atoi(attrTokens[0].c_str());
if ( payloadType != currMedia->getPayloadType() ) 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 ); //currMedia->setPayloadType( payloadType );
if ( attrTokens.size() > 1 ) if ( attrTokens.size() > 1 ) {
{
StringVector attr2Tokens = split( attrTokens[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], "=" ); StringVector attr3Tokens = split( attr2Tokens[i], "=" );
//Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() ); //Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() );
if ( attr3Tokens[0] == "profile-level-id" ) { if ( attr3Tokens[0] == "profile-level-id" ) {
@ -292,40 +283,39 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
} else if ( attrName == "mpeg4-esid" ) { } else if ( attrName == "mpeg4-esid" ) {
// a=mpeg4-esid:201 // a=mpeg4-esid:201
} else { } 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 { } else {
Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() ); Debug(3, "Ignoring general SDP attribute '%s'", line.c_str());
} }
break; break;
} }
case 'm' : case 'm' :
{ {
StringVector tokens = split( line, " " ); StringVector tokens = split(line, " ");
if ( tokens.size() < 4 ) if ( tokens.size() < 4 )
throw Exception( "Can't parse SDP media description '"+line+"'" ); throw Exception("Can't parse SDP media description '"+line+"'");
std::string mediaType = tokens[0]; std::string mediaType = tokens[0];
if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" ) if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" )
throw Exception( "Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'" ); throw Exception("Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'");
StringVector portTokens = split( tokens[1], "/" ); StringVector portTokens = split(tokens[1], "/");
int mediaPort = atoi(portTokens[0].c_str()); int mediaPort = atoi(portTokens[0].c_str());
int mediaNumPorts = 1; int mediaNumPorts = 1;
if ( portTokens.size() > 1 ) if ( portTokens.size() > 1 )
mediaNumPorts = atoi(portTokens[1].c_str()); mediaNumPorts = atoi(portTokens[1].c_str());
std::string mediaTransport = tokens[2]; std::string mediaTransport = tokens[2];
if ( mediaTransport != "RTP/AVP" ) if ( mediaTransport != "RTP/AVP" )
throw Exception( "Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'" ); throw Exception("Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'");
int payloadType = atoi(tokens[3].c_str()); int payloadType = atoi(tokens[3].c_str());
currMedia = new MediaDescriptor( mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType ); currMedia = new MediaDescriptor(mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType);
mMediaList.push_back( currMedia ); mMediaList.push_back(currMedia);
break; break;
} }
} } // end switch
} } // end foreach line
} }
SessionDescriptor::~SessionDescriptor() SessionDescriptor::~SessionDescriptor() {
{
if ( mConnInfo ) if ( mConnInfo )
delete mConnInfo; delete mConnInfo;
if ( mBandInfo ) if ( mBandInfo )
@ -334,8 +324,7 @@ SessionDescriptor::~SessionDescriptor()
delete mMediaList[i]; delete mMediaList[i];
} }
AVFormatContext *SessionDescriptor::generateFormatContext() const AVFormatContext *SessionDescriptor::generateFormatContext() const {
{
AVFormatContext *formatContext = avformat_alloc_context(); AVFormatContext *formatContext = avformat_alloc_context();
#if (LIBAVFORMAT_VERSION_CHECK(58, 12, 0, 0, 100)) #if (LIBAVFORMAT_VERSION_CHECK(58, 12, 0, 0, 100))
@ -353,35 +342,40 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
for ( unsigned int i = 0; i < mMediaList.size(); i++ ) { for ( unsigned int i = 0; i < mMediaList.size(); i++ ) {
const MediaDescriptor *mediaDesc = mMediaList[i]; const MediaDescriptor *mediaDesc = mMediaList[i];
#if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0) #if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0)
AVStream *stream = av_new_stream( formatContext, i ); AVStream *stream = av_new_stream(formatContext, i);
#else #else
AVStream *stream = avformat_new_stream( formatContext, NULL ); AVStream *stream = avformat_new_stream(formatContext, NULL);
stream->id = i; stream->id = i;
#endif #endif
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
AVCodecContext *codec_context = avcodec_alloc_context3(NULL); AVCodecContext *codec_context = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(codec_context, stream->codecpar); avcodec_parameters_to_context(codec_context, stream->codecpar);
stream->codec = codec_context;
#else #else
AVCodecContext *codec_context = stream->codec; AVCodecContext *codec_context = stream->codec;
#endif #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 (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; codec_context->codec_type = AVMEDIA_TYPE_VIDEO;
else if ( mediaDesc->getType() == "audio" ) else if ( type == "audio" )
codec_context->codec_type = AVMEDIA_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; codec_context->codec_type = AVMEDIA_TYPE_DATA;
#else #else
if ( mediaDesc->getType() == "video" ) if ( type == "video" )
codec_context->codec_type = CODEC_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; codec_context->codec_type = CODEC_TYPE_AUDIO;
else if ( mediaDesc->getType() == "application" ) else if ( type == "application" )
codec_context->codec_type = CODEC_TYPE_DATA; codec_context->codec_type = CODEC_TYPE_DATA;
#endif #endif
else
Warning("Unknown media_type %s", type.c_str());
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
std::string codec_name; std::string codec_name;
@ -392,9 +386,9 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() ) { if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() ) {
Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName ); Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName );
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
codec_name = std::string( smStaticPayloads[i].payloadName ); codec_name = std::string(smStaticPayloads[i].payloadName);
#else #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 #endif
codec_context->codec_type = smStaticPayloads[i].codecType; codec_context->codec_type = smStaticPayloads[i].codecType;
codec_context->codec_id = smStaticPayloads[i].codecId; codec_context->codec_id = smStaticPayloads[i].codecId;
@ -406,11 +400,11 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
// Look in dynamic table // Look in dynamic table
for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ ) { for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ ) {
if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() ) { if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() ) {
Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName ); Debug(1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName);
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
codec_name = std::string( smStaticPayloads[i].payloadName ); codec_name = std::string(smStaticPayloads[i].payloadName);
#else #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 #endif
codec_context->codec_type = smDynamicPayloads[i].codecType; codec_context->codec_type = smDynamicPayloads[i].codecType;
codec_context->codec_id = smDynamicPayloads[i].codecId; codec_context->codec_id = smDynamicPayloads[i].codecId;
@ -418,7 +412,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
break; break;
} }
} }
} } /// end if static or dynamic
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
if ( codec_name.empty() ) if ( codec_name.empty() )
@ -426,7 +420,8 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
if ( !stream->codec->codec_name[0] ) if ( !stream->codec->codec_name[0] )
#endif #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 ); //return( 0 );
} }
if ( mediaDesc->getWidth() ) if ( mediaDesc->getWidth() )
@ -449,11 +444,11 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
while (*value && *value != ',' while (*value && *value != ','
&& (dst - base64packet) < (long)(sizeof(base64packet)) - 1) { && (dst - base64packet) < (long)(sizeof(base64packet)) - 1) {
*dst++ = *value++; *dst++ = *value++;
} }
*dst++ = '\0'; *dst++ = '\0';
if (*value == ',') if ( *value == ',' )
value++; value++;
packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet)); packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet));
@ -468,23 +463,23 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
FF_INPUT_BUFFER_PADDING_SIZE FF_INPUT_BUFFER_PADDING_SIZE
#endif #endif
); );
if(dest) { if ( dest ) {
if(codec_context->extradata_size) { if ( codec_context->extradata_size ) {
// av_realloc? // av_realloc?
memcpy(dest, codec_context->extradata, codec_context->extradata_size); memcpy(dest, codec_context->extradata, codec_context->extradata_size);
av_free(codec_context->extradata); av_free(codec_context->extradata);
} }
memcpy(dest+codec_context->extradata_size, start_sequence, sizeof(start_sequence)); memcpy(dest+codec_context->extradata_size, start_sequence, sizeof(start_sequence));
memcpy(dest+codec_context->extradata_size+sizeof(start_sequence), decoded_packet, packet_size); memcpy(dest+codec_context->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
memset(dest+codec_context->extradata_size+sizeof(start_sequence)+ memset(dest+codec_context->extradata_size+sizeof(start_sequence)+
packet_size, 0, packet_size, 0,
#if LIBAVCODEC_VERSION_CHECK(57, 0, 0, 0, 0) #if LIBAVCODEC_VERSION_CHECK(57, 0, 0, 0, 0)
AV_INPUT_BUFFER_PADDING_SIZE AV_INPUT_BUFFER_PADDING_SIZE
#else #else
FF_INPUT_BUFFER_PADDING_SIZE FF_INPUT_BUFFER_PADDING_SIZE
#endif #endif
); );
codec_context->extradata= dest; codec_context->extradata= dest;
codec_context->extradata_size+= sizeof(start_sequence)+packet_size; codec_context->extradata_size+= sizeof(start_sequence)+packet_size;
@ -497,7 +492,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
} }
} }
return( formatContext ); return formatContext;
} }
#endif // HAVE_LIBAVFORMAT #endif // HAVE_LIBAVFORMAT

View File

@ -1282,13 +1282,29 @@ int VideoStore::writeAudioFramePacket(ZMPacket *zm_packet) {
int frame_size = out_frame->nb_samples; int frame_size = out_frame->nb_samples;
in_frame->pts = audio_next_pts; in_frame->pts = audio_next_pts;
if ( ! resample_audio() ) { if ( !resample_audio() ) {
//av_frame_unref(in_frame); //av_frame_unref(in_frame);
return 0; return 0;
} }
zm_dump_frame(out_frame, "Out frame after resample");
out_frame->pts = in_frame->pts; out_frame->pts = in_frame->pts;
zm_dump_frame(out_frame, "Out frame after resample");
// out_frame pts is in the input pkt pts... needs to be adjusted before sending to the encoder
if ( out_frame->pts != AV_NOPTS_VALUE ) {
if ( !audio_first_pts ) {
audio_first_pts = out_frame->pts;
Debug(1, "No audio_first_pts setting to %" PRId64, audio_first_pts);
out_frame->pts = 0;
} else {
out_frame->pts = out_frame->pts - audio_first_pts;
zm_dump_frame(out_frame, "Out frame after pts adjustment");
}
//
} else {
// sending AV_NOPTS_VALUE doesn't really work but we seem to get it in ffmpeg 2.8
out_frame->pts = audio_next_pts;
}
audio_next_pts = out_frame->pts + out_frame->nb_samples;
av_init_packet(&opkt); av_init_packet(&opkt);
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
@ -1302,15 +1318,12 @@ int VideoStore::writeAudioFramePacket(ZMPacket *zm_packet) {
if ( (ret = avcodec_receive_packet(audio_out_ctx, &opkt)) < 0 ) { if ( (ret = avcodec_receive_packet(audio_out_ctx, &opkt)) < 0 ) {
if ( AVERROR(EAGAIN) == ret ) { if ( AVERROR(EAGAIN) == ret ) {
// The codec may need more samples than it has, perfectly valid // The codec may need more samples than it has, perfectly valid
Debug(3, "Could not recieve packet (error '%s')", Debug(2, "Codec not ready to give us a packet");
av_make_error_string(ret).c_str());
} else { } else {
Error("Could not recieve packet (error %d = '%s')", ret, Error("Could not recieve packet (error %d = '%s')", ret,
av_make_error_string(ret).c_str()); av_make_error_string(ret).c_str());
} }
//zm_av_packet_unref(&opkt); zm_av_packet_unref(&opkt);
av_frame_unref(in_frame);
// av_frame_unref( out_frame );
return 0; return 0;
} }
#else #else
@ -1328,6 +1341,8 @@ int VideoStore::writeAudioFramePacket(ZMPacket *zm_packet) {
return 0; return 0;
} }
#endif #endif
#if 0
// These should be set by encoder. They may not directly relate to ipkt due to buffering in codec.
opkt.duration = av_rescale_q(opkt.duration, opkt.duration = av_rescale_q(opkt.duration,
audio_in_stream->time_base, audio_in_stream->time_base,
audio_out_stream->time_base); audio_out_stream->time_base);
@ -1337,6 +1352,7 @@ int VideoStore::writeAudioFramePacket(ZMPacket *zm_packet) {
opkt.dts = av_rescale_q(opkt.dts, opkt.dts = av_rescale_q(opkt.dts,
audio_in_stream->time_base, audio_in_stream->time_base,
audio_out_stream->time_base); audio_out_stream->time_base);
#endif
dumpPacket(audio_out_stream, &opkt, "raw opkt"); dumpPacket(audio_out_stream, &opkt, "raw opkt");
} else { } else {
@ -1430,28 +1446,48 @@ int VideoStore::write_packets( zm_packetqueue &queue ) {
} // end int VideoStore::write_packets( PacketQueue &queue ) { } // end int VideoStore::write_packets( PacketQueue &queue ) {
int VideoStore::resample_audio() { 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 // decoded data. Note: pts does not survive resampling or converting
#if defined(HAVE_LIBSWRESAMPLE) || defined(HAVE_LIBAVRESAMPLE) #if defined(HAVE_LIBSWRESAMPLE) || defined(HAVE_LIBAVRESAMPLE)
#if defined(HAVE_LIBSWRESAMPLE) #if defined(HAVE_LIBSWRESAMPLE)
Debug(2, "Converting %d to %d samples using swresample", Debug(2, "Converting %d to %d samples using swresample",
in_frame->nb_samples, out_frame->nb_samples); in_frame->nb_samples, out_frame->nb_samples);
ret = swr_convert_frame(resample_ctx, out_frame, in_frame); ret = swr_convert_frame(resample_ctx, out_frame, in_frame);
zm_dump_frame(out_frame, "Out frame after convert");
if ( ret < 0 ) { if ( ret < 0 ) {
Error("Could not resample frame (error '%s')", Error("Could not resample frame (error '%s')",
av_make_error_string(ret).c_str()); av_make_error_string(ret).c_str());
return 0; return 0;
} }
zm_dump_frame(out_frame, "Out frame after convert");
#if 0
// out_frame pts is in the input pkt pts... needs to be adjusted before sending to the encoder
if ( out_frame->pts != AV_NOPTS_VALUE ) {
if ( !audio_first_pts ) {
audio_first_pts = out_frame->pts;
Debug(1, "No audio_first_pts setting to %" PRId64, audio_first_pts);
out_frame->pts = 0;
} else {
out_frame->pts = out_frame->pts - audio_first_pts;
}
//
} else {
// sending AV_NOPTS_VALUE doesn't really work but we seem to get it in ffmpeg 2.8
out_frame->pts = audio_next_pts;
}
audio_next_pts = out_frame->pts + out_frame->nb_samples;
#endif
if ((ret = av_audio_fifo_realloc(fifo, av_audio_fifo_size(fifo) + out_frame->nb_samples)) < 0) { if ((ret = av_audio_fifo_realloc(fifo, av_audio_fifo_size(fifo) + out_frame->nb_samples)) < 0) {
Error("Could not reallocate FIFO"); Error("Could not reallocate FIFO");
return 0; return 0;
} }
/** Store the new samples in the FIFO buffer. */ /** Store the new samples in the FIFO buffer. */
ret = av_audio_fifo_write(fifo, (void **)out_frame->data, out_frame->nb_samples); ret = av_audio_fifo_write(fifo, (void **)out_frame->data, out_frame->nb_samples);
if ( ret < in_frame->nb_samples ) { if ( ret < out_frame->nb_samples ) {
Error("Could not write data to FIFO on %d written", ret); Error("Could not write data to FIFO. %d written, expecting %d. Reason %s",
ret, out_frame->nb_samples, av_make_error_string(ret).c_str());
return 0; return 0;
} }
@ -1468,6 +1504,17 @@ int VideoStore::resample_audio() {
return 0; return 0;
} }
out_frame->nb_samples = frame_size; out_frame->nb_samples = frame_size;
// resampling changes the duration because the timebase is 1/samples
if ( in_frame->pts != AV_NOPTS_VALUE ) {
out_frame->pkt_duration = av_rescale_q(
in_frame->pkt_duration,
audio_in_stream->time_base,
audio_out_stream->time_base);
out_frame->pts = av_rescale_q(
in_frame->pts,
audio_in_stream->time_base,
audio_out_stream->time_base);
}
#else #else
#if defined(HAVE_LIBAVRESAMPLE) #if defined(HAVE_LIBAVRESAMPLE)
ret = avresample_convert(resample_ctx, NULL, 0, 0, in_frame->data, ret = avresample_convert(resample_ctx, NULL, 0, 0, in_frame->data,

View File

@ -1 +1 @@
1.33.7 1.33.8

View File

@ -44,7 +44,7 @@ class Storage {
} }
public function Path() { public function Path() {
if ( isset( $this->{'Path'} ) and ( $this->{'Path'} != '' ) ) { if ( isset($this->{'Path'}) and ( $this->{'Path'} != '' ) ) {
return $this->{'Path'}; return $this->{'Path'};
} else if ( ! isset($this->{'Id'}) ) { } else if ( ! isset($this->{'Id'}) ) {
$path = ZM_DIR_EVENTS; $path = ZM_DIR_EVENTS;
@ -58,7 +58,7 @@ class Storage {
return $this->{'Name'}; return $this->{'Name'};
} }
public function Name() { public function Name() {
if ( isset( $this->{'Name'} ) and ( $this->{'Name'} != '' ) ) { if ( isset($this->{'Name'}) and ( $this->{'Name'} != '' ) ) {
return $this->{'Name'}; return $this->{'Name'};
} else if ( ! isset($this->{'Id'}) ) { } else if ( ! isset($this->{'Id'}) ) {
return 'Default'; return 'Default';
@ -73,7 +73,7 @@ class Storage {
if ( array_key_exists($fn, $this) ) if ( array_key_exists($fn, $this) )
return $this->{$fn}; return $this->{$fn};
if ( array_key_exists( $fn, $this->defaults ) ) if ( array_key_exists($fn, $this->defaults) )
return $this->defaults{$fn}; return $this->defaults{$fn};
$backTrace = debug_backtrace(); $backTrace = debug_backtrace();
@ -96,7 +96,7 @@ class Storage {
$results = Storage::find($parameters, $options); $results = Storage::find($parameters, $options);
if ( count($results) > 1 ) { if ( count($results) > 1 ) {
Error("Storage Returned more than 1"); Error('Storage Returned more than 1');
return $results[0]; return $results[0];
} else if ( count($results) ) { } else if ( count($results) ) {
return $results[0]; return $results[0];
@ -116,7 +116,7 @@ class Storage {
$fields[] = $field.' IS NULL'; $fields[] = $field.' IS NULL';
} else if ( is_array($value) ) { } else if ( is_array($value) ) {
$func = function(){return '?';}; $func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map($func, $value)). ')'; $fields[] = $field.' IN ('.implode(',', array_map($func, $value)).')';
$values += $value; $values += $value;
} else { } else {
@ -165,11 +165,11 @@ class Storage {
$total = $this->disk_total_space(); $total = $this->disk_total_space();
if ( ! $total ) { if ( ! $total ) {
Error('disk_total_space returned false for ' . $path ); Error('disk_total_space returned false for ' . $path);
return 0; return 0;
} }
$used = $this->disk_used_space(); $used = $this->disk_used_space();
$usage = round( ($used / $total) * 100); $usage = round(($used / $total) * 100);
//Logger::Debug("Used $usage = round( ( $used / $total ) * 100 )"); //Logger::Debug("Used $usage = round( ( $used / $total ) * 100 )");
return $usage; return $usage;
} }
@ -208,7 +208,7 @@ class Storage {
public function event_disk_space() { public function event_disk_space() {
# This isn't a function like this in php, so we have to add up the space used in each event. # This isn't a function like this in php, so we have to add up the space used in each event.
if ( (! array_key_exists('DiskSpace', $this)) or (!$this->{'DiskSpace'}) ) { if ( (! array_key_exists('DiskSpace', $this)) or (!$this->{'DiskSpace'}) ) {
$used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id()) ); $used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id()));
foreach ( Event::find(array('StorageId'=>$this->Id(), 'DiskSpace'=>null)) as $Event ) { foreach ( Event::find(array('StorageId'=>$this->Id(), 'DiskSpace'=>null)) as $Event ) {
$Event->Storage($this); // Prevent further db hit $Event->Storage($this); // Prevent further db hit
@ -221,7 +221,7 @@ class Storage {
public function Server() { public function Server() {
if ( ! array_key_exists('Server',$this) ) { if ( ! array_key_exists('Server',$this) ) {
$this->{'Server'}= new Server( $this->{'ServerId'} ); $this->{'Server'}= new Server($this->{'ServerId'});
} }
return $this->{'Server'}; return $this->{'Server'};
} }
@ -239,5 +239,5 @@ class Storage {
} }
return json_encode($json); return json_encode($json);
} }
} } // end class Storage
?> ?>

View File

@ -68,6 +68,9 @@ if ( $action == 'monitor' ) {
$columns = getTableColumns('Monitors'); $columns = getTableColumns('Monitors');
$changes = getFormChanges($monitor, $_REQUEST['newMonitor'], $types, $columns); $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 ( count($changes) ) {
if ( $mid ) { if ( $mid ) {
@ -88,12 +91,12 @@ if ( $action == 'monitor' ) {
$NewStorage = new ZM\Storage($_REQUEST['newMonitor']['StorageId']); $NewStorage = new ZM\Storage($_REQUEST['newMonitor']['StorageId']);
if ( !file_exists($NewStorage->Path().'/'.$mid) ) { if ( !file_exists($NewStorage->Path().'/'.$mid) ) {
if ( !mkdir($NewStorage->Path().'/'.$mid, 0755) ) { 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']); $saferNewName = basename($_REQUEST['newMonitor']['Name']);
if ( !symlink($NewStorage->Path().'/'.$mid, $NewStorage->Path().'/'.$saferNewName) ) { 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']) ) { if ( isset($changes['Width']) || isset($changes['Height']) ) {

View File

@ -93,7 +93,7 @@ if ( isset($_GET['skin']) ) {
$skins = array_map('basename', glob('skins/*', GLOB_ONLYDIR)); $skins = array_map('basename', glob('skins/*', GLOB_ONLYDIR));
if ( ! in_array($skin, $skins) ) { 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]; $skin = $skins[0];
} }
@ -109,7 +109,7 @@ if ( isset($_GET['css']) ) {
$css_skins = array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR)); $css_skins = array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR));
if ( !in_array($css, $css_skins) ) { 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]; $css = $css_skins[0];
} }

View File

@ -294,7 +294,7 @@ $SLANG = array(
'Display' => 'Prikaz', 'Display' => 'Prikaz',
'Displaying' => 'Prikazujem', 'Displaying' => 'Prikazujem',
'DonateAlready' => 'Ne, već sam napravio donaciju.', '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', 'Donate' => 'Molimo donirajte',
'DonateRemindDay' => 'Ne još, podsjetime za 1 dan', 'DonateRemindDay' => 'Ne još, podsjetime za 1 dan',
'DonateRemindHour' => 'Ne još, podsjetime za 1 sat', 'DonateRemindHour' => 'Ne još, podsjetime za 1 sat',

View File

@ -293,7 +293,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Please Donate', 'Donate' => 'Please Donate',
'DonateAlready' => 'No, I\'ve already donated', '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', 'DonateRemindDay' => 'Not yet, remind again in 1 day',
'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindHour' => 'Not yet, remind again in 1 hour',
'DonateRemindMonth' => 'Not yet, remind again in 1 month', 'DonateRemindMonth' => 'Not yet, remind again in 1 month',

View File

@ -289,7 +289,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => '请捐款', 'Donate' => '请捐款',
'DonateAlready' => '不,我已经捐赠过了', '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天内再次提醒我', 'DonateRemindDay' => '现在不1天内再次提醒我',
'DonateRemindHour' => '现在不1小时内再次提醒我', 'DonateRemindHour' => '现在不1小时内再次提醒我',
'DonateRemindMonth' => '现在不1个月内再次提醒我', 'DonateRemindMonth' => '现在不1个月内再次提醒我',

View File

@ -289,7 +289,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Prosím podpořte', 'Donate' => 'Prosím podpořte',
'DonateAlready' => 'Ne, už jsem podpořil', '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', 'DonateRemindDay' => 'Nyní ne, připomenout za 1 den',
'DonateRemindHour' => 'Nyní ne, připomenout za hodinu', 'DonateRemindHour' => 'Nyní ne, připomenout za hodinu',
'DonateRemindMonth' => 'Nyní ne, připomenout za měsíc', 'DonateRemindMonth' => 'Nyní ne, připomenout za měsíc',

View File

@ -291,7 +291,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Bitte spenden Sie.', 'Donate' => 'Bitte spenden Sie.',
'DonateAlready' => 'Nein, ich habe schon gespendet', '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.', 'DonateRemindDay' => 'Noch nicht, erinnere mich in einem Tag noch mal.',
'DonateRemindHour' => 'Noch nicht, erinnere mich in einer Stunde noch mal.', 'DonateRemindHour' => 'Noch nicht, erinnere mich in einer Stunde noch mal.',
'DonateRemindMonth' => 'Noch nicht, erinnere mich in einem Monat noch mal.', 'DonateRemindMonth' => 'Noch nicht, erinnere mich in einem Monat noch mal.',

View File

@ -290,7 +290,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Venligst Donér', 'Donate' => 'Venligst Donér',
'DonateAlready' => 'Nej, jeg har allerede doneret', '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', 'DonateRemindDay' => 'Ikke endnu, påmind igen on 1 dag',
'DonateRemindHour' => 'Ikke endnu, påmind igen on 1 time', 'DonateRemindHour' => 'Ikke endnu, påmind igen on 1 time',
'DonateRemindMonth' => 'Ikke endnu, påmind igen on 1 måned', 'DonateRemindMonth' => 'Ikke endnu, påmind igen on 1 måned',

View File

@ -32,7 +32,7 @@
// a formatting string. If the dynamic element is a number you will usually need to use a variable // a formatting string. If the dynamic element is a number you will usually need to use a variable
// replacement also as described below. // replacement also as described below.
// c) Variable replacements are used in conjunction with complex replacements and involve the generation // c) Variable replacements are used in conjunction with complex replacements and involve the generation
// of a singular or plural noun depending on the number passed into the zmVlang function. See the // of a singular or plural noun depending on the number passed into the zmVlang function. See the
// the zmVlang section below for a further description of this. // the zmVlang section below for a further description of this.
// d) Optional strings which can be used to replace the prompts and/or help text for the Options section // d) Optional strings which can be used to replace the prompts and/or help text for the Options section
// of the web interface. These are not listed below as they are quite large and held in the database // of the web interface. These are not listed below as they are quite large and held in the database
@ -40,7 +40,7 @@
// quite easily from the Config table in the database if necessary. // quite easily from the Config table in the database if necessary.
// 3. The tokens listed below are not used to build up phrases or sentences from single words. Therefore // 3. The tokens listed below are not used to build up phrases or sentences from single words. Therefore
// you can safely assume that a single word token will only be used in that context. // you can safely assume that a single word token will only be used in that context.
// 4. In new language files, or if you are changing only a few words or phrases it makes sense from a // 4. In new language files, or if you are changing only a few words or phrases it makes sense from a
// maintenance point of view to include the original language file and override the old definitions rather // maintenance point of view to include the original language file and override the old definitions rather
// than copy all the language tokens across. To do this change the line below to whatever your base language // than copy all the language tokens across. To do this change the line below to whatever your base language
// is and uncomment it. // is and uncomment it.
@ -57,10 +57,10 @@
// If you do need to change your locale, be aware that the format of this function // If you do need to change your locale, be aware that the format of this function
// is subtlely different in versions of PHP before and after 4.3.0, see // is subtlely different in versions of PHP before and after 4.3.0, see
// http://uk2.php.net/manual/en/function.setlocale.php for details. // http://uk2.php.net/manual/en/function.setlocale.php for details.
// Also be aware that changing the whole locale may affect some floating point or decimal // Also be aware that changing the whole locale may affect some floating point or decimal
// arithmetic in the database, if this is the case change only the individual locale areas // arithmetic in the database, if this is the case change only the individual locale areas
// that don't affect this rather than all at once. See the examples below. // that don't affect this rather than all at once. See the examples below.
// Finally, depending on your setup, PHP may not enjoy have multiple locales in a shared // Finally, depending on your setup, PHP may not enjoy have multiple locales in a shared
// threaded environment, if you get funny errors it may be this. // threaded environment, if you get funny errors it may be this.
// //
// Examples // Examples
@ -296,7 +296,7 @@ $SLANG = array(
'Display' => 'Display', 'Display' => 'Display',
'Displaying' => 'Displaying', 'Displaying' => 'Displaying',
'DonateAlready' => 'No, I\'ve already donated', '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', 'Donate' => 'Please Donate',
'DonateRemindDay' => 'Not yet, remind again in 1 day', 'DonateRemindDay' => 'Not yet, remind again in 1 day',
'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindHour' => 'Not yet, remind again in 1 hour',
@ -769,7 +769,7 @@ $SLANG = array(
'Update' => 'Update', 'Update' => 'Update',
'Upload' => 'Upload', 'Upload' => 'Upload',
'Updated' => 'Updated', 'Updated' => 'Updated',
'UsedPlugins' => 'Used Plugins', 'UsedPlugins' => 'Used Plugins',
'UseFilterExprsPost' => '&nbsp;filter&nbsp;expressions', // This is used at the end of the phrase 'use N filter expressions' 'UseFilterExprsPost' => '&nbsp;filter&nbsp;expressions', // This is used at the end of the phrase 'use N filter expressions'
'UseFilterExprsPre' => 'Use&nbsp;', // This is used at the beginning of the phrase 'use N filter expressions' 'UseFilterExprsPre' => 'Use&nbsp;', // This is used at the beginning of the phrase 'use N filter expressions'
'UseFilter' => 'Use Filter', 'UseFilter' => 'Use Filter',
@ -848,7 +848,7 @@ $CLANG = array(
'VersionMismatch' => 'Version mismatch, system is version %1$s, database is %2$s.', 'VersionMismatch' => 'Version mismatch, system is version %1$s, database is %2$s.',
); );
// The next section allows you to describe a series of word ending and counts used to // The next section allows you to describe a series of word ending and counts used to
// generate the correctly conjugated forms of words depending on a count that is associated // generate the correctly conjugated forms of words depending on a count that is associated
// with that word. // with that word.
// This intended to allow phrases such a '0 potatoes', '1 potato', '2 potatoes' etc to // This intended to allow phrases such a '0 potatoes', '1 potato', '2 potatoes' etc to
@ -889,7 +889,7 @@ $VLANG = array(
// with variable counts. This is used to conjugate the Vlang arrays above with a number passed // with variable counts. This is used to conjugate the Vlang arrays above with a number passed
// in to generate the correct noun form. // in to generate the correct noun form.
// //
// In languages such as English this is fairly simple // In languages such as English this is fairly simple
// Note this still has to be used with printf etc to get the right formatting // Note this still has to be used with printf etc to get the right formatting
function zmVlang( $langVarArray, $count ) function zmVlang( $langVarArray, $count )
{ {
@ -907,9 +907,9 @@ function zmVlang( $langVarArray, $count )
// This is an version that could be used in the Russian example above // This is an version that could be used in the Russian example above
// The rules are that the first word form is used if the count ends in // The rules are that the first word form is used if the count ends in
// 0, 5-9 or 11-19. The second form is used then the count ends in 1 // 0, 5-9 or 11-19. The second form is used then the count ends in 1
// (not including 11 as above) and the third form is used when the // (not including 11 as above) and the third form is used when the
// count ends in 2-4, again excluding any values ending in 12-14. // count ends in 2-4, again excluding any values ending in 12-14.
// //
// function zmVlang( $langVarArray, $count ) // function zmVlang( $langVarArray, $count )
// { // {
// $secondlastdigit = substr( $count, -2, 1 ); // $secondlastdigit = substr( $count, -2, 1 );
@ -917,7 +917,7 @@ function zmVlang( $langVarArray, $count )
// // or // // or
// // $secondlastdigit = ($count/10)%10; // // $secondlastdigit = ($count/10)%10;
// // $lastdigit = $count%10; // // $lastdigit = $count%10;
// //
// // Get rid of the special cases first, the teens // // Get rid of the special cases first, the teens
// if ( $secondlastdigit == 1 && $lastdigit != 0 ) // if ( $secondlastdigit == 1 && $lastdigit != 0 )
// { // {
@ -951,7 +951,7 @@ function zmVlang( $langVarArray, $count )
// die( 'Error, unable to correlate variable language string' ); // die( 'Error, unable to correlate variable language string' );
// } // }
// This is an example of how the function is used in the code which you can uncomment and // This is an example of how the function is used in the code which you can uncomment and
// use to test your custom function. // use to test your custom function.
//$monitors = array(); //$monitors = array();
//$monitors[] = 1; // Choose any number //$monitors[] = 1; // Choose any number
@ -968,17 +968,17 @@ $OLANG = array(
"\"reorder_queue_size=nnn\" Set number of packets to buffer for handling of reordered packets~~~~". "\"reorder_queue_size=nnn\" Set number of packets to buffer for handling of reordered packets~~~~".
"\"loglevel=debug\" Set verbosity of FFmpeg (quiet, panic, fatal, error, warning, info, verbose, debug)" "\"loglevel=debug\" Set verbosity of FFmpeg (quiet, panic, fatal, error, warning, info, verbose, debug)"
), ),
'OPTIONS_RTSPTrans' => array( 'OPTIONS_RTSPTrans' => array(
'Help' => "This sets the RTSP Transport Protocol for FFmpeg.~~ ". 'Help' => "This sets the RTSP Transport Protocol for FFmpeg.~~ ".
"TCP - Use TCP (interleaving within the RTSP control channel) as transport protocol.~~". "TCP - Use TCP (interleaving within the RTSP control channel) as transport protocol.~~".
"UDP - Use UDP as transport protocol. Higher resolution cameras have experienced some 'smearing' while using UDP, if so try TCP~~". "UDP - Use UDP as transport protocol. Higher resolution cameras have experienced some 'smearing' while using UDP, if so try TCP~~".
"UDP Multicast - Use UDP Multicast as transport protocol~~". "UDP Multicast - Use UDP Multicast as transport protocol~~".
"HTTP - Use HTTP tunneling as transport protocol, which is useful for passing proxies.~~" "HTTP - Use HTTP tunneling as transport protocol, which is useful for passing proxies.~~"
), ),
'OPTIONS_LIBVLC' => array( 'OPTIONS_LIBVLC' => array(
'Help' => "Parameters in this field are passed on to libVLC. Multiple parameters can be separated by ,~~ ". 'Help' => "Parameters in this field are passed on to libVLC. Multiple parameters can be separated by ,~~ ".
"Examples (do not enter quotes)~~~~". "Examples (do not enter quotes)~~~~".
"\"--rtp-client-port=nnn\" Set local port to use for rtp data~~~~". "\"--rtp-client-port=nnn\" Set local port to use for rtp data~~~~".
"\"--verbose=2\" Set verbosity of libVLC" "\"--verbose=2\" Set verbosity of libVLC"
), ),
'OPTIONS_EXIF' => array( 'OPTIONS_EXIF' => array(
@ -987,7 +987,7 @@ $OLANG = array(
'OPTIONS_RTSPDESCRIBE' => array( 'OPTIONS_RTSPDESCRIBE' => array(
'Help' => "Sometimes, during the initial RTSP handshake, the camera will send an updated media URL. ". 'Help' => "Sometimes, during the initial RTSP handshake, the camera will send an updated media URL. ".
"Enable this option to tell ZoneMinder to use this URL. Disable this option to ignore the ". "Enable this option to tell ZoneMinder to use this URL. Disable this option to ignore the ".
"value from the camera and use the value as entered in the monitor configuration~~~~". "value from the camera and use the value as entered in the monitor configuration~~~~".
"Generally this should be enabled. However, there are cases where the camera can get its". "Generally this should be enabled. However, there are cases where the camera can get its".
"own URL incorrect, such as when the camera is streaming through a firewall"), "own URL incorrect, such as when the camera is streaming through a firewall"),
'OPTIONS_MAXFPS' => array( 'OPTIONS_MAXFPS' => array(
@ -995,12 +995,12 @@ $OLANG = array(
"Failure to adhere to these limitations will cause a delay in live video, irregular frame skipping, ". "Failure to adhere to these limitations will cause a delay in live video, irregular frame skipping, ".
"and missed events~~". "and missed events~~".
"For streaming IP cameras, do not use this field to reduce the frame rate. Set the frame rate in the". "For streaming IP cameras, do not use this field to reduce the frame rate. Set the frame rate in the".
" camera, instead. You can, however, use a value that is slightly higher than the frame rate in the camera. ". " camera, instead. You can, however, use a value that is slightly higher than the frame rate in the camera. ".
"In this case, this helps keep the cpu from being overtaxed in the event of a network problem.~~". "In this case, this helps keep the cpu from being overtaxed in the event of a network problem.~~".
"Some, mostly older, IP cameras support snapshot mode. In this case ZoneMinder is actively polling the camera ". "Some, mostly older, IP cameras support snapshot mode. In this case ZoneMinder is actively polling the camera ".
"for new images. In this case, it is safe to use the field." "for new images. In this case, it is safe to use the field."
), ),
// 'LANG_DEFAULT' => array( // 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option", // 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked" // 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"

View File

@ -240,7 +240,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Please Donate', 'Donate' => 'Please Donate',
'DonateAlready' => 'No, I\'ve already donated', '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', 'DonateRemindDay' => 'Not yet, remind again in 1 day',
'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindHour' => 'Not yet, remind again in 1 hour',
'DonateRemindMonth' => 'Not yet, remind again in 1 month', 'DonateRemindMonth' => 'Not yet, remind again in 1 month',

View File

@ -289,7 +289,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18 'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18
'Donate' => 'Por favor, done', 'Donate' => 'Por favor, done',
'DonateAlready' => 'No, ya he donado', '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', 'DonateRemindDay' => 'Aún no, recordarme de nuevo en 1 día',
'DonateRemindHour' => 'Aún no, recordarme de nuevo en 1 hora', 'DonateRemindHour' => 'Aún no, recordarme de nuevo en 1 hora',
'DonateRemindMonth' => 'Aún no, recordarme de nuevo en 1 mes', 'DonateRemindMonth' => 'Aún no, recordarme de nuevo en 1 mes',

View File

@ -296,7 +296,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18 'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18
'Donate' => 'Palun Anneta', 'Donate' => 'Palun Anneta',
'DonateAlready' => 'EI, Ma olen juba annetanud', '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', 'DonateRemindDay' => 'Ei veel, tuleta meelde ühe päeva pärast',
'DonateRemindHour' => 'Ei veel, tuleta meelde ühe tunni pärast', 'DonateRemindHour' => 'Ei veel, tuleta meelde ühe tunni pärast',
'DonateRemindMonth' => 'Ei veel, tuleta meelde ühe kuu pärast', 'DonateRemindMonth' => 'Ei veel, tuleta meelde ühe kuu pärast',

View File

@ -295,7 +295,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Réaliser détection native', 'DoNativeMotionDetection'=> 'Réaliser détection native',
'Donate' => 'Veuillez faire un don', 'Donate' => 'Veuillez faire un don',
'DonateAlready' => 'Non, j\'ai déjà donné', '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', 'DonateRemindDay' => 'Pas encore, me rappeler dans 1 jour',
'DonateRemindHour' => 'Pas encore, me rappeler dans 1 heure', 'DonateRemindHour' => 'Pas encore, me rappeler dans 1 heure',
'DonateRemindMonth' => 'Pas encore, me rappeler dans 1 mois', 'DonateRemindMonth' => 'Pas encore, me rappeler dans 1 mois',

View File

@ -289,7 +289,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'úøåí áá÷ùä', 'Donate' => 'úøåí áá÷ùä',
'DonateAlready' => 'ìà, úøîúé ëáø', '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' => 'òãééï ìà, äæëø ìà áòåã éåí àçã', 'DonateRemindDay' => 'òãééï ìà, äæëø ìà áòåã éåí àçã',
'DonateRemindHour' => 'òãééï ìà, äæëø ìé áòåã ùòä àçú', 'DonateRemindHour' => 'òãééï ìà, äæëø ìé áòåã ùòä àçú',
'DonateRemindMonth' => 'òãééï ìà, äæëø ìé áòåã çåãù àçã', 'DonateRemindMonth' => 'òãééï ìà, äæëø ìé áòåã çåãù àçã',

View File

@ -332,7 +332,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18 'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18
'Donate' => 'Kérem támogasson', 'Donate' => 'Kérem támogasson',
'DonateAlready' => 'Nem, én már támogattam', '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', 'DonateRemindDay' => 'Nem most, figyelmeztessen egy nap múlva',
'DonateRemindHour' => 'Nem most, figyelmeztessen egy óra múlva', 'DonateRemindHour' => 'Nem most, figyelmeztessen egy óra múlva',
'DonateRemindMonth' => 'Nem most, figyelmeztessen egy hónap múlva', 'DonateRemindMonth' => 'Nem most, figyelmeztessen egy hónap múlva',

View File

@ -294,7 +294,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Donate,per favore', 'Donate' => 'Donate,per favore',
'DonateAlready' => 'No, ho gia donato... ', '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', 'DonateRemindDay' => 'Non ancora, ricordamelo ancora tra 1 giorno',
'DonateRemindHour' => 'Non ancora, ricordamelo ancora tra 1 ora', 'DonateRemindHour' => 'Non ancora, ricordamelo ancora tra 1 ora',
'DonateRemindMonth' => 'Non ancora, ricordamelo ancora tra 1 mese', 'DonateRemindMonth' => 'Non ancora, ricordamelo ancora tra 1 mese',

View File

@ -290,7 +290,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Please Donate', 'Donate' => 'Please Donate',
'DonateAlready' => 'No, I\'ve already donated', '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', 'DonateRemindDay' => 'Not yet, remind again in 1 day',
'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindHour' => 'Not yet, remind again in 1 hour',
'DonateRemindMonth' => 'Not yet, remind again in 1 month', 'DonateRemindMonth' => 'Not yet, remind again in 1 month',

View File

@ -290,7 +290,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18 'DoNativeMotionDetection'=> 'Do Native Motion Detection', // Added - 2015-04-18
'Donate' => 'Geef a.u.b. een donatie', 'Donate' => 'Geef a.u.b. een donatie',
'DonateAlready' => 'Nee, ik heb al gedoneerd', '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', 'DonateRemindDay' => 'Nu niet, herinner mij over 1 dag hieraan',
'DonateRemindHour' => 'Nu niet, herinner mij over een uur hieraan', 'DonateRemindHour' => 'Nu niet, herinner mij over een uur hieraan',
'DonateRemindMonth' => 'Nu niet, herinner mij over een maand hieraan', 'DonateRemindMonth' => 'Nu niet, herinner mij over een maand hieraan',

View File

@ -304,7 +304,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Please Donate', 'Donate' => 'Please Donate',
'DonateAlready' => 'No, I\'ve already donated', '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', 'DonateRemindDay' => 'Not yet, remind again in 1 day',
'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindHour' => 'Not yet, remind again in 1 hour',
'DonateRemindMonth' => 'Not yet, remind again in 1 month', 'DonateRemindMonth' => 'Not yet, remind again in 1 month',

View File

@ -229,7 +229,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Please Donate', 'Donate' => 'Please Donate',
'DonateAlready' => 'No, I\'ve already donated', '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', 'DonateRemindDay' => 'Not yet, remind again in 1 day',
'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindHour' => 'Not yet, remind again in 1 hour',
'DonateRemindMonth' => 'Not yet, remind again in 1 month', 'DonateRemindMonth' => 'Not yet, remind again in 1 month',

View File

@ -260,7 +260,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Please Donate', 'Donate' => 'Please Donate',
'DonateAlready' => 'No, I\'ve already donated', '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', 'DonateRemindDay' => 'Not yet, remind again in 1 day',
'DonateRemindHour' => 'Not yet, remind again in 1 hour', 'DonateRemindHour' => 'Not yet, remind again in 1 hour',
'DonateRemindMonth' => 'Not yet, remind again in 1 month', 'DonateRemindMonth' => 'Not yet, remind again in 1 month',

View File

@ -290,7 +290,7 @@ $SLANG = array(
'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'DoNativeMotionDetection'=> 'Do Native Motion Detection',
'Donate' => 'Var vänlig och donera', 'Donate' => 'Var vänlig och donera',
'DonateAlready' => 'Nej, Jag har redan donerat', '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', 'DonateRemindDay' => 'Inte än, påminn om 1 dag',
'DonateRemindHour' => 'Inte än, påminn om en 1 timme', 'DonateRemindHour' => 'Inte än, påminn om en 1 timme',
'DonateRemindMonth' => 'Inte än, påminn om 1 månad', 'DonateRemindMonth' => 'Inte än, påminn om 1 månad',

View File

@ -173,12 +173,12 @@ echo output_link_if_exists( array(
<script src="<?php echo cache_bust('skins/classic/js/base.js') ?>"></script> <script src="<?php echo cache_bust('skins/classic/js/base.js') ?>"></script>
<?php } ?> <?php } ?>
<script src="<?php echo cache_bust($skinJsFile) ?>"></script> <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 <?php
if ($basename == 'watch' or $basename == 'log' ) { 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 // 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 } ?>
<?php <?php
if ( $viewJsFile ) { if ( $viewJsFile ) {
@ -233,7 +233,12 @@ function getNavBarHTML($reload = null) {
ob_start(); ob_start();
if ( $running == null ) if ( $running == null )
$running = daemonCheck(); $running = daemonCheck();
$status = $running?translate('Running'):translate('Stopped'); if ( $running ) {
$state = dbFetchOne('SELECT Name FROM States WHERE isActive=1', 'Name');
if ( $state == 'default' )
$state = '';
}
$status = $running ? ($state ? $state : translate('Running')) : translate('Stopped');
?> ?>
<div class="navbar navbar-inverse navbar-static-top"> <div class="navbar navbar-inverse navbar-static-top">
<div class="container-fluid"> <div class="container-fluid">
@ -244,7 +249,9 @@ function getNavBarHTML($reload = null) {
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<div class="navbar-brand"><a href="<?php echo validHtmlStr(ZM_HOME_URL); ?>" target="<?php echo validHtmlStr(ZM_WEB_TITLE); ?>"><?php echo ZM_HOME_CONTENT ?></a></div> <div class="navbar-brand">
<a href="<?php echo validHtmlStr(ZM_HOME_URL); ?>" target="<?php echo validHtmlStr(ZM_WEB_TITLE); ?>"><?php echo ZM_HOME_CONTENT ?></a>
</div>
</div> </div>
<div class="collapse navbar-collapse" id="main-header-nav"> <div class="collapse navbar-collapse" id="main-header-nav">
@ -329,7 +336,7 @@ if (isset($_REQUEST['filter']['Query']['terms']['attr'])) {
</p> </p>
<?php } ?> <?php } ?>
<?php } else if ( canView('System') ) { ?> <?php } else if ( canView('System') ) { ?>
<p class="navbar-text"> <?php echo $status ?></p> <p class="navbar-text"><?php echo $status ?></p>
<?php } ?> <?php } ?>
</div> </div>
<?php } # end if !$user or $user['Id'] meaning logged in ?> <?php } # end if !$user or $user['Id'] meaning logged in ?>

View File

@ -64,6 +64,15 @@ function initPage() {
return false; return false;
}; };
}); });
}
window.addEventListener( 'DOMContentLoaded', initPage ); // 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);

View File

@ -39,7 +39,7 @@ if ( ! empty($_REQUEST['mid']) ) {
$monitor = new ZM\Monitor( $_REQUEST['mid'] ); $monitor = new ZM\Monitor( $_REQUEST['mid'] );
if ( $monitor and ZM_OPT_X10 ) if ( $monitor and ZM_OPT_X10 )
$x10Monitor = dbFetchOne('SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($_REQUEST['mid'])); $x10Monitor = dbFetchOne('SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($_REQUEST['mid']));
} }
if ( ! $monitor ) { if ( ! $monitor ) {
$nextId = getTableAutoInc('Monitors'); $nextId = getTableAutoInc('Monitors');
@ -132,19 +132,19 @@ if ( ! $monitor ) {
if ( ZM_OPT_X10 && empty($x10Monitor) ) { if ( ZM_OPT_X10 && empty($x10Monitor) ) {
$x10Monitor = array( $x10Monitor = array(
'Activation' => '', 'Activation' => '',
'AlarmInput' => '', 'AlarmInput' => '',
'AlarmOutput' => '', 'AlarmOutput' => '',
); );
} }
function fourcc( $a, $b, $c, $d ) { 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'] ) ) { if ( isset($_REQUEST['newMonitor']) ) {
# Update the monitor object with whatever has been set so far. # Update the monitor object with whatever has been set so far.
$monitor->set( $_REQUEST['newMonitor'] ); $monitor->set($_REQUEST['newMonitor']);
if ( ZM_OPT_X10 ) if ( ZM_OPT_X10 )
$newX10Monitor = $_REQUEST['newX10Monitor']; $newX10Monitor = $_REQUEST['newX10Monitor'];
@ -157,26 +157,27 @@ if ( isset( $_REQUEST['newMonitor'] ) ) {
# What if it has less zeros? This is not robust code. # What if it has less zeros? This is not robust code.
if ( $monitor->AnalysisFPSLimit() == '0.00' ) if ( $monitor->AnalysisFPSLimit() == '0.00' )
$monitor->AnalysisFPSLimit( '' ); $monitor->AnalysisFPSLimit('');
if ( $monitor->MaxFPS() == '0.00' ) if ( $monitor->MaxFPS() == '0.00' )
$monitor->MaxFPS( '' ); $monitor->MaxFPS('');
if ( $monitor->AlarmMaxFPS() == '0.00' ) if ( $monitor->AlarmMaxFPS() == '0.00' )
$monitor->AlarmMaxFPS( '' ); $monitor->AlarmMaxFPS('');
if ( !empty($_REQUEST['preset']) ) { if ( !empty($_REQUEST['preset']) ) {
$preset = dbFetchOne( 'SELECT Type, Device, Channel, Format, Protocol, Method, Host, Port, Path, Width, Height, Palette, MaxFPS, Controllable, ControlId, ControlDevice, ControlAddress, DefaultRate, DefaultScale FROM MonitorPresets WHERE Id = ?', NULL, array($_REQUEST['preset']) ); $preset = dbFetchOne( 'SELECT Type, Device, Channel, Format, Protocol, Method, Host, Port, Path, Width, Height, Palette, MaxFPS, Controllable, ControlId, ControlDevice, ControlAddress, DefaultRate, DefaultScale FROM MonitorPresets WHERE Id = ?', NULL, array($_REQUEST['preset']) );
foreach ( $preset as $name=>$value ) { foreach ( $preset as $name=>$value ) {
# Does isset handle NULL's? I don't think this code is correct. # Does isset handle NULL's? I don't think this code is correct.
if ( isset($value) ) { if ( isset($value) ) {
$monitor->$name = $value; $monitor->$name = $value;
} }
} }
} } # end if preset
if ( !empty($_REQUEST['probe']) ) { if ( !empty($_REQUEST['probe']) ) {
$probe = json_decode(base64_decode($_REQUEST['probe'])); $probe = json_decode(base64_decode($_REQUEST['probe']));
foreach ( $probe as $name=>$value ) { foreach ( $probe as $name=>$value ) {
if ( isset($value) ) { if ( isset($value) ) {
# Does isset handle NULL's? I don't think this code is correct. # Does isset handle NULL's? I don't think this code is correct.
$monitor->$name = urldecode($value); $monitor->$name = urldecode($value);
} }
} }
@ -187,7 +188,7 @@ if ( !empty($_REQUEST['probe']) ) {
elseif ( $monitor->Format() == 'NTSC' ) elseif ( $monitor->Format() == 'NTSC' )
$monitor->Format( 0x0000b000 ); $monitor->Format( 0x0000b000 );
} }
} } # end if apply probe settings
$sourceTypes = array( $sourceTypes = array(
'Local' => translate('Local'), 'Local' => translate('Local'),
@ -389,7 +390,7 @@ $orientations = array(
'90' => translate('RotateRight'), '90' => translate('RotateRight'),
'180' => translate('Inverted'), '180' => translate('Inverted'),
'270' => translate('RotateLeft'), '270' => translate('RotateLeft'),
'horz' => translate('FlippedHori'), 'hori' => translate('FlippedHori'),
'vert' => translate('FlippedVert') 'vert' => translate('FlippedVert')
); );
@ -458,14 +459,14 @@ $codecs = array(
'MJPEG' => translate('MJPEG'), 'MJPEG' => translate('MJPEG'),
); );
xhtmlHeaders(__FILE__, translate('Monitor')." - ".validHtmlStr($monitor->Name()) ); xhtmlHeaders(__FILE__, translate('Monitor').' - '.validHtmlStr($monitor->Name()));
getBodyTopHTML(); getBodyTopHTML();
?> ?>
<div id="page"> <div id="page">
<div id="header"> <div id="header">
<?php <?php
if ( canEdit( 'Monitors' ) ) { if ( canEdit('Monitors') ) {
if ( isset ($_REQUEST['dupId'])) { if ( isset($_REQUEST['dupId']) ) {
?> ?>
<div class="alert alert-info"> <div class="alert alert-info">
Configuration cloned from Monitor: <?php echo validHtmlStr($clonedName) ?> Configuration cloned from Monitor: <?php echo validHtmlStr($clonedName) ?>
@ -474,10 +475,10 @@ if ( canEdit( 'Monitors' ) ) {
} }
?> ?>
<div id="headerButtons"> <div id="headerButtons">
<?php echo makePopupLink('?view=monitorprobe&mid=' . $monitor->Id(), 'zmMonitorProbe' . $monitor->Id(), 'monitorprobe', translate('Probe')); ?> <?php echo makePopupLink('?view=monitorprobe&mid='.$monitor->Id(), 'zmMonitorProbe'.$monitor->Id(), 'monitorprobe', translate('Probe')); ?>
<?php <?php
if ( ZM_HAS_ONVIF ) { if ( ZM_HAS_ONVIF ) {
echo makePopupLink('?view=onvifprobe&mid=' . $monitor->Id(), 'zmOnvifProbe' . $monitor->Id(), 'onvifprobe', translate('OnvifProbe')); echo makePopupLink('?view=onvifprobe&mid='.$monitor->Id(), 'zmOnvifProbe'.$monitor->Id(), 'onvifprobe', translate('OnvifProbe'));
} }
?> ?>
<?php echo makePopupLink('?view=monitorpreset&mid=' . $monitor->Id(), 'zmMonitorPreset' . $monitor->Id(), 'monitorpreset', translate('Presets')); ?> <?php echo makePopupLink('?view=monitorpreset&mid=' . $monitor->Id(), 'zmMonitorPreset' . $monitor->Id(), 'monitorpreset', translate('Presets')); ?>
@ -497,7 +498,7 @@ if ( $monitor->Type() != 'WebSite' ) {
$tabs['storage'] = translate('Storage'); $tabs['storage'] = translate('Storage');
$tabs['timestamp'] = translate('Timestamp'); $tabs['timestamp'] = translate('Timestamp');
$tabs['buffers'] = translate('Buffers'); $tabs['buffers'] = translate('Buffers');
if ( ZM_OPT_CONTROL && canView( 'Control' ) ) if ( ZM_OPT_CONTROL && canView('Control') )
$tabs['control'] = translate('Control'); $tabs['control'] = translate('Control');
if ( ZM_OPT_X10 ) if ( ZM_OPT_X10 )
$tabs['x10'] = translate('X10'); $tabs['x10'] = translate('X10');
@ -686,7 +687,7 @@ switch ( $tab ) {
?> ?>
<tr class="Name"><td><?php echo translate('Name') ?></td><td><input type="text" name="newMonitor[Name]" value="<?php echo validHtmlStr($monitor->Name()) ?>" /></td></tr> <tr class="Name"><td><?php echo translate('Name') ?></td><td><input type="text" name="newMonitor[Name]" value="<?php echo validHtmlStr($monitor->Name()) ?>" /></td></tr>
<tr><td><?php echo translate('Server') ?></td><td> <tr><td><?php echo translate('Server') ?></td><td>
<?php <?php
$servers = array(''=>'None','auto'=>'Auto'); $servers = array(''=>'None','auto'=>'Auto');
foreach ( ZM\Server::find(NULL, array('order'=>'lower(Name)')) as $Server ) { foreach ( ZM\Server::find(NULL, array('order'=>'lower(Name)')) as $Server ) {
$servers[$Server->Id()] = $Server->Name(); $servers[$Server->Id()] = $Server->Name();
@ -766,7 +767,7 @@ echo htmlOptions(ZM\Group::get_dropdown_options( ), $monitor->GroupIds() );
<td><?php echo translate('RefImageBlendPct') ?></td> <td><?php echo translate('RefImageBlendPct') ?></td>
<td><select name="newMonitor[RefBlendPerc]"><?php foreach ( $fastblendopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->RefBlendPerc() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td> <td><select name="newMonitor[RefBlendPerc]"><?php foreach ( $fastblendopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->RefBlendPerc() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td>
</tr> </tr>
<tr> <tr>
<td><?php echo translate('AlarmRefImageBlendPct') ?></td> <td><?php echo translate('AlarmRefImageBlendPct') ?></td>
<td> <td>
<select name="newMonitor[AlarmRefBlendPerc]"> <select name="newMonitor[AlarmRefBlendPerc]">
@ -843,7 +844,7 @@ echo htmlOptions(ZM\Group::get_dropdown_options( ), $monitor->GroupIds() );
</td></tr> </td></tr>
<tr><td><?php echo translate('V4LCapturesPerFrame') ?></td><td><input type="number" name="newMonitor[V4LCapturesPerFrame]" value="<?php echo validHtmlStr($monitor->V4LCapturesPerFrame()); ?>"/></td></tr> <tr><td><?php echo translate('V4LCapturesPerFrame') ?></td><td><input type="number" name="newMonitor[V4LCapturesPerFrame]" value="<?php echo validHtmlStr($monitor->V4LCapturesPerFrame()); ?>"/></td></tr>
<?php <?php
} else if ( $monitor->Type() == 'NVSocket' ) { } else if ( $monitor->Type() == 'NVSocket' ) {
include('_monitor_source_nvsocket.php'); include('_monitor_source_nvsocket.php');
} else if ( $monitor->Type() == 'Remote' ) { } else if ( $monitor->Type() == 'Remote' ) {
@ -897,10 +898,10 @@ if ( $monitor->Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) {
</td></tr> </td></tr>
<tr><td><?php echo translate('CaptureWidth') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Width]" value="<?php echo validHtmlStr($monitor->Width()) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr> <tr><td><?php echo translate('CaptureWidth') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Width]" value="<?php echo validHtmlStr($monitor->Width()) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr>
<tr><td><?php echo translate('CaptureHeight') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Height]" value="<?php echo validHtmlStr($monitor->Height()) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr> <tr><td><?php echo translate('CaptureHeight') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Height]" value="<?php echo validHtmlStr($monitor->Height()) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr>
<tr><td><?php echo translate('PreserveAspect') ?></td><td><input type="checkbox" name="preserveAspectRatio" value="1"/></td></tr> <tr><td><?php echo translate('PreserveAspect') ?></td><td><input type="checkbox" name="preserveAspectRatio" value="1"/></td></tr>
<tr><td><?php echo translate('Orientation') ?></td><td><?php echo htmlselect( 'newMonitor[Orientation]', $orientations, $monitor->Orientation() );?></td></tr> <tr><td><?php echo translate('Orientation') ?></td><td><?php echo htmlselect( 'newMonitor[Orientation]', $orientations, $monitor->Orientation() );?></td></tr>
<?php <?php
} }
if ( $monitor->Type() == 'Local' ) { if ( $monitor->Type() == 'Local' ) {
?> ?>
<tr><td><?php echo translate('Deinterlacing') ?></td><td><select name="newMonitor[Deinterlacing]"><?php foreach ( $deinterlaceopts_v4l2 as $name => $value ) { ?><option value="<?php echo validHtmlStr($value); ?>"<?php if ( $value == $monitor->Deinterlacing()) { ?> selected="selected"<?php } ?>><?php echo validHtmlStr($name); ?></option><?php } ?></select></td></tr> <tr><td><?php echo translate('Deinterlacing') ?></td><td><select name="newMonitor[Deinterlacing]"><?php foreach ( $deinterlaceopts_v4l2 as $name => $value ) { ?><option value="<?php echo validHtmlStr($value); ?>"<?php if ( $value == $monitor->Deinterlacing()) { ?> selected="selected"<?php } ?>><?php echo validHtmlStr($name); ?></option><?php } ?></select></td></tr>
@ -923,7 +924,7 @@ if ( $monitor->Type() == 'Local' ) {
?> ?>
<tr><td><?php echo translate('SaveJPEGs') ?></td><td><select name="newMonitor[SaveJPEGs]"><?php foreach ( $savejpegopts as $name => $value ) { ?><option value="<?php echo validHtmlStr($value); ?>"<?php if ( $value == $monitor->SaveJPEGs() ) { ?> selected="selected"<?php } ?>><?php echo validHtmlStr($name); ?></option><?php } ?></select></td></tr> <tr><td><?php echo translate('SaveJPEGs') ?></td><td><select name="newMonitor[SaveJPEGs]"><?php foreach ( $savejpegopts as $name => $value ) { ?><option value="<?php echo validHtmlStr($value); ?>"<?php if ( $value == $monitor->SaveJPEGs() ) { ?> selected="selected"<?php } ?>><?php echo validHtmlStr($name); ?></option><?php } ?></select></td></tr>
<tr><td><?php echo translate('VideoWriter') ?></td><td> <tr><td><?php echo translate('VideoWriter') ?></td><td>
<?php <?php
$videowriteropts = array( $videowriteropts = array(
0 => 'Disabled', 0 => 'Disabled',
); );

View File

@ -353,18 +353,18 @@ foreach ( array_map('basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<input type="checkbox" id="<?php echo $name ?>" name="newConfig[<?php echo $name ?>]" value="1"<?php if ( $value['Value'] ) { ?> checked="checked"<?php } ?><?php echo $canEdit?'':' disabled="disabled"' ?>/> <input type="checkbox" id="<?php echo $name ?>" name="newConfig[<?php echo $name ?>]" value="1"<?php if ( $value['Value'] ) { ?> checked="checked"<?php } ?><?php echo $canEdit?'':' disabled="disabled"' ?>/>
<?php <?php
} elseif ( is_array( $value['Hint'] ) ) { } elseif ( is_array( $value['Hint'] ) ) {
echo htmlSelect( "newConfig[$name]", $value['Hint'], $value['Value'] ); echo htmlSelect("newConfig[$name]", $value['Hint'], $value['Value']);
} elseif ( preg_match( '/\|/', $value['Hint'] ) ) { } elseif ( preg_match('/\|/', $value['Hint']) ) {
?> ?>
<?php <?php
$options = explode( '|', $value['Hint'] ); $options = explode('|', $value['Hint']);
if ( count( $options ) > 3 ) { if ( count($options) > 3 ) {
?> ?>
<select class="form-control" name="newConfig[<?php echo $name ?>]"<?php echo $canEdit?'':' disabled="disabled"' ?>> <select class="form-control" name="newConfig[<?php echo $name ?>]"<?php echo $canEdit?'':' disabled="disabled"' ?>>
<?php <?php
foreach ( $options as $option ) { foreach ( $options as $option ) {
if ( preg_match( '/^([^=]+)=(.+)$/', $option, $matches ) ) { if ( preg_match('/^([^=]+)=(.+)$/', $option, $matches) ) {
$optionLabel = $matches[1]; $optionLabel = $matches[1];
$optionValue = $matches[2]; $optionValue = $matches[2];
} else { } else {
@ -379,7 +379,7 @@ foreach ( array_map('basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<?php <?php
} else { } else {
foreach ( $options as $option ) { foreach ( $options as $option ) {
if ( preg_match( '/^([^=]+)=(.+)$/', $option ) ) { if ( preg_match('/^([^=]+)=(.+)$/', $option) ) {
$optionLabel = $matches[1]; $optionLabel = $matches[1];
$optionValue = $matches[2]; $optionValue = $matches[2];
} else { } else {
@ -387,7 +387,7 @@ foreach ( array_map('basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
} }
?> ?>
<label> <label>
<input type="radio" id="<?php echo $name.'_'.preg_replace( '/[^a-zA-Z0-9]/', '', $optionValue ) ?>" name="newConfig[<?php echo $name ?>]" value="<?php echo $optionValue ?>"<?php if ( $value['Value'] == $optionValue ) { ?> checked="checked"<?php } ?><?php echo $canEdit?'':' disabled="disabled"' ?>/> <input type="radio" id="<?php echo $name.'_'.preg_replace('/[^a-zA-Z0-9]/', '', $optionValue) ?>" name="newConfig[<?php echo $name ?>]" value="<?php echo $optionValue ?>"<?php if ( $value['Value'] == $optionValue ) { ?> checked="checked"<?php } ?><?php echo $canEdit?'':' disabled="disabled"' ?>/>
<?php echo htmlspecialchars($optionLabel) ?> <?php echo htmlspecialchars($optionLabel) ?>
</label> </label>
<?php <?php

View File

@ -54,7 +54,9 @@ if ( $running ) {
$states = dbFetchAll('SELECT * FROM States'); $states = dbFetchAll('SELECT * FROM States');
foreach ( $states as $state ) { foreach ( $states as $state ) {
?> ?>
<option value="<?php echo validHtmlStr($state['Name']) ?>"><?php echo validHtmlStr($state['Name']); ?></option> <option value="<?php echo validHtmlStr($state['Name']) ?>" <?php echo $state['IsActive'] ? 'selected="selected"' : '' ?>>
<?php echo validHtmlStr($state['Name']); ?>
</option>
<?php <?php
} }
?> ?>

View File

@ -46,7 +46,7 @@ if ( isset($_REQUEST['scale']) )
else else
$scale = reScale(SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE); $scale = reScale(SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE);
$Event = new Event($event['Id']); $Event = new ZM\Event($event['Id']);
$eventPath = $Event->Path(); $eventPath = $Event->Path();
$videoFormats = array(); $videoFormats = array();