Merge branch 'master' into storageareas
This commit is contained in:
commit
25614bb75e
|
@ -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;
|
||||||
|
|
|
@ -789,7 +789,7 @@ INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0
|
||||||
INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0);
|
INSERT INTO `Controls` VALUES (NULL,'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,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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -947,9 +947,15 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
|
||||||
if ( ret < 0 ) {
|
if ( ret < 0 ) {
|
||||||
av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||||
Warning("Unable to receive frame %d: %s, continuing", frameCount, errbuf);
|
Warning("Unable to receive frame %d: %s, continuing", frameCount, errbuf);
|
||||||
|
error_count += 1;
|
||||||
|
if ( error_count > 100 ) {
|
||||||
|
Error("Error count over 100, going to close and re-open stream");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
zm_av_packet_unref(&packet);
|
zm_av_packet_unref(&packet);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if ( error_count > 0 ) error_count --;
|
||||||
|
|
||||||
#if HAVE_AVUTIL_HWCONTEXT_H
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 )
|
||||||
|
@ -63,13 +78,13 @@ RemoteCameraRtsp::RemoteCameraRtsp( unsigned int p_monitor_id, const std::string
|
||||||
mConvertContext = NULL;
|
mConvertContext = NULL;
|
||||||
#endif
|
#endif
|
||||||
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
|
||||||
if(colours == ZM_COLOUR_RGB32) {
|
if ( colours == ZM_COLOUR_RGB32 ) {
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||||
imagePixFormat = AV_PIX_FMT_RGBA;
|
imagePixFormat = AV_PIX_FMT_RGBA;
|
||||||
} else if(colours == ZM_COLOUR_RGB24) {
|
} else if ( colours == ZM_COLOUR_RGB24 ) {
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||||
imagePixFormat = AV_PIX_FMT_RGB24;
|
imagePixFormat = AV_PIX_FMT_RGB24;
|
||||||
} else if(colours == ZM_COLOUR_GRAY8) {
|
} else if ( colours == ZM_COLOUR_GRAY8 ) {
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||||
imagePixFormat = AV_PIX_FMT_GRAY8;
|
imagePixFormat = AV_PIX_FMT_GRAY8;
|
||||||
} else {
|
} else {
|
||||||
|
@ -169,7 +184,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 ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO )
|
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO )
|
||||||
#else
|
#else
|
||||||
|
@ -181,6 +196,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
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,7 @@
|
||||||
// accessed over a network connection using rtsp protocol
|
// accessed over a network connection using rtsp protocol
|
||||||
// (Real Time Streaming Protocol)
|
// (Real Time Streaming Protocol)
|
||||||
//
|
//
|
||||||
class RemoteCameraRtsp : public RemoteCamera
|
class RemoteCameraRtsp : public RemoteCamera {
|
||||||
{
|
|
||||||
protected:
|
protected:
|
||||||
struct sockaddr_in rtsp_sa;
|
struct sockaddr_in rtsp_sa;
|
||||||
struct sockaddr_in rtcp_sa;
|
struct sockaddr_in rtcp_sa;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
213
src/zm_rtsp.cpp
213
src/zm_rtsp.cpp
|
@ -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
|
||||||
|
|
197
src/zm_sdp.cpp
197
src/zm_sdp.cpp
|
@ -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
|
||||||
|
|
|
@ -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];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue