diff --git a/db/triggers.sql b/db/triggers.sql index fb7a32b67..87c8465b4 100644 --- a/db/triggers.sql +++ b/db/triggers.sql @@ -4,7 +4,7 @@ DROP TRIGGER IF EXISTS Events_Hour_delete_trigger// CREATE TRIGGER Events_Hour_delete_trigger BEFORE DELETE ON Events_Hour FOR EACH ROW BEGIN UPDATE Monitors SET - HourEvents = COALESCE(HourEvents,1)-1, + HourEvents = GREATEST(COALESCE(HourEvents,1)-1,0), HourEventDiskSpace=GREATEST(COALESCE(HourEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Id=OLD.MonitorId; END; @@ -62,7 +62,7 @@ DROP TRIGGER IF EXISTS Events_Week_delete_trigger// CREATE TRIGGER Events_Week_delete_trigger BEFORE DELETE ON Events_Week FOR EACH ROW BEGIN UPDATE Monitors SET - WeekEvents = COALESCE(WeekEvents,1)-1, + WeekEvents = GREATEST(COALESCE(WeekEvents,1)-1,0), WeekEventDiskSpace=GREATEST(COALESCE(WeekEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Id=OLD.MonitorId; END; @@ -90,7 +90,7 @@ DROP TRIGGER IF EXISTS Events_Month_delete_trigger// CREATE TRIGGER Events_Month_delete_trigger BEFORE DELETE ON Events_Month FOR EACH ROW BEGIN UPDATE Monitors SET - MonthEvents = COALESCE(MonthEvents,1)-1, + MonthEvents = GREATEST(COALESCE(MonthEvents,1)-1,0), MonthEventDiskSpace=GREATEST(COALESCE(MonthEventDiskSpace,0)-COALESCE(OLD.DiskSpace,0),0) WHERE Id=OLD.MonitorId; END; diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index c197d09ab..787854091 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -789,7 +789,7 @@ INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0 INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'PSIA','Remote','PSIA',0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,-100,100,0,0,1,0,0,0,0,1,-100,100,0,0,0,0); -INSERT INTO `Controls` VALUES (NULL,'Dahua','Remote','Dahua',0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Dahua','Ffmpeg','Dahua',0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'FOSCAMR2C','Libvlc','FOSCAMR2C',1,1,1,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,0,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,0,0); INSERT INTO `Controls` VALUES (NULL,'Amcrest HTTP API','Ffmpeg','Amcrest_HTTP',0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,5); diff --git a/db/zm_update-1.33.8.sql b/db/zm_update-1.33.8.sql new file mode 100644 index 000000000..a3fed9629 --- /dev/null +++ b/db/zm_update-1.33.8.sql @@ -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; + diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Dahua.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Dahua.pm index e55a6da1c..23245a2ea 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/Dahua.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Dahua.pm @@ -381,6 +381,22 @@ sub focusRelFar Debug("focusRelFar response: " . $response); } +sub irisRelOpen +{ + my $self = shift; + + my $response = $self->_sendPtzCommand("start", "IrisLarge", 0, 1, 0, 0); + Debug("irisRelOpen response: " . $response); +} + +sub irisRelClose +{ + my $self = shift; + + my $response = $self->_sendPtzCommand("start", "IrisSmall", 0, 1, 0, 0); + Debug("irisRelClose response: " . $response); +} + sub moveStop { my $self = shift; @@ -592,6 +608,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. We pass in a 1 for that as it does not seem to matter what number (0-8) is provided, the camera focus behaves the same. +=head2 irisRel + + This set of methods invoke realtive iris size in the direction indicated by + the portion of their name. They accept no arguments. + + NOTE: + + This only just does work. The Dahua API specifies "multiples" as the input. + We pass in a 1 for that as it does not seem to matter what number (0-8) is + provided, the camera iris behaves the same. + =head2 moveStop This method attempts to stop the camera. The problem is that if continuous diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm index efffd2d19..2d9573299 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm @@ -28,6 +28,9 @@ package ZoneMinder::Control::Netcat; use 5.006; use strict; use warnings; +use MIME::Base64; +use Digest::SHA; +use DateTime; require ZoneMinder::Base; require ZoneMinder::Control; @@ -36,6 +39,8 @@ our @ISA = qw(ZoneMinder::Control); our %CamParams = (); +our ($profileToken, $address, $port, %identity); + # ========================================================================== # # Netcat IP Control Protocol @@ -50,7 +55,6 @@ our %CamParams = (); # # # Possible future improvements (for anyone to improve upon): -# - Onvif authentication # - Build the SOAP commands at runtime rather than use templates # - Implement previously mentioned advanced features # @@ -58,9 +62,10 @@ our %CamParams = (); # more dependencies to ZoneMinder is always a concern. # # On ControlAddress use the format : -# ADDRESS:PORT +# [USERNAME:PASSWORD@]ADDRESS:PORT # eg : 10.1.2.1:8899 # 10.0.100.1:8899 +# username:password@10.0.100.1:8899 # # Use port 8899 for the Netcat camera # @@ -79,6 +84,11 @@ sub open { $self->loadMonitor(); + $profileToken = $self->{Monitor}->{ControlDevice}; + if ($profileToken eq '') { $profileToken = '000'; } + + parseControlAddress($self->{Monitor}->{ControlAddress}); + use LWP::UserAgent; $self->{ua} = LWP::UserAgent->new; $self->{ua}->agent('ZoneMinder Control Agent/'.ZoneMinder::Base::ZM_VERSION); @@ -86,6 +96,39 @@ sub open { $self->{state} = 'open'; } +sub parseControlAddress +{ + my $controlAddress = shift; + my ($usernamepassword, $addressport) = split /@/, $controlAddress; + if ( !defined $addressport ) { + # If value of "Control address" does not consist of two parts, then only address is given + $addressport = $usernamepassword; + } else { + my ($username , $password) = split /:/, $usernamepassword; + %identity = (username => "$username", password => "$password"); + } + ($address, $port) = split /:/, $addressport; +} + +sub digestBase64 +{ + my ($nonce, $date, $password) = @_; + my $shaGenerator = Digest::SHA->new(1); + $shaGenerator->add($nonce . $date . $password); + return encode_base64($shaGenerator->digest, ""); +} + +sub authentificationHeader +{ + my ($username, $password) = @_; + my $nonce; + $nonce .= chr(int(rand(254))) for (0 .. 20); + my $nonceBase64 = encode_base64($nonce, ""); + my $currentDate = DateTime->now()->iso8601().'Z'; + + return '' . $username . '' . digestBase64($nonce, $currentDate, $password) . '' . $nonceBase64 . '' . $currentDate . ''; +} + sub printMsg { my $self = shift; my $msg = shift; @@ -103,10 +146,10 @@ sub sendCmd { printMsg($cmd, 'Tx'); - my $server_endpoint = 'http://'.$self->{Monitor}->{ControlAddress}.'/'.$cmd; + my $server_endpoint = 'http://' . $address . ':' . $port . "/$cmd"; my $req = HTTP::Request->new(POST => $server_endpoint); $req->header('content-type' => $content_type); - $req->header('Host' => $self->{Monitor}->{ControlAddress}); + $req->header('Host' => $address . ':' . $port); $req->header('content-length' => length($msg)); $req->header('accept-encoding' => 'gzip, deflate'); $req->header('connection' => 'Close'); @@ -125,10 +168,10 @@ sub sendCmd { sub getCamParams { my $self = shift; my $msg = '000'; - my $server_endpoint = 'http://'.$self->{Monitor}->{ControlAddress}.'/onvif/imaging'; + my $server_endpoint = 'http://' . $address . ':' . $port . '/onvif/imaging'; my $req = HTTP::Request->new(POST => $server_endpoint); $req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"'); - $req->header('Host' => $self->{Monitor}->{ControlAddress}); + $req->header('Host' => $address . ':' . $port); $req->header('content-length' => length($msg)); $req->header('accept-encoding' => 'gzip, deflate'); $req->header('connection' => 'Close'); @@ -160,7 +203,7 @@ sub autoStop { if ( $autostop ) { Debug('Auto Stop'); my $cmd = 'onvif/PTZ'; - my $msg = '000truefalse'; + my $msg = '' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . 'truefalse'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; usleep($autostop); $self->sendCmd($cmd, $msg, $content_type); @@ -172,7 +215,7 @@ sub reset { Debug('Camera Reset'); my $self = shift; my $cmd = ''; - my $msg = ''; + my $msg = '' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"'; $self->sendCmd($cmd, $msg, $content_type); } @@ -182,7 +225,7 @@ sub moveConUp { Debug('Move Up'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -193,7 +236,7 @@ sub moveConDown { Debug('Move Down'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -204,7 +247,7 @@ sub moveConLeft { Debug('Move Left'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -215,7 +258,7 @@ sub moveConRight { Debug('Move Right'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -226,7 +269,7 @@ sub zoomConTele { Debug('Zoom Tele'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -237,7 +280,7 @@ sub zoomConWide { Debug('Zoom Wide'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -249,7 +292,7 @@ sub moveConUpRight { Debug('Move Diagonally Up Right'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -261,7 +304,7 @@ sub moveConDownRight { Debug('Move Diagonally Down Right'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -273,7 +316,7 @@ sub moveConUpLeft { Debug('Move Diagonally Up Left'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -285,7 +328,7 @@ sub moveConDownLeft { Debug('Move Diagonally Down Left'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); $self->autoStop($self->{Monitor}->{AutoStopTimeout}); @@ -296,7 +339,7 @@ sub moveStop { Debug('Move Stop'); my $self = shift; my $cmd = 'onvif/PTZ'; - my $msg ='000truefalse'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . 'truefalse'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; $self->sendCmd($cmd, $msg, $content_type); } @@ -308,7 +351,7 @@ sub presetSet { my $preset = $self->getParam($params, 'preset'); Debug("Set Preset $preset"); my $cmd = 'onvif/PTZ'; - my $msg ='000'.$preset.''; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''.$preset.''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/SetPreset"'; $self->sendCmd($cmd, $msg, $content_type); } @@ -320,7 +363,7 @@ sub presetGoto { my $preset = $self->getParam($params, 'preset'); Debug("Goto Preset $preset"); my $cmd = 'onvif/PTZ'; - my $msg ='000'.$preset.''; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '' . $profileToken . ''.$preset.''; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"'; $self->sendCmd( $cmd, $msg, $content_type ); } @@ -362,7 +405,7 @@ sub irisAbsOpen { $CamParams{Brightness} = $max if ($CamParams{Brightness} > $max); my $cmd = 'onvif/imaging'; - my $msg ='000'.$CamParams{Brightness}.'true'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '000'.$CamParams{Brightness}.'true'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; $self->sendCmd( $cmd, $msg, $content_type ); } @@ -381,7 +424,7 @@ sub irisAbsClose $CamParams{Brightness} = $min if ($CamParams{Brightness} < $min); my $cmd = 'onvif/imaging'; - my $msg ='000'.$CamParams{Brightness}.'true'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '000'.$CamParams{Brightness}.'true'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; $self->sendCmd( $cmd, $msg, $content_type ); } @@ -399,7 +442,7 @@ sub whiteAbsIn { $CamParams{Contrast} = $max if ($CamParams{Contrast} > $max); my $cmd = 'onvif/imaging'; - my $msg ='000'.$CamParams{Contrast}.'true'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '000'.$CamParams{Contrast}.'true'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; } @@ -416,7 +459,7 @@ sub whiteAbsOut { $CamParams{Contrast} = $min if ($CamParams{Contrast} < $min); my $cmd = 'onvif/imaging'; - my $msg ='000'.$CamParams{Contrast}.'true'; + my $msg ='' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '000'.$CamParams{Contrast}.'true'; my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; } diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 152f426f2..dc2a32475 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -947,9 +947,15 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event if ( ret < 0 ) { av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE); Warning("Unable to receive frame %d: %s, continuing", frameCount, errbuf); + error_count += 1; + if ( error_count > 100 ) { + Error("Error count over 100, going to close and re-open stream"); + return -1; + } zm_av_packet_unref(&packet); continue; } + if ( error_count > 0 ) error_count --; #if HAVE_AVUTIL_HWCONTEXT_H } diff --git a/src/zm_remote_camera_rtsp.cpp b/src/zm_remote_camera_rtsp.cpp index 7e2143203..1f94f4849 100644 --- a/src/zm_remote_camera_rtsp.cpp +++ b/src/zm_remote_camera_rtsp.cpp @@ -28,7 +28,22 @@ #include #include -RemoteCameraRtsp::RemoteCameraRtsp( unsigned int p_monitor_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) : +RemoteCameraRtsp::RemoteCameraRtsp( + unsigned int p_monitor_id, + const std::string &p_method, + const std::string &p_host, + const std::string &p_port, + const std::string &p_path, + int p_width, + int p_height, + bool p_rtsp_describe, + int p_colours, + int p_brightness, + int p_contrast, + int p_hue, + int p_colour, + bool p_capture, + bool p_record_audio ) : RemoteCamera( p_monitor_id, "rtsp", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ), rtsp_describe( p_rtsp_describe ), rtspThread( 0 ) @@ -63,13 +78,13 @@ RemoteCameraRtsp::RemoteCameraRtsp( unsigned int p_monitor_id, const std::string mConvertContext = NULL; #endif /* 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; imagePixFormat = AV_PIX_FMT_RGBA; - } else if(colours == ZM_COLOUR_RGB24) { + } else if ( colours == ZM_COLOUR_RGB24 ) { subpixelorder = ZM_SUBPIX_ORDER_RGB; imagePixFormat = AV_PIX_FMT_RGB24; - } else if(colours == ZM_COLOUR_GRAY8) { + } else if ( colours == ZM_COLOUR_GRAY8 ) { subpixelorder = ZM_SUBPIX_ORDER_NONE; imagePixFormat = AV_PIX_FMT_GRAY8; } else { @@ -169,7 +184,7 @@ int RemoteCameraRtsp::PrimeCapture() { } else { Debug(2, "Have another video stream." ); } - } + } else #if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO ) #else @@ -181,6 +196,8 @@ int RemoteCameraRtsp::PrimeCapture() { } else { Debug(2, "Have another audio stream." ); } + } else { + Debug(1, "Have unknown codec type in stream %d : %d", i, mFormatContext->streams[i]->codec->codec_type); } } // end foreach stream diff --git a/src/zm_remote_camera_rtsp.h b/src/zm_remote_camera_rtsp.h index b3e392a77..5fa5a0778 100644 --- a/src/zm_remote_camera_rtsp.h +++ b/src/zm_remote_camera_rtsp.h @@ -34,8 +34,7 @@ // accessed over a network connection using rtsp protocol // (Real Time Streaming Protocol) // -class RemoteCameraRtsp : public RemoteCamera -{ +class RemoteCameraRtsp : public RemoteCamera { protected: struct sockaddr_in rtsp_sa; struct sockaddr_in rtcp_sa; diff --git a/src/zm_rtp_ctrl.cpp b/src/zm_rtp_ctrl.cpp index b586c979f..bc6725c5d 100644 --- a/src/zm_rtp_ctrl.cpp +++ b/src/zm_rtp_ctrl.cpp @@ -28,12 +28,12 @@ #include -RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ) : mRtspThread( rtspThread ), mRtpSource( rtpSource ), mStop( false ) +RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ) + : mRtspThread( rtspThread ), mRtpSource( rtpSource ), mStop( false ) { } -int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) -{ +int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) { const RtcpPacket *rtcpPacket; rtcpPacket = (RtcpPacket *)packet; @@ -48,33 +48,24 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) int pt = rtcpPacket->header.pt; int len = ntohs(rtcpPacket->header.lenN); - Debug( 5, "RTCP Ver: %d", ver ); - Debug( 5, "RTCP Count: %d", count ); - Debug( 5, "RTCP Pt: %d", pt ); - Debug( 5, "RTCP len: %d", len ); + Debug( 5, "RTCP Ver: %d Count: %d Pt: %d len: %d", ver, count, pt, len); - switch( pt ) - { + switch( pt ) { case RTCP_SR : { uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN); Debug( 5, "RTCP Got SR (%x)", ssrc ); - if ( mRtpSource.getSsrc() ) - { - if ( ssrc != mRtpSource.getSsrc() ) - { + if ( mRtpSource.getSsrc() ) { + if ( ssrc != mRtpSource.getSsrc() ) { Warning( "Discarding packet for unrecognised ssrc %x", ssrc ); return( -1 ); } - } - else if ( ssrc ) - { + } else if ( ssrc ) { mRtpSource.setSsrc( ssrc ); } - if ( len > 1 ) - { + if ( len > 1 ) { //printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts ); uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN); uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN); @@ -89,25 +80,21 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) case RTCP_SDES : { ssize_t contentLen = packetLen - sizeof(rtcpPacket->header); - while ( contentLen ) - { + while ( contentLen ) { Debug( 5, "RTCP CL: %zd", contentLen ); uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN); Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count ); - if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) - { + if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) { Warning( "Discarding packet for unrecognised ssrc %x", ssrc ); return( -1 ); } unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item; - for ( int i = 0; i < count; i++ ) - { + for ( int i = 0; i < count; i++ ) { RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr; Debug( 5, "RTCP Item length %d", item->len ); - switch( item->type ) - { + switch( item->type ) { case RTCP_SDES_CNAME : { std::string cname( item->data, item->len ); @@ -123,50 +110,39 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) case RTCP_SDES_NOTE : case RTCP_SDES_PRIV : default : - { Error( "Received unexpected SDES item type %d, ignoring", item->type ); - return( -1 ); - } + return -1; } int paddedLen = 4+2+item->len+1; // Add null byte paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4 - Debug( 5, "RTCP PL:%d", paddedLen ); + Debug(5, "RTCP PL:%d", paddedLen); sdesPtr += paddedLen; contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0; } - } + } // end whiel contentLen break; } case RTCP_BYE : - { - Debug( 5, "RTCP Got BYE" ); + Debug(5, "RTCP Got BYE"); mStop = true; break; - } case RTCP_APP : - { // Ignoring as per RFC 3550 - Debug( 5, "Received RTCP_APP packet, ignoring."); + Debug(5, "Received RTCP_APP packet, ignoring."); break; - } case RTCP_RR : - { - Error( "Received RTCP_RR packet." ); - return( -1 ); - } + Error("Received RTCP_RR packet."); + return -1; default : - { // 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; - } } consumed = sizeof(uint32_t)*(len+1); - return( consumed ); + return consumed; } -int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen ) -{ +int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen ) { RtcpPacket *rtcpPacket = (RtcpPacket *)packet; int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]); @@ -180,11 +156,13 @@ int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen ) mRtpSource.updateRtcpStats(); - Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 ); - Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() ); - Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() ); - Debug( 5, "Jitter = %d", mRtpSource.getJitter() ); - Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() ); + Debug(5, "Ssrc = %d Ssrc_1 = %d Last Seq = %d Jitter = %d Last SR = %d", + mRtspThread.getSsrc()+1, + mRtpSource.getSsrc(), + mRtpSource.getMaxSeq(), + mRtpSource.getJitter(), + mRtpSource.getLastSrTimestamp() + ); rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1); rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc()); @@ -195,11 +173,10 @@ int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen ) rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp()); rtcpPacket->body.rr.rr[0].dlsrN = 0; - return( wordLen*sizeof(uint32_t) ); -} + return wordLen*sizeof(uint32_t); +} // end RtpCtrlThread::generateRr -int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen ) -{ +int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen ) { RtcpPacket *rtcpPacket = (RtcpPacket *)packet; const std::string &cname = mRtpSource.getCname(); @@ -218,11 +195,10 @@ int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen rtcpPacket->body.sdes.item[0].len = cname.size(); memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() ); - return( wordLen*sizeof(uint32_t) ); -} + return wordLen*sizeof(uint32_t); +} // end RtpCtrlThread::generateSdes -int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen ) -{ +int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen ) { RtcpPacket *rtcpPacket = (RtcpPacket *)packet; int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]); @@ -236,11 +212,10 @@ int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen ) rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc()); - return( wordLen*sizeof(uint32_t) ); -} + return wordLen*sizeof(uint32_t); +} // end RtpCtrolThread::generateBye -int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes ) -{ +int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes ) { unsigned char *bufferPtr = buffer; // u_int32 len; /* length of compound RTCP packet in words */ @@ -259,33 +234,28 @@ int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes ) // /* something wrong with packet format */ // } - while ( nBytes > 0 ) - { + while ( nBytes > 0 ) { int consumed = recvPacket( bufferPtr, nBytes ); if ( consumed <= 0 ) break; bufferPtr += consumed; nBytes -= consumed; } - return( nBytes ); + return nBytes; } -int RtpCtrlThread::run() -{ +int RtpCtrlThread::run() { Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() ); SockAddrInet localAddr, remoteAddr; bool sendReports; UdpInetSocket rtpCtrlServer; - if ( mRtpSource.getLocalHost() != "" ) - { + if ( mRtpSource.getLocalHost() != "" ) { if ( !rtpCtrlServer.bind( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ) ) Fatal( "Failed to bind RTCP server" ); sendReports = false; Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); - } - else - { + } else { if ( !rtpCtrlServer.bind( mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0", mRtpSource.getLocalCtrlPort() ) ) Fatal( "Failed to bind RTCP server" ); Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); @@ -309,17 +279,17 @@ int RtpCtrlThread::run() time_t now = time(NULL); Select::CommsList readable = select.getReadable(); - if ( readable.size() == 0 ) - { + if ( readable.size() == 0 ) { if ( ! timeout ) { // With this code here, we will send an SDES and RR packet every 10 seconds ssize_t nBytes; unsigned char *bufferPtr = buffer; bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); - Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) ); - if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 ) - Error( "Unable to send: %s", strerror( errno ) ); + Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", + bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) ); + if ( (nBytes = rtpCtrlServer.send(buffer, bufferPtr-buffer)) < 0 ) + Error("Unable to send: %s", strerror(errno)); timeout = true; continue; } else { @@ -332,25 +302,21 @@ int RtpCtrlThread::run() timeout = false; last_receive = time(NULL); } - for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter ) - { - if ( UdpInetSocket *socket = dynamic_cast(*iter) ) - { + for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter ) { + if ( UdpInetSocket *socket = dynamic_cast(*iter) ) { ssize_t nBytes = socket->recv( buffer, sizeof(buffer) ); Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() ); - if ( nBytes ) - { + if ( nBytes ) { recvPackets( buffer, nBytes ); - if ( sendReports ) - { + if ( sendReports ) { unsigned char *bufferPtr = buffer; bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); - 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 ) - 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() ); } } else { @@ -358,16 +324,14 @@ int RtpCtrlThread::run() mStop = true; break; } - } - else - { - Panic( "Barfed" ); - } - } + } else { + Panic("Barfed"); + } // end if socket + } // end foeach comms iterator } rtpCtrlServer.close(); mRtspThread.stop(); - return( 0 ); + return 0; } #endif // HAVE_LIBAVFORMAT diff --git a/src/zm_rtp_ctrl.h b/src/zm_rtp_ctrl.h index 6d8f3024c..9e5306f92 100644 --- a/src/zm_rtp_ctrl.h +++ b/src/zm_rtp_ctrl.h @@ -34,13 +34,11 @@ class RtspThread; class RtpSource; -class RtpCtrlThread : public Thread -{ +class RtpCtrlThread : public Thread { friend class RtspThread; private: - typedef enum - { + typedef enum { RTCP_SR = 200, RTCP_RR = 201, RTCP_SDES = 202, @@ -48,8 +46,7 @@ private: RTCP_APP = 204 } RtcpType; - typedef enum - { + typedef enum { RTCP_SDES_END = 0, RTCP_SDES_CNAME = 1, RTCP_SDES_NAME = 2, @@ -61,8 +58,7 @@ private: RTCP_SDES_PRIV = 8 } RtcpSdesType; - struct RtcpCommonHeader - { + struct RtcpCommonHeader { uint8_t count:5; // varies by packet type uint8_t p:1; // padding flag uint8_t version:2; // protocol version @@ -71,8 +67,7 @@ private: }; // Reception report block - struct RtcpRr - { + struct RtcpRr { uint32_t ssrcN; // data source being reported int32_t lost:24; // cumul. no. pkts lost (signed!) uint32_t fraction:8; // fraction lost since last SR/RR @@ -83,22 +78,18 @@ private: }; // SDES item - struct RtcpSdesItem - { + struct RtcpSdesItem { uint8_t type; // type of item (rtcp_sdes_type_t) uint8_t len; // length of item (in octets) char data[]; // text, not null-terminated }; // RTCP packet - struct RtcpPacket - { + struct RtcpPacket { RtcpCommonHeader header; // common header - union - { + union { // Sender Report (SR) - struct Sr - { + struct Sr { uint32_t ssrcN; // sender generating this report, network order uint32_t ntpSecN; // NTP timestamp, network order uint32_t ntpFracN; @@ -109,22 +100,19 @@ private: } sr; // Reception Report (RR) - struct Rr - { + struct Rr { uint32_t ssrcN; // receiver generating this report RtcpRr rr[]; // variable-length list } rr; // source description (SDES) - struct Sdes - { + struct Sdes { uint32_t srcN; // first SSRC/CSRC RtcpSdesItem item[]; // list of SDES items } sdes; // BYE - struct - { + struct { uint32_t srcN[]; // list of sources // can't express trailing text for reason (what does this mean? it's not even english!) } bye; @@ -148,8 +136,7 @@ private: public: RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ); - void stop() - { + void stop() { mStop = true; } }; diff --git a/src/zm_rtp_source.cpp b/src/zm_rtp_source.cpp index 0cd838902..02e8ed0c5 100644 --- a/src/zm_rtp_source.cpp +++ b/src/zm_rtp_source.cpp @@ -26,7 +26,17 @@ #if HAVE_LIBAVCODEC -RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ) : +RtpSource::RtpSource( + int id, + const std::string &localHost, + int localPortBase, + const std::string &remoteHost, + int remotePortBase, + uint32_t ssrc, + uint16_t seq, + uint32_t rtpClock, + uint32_t rtpTime, + _AVCODECID codecId ) : mId( id ), mSsrc( ssrc ), mLocalHost( localHost ), @@ -65,13 +75,12 @@ RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, c mLastSrTimeNtp = tvZero(); mLastSrTimeRtp = 0; - if(mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4) - Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." ); + if ( mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4 ) + 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 ) -{ - Debug( 3, "Initialising sequence" ); +void RtpSource::init( uint16_t seq ) { + Debug(3, "Initialising sequence"); mBaseSeq = seq; mMaxSeq = seq; mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false @@ -84,77 +93,58 @@ void RtpSource::init( uint16_t seq ) mTransit = 0; } -bool RtpSource::updateSeq( uint16_t seq ) -{ +bool RtpSource::updateSeq( uint16_t seq ) { uint16_t uDelta = seq - mMaxSeq; // Source is not valid until MIN_SEQUENTIAL packets with // sequential sequence numbers have been received. Debug( 5, "Seq: %d", seq ); - if ( mProbation) - { + if ( mProbation) { // packet is in sequence - if ( seq == mMaxSeq + 1) - { + if ( seq == mMaxSeq + 1) { Debug( 3, "Sequence in probation %d, in sequence", mProbation ); mProbation--; mMaxSeq = seq; - if ( mProbation == 0 ) - { + if ( mProbation == 0 ) { init( seq ); mReceivedPackets++; return( true ); } - } - else - { + } else { Warning( "Sequence in probation %d, out of sequence", mProbation ); mProbation = MIN_SEQUENTIAL - 1; mMaxSeq = seq; return( false ); } return( true ); - } - else if ( uDelta < MAX_DROPOUT ) - { - if ( uDelta == 1 ) - { + } else if ( uDelta < MAX_DROPOUT ) { + if ( uDelta == 1 ) { Debug( 4, "Packet in sequence, gap %d", uDelta ); - } - else - { + } else { Warning( "Packet in sequence, gap %d", uDelta ); } // in order, with permissible gap - if ( seq < mMaxSeq ) - { + if ( seq < mMaxSeq ) { // Sequence number wrapped - count another 64K cycle. mCycles += RTP_SEQ_MOD; } mMaxSeq = seq; - } - else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER ) - { + } else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER ) { Warning( "Packet out of sequence, gap %d", uDelta ); // the sequence number made a very large jump - if ( seq == mBadSeq ) - { + if ( seq == mBadSeq ) { Debug( 3, "Restarting sequence" ); // Two sequential packets -- assume that the other side // restarted without telling us so just re-sync // (i.e., pretend this was the first packet). init( seq ); - } - else - { + } else { mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1); return( false ); } - } - else - { + } else { Warning( "Packet duplicate or reordered, gap %d", uDelta ); // duplicate or reordered packet return( false ); @@ -163,10 +153,8 @@ bool RtpSource::updateSeq( uint16_t seq ) return( uDelta==1?true:false ); } -void RtpSource::updateJitter( const RtpDataHeader *header ) -{ - if ( mRtpFactor > 0 ) - { +void RtpSource::updateJitter( const RtpDataHeader *header ) { + if ( mRtpFactor > 0 ) { Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) ); uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor ); Debug( 5, "Local RTP time = %x", localTimeRtp ); @@ -174,8 +162,7 @@ void RtpSource::updateJitter( const RtpDataHeader *header ) uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN); Debug( 5, "Packet transit RTP time = %x", packetTransit ); - if ( mTransit > 0 ) - { + if ( mTransit > 0 ) { // Jitter int d = packetTransit - mTransit; Debug( 5, "Jitter D = %d", d ); @@ -185,28 +172,22 @@ void RtpSource::updateJitter( const RtpDataHeader *header ) mJitter += d - ((mJitter + 8) >> 4); } mTransit = packetTransit; - } - else - { + } else { mJitter = 0; } Debug( 5, "RTP Jitter: %d", mJitter ); } -void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ) -{ +void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ) { struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) ); Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime ); - if ( mBaseTimeNtp.tv_sec == 0 ) - { + if ( mBaseTimeNtp.tv_sec == 0 ) { mBaseTimeReal = tvNow(); mBaseTimeNtp = ntpTime; mBaseTimeRtp = rtpTime; - } - else if ( !mRtpClock ) - { + } else if ( !mRtpClock ) { Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime ); Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime ); @@ -227,8 +208,7 @@ void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint mLastSrTimeRtp = rtpTime; } -void RtpSource::updateRtcpStats() -{ +void RtpSource::updateRtcpStats() { uint32_t extendedMax = mCycles + mMaxSeq; mExpectedPackets = extendedMax - mBaseSeq + 1; @@ -255,8 +235,7 @@ void RtpSource::updateRtcpStats() Debug( 5, "Lost fraction = %d", mLostFraction ); } -bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) -{ +bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) { const RtpDataHeader *rtpHeader; rtpHeader = (RtpDataHeader *)packet; int rtpHeaderSize = 12 + rtpHeader->cc * 4; @@ -269,39 +248,29 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) // that there is no marker bit by changing the number of bits in the payload type field. bool thisM = rtpHeader->m || h264FragmentEnd; - if ( updateSeq( ntohs(rtpHeader->seqN) ) ) - { + if ( updateSeq( ntohs(rtpHeader->seqN) ) ) { Hexdump( 4, packet+rtpHeaderSize, 16 ); - if ( mFrameGood ) - { + if ( mFrameGood ) { int extraHeader = 0; - if( mCodecId == AV_CODEC_ID_H264 ) - { + if ( mCodecId == AV_CODEC_ID_H264 ) { int nalType = (packet[rtpHeaderSize] & 0x1f); Debug( 3, "Have H264 frame: nal type is %d", nalType ); - switch (nalType) - { + switch (nalType) { case 24: // STAP-A - { extraHeader = 2; break; - } case 25: // STAP-B case 26: // MTAP-16 case 27: // MTAP-24 - { extraHeader = 3; break; - } // FU-A and FU-B case 28: case 29: - { // Is this NAL the first NAL in fragmentation sequence - if ( packet[rtpHeaderSize+1] & 0x80 ) - { + if ( packet[rtpHeaderSize+1] & 0x80 ) { // Now we will form new header of frame mFrame.append( "\x0\x0\x1\x0", 4 ); // Reconstruct NAL header from FU headers @@ -311,17 +280,14 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) extraHeader = 2; break; - } default: - { Debug(3, "Unhandled nalType %d", nalType ); - } } // Append NAL frame start code if ( !mFrame.size() ) mFrame.append( "\x0\x0\x1", 3 ); - } + } // end if H264 mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader ); } else { Debug( 3, "NOT H264 frame: type is %d", mCodecId ); @@ -329,16 +295,13 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) Hexdump( 4, mFrame.head(), 16 ); - if ( thisM ) - { - if ( mFrameGood ) - { + if ( thisM ) { + if ( mFrameGood ) { Debug( 3, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() ); mFrameProcessed.setValueImmediate( false ); mFrameReady.updateValueSignal( true ); - if ( !mFrameProcessed.getValueImmediate() ) - { + if ( !mFrameProcessed.getValueImmediate() ) { // What is the point of this for loop? Is it just me, or will it call getUpdatedValue once or twice? Could it not be better written as // if ( ! mFrameProcessed.getUpdatedValue( 1 ) && mFrameProcessed.getUpdatedValue( 1 ) ) return false; @@ -347,45 +310,34 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) return( false ); } mFrameCount++; - } - else - { + } else { Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() ); } mFrame.clear(); } - } - else - { - if ( mFrame.size() ) - { + } else { + if ( mFrame.size() ) { Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() ); - } - else - { + } else { Warning( "Discarding frame %d", mFrameCount ); } mFrameGood = false; mFrame.clear(); } - if ( thisM ) - { + if ( thisM ) { mFrameGood = true; prevM = true; - } - else + } else prevM = false; updateJitter( rtpHeader ); - return( true ); + return true; } -bool RtpSource::getFrame( Buffer &buffer ) -{ +bool RtpSource::getFrame( Buffer &buffer ) { Debug( 3, "Getting frame" ); - if ( !mFrameReady.getValueImmediate() ) - { + if ( !mFrameReady.getValueImmediate() ) { // Allow for a couple of spurious returns for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ ) if ( count > 1 ) @@ -395,7 +347,7 @@ bool RtpSource::getFrame( Buffer &buffer ) mFrameReady.setValueImmediate( false ); mFrameProcessed.updateValueSignal( true ); Debug( 4, "Copied %d bytes", buffer.size() ); - return( true ); + return true; } #endif // HAVE_LIBAVCODEC diff --git a/src/zm_rtsp.cpp b/src/zm_rtsp.cpp index 703328e2e..5e2858e4f 100644 --- a/src/zm_rtsp.cpp +++ b/src/zm_rtsp.cpp @@ -46,53 +46,54 @@ bool RtspThread::sendCommand( std::string message ) { message += stringtf( "CSeq: %d\r\n\r\n", ++mSeq ); Debug( 2, "Sending RTSP message: %s", message.c_str() ); if ( mMethod == RTP_RTSP_HTTP ) { - message = base64Encode( message ); - Debug( 2, "Sending encoded RTSP message: %s", message.c_str() ); - if ( mRtspSocket2.send( message.c_str(), message.size() ) != (int)message.length() ) { - Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); - return( false ); + message = base64Encode(message); + Debug(2, "Sending encoded RTSP message: %s", message.c_str()); + if ( mRtspSocket2.send(message.c_str(), message.size()) != (int)message.length() ) { + Error("Unable to send message '%s': %s", message.c_str(), strerror(errno)); + return false; } } else { - if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) { - Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); - return( false ); + if ( mRtspSocket.send(message.c_str(), message.size()) != (int)message.length() ) { + Error("Unable to send message '%s': %s", message.c_str(), strerror(errno)); + return false; } } - return( true ); + return true; } bool RtspThread::recvResponse( std::string &response ) { if ( mRtspSocket.recv( response ) < 0 ) - Error( "Recv failed; %s", strerror(errno) ); - Debug( 2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size() ); + Error("Recv failed; %s", strerror(errno)); + Debug(2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size()); float respVer = 0; respCode = -1; 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]) ) { - Error( "Response parse failure in '%s'", response.c_str() ); + Error("Response parse failure in '%s'", response.c_str()); } else { - Error( "Response parse failure, %zd bytes follow", response.size() ); + Error("Response parse failure, %zd bytes follow", response.size()); if ( response.size() ) 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"); mAuthenticator->checkAuthResponse(response); mNeedAuth = true; - return( false ); + return false; } else if ( respCode != 200 ) { - Error( "Unexpected response code %d, text is '%s'", respCode, respText ); - return( false ); + Error("Unexpected response code %d, text is '%s'", respCode, respText); + return false; } - return( true ); -} + return true; +} // end RtspThread::recResponse int RtspThread::requestPorts() { if ( !smMinDataPort ) { char sql[ZM_SQL_SML_BUFSIZ]; + //FIXME Why not load specifically by Id? This will get ineffeicient with a lot of monitors strncpy( sql, "select Id from Monitors where Function != 'None' and Type = 'Remote' and Protocol = 'rtsp' and Method = 'rtpUni' order by Id asc", sizeof(sql) ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); @@ -107,7 +108,7 @@ int RtspThread::requestPorts() { int nMonitors = mysql_num_rows( result ); int position = 0; 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]); if ( mId == id ) { position = i; @@ -126,22 +127,30 @@ int RtspThread::requestPorts() { Debug( 2, "Assigned RTP port range is %d-%d", smMinDataPort, smMaxDataPort ); } 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() ) { - smAssignedPorts.insert( i ); - return( i ); + smAssignedPorts.insert(i); + return i; } } - Panic( "Can assign RTP port, no ports left in pool" ); - return( -1 ); + Panic("Can assign RTP port, no ports left in pool"); + return -1; } void RtspThread::releasePorts( int port ) { 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 ), mMethod( method ), mProtocol( protocol ), @@ -168,10 +177,10 @@ RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol, mSsrc = rand(); - Debug( 2, "RTSP Local SSRC is %x", mSsrc ); + Debug(2, "RTSP Local SSRC is %x", mSsrc); if ( mMethod == RTP_RTSP_HTTP ) - mHttpSession = stringtf( "%d", rand() ); + mHttpSession = stringtf("%d", rand()); mNeedAuth = false; StringVector parts = split(auth,":"); @@ -216,8 +225,8 @@ int RtspThread::run() { bool authTried = false; if ( mMethod == RTP_RTSP_HTTP ) { - if ( !mRtspSocket2.connect( mHost.c_str(), mPort.c_str() ) ) - Fatal( "Unable to connect auxiliary RTSP/HTTP socket" ); + if ( !mRtspSocket2.connect(mHost.c_str(), mPort.c_str()) ) + Fatal("Unable to connect auxiliary RTSP/HTTP socket"); //Select select( 0.25 ); //select.addReader( &mRtspSocket2 ); //while ( select.wait() ) @@ -240,15 +249,15 @@ int RtspThread::run() { message += "\r\n"; Debug( 2, "Sending HTTP message: %s", message.c_str() ); if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) { - Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); - return( -1 ); + Error("Unable to send message '%s': %s", message.c_str(), strerror(errno)); + return -1; } if ( mRtspSocket.recv( response ) < 0 ) { - Error( "Recv failed; %s", strerror(errno) ); - return( -1 ); + Error("Recv failed; %s", strerror(errno)); + 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; respCode = -1; 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() ) Hexdump( Logger::ERROR, response.data(), min(response.size(),16) ); } - return( -1 ); + return -1; } // If Server requests authentication, check WWW-Authenticate header and fill required fields // for requested authentication method - if (respCode == 401 && !authTried) { + if ( respCode == 401 && !authTried ) { mNeedAuth = true; mAuthenticator->checkAuthResponse(response); Debug(2, "Processed 401 response"); mRtspSocket.close(); - if ( !mRtspSocket.connect( mHost.c_str(), mPort.c_str() ) ) - Fatal( "Unable to reconnect RTSP socket" ); + if ( !mRtspSocket.connect(mHost.c_str(), mPort.c_str()) ) + Fatal("Unable to reconnect RTSP socket"); Debug(2, "connection should be reopened now"); } } while (respCode == 401 && !authTried); if ( respCode != 200 ) { - Error( "Unexpected response code %d, text is '%s'", respCode, respText ); - return( -1 ); + Error("Unexpected response code %d, text is '%s'", respCode, respText); + return -1; } message = "POST "+mPath+" HTTP/1.0\r\n"; @@ -300,25 +309,25 @@ int RtspThread::run() { // Request supported RTSP commands by the server message = "OPTIONS "+mUrl+" RTSP/1.0\r\n"; if ( !sendCommand( message ) ) - return( -1 ); + return -1; // A negative return here may indicate auth failure, but we will have setup the auth mechanisms so we need to retry. - if ( !recvResponse( response ) ) { + if ( !recvResponse(response) ) { if ( mNeedAuth ) { Debug( 2, "Resending OPTIONS due to possible auth requirement" ); - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); + if ( !sendCommand(message) ) + return -1; + if ( !recvResponse(response) ) + return -1; } else { - return( -1 ); + return -1; } } // end if failed response maybe due to auth char publicLine[256] = ""; - StringVector lines = split( response, "\r\n" ); + StringVector lines = split(response, "\r\n"); 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 // If yes, it is likely that the server will request this command as a keepalive message @@ -331,44 +340,45 @@ int RtspThread::run() { do { if (mNeedAuth) authTried = true; - sendCommand( message ); - sleep( 1 ); - res = recvResponse( response ); - if (!res && respCode==401) + sendCommand(message); + // FIXME WHy sleep 1? + sleep(1); + res = recvResponse(response); + if ( !res && respCode==401 ) mNeedAuth = true; } while (!res && respCode==401 && !authTried); const std::string endOfHeaders = "\r\n\r\n"; - size_t sdpStart = response.find( endOfHeaders ); - if( sdpStart == std::string::npos ) - return( -1 ); + size_t sdpStart = response.find(endOfHeaders); + if ( sdpStart == std::string::npos ) + return -1; if ( mRtspDescribe ) { - std::string DescHeader = response.substr( 0,sdpStart ); - Debug( 1, "Processing DESCRIBE response header '%s'", DescHeader.c_str() ); + std::string DescHeader = response.substr(0, sdpStart); + 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++ ) { - // 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:" ) ) { - mUrl = trimSpaces( lines[i].substr( 13 ) ); - Info("Received new Content-Base in DESCRIBE response header. Updated device Url to: '%s'", mUrl.c_str() ); - break; - } + // 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:" ) ) { + mUrl = trimSpaces( lines[i].substr( 13 ) ); + Info("Received new Content-Base in DESCRIBE response header. Updated device Url to: '%s'", mUrl.c_str() ); + break; } - } + } // end foreach line + } // end if mRtspDescribe sdpStart += endOfHeaders.length(); - std::string sdp = response.substr( sdpStart ); - Debug( 1, "Processing SDP '%s'", sdp.c_str() ); + std::string sdp = response.substr(sdpStart); + Debug(1, "Processing SDP '%s'", sdp.c_str()); try { mSessDesc = new SessionDescriptor( mUrl, sdp ); mFormatContext = mSessDesc->generateFormatContext(); } catch( const Exception &e ) { Error( e.getMessage().c_str() ); - return( -1 ); + return -1; } #if 0 @@ -672,51 +682,36 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali Hexdump( 4, (char *)buffer, 16 ); rtpDataThread.recvPacket( buffer+4, len ); Debug( 4, "Received" ); - } - else if ( channel == remoteChannels[1] ) - { + } else if ( channel == remoteChannels[1] ) { // len = ntohs( *((unsigned short *)(buffer+2)) ); // Debug( 4, "Got %d bytes on control channel %d", nBytes, channel ); Debug( 4, "Got %d bytes on control channel %d, packet length is %d", buffer.size(), channel, len ); Hexdump( 4, (char *)buffer, 16 ); rtpCtrlThread.recvPackets( buffer+4, len ); - } - else - { + } else { Error( "Unexpected channel selector %d in RTSP interleaved data", buffer[1] ); buffer.clear(); break; } buffer.consume( len+4 ); nBytes -= len+4; - } - else - { - if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 ) - { + } else { + if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 ) { Debug( 4, "Got keepalive response '%s'", (char *)buffer ); //buffer.consume( keepaliveResponse.size() ); - if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) - { + if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) { int discardBytes = charPtr-(char *)buffer; buffer -= discardBytes; - } - else - { + } else { buffer.clear(); } - } - else - { - if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) - { + } else { + if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) { int discardBytes = charPtr-(char *)buffer; Warning( "Unexpected format RTSP interleaved data, resyncing by %d bytes", discardBytes ); Hexdump( -1, (char *)buffer, discardBytes ); buffer -= discardBytes; - } - else - { + } else { Warning( "Unexpected format RTSP interleaved data, dumping %d bytes", buffer.size() ); Hexdump( -1, (char *)buffer, 32 ); buffer.clear(); @@ -764,16 +759,14 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali rtpDataThread.start(); rtpCtrlThread.start(); - while( !mStop ) - { + while ( !mStop ) { // Send a keepalive message if the server supports this feature and we are close to the timeout expiration - if ( sendKeepalive && (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) ) - { + if ( sendKeepalive && (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) ) { if ( !sendCommand( message ) ) - return( -1 ); + return -1; lastKeepalive = time(NULL); } - usleep( 100000 ); + usleep(100000); } #if 0 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 ); #endif message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); + if ( !sendCommand(message) ) + return -1; + if ( !recvResponse(response) ) + return -1; rtpDataThread.stop(); rtpCtrlThread.stop(); @@ -801,13 +794,11 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali break; } default: - { - Panic( "Got unexpected method %d", mMethod ); + Panic("Got unexpected method %d", mMethod); break; - } } - return( 0 ); + return 0; } #endif // HAVE_LIBAVFORMAT diff --git a/src/zm_sdp.cpp b/src/zm_sdp.cpp index af293d64a..c7bf1b578 100644 --- a/src/zm_sdp.cpp +++ b/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)) SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = { { 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 }, { 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 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 }, { 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, { 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 }, { 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 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_MP3, -1, -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 }, { 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 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 }, { 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 }, { 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 }, { 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 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[] = { - { "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 }, - { "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC }, - { "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, - { "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB }, + { "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 }, + { "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC }, + { "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, + { "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB }, { "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE } }; #else SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = { { 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 }, { 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 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 }, { 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, { 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 }, { 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 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_MP3, -1, -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 }, { 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 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 }, { 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 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 ), mNoAddresses( 0 ) { - StringVector tokens = split( connInfo, " " ); + StringVector tokens = split(connInfo, " "); if ( tokens.size() < 3 ) throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" ); mNetworkType = tokens[0]; @@ -136,7 +136,12 @@ SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) : mValue = atoi(tokens[1].c_str()); } -SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) : +SessionDescriptor::MediaDescriptor::MediaDescriptor( + const std::string &type, + int port, + int numPorts, + const std::string &transport, + int payloadType ) : mType( type ), mPort( port ), mNumPorts( numPorts ), @@ -164,14 +169,13 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string if ( line.empty() ) break; - Debug( 3, "Processing SDP line '%s'", line.c_str() ); + Debug(3, "Processing SDP line '%s'", line.c_str()); const char sdpType = line[0]; if ( line[1] != '=' ) - throw Exception( "Invalid SDP format at '"+line+"'" ); + throw Exception("Invalid SDP format at '"+line+"'"); - line.erase( 0, 2 ); - switch( sdpType ) - { + line.erase(0, 2); + switch( sdpType ) { case 'v' : mVersion = line; break; @@ -204,19 +208,13 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string mAttributes.push_back( line ); StringVector tokens = split( line, ":", 2 ); std::string attrName = tokens[0]; - if ( currMedia ) - { - if ( attrName == "control" ) - { + if ( currMedia ) { + if ( attrName == "control" ) { if ( tokens.size() < 2 ) throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" ); currMedia->setControlUrl( tokens[1] ); - } - else if ( attrName == "range" ) - { - } - else if ( attrName == "rtpmap" ) - { + } else if ( attrName == "range" ) { + } else if ( attrName == "rtpmap" ) { // a=rtpmap:96 MP4V-ES/90000 if ( tokens.size() < 2 ) throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" ); @@ -226,53 +224,46 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) ); std::string payloadDesc = attrTokens[1]; //currMedia->setPayloadType( payloadType ); - if ( attrTokens.size() > 1 ) - { + if ( attrTokens.size() > 1 ) { StringVector payloadTokens = split( attrTokens[1], "/" ); std::string payloadDesc = payloadTokens[0]; int payloadClock = atoi(payloadTokens[1].c_str()); currMedia->setPayloadDesc( payloadDesc ); currMedia->setClock( payloadClock ); } - } - else if ( attrName == "framesize" ) - { + } else if ( attrName == "framesize" ) { // a=framesize:96 320-240 if ( tokens.size() < 2 ) - throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" ); - StringVector attrTokens = split( tokens[1], " " ); + throw Exception("Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'"); + StringVector attrTokens = split(tokens[1], " "); int payloadType = atoi(attrTokens[0].c_str()); if ( payloadType != currMedia->getPayloadType() ) - throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) ); + throw Exception( stringtf("Payload type mismatch, expected %d, got %d in '%s'", + currMedia->getPayloadType(), payloadType, line.c_str())); //currMedia->setPayloadType( payloadType ); - StringVector sizeTokens = split( attrTokens[1], "-" ); + StringVector sizeTokens = split(attrTokens[1], "-"); int width = atoi(sizeTokens[0].c_str()); int height = atoi(sizeTokens[1].c_str()); - currMedia->setFrameSize( width, height ); - } - else if ( attrName == "framerate" ) - { + currMedia->setFrameSize(width, height); + } else if ( attrName == "framerate" ) { // a=framerate:5.0 if ( tokens.size() < 2 ) - throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" ); + throw Exception("Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'"); double frameRate = atof(tokens[1].c_str()); - currMedia->setFrameRate( frameRate ); - } - else if ( attrName == "fmtp" ) - { + currMedia->setFrameRate(frameRate); + } else if ( attrName == "fmtp" ) { // a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F if ( tokens.size() < 2 ) - throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" ); - StringVector attrTokens = split( tokens[1], " ", 2 ); + throw Exception("Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'"); + StringVector attrTokens = split(tokens[1], " ", 2); int payloadType = atoi(attrTokens[0].c_str()); if ( payloadType != currMedia->getPayloadType() ) - throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) ); + throw Exception(stringtf("Payload type mismatch, expected %d, got %d in '%s'", + currMedia->getPayloadType(), payloadType, line.c_str())); //currMedia->setPayloadType( payloadType ); - if ( attrTokens.size() > 1 ) - { + if ( attrTokens.size() > 1 ) { StringVector attr2Tokens = split( attrTokens[1], "; " ); - for ( unsigned int i = 0; i < attr2Tokens.size(); i++ ) - { + for ( unsigned int i = 0; i < attr2Tokens.size(); i++ ) { StringVector attr3Tokens = split( attr2Tokens[i], "=" ); //Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() ); if ( attr3Tokens[0] == "profile-level-id" ) { @@ -292,40 +283,39 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string } else if ( attrName == "mpeg4-esid" ) { // a=mpeg4-esid:201 } else { - Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() ) + Debug(3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str()); } } else { - Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() ); + Debug(3, "Ignoring general SDP attribute '%s'", line.c_str()); } break; } case 'm' : { - StringVector tokens = split( line, " " ); + StringVector tokens = split(line, " "); 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]; if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" ) - throw Exception( "Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'" ); - StringVector portTokens = split( tokens[1], "/" ); + throw Exception("Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'"); + StringVector portTokens = split(tokens[1], "/"); int mediaPort = atoi(portTokens[0].c_str()); int mediaNumPorts = 1; if ( portTokens.size() > 1 ) mediaNumPorts = atoi(portTokens[1].c_str()); std::string mediaTransport = tokens[2]; 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()); - currMedia = new MediaDescriptor( mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType ); - mMediaList.push_back( currMedia ); + currMedia = new MediaDescriptor(mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType); + mMediaList.push_back(currMedia); break; } - } - } + } // end switch + } // end foreach line } -SessionDescriptor::~SessionDescriptor() -{ +SessionDescriptor::~SessionDescriptor() { if ( mConnInfo ) delete mConnInfo; if ( mBandInfo ) @@ -334,8 +324,7 @@ SessionDescriptor::~SessionDescriptor() delete mMediaList[i]; } -AVFormatContext *SessionDescriptor::generateFormatContext() const -{ +AVFormatContext *SessionDescriptor::generateFormatContext() const { AVFormatContext *formatContext = avformat_alloc_context(); #if (LIBAVFORMAT_VERSION_CHECK(58, 12, 0, 0, 100)) @@ -353,35 +342,40 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const for ( unsigned int i = 0; i < mMediaList.size(); i++ ) { const MediaDescriptor *mediaDesc = mMediaList[i]; #if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0) - AVStream *stream = av_new_stream( formatContext, i ); + AVStream *stream = av_new_stream(formatContext, i); #else - AVStream *stream = avformat_new_stream( formatContext, NULL ); + AVStream *stream = avformat_new_stream(formatContext, NULL); stream->id = i; #endif #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) AVCodecContext *codec_context = avcodec_alloc_context3(NULL); avcodec_parameters_to_context(codec_context, stream->codecpar); + stream->codec = codec_context; #else AVCodecContext *codec_context = stream->codec; #endif - Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() ); + std::string type = mediaDesc->getType(); + Debug(1, "Looking for codec for %s payload type %d / %s", + type.c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str()); #if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) - if ( mediaDesc->getType() == "video" ) + if ( type == "video" ) codec_context->codec_type = AVMEDIA_TYPE_VIDEO; - else if ( mediaDesc->getType() == "audio" ) + else if ( type == "audio" ) codec_context->codec_type = AVMEDIA_TYPE_AUDIO; - else if ( mediaDesc->getType() == "application" ) + else if ( type == "application" ) codec_context->codec_type = AVMEDIA_TYPE_DATA; #else - if ( mediaDesc->getType() == "video" ) + if ( type == "video" ) codec_context->codec_type = CODEC_TYPE_VIDEO; - else if ( mediaDesc->getType() == "audio" ) + else if ( type == "audio" ) codec_context->codec_type = CODEC_TYPE_AUDIO; - else if ( mediaDesc->getType() == "application" ) + else if ( type == "application" ) codec_context->codec_type = CODEC_TYPE_DATA; #endif + else + Warning("Unknown media_type %s", type.c_str()); #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) std::string codec_name; @@ -392,9 +386,9 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() ) { Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName ); #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) - codec_name = std::string( smStaticPayloads[i].payloadName ); + codec_name = std::string(smStaticPayloads[i].payloadName); #else - strncpy( codec_context->codec_name, smStaticPayloads[i].payloadName, sizeof(codec_context->codec_name) );; + strncpy(codec_context->codec_name, smStaticPayloads[i].payloadName, sizeof(codec_context->codec_name)); #endif codec_context->codec_type = smStaticPayloads[i].codecType; codec_context->codec_id = smStaticPayloads[i].codecId; @@ -406,11 +400,11 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const // Look in dynamic table for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ ) { 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) - codec_name = std::string( smStaticPayloads[i].payloadName ); + codec_name = std::string(smStaticPayloads[i].payloadName); #else - strncpy( codec_context->codec_name, smDynamicPayloads[i].payloadName, sizeof(codec_context->codec_name) );; + strncpy(codec_context->codec_name, smDynamicPayloads[i].payloadName, sizeof(codec_context->codec_name)); #endif codec_context->codec_type = smDynamicPayloads[i].codecType; codec_context->codec_id = smDynamicPayloads[i].codecId; @@ -418,7 +412,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const break; } } - } + } /// end if static or dynamic #if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) if ( codec_name.empty() ) @@ -426,7 +420,8 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const if ( !stream->codec->codec_name[0] ) #endif { - Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() ); + Warning( "Can't find payload details for %s payload type %d, name %s", + mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() ); //return( 0 ); } if ( mediaDesc->getWidth() ) @@ -449,11 +444,11 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const while (*value && *value != ',' && (dst - base64packet) < (long)(sizeof(base64packet)) - 1) { - *dst++ = *value++; + *dst++ = *value++; } *dst++ = '\0'; - if (*value == ',') + if ( *value == ',' ) value++; 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 #endif ); - if(dest) { - if(codec_context->extradata_size) { - // av_realloc? + if ( dest ) { + if ( codec_context->extradata_size ) { + // av_realloc? 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+sizeof(start_sequence), decoded_packet, packet_size); - memset(dest+codec_context->extradata_size+sizeof(start_sequence)+ - packet_size, 0, + memcpy(dest+codec_context->extradata_size, start_sequence, sizeof(start_sequence)); + memcpy(dest+codec_context->extradata_size+sizeof(start_sequence), decoded_packet, packet_size); + memset(dest+codec_context->extradata_size+sizeof(start_sequence)+ + packet_size, 0, #if LIBAVCODEC_VERSION_CHECK(57, 0, 0, 0, 0) - AV_INPUT_BUFFER_PADDING_SIZE + AV_INPUT_BUFFER_PADDING_SIZE #else - FF_INPUT_BUFFER_PADDING_SIZE + FF_INPUT_BUFFER_PADDING_SIZE #endif -); + ); codec_context->extradata= dest; codec_context->extradata_size+= sizeof(start_sequence)+packet_size; @@ -497,7 +492,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const } } - return( formatContext ); + return formatContext; } #endif // HAVE_LIBAVFORMAT diff --git a/version b/version index 1304b01de..692c2e30d 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.33.7 +1.33.8 diff --git a/web/index.php b/web/index.php index df190a700..a4f6160d4 100644 --- a/web/index.php +++ b/web/index.php @@ -93,7 +93,7 @@ if ( isset($_GET['skin']) ) { $skins = array_map('basename', glob('skins/*', GLOB_ONLYDIR)); if ( ! in_array($skin, $skins) ) { - Error("Invalid skin '$skin' setting to " . $skins[0]); + ZM\Error("Invalid skin '$skin' setting to " . $skins[0]); $skin = $skins[0]; } @@ -109,7 +109,7 @@ if ( isset($_GET['css']) ) { $css_skins = array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR)); if ( !in_array($css, $css_skins) ) { - Error("Invalid skin css '$css' setting to " . $css_skins[0]); + ZM\Error("Invalid skin css '$css' setting to " . $css_skins[0]); $css = $css_skins[0]; }