Merge branch 'smarter_packetqueue' into storageareas
This commit is contained in:
commit
7149576a7c
|
@ -781,6 +781,7 @@ INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,1
|
||||||
INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,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,0);
|
INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,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,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,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,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,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,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,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,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','Remote','Dahua',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);
|
||||||
|
|
||||||
--
|
--
|
||||||
|
|
|
@ -0,0 +1,365 @@
|
||||||
|
package ZoneMinder::Control::PSIA;
|
||||||
|
|
||||||
|
use 5.006;
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
require ZoneMinder::Base;
|
||||||
|
require ZoneMinder::Control;
|
||||||
|
|
||||||
|
our @ISA = qw(ZoneMinder::Control);
|
||||||
|
|
||||||
|
our $REALM = 'TV-IP450PI';
|
||||||
|
our $USERNAME = 'admin';
|
||||||
|
our $PASSWORD = '';
|
||||||
|
our $ADDRESS = '';
|
||||||
|
our $PROTOCOL = 'http://';
|
||||||
|
|
||||||
|
use ZoneMinder::Logger qw(:all);
|
||||||
|
use ZoneMinder::Config qw(:all);
|
||||||
|
use ZoneMinder::Database qw(zmDbConnect);
|
||||||
|
|
||||||
|
sub open
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
$self->loadMonitor();
|
||||||
|
|
||||||
|
if ( ( $self->{Monitor}->{ControlAddress} =~ /^(?<PROTOCOL>https?:\/\/)?(?<USERNAME>[^:@]+)?:?(?<PASSWORD>[^\/@]+)?@?(?<ADDRESS>.*)$/ ) ) {
|
||||||
|
$PROTOCOL = $+{PROTOCOL} if $+{PROTOCOL};
|
||||||
|
$USERNAME = $+{USERNAME} if $+{USERNAME};
|
||||||
|
$PASSWORD = $+{PASSWORD} if $+{PASSWORD};
|
||||||
|
$ADDRESS = $+{ADDRESS} if $+{ADDRESS};
|
||||||
|
} else {
|
||||||
|
Error('Failed to parse auth from address ' . $self->{Monitor}->{ControlAddress});
|
||||||
|
$ADDRESS = $self->{Monitor}->{ControlAddress};
|
||||||
|
}
|
||||||
|
if ( !($ADDRESS =~ /:/) ) {
|
||||||
|
Error('You generally need to also specify the port. I will append :80');
|
||||||
|
$ADDRESS .= ':80';
|
||||||
|
}
|
||||||
|
|
||||||
|
use LWP::UserAgent;
|
||||||
|
$self->{ua} = LWP::UserAgent->new;
|
||||||
|
$self->{ua}->agent( "ZoneMinder Control Agent/".$ZoneMinder::Base::ZM_VERSION );
|
||||||
|
$self->{state} = 'closed';
|
||||||
|
Debug( "sendCmd credentials control address:'".$ADDRESS
|
||||||
|
."' realm:'" . $REALM
|
||||||
|
. "' username:'" . $USERNAME
|
||||||
|
. "' password:'".$PASSWORD
|
||||||
|
."'"
|
||||||
|
);
|
||||||
|
$self->{ua}->credentials($ADDRESS, $REALM, $USERNAME, $PASSWORD);
|
||||||
|
|
||||||
|
# Detect REALM
|
||||||
|
my $req = HTTP::Request->new(GET=>$PROTOCOL . $ADDRESS . "/PSIA/PTZ/channels");
|
||||||
|
my $res = $self->{ua}->request($req);
|
||||||
|
|
||||||
|
if ($res->is_success) {
|
||||||
|
$self->{state} = 'open';
|
||||||
|
return;
|
||||||
|
} elsif (! $res->is_success) {
|
||||||
|
Debug("Need newer REALM");
|
||||||
|
if ( $res->status_line() eq '401 Unauthorized' ) {
|
||||||
|
my $headers = $res->headers();
|
||||||
|
foreach my $k ( keys %$headers ) {
|
||||||
|
Debug("Initial Header $k => $$headers{$k}");
|
||||||
|
} # end foreach
|
||||||
|
if ( $$headers{'www-authenticate'} ) {
|
||||||
|
my ($auth, $tokens) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
|
||||||
|
if ($tokens =~ /\w+="([^"]+)"/i) {
|
||||||
|
$REALM = $1;
|
||||||
|
Debug("Changing REALM to $REALM");
|
||||||
|
$self->{ua}->credentials($ADDRESS, $REALM, $USERNAME, $PASSWORD);
|
||||||
|
} # end if
|
||||||
|
} else {
|
||||||
|
Debug("No WWW-Authenticate header");
|
||||||
|
} # end if www-authenticate header
|
||||||
|
} # end if $res->status_line() eq '401 Unauthorized'
|
||||||
|
} # end elsif ! $res->is_success
|
||||||
|
}
|
||||||
|
|
||||||
|
sub close
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
$self->{state} = 'closed';
|
||||||
|
}
|
||||||
|
|
||||||
|
sub printMsg
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $msg = shift;
|
||||||
|
my $msg_len = length($msg);
|
||||||
|
|
||||||
|
Debug( $msg."[".$msg_len."]" );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub sendGetRequest {
|
||||||
|
my $self = shift;
|
||||||
|
my $url_path = shift;
|
||||||
|
|
||||||
|
my $result = undef;
|
||||||
|
|
||||||
|
my $url = $PROTOCOL . $ADDRESS . $url_path;
|
||||||
|
my $req = HTTP::Request->new(GET=>$url);
|
||||||
|
|
||||||
|
my $res = $self->{ua}->request($req);
|
||||||
|
|
||||||
|
if ($res->is_success) {
|
||||||
|
$result = !undef;
|
||||||
|
} else {
|
||||||
|
if ( $res->status_line() eq '401 Unauthorized' ) {
|
||||||
|
Error( "Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
|
||||||
|
Error("Content was " . $res->content() );
|
||||||
|
my $res = $self->{ua}->request($req);
|
||||||
|
if ( $res->is_success ) {
|
||||||
|
$result = !undef;
|
||||||
|
} else {
|
||||||
|
Error("Content was " . $res->content() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( ! $result ) {
|
||||||
|
Error("Error check failed: '".$res->status_line());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return($result);
|
||||||
|
}
|
||||||
|
sub sendPutRequest {
|
||||||
|
my $self = shift;
|
||||||
|
my $url_path = shift;
|
||||||
|
my $content = shift;
|
||||||
|
|
||||||
|
my $result = undef;
|
||||||
|
|
||||||
|
my $url = $PROTOCOL . $ADDRESS . $url_path;
|
||||||
|
my $req = HTTP::Request->new(PUT=>$url);
|
||||||
|
if(defined($content)) {
|
||||||
|
$req->content_type("application/x-www-form-urlencoded; charset=UTF-8");
|
||||||
|
$req->content('<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $res = $self->{ua}->request($req);
|
||||||
|
|
||||||
|
if ($res->is_success) {
|
||||||
|
$result = !undef;
|
||||||
|
} else {
|
||||||
|
if ( $res->status_line() eq '401 Unauthorized' ) {
|
||||||
|
Error( "Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
|
||||||
|
Error("Content was " . $res->content() );
|
||||||
|
my $res = $self->{ua}->request($req);
|
||||||
|
if ( $res->is_success ) {
|
||||||
|
$result = !undef;
|
||||||
|
} else {
|
||||||
|
Error("Content was " . $res->content() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( ! $result ) {
|
||||||
|
Error( "Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return($result);
|
||||||
|
}
|
||||||
|
sub sendDeleteRequest {
|
||||||
|
my $self = shift;
|
||||||
|
my $url_path = shift;
|
||||||
|
|
||||||
|
my $result = undef;
|
||||||
|
|
||||||
|
my $url = $PROTOCOL . $ADDRESS . $url_path;
|
||||||
|
my $req = HTTP::Request->new(DELETE=>$url);
|
||||||
|
my $res = $self->{ua}->request($req);
|
||||||
|
if ($res->is_success) {
|
||||||
|
$result = !undef;
|
||||||
|
} else {
|
||||||
|
if ( $res->status_line() eq '401 Unauthorized' ) {
|
||||||
|
Error( "Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
|
||||||
|
Error("Content was " . $res->content() );
|
||||||
|
my $res = $self->{ua}->request($req);
|
||||||
|
if ( $res->is_success ) {
|
||||||
|
$result = !undef;
|
||||||
|
} else {
|
||||||
|
Error("Content was " . $res->content() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( ! $result ) {
|
||||||
|
Error( "Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub move
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $panPercentage = shift;
|
||||||
|
my $tiltPercentage = shift;
|
||||||
|
my $zoomPercentage = shift;
|
||||||
|
|
||||||
|
my $cmd = "set_relative_pos&posX=$panSteps&posY=$tiltSteps";
|
||||||
|
my $ptzdata = '<PTZData version="1.0" xmlns="urn:psialliance-org">';
|
||||||
|
$ptzdata .= '<pan>' . $panPercentage . '</pan>';
|
||||||
|
$ptzdata .= '<tilt>' . $tiltPercentage . '</tilt>';
|
||||||
|
$ptzdata .= '<zoom>' . $zoomPercentage . '</zoom>';
|
||||||
|
$ptzdata .= '<Momentary><duration>500</duration></Momentary>';
|
||||||
|
$ptzdata .= '</PTZData>';
|
||||||
|
$self->sendPutRequest("/PSIA/PTZ/channels/1/momentary", $ptzdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelUpLeft
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Up Left" );
|
||||||
|
$self->move(-50, 50, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelUp
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Up" );
|
||||||
|
$self->move(0, 50, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelUpRight
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Up Right" );
|
||||||
|
$self->move(50, 50, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelLeft
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Left" );
|
||||||
|
$self->move(-50, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelRight
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Right" );
|
||||||
|
$self->move(50, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelDownLeft
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Down Left" );
|
||||||
|
$self->move(-50, -50, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelDown
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Down" );
|
||||||
|
$self->move(0, -50, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub moveRelDownRight
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug( "Move Down Right" );
|
||||||
|
$self->move(50, -50, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub zoomRelTele
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug("Zoom Relative Tele");
|
||||||
|
$self->move(0, 0, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub zoomRelWide
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
Debug("Zoom Relative Wide");
|
||||||
|
$self->move(0, 0, -50);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub presetClear
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $preset_id = $self->getParam($params, 'preset');
|
||||||
|
my $url_path = "/PSIA/PTZ/channels/1/presets/" . $preset_id;
|
||||||
|
$self->sendDeleteRequest($url_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub presetSet
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
|
||||||
|
my $preset_id = $self->getParam($params, 'preset');
|
||||||
|
|
||||||
|
my $dbh = zmDbConnect(1);
|
||||||
|
my $sql = 'SELECT * FROM ControlPresets WHERE MonitorId = ? AND Preset = ?';
|
||||||
|
my $sth = $dbh->prepare($sql)
|
||||||
|
or Fatal("Can't prepare sql '$sql': " . $dbh->errstr());
|
||||||
|
my $res = $sth->execute($self->{Monitor}->{Id}, $preset_id)
|
||||||
|
or Fatal("Can't execute sql '$sql': " . $sth->errstr());
|
||||||
|
my $control_preset_row = $sth->fetchrow_hashref();
|
||||||
|
my $new_label_name = $control_preset_row->{'Label'};
|
||||||
|
|
||||||
|
my $url_path = "/PSIA/PTZ/channels/1/presets/" . $preset_id;
|
||||||
|
my $ptz_preset_data = '<PTZPreset>';
|
||||||
|
$ptz_preset_data .= '<id>' . $preset_id . '</id>';
|
||||||
|
$ptz_preset_data .= '<presetName>' . $new_label_name . '</presetName>';
|
||||||
|
$ptz_preset_data .= '</PTZPreset>';
|
||||||
|
$self->sendPutRequest($url_path, $ptz_preset_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub presetGoto
|
||||||
|
{
|
||||||
|
my $self = shift;
|
||||||
|
my $params = shift;
|
||||||
|
my $preset_id = $self->getParam($params, 'preset');
|
||||||
|
|
||||||
|
my $url_path = '/PSIA/PTZ/channels/1/presets/' . $preset_id . '/goto';
|
||||||
|
|
||||||
|
$self->sendPutRequest($url_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
1;
|
||||||
|
__END__
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
ZoneMinder::Control::PSIA - Perl module for cameras implementing the PSIA
|
||||||
|
(Physical Security Interoperability Alliance), IP Media Devices API
|
||||||
|
specification
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use ZoneMinder::Control::PSIA;
|
||||||
|
place this in /usr/share/perl5/ZoneMinder/Control
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
This has so far been tested with:
|
||||||
|
- Trendnet TV-IP450PI
|
||||||
|
|
||||||
|
=head2 EXPORT
|
||||||
|
|
||||||
|
None by default.
|
||||||
|
|
||||||
|
=head1 COPYRIGHT AND LICENSE
|
||||||
|
|
||||||
|
Copyright (C) 2018 ZoneMinder LLC
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
=cut
|
|
@ -162,7 +162,7 @@ my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL};
|
||||||
my $event_id = 0;
|
my $event_id = 0;
|
||||||
|
|
||||||
if ( ! EVENT_PATH ) {
|
if ( ! EVENT_PATH ) {
|
||||||
Error("No event path defined. Config was $Config{ZM_DIR_EVENTS}\n");
|
Error("No event path defined. Config was $Config{ZM_DIR_EVENTS}");
|
||||||
die;
|
die;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,11 +173,11 @@ chdir( EVENT_PATH );
|
||||||
my $dbh = zmDbConnect();
|
my $dbh = zmDbConnect();
|
||||||
|
|
||||||
if ( $filter_name ) {
|
if ( $filter_name ) {
|
||||||
Info("Scanning for events using filter '$filter_name'\n");
|
Info("Scanning for events using filter '$filter_name'");
|
||||||
} elsif ( $filter_id ) {
|
} elsif ( $filter_id ) {
|
||||||
Info("Scanning for events using filter id '$filter_id'\n");
|
Info("Scanning for events using filter id '$filter_id'");
|
||||||
} else {
|
} else {
|
||||||
Info("Scanning for events using all filters\n");
|
Info("Scanning for events using all filters");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! ( $filter_name or $filter_id ) ) {
|
if ( ! ( $filter_name or $filter_id ) ) {
|
||||||
|
@ -191,7 +191,7 @@ my $last_action = 0;
|
||||||
while( !$zm_terminate ) {
|
while( !$zm_terminate ) {
|
||||||
my $now = time;
|
my $now = time;
|
||||||
if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) {
|
if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) {
|
||||||
Debug("Reloading filters\n");
|
Debug("Reloading filters");
|
||||||
$last_action = $now;
|
$last_action = $now;
|
||||||
@filters = getFilters({ Name=>$filter_name, Id=>$filter_id });
|
@filters = getFilters({ Name=>$filter_name, Id=>$filter_id });
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ while( !$zm_terminate ) {
|
||||||
|
|
||||||
last if (!$daemon and ($filter_name or $filter_id)) or $zm_terminate;
|
last if (!$daemon and ($filter_name or $filter_id)) or $zm_terminate;
|
||||||
|
|
||||||
Debug("Sleeping for $delay seconds\n");
|
Debug("Sleeping for $delay seconds");
|
||||||
sleep($delay);
|
sleep($delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,10 +405,10 @@ sub generateVideo {
|
||||||
chomp($output);
|
chomp($output);
|
||||||
my $status = $? >> 8;
|
my $status = $? >> 8;
|
||||||
if ( $status || logDebugging() ) {
|
if ( $status || logDebugging() ) {
|
||||||
Debug("Output: $output\n");
|
Debug("Output: $output");
|
||||||
}
|
}
|
||||||
if ( $status ) {
|
if ( $status ) {
|
||||||
Error("Video generation '$command' failed with status: $status\n");
|
Error("Video generation '$command' failed with status: $status");
|
||||||
if ( wantarray() ) {
|
if ( wantarray() ) {
|
||||||
return( undef, undef );
|
return( undef, undef );
|
||||||
}
|
}
|
||||||
|
@ -453,7 +453,7 @@ sub generateImage {
|
||||||
chomp( $output );
|
chomp( $output );
|
||||||
my $status = $? >> 8;
|
my $status = $? >> 8;
|
||||||
if ( $status || logDebugging() ) {
|
if ( $status || logDebugging() ) {
|
||||||
Debug("Output: $output\n");
|
Debug("Output: $output");
|
||||||
}
|
}
|
||||||
if ( $status ) {
|
if ( $status ) {
|
||||||
Error("Failed $command status $status");
|
Error("Failed $command status $status");
|
||||||
|
@ -479,10 +479,9 @@ sub uploadArchFile {
|
||||||
.'/'
|
.'/'
|
||||||
.(
|
.(
|
||||||
( $Config{ZM_UPLOAD_ARCH_ANALYSE} )
|
( $Config{ZM_UPLOAD_ARCH_ANALYSE} )
|
||||||
? '{*analyse,*capture}'
|
? '{*analyse.jpg,*capture.jpg,snapshot.jpg,*.mp4}'
|
||||||
: '*capture'
|
: '*capture.jpg,snapshot.jpg,*.mp4'
|
||||||
)
|
)
|
||||||
.'.jpg'
|
|
||||||
;
|
;
|
||||||
my @archImageFiles = glob($archImagePath);
|
my @archImageFiles = glob($archImagePath);
|
||||||
my $archLocPath;
|
my $archLocPath;
|
||||||
|
@ -492,11 +491,11 @@ sub uploadArchFile {
|
||||||
$archFile .= '.zip';
|
$archFile .= '.zip';
|
||||||
$archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile;
|
$archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile;
|
||||||
my $zip = Archive::Zip->new();
|
my $zip = Archive::Zip->new();
|
||||||
Info("Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n");
|
Info("Creating upload file '$archLocPath', ".int(@archImageFiles).' files');
|
||||||
|
|
||||||
my $status = &AZ_OK;
|
my $status = &AZ_OK;
|
||||||
foreach my $imageFile ( @archImageFiles ) {
|
foreach my $imageFile ( @archImageFiles ) {
|
||||||
Debug("Adding $imageFile\n");
|
Debug("Adding $imageFile");
|
||||||
my $member = $zip->addFile($imageFile);
|
my $member = $zip->addFile($imageFile);
|
||||||
if ( !$member ) {
|
if ( !$member ) {
|
||||||
Error("Unable toto add image file $imageFile to zip archive $archLocPath");
|
Error("Unable toto add image file $imageFile to zip archive $archLocPath");
|
||||||
|
@ -524,7 +523,7 @@ sub uploadArchFile {
|
||||||
$archFile .= '.tar';
|
$archFile .= '.tar';
|
||||||
}
|
}
|
||||||
$archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile;
|
$archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile;
|
||||||
Info("Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n");
|
Info("Creating upload file '$archLocPath', ".int(@archImageFiles).' files');
|
||||||
|
|
||||||
if ( $archError = !Archive::Tar->create_archive(
|
if ( $archError = !Archive::Tar->create_archive(
|
||||||
$archLocPath,
|
$archLocPath,
|
||||||
|
|
|
@ -755,6 +755,12 @@ void EventStream::runStream() {
|
||||||
// commands may set send_frame to true
|
// commands may set send_frame to true
|
||||||
while(checkCommandQueue());
|
while(checkCommandQueue());
|
||||||
|
|
||||||
|
// Update modified time of the socket .lock file so that we can tell which ones are stale.
|
||||||
|
if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) {
|
||||||
|
touch(sock_path_lock);
|
||||||
|
last_comm_update = now;
|
||||||
|
}
|
||||||
|
|
||||||
if ( step != 0 )
|
if ( step != 0 )
|
||||||
curr_frame_id += step;
|
curr_frame_id += step;
|
||||||
|
|
||||||
|
|
|
@ -82,8 +82,35 @@ static AVPixelFormat get_format(AVCodecContext *avctx, const enum AVPixelFormat
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
|
FfmpegCamera::FfmpegCamera(
|
||||||
Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ),
|
int p_id,
|
||||||
|
const std::string &p_path,
|
||||||
|
const std::string &p_method,
|
||||||
|
const std::string &p_options,
|
||||||
|
int p_width,
|
||||||
|
int p_height,
|
||||||
|
int p_colours,
|
||||||
|
int p_brightness,
|
||||||
|
int p_contrast,
|
||||||
|
int p_hue,
|
||||||
|
int p_colour,
|
||||||
|
bool p_capture,
|
||||||
|
bool p_record_audio
|
||||||
|
) :
|
||||||
|
Camera(
|
||||||
|
p_id,
|
||||||
|
FFMPEG_SRC,
|
||||||
|
p_width,
|
||||||
|
p_height,
|
||||||
|
p_colours,
|
||||||
|
ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours),
|
||||||
|
p_brightness,
|
||||||
|
p_contrast,
|
||||||
|
p_hue,
|
||||||
|
p_colour,
|
||||||
|
p_capture,
|
||||||
|
p_record_audio
|
||||||
|
),
|
||||||
mPath( p_path ),
|
mPath( p_path ),
|
||||||
mMethod( p_method ),
|
mMethod( p_method ),
|
||||||
mOptions( p_options )
|
mOptions( p_options )
|
||||||
|
@ -113,6 +140,7 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
|
||||||
videoStore = NULL;
|
videoStore = NULL;
|
||||||
video_last_pts = 0;
|
video_last_pts = 0;
|
||||||
have_video_keyframe = false;
|
have_video_keyframe = false;
|
||||||
|
packetqueue = NULL;
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
mConvertContext = NULL;
|
mConvertContext = NULL;
|
||||||
|
@ -151,12 +179,12 @@ void FfmpegCamera::Terminate() {
|
||||||
|
|
||||||
int FfmpegCamera::PrimeCapture() {
|
int FfmpegCamera::PrimeCapture() {
|
||||||
if ( mCanCapture ) {
|
if ( mCanCapture ) {
|
||||||
Info( "Priming capture from %s, CLosing", mPath.c_str() );
|
Info("Priming capture from %s, Closing", mPath.c_str());
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
mVideoStreamId = -1;
|
mVideoStreamId = -1;
|
||||||
mAudioStreamId = -1;
|
mAudioStreamId = -1;
|
||||||
Info( "Priming capture from %s", mPath.c_str() );
|
Info("Priming capture from %s", mPath.c_str());
|
||||||
|
|
||||||
return OpenFfmpeg();
|
return OpenFfmpeg();
|
||||||
}
|
}
|
||||||
|
@ -166,7 +194,7 @@ int FfmpegCamera::PreCapture() {
|
||||||
if ( ! mCanCapture )
|
if ( ! mCanCapture )
|
||||||
return OpenFfmpeg();
|
return OpenFfmpeg();
|
||||||
// Nothing to do here
|
// Nothing to do here
|
||||||
return( 0 );
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FfmpegCamera::Capture( Image &image ) {
|
int FfmpegCamera::Capture( Image &image ) {
|
||||||
|
@ -433,6 +461,7 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
|
|
||||||
Debug(3, "Found video stream at index %d", mVideoStreamId);
|
Debug(3, "Found video stream at index %d", mVideoStreamId);
|
||||||
Debug(3, "Found audio stream at index %d", mAudioStreamId);
|
Debug(3, "Found audio stream at index %d", mAudioStreamId);
|
||||||
|
packetqueue = new zm_packetqueue( mVideoStreamId > mAudioStreamId ? mVideoStreamId : mAudioStreamId );
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
mVideoCodecContext = avcodec_alloc_context3(NULL);
|
mVideoCodecContext = avcodec_alloc_context3(NULL);
|
||||||
|
@ -678,6 +707,10 @@ int FfmpegCamera::Close() {
|
||||||
delete videoStore;
|
delete videoStore;
|
||||||
videoStore = NULL;
|
videoStore = NULL;
|
||||||
}
|
}
|
||||||
|
if ( packetqueue ) {
|
||||||
|
delete packetqueue;
|
||||||
|
packetqueue = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} // end FfmpegCamera::Close
|
} // end FfmpegCamera::Close
|
||||||
|
@ -795,14 +828,14 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
|
||||||
ZMPacket *queued_packet;
|
ZMPacket *queued_packet;
|
||||||
|
|
||||||
// Clear all packets that predate the moment when the recording began
|
// Clear all packets that predate the moment when the recording began
|
||||||
packetqueue.clear_unwanted_packets( &recording, mVideoStreamId );
|
packetqueue->clear_unwanted_packets( &recording, mVideoStreamId );
|
||||||
|
|
||||||
while ( ( queued_packet = packetqueue.popPacket() ) ) {
|
while ( ( queued_packet = packetqueue->popPacket() ) ) {
|
||||||
AVPacket *avp = queued_packet->av_packet();
|
AVPacket *avp = queued_packet->av_packet();
|
||||||
|
|
||||||
packet_count += 1;
|
packet_count += 1;
|
||||||
//Write the packet to our video store
|
//Write the packet to our video store
|
||||||
Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, packetqueue.size() );
|
Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, packetqueue->size() );
|
||||||
if ( avp->stream_index == mVideoStreamId ) {
|
if ( avp->stream_index == mVideoStreamId ) {
|
||||||
ret = videoStore->writeVideoFramePacket( avp );
|
ret = videoStore->writeVideoFramePacket( avp );
|
||||||
have_video_keyframe = true;
|
have_video_keyframe = true;
|
||||||
|
@ -836,18 +869,18 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
|
||||||
if ( packet.stream_index == mVideoStreamId ) {
|
if ( packet.stream_index == mVideoStreamId ) {
|
||||||
if ( keyframe ) {
|
if ( keyframe ) {
|
||||||
Debug(3, "Clearing queue");
|
Debug(3, "Clearing queue");
|
||||||
packetqueue.clearQueue(monitor->GetPreEventCount(), mVideoStreamId);
|
packetqueue->clearQueue(monitor->GetPreEventCount(), mVideoStreamId);
|
||||||
packetqueue.queuePacket(&packet);
|
packetqueue->queuePacket(&packet);
|
||||||
} else if ( packetqueue.size() ) {
|
} else if ( packetqueue->size() ) {
|
||||||
// it's a keyframe or we already have something in the queue
|
// it's a keyframe or we already have something in the queue
|
||||||
packetqueue.queuePacket(&packet);
|
packetqueue->queuePacket(&packet);
|
||||||
}
|
}
|
||||||
} else if ( packet.stream_index == mAudioStreamId ) {
|
} else if ( packet.stream_index == mAudioStreamId ) {
|
||||||
// The following lines should ensure that the queue always begins with a video keyframe
|
// The following lines should ensure that the queue always begins with a video keyframe
|
||||||
//Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", record_audio, packetqueue.size() );
|
//Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", record_audio, packetqueue.size() );
|
||||||
if ( record_audio && packetqueue.size() ) {
|
if ( record_audio && packetqueue->size() ) {
|
||||||
// if it's audio, and we are doing audio, and there is already something in the queue
|
// if it's audio, and we are doing audio, and there is already something in the queue
|
||||||
packetqueue.queuePacket(&packet);
|
packetqueue->queuePacket(&packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // end if recording or not
|
} // end if recording or not
|
||||||
|
|
|
@ -79,7 +79,7 @@ class FfmpegCamera : public Camera {
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
||||||
VideoStore *videoStore;
|
VideoStore *videoStore;
|
||||||
zm_packetqueue packetqueue;
|
zm_packetqueue *packetqueue;
|
||||||
bool have_video_keyframe;
|
bool have_video_keyframe;
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
#if HAVE_LIBSWSCALE
|
||||||
|
|
|
@ -531,6 +531,12 @@ void MonitorStream::runStream() {
|
||||||
Debug(2, "Have checking command Queue for connkey: %d", connkey );
|
Debug(2, "Have checking command Queue for connkey: %d", connkey );
|
||||||
got_command = true;
|
got_command = true;
|
||||||
}
|
}
|
||||||
|
// Update modified time of the socket .lock file so that we can tell which ones are stale.
|
||||||
|
if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) {
|
||||||
|
touch(sock_path_lock);
|
||||||
|
last_comm_update = now;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( paused ) {
|
if ( paused ) {
|
||||||
|
|
|
@ -21,27 +21,31 @@
|
||||||
#include "zm_ffmpeg.h"
|
#include "zm_ffmpeg.h"
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
#define VIDEO_QUEUESIZE 200
|
zm_packetqueue::zm_packetqueue( int p_max_stream_id ) {
|
||||||
#define AUDIO_QUEUESIZE 50
|
max_stream_id = p_max_stream_id;
|
||||||
|
packet_counts = new int[max_stream_id+1];
|
||||||
zm_packetqueue::zm_packetqueue(){
|
for ( int i=0; i <= max_stream_id; ++i )
|
||||||
|
packet_counts = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
zm_packetqueue::~zm_packetqueue() {
|
zm_packetqueue::~zm_packetqueue() {
|
||||||
clearQueue();
|
clearQueue();
|
||||||
|
delete[] packet_counts;
|
||||||
|
packet_counts = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool zm_packetqueue::queuePacket( ZMPacket* zm_packet ) {
|
bool zm_packetqueue::queuePacket(ZMPacket* zm_packet) {
|
||||||
pktQueue.push_back( zm_packet );
|
pktQueue.push_back(zm_packet);
|
||||||
|
packet_counts[zm_packet->packet.stream_index] += 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool zm_packetqueue::queuePacket( AVPacket* av_packet ) {
|
|
||||||
|
bool zm_packetqueue::queuePacket(AVPacket* av_packet) {
|
||||||
|
|
||||||
ZMPacket *zm_packet = new ZMPacket( av_packet );
|
ZMPacket *zm_packet = new ZMPacket(av_packet);
|
||||||
|
|
||||||
pktQueue.push_back( zm_packet );
|
pktQueue.push_back(zm_packet);
|
||||||
|
packet_counts[zm_packet->packet.stream_index] += 1;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -53,13 +57,14 @@ ZMPacket* zm_packetqueue::popPacket( ) {
|
||||||
|
|
||||||
ZMPacket *packet = pktQueue.front();
|
ZMPacket *packet = pktQueue.front();
|
||||||
pktQueue.pop_front();
|
pktQueue.pop_front();
|
||||||
|
packet_counts[packet->packet.stream_index] -= 1;
|
||||||
|
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream_id ) {
|
unsigned int zm_packetqueue::clearQueue(unsigned int frames_to_keep, int stream_id) {
|
||||||
|
|
||||||
Debug(3, "Clearing all but %d frames, queue has %d", frames_to_keep, pktQueue.size() );
|
Debug(3, "Clearing all but %d frames, queue has %d", frames_to_keep, pktQueue.size());
|
||||||
frames_to_keep += 1;
|
frames_to_keep += 1;
|
||||||
|
|
||||||
if ( pktQueue.empty() ) {
|
if ( pktQueue.empty() ) {
|
||||||
|
@ -74,7 +79,8 @@ unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream
|
||||||
ZMPacket *zm_packet = *it;
|
ZMPacket *zm_packet = *it;
|
||||||
AVPacket *av_packet = &(zm_packet->packet);
|
AVPacket *av_packet = &(zm_packet->packet);
|
||||||
|
|
||||||
Debug(4, "Looking at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
|
Debug(4, "Looking at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)",
|
||||||
|
av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
|
||||||
|
|
||||||
// Want frames_to_keep video keyframes. Otherwise, we may not have enough
|
// Want frames_to_keep video keyframes. Otherwise, we may not have enough
|
||||||
if ( ( av_packet->stream_index == stream_id) ) {
|
if ( ( av_packet->stream_index == stream_id) ) {
|
||||||
|
@ -83,21 +89,23 @@ unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we start on a keyframe
|
// Make sure we start on a keyframe
|
||||||
for ( ; it != pktQueue.rend(); ++it ) {
|
for ( ; it != pktQueue.rend(); ++it ) {
|
||||||
ZMPacket *zm_packet = *it;
|
ZMPacket *zm_packet = *it;
|
||||||
AVPacket *av_packet = &(zm_packet->packet);
|
AVPacket *av_packet = &(zm_packet->packet);
|
||||||
|
|
||||||
Debug(5, "Looking for keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
|
Debug(5, "Looking for keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)",
|
||||||
|
av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
|
||||||
|
|
||||||
// Want frames_to_keep video keyframes. Otherwise, we may not have enough
|
// Want frames_to_keep video keyframes. Otherwise, we may not have enough
|
||||||
if ( ( av_packet->stream_index == stream_id) && ( av_packet->flags & AV_PKT_FLAG_KEY ) ) {
|
if ( ( av_packet->stream_index == stream_id) && ( av_packet->flags & AV_PKT_FLAG_KEY ) ) {
|
||||||
Debug(4, "Found keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
|
Debug(4, "Found keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)",
|
||||||
|
av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( frames_to_keep ) {
|
if ( frames_to_keep ) {
|
||||||
Debug(3, "Hit end of queue, still need (%d) video frames", frames_to_keep );
|
Debug(3, "Hit end of queue, still need (%d) video frames", frames_to_keep);
|
||||||
}
|
}
|
||||||
if ( it != pktQueue.rend() ) {
|
if ( it != pktQueue.rend() ) {
|
||||||
// We want to keep this packet, so advance to the next
|
// We want to keep this packet, so advance to the next
|
||||||
|
@ -105,22 +113,25 @@ unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream
|
||||||
}
|
}
|
||||||
unsigned int delete_count = 0;
|
unsigned int delete_count = 0;
|
||||||
while ( it != pktQueue.rend() ) {
|
while ( it != pktQueue.rend() ) {
|
||||||
Debug(4, "Deleting a packet from the front, count is (%d)", delete_count );
|
Debug(4, "Deleting a packet from the front, count is (%d)", delete_count);
|
||||||
|
|
||||||
packet = pktQueue.front();
|
packet = pktQueue.front();
|
||||||
pktQueue.pop_front();
|
pktQueue.pop_front();
|
||||||
|
packet_counts[packet->packet.stream_index] -= 1;
|
||||||
delete packet;
|
delete packet;
|
||||||
|
|
||||||
delete_count += 1;
|
delete_count += 1;
|
||||||
}
|
}
|
||||||
Debug(3, "Deleted (%d) packets", delete_count );
|
packet = NULL; // tidy up for valgrind
|
||||||
|
Debug(3, "Deleted %d packets, %d remaining", delete_count, pktQueue.size());
|
||||||
return delete_count;
|
return delete_count;
|
||||||
} // end unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream_id )
|
} // end unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream_id )
|
||||||
|
|
||||||
void zm_packetqueue::clearQueue() {
|
void zm_packetqueue::clearQueue() {
|
||||||
ZMPacket *packet = NULL;
|
ZMPacket *packet = NULL;
|
||||||
while(!pktQueue.empty()) {
|
while (!pktQueue.empty()) {
|
||||||
packet = pktQueue.front();
|
packet = pktQueue.front();
|
||||||
|
packet_counts[packet->packet.stream_index] -= 1;
|
||||||
pktQueue.pop_front();
|
pktQueue.pop_front();
|
||||||
delete packet;
|
delete packet;
|
||||||
}
|
}
|
||||||
|
@ -130,18 +141,20 @@ unsigned int zm_packetqueue::size() {
|
||||||
return pktQueue.size();
|
return pktQueue.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int zm_packetqueue::packet_count( int stream_id ) {
|
||||||
|
return packet_counts[stream_id];
|
||||||
|
} // end int zm_packetqueue::packet_count( int stream_id )
|
||||||
|
|
||||||
void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVideoStreamId ) {
|
void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVideoStreamId ) {
|
||||||
// Need to find the keyframe <= recording_started. Can get rid of audio packets.
|
// Need to find the keyframe <= recording_started. Can get rid of audio packets.
|
||||||
if ( pktQueue.empty() ) {
|
if ( pktQueue.empty() )
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// Step 1 - find keyframe < recording_started.
|
// Step 1 - find keyframe < recording_started.
|
||||||
// Step 2 - pop packets until we get to the packet in step 2
|
// Step 2 - pop packets until we get to the packet in step 2
|
||||||
std::list<ZMPacket *>::reverse_iterator it;
|
std::list<ZMPacket *>::reverse_iterator it;
|
||||||
|
|
||||||
Debug(3, "Looking for keyframe after start recording stream id (%d)", mVideoStreamId );
|
Debug(3, "Looking for keyframe after start recording stream id (%d)", mVideoStreamId);
|
||||||
for ( it = pktQueue.rbegin(); it != pktQueue.rend(); ++ it ) {
|
for ( it = pktQueue.rbegin(); it != pktQueue.rend(); ++ it ) {
|
||||||
ZMPacket *zm_packet = *it;
|
ZMPacket *zm_packet = *it;
|
||||||
AVPacket *av_packet = &(zm_packet->packet);
|
AVPacket *av_packet = &(zm_packet->packet);
|
||||||
|
@ -175,9 +188,11 @@ void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVi
|
||||||
//while ( pktQueue.rend() != it ) {
|
//while ( pktQueue.rend() != it ) {
|
||||||
packet = pktQueue.front();
|
packet = pktQueue.front();
|
||||||
pktQueue.pop_front();
|
pktQueue.pop_front();
|
||||||
|
packet_counts[packet->packet.stream_index] -= 1;
|
||||||
delete packet;
|
delete packet;
|
||||||
deleted_frames += 1;
|
deleted_frames += 1;
|
||||||
}
|
}
|
||||||
|
packet = NULL; // tidy up for valgrind
|
||||||
|
|
||||||
zm_packet = pktQueue.front();
|
zm_packet = pktQueue.front();
|
||||||
av_packet = &(zm_packet->packet);
|
av_packet = &(zm_packet->packet);
|
||||||
|
@ -186,4 +201,4 @@ void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVi
|
||||||
} else {
|
} else {
|
||||||
Debug(1, "Done looking for keyframe. Deleted %d frames. Remaining frames in queue: %d stream of head packet is (%d), keyframe (%d), distance(%d), packets(%d)", deleted_frames, pktQueue.size(), av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), distance( it, pktQueue.rend() ), pktQueue.size() );
|
Debug(1, "Done looking for keyframe. Deleted %d frames. Remaining frames in queue: %d stream of head packet is (%d), keyframe (%d), distance(%d), packets(%d)", deleted_frames, pktQueue.size(), av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), distance( it, pktQueue.rend() ), pktQueue.size() );
|
||||||
}
|
}
|
||||||
}
|
} // end void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVideoStreamId )
|
||||||
|
|
|
@ -29,23 +29,25 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
class zm_packetqueue {
|
class zm_packetqueue {
|
||||||
public:
|
public:
|
||||||
zm_packetqueue();
|
zm_packetqueue(int max_stream_id);
|
||||||
virtual ~zm_packetqueue();
|
virtual ~zm_packetqueue();
|
||||||
bool queuePacket( AVPacket* packet, struct timeval *timestamp );
|
bool queuePacket(AVPacket* packet, struct timeval *timestamp);
|
||||||
bool queuePacket( ZMPacket* packet );
|
bool queuePacket(ZMPacket* packet);
|
||||||
bool queuePacket( AVPacket* packet );
|
bool queuePacket(AVPacket* packet);
|
||||||
ZMPacket * popPacket( );
|
ZMPacket * popPacket();
|
||||||
bool popVideoPacket(ZMPacket* packet);
|
bool popVideoPacket(ZMPacket* packet);
|
||||||
bool popAudioPacket(ZMPacket* packet);
|
bool popAudioPacket(ZMPacket* packet);
|
||||||
unsigned int clearQueue( unsigned int video_frames_to_keep, int stream_id );
|
unsigned int clearQueue(unsigned int video_frames_to_keep, int stream_id);
|
||||||
void clearQueue( );
|
void clearQueue();
|
||||||
unsigned int size();
|
unsigned int size();
|
||||||
void clear_unwanted_packets( timeval *recording, int mVideoStreamId );
|
void clear_unwanted_packets(timeval *recording, int mVideoStreamId);
|
||||||
|
int packet_count(int stream_id);
|
||||||
private:
|
private:
|
||||||
std::list<ZMPacket *> pktQueue;
|
std::list<ZMPacket *> pktQueue;
|
||||||
|
int max_stream_id;
|
||||||
|
int *packet_counts; /* packet count for each stream_id, to keep track of how many video vs audio packets are in the queue */
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -332,6 +332,8 @@ void StreamBase::openComms() {
|
||||||
snprintf(rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", staticConfig.PATH_SOCKS.c_str(), connkey);
|
snprintf(rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", staticConfig.PATH_SOCKS.c_str(), connkey);
|
||||||
strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path)-1);
|
strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path)-1);
|
||||||
rem_addr.sun_family = AF_UNIX;
|
rem_addr.sun_family = AF_UNIX;
|
||||||
|
|
||||||
|
gettimeofday(&last_comm_update, NULL);
|
||||||
} // end if connKey > 0
|
} // end if connKey > 0
|
||||||
Debug(2, "comms open");
|
Debug(2, "comms open");
|
||||||
} // end void StreamBase::openComms()
|
} // end void StreamBase::openComms()
|
||||||
|
|
|
@ -85,6 +85,7 @@ protected:
|
||||||
int step;
|
int step;
|
||||||
|
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
|
struct timeval last_comm_update;
|
||||||
|
|
||||||
double base_fps;
|
double base_fps;
|
||||||
double effective_fps;
|
double effective_fps;
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <fcntl.h> /* Definition of AT_* constants */
|
||||||
|
#include <sys/stat.h>
|
||||||
#if defined(__arm__)
|
#if defined(__arm__)
|
||||||
#include <sys/auxv.h>
|
#include <sys/auxv.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -414,3 +416,22 @@ Warning("ZM Compiled without LIBCURL. UriDecoding not implemented.");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void touch(const char *pathname) {
|
||||||
|
int fd = open(pathname,
|
||||||
|
O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK,
|
||||||
|
0666);
|
||||||
|
if ( fd < 0 ) {
|
||||||
|
// Couldn't open that path.
|
||||||
|
Error("Couldn't open() path \"%s in touch", pathname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int rc = utimensat(AT_FDCWD,
|
||||||
|
pathname,
|
||||||
|
nullptr,
|
||||||
|
0);
|
||||||
|
if ( rc ) {
|
||||||
|
Error("Couldn't utimensat() path %s in touch", pathname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,5 +63,5 @@ extern unsigned int neonversion;
|
||||||
|
|
||||||
char *timeval_to_string( struct timeval tv );
|
char *timeval_to_string( struct timeval tv );
|
||||||
std::string UriDecode( const std::string &encoded );
|
std::string UriDecode( const std::string &encoded );
|
||||||
|
void touch( const char *pathname );
|
||||||
#endif // ZM_UTILS_H
|
#endif // ZM_UTILS_H
|
||||||
|
|
|
@ -55,7 +55,7 @@ class Frame {
|
||||||
#return $_SERVER['PHP_SELF'].'?view=image&fid='.$this->{'Id'}.'&show='.$show.'&filename='.$this->Event()->MonitorId().'_'.$this->{'EventId'}.'_'.$this->{'FrameId'}.'.jpg';
|
#return $_SERVER['PHP_SELF'].'?view=image&fid='.$this->{'Id'}.'&show='.$show.'&filename='.$this->Event()->MonitorId().'_'.$this->{'EventId'}.'_'.$this->{'FrameId'}.'.jpg';
|
||||||
} // end function getImageSrc
|
} // end function getImageSrc
|
||||||
|
|
||||||
public static function find( $parameters = array(), $limit = NULL ) {
|
public static function find( $parameters = array(), $options = NULL ) {
|
||||||
$sql = 'SELECT * FROM Frames';
|
$sql = 'SELECT * FROM Frames';
|
||||||
$values = array();
|
$values = array();
|
||||||
if ( sizeof($parameters) ) {
|
if ( sizeof($parameters) ) {
|
||||||
|
@ -65,17 +65,23 @@ class Frame {
|
||||||
) );
|
) );
|
||||||
$values = array_values( $parameters );
|
$values = array_values( $parameters );
|
||||||
}
|
}
|
||||||
if ( $limit ) {
|
if ( $options ) {
|
||||||
if ( is_integer( $limit ) or ctype_digit( $limit ) ) {
|
if ( isset($options['order']) ) {
|
||||||
$sql .= ' LIMIT ' . $limit;
|
$sql .= ' ORDER BY ' . $options['order'];
|
||||||
} else {
|
}
|
||||||
$backTrace = debug_backtrace();
|
if ( isset($options['limit']) ) {
|
||||||
$file = $backTrace[1]['file'];
|
if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) {
|
||||||
$line = $backTrace[1]['line'];
|
$sql .= ' LIMIT ' . $options['limit'];
|
||||||
Error("Invalid value for limit($limit) passed to Frame::find from $file:$line");
|
} else {
|
||||||
return array();
|
$backTrace = debug_backtrace();
|
||||||
}
|
$file = $backTrace[1]['file'];
|
||||||
}
|
$line = $backTrace[1]['line'];
|
||||||
|
Error("Invalid value for limit(".$options['limit'].") passed to Frame::find from $file:$line");
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$results = dbFetchAll($sql, NULL, $values);
|
$results = dbFetchAll($sql, NULL, $values);
|
||||||
if ( $results ) {
|
if ( $results ) {
|
||||||
return array_map( function($id){ return new Frame($id); }, $results );
|
return array_map( function($id){ return new Frame($id); }, $results );
|
||||||
|
@ -83,8 +89,9 @@ class Frame {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function find_one( $parameters = array() ) {
|
public static function find_one( $parameters = array(), $options = null ) {
|
||||||
$results = Frame::find( $parameters, 1 );
|
$options['limit'] = 1;
|
||||||
|
$results = Frame::find($parameters, $options);
|
||||||
if ( ! sizeof($results) ) {
|
if ( ! sizeof($results) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,19 +152,23 @@ private $control_fields = array(
|
||||||
}
|
}
|
||||||
if ( $this->{'Controllable'} ) {
|
if ( $this->{'Controllable'} ) {
|
||||||
$s = dbFetchOne('SELECT * FROM Controls WHERE Id=?', NULL, array($this->{'ControlId'}) );
|
$s = dbFetchOne('SELECT * FROM Controls WHERE Id=?', NULL, array($this->{'ControlId'}) );
|
||||||
foreach ($s as $k => $v) {
|
if ( $s ) {
|
||||||
if ( $k == 'Id' ) {
|
foreach ($s as $k => $v) {
|
||||||
continue;
|
if ( $k == 'Id' ) {
|
||||||
# The reason for these is that the name overlaps Monitor fields.
|
continue;
|
||||||
} else if ( $k == 'Protocol' ) {
|
# The reason for these is that the name overlaps Monitor fields.
|
||||||
$this->{'ControlProtocol'} = $v;
|
} else if ( $k == 'Protocol' ) {
|
||||||
} else if ( $k == 'Name' ) {
|
$this->{'ControlProtocol'} = $v;
|
||||||
$this->{'ControlName'} = $v;
|
} else if ( $k == 'Name' ) {
|
||||||
} else if ( $k == 'Type' ) {
|
$this->{'ControlName'} = $v;
|
||||||
$this->{'ControlType'} = $v;
|
} else if ( $k == 'Type' ) {
|
||||||
} else {
|
$this->{'ControlType'} = $v;
|
||||||
$this->{$k} = $v;
|
} else {
|
||||||
|
$this->{$k} = $v;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Warning('No Controls found for monitor '.$this->{'Id'} . ' ' . $this->{'Name'}.' althrough it is marked as controllable');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
global $monitor_cache;
|
global $monitor_cache;
|
||||||
|
|
|
@ -195,11 +195,12 @@ if ( currentView != 'none' && currentView != 'login' ) {
|
||||||
$j.getJSON(thisUrl + '?view=request&request=status&entity=navBar')
|
$j.getJSON(thisUrl + '?view=request&request=status&entity=navBar')
|
||||||
.done(setNavBar)
|
.done(setNavBar)
|
||||||
.fail(function( jqxhr, textStatus, error ) {
|
.fail(function( jqxhr, textStatus, error ) {
|
||||||
var err = textStatus + ", " + error;
|
console.log( "Request Failed: " + textStatus + ", " + error);
|
||||||
console.log( "Request Failed: " + err );
|
if ( textStatus != "timeout" ) {
|
||||||
// The idea is that this should only fail due to auth, so reload the page
|
// The idea is that this should only fail due to auth, so reload the page
|
||||||
// which should go to login if it can't stay logged in.
|
// which should go to login if it can't stay logged in.
|
||||||
window.location.reload( true );
|
window.location.reload( true );
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -253,7 +253,7 @@ for ( $i=0; $i < count($terms); $i++ ) {
|
||||||
<td><?php echo htmlSelect( "filter[Query][terms][$i][op]", $opTypes, $term['op'] ); ?></td>
|
<td><?php echo htmlSelect( "filter[Query][terms][$i][op]", $opTypes, $term['op'] ); ?></td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="filter[Query][terms][<?php echo $i ?>][val]" id="filter[Query][terms][<?php echo $i ?>][val]" value="<?php echo isset($term['val'])?validHtmlStr(str_replace('T', ' ', $term['val'])):'' ?>"/>
|
<input type="text" name="filter[Query][terms][<?php echo $i ?>][val]" id="filter[Query][terms][<?php echo $i ?>][val]" value="<?php echo isset($term['val'])?validHtmlStr(str_replace('T', ' ', $term['val'])):'' ?>"/>
|
||||||
<script type="text/javascript">$j("[name$='\\[<?php echo $i ?>\\]\\[val\\]']").timepicker({timeFormat: "HH:mm:ss", constrainInput: falsepi}); </script>
|
<script type="text/javascript">$j("[name$='\\[<?php echo $i ?>\\]\\[val\\]']").timepicker({timeFormat: "HH:mm:ss", constrainInput: false}); </script>
|
||||||
</td>
|
</td>
|
||||||
<?php
|
<?php
|
||||||
} elseif ( $term['attr'] == 'StateId' ) {
|
} elseif ( $term['attr'] == 'StateId' ) {
|
||||||
|
|
|
@ -76,6 +76,23 @@ if ( empty($_REQUEST['path']) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# if alarm, get the fid of the first alarmed frame if available and let the
|
||||||
|
# fid= code continue processing it. Sort it to get the first alarmed frame
|
||||||
|
if ( $_REQUEST['fid'] == 'alarm' ) {
|
||||||
|
$Frame = Frame::find_one(array('EventId'=>$_REQUEST['eid'], 'Type'=>'Alarm'),
|
||||||
|
array('order'=>'FrameId ASC'));
|
||||||
|
if ( !$Frame ) # no alarms
|
||||||
|
$Frame = Frame::find_one(array('EventId'=>$_REQUEST['eid'])); # first frame
|
||||||
|
if ( !$Frame ) {
|
||||||
|
Warning("No frame found for event " + $_REQUEST['eid']);
|
||||||
|
$Frame = new Frame();
|
||||||
|
$Frame->Delta(1);
|
||||||
|
$Frame->FrameId('snapshot');
|
||||||
|
}
|
||||||
|
$_REQUEST['fid']=$Frame->FrameId();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if ( $_REQUEST['fid'] == 'snapshot' ) {
|
if ( $_REQUEST['fid'] == 'snapshot' ) {
|
||||||
$Frame = Frame::find_one(array('EventId'=>$_REQUEST['eid'], 'Score'=>$Event->MaxScore()));
|
$Frame = Frame::find_one(array('EventId'=>$_REQUEST['eid'], 'Score'=>$Event->MaxScore()));
|
||||||
if ( !$Frame )
|
if ( !$Frame )
|
||||||
|
|
Loading…
Reference in New Issue