wip
This commit is contained in:
commit
ef0379dd18
|
@ -232,6 +232,7 @@ CREATE TABLE `Filters` (
|
||||||
`AutoExecute` tinyint(3) unsigned NOT NULL default '0',
|
`AutoExecute` tinyint(3) unsigned NOT NULL default '0',
|
||||||
`AutoExecuteCmd` tinytext,
|
`AutoExecuteCmd` tinytext,
|
||||||
`AutoDelete` tinyint(3) unsigned NOT NULL default '0',
|
`AutoDelete` tinyint(3) unsigned NOT NULL default '0',
|
||||||
|
`UpdateDiskSpace` tinyint(3) unsigned NOT NULL default '0',
|
||||||
`Background` tinyint(1) unsigned NOT NULL default '0',
|
`Background` tinyint(1) unsigned NOT NULL default '0',
|
||||||
`Concurrent` tinyint(1) unsigned NOT NULL default '0',
|
`Concurrent` tinyint(1) unsigned NOT NULL default '0',
|
||||||
PRIMARY KEY (`Id`),
|
PRIMARY KEY (`Id`),
|
||||||
|
@ -289,6 +290,7 @@ CREATE TABLE `Logs` (
|
||||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||||
|
|
||||||
CREATE INDEX `Logs_TimeKey_idx` ON `Logs` (`TimeKey`);
|
CREATE INDEX `Logs_TimeKey_idx` ON `Logs` (`TimeKey`);
|
||||||
|
CREATE INDEX `Logs_Level_idx` ON `Logs` (`Level`);
|
||||||
--
|
--
|
||||||
-- Table structure for table `Manufacturers`
|
-- Table structure for table `Manufacturers`
|
||||||
--
|
--
|
||||||
|
@ -382,6 +384,8 @@ CREATE TABLE `Monitors` (
|
||||||
`Deinterlacing` int(10) unsigned NOT NULL default '0',
|
`Deinterlacing` int(10) unsigned NOT NULL default '0',
|
||||||
`SaveJPEGs` TINYINT NOT NULL DEFAULT '3' ,
|
`SaveJPEGs` TINYINT NOT NULL DEFAULT '3' ,
|
||||||
`VideoWriter` TINYINT NOT NULL DEFAULT '0',
|
`VideoWriter` TINYINT NOT NULL DEFAULT '0',
|
||||||
|
`OutputCodec` enum('h264','mjpeg'),
|
||||||
|
`OutputContainer` enum('mp4','mkv'),
|
||||||
`EncoderParameters` TEXT,
|
`EncoderParameters` TEXT,
|
||||||
`RecordAudio` TINYINT NOT NULL DEFAULT '0',
|
`RecordAudio` TINYINT NOT NULL DEFAULT '0',
|
||||||
`RTSPDescribe` tinyint(1) unsigned,
|
`RTSPDescribe` tinyint(1) unsigned,
|
||||||
|
|
|
@ -125,7 +125,7 @@ sub Execute {
|
||||||
push @results, $event;
|
push @results, $event;
|
||||||
}
|
}
|
||||||
$sth->finish();
|
$sth->finish();
|
||||||
Debug("Loaded " . @results . " events for filter $_[0]{Name} using query ($sql)");
|
Debug('Loaded ' . @results . " events for filter $_[0]{Name} using query ($sql)");
|
||||||
return @results;
|
return @results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,38 +147,59 @@ sub Sql {
|
||||||
foreach my $term ( @{$filter_expr->{terms}} ) {
|
foreach my $term ( @{$filter_expr->{terms}} ) {
|
||||||
|
|
||||||
if ( exists($term->{cnj}) ) {
|
if ( exists($term->{cnj}) ) {
|
||||||
$self->{Sql} .= " ".$term->{cnj}." ";
|
$self->{Sql} .= ' '.$term->{cnj}." ";
|
||||||
}
|
}
|
||||||
if ( exists($term->{obr}) ) {
|
if ( exists($term->{obr}) ) {
|
||||||
$self->{Sql} .= " ".str_repeat( "(", $term->{obr} )." ";
|
$self->{Sql} .= ' '.str_repeat( "(", $term->{obr} )." ";
|
||||||
}
|
}
|
||||||
my $value = $term->{val};
|
my $value = $term->{val};
|
||||||
my @value_list;
|
my @value_list;
|
||||||
if ( $term->{attr} ) {
|
if ( $term->{attr} ) {
|
||||||
if ( $term->{attr} =~ /^Monitor/ ) {
|
if ( $term->{attr} =~ /^Monitor/ ) {
|
||||||
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
|
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
|
||||||
$self->{Sql} .= "M.".$temp_attr_name;
|
$self->{Sql} .= 'M.'.$temp_attr_name;
|
||||||
} elsif ( $term->{attr} =~ /^Server/ ) {
|
} elsif ( $term->{attr} =~ /^Server/ ) {
|
||||||
$self->{Sql} .= "M.".$term->{attr};
|
$self->{Sql} .= 'M.'.$term->{attr};
|
||||||
|
|
||||||
|
# StartTime options
|
||||||
} elsif ( $term->{attr} eq 'DateTime' ) {
|
} elsif ( $term->{attr} eq 'DateTime' ) {
|
||||||
$self->{Sql} .= "E.StartTime";
|
$self->{Sql} .= 'E.StartTime';
|
||||||
|
} elsif ( $term->{attr} eq 'StartDateTime' ) {
|
||||||
|
$self->{Sql} .= 'E.StartTime';
|
||||||
} elsif ( $term->{attr} eq 'Date' ) {
|
} elsif ( $term->{attr} eq 'Date' ) {
|
||||||
$self->{Sql} .= "to_days( E.StartTime )";
|
$self->{Sql} .= 'to_days( E.StartTime )';
|
||||||
|
} elsif ( $term->{attr} eq 'StartDate' ) {
|
||||||
|
$self->{Sql} .= 'to_days( E.StartTime )';
|
||||||
} elsif ( $term->{attr} eq 'Time' ) {
|
} elsif ( $term->{attr} eq 'Time' ) {
|
||||||
$self->{Sql} .= "extract( hour_second from E.StartTime )";
|
$self->{Sql} .= "extract( hour_second from E.StartTime )";
|
||||||
} elsif ( $term->{attr} eq 'Weekday' ) {
|
} elsif ( $term->{attr} eq 'Weekday' ) {
|
||||||
$self->{Sql} .= "weekday( E.StartTime )";
|
$self->{Sql} .= "weekday( E.StartTime )";
|
||||||
|
|
||||||
|
# EndTIme options
|
||||||
|
} elsif ( $term->{attr} eq 'EndDateTime' ) {
|
||||||
|
$self->{Sql} .= 'E.EndTime';
|
||||||
|
} elsif ( $term->{attr} eq 'EndDate' ) {
|
||||||
|
$self->{Sql} .= 'to_days( E.EndTime )';
|
||||||
|
} elsif ( $term->{attr} eq 'EndTime' ) {
|
||||||
|
$self->{Sql} .= "extract( hour_second from E.EndTime )";
|
||||||
|
} elsif ( $term->{attr} eq 'EndWeekday' ) {
|
||||||
|
$self->{Sql} .= "weekday( E.EndTime )";
|
||||||
|
|
||||||
|
#
|
||||||
|
} elsif ( $term->{attr} eq 'DiskSpace' ) {
|
||||||
|
$self->{Sql} .= 'E.DiskSpace';
|
||||||
|
$self->{HasDiskPercent} = !undef;
|
||||||
} elsif ( $term->{attr} eq 'DiskPercent' ) {
|
} elsif ( $term->{attr} eq 'DiskPercent' ) {
|
||||||
$self->{Sql} .= "zmDiskPercent";
|
$self->{Sql} .= 'zmDiskPercent';
|
||||||
$self->{HasDiskPercent} = !undef;
|
$self->{HasDiskPercent} = !undef;
|
||||||
} elsif ( $term->{attr} eq 'DiskBlocks' ) {
|
} elsif ( $term->{attr} eq 'DiskBlocks' ) {
|
||||||
$self->{Sql} .= "zmDiskBlocks";
|
$self->{Sql} .= 'zmDiskBlocks';
|
||||||
$self->{HasDiskBlocks} = !undef;
|
$self->{HasDiskBlocks} = !undef;
|
||||||
} elsif ( $term->{attr} eq 'SystemLoad' ) {
|
} elsif ( $term->{attr} eq 'SystemLoad' ) {
|
||||||
$self->{Sql} .= "zmSystemLoad";
|
$self->{Sql} .= 'zmSystemLoad';
|
||||||
$self->{HasSystemLoad} = !undef;
|
$self->{HasSystemLoad} = !undef;
|
||||||
} else {
|
} else {
|
||||||
$self->{Sql} .= "E.".$term->{attr};
|
$self->{Sql} .= 'E.'.$term->{attr};
|
||||||
}
|
}
|
||||||
|
|
||||||
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
|
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
|
||||||
|
@ -243,11 +264,11 @@ sub Sql {
|
||||||
} elsif ( $term->{op} eq '!~' ) {
|
} elsif ( $term->{op} eq '!~' ) {
|
||||||
$self->{Sql} .= " not in (".join( ",", @value_list ).")";
|
$self->{Sql} .= " not in (".join( ",", @value_list ).")";
|
||||||
} else {
|
} else {
|
||||||
$self->{Sql} .= " ".$term->{op}." $value";
|
$self->{Sql} .= ' '.$term->{op}." $value";
|
||||||
}
|
}
|
||||||
} # end if has an operator
|
} # end if has an operator
|
||||||
if ( exists($term->{cbr}) ) {
|
if ( exists($term->{cbr}) ) {
|
||||||
$self->{Sql} .= " ".str_repeat( ")", $term->{cbr} )." ";
|
$self->{Sql} .= ' '.str_repeat( ")", $term->{cbr} )." ";
|
||||||
}
|
}
|
||||||
} # end foreach term
|
} # end foreach term
|
||||||
} # end if terms
|
} # end if terms
|
||||||
|
@ -284,7 +305,7 @@ sub Sql {
|
||||||
push @auto_terms, "E.Executed = 0";
|
push @auto_terms, "E.Executed = 0";
|
||||||
}
|
}
|
||||||
if ( @auto_terms ) {
|
if ( @auto_terms ) {
|
||||||
$sql .= " and ( ".join( " or ", @auto_terms )." )";
|
$sql .= " and ( ".join( ' or ', @auto_terms )." )";
|
||||||
}
|
}
|
||||||
if ( !$filter_expr->{sort_field} ) {
|
if ( !$filter_expr->{sort_field} ) {
|
||||||
$filter_expr->{sort_field} = 'StartTime';
|
$filter_expr->{sort_field} = 'StartTime';
|
||||||
|
@ -292,30 +313,34 @@ sub Sql {
|
||||||
}
|
}
|
||||||
my $sort_column = '';
|
my $sort_column = '';
|
||||||
if ( $filter_expr->{sort_field} eq 'Id' ) {
|
if ( $filter_expr->{sort_field} eq 'Id' ) {
|
||||||
$sort_column = "E.Id";
|
$sort_column = 'E.Id';
|
||||||
} elsif ( $filter_expr->{sort_field} eq 'MonitorName' ) {
|
} elsif ( $filter_expr->{sort_field} eq 'MonitorName' ) {
|
||||||
$sort_column = "M.Name";
|
$sort_column = 'M.Name';
|
||||||
} elsif ( $filter_expr->{sort_field} eq 'Name' ) {
|
} elsif ( $filter_expr->{sort_field} eq 'Name' ) {
|
||||||
$sort_column = "E.Name";
|
$sort_column = 'E.Name';
|
||||||
} elsif ( $filter_expr->{sort_field} eq 'StartTime' ) {
|
} elsif ( $filter_expr->{sort_field} eq 'StartTime' ) {
|
||||||
$sort_column = "E.StartTime";
|
$sort_column = 'E.StartTime';
|
||||||
|
} elsif ( $filter_expr->{sort_field} eq 'EndTime' ) {
|
||||||
|
$sort_column = 'E.EndTime';
|
||||||
} elsif ( $filter_expr->{sort_field} eq 'Secs' ) {
|
} elsif ( $filter_expr->{sort_field} eq 'Secs' ) {
|
||||||
$sort_column = "E.Length";
|
$sort_column = 'E.Length';
|
||||||
} elsif ( $filter_expr->{sort_field} eq 'Frames' ) {
|
} elsif ( $filter_expr->{sort_field} eq 'Frames' ) {
|
||||||
$sort_column = "E.Frames";
|
$sort_column = 'E.Frames';
|
||||||
} elsif ( $filter_expr->{sort_field} eq 'AlarmFrames' ) {
|
} elsif ( $filter_expr->{sort_field} eq 'AlarmFrames' ) {
|
||||||
$sort_column = "E.AlarmFrames";
|
$sort_column = 'E.AlarmFrames';
|
||||||
} elsif ( $filter_expr->{sort_field} eq 'TotScore' ) {
|
} elsif ( $filter_expr->{sort_field} eq 'TotScore' ) {
|
||||||
$sort_column = "E.TotScore";
|
$sort_column = 'E.TotScore';
|
||||||
} elsif ( $filter_expr->{sort_field} eq 'AvgScore' ) {
|
} elsif ( $filter_expr->{sort_field} eq 'AvgScore' ) {
|
||||||
$sort_column = "E.AvgScore";
|
$sort_column = 'E.AvgScore';
|
||||||
} elsif ( $filter_expr->{sort_field} eq 'MaxScore' ) {
|
} elsif ( $filter_expr->{sort_field} eq 'MaxScore' ) {
|
||||||
$sort_column = "E.MaxScore";
|
$sort_column = 'E.MaxScore';
|
||||||
|
} elsif ( $filter_expr->{sort_field} eq 'DiskSpace' ) {
|
||||||
|
$sort_column = 'E.DiskSpace';
|
||||||
} else {
|
} else {
|
||||||
$sort_column = "E.StartTime";
|
$sort_column = 'E.StartTime';
|
||||||
}
|
}
|
||||||
my $sort_order = $filter_expr->{sort_asc}?"asc":"desc";
|
my $sort_order = $filter_expr->{sort_asc}?'asc':'desc';
|
||||||
$sql .= " order by ".$sort_column." ".$sort_order;
|
$sql .= ' order by '.$sort_column." ".$sort_order;
|
||||||
if ( $filter_expr->{limit} ) {
|
if ( $filter_expr->{limit} ) {
|
||||||
$sql .= " limit 0,".$filter_expr->{limit};
|
$sql .= " limit 0,".$filter_expr->{limit};
|
||||||
}
|
}
|
||||||
|
@ -325,7 +350,7 @@ sub Sql {
|
||||||
} # end sub Sql
|
} # end sub Sql
|
||||||
|
|
||||||
sub getDiskPercent {
|
sub getDiskPercent {
|
||||||
my $command = "df " . ($_[0] ? $_[0] : '.');
|
my $command = 'df ' . ($_[0] ? $_[0] : '.');
|
||||||
my $df = qx( $command );
|
my $df = qx( $command );
|
||||||
my $space = -1;
|
my $space = -1;
|
||||||
if ( $df =~ /\s(\d+)%/ms ) {
|
if ( $df =~ /\s(\d+)%/ms ) {
|
||||||
|
@ -335,7 +360,7 @@ sub getDiskPercent {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub getDiskBlocks {
|
sub getDiskBlocks {
|
||||||
my $command = "df .";
|
my $command = 'df .';
|
||||||
my $df = qx( $command );
|
my $df = qx( $command );
|
||||||
my $space = -1;
|
my $space = -1;
|
||||||
if ( $df =~ /\s(\d+)\s+\d+\s+\d+%/ms ) {
|
if ( $df =~ /\s(\d+)\s+\d+\s+\d+%/ms ) {
|
||||||
|
@ -345,7 +370,7 @@ sub getDiskBlocks {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub getLoad {
|
sub getLoad {
|
||||||
my $command = "uptime .";
|
my $command = 'uptime .';
|
||||||
my $uptime = qx( $command );
|
my $uptime = qx( $command );
|
||||||
my $load = -1;
|
my $load = -1;
|
||||||
if ( $uptime =~ /load average:\s+([\d.]+)/ms ) {
|
if ( $uptime =~ /load average:\s+([\d.]+)/ms ) {
|
||||||
|
|
|
@ -678,6 +678,8 @@ sub Dump {
|
||||||
fetch()->logPrint( DEBUG, Data::Dumper->Dump( [ $var ], [ $label ] ) );
|
fetch()->logPrint( DEBUG, Data::Dumper->Dump( [ $var ], [ $label ] ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub debug { fetch()->logPrint( DEBUG, @_ ); }
|
||||||
|
|
||||||
sub Debug( @ ) {
|
sub Debug( @ ) {
|
||||||
fetch()->logPrint( DEBUG, @_ );
|
fetch()->logPrint( DEBUG, @_ );
|
||||||
}
|
}
|
||||||
|
@ -685,14 +687,24 @@ sub Debug( @ ) {
|
||||||
sub Info( @ ) {
|
sub Info( @ ) {
|
||||||
fetch()->logPrint( INFO, @_ );
|
fetch()->logPrint( INFO, @_ );
|
||||||
}
|
}
|
||||||
|
sub info {
|
||||||
|
fetch()->logPrint( INFO, @_ );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub Warning( @ ) {
|
sub Warning( @ ) {
|
||||||
fetch()->logPrint( WARNING, @_ );
|
fetch()->logPrint( WARNING, @_ );
|
||||||
}
|
}
|
||||||
|
sub warn {
|
||||||
|
fetch()->logPrint( WARNING, @_ );
|
||||||
|
}
|
||||||
|
|
||||||
sub Error( @ ) {
|
sub Error( @ ) {
|
||||||
fetch()->logPrint( ERROR, @_ );
|
fetch()->logPrint( ERROR, @_ );
|
||||||
}
|
}
|
||||||
|
sub error {
|
||||||
|
fetch()->logPrint( ERROR, @_ );
|
||||||
|
}
|
||||||
|
|
||||||
sub Fatal( @ ) {
|
sub Fatal( @ ) {
|
||||||
fetch()->logPrint( FATAL, @_ );
|
fetch()->logPrint( FATAL, @_ );
|
||||||
|
|
|
@ -42,7 +42,13 @@ use ZoneMinder::Config qw(:all);
|
||||||
use ZoneMinder::Logger qw(:all);
|
use ZoneMinder::Logger qw(:all);
|
||||||
use ZoneMinder::Database qw(:all);
|
use ZoneMinder::Database qw(:all);
|
||||||
|
|
||||||
use vars qw/ $AUTOLOAD /;
|
use vars qw/ $AUTOLOAD $log $dbh/;
|
||||||
|
|
||||||
|
*log = \$ZoneMinder::Logger::logger;
|
||||||
|
*dbh = \$ZoneMinder::Database::dbh;
|
||||||
|
|
||||||
|
my $debug = 1;
|
||||||
|
use constant DEBUG_ALL=>0;
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
my ( $parent, $id, $data ) = @_;
|
my ( $parent, $id, $data ) = @_;
|
||||||
|
@ -110,7 +116,269 @@ sub AUTOLOAD {
|
||||||
return $_[0]{$name};
|
return $_[0]{$name};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub save {
|
||||||
|
my ( $self, $data, $force_insert ) = @_;
|
||||||
|
|
||||||
|
my $type = ref $self;
|
||||||
|
if ( ! $type ) {
|
||||||
|
my ( $caller, undef, $line ) = caller;
|
||||||
|
$log->error("No type in Object::save. self:$self from $caller:$line");
|
||||||
|
}
|
||||||
|
my $local_dbh = eval '$'.$type.'::dbh';
|
||||||
|
$local_dbh = $ZoneMinder::Database::dbh if ! $local_dbh;
|
||||||
|
$self->set( $data ? $data : {} );
|
||||||
|
if ( $debug or DEBUG_ALL ) {
|
||||||
|
if ( $data ) {
|
||||||
|
foreach my $k ( keys %$data ) {
|
||||||
|
$log->debug("Object::save after set $k => $$data{$k} $$self{$k}");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$log->debug("No data after set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#$debug = 0;
|
||||||
|
|
||||||
|
my $table = eval '$'.$type.'::table';
|
||||||
|
my $fields = eval '\%'.$type.'::fields';
|
||||||
|
my $debug = eval '$'.$type.'::debug';
|
||||||
|
#$debug = DEBUG_ALL if ! $debug;
|
||||||
|
|
||||||
|
my %sql;
|
||||||
|
foreach my $k ( keys %$fields ) {
|
||||||
|
$sql{$$fields{$k}} = $$self{$k} if defined $$fields{$k};
|
||||||
|
} # end foreach
|
||||||
|
if ( ! $force_insert ) {
|
||||||
|
$sql{$$fields{updated_on}} = 'NOW()' if exists $$fields{updated_on};
|
||||||
|
} # end if
|
||||||
|
my $serial = eval '$'.$type.'::serial';
|
||||||
|
my @identified_by = eval '@'.$type.'::identified_by';
|
||||||
|
|
||||||
|
my $ac = sql::start_transaction( $local_dbh );
|
||||||
|
if ( ! $serial ) {
|
||||||
|
my $insert = $force_insert;
|
||||||
|
my %serial = eval '%'.$type.'::serial';
|
||||||
|
if ( ! %serial ) {
|
||||||
|
$log->debug("No serial") if $debug;
|
||||||
|
# No serial columns defined, which means that we will do saving by delete/insert instead of insert/update
|
||||||
|
if ( @identified_by ) {
|
||||||
|
my $where = join(' AND ', map { $$fields{$_}.'=?' } @identified_by );
|
||||||
|
if ( $debug ) {
|
||||||
|
$log->debug("DELETE FROM $table WHERE $where");
|
||||||
|
} # end if
|
||||||
|
|
||||||
|
if ( ! ( ( $_ = $local_dbh->prepare("DELETE FROM $table WHERE $where") ) and $_->execute( @$self{@identified_by} ) ) ) {
|
||||||
|
$where =~ s/\?/\%s/g;
|
||||||
|
$log->error("Error deleting: DELETE FROM $table WHERE " . sprintf($where, map { defined $_ ? $_ : 'undef' } ( @$self{@identified_by}) ).'):' . $local_dbh->errstr);
|
||||||
|
$local_dbh->rollback();
|
||||||
|
sql::end_transaction( $local_dbh, $ac );
|
||||||
|
return $local_dbh->errstr;
|
||||||
|
} elsif ( $debug ) {
|
||||||
|
$log->debug("SQL succesful DELETE FROM $table WHERE $where");
|
||||||
|
} # end if
|
||||||
|
} # end if
|
||||||
|
$insert = 1;
|
||||||
|
} else {
|
||||||
|
foreach my $id ( @identified_by ) {
|
||||||
|
if ( ! $serial{$id} ) {
|
||||||
|
my ( $caller, undef, $line ) = caller;
|
||||||
|
$log->error("$id nor in serial for $type from $caller:$line") if $debug;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if ( ! $$self{$id} ) {
|
||||||
|
($$self{$id}) = ($sql{$$fields{$id}}) = $local_dbh->selectrow_array( q{SELECT nextval('} . $serial{$id} . q{')} );
|
||||||
|
$log->debug("SQL statement execution SELECT nextval('$serial{$id}') returned $$self{$id}") if $debug or DEBUG_ALL;
|
||||||
|
$insert = 1;
|
||||||
|
} # end if
|
||||||
|
} # end foreach
|
||||||
|
} # end if ! %serial
|
||||||
|
|
||||||
|
if ( $insert ) {
|
||||||
|
my @keys = keys %sql;
|
||||||
|
my $command = "INSERT INTO $table (" . join(',', @keys ) . ') VALUES (' . join(',', map { '?' } @sql{@keys} ) . ')';
|
||||||
|
if ( ! ( ( $_ = $local_dbh->prepare($command) ) and $_->execute( @sql{@keys} ) ) ) {
|
||||||
|
my $error = $local_dbh->errstr;
|
||||||
|
$command =~ s/\?/\%s/g;
|
||||||
|
$log->error('SQL statement execution failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $local_dbh->errstr);
|
||||||
|
$local_dbh->rollback();
|
||||||
|
sql::end_transaction( $local_dbh, $ac );
|
||||||
|
return $error;
|
||||||
|
} # end if
|
||||||
|
if ( $debug or DEBUG_ALL ) {
|
||||||
|
$command =~ s/\?/\%s/g;
|
||||||
|
$log->debug('SQL statement execution: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys} ) ).'):' );
|
||||||
|
} # end if
|
||||||
|
} else {
|
||||||
|
my @keys = keys %sql;
|
||||||
|
my $command = "UPDATE $table SET " . join(',', map { $_ . ' = ?' } @keys ) . ' WHERE ' . join(' AND ', map { $_ . ' = ?' } @$fields{@identified_by} );
|
||||||
|
if ( ! ( $_ = $local_dbh->prepare($command) and $_->execute( @sql{@keys,@$fields{@identified_by}} ) ) ) {
|
||||||
|
my $error = $local_dbh->errstr;
|
||||||
|
$command =~ s/\?/\%s/g;
|
||||||
|
$log->error('SQL failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys, @$fields{@identified_by}}) ).'):' . $local_dbh->errstr);
|
||||||
|
$local_dbh->rollback();
|
||||||
|
sql::end_transaction( $local_dbh, $ac );
|
||||||
|
return $error;
|
||||||
|
} # end if
|
||||||
|
if ( $debug or DEBUG_ALL ) {
|
||||||
|
$command =~ s/\?/\%s/g;
|
||||||
|
$log->debug('SQL DEBUG: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys,@$fields{@identified_by}} ) ).'):' );
|
||||||
|
} # end if
|
||||||
|
} # end if
|
||||||
|
} else { # not identified_by
|
||||||
|
@identified_by = ('id') if ! @identified_by;
|
||||||
|
my $need_serial = ! ( @identified_by == map { $$self{$_} ? $_ : () } @identified_by );
|
||||||
|
|
||||||
|
if ( $force_insert or $need_serial ) {
|
||||||
|
|
||||||
|
if ( $need_serial ) {
|
||||||
|
if ( $serial ) {
|
||||||
|
@$self{@identified_by} = @sql{@$fields{@identified_by}} = $local_dbh->selectrow_array( q{SELECT nextval('} . $serial . q{')} );
|
||||||
|
if ( $local_dbh->errstr() ) {
|
||||||
|
$log->error("Error getting next id. " . $local_dbh->errstr() );
|
||||||
|
$log->error("SQL statement execution SELECT nextval('$serial') returned ".join(',',@$self{@identified_by}));
|
||||||
|
} elsif ( $debug or DEBUG_ALL ) {
|
||||||
|
$log->debug("SQL statement execution SELECT nextval('$serial') returned ".join(',',@$self{@identified_by}));
|
||||||
|
} # end if
|
||||||
|
} # end if
|
||||||
|
} # end if
|
||||||
|
my @keys = keys %sql;
|
||||||
|
my $command = "INSERT INTO $table (" . join(',', @keys ) . ') VALUES (' . join(',', map { '?' } @sql{@keys} ) . ')';
|
||||||
|
if ( ! ( $_ = $local_dbh->prepare($command) and $_->execute( @sql{@keys} ) ) ) {
|
||||||
|
$command =~ s/\?/\%s/g;
|
||||||
|
my $error = $local_dbh->errstr;
|
||||||
|
$log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $error);
|
||||||
|
$local_dbh->rollback();
|
||||||
|
sql::end_transaction( $local_dbh, $ac );
|
||||||
|
return $error;
|
||||||
|
} # end if
|
||||||
|
if ( $debug or DEBUG_ALL ) {
|
||||||
|
$command =~ s/\?/\%s/g;
|
||||||
|
$log->debug('SQL DEBUG: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys} ) ).'):' );
|
||||||
|
} # end if
|
||||||
|
} else {
|
||||||
|
delete $sql{created_on};
|
||||||
|
my @keys = keys %sql;
|
||||||
|
@keys = sets::exclude( [ @$fields{@identified_by} ], \@keys );
|
||||||
|
my $command = "UPDATE $table SET " . join(',', map { $_ . ' = ?' } @keys ) . ' WHERE ' . join(' AND ', map { $$fields{$_} .'= ?' } @identified_by );
|
||||||
|
if ( ! ( $_ = $local_dbh->prepare($command) and $_->execute( @sql{@keys}, @sql{@$fields{@identified_by}} ) ) ) {
|
||||||
|
my $error = $local_dbh->errstr;
|
||||||
|
$command =~ s/\?/\%s/g;
|
||||||
|
$log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}, @sql{@$fields{@identified_by}} ) ).'):' . $error) if $log;
|
||||||
|
$local_dbh->rollback();
|
||||||
|
sql::end_transaction( $local_dbh, $ac );
|
||||||
|
return $error;
|
||||||
|
} # end if
|
||||||
|
if ( $debug or DEBUG_ALL ) {
|
||||||
|
$command =~ s/\?/\%s/g;
|
||||||
|
$log->debug('SQL DEBUG: ('.sprintf($command, map { defined $_ ? ( ref $_ eq 'ARRAY' ? join(',',@{$_}) : $_ ) : 'undef' } ( @sql{@keys}, @$self{@identified_by} ) ).'):' );
|
||||||
|
} # end if
|
||||||
|
} # end if
|
||||||
|
} # end if
|
||||||
|
sql::end_transaction( $local_dbh, $ac );
|
||||||
|
$self->load();
|
||||||
|
#if ( $$fields{id} ) {
|
||||||
|
#if ( ! $ZoneMinder::Object::cache{$type}{$$self{id}} ) {
|
||||||
|
#$ZoneMinder::Object::cache{$type}{$$self{id}} = $self;
|
||||||
|
#} # end if
|
||||||
|
#delete $ZoneMinder::Object::cache{$config{db_name}}{$type}{$$self{id}};
|
||||||
|
#} # end if
|
||||||
|
#$log->debug("after delete");
|
||||||
|
#eval 'if ( %'.$type.'::find_cache ) { %'.$type.'::find_cache = (); }';
|
||||||
|
#$log->debug("after clear cache");
|
||||||
|
return '';
|
||||||
|
} # end sub save
|
||||||
|
|
||||||
|
sub set {
|
||||||
|
my ( $self, $params ) = @_;
|
||||||
|
my @set_fields = ();
|
||||||
|
|
||||||
|
my $type = ref $self;
|
||||||
|
my %fields = eval ('%'.$type.'::fields');
|
||||||
|
if ( ! %fields ) {
|
||||||
|
$log->warn('ZoneMinder::Object::set called on an object with no fields');
|
||||||
|
} # end if
|
||||||
|
my %defaults = eval('%'.$type.'::defaults');
|
||||||
|
if ( ref $params ne 'HASH' ) {
|
||||||
|
my ( $caller, undef, $line ) = caller;
|
||||||
|
$openprint::log->error("$type -> set called with non-hash params from $caller $line");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach my $field ( keys %fields ) {
|
||||||
|
$log->debug("field: $field, param: ".$$params{$field}) if $debug;
|
||||||
|
if ( exists $$params{$field} ) {
|
||||||
|
$openprint::log->debug("field: $field, $$self{$field} =? param: ".$$params{$field}) if $debug;
|
||||||
|
if ( ( ! defined $$self{$field} ) or ($$self{$field} ne $params->{$field}) ) {
|
||||||
|
# Only make changes to fields that have changed
|
||||||
|
if ( defined $fields{$field} ) {
|
||||||
|
$$self{$field} = $$params{$field} if defined $fields{$field};
|
||||||
|
push @set_fields, $fields{$field}, $$params{$field}; #mark for sql updating
|
||||||
|
} # end if
|
||||||
|
$openprint::log->debug("Running $field with $$params{$field}") if $debug;
|
||||||
|
if ( my $func = $self->can( $field ) ) {
|
||||||
|
$func->( $self, $$params{$field} );
|
||||||
|
} # end if
|
||||||
|
} # end if
|
||||||
|
} # end if
|
||||||
|
|
||||||
|
if ( defined $fields{$field} ) {
|
||||||
|
if ( $$self{$field} ) {
|
||||||
|
$$self{$field} = transform( $type, $field, $$self{$field} );
|
||||||
|
} # end if $$self{field}
|
||||||
|
}
|
||||||
|
} # end foreach field
|
||||||
|
|
||||||
|
foreach my $field ( keys %defaults ) {
|
||||||
|
|
||||||
|
if ( ( ! exists $$self{$field} ) or (!defined $$self{$field}) or ( $$self{$field} eq '' ) ) {
|
||||||
|
$log->debug("Setting default ($field) ($$self{$field}) ($defaults{$field}) ") if $debug;
|
||||||
|
if ( defined $defaults{$field} ) {
|
||||||
|
$log->debug("Default $field is defined: $defaults{$field}") if $debug;
|
||||||
|
if ( $defaults{$field} eq 'NOW()' ) {
|
||||||
|
$$self{$field} = 'NOW()';
|
||||||
|
} else {
|
||||||
|
$$self{$field} = eval($defaults{$field});
|
||||||
|
$log->error( "Eval error of object default $field default ($defaults{$field}) Reason: " . $@ ) if $@;
|
||||||
|
} # end if
|
||||||
|
} else {
|
||||||
|
$$self{$field} = $defaults{$field};
|
||||||
|
} # end if
|
||||||
|
#$$self{$field} = ( defined $defaults{$field} ) ? eval($defaults{$field}) : $defaults{$field};
|
||||||
|
$log->debug("Setting default for ($field) using ($defaults{$field}) to ($$self{$field}) ") if $debug;
|
||||||
|
} # end if
|
||||||
|
} # end foreach default
|
||||||
|
return @set_fields;
|
||||||
|
} # end sub set
|
||||||
|
|
||||||
|
sub transform {
|
||||||
|
my $type = ref $_[0];
|
||||||
|
$type = $_[0] if ! $type;
|
||||||
|
my $fields = eval '\%'.$type.'::fields';
|
||||||
|
my $value = $_[2];
|
||||||
|
|
||||||
|
if ( defined $$fields{$_[1]} ) {
|
||||||
|
my @transforms = eval('@{$'.$type.'::transforms{$_[1]}}');
|
||||||
|
$openprint::log->debug("Transforms for $_[1] before $_[2]: @transforms") if $debug;
|
||||||
|
if ( @transforms ) {
|
||||||
|
foreach my $transform ( @transforms ) {
|
||||||
|
if ( $transform =~ /^s\// or $transform =~ /^tr\// ) {
|
||||||
|
eval '$value =~ ' . $transform;
|
||||||
|
} elsif ( $transform =~ /^<(\d+)/ ) {
|
||||||
|
if ( $value > $1 ) {
|
||||||
|
$value = undef;
|
||||||
|
} # end if
|
||||||
|
} else {
|
||||||
|
$openprint::log->debug("evalling $value ".$transform . " Now value is $value" );
|
||||||
|
eval '$value '.$transform;
|
||||||
|
$openprint::log->error("Eval error $@") if $@;
|
||||||
|
}
|
||||||
|
$openprint::log->debug("After $transform: $value") if $debug;
|
||||||
|
} # end foreach
|
||||||
|
} # end if
|
||||||
|
} else {
|
||||||
|
$openprint::log->error("Object::transform ($_[1]) not in fields for $type");
|
||||||
|
} # end if
|
||||||
|
return $value;
|
||||||
|
|
||||||
|
} # end sub transform
|
||||||
1;
|
1;
|
||||||
__END__
|
__END__
|
||||||
|
|
||||||
|
|
|
@ -214,6 +214,7 @@ sub getFilters {
|
||||||
or AutoMessage = 1
|
or AutoMessage = 1
|
||||||
or AutoExecute = 1
|
or AutoExecute = 1
|
||||||
or AutoDelete = 1
|
or AutoDelete = 1
|
||||||
|
or UpdateDiskSpace = 1
|
||||||
) ORDER BY Name';
|
) ORDER BY Name';
|
||||||
my $sth = $dbh->prepare_cached( $sql )
|
my $sth = $dbh->prepare_cached( $sql )
|
||||||
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
|
or Fatal( "Unable toprepare '$sql': ".$dbh->errstr() );
|
||||||
|
|
|
@ -168,7 +168,6 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
|
||||||
snprintf( video_name, sizeof(video_name), "%d-%s", id, "video.mp4" );
|
snprintf( video_name, sizeof(video_name), "%d-%s", id, "video.mp4" );
|
||||||
snprintf( video_file, sizeof(video_file), staticConfig.video_file_format, path, video_name );
|
snprintf( video_file, sizeof(video_file), staticConfig.video_file_format, path, video_name );
|
||||||
Debug(1,"Writing video file to %s", video_file );
|
Debug(1,"Writing video file to %s", video_file );
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/* X264 MP4 video writer */
|
/* X264 MP4 video writer */
|
||||||
if ( monitor->GetOptVideoWriter() == Monitor::X264ENCODE ) {
|
if ( monitor->GetOptVideoWriter() == Monitor::X264ENCODE ) {
|
||||||
|
@ -200,8 +199,8 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
|
||||||
} else {
|
} else {
|
||||||
/* No video object */
|
/* No video object */
|
||||||
videowriter = NULL;
|
videowriter = NULL;
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent )
|
} // Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent )
|
||||||
|
|
||||||
|
@ -239,7 +238,7 @@ Event::~Event() {
|
||||||
Error( "Can't update event: %s", mysql_error( &dbconn ) );
|
Error( "Can't update event: %s", mysql_error( &dbconn ) );
|
||||||
exit( mysql_errno( &dbconn ) );
|
exit( mysql_errno( &dbconn ) );
|
||||||
}
|
}
|
||||||
}
|
} // ~Event
|
||||||
|
|
||||||
void Event::createNotes( std::string ¬es ) {
|
void Event::createNotes( std::string ¬es ) {
|
||||||
notes.clear();
|
notes.clear();
|
||||||
|
|
|
@ -113,22 +113,6 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
|
||||||
mOpenStart = 0;
|
mOpenStart = 0;
|
||||||
mReopenThread = 0;
|
mReopenThread = 0;
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
|
||||||
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 ) {
|
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
|
||||||
imagePixFormat = AV_PIX_FMT_RGBA;
|
|
||||||
} else if ( colours == ZM_COLOUR_RGB24 ) {
|
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
|
||||||
imagePixFormat = AV_PIX_FMT_RGB24;
|
|
||||||
} else if ( colours == ZM_COLOUR_GRAY8 ) {
|
|
||||||
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
|
||||||
imagePixFormat = AV_PIX_FMT_GRAY8;
|
|
||||||
} else {
|
|
||||||
Panic("Unexpected colours: %d",colours);
|
|
||||||
}
|
|
||||||
} // end FFmpegCamera::FFmpegCamera
|
} // end FFmpegCamera::FFmpegCamera
|
||||||
|
|
||||||
FfmpegCamera::~FfmpegCamera() {
|
FfmpegCamera::~FfmpegCamera() {
|
||||||
|
@ -218,94 +202,6 @@ ZMPacket * FfmpegCamera::Capture( Image &image ) {
|
||||||
}
|
}
|
||||||
Debug( 5, "Got packet from stream %d dts (%d) pts(%d)", packet.stream_index, packet.pts, packet.dts );
|
Debug( 5, "Got packet from stream %d dts (%d) pts(%d)", packet.stream_index, packet.pts, packet.dts );
|
||||||
|
|
||||||
#if 0
|
|
||||||
// What about audio stream? Maybe someday we could do sound detection...
|
|
||||||
if ( ( packet.stream_index == mVideoStreamId ) && ( keyframe || have_video_keyframe ) ) {
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
|
||||||
ret = avcodec_send_packet( mVideoCodecContext, &packet );
|
|
||||||
if ( ret < 0 ) {
|
|
||||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
|
||||||
Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
|
|
||||||
zm_av_packet_unref( &packet );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HAVE_AVUTIL_HWCONTEXT_H
|
|
||||||
if ( hwaccel ) {
|
|
||||||
ret = avcodec_receive_frame( mVideoCodecContext, hwFrame );
|
|
||||||
if ( ret < 0 ) {
|
|
||||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
|
||||||
Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
|
|
||||||
zm_av_packet_unref( &packet );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ret = av_hwframe_transfer_data(mRawFrame, hwFrame, 0);
|
|
||||||
if (ret < 0) {
|
|
||||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
|
||||||
Error( "Unable to transfer frame at frame %d: %s, continuing", frameCount, errbuf );
|
|
||||||
zm_av_packet_unref( &packet );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
#endif
|
|
||||||
ret = avcodec_receive_frame( mVideoCodecContext, mRawFrame );
|
|
||||||
if ( ret < 0 ) {
|
|
||||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
|
||||||
Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
|
|
||||||
zm_av_packet_unref( &packet );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HAVE_AVUTIL_HWCONTEXT_H
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
frameComplete = 1;
|
|
||||||
# else
|
|
||||||
ret = zm_avcodec_decode_video( mVideoCodecContext, mRawFrame, &frameComplete, &packet );
|
|
||||||
if ( ret < 0 ) {
|
|
||||||
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
|
||||||
Error( "Unable to decode frame at frame %d: %s, continuing", frameCount, errbuf );
|
|
||||||
zm_av_packet_unref( &packet );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Debug( 4, "Decoded video packet at frame %d", frameCount );
|
|
||||||
|
|
||||||
if ( frameComplete ) {
|
|
||||||
Debug( 4, "Got frame %d", frameCount );
|
|
||||||
|
|
||||||
uint8_t* directbuffer;
|
|
||||||
|
|
||||||
/* Request a writeable buffer of the target image */
|
|
||||||
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
|
||||||
if ( directbuffer == NULL ) {
|
|
||||||
Error("Failed requesting writeable buffer for the captured image.");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
|
||||||
av_image_fill_arrays(mFrame->data, mFrame->linesize,
|
|
||||||
directbuffer, imagePixFormat, width, height, 1);
|
|
||||||
#else
|
|
||||||
avpicture_fill( (AVPicture *)mFrame, directbuffer,
|
|
||||||
imagePixFormat, width, height);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
|
||||||
if ( sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0 )
|
|
||||||
Fatal("Unable to convert raw format %u to target format %u at frame %d", mVideoCodecContext->pix_fmt, imagePixFormat, frameCount);
|
|
||||||
#else // HAVE_LIBSWSCALE
|
|
||||||
Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras");
|
|
||||||
#endif // HAVE_LIBSWSCALE
|
|
||||||
|
|
||||||
frameCount++;
|
|
||||||
} // end if frameComplete
|
|
||||||
} else {
|
|
||||||
Debug( 4, "Different stream_index %d", packet.stream_index );
|
|
||||||
} // end if packet.stream_index == mVideoStreamId
|
|
||||||
#endif
|
|
||||||
zm_packet = new ZMPacket( &packet );
|
zm_packet = new ZMPacket( &packet );
|
||||||
zm_av_packet_unref( &packet );
|
zm_av_packet_unref( &packet );
|
||||||
return zm_packet;
|
return zm_packet;
|
||||||
|
@ -517,6 +413,18 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( mVideoCodecContext->codec_id != AV_CODEC_ID_H264 ) {
|
||||||
|
#ifdef AV_CODEC_ID_H265
|
||||||
|
if ( mVideoCodecContext->codec_id == AV_CODEC_ID_H265 ) {
|
||||||
|
Debug( 1, "Input stream appears to be h265. The stored event file may not be viewable in browser." );
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
|
Warning( "Input stream is not h264. The stored event file may not be viewable in browser." );
|
||||||
|
#ifdef AV_CODEC_ID_H265
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
if (mVideoCodecContext->hwaccel != NULL) {
|
if (mVideoCodecContext->hwaccel != NULL) {
|
||||||
Debug(1, "HWACCEL in use");
|
Debug(1, "HWACCEL in use");
|
||||||
} else {
|
} else {
|
||||||
|
@ -551,51 +459,11 @@ int FfmpegCamera::OpenFfmpeg() {
|
||||||
// Allocate space for the native video frame
|
// Allocate space for the native video frame
|
||||||
mRawFrame = zm_av_frame_alloc();
|
mRawFrame = zm_av_frame_alloc();
|
||||||
|
|
||||||
// Allocate space for the converted video frame
|
if ( mRawFrame == NULL )
|
||||||
mFrame = zm_av_frame_alloc();
|
|
||||||
|
|
||||||
if ( mRawFrame == NULL || mFrame == NULL )
|
|
||||||
Fatal( "Unable to allocate frame for %s", mPath.c_str() );
|
Fatal( "Unable to allocate frame for %s", mPath.c_str() );
|
||||||
|
|
||||||
Debug ( 1, "Allocated frames" );
|
Debug ( 1, "Allocated frames" );
|
||||||
|
|
||||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
|
||||||
int pSize = av_image_get_buffer_size( imagePixFormat, width, height,1 );
|
|
||||||
#else
|
|
||||||
int pSize = avpicture_get_size( imagePixFormat, width, height );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ( (unsigned int)pSize != imagesize ) {
|
|
||||||
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug ( 1, "Validated imagesize" );
|
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
|
||||||
Debug ( 1, "Calling sws_isSupportedInput" );
|
|
||||||
if ( !sws_isSupportedInput(mVideoCodecContext->pix_fmt) ) {
|
|
||||||
Fatal("swscale does not support the codec format: %c%c%c%c", (mVideoCodecContext->pix_fmt)&0xff, ((mVideoCodecContext->pix_fmt >> 8)&0xff), ((mVideoCodecContext->pix_fmt >> 16)&0xff), ((mVideoCodecContext->pix_fmt >> 24)&0xff));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !sws_isSupportedOutput(imagePixFormat) ) {
|
|
||||||
Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff));
|
|
||||||
}
|
|
||||||
|
|
||||||
mConvertContext = sws_getContext(mVideoCodecContext->width,
|
|
||||||
mVideoCodecContext->height,
|
|
||||||
mVideoCodecContext->pix_fmt,
|
|
||||||
width, height,
|
|
||||||
imagePixFormat, SWS_BICUBIC, NULL,
|
|
||||||
NULL, NULL);
|
|
||||||
if ( mConvertContext == NULL )
|
|
||||||
Fatal( "Unable to create conversion context for %s", mPath.c_str() );
|
|
||||||
#else // HAVE_LIBSWSCALE
|
|
||||||
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
|
|
||||||
#endif // HAVE_LIBSWSCALE
|
|
||||||
|
|
||||||
if ( (unsigned int)mVideoCodecContext->width != width || (unsigned int)mVideoCodecContext->height != height ) {
|
|
||||||
Warning( "Monitor dimensions are %dx%d but camera is sending %dx%d", width, height, mVideoCodecContext->width, mVideoCodecContext->height );
|
|
||||||
}
|
|
||||||
|
|
||||||
mCanCapture = true;
|
mCanCapture = true;
|
||||||
|
|
||||||
|
@ -636,12 +504,6 @@ int FfmpegCamera::CloseFfmpeg() {
|
||||||
mRawFrame = NULL;
|
mRawFrame = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
|
||||||
if ( mConvertContext ) {
|
|
||||||
sws_freeContext( mConvertContext );
|
|
||||||
mConvertContext = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ( mVideoCodecContext ) {
|
if ( mVideoCodecContext ) {
|
||||||
avcodec_close(mVideoCodecContext);
|
avcodec_close(mVideoCodecContext);
|
||||||
|
@ -664,7 +526,7 @@ int FfmpegCamera::CloseFfmpeg() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
} // end int FfmpegCamera::CloseFfmpeg()
|
||||||
|
|
||||||
int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
|
int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
|
||||||
Debug(3,"FfmpegInteruptCallback");
|
Debug(3,"FfmpegInteruptCallback");
|
||||||
|
@ -678,9 +540,9 @@ int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
} // end int FfmpegCamera::FfmpegInterruptCallback(void *ctx)
|
||||||
|
|
||||||
void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){
|
void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx) {
|
||||||
Debug(3,"FfmpegReopenThreadtCallback");
|
Debug(3,"FfmpegReopenThreadtCallback");
|
||||||
if ( ctx == NULL ) return NULL;
|
if ( ctx == NULL ) return NULL;
|
||||||
|
|
||||||
|
@ -702,6 +564,47 @@ void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // end void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Responsible for populating the decoded frame member of the zm_packet.
|
||||||
|
*/
|
||||||
|
Image * FfmpegCamera::decode( Image *i = NULL, _AVPIXELFORMAT imagePixFormat, struct SwsContext *mConvertContext ) {
|
||||||
|
|
||||||
|
if ( ! frame ) {
|
||||||
|
Error("Can't get image without frame.. maybe need to decode first");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! image ) {
|
||||||
|
if ( ! i ) {
|
||||||
|
Error("Need a pre-allocated image buffer");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
image = i;
|
||||||
|
}
|
||||||
|
/* Request a writeable buffer of the target image */
|
||||||
|
uint8_t* directbuffer = image->WriteBuffer();
|
||||||
|
//uint8_t* directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
||||||
|
if ( directbuffer == NULL ) {
|
||||||
|
Error("Failed requesting writeable buffer for the captured image.");
|
||||||
|
image = NULL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mFrame is an intermediate.
|
||||||
|
AVFrame *mFrame = zm_av_frame_alloc();
|
||||||
|
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
||||||
|
av_image_fill_arrays(mFrame->data, mFrame->linesize, directbuffer, imagePixFormat, frame->width, frame->height, 1);
|
||||||
|
#else
|
||||||
|
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, frame->width, frame->height);
|
||||||
|
#endif
|
||||||
|
if (sws_scale(mConvertContext, frame->data, frame->linesize,
|
||||||
|
0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0) {
|
||||||
|
Fatal("Unable to convert raw format %u to target format %u",
|
||||||
|
mVideoCodecContext->pix_fmt, imagePixFormat);
|
||||||
|
}
|
||||||
|
av_frame_free( &mFrame );
|
||||||
|
return image;
|
||||||
|
}
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
|
@ -40,6 +40,7 @@ class FfmpegCamera : public Camera {
|
||||||
std::string mPath;
|
std::string mPath;
|
||||||
std::string mMethod;
|
std::string mMethod;
|
||||||
std::string mOptions;
|
std::string mOptions;
|
||||||
|
std::string encoder_options;
|
||||||
|
|
||||||
int frameCount;
|
int frameCount;
|
||||||
|
|
||||||
|
@ -51,7 +52,6 @@ class FfmpegCamera : public Camera {
|
||||||
AVCodec *mAudioCodec;
|
AVCodec *mAudioCodec;
|
||||||
AVFrame *mRawFrame;
|
AVFrame *mRawFrame;
|
||||||
AVFrame *mFrame;
|
AVFrame *mFrame;
|
||||||
_AVPIXELFORMAT imagePixFormat;
|
|
||||||
|
|
||||||
bool hwaccel;
|
bool hwaccel;
|
||||||
#if HAVE_AVUTIL_HWCONTEXT_H
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
|
@ -82,9 +82,6 @@ class FfmpegCamera : public Camera {
|
||||||
#endif // HAVE_LIBAVFORMAT
|
#endif // HAVE_LIBAVFORMAT
|
||||||
|
|
||||||
|
|
||||||
#if HAVE_LIBSWSCALE
|
|
||||||
struct SwsContext *mConvertContext;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int64_t startTime;
|
int64_t startTime;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
|
||||||
|
#include "zm_ffmpeg_input.h"
|
||||||
|
#include "zm_logger.h"
|
||||||
|
#include "zm_ffmpeg.h"
|
||||||
|
|
||||||
|
FFmpeg_Output::FFmpeg_Output() {
|
||||||
|
input_format_context = NULL;
|
||||||
|
video_stream_id = -1;
|
||||||
|
audio_stream_id = -1;
|
||||||
|
av_register_all();
|
||||||
|
avcodec_register_all();
|
||||||
|
|
||||||
|
}
|
||||||
|
FFmpeg_Output::~FFmpeg_Output() {
|
||||||
|
}
|
||||||
|
|
||||||
|
int FFmpeg_Output::Open( const char *filepath ) {
|
||||||
|
|
||||||
|
int error;
|
||||||
|
|
||||||
|
/** Open the input file to read from it. */
|
||||||
|
if ( (error = avformat_open_input( &input_format_context, filepath, NULL, NULL)) < 0 ) {
|
||||||
|
|
||||||
|
Error("Could not open input file '%s' (error '%s')\n",
|
||||||
|
filepath, av_make_error_string(error).c_str() );
|
||||||
|
input_format_context = NULL;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get information on the input file (number of streams etc.). */
|
||||||
|
if ( (error = avformat_find_stream_info(input_format_context, NULL)) < 0 ) {
|
||||||
|
Error( "Could not open find stream info (error '%s')\n",
|
||||||
|
av_make_error_string(error).c_str() );
|
||||||
|
avformat_close_input(&input_format_context);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) {
|
||||||
|
if ( is_video_stream( input_format_context->streams[i] ) ) {
|
||||||
|
zm_dump_stream_format(input_format_context, i, 0, 0);
|
||||||
|
if ( video_stream_id == -1 ) {
|
||||||
|
video_stream_id = i;
|
||||||
|
// if we break, then we won't find the audio stream
|
||||||
|
} else {
|
||||||
|
Warning( "Have another video stream." );
|
||||||
|
}
|
||||||
|
} else if ( is_audio_stream( input_format_context->streams[i] ) ) {
|
||||||
|
if ( audio_stream_id == -1 ) {
|
||||||
|
audio_stream_id = i;
|
||||||
|
} else {
|
||||||
|
Warning( "Have another audio stream." );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
streams[i].frame_count = 0;
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
|
streams[i].context = avcodec_alloc_context3( NULL );
|
||||||
|
avcodec_parameters_to_context( streams[i].context, input_format_context->streams[i]->codecpar );
|
||||||
|
#else
|
||||||
|
streams[i].context = input_format_context->streams[i]->codec;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ( !(streams[i].codec = avcodec_find_decoder(streams[i].context->codec_id)) ) {
|
||||||
|
Error( "Could not find input codec\n");
|
||||||
|
avformat_close_input(&input_format_context);
|
||||||
|
return AVERROR_EXIT;
|
||||||
|
} else {
|
||||||
|
Debug(1, "Using codec (%s) for stream %d", streams[i].codec->name, i );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((error = avcodec_open2( streams[i].context, streams[i].codec, NULL)) < 0) {
|
||||||
|
Error( "Could not open input codec (error '%s')\n",
|
||||||
|
av_make_error_string(error).c_str() );
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
|
avcodec_free_context( &streams[i].context );
|
||||||
|
#endif
|
||||||
|
avformat_close_input(&input_format_context);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
} // end foreach stream
|
||||||
|
|
||||||
|
if ( video_stream_id == -1 )
|
||||||
|
Error( "Unable to locate video stream in %s", filepath );
|
||||||
|
if ( audio_stream_id == -1 )
|
||||||
|
Debug( 3, "Unable to locate audio stream in %s", filepath );
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} // end int FFmpeg_Output::Open( const char * filepath )
|
||||||
|
|
||||||
|
AVFrame *FFmpeg_Output::get_frame( int stream_id ) {
|
||||||
|
Debug(1, "Getting frame from stream %d", stream_id );
|
||||||
|
|
||||||
|
int frameComplete = false;
|
||||||
|
AVPacket packet;
|
||||||
|
av_init_packet( &packet );
|
||||||
|
AVFrame *frame = zm_av_frame_alloc();
|
||||||
|
char errbuf[AV_ERROR_MAX_STRING_SIZE];
|
||||||
|
|
||||||
|
while ( !frameComplete ) {
|
||||||
|
int ret = av_read_frame( input_format_context, &packet );
|
||||||
|
if ( ret < 0 ) {
|
||||||
|
av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
|
||||||
|
if (
|
||||||
|
// Check if EOF.
|
||||||
|
(ret == AVERROR_EOF || (input_format_context->pb && input_format_context->pb->eof_reached)) ||
|
||||||
|
// Check for Connection failure.
|
||||||
|
(ret == -110)
|
||||||
|
) {
|
||||||
|
Info( "av_read_frame returned %s.", errbuf );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( (stream_id < 0 ) || ( packet.stream_index == stream_id ) ) {
|
||||||
|
Debug(1,"Packet is for our stream (%d)", packet.stream_index );
|
||||||
|
|
||||||
|
AVCodecContext *context = streams[packet.stream_index].context;
|
||||||
|
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
|
ret = avcodec_send_packet( context, &packet );
|
||||||
|
if ( ret < 0 ) {
|
||||||
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
|
Error( "Unable to send packet at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
Debug(1, "Success getting a packet");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
|
if ( hwaccel ) {
|
||||||
|
ret = avcodec_receive_frame( context, hwFrame );
|
||||||
|
if ( ret < 0 ) {
|
||||||
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
|
Error( "Unable to receive frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ret = av_hwframe_transfer_data(frame, hwFrame, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
|
Error( "Unable to transfer frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
|
Debug(1,"Getting a frame?");
|
||||||
|
ret = avcodec_receive_frame( context, frame );
|
||||||
|
if ( ret < 0 ) {
|
||||||
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
|
Error( "Unable to send packet at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HAVE_AVUTIL_HWCONTEXT_H
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
frameComplete = 1;
|
||||||
|
# else
|
||||||
|
ret = zm_avcodec_decode_video( streams[packet.stream_index].context, frame, &frameComplete, &packet );
|
||||||
|
if ( ret < 0 ) {
|
||||||
|
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
|
||||||
|
Error( "Unable to decode frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf );
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // end if it's the right stream
|
||||||
|
|
||||||
|
zm_av_packet_unref( &packet );
|
||||||
|
|
||||||
|
} // end while ! frameComplete
|
||||||
|
return frame;
|
||||||
|
|
||||||
|
} // end AVFrame *FFmpeg_Output::get_frame
|
|
@ -0,0 +1,46 @@
|
||||||
|
#ifndef ZM_FFMPEG_INPUT_H
|
||||||
|
#define ZM_FFMPEG_INPUT_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "libavformat/avformat.h"
|
||||||
|
#include "libavformat/avio.h"
|
||||||
|
#include "libavcodec/avcodec.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class FFmpeg_Output {
|
||||||
|
|
||||||
|
public:
|
||||||
|
FFmpeg_Output();
|
||||||
|
~FFmpeg_Output();
|
||||||
|
|
||||||
|
int Open( const char *filename );
|
||||||
|
int Close();
|
||||||
|
AVFrame *put_frame( int stream_id=-1 );
|
||||||
|
AVFrame *put_packet( int stream_id=-1 );
|
||||||
|
int get_video_stream_id() {
|
||||||
|
return video_stream_id;
|
||||||
|
}
|
||||||
|
int get_audio_stream_id() {
|
||||||
|
return audio_stream_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef struct {
|
||||||
|
AVCodecContext *context;
|
||||||
|
AVCodec *codec;
|
||||||
|
int frame_count;
|
||||||
|
} stream;
|
||||||
|
|
||||||
|
stream streams[2];
|
||||||
|
int video_stream_id;
|
||||||
|
int audio_stream_id;
|
||||||
|
AVFormatContext *input_format_context;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -241,6 +241,7 @@ Monitor::Monitor(
|
||||||
int p_orientation,
|
int p_orientation,
|
||||||
unsigned int p_deinterlacing,
|
unsigned int p_deinterlacing,
|
||||||
int p_savejpegs,
|
int p_savejpegs,
|
||||||
|
int p_colours,
|
||||||
VideoWriter p_videowriter,
|
VideoWriter p_videowriter,
|
||||||
std::string p_encoderparams,
|
std::string p_encoderparams,
|
||||||
bool p_record_audio,
|
bool p_record_audio,
|
||||||
|
@ -280,6 +281,7 @@ Monitor::Monitor(
|
||||||
orientation( (Orientation)p_orientation ),
|
orientation( (Orientation)p_orientation ),
|
||||||
deinterlacing( p_deinterlacing ),
|
deinterlacing( p_deinterlacing ),
|
||||||
savejpegspref( p_savejpegs ),
|
savejpegspref( p_savejpegs ),
|
||||||
|
colours( p_colours ),
|
||||||
videowriter( p_videowriter ),
|
videowriter( p_videowriter ),
|
||||||
encoderparams( p_encoderparams ),
|
encoderparams( p_encoderparams ),
|
||||||
record_audio( p_record_audio ),
|
record_audio( p_record_audio ),
|
||||||
|
@ -336,6 +338,49 @@ Monitor::Monitor(
|
||||||
|
|
||||||
/* Parse encoder parameters */
|
/* Parse encoder parameters */
|
||||||
ParseEncoderParameters(encoderparams.c_str(), &encoderparamsvec);
|
ParseEncoderParameters(encoderparams.c_str(), &encoderparamsvec);
|
||||||
|
#if HAVE_LIBSWSCALE
|
||||||
|
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 ) {
|
||||||
|
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||||
|
imagePixFormat = AV_PIX_FMT_RGBA;
|
||||||
|
} else if ( colours == ZM_COLOUR_RGB24 ) {
|
||||||
|
subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||||
|
imagePixFormat = AV_PIX_FMT_RGB24;
|
||||||
|
} else if ( colours == ZM_COLOUR_GRAY8 ) {
|
||||||
|
subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||||
|
imagePixFormat = AV_PIX_FMT_GRAY8;
|
||||||
|
} else {
|
||||||
|
Panic("Unexpected colours: %d",colours);
|
||||||
|
}
|
||||||
|
#if HAVE_LIBSWSCALE
|
||||||
|
//FIXME, need to be able to query the camera input for what it is going to be getting, which needs to be called after the camera is open.
|
||||||
|
//Debug ( 1, "Calling sws_isSupportedInput" );
|
||||||
|
//if ( !sws_isSupportedInput(mVideoCodecContext->pix_fmt) ) {
|
||||||
|
//Fatal("swscale does not support the codec format: %c%c%c%c", (mVideoCodecContext->pix_fmt)&0xff, ((mVideoCodecContext->pix_fmt >> 8)&0xff), ((mVideoCodecContext->pix_fmt >> 16)&0xff), ((mVideoCodecContext->pix_fmt >> 24)&0xff));
|
||||||
|
//}
|
||||||
|
|
||||||
|
if ( !sws_isSupportedOutput(imagePixFormat) ) {
|
||||||
|
Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff));
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't know this yet, need to open the camera first.
|
||||||
|
//mConvertContext = sws_getContext(mVideoCodecContext->width,
|
||||||
|
//mVideoCodecContext->height,
|
||||||
|
//mVideoCodecContext->pix_fmt,
|
||||||
|
//width, height,
|
||||||
|
//imagePixFormat, SWS_BICUBIC, NULL,
|
||||||
|
//NULL, NULL);
|
||||||
|
//if ( mConvertContext == NULL )
|
||||||
|
//Fatal( "Unable to create conversion context for %s", mPath.c_str() );
|
||||||
|
#else // HAVE_LIBSWSCALE
|
||||||
|
//Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
|
||||||
|
#endif // HAVE_LIBSWSCALE
|
||||||
|
|
||||||
|
//if ( (unsigned int)mVideoCodecContext->width != width || (unsigned int)mVideoCodecContext->height != height ) {
|
||||||
|
//Warning( "Monitor dimensions are %dx%d but camera is sending %dx%d", width, height, mVideoCodecContext->width, mVideoCodecContext->height );
|
||||||
|
//}
|
||||||
|
|
||||||
fps = 0.0;
|
fps = 0.0;
|
||||||
event_count = 0;
|
event_count = 0;
|
||||||
|
@ -568,6 +613,12 @@ Monitor::~Monitor() {
|
||||||
delete videoStore;
|
delete videoStore;
|
||||||
videoStore = NULL;
|
videoStore = NULL;
|
||||||
}
|
}
|
||||||
|
#if HAVE_LIBSWSCALE
|
||||||
|
if ( mConvertContext ) {
|
||||||
|
sws_freeContext( mConvertContext );
|
||||||
|
mConvertContext = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if ( timestamps ) {
|
if ( timestamps ) {
|
||||||
delete[] timestamps;
|
delete[] timestamps;
|
||||||
timestamps = 0;
|
timestamps = 0;
|
||||||
|
@ -1126,7 +1177,7 @@ bool Monitor::CheckSignal( const Image *image ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if(colours == ZM_COLOUR_RGB32) {
|
} else if ( colours == ZM_COLOUR_RGB32 ) {
|
||||||
if ( usedsubpixorder == ZM_SUBPIX_ORDER_ARGB || usedsubpixorder == ZM_SUBPIX_ORDER_ABGR) {
|
if ( usedsubpixorder == ZM_SUBPIX_ORDER_ARGB || usedsubpixorder == ZM_SUBPIX_ORDER_ABGR) {
|
||||||
if ( ARGB_ABGR_ZEROALPHA(*(((const Rgb*)buffer)+index)) != ARGB_ABGR_ZEROALPHA(colour_val) )
|
if ( ARGB_ABGR_ZEROALPHA(*(((const Rgb*)buffer)+index)) != ARGB_ABGR_ZEROALPHA(colour_val) )
|
||||||
return true;
|
return true;
|
||||||
|
@ -1359,12 +1410,12 @@ Debug(3,"before DetectMotion");
|
||||||
if ( event ) {
|
if ( event ) {
|
||||||
//TODO: We shouldn't have to do this every time. Not sure why it clears itself if this isn't here??
|
//TODO: We shouldn't have to do this every time. Not sure why it clears itself if this isn't here??
|
||||||
//snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
|
//snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
|
||||||
Debug( 3, "Detected new event at (%d.%d)", timestamp->tv_sec,timestamp->tv_usec );
|
//Debug( 3, "Detected new event at (%d.%d)", timestamp->tv_sec,timestamp->tv_usec );
|
||||||
|
|
||||||
if ( section_length ) {
|
if ( section_length ) {
|
||||||
// TODO: Wouldn't this be clearer if we just did something like if now - event->start > section_length ?
|
// TODO: Wouldn't this be clearer if we just did something like if now - event->start > section_length ?
|
||||||
int section_mod = timestamp->tv_sec % section_length;
|
int section_mod = timestamp->tv_sec % section_length;
|
||||||
Debug( 3, "Section length (%d) Last Section Mod(%d), new section mod(%d)", section_length, last_section_mod, section_mod );
|
Debug( 4, "Section length (%d) Last Section Mod(%d), new section mod(%d)", section_length, last_section_mod, section_mod );
|
||||||
if ( section_mod < last_section_mod ) {
|
if ( section_mod < last_section_mod ) {
|
||||||
//if ( state == IDLE || state == TAPE || event_close_mode == CLOSE_TIME ) {
|
//if ( state == IDLE || state == TAPE || event_close_mode == CLOSE_TIME ) {
|
||||||
//if ( state == TAPE ) {
|
//if ( state == TAPE ) {
|
||||||
|
@ -1976,6 +2027,7 @@ int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose
|
||||||
orientation,
|
orientation,
|
||||||
deinterlacing,
|
deinterlacing,
|
||||||
savejpegs,
|
savejpegs,
|
||||||
|
colours,
|
||||||
videowriter,
|
videowriter,
|
||||||
encoderparams,
|
encoderparams,
|
||||||
record_audio,
|
record_audio,
|
||||||
|
@ -2160,6 +2212,7 @@ int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const c
|
||||||
orientation,
|
orientation,
|
||||||
deinterlacing,
|
deinterlacing,
|
||||||
savejpegs,
|
savejpegs,
|
||||||
|
colours,
|
||||||
videowriter,
|
videowriter,
|
||||||
encoderparams,
|
encoderparams,
|
||||||
record_audio,
|
record_audio,
|
||||||
|
@ -2309,6 +2362,7 @@ int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose pu
|
||||||
orientation,
|
orientation,
|
||||||
deinterlacing,
|
deinterlacing,
|
||||||
savejpegs,
|
savejpegs,
|
||||||
|
colours,
|
||||||
videowriter,
|
videowriter,
|
||||||
encoderparams,
|
encoderparams,
|
||||||
record_audio,
|
record_audio,
|
||||||
|
@ -2468,6 +2522,7 @@ int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose
|
||||||
orientation,
|
orientation,
|
||||||
deinterlacing,
|
deinterlacing,
|
||||||
savejpegs,
|
savejpegs,
|
||||||
|
colours,
|
||||||
videowriter,
|
videowriter,
|
||||||
encoderparams,
|
encoderparams,
|
||||||
record_audio,
|
record_audio,
|
||||||
|
@ -2795,6 +2850,7 @@ Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) {
|
||||||
orientation,
|
orientation,
|
||||||
deinterlacing,
|
deinterlacing,
|
||||||
savejpegs,
|
savejpegs,
|
||||||
|
colours,
|
||||||
videowriter,
|
videowriter,
|
||||||
encoderparams,
|
encoderparams,
|
||||||
record_audio,
|
record_audio,
|
||||||
|
|
|
@ -240,9 +240,15 @@ protected:
|
||||||
bool videoRecording;
|
bool videoRecording;
|
||||||
|
|
||||||
int savejpegspref;
|
int savejpegspref;
|
||||||
|
int colours;
|
||||||
VideoWriter videowriter;
|
VideoWriter videowriter;
|
||||||
std::string encoderparams;
|
std::string encoderparams;
|
||||||
std::vector<EncoderParameter_t> encoderparamsvec;
|
std::vector<EncoderParameter_t> encoderparamsvec;
|
||||||
|
_AVPIXELFORMAT imagePixFormat;
|
||||||
|
unsigned int subpixelorder;
|
||||||
|
#if HAVE_LIBSWSCALE
|
||||||
|
struct SwsContext *mConvertContext;
|
||||||
|
#endif
|
||||||
bool record_audio; // Whether to store the audio that we receive
|
bool record_audio; // Whether to store the audio that we receive
|
||||||
|
|
||||||
int brightness; // The statically saved brightness of the camera
|
int brightness; // The statically saved brightness of the camera
|
||||||
|
@ -348,6 +354,7 @@ public:
|
||||||
int p_orientation,
|
int p_orientation,
|
||||||
unsigned int p_deinterlacing,
|
unsigned int p_deinterlacing,
|
||||||
int p_savejpegs,
|
int p_savejpegs,
|
||||||
|
int p_colours,
|
||||||
VideoWriter p_videowriter,
|
VideoWriter p_videowriter,
|
||||||
std::string p_encoderparams,
|
std::string p_encoderparams,
|
||||||
bool p_record_audio,
|
bool p_record_audio,
|
||||||
|
@ -434,6 +441,7 @@ public:
|
||||||
int GetOptSaveJPEGs() const { return( savejpegspref ); }
|
int GetOptSaveJPEGs() const { return( savejpegspref ); }
|
||||||
VideoWriter GetOptVideoWriter() const { return( videowriter ); }
|
VideoWriter GetOptVideoWriter() const { return( videowriter ); }
|
||||||
const std::vector<EncoderParameter_t>* GetOptEncoderParams() const { return( &encoderparamsvec ); }
|
const std::vector<EncoderParameter_t>* GetOptEncoderParams() const { return( &encoderparamsvec ); }
|
||||||
|
const std::string &GetEncoderOptions() const { return( encoderparams ); }
|
||||||
uint32_t GetLastEventId() const { return shared_data->last_event_id; }
|
uint32_t GetLastEventId() const { return shared_data->last_event_id; }
|
||||||
uint32_t GetVideoWriterEventId() const { return video_store_data->current_event; }
|
uint32_t GetVideoWriterEventId() const { return video_store_data->current_event; }
|
||||||
void SetVideoWriterEventId( uint32_t p_event_id ) { video_store_data->current_event = p_event_id; }
|
void SetVideoWriterEventId( uint32_t p_event_id ) { video_store_data->current_event = p_event_id; }
|
||||||
|
|
|
@ -123,41 +123,4 @@ int ZMPacket::decode( AVCodecContext *ctx ) {
|
||||||
return 1;
|
return 1;
|
||||||
} // end ZMPacket::decode
|
} // end ZMPacket::decode
|
||||||
|
|
||||||
Image * ZMPacket::get_image( Image *i = NULL ) {
|
|
||||||
|
|
||||||
if ( ! frame ) {
|
|
||||||
Error("Can't get image without frame.. maybe need to decode first");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! image ) {
|
|
||||||
if ( ! i ) {
|
|
||||||
Error("Need a pre-allocated image buffer");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
image = i;
|
|
||||||
}
|
|
||||||
/* Request a writeable buffer of the target image */
|
|
||||||
uint8_t* directbuffer = image->WriteBuffer();
|
|
||||||
//uint8_t* directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
|
|
||||||
if ( directbuffer == NULL ) {
|
|
||||||
Error("Failed requesting writeable buffer for the captured image.");
|
|
||||||
image = NULL;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
AVFrame *mFrame = zm_av_frame_alloc();
|
|
||||||
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
|
||||||
av_image_fill_arrays(mFrame->data, mFrame->linesize, directbuffer, imagePixFormat, frame->width, frame->height, 1);
|
|
||||||
#else
|
|
||||||
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, frame->width, frame->height);
|
|
||||||
#endif
|
|
||||||
if (sws_scale(mConvertContext, frame->data, frame->linesize,
|
|
||||||
0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0) {
|
|
||||||
Fatal("Unable to convert raw format %u to target format %u",
|
|
||||||
mVideoCodecContext->pix_fmt, imagePixFormat);
|
|
||||||
}
|
|
||||||
av_frame_free( &mFrame );
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ class ZMPacket {
|
||||||
public:
|
public:
|
||||||
AVPacket *av_packet() { return &packet; }
|
AVPacket *av_packet() { return &packet; }
|
||||||
AVFrame *av_frame() { return frame; }
|
AVFrame *av_frame() { return frame; }
|
||||||
Image *get_image( Image * );
|
Image *get_image( Image *, _AVPIXELFORMAT imagePixFormat, struct SwsContext *mConvertContext );
|
||||||
int is_keyframe() { return keyframe; };
|
int is_keyframe() { return keyframe; };
|
||||||
int decode( AVCodecContext *ctx );
|
int decode( AVCodecContext *ctx );
|
||||||
ZMPacket( AVPacket *packet, struct timeval *timestamp );
|
ZMPacket( AVPacket *packet, struct timeval *timestamp );
|
||||||
|
|
|
@ -31,10 +31,12 @@ extern "C" {
|
||||||
#include "libavutil/time.h"
|
#include "libavutil/time.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
VideoStore::VideoStore(
|
||||||
AVStream *p_video_in_stream,
|
const char *filename_in, const char *format_in,
|
||||||
AVStream *p_audio_in_stream,
|
AVStream *p_video_in_stream,
|
||||||
Monitor *monitor) {
|
AVStream *p_audio_in_stream, int64_t nStartTime,
|
||||||
|
Monitor *monitor
|
||||||
|
) {
|
||||||
video_in_stream = p_video_in_stream;
|
video_in_stream = p_video_in_stream;
|
||||||
audio_in_stream = p_audio_in_stream;
|
audio_in_stream = p_audio_in_stream;
|
||||||
|
|
||||||
|
@ -50,9 +52,12 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||||
// store ins in variables local to class
|
// store ins in variables local to class
|
||||||
filename = filename_in;
|
filename = filename_in;
|
||||||
format = format_in;
|
format = format_in;
|
||||||
|
packets_written = 0;
|
||||||
|
frame_count = 0;
|
||||||
|
|
||||||
Info("Opening video storage stream %s format: %s", filename, format);
|
Info("Opening video storage stream %s format: %s", filename, format);
|
||||||
|
|
||||||
|
#if 0
|
||||||
ret = avformat_alloc_output_context2(&oc, NULL, NULL, filename);
|
ret = avformat_alloc_output_context2(&oc, NULL, NULL, filename);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
Warning(
|
Warning(
|
||||||
|
@ -65,6 +70,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||||
|
|
||||||
// Couldn't deduce format from filename, trying from format name
|
// Couldn't deduce format from filename, trying from format name
|
||||||
if (!oc) {
|
if (!oc) {
|
||||||
|
#endif
|
||||||
avformat_alloc_output_context2(&oc, NULL, format, filename);
|
avformat_alloc_output_context2(&oc, NULL, format, filename);
|
||||||
if (!oc) {
|
if (!oc) {
|
||||||
Fatal(
|
Fatal(
|
||||||
|
@ -72,9 +78,11 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||||
" could not be assigned based on filename or format %s",
|
" could not be assigned based on filename or format %s",
|
||||||
filename, format);
|
filename, format);
|
||||||
} else {
|
} else {
|
||||||
Debug(4, "Success alocateing out ctx");
|
Debug(4, "Success alocating out ctx");
|
||||||
}
|
}
|
||||||
|
#if 0
|
||||||
} // end if ! oc
|
} // end if ! oc
|
||||||
|
#endif
|
||||||
|
|
||||||
AVDictionary *pmetadata = NULL;
|
AVDictionary *pmetadata = NULL;
|
||||||
int dsr =
|
int dsr =
|
||||||
|
@ -84,15 +92,14 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||||
oc->metadata = pmetadata;
|
oc->metadata = pmetadata;
|
||||||
out_format = oc->oformat;
|
out_format = oc->oformat;
|
||||||
|
|
||||||
|
in_frame = NULL;
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
|
|
||||||
// Since we are not re-encoding, all we have to do is copy the parameters
|
|
||||||
video_out_ctx = avcodec_alloc_context3(NULL);
|
video_out_ctx = avcodec_alloc_context3(NULL);
|
||||||
|
|
||||||
// Copy params from instream to ctx
|
// Copy params from instream to ctx
|
||||||
ret = avcodec_parameters_to_context(video_out_ctx,
|
ret = avcodec_parameters_to_context(video_out_ctx,
|
||||||
video_in_stream->codecpar);
|
video_in_stream->codecpar);
|
||||||
if (ret < 0) {
|
if ( ret < 0 ) {
|
||||||
Error("Could not initialize ctx parameteres");
|
Error("Could not initialize ctx parameteres");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
@ -106,7 +113,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||||
Debug(2, "Success creating video out stream");
|
Debug(2, "Success creating video out stream");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!video_out_ctx->codec_tag) {
|
if ( !video_out_ctx->codec_tag ) {
|
||||||
video_out_ctx->codec_tag =
|
video_out_ctx->codec_tag =
|
||||||
av_codec_get_tag(oc->oformat->codec_tag, video_in_ctx->codec_id);
|
av_codec_get_tag(oc->oformat->codec_tag, video_in_ctx->codec_id);
|
||||||
Debug(2, "No codec_tag, setting to %d", video_out_ctx->codec_tag);
|
Debug(2, "No codec_tag, setting to %d", video_out_ctx->codec_tag);
|
||||||
|
@ -124,22 +131,80 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||||
zm_dump_codecpar(video_out_stream->codecpar);
|
zm_dump_codecpar(video_out_stream->codecpar);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
video_out_stream =
|
if ( video_in_ctx->codec_id == AV_CODEC_ID_H264 ) {
|
||||||
avformat_new_stream(oc,(const AVCodec *)(video_in_ctx->codec));
|
// Same codec, just copy the packets, otherwise we have to decode/encode
|
||||||
if (!video_out_stream) {
|
video_out_codec = (AVCodec *)video_in_ctx->codec;
|
||||||
|
video_out_stream = avformat_new_stream(oc, video_out_codec);
|
||||||
|
if ( !video_out_stream ) {
|
||||||
|
Fatal("Unable to create video out stream\n");
|
||||||
|
} else {
|
||||||
|
Debug(2, "Success creating video out stream");
|
||||||
|
}
|
||||||
|
video_out_ctx = video_out_stream->codec;
|
||||||
|
// Just copy them from the in, no reason to choose different
|
||||||
|
video_out_ctx->time_base = video_in_ctx->time_base;
|
||||||
|
video_out_stream->time_base = video_in_stream->time_base;
|
||||||
|
} else {
|
||||||
|
/** Create a new frame to store the audio samples. */
|
||||||
|
if ( !(in_frame = zm_av_frame_alloc()) ) {
|
||||||
|
Error("Could not allocate in frame");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
video_out_codec = avcodec_find_encoder(AV_CODEC_ID_H264);
|
||||||
|
if (!video_out_codec) {
|
||||||
|
Fatal("Could not find codec for H264");
|
||||||
|
}
|
||||||
|
Debug(2, "Have video out codec");
|
||||||
|
}
|
||||||
|
video_out_stream = avformat_new_stream(oc, video_out_codec);
|
||||||
|
if ( !video_out_stream ) {
|
||||||
Fatal("Unable to create video out stream\n");
|
Fatal("Unable to create video out stream\n");
|
||||||
} else {
|
} else {
|
||||||
Debug(2, "Success creating video out stream");
|
Debug(2, "Success creating video out stream");
|
||||||
}
|
}
|
||||||
video_out_ctx = video_out_stream->codec;
|
video_out_ctx = video_out_stream->codec;
|
||||||
ret = avcodec_copy_context(video_out_ctx, video_in_ctx);
|
|
||||||
if (ret < 0) {
|
// * Copy over the useful; parameters
|
||||||
Fatal("Unable to copy in video ctx to out video ctx %s\n",
|
video_out_ctx->height = video_in_ctx->height;
|
||||||
av_make_error_string(ret).c_str());
|
video_out_ctx->width = video_in_ctx->width;
|
||||||
|
video_out_ctx->sample_aspect_ratio = video_in_ctx->sample_aspect_ratio;
|
||||||
|
/* take first format from list of supported formats */
|
||||||
|
video_out_ctx->pix_fmt = video_out_codec->pix_fmts[0];
|
||||||
|
/* video time_base can be set to whatever is handy and supported by encoder */
|
||||||
|
//video_out_ctx->time_base = video_in_ctx->time_base;
|
||||||
|
video_out_ctx->time_base = (AVRational){1, 1000}; // Milliseconds as base frame rate
|
||||||
|
video_out_ctx->framerate = (AVRational){0,1}; // Unknown framerate
|
||||||
|
|
||||||
|
AVDictionary *opts = 0;
|
||||||
|
std::string Options = monitor->GetEncoderOptions();
|
||||||
|
ret = av_dict_parse_string(&opts, Options.c_str(), "=", ",#\n", 0);
|
||||||
|
if ( ret < 0 ) {
|
||||||
|
Warning("Could not parse ffmpeg encoder options list '%s'\n", Options.c_str());
|
||||||
} else {
|
} else {
|
||||||
Debug(3, "Success copying ctx");
|
AVDictionaryEntry *e = NULL;
|
||||||
|
while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
|
||||||
|
Debug( 3, "Encoder Option %s=%s", e->key, e->value );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!video_out_ctx->codec_tag) {
|
ret = avcodec_open2(video_out_ctx, video_out_codec, &opts );
|
||||||
|
if (ret < 0) {
|
||||||
|
Error("Can't open video codec!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AVDictionaryEntry *e = NULL;
|
||||||
|
while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
|
||||||
|
Warning( "Encoder Option %s not recognized by ffmpeg codec", e->key);
|
||||||
|
}
|
||||||
|
av_dict_free(&opts);
|
||||||
|
//ret = avcodec_copy_context(video_out_ctx, video_in_ctx);
|
||||||
|
//if ( ret < 0 ) {
|
||||||
|
//Fatal("Unable to copy in video ctx to out video ctx %s\n",
|
||||||
|
//av_make_error_string(ret).c_str());
|
||||||
|
////} else {
|
||||||
|
//Debug(3, "Success copying ctx");
|
||||||
|
//}
|
||||||
|
#if 0
|
||||||
|
if ( !video_out_ctx->codec_tag ) {
|
||||||
Debug(2, "No codec_tag");
|
Debug(2, "No codec_tag");
|
||||||
if (!oc->oformat->codec_tag ||
|
if (!oc->oformat->codec_tag ||
|
||||||
av_codec_get_id(oc->oformat->codec_tag,
|
av_codec_get_id(oc->oformat->codec_tag,
|
||||||
|
@ -152,10 +217,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
// Just copy them from the in, no reason to choose different
|
|
||||||
video_out_ctx->time_base = video_in_ctx->time_base;
|
|
||||||
video_out_stream->time_base = video_in_stream->time_base;
|
|
||||||
|
|
||||||
Debug(3,
|
Debug(3,
|
||||||
"Time bases: VIDEO in stream (%d/%d) in codec: (%d/%d) out "
|
"Time bases: VIDEO in stream (%d/%d) in codec: (%d/%d) out "
|
||||||
|
@ -192,7 +254,6 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in,
|
||||||
audio_out_codec = NULL;
|
audio_out_codec = NULL;
|
||||||
audio_in_ctx = NULL;
|
audio_in_ctx = NULL;
|
||||||
audio_out_stream = NULL;
|
audio_out_stream = NULL;
|
||||||
in_frame = NULL;
|
|
||||||
out_frame = NULL;
|
out_frame = NULL;
|
||||||
#ifdef HAVE_LIBAVRESAMPLE
|
#ifdef HAVE_LIBAVRESAMPLE
|
||||||
resample_ctx = NULL;
|
resample_ctx = NULL;
|
||||||
|
@ -329,17 +390,86 @@ bool VideoStore::open() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VideoStore::write_audio_packet( AVPacket &pkt ) {
|
||||||
|
Debug(2, "writing flushed packet pts(%d) dts(%d) duration(%d)", pkt.pts,
|
||||||
|
pkt.dts, pkt.duration);
|
||||||
|
pkt.pts = audio_next_pts;
|
||||||
|
pkt.dts = audio_next_dts;
|
||||||
|
|
||||||
|
if (pkt.duration > 0)
|
||||||
|
pkt.duration =
|
||||||
|
av_rescale_q(pkt.duration, audio_out_ctx->time_base,
|
||||||
|
audio_out_stream->time_base);
|
||||||
|
audio_next_pts += pkt.duration;
|
||||||
|
audio_next_dts += pkt.duration;
|
||||||
|
|
||||||
|
Debug(2, "writing flushed packet pts(%d) dts(%d) duration(%d)", pkt.pts,
|
||||||
|
pkt.dts, pkt.duration);
|
||||||
|
pkt.stream_index = audio_out_stream->index;
|
||||||
|
av_interleaved_write_frame(oc, &pkt);
|
||||||
|
}
|
||||||
|
|
||||||
VideoStore::~VideoStore() {
|
VideoStore::~VideoStore() {
|
||||||
|
if ( video_out_ctx->codec_id != video_in_ctx->codec_id ) {
|
||||||
|
|
||||||
|
if (video_out_ctx->codec->capabilities & AV_CODEC_CAP_DELAY) {
|
||||||
|
// The codec queues data. We need to send a flush command and out
|
||||||
|
// whatever we get. Failures are not fatal.
|
||||||
|
AVPacket pkt;
|
||||||
|
av_init_packet(&pkt);
|
||||||
|
|
||||||
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
|
// Put encoder into flushing mode
|
||||||
|
avcodec_send_frame(video_out_ctx, NULL);
|
||||||
|
while (1) {
|
||||||
|
ret = avcodec_receive_packet(video_out_ctx, &pkt);
|
||||||
|
if (ret < 0) {
|
||||||
|
if (AVERROR_EOF != ret) {
|
||||||
|
Error("ERror encoding audio while flushing (%d) (%s)", ret,
|
||||||
|
av_err2str(ret));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
while (1) {
|
||||||
|
// WIthout these we seg fault I don't know why.
|
||||||
|
pkt.data = NULL;
|
||||||
|
pkt.size = 0;
|
||||||
|
av_init_packet(&pkt);
|
||||||
|
int got_packet = 0;
|
||||||
|
ret = avcodec_encode_video2(video_out_ctx, &pkt, NULL, &got_packet);
|
||||||
|
if ( ret < 0 ) {
|
||||||
|
Error("ERror encoding video while flushing (%d) (%s)", ret,
|
||||||
|
av_err2str(ret));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!got_packet) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
Debug(3, "(%d, %d)", video_next_dts, video_next_pts );
|
||||||
|
pkt.dts = video_next_dts;
|
||||||
|
pkt.pts = video_next_pts;
|
||||||
|
pkt.duration = video_last_duration;
|
||||||
|
write_video_packet(pkt);
|
||||||
|
zm_av_packet_unref(&pkt);
|
||||||
|
} // while have buffered frames
|
||||||
|
} // end if have delay capability
|
||||||
|
} // end if have buffered video
|
||||||
|
|
||||||
if (audio_out_codec) {
|
if (audio_out_codec) {
|
||||||
// The codec queues data. We need to send a flush command and out
|
// The codec queues data. We need to send a flush command and out
|
||||||
// whatever we get. Failures are not fatal.
|
// whatever we get. Failures are not fatal.
|
||||||
AVPacket pkt;
|
AVPacket pkt;
|
||||||
|
// WIthout these we seg fault I don't know why.
|
||||||
|
pkt.data = NULL;
|
||||||
|
pkt.size = 0;
|
||||||
av_init_packet(&pkt);
|
av_init_packet(&pkt);
|
||||||
|
|
||||||
while (1) {
|
|
||||||
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
// Put encoder into flushing mode
|
// Put encoder into flushing mode
|
||||||
avcodec_send_frame(audio_out_ctx, NULL);
|
avcodec_send_frame(audio_out_ctx, NULL);
|
||||||
|
while (1) {
|
||||||
ret = avcodec_receive_packet(audio_out_ctx, &pkt);
|
ret = avcodec_receive_packet(audio_out_ctx, &pkt);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (AVERROR_EOF != ret) {
|
if (AVERROR_EOF != ret) {
|
||||||
|
@ -349,6 +479,7 @@ VideoStore::~VideoStore() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
while (1) {
|
||||||
int got_packet = 0;
|
int got_packet = 0;
|
||||||
ret =
|
ret =
|
||||||
avcodec_encode_audio2(audio_out_ctx, &pkt, NULL, &got_packet);
|
avcodec_encode_audio2(audio_out_ctx, &pkt, NULL, &got_packet);
|
||||||
|
@ -362,22 +493,7 @@ VideoStore::~VideoStore() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
Debug(2, "writing flushed packet pts(%d) dts(%d) duration(%d)", pkt.pts,
|
write_audio_packet(pkt);
|
||||||
pkt.dts, pkt.duration);
|
|
||||||
pkt.pts = audio_next_pts;
|
|
||||||
pkt.dts = audio_next_dts;
|
|
||||||
|
|
||||||
if (pkt.duration > 0)
|
|
||||||
pkt.duration =
|
|
||||||
av_rescale_q(pkt.duration, audio_out_ctx->time_base,
|
|
||||||
audio_out_stream->time_base);
|
|
||||||
audio_next_pts += pkt.duration;
|
|
||||||
audio_next_dts += pkt.duration;
|
|
||||||
|
|
||||||
Debug(2, "writing flushed packet pts(%d) dts(%d) duration(%d)", pkt.pts,
|
|
||||||
pkt.dts, pkt.duration);
|
|
||||||
pkt.stream_index = audio_out_stream->index;
|
|
||||||
av_interleaved_write_frame(oc, &pkt);
|
|
||||||
zm_av_packet_unref(&pkt);
|
zm_av_packet_unref(&pkt);
|
||||||
} // while have buffered frames
|
} // while have buffered frames
|
||||||
} // end if audio_out_codec
|
} // end if audio_out_codec
|
||||||
|
@ -402,6 +518,11 @@ VideoStore::~VideoStore() {
|
||||||
video_out_ctx = NULL;
|
video_out_ctx = NULL;
|
||||||
Debug(4, "Success freeing video_out_ctx");
|
Debug(4, "Success freeing video_out_ctx");
|
||||||
}
|
}
|
||||||
|
// Used by both audio and video conversions
|
||||||
|
if (in_frame) {
|
||||||
|
av_frame_free(&in_frame);
|
||||||
|
in_frame = NULL;
|
||||||
|
}
|
||||||
if (audio_out_stream) {
|
if (audio_out_stream) {
|
||||||
avcodec_close(audio_out_ctx);
|
avcodec_close(audio_out_ctx);
|
||||||
audio_out_ctx = NULL;
|
audio_out_ctx = NULL;
|
||||||
|
@ -410,10 +531,6 @@ VideoStore::~VideoStore() {
|
||||||
avresample_close(resample_ctx);
|
avresample_close(resample_ctx);
|
||||||
avresample_free(&resample_ctx);
|
avresample_free(&resample_ctx);
|
||||||
}
|
}
|
||||||
if (in_frame) {
|
|
||||||
av_frame_free(&in_frame);
|
|
||||||
in_frame = NULL;
|
|
||||||
}
|
|
||||||
if (out_frame) {
|
if (out_frame) {
|
||||||
av_frame_free(&out_frame);
|
av_frame_free(&out_frame);
|
||||||
out_frame = NULL;
|
out_frame = NULL;
|
||||||
|
@ -547,10 +664,12 @@ bool VideoStore::setup_resampler() {
|
||||||
audio_out_ctx->channel_layout, audio_out_ctx->frame_size);
|
audio_out_ctx->channel_layout, audio_out_ctx->frame_size);
|
||||||
|
|
||||||
/** Create a new frame to store the audio samples. */
|
/** Create a new frame to store the audio samples. */
|
||||||
|
if ( ! in_frame ) {
|
||||||
if (!(in_frame = zm_av_frame_alloc())) {
|
if (!(in_frame = zm_av_frame_alloc())) {
|
||||||
Error("Could not allocate in frame");
|
Error("Could not allocate in frame");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Create a new frame to store the audio samples. */
|
/** Create a new frame to store the audio samples. */
|
||||||
if (!(out_frame = zm_av_frame_alloc())) {
|
if (!(out_frame = zm_av_frame_alloc())) {
|
||||||
|
@ -687,92 +806,117 @@ int VideoStore::writePacket( ZMPacket *ipkt ) {
|
||||||
|
|
||||||
int VideoStore::writeVideoFramePacket(AVPacket *ipkt) {
|
int VideoStore::writeVideoFramePacket(AVPacket *ipkt) {
|
||||||
av_init_packet(&opkt);
|
av_init_packet(&opkt);
|
||||||
|
frame_count += 1;
|
||||||
|
if ( video_out_ctx->codec_id != video_in_ctx->codec_id ) {
|
||||||
|
Debug(3, "Have encoding video frmae count (%d)", frame_count);
|
||||||
|
|
||||||
opkt.pts = video_next_pts;
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
||||||
opkt.dts = video_next_dts;
|
ret = avcodec_send_packet(video_in_ctx, ipkt);
|
||||||
opkt.duration = 0;
|
if (ret < 0) {
|
||||||
|
Error("avcodec_send_packet fail %s", av_make_error_string(ret).c_str());
|
||||||
int duration;
|
return 0;
|
||||||
if (!video_last_pts) {
|
|
||||||
duration = 0;
|
|
||||||
} else {
|
|
||||||
duration =
|
|
||||||
av_rescale_q(ipkt->pts - video_last_pts, video_in_stream->time_base,
|
|
||||||
video_out_stream->time_base);
|
|
||||||
Debug(1, "duration calc: pts(%d) - last_pts(%d) = (%d)", ipkt->pts,
|
|
||||||
video_last_pts, duration);
|
|
||||||
if (duration < 0) {
|
|
||||||
duration = ipkt->duration;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//#if ( 0 && video_last_pts && ( ipkt->duration == AV_NOPTS_VALUE || !
|
ret = avcodec_receive_frame(video_in_ctx, in_frame);
|
||||||
//ipkt->duration ) ) {
|
if ( ret < 0 ) {
|
||||||
// Video packets don't really have a duration. Audio does.
|
Error("avcodec_receive_frame fail %s", av_make_error_string(ret).c_str());
|
||||||
// opkt.duration = av_rescale_q(duration, video_in_stream->time_base,
|
return 0;
|
||||||
// video_out_stream->time_base);
|
}
|
||||||
// opkt.duration = 0;
|
if ((ret = avcodec_send_frame(video_out_ctx, in_frame)) < 0) {
|
||||||
//} else {
|
Error("Could not send frame (error '%s')",
|
||||||
// duration = opkt.duration = av_rescale_q(ipkt->duration,
|
av_make_error_string(ret).c_str());
|
||||||
// video_in_stream->time_base, video_out_stream->time_base);
|
zm_av_packet_unref(&opkt);
|
||||||
//}
|
return 0;
|
||||||
video_last_pts = ipkt->pts;
|
}
|
||||||
video_last_dts = ipkt->dts;
|
|
||||||
|
|
||||||
#if 0
|
if ((ret = avcodec_receive_packet(video_out_ctx, &opkt)) < 0) {
|
||||||
//Scale the PTS of the outgoing packet to be the correct time base
|
if (AVERROR(EAGAIN) == ret) {
|
||||||
if ( ipkt->pts != AV_NOPTS_VALUE ) {
|
// THe codec may need more samples than it has, perfectly valid
|
||||||
|
Debug(3, "Could not recieve packet (error '%s')",
|
||||||
if ( ! video_last_pts ) {
|
av_make_error_string(ret).c_str());
|
||||||
// This is the first packet.
|
} else {
|
||||||
opkt.pts = 0;
|
Error("Could not recieve packet (error %d = '%s')", ret,
|
||||||
Debug(2, "Starting video video_last_pts will become (%d)", ipkt->pts);
|
av_make_error_string(ret).c_str());
|
||||||
|
}
|
||||||
|
zm_av_packet_unref(&opkt);
|
||||||
|
av_frame_unref(in_frame);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/**
|
||||||
|
* Decode the video frame stored in the packet.
|
||||||
|
* The in video stream decoder is used to do this.
|
||||||
|
* If we are at the end of the file, pass an empty packet to the decoder
|
||||||
|
* to flush it.
|
||||||
|
*/
|
||||||
|
if ((ret = avcodec_decode_video2(video_in_ctx, in_frame,
|
||||||
|
&data_present, ipkt)) < 0) {
|
||||||
|
Error("Could not decode frame (error '%s')\n",
|
||||||
|
av_make_error_string(ret).c_str());
|
||||||
|
dumpPacket(ipkt);
|
||||||
|
av_frame_free(&in_frame);
|
||||||
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
if ( ipkt->pts < video_last_pts ) {
|
Debug(3, "Decoded frame data_present(%d)", data_present);
|
||||||
Debug(1, "Resetting video_last_pts from (%d) to (%d)", video_last_pts, ipkt->pts);
|
|
||||||
// wrap around, need to figure out the distance FIXME having this wrong should cause a jump, but then play ok?
|
|
||||||
opkt.pts = video_next_pts + av_rescale_q( ipkt->pts, video_in_stream->time_base, video_out_stream->time_base);
|
|
||||||
} else {
|
|
||||||
opkt.pts = video_next_pts + av_rescale_q( ipkt->pts - video_last_pts, video_in_stream->time_base, video_out_stream->time_base);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Debug(3, "opkt.pts = %d from ipkt->pts(%d) - last_pts(%d)", opkt.pts, ipkt->pts, video_last_pts);
|
if ( !data_present ) {
|
||||||
video_last_pts = ipkt->pts;
|
Debug(2, "Not ready to transcode a frame yet.");
|
||||||
} else {
|
return 0;
|
||||||
Debug(3, "opkt.pts = undef");
|
}
|
||||||
opkt.pts = AV_NOPTS_VALUE;
|
if ((ret = avcodec_encode_video2(video_out_ctx, &opkt, in_frame,
|
||||||
}
|
&data_present)) < 0) {
|
||||||
// Just because the in stream wraps, doesn't mean the out needs to. Really, if we are limiting ourselves to 10min segments I can't imagine every wrapping in the out. So need to handle in wrap, without causing out wrap.
|
Error("Could not encode frame (error '%s')",
|
||||||
if ( !video_last_dts ) {
|
av_make_error_string(ret).c_str());
|
||||||
// This is the first packet.
|
zm_av_packet_unref(&opkt);
|
||||||
opkt.dts = 0;
|
return 0;
|
||||||
Debug(1, "Starting video video_last_dts will become (%lu)", ipkt->dts);
|
}
|
||||||
video_last_dts = ipkt->dts;
|
if (!data_present) {
|
||||||
} else {
|
Debug(2, "Not ready to out a frame yet.");
|
||||||
// Scale the DTS of the outgoing packet to be the correct time base
|
zm_av_packet_unref(&opkt);
|
||||||
|
return 0;
|
||||||
if ( ipkt->dts == AV_NOPTS_VALUE ) {
|
|
||||||
// why are we using cur_dts instead of packet.dts? I think cur_dts is in AV_TIME_BASE_Q, but ipkt.dts is in video_in_stream->time_base
|
|
||||||
if ( video_in_stream->cur_dts < video_last_dts ) {
|
|
||||||
Debug(1, "Resetting video_last_dts from (%d) to (%d) p.dts was (%d)", video_last_dts, video_in_stream->cur_dts, ipkt->dts);
|
|
||||||
opkt.dts = video_next_dts + av_rescale_q(video_in_stream->cur_dts, AV_TIME_BASE_Q, video_out_stream->time_base);
|
|
||||||
} else {
|
|
||||||
opkt.dts = video_next_dts + av_rescale_q(video_in_stream->cur_dts - video_last_dts, AV_TIME_BASE_Q, video_out_stream->time_base);
|
|
||||||
}
|
|
||||||
Debug(3, "opkt.dts = %d from video_in_stream->cur_dts(%d) - previus_dts(%d)", opkt.dts, video_in_stream->cur_dts, video_last_dts);
|
|
||||||
video_last_dts = video_in_stream->cur_dts;
|
|
||||||
} else {
|
|
||||||
if ( ipkt->dts < video_last_dts ) {
|
|
||||||
Debug(1, "Resetting video_last_dts from (%d) to (%d)", video_last_dts, ipkt->dts);
|
|
||||||
opkt.dts = video_next_dts + av_rescale_q( ipkt->dts, video_in_stream->time_base, video_out_stream->time_base);
|
|
||||||
} else {
|
|
||||||
opkt.dts = video_next_dts + av_rescale_q( ipkt->dts - video_last_dts, video_in_stream->time_base, video_out_stream->time_base);
|
|
||||||
}
|
|
||||||
Debug(3, "opkt.dts = %d from ipkt.dts(%d) - previus_dts(%d)", opkt.dts, ipkt->dts, video_last_dts);
|
|
||||||
video_last_dts = ipkt->dts;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
} else {
|
||||||
|
Debug(3, "Doing passthrough, just copy packet");
|
||||||
|
// Just copy it because the codec is the same
|
||||||
|
opkt.data = ipkt->data;
|
||||||
|
opkt.size = ipkt->size;
|
||||||
|
opkt.flags = ipkt->flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
opkt.dts = video_next_dts;
|
||||||
|
opkt.pts = video_next_pts;
|
||||||
|
|
||||||
|
int duration;
|
||||||
|
if ( !video_last_pts ) {
|
||||||
|
duration = 0;
|
||||||
|
} else {
|
||||||
|
duration = av_rescale_q(
|
||||||
|
ipkt->pts - video_last_pts,
|
||||||
|
video_in_stream->time_base,
|
||||||
|
video_out_stream->time_base
|
||||||
|
);
|
||||||
|
Debug(1, "duration calc: pts(%d) - last_pts(%d) = (%d)", ipkt->pts,
|
||||||
|
video_last_pts, duration);
|
||||||
|
if ( duration < 0 ) {
|
||||||
|
duration = ipkt->duration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug(1, "ipkt.dts(%d) ipkt.pts(%d)", ipkt->dts, ipkt->pts);
|
||||||
|
video_last_pts = ipkt->pts;
|
||||||
|
video_last_dts = ipkt->dts;
|
||||||
|
video_last_duration = duration;
|
||||||
|
opkt.duration = duration;
|
||||||
|
|
||||||
|
write_video_packet( opkt );
|
||||||
|
zm_av_packet_unref(&opkt);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} // end int VideoStore::writeVideoFramePacket( AVPacket *ipkt )
|
||||||
|
|
||||||
|
void VideoStore::write_video_packet( AVPacket &opkt ) {
|
||||||
|
|
||||||
if (opkt.dts > opkt.pts) {
|
if (opkt.dts > opkt.pts) {
|
||||||
Debug(1,
|
Debug(1,
|
||||||
"opkt.dts(%d) must be <= opkt.pts(%d). Decompression must happen "
|
"opkt.dts(%d) must be <= opkt.pts(%d). Decompression must happen "
|
||||||
|
@ -781,36 +925,30 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) {
|
||||||
opkt.dts = opkt.pts;
|
opkt.dts = opkt.pts;
|
||||||
}
|
}
|
||||||
|
|
||||||
opkt.flags = ipkt->flags;
|
|
||||||
int keyframe = opkt.flags & AV_PKT_FLAG_KEY;
|
int keyframe = opkt.flags & AV_PKT_FLAG_KEY;
|
||||||
opkt.pos = -1;
|
opkt.pos = -1;
|
||||||
|
|
||||||
opkt.data = ipkt->data;
|
|
||||||
opkt.size = ipkt->size;
|
|
||||||
|
|
||||||
opkt.stream_index = video_out_stream->index;
|
opkt.stream_index = video_out_stream->index;
|
||||||
|
|
||||||
|
video_next_dts += opkt.duration;
|
||||||
|
video_next_pts += opkt.duration;
|
||||||
|
|
||||||
AVPacket safepkt;
|
AVPacket safepkt;
|
||||||
memcpy(&safepkt, &opkt, sizeof(AVPacket));
|
memcpy(&safepkt, &opkt, sizeof(AVPacket));
|
||||||
|
|
||||||
Debug(1,
|
Debug(1,
|
||||||
"writing video packet keyframe(%d) pts(%d) dts(%d) duration(%d) "
|
"writing video packet keyframe(%d) pts(%d) dts(%d) duration(%d) packet_count(%d)",
|
||||||
"ipkt.duration(%d)",
|
keyframe, opkt.pts, opkt.dts, opkt.duration, packets_written );
|
||||||
keyframe, opkt.pts, opkt.dts, duration, ipkt->duration);
|
if ( (opkt.data == NULL) || (opkt.size < 1) ) {
|
||||||
if ((opkt.data == NULL) || (opkt.size < 1)) {
|
|
||||||
Warning("%s:%d: Mangled AVPacket: discarding frame", __FILE__, __LINE__);
|
Warning("%s:%d: Mangled AVPacket: discarding frame", __FILE__, __LINE__);
|
||||||
dumpPacket(ipkt);
|
|
||||||
dumpPacket(&opkt);
|
dumpPacket(&opkt);
|
||||||
|
|
||||||
} else if ((video_next_dts > 0) && (video_next_dts > opkt.dts)) {
|
//} else if ((video_next_dts > 0) && (video_next_dts > opkt.dts)) {
|
||||||
Warning("%s:%d: DTS out of order: %lld \u226E %lld; discarding frame",
|
//Warning("%s:%d: DTS out of order: next:%lld \u226E opkt.dts %lld; discarding frame",
|
||||||
__FILE__, __LINE__, video_next_dts, opkt.dts);
|
//__FILE__, __LINE__, video_next_dts, opkt.dts);
|
||||||
video_next_dts = opkt.dts;
|
//video_next_dts = opkt.dts;
|
||||||
dumpPacket(&opkt);
|
//dumpPacket(&opkt);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
video_next_dts = opkt.dts + duration;
|
|
||||||
video_next_pts = opkt.pts + duration;
|
|
||||||
ret = av_interleaved_write_frame(oc, &opkt);
|
ret = av_interleaved_write_frame(oc, &opkt);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
// There's nothing we can really do if the frame is rejected, just drop it
|
// There's nothing we can really do if the frame is rejected, just drop it
|
||||||
|
@ -824,13 +962,12 @@ int VideoStore::writeVideoFramePacket(AVPacket *ipkt) {
|
||||||
zm_dump_codecpar(video_in_stream->codecpar);
|
zm_dump_codecpar(video_in_stream->codecpar);
|
||||||
zm_dump_codecpar(video_out_stream->codecpar);
|
zm_dump_codecpar(video_out_stream->codecpar);
|
||||||
#endif
|
#endif
|
||||||
|
} else {
|
||||||
|
packets_written += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
zm_av_packet_unref(&opkt);
|
} // end void VideoStore::write_video_packet
|
||||||
|
|
||||||
return 0;
|
|
||||||
} // end int VideoStore::writeVideoFramePacket( AVPacket *ipkt )
|
|
||||||
|
|
||||||
int VideoStore::writeAudioFramePacket(AVPacket *ipkt) {
|
int VideoStore::writeAudioFramePacket(AVPacket *ipkt) {
|
||||||
Debug(4, "writeAudioFrame");
|
Debug(4, "writeAudioFrame");
|
||||||
|
|
|
@ -18,11 +18,14 @@ extern "C" {
|
||||||
class VideoStore {
|
class VideoStore {
|
||||||
private:
|
private:
|
||||||
unsigned int packets_written;
|
unsigned int packets_written;
|
||||||
|
unsigned int frame_count;
|
||||||
|
|
||||||
AVOutputFormat *out_format;
|
AVOutputFormat *out_format;
|
||||||
AVFormatContext *oc;
|
AVFormatContext *oc;
|
||||||
AVStream *video_out_stream;
|
AVStream *video_out_stream;
|
||||||
AVStream *audio_out_stream;
|
AVStream *audio_out_stream;
|
||||||
|
|
||||||
|
AVCodec *video_out_codec;
|
||||||
AVCodecContext *video_out_ctx;
|
AVCodecContext *video_out_ctx;
|
||||||
|
|
||||||
AVStream *video_in_stream;
|
AVStream *video_in_stream;
|
||||||
|
@ -31,6 +34,7 @@ private:
|
||||||
// Move this into the object so that we aren't constantly allocating/deallocating it on the stack
|
// Move this into the object so that we aren't constantly allocating/deallocating it on the stack
|
||||||
AVPacket opkt;
|
AVPacket opkt;
|
||||||
// we are transcoding
|
// we are transcoding
|
||||||
|
AVFrame *video_in_frame;
|
||||||
AVFrame *in_frame;
|
AVFrame *in_frame;
|
||||||
AVFrame *out_frame;
|
AVFrame *out_frame;
|
||||||
|
|
||||||
|
@ -58,12 +62,14 @@ AVAudioResampleContext* resample_ctx;
|
||||||
// These are for in
|
// These are for in
|
||||||
int64_t video_last_pts;
|
int64_t video_last_pts;
|
||||||
int64_t video_last_dts;
|
int64_t video_last_dts;
|
||||||
|
int64_t video_last_duration;
|
||||||
int64_t audio_last_pts;
|
int64_t audio_last_pts;
|
||||||
int64_t audio_last_dts;
|
int64_t audio_last_dts;
|
||||||
|
|
||||||
// These are for out, should start at zero. We assume they do not wrap because we just aren't going to save files that big.
|
// These are for out, should start at zero. We assume they do not wrap because we just aren't going to save files that big.
|
||||||
int64_t video_next_pts;
|
int64_t video_next_pts;
|
||||||
int64_t video_next_dts;
|
int64_t video_next_dts;
|
||||||
|
;
|
||||||
int64_t audio_next_pts;
|
int64_t audio_next_pts;
|
||||||
int64_t audio_next_dts;
|
int64_t audio_next_dts;
|
||||||
|
|
||||||
|
@ -81,6 +87,8 @@ public:
|
||||||
bool open();
|
bool open();
|
||||||
~VideoStore();
|
~VideoStore();
|
||||||
|
|
||||||
|
void write_video_packet( AVPacket &pkt );
|
||||||
|
void write_audio_packet( AVPacket &pkt );
|
||||||
int writeVideoFramePacket( AVPacket *pkt );
|
int writeVideoFramePacket( AVPacket *pkt );
|
||||||
int writeAudioFramePacket( AVPacket *pkt );
|
int writeAudioFramePacket( AVPacket *pkt );
|
||||||
int writePacket( ZMPacket *pkt );
|
int writePacket( ZMPacket *pkt );
|
||||||
|
|
|
@ -198,14 +198,6 @@ class Event {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createListThumbnail( $overwrite=false ) {
|
function createListThumbnail( $overwrite=false ) {
|
||||||
# Load the frame with the highest score to use as a thumbnail
|
|
||||||
if ( !($frame = dbFetchOne( 'SELECT * FROM Frames WHERE EventId=? AND Score=? ORDER BY FrameId LIMIT 1', NULL, array( $this->{'Id'}, $this->{'MaxScore'} ) )) ) {
|
|
||||||
Error("Unable to find a Frame matching max score " . $this->{'MaxScore'} . ' for event ' . $this->{'Id'} );
|
|
||||||
// FIXME: What if somehow the db frame was lost or score was changed? Should probably try another search for any frame.
|
|
||||||
return( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
$frameId = $frame['FrameId'];
|
|
||||||
|
|
||||||
if ( ZM_WEB_LIST_THUMB_WIDTH ) {
|
if ( ZM_WEB_LIST_THUMB_WIDTH ) {
|
||||||
$thumbWidth = ZM_WEB_LIST_THUMB_WIDTH;
|
$thumbWidth = ZM_WEB_LIST_THUMB_WIDTH;
|
||||||
|
@ -219,12 +211,25 @@ class Event {
|
||||||
Fatal( "No thumbnail width or height specified, please check in Options->Web" );
|
Fatal( "No thumbnail width or height specified, please check in Options->Web" );
|
||||||
}
|
}
|
||||||
|
|
||||||
$imageData = $this->getImageSrc( $frame, $scale, false, $overwrite );
|
$eventPath = $this->Path();
|
||||||
if ( ! $imageData ) {
|
if ( file_exists( $eventPath.'/snapshot.jpg' ) ) {
|
||||||
return ( false );
|
$frame = 'snapshot';
|
||||||
|
$humbData = array('Path'=>$eventPath.'/snapshot.jpg');
|
||||||
|
} else {
|
||||||
|
# Load the frame with the highest score to use as a thumbnail
|
||||||
|
if ( !($frame = dbFetchOne( 'SELECT * FROM Frames WHERE EventId=? AND Score=? ORDER BY FrameId LIMIT 1', NULL, array( $this->{'Id'}, $this->{'MaxScore'} ) )) ) {
|
||||||
|
Error("Unable to find a Frame matching max score " . $this->{'MaxScore'} . ' for event ' . $this->{'Id'} );
|
||||||
|
// FIXME: What if somehow the db frame was lost or score was changed? Should probably try another search for any frame.
|
||||||
|
return( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
$imageData = $this->getImageSrc( $frame, $scale, false, $overwrite );
|
||||||
|
if ( ! $imageData ) {
|
||||||
|
return ( false );
|
||||||
|
}
|
||||||
|
$thumbData = $frame;
|
||||||
|
$thumbData['Path'] = $imageData['thumbPath'];
|
||||||
}
|
}
|
||||||
$thumbData = $frame;
|
|
||||||
$thumbData['Path'] = $imageData['thumbPath'];
|
|
||||||
$thumbData['Width'] = (int)$thumbWidth;
|
$thumbData['Width'] = (int)$thumbWidth;
|
||||||
$thumbData['Height'] = (int)$thumbHeight;
|
$thumbData['Height'] = (int)$thumbHeight;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ public $defaults = array(
|
||||||
'AutoArchive' => 0,
|
'AutoArchive' => 0,
|
||||||
'AutoVideo' => 0,
|
'AutoVideo' => 0,
|
||||||
'AutoMessage' => 0,
|
'AutoMessage' => 0,
|
||||||
|
'UpdateDiskSpace' => 0,
|
||||||
'Background' => 0,
|
'Background' => 0,
|
||||||
'Concurrent' => 0,
|
'Concurrent' => 0,
|
||||||
'limit' => 100,
|
'limit' => 100,
|
||||||
|
|
|
@ -148,6 +148,7 @@ Warning("Addterm");
|
||||||
$sql .= ', AutoExecute = '. ( !empty($_REQUEST['filter']['AutoExecute']) ? 1 : 0);
|
$sql .= ', AutoExecute = '. ( !empty($_REQUEST['filter']['AutoExecute']) ? 1 : 0);
|
||||||
$sql .= ', AutoExecuteCmd = '.dbEscape($_REQUEST['filter']['AutoExecuteCmd']);
|
$sql .= ', AutoExecuteCmd = '.dbEscape($_REQUEST['filter']['AutoExecuteCmd']);
|
||||||
$sql .= ', AutoDelete = '. ( !empty($_REQUEST['filter']['AutoDelete']) ? 1 : 0);
|
$sql .= ', AutoDelete = '. ( !empty($_REQUEST['filter']['AutoDelete']) ? 1 : 0);
|
||||||
|
$sql .= ', UpdateDiskSpace = '. ( !empty($_REQUEST['filter']['UpdateDiskSpace']) ? 1 : 0);
|
||||||
$sql .= ', Background = '. ( !empty($_REQUEST['filter']['Background']) ? 1 : 0);
|
$sql .= ', Background = '. ( !empty($_REQUEST['filter']['Background']) ? 1 : 0);
|
||||||
$sql .= ', Concurrent = '. ( !empty($_REQUEST['filter']['Concurrent']) ? 1 : 0);
|
$sql .= ', Concurrent = '. ( !empty($_REQUEST['filter']['Concurrent']) ? 1 : 0);
|
||||||
|
|
||||||
|
|
|
@ -127,10 +127,10 @@ function dbQuery( $sql, $params=NULL ) {
|
||||||
} else {
|
} else {
|
||||||
$result = $dbConn->query( $sql );
|
$result = $dbConn->query( $sql );
|
||||||
}
|
}
|
||||||
//if ( $params )
|
if ( $params )
|
||||||
//Warning("SQL: $sql" . implode(',',$params));
|
Warning("SQL: $sql" . implode(',',$params));
|
||||||
//else
|
else
|
||||||
//Warning("SQL: $sql" );
|
Warning("SQL: $sql" );
|
||||||
} catch(PDOException $e) {
|
} catch(PDOException $e) {
|
||||||
Error( "SQL-ERR '".$e->getMessage()."', statement was '".$sql."' params:" . implode(',',$params) );
|
Error( "SQL-ERR '".$e->getMessage()."', statement was '".$sql."' params:" . implode(',',$params) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -1057,10 +1057,17 @@ function parseSort( $saveToSession=false, $querySep='&' ) {
|
||||||
$sortColumn = 'E.Cause';
|
$sortColumn = 'E.Cause';
|
||||||
break;
|
break;
|
||||||
case 'DateTime' :
|
case 'DateTime' :
|
||||||
$_REQUEST['sort_field'] = 'StartTime';
|
$sortColumn = 'E.StartTime';
|
||||||
|
break;
|
||||||
|
case 'DiskSpace' :
|
||||||
|
$sortColumn = 'E.DiskSpace';
|
||||||
|
break;
|
||||||
case 'StartTime' :
|
case 'StartTime' :
|
||||||
$sortColumn = 'E.StartTime';
|
$sortColumn = 'E.StartTime';
|
||||||
break;
|
break;
|
||||||
|
case 'EndTime' :
|
||||||
|
$sortColumn = 'E.EndTime';
|
||||||
|
break;
|
||||||
case 'Length' :
|
case 'Length' :
|
||||||
$sortColumn = 'E.Length';
|
$sortColumn = 'E.Length';
|
||||||
break;
|
break;
|
||||||
|
@ -1126,6 +1133,7 @@ function parseFilter( &$filter, $saveToSession=false, $querySep='&' ) {
|
||||||
case 'ServerId':
|
case 'ServerId':
|
||||||
$filter['sql'] .= 'M.ServerId';
|
$filter['sql'] .= 'M.ServerId';
|
||||||
break;
|
break;
|
||||||
|
# Unspecified start or end, so assume start, this is to support legacy filters
|
||||||
case 'DateTime':
|
case 'DateTime':
|
||||||
$filter['sql'] .= 'E.StartTime';
|
$filter['sql'] .= 'E.StartTime';
|
||||||
break;
|
break;
|
||||||
|
@ -1138,8 +1146,35 @@ function parseFilter( &$filter, $saveToSession=false, $querySep='&' ) {
|
||||||
case 'Weekday':
|
case 'Weekday':
|
||||||
$filter['sql'] .= 'weekday( E.StartTime )';
|
$filter['sql'] .= 'weekday( E.StartTime )';
|
||||||
break;
|
break;
|
||||||
|
# Starting Time
|
||||||
|
case 'StartDateTime':
|
||||||
|
$filter['sql'] .= 'E.StartTime';
|
||||||
|
break;
|
||||||
|
case 'StartDate':
|
||||||
|
$filter['sql'] .= 'to_days( E.StartTime )';
|
||||||
|
break;
|
||||||
|
case 'StartTime':
|
||||||
|
$filter['sql'] .= 'extract( hour_second from E.StartTime )';
|
||||||
|
break;
|
||||||
|
case 'StartWeekday':
|
||||||
|
$filter['sql'] .= 'weekday( E.StartTime )';
|
||||||
|
break;
|
||||||
|
# Ending Time
|
||||||
|
case 'EndDateTime':
|
||||||
|
$filter['sql'] .= 'E.EndTime';
|
||||||
|
break;
|
||||||
|
case 'EndDate':
|
||||||
|
$filter['sql'] .= 'to_days( E.EndTime )';
|
||||||
|
break;
|
||||||
|
case 'EndTime':
|
||||||
|
$filter['sql'] .= 'extract( hour_second from E.EndTime )';
|
||||||
|
break;
|
||||||
|
case 'EndWeekday':
|
||||||
|
$filter['sql'] .= 'weekday( E.EndTime )';
|
||||||
|
break;
|
||||||
case 'Id':
|
case 'Id':
|
||||||
case 'Name':
|
case 'Name':
|
||||||
|
case 'DiskSpace':
|
||||||
case 'MonitorId':
|
case 'MonitorId':
|
||||||
case 'StorageId':
|
case 'StorageId':
|
||||||
case 'Length':
|
case 'Length':
|
||||||
|
@ -1853,7 +1888,8 @@ function logState() {
|
||||||
Logger::WARNING => array( ZM_LOG_ALERT_WAR_COUNT, ZM_LOG_ALARM_WAR_COUNT ),
|
Logger::WARNING => array( ZM_LOG_ALERT_WAR_COUNT, ZM_LOG_ALARM_WAR_COUNT ),
|
||||||
);
|
);
|
||||||
|
|
||||||
$sql = "select Level, count(Level) as LevelCount from Logs where Level < ".Logger::INFO." and TimeKey > unix_timestamp(now() - interval ".ZM_LOG_CHECK_PERIOD." second) group by Level order by Level asc";
|
# This is an expensive request, as it has to hit every row of the Logs Table
|
||||||
|
$sql = 'SELECT Level, COUNT(Level) AS LevelCount FROM Logs WHERE Level < '.Logger::INFO.' AND TimeKey > unix_timestamp(now() - interval '.ZM_LOG_CHECK_PERIOD.' second) GROUP BY Level ORDER BY Level ASC';
|
||||||
$counts = dbFetchAll( $sql );
|
$counts = dbFetchAll( $sql );
|
||||||
|
|
||||||
foreach ( $counts as $count ) {
|
foreach ( $counts as $count ) {
|
||||||
|
|
|
@ -116,8 +116,11 @@ $SLANG = array(
|
||||||
'AttrArchiveStatus' => 'Archive Status',
|
'AttrArchiveStatus' => 'Archive Status',
|
||||||
'AttrAvgScore' => 'Avg. Score',
|
'AttrAvgScore' => 'Avg. Score',
|
||||||
'AttrCause' => 'Cause',
|
'AttrCause' => 'Cause',
|
||||||
'AttrDate' => 'Date',
|
'AttrStartDate' => 'Start Date',
|
||||||
'AttrDateTime' => 'Date/Time',
|
'AttrEndDate' => 'End Date',
|
||||||
|
'AttrStartDateTime' => 'Start Date/Time',
|
||||||
|
'AttrEndDateTime' => 'End Date/Time',
|
||||||
|
'AttrDiskSpace' => 'Disk Space',
|
||||||
'AttrDiskBlocks' => 'Disk Blocks',
|
'AttrDiskBlocks' => 'Disk Blocks',
|
||||||
'AttrDiskPercent' => 'Disk Percent',
|
'AttrDiskPercent' => 'Disk Percent',
|
||||||
'AttrDuration' => 'Duration',
|
'AttrDuration' => 'Duration',
|
||||||
|
@ -132,9 +135,11 @@ $SLANG = array(
|
||||||
'AttrName' => 'Name',
|
'AttrName' => 'Name',
|
||||||
'AttrNotes' => 'Notes',
|
'AttrNotes' => 'Notes',
|
||||||
'AttrSystemLoad' => 'System Load',
|
'AttrSystemLoad' => 'System Load',
|
||||||
'AttrTime' => 'Time',
|
'AttrStartTime' => 'Start Time',
|
||||||
|
'AttrEndTime' => 'End Time',
|
||||||
'AttrTotalScore' => 'Total Score',
|
'AttrTotalScore' => 'Total Score',
|
||||||
'AttrWeekday' => 'Weekday',
|
'AttrStartWeekday' => 'Start Weekday',
|
||||||
|
'AttrEndWeekday' => 'End Weekday',
|
||||||
'Auto' => 'Auto',
|
'Auto' => 'Auto',
|
||||||
'AutoStopTimeout' => 'Auto Stop Timeout',
|
'AutoStopTimeout' => 'Auto Stop Timeout',
|
||||||
'Available' => 'Available',
|
'Available' => 'Available',
|
||||||
|
@ -334,6 +339,7 @@ $SLANG = array(
|
||||||
'Ffmpeg' => 'Ffmpeg',
|
'Ffmpeg' => 'Ffmpeg',
|
||||||
'File' => 'File',
|
'File' => 'File',
|
||||||
'FilterArchiveEvents' => 'Archive all matches',
|
'FilterArchiveEvents' => 'Archive all matches',
|
||||||
|
'FilterUpdateDiskSpace' => 'Update used disk space',
|
||||||
'FilterDeleteEvents' => 'Delete all matches',
|
'FilterDeleteEvents' => 'Delete all matches',
|
||||||
'FilterEmailEvents' => 'Email details of all matches',
|
'FilterEmailEvents' => 'Email details of all matches',
|
||||||
'FilterExecuteEvents' => 'Execute command on all matches',
|
'FilterExecuteEvents' => 'Execute command on all matches',
|
||||||
|
@ -413,6 +419,7 @@ $SLANG = array(
|
||||||
'LimitResultsPre' => 'Limit to first', // This is used at the beginning of the phrase 'Limit to first N results only'
|
'LimitResultsPre' => 'Limit to first', // This is used at the beginning of the phrase 'Limit to first N results only'
|
||||||
'LinkedMonitors' => 'Linked Monitors',
|
'LinkedMonitors' => 'Linked Monitors',
|
||||||
'List' => 'List',
|
'List' => 'List',
|
||||||
|
'ListMatches' => 'List Matches',
|
||||||
'Load' => 'Load',
|
'Load' => 'Load',
|
||||||
'Local' => 'Local',
|
'Local' => 'Local',
|
||||||
'Log' => 'Log',
|
'Log' => 'Log',
|
||||||
|
|
|
@ -194,9 +194,19 @@ ZoneMinder requires Javascript. Please enable Javascript in your browser for thi
|
||||||
<li><a href="?view=console"><?php echo translate('Console') ?></a></li>
|
<li><a href="?view=console"><?php echo translate('Console') ?></a></li>
|
||||||
<?php if ( canView( 'System' ) ) { ?>
|
<?php if ( canView( 'System' ) ) { ?>
|
||||||
<li><a href="?view=options"><?php echo translate('Options') ?></a></li>
|
<li><a href="?view=options"><?php echo translate('Options') ?></a></li>
|
||||||
<li><?php if ( logToDatabase() > Logger::NOLOG ) { ?> <?php echo makePopupLink( '?view=log', 'zmLog', 'log', '<span class="'.logState().'">'.translate('Log').'</span>' ) ?><?php } ?></li>
|
<li>
|
||||||
<?php } ?>
|
<?php
|
||||||
<?php if ( ZM_OPT_X10 && canView( 'Devices' ) ) { ?>
|
if ( logToDatabase() > Logger::NOLOG ) {
|
||||||
|
if ( ! ZM_RUN_AUDIT ) {
|
||||||
|
# zmaudit can clean the logs, but if we aren't running it, then we should clecan them regularly
|
||||||
|
dbQuery("DELETE FROM Logs WHERE TimeKey < NOW()-to_days('".ZM_LOG_DATABASE_LIMIT."')");
|
||||||
|
}
|
||||||
|
echo makePopupLink( '?view=log', 'zmLog', 'log', '<span class="'.logState().'">'.translate('Log').'</span>' );
|
||||||
|
}
|
||||||
|
} // end if canview(System)
|
||||||
|
?></li>
|
||||||
|
<?php
|
||||||
|
if ( ZM_OPT_X10 && canView( 'Devices' ) ) { ?>
|
||||||
<li><a href="?view=devices">Devices</a></li>
|
<li><a href="?view=devices">Devices</a></li>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
<li><a href="?view=groups"<?php echo $view=='groups'?' class="selected"':''?>><?php echo translate('Groups') ?></a></li>
|
<li><a href="?view=groups"<?php echo $view=='groups'?' class="selected"':''?>><?php echo translate('Groups') ?></a></li>
|
||||||
|
|
|
@ -258,6 +258,17 @@ echo htmlSelect( 'StorageFilter', array(''=>'All')+$StorageById, (isset($_SESSIO
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
<span class="StatusFilter"><label><?php echo translate('Status')?>:</label>
|
||||||
|
<?php
|
||||||
|
$status_options = array(
|
||||||
|
''=>'All',
|
||||||
|
'Unknown' => translate('Unknown'),
|
||||||
|
'NotRunning' => translate('NotRunning'),
|
||||||
|
'Running' => translate('Running'),
|
||||||
|
);
|
||||||
|
echo htmlSelect( 'StatusFilter', $status_options, ( isset($_SESSION['StatusFilter']) ? $_SESSION['StatusFilter'] : '' ), array('onchange'=>'changeFilter(this);') );
|
||||||
|
?>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
|
|
|
@ -69,10 +69,14 @@ $attrTypes = array(
|
||||||
'Name' => translate('AttrName'),
|
'Name' => translate('AttrName'),
|
||||||
'Cause' => translate('AttrCause'),
|
'Cause' => translate('AttrCause'),
|
||||||
'Notes' => translate('AttrNotes'),
|
'Notes' => translate('AttrNotes'),
|
||||||
'DateTime' => translate('AttrDateTime'),
|
'StartDateTime' => translate('AttrStartDateTime'),
|
||||||
'Date' => translate('AttrDate'),
|
'StartDate' => translate('AttrStartDate'),
|
||||||
'Time' => translate('AttrTime'),
|
'StartTime' => translate('AttrStartTime'),
|
||||||
'Weekday' => translate('AttrWeekday'),
|
'StartWeekday' => translate('AttrStartWeekday'),
|
||||||
|
'EndDateTime' => translate('AttrEndDateTime'),
|
||||||
|
'EndDate' => translate('AttrEndDate'),
|
||||||
|
'EndTime' => translate('AttrEndTime'),
|
||||||
|
'EndWeekday' => translate('AttrEndWeekday'),
|
||||||
'Length' => translate('AttrDuration'),
|
'Length' => translate('AttrDuration'),
|
||||||
'Frames' => translate('AttrFrames'),
|
'Frames' => translate('AttrFrames'),
|
||||||
'AlarmFrames' => translate('AttrAlarmFrames'),
|
'AlarmFrames' => translate('AttrAlarmFrames'),
|
||||||
|
@ -80,8 +84,9 @@ $attrTypes = array(
|
||||||
'AvgScore' => translate('AttrAvgScore'),
|
'AvgScore' => translate('AttrAvgScore'),
|
||||||
'MaxScore' => translate('AttrMaxScore'),
|
'MaxScore' => translate('AttrMaxScore'),
|
||||||
'Archived' => translate('AttrArchiveStatus'),
|
'Archived' => translate('AttrArchiveStatus'),
|
||||||
'DiskPercent' => translate('AttrDiskPercent'),
|
|
||||||
'DiskBlocks' => translate('AttrDiskBlocks'),
|
'DiskBlocks' => translate('AttrDiskBlocks'),
|
||||||
|
'DiskPercent' => translate('AttrDiskPercent'),
|
||||||
|
'DiskSpace' => translate('AttrDiskSpace'),
|
||||||
'SystemLoad' => translate('AttrSystemLoad'),
|
'SystemLoad' => translate('AttrSystemLoad'),
|
||||||
'StorageId' => translate('AttrStorageArea'),
|
'StorageId' => translate('AttrStorageArea'),
|
||||||
'ServerId' => translate('AttrServer'),
|
'ServerId' => translate('AttrServer'),
|
||||||
|
@ -297,18 +302,19 @@ if ( count($terms) == 0 ) {
|
||||||
<label for="filter[Query][sort_field]"><?php echo translate('SortBy') ?></label>
|
<label for="filter[Query][sort_field]"><?php echo translate('SortBy') ?></label>
|
||||||
<?php
|
<?php
|
||||||
$sort_fields = array(
|
$sort_fields = array(
|
||||||
'Id' => translate('AttrId'),
|
'Id' => translate('AttrId'),
|
||||||
'Name' => translate('AttrName'),
|
'Name' => translate('AttrName'),
|
||||||
'Cause' => translate('AttrCause'),
|
'Cause' => translate('AttrCause'),
|
||||||
'Notes' => translate('AttrNotes'),
|
'DiskSpace' => translate('AttrDiskSpace'),
|
||||||
'MonitorName' => translate('AttrMonitorName'),
|
'Notes' => translate('AttrNotes'),
|
||||||
'DateTime' => translate('AttrDateTime'),
|
'MonitorName' => translate('AttrMonitorName'),
|
||||||
'Length' => translate('AttrDuration'),
|
'StartDateTime' => translate('AttrStartDateTime'),
|
||||||
'Frames' => translate('AttrFrames'),
|
'Length' => translate('AttrDuration'),
|
||||||
'AlarmFrames' => translate('AttrAlarmFrames'),
|
'Frames' => translate('AttrFrames'),
|
||||||
'TotScore' => translate('AttrTotalScore'),
|
'AlarmFrames' => translate('AttrAlarmFrames'),
|
||||||
'AvgScore' => translate('AttrAvgScore'),
|
'TotScore' => translate('AttrTotalScore'),
|
||||||
'MaxScore' => translate('AttrMaxScore'),
|
'AvgScore' => translate('AttrAvgScore'),
|
||||||
|
'MaxScore' => translate('AttrMaxScore'),
|
||||||
);
|
);
|
||||||
echo htmlSelect( 'filter[Query][sort_field]', $sort_fields, $filter->sort_field() );
|
echo htmlSelect( 'filter[Query][sort_field]', $sort_fields, $filter->sort_field() );
|
||||||
$sort_dirns = array(
|
$sort_dirns = array(
|
||||||
|
@ -332,6 +338,9 @@ echo htmlSelect( 'filter[Query][sort_asc]', $sort_dirns, $filter->sort_asc() );
|
||||||
<label><?php echo translate('FilterArchiveEvents') ?></label>
|
<label><?php echo translate('FilterArchiveEvents') ?></label>
|
||||||
<input type="checkbox" name="filter[AutoArchive]" value="1"<?php if ( !empty($filter->AutoArchive()) ) { ?> checked="checked"<?php } ?> onclick="updateButtons( this )"/>
|
<input type="checkbox" name="filter[AutoArchive]" value="1"<?php if ( !empty($filter->AutoArchive()) ) { ?> checked="checked"<?php } ?> onclick="updateButtons( this )"/>
|
||||||
</p>
|
</p>
|
||||||
|
<p><label><?php echo translate('FilterUpdateDiskSpace') ?></label>
|
||||||
|
<input type="checkbox" name="filter[UpdateDiskSpace]" value="1"<?php echo empty($filter->UpdateDiskSpace()) ? '' : ' checked="checked"' ?> onclick="updateButtons(this);"/>
|
||||||
|
</p>
|
||||||
<?php
|
<?php
|
||||||
if ( ZM_OPT_FFMPEG ) {
|
if ( ZM_OPT_FFMPEG ) {
|
||||||
?>
|
?>
|
||||||
|
@ -387,7 +396,7 @@ if ( ZM_OPT_MESSAGE ) {
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div id="contentButtons">
|
<div id="contentButtons">
|
||||||
<input type="submit" value="<?php echo translate('Submit') ?>" onclick="submitToEvents( this );"/>
|
<input type="submit" value="<?php echo translate('ListMatches') ?>" onclick="submitToEvents( this );"/>
|
||||||
<input type="button" name="executeButton" id="executeButton" value="<?php echo translate('Execute') ?>" onclick="executeFilter( this );"/>
|
<input type="button" name="executeButton" id="executeButton" value="<?php echo translate('Execute') ?>" onclick="executeFilter( this );"/>
|
||||||
<?php
|
<?php
|
||||||
if ( canEdit( 'Events' ) ) {
|
if ( canEdit( 'Events' ) ) {
|
||||||
|
|
|
@ -19,6 +19,8 @@ function updateButtons( element ) {
|
||||||
canExecute = true;
|
canExecute = true;
|
||||||
else if ( form.elements['filter[AutoDelete]'].checked )
|
else if ( form.elements['filter[AutoDelete]'].checked )
|
||||||
canExecute = true;
|
canExecute = true;
|
||||||
|
else if ( form.elements['filter[UpdateDiskSpace]'].checked )
|
||||||
|
canExecute = true;
|
||||||
form.elements['executeButton'].disabled = !canExecute;
|
form.elements['executeButton'].disabled = !canExecute;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ function validateForm( form ) {
|
||||||
else if ( form.elements.mid.value == 0 && monitorNames[form.elements['newMonitor[Name]'].value] )
|
else if ( form.elements.mid.value == 0 && monitorNames[form.elements['newMonitor[Name]'].value] )
|
||||||
errors[errors.length] = "<?php echo translate('DuplicateMonitorName') ?>";
|
errors[errors.length] = "<?php echo translate('DuplicateMonitorName') ?>";
|
||||||
|
|
||||||
if ( form.elements['newMonitor[AnalysisFPS]'].value && !(parseFloat(form.elements['newMonitor[AnalysisFPS]'].value) > 0 ) )
|
if ( form.elements['newMonitor[AnalysisFPSLimit]'].value && !(parseFloat(form.elements['newMonitor[AnalysisFPSLimit]'].value) > 0 ) )
|
||||||
errors[errors.length] = "<?php echo translate('BadAnalysisFPS') ?>";
|
errors[errors.length] = "<?php echo translate('BadAnalysisFPS') ?>";
|
||||||
if ( form.elements['newMonitor[MaxFPS]'].value && !(parseFloat(form.elements['newMonitor[MaxFPS]'].value) > 0 ) )
|
if ( form.elements['newMonitor[MaxFPS]'].value && !(parseFloat(form.elements['newMonitor[MaxFPS]'].value) > 0 ) )
|
||||||
errors[errors.length] = "<?php echo translate('BadMaxFPS') ?>";
|
errors[errors.length] = "<?php echo translate('BadMaxFPS') ?>";
|
||||||
|
|
|
@ -84,7 +84,11 @@ function imagedone( obj, monId, success ) {
|
||||||
if ( ! success ) {
|
if ( ! success ) {
|
||||||
// if we had a failrue queue up the no-data image
|
// if we had a failrue queue up the no-data image
|
||||||
//loadImage2Monitor(monId,"no data"); // leave the staged URL if there is one, just ignore it here.
|
//loadImage2Monitor(monId,"no data"); // leave the staged URL if there is one, just ignore it here.
|
||||||
loadNoData( monId );
|
if ( liveMode ) {
|
||||||
|
writeText( monId, "Camera Offline" );
|
||||||
|
} else {
|
||||||
|
writeText( monId, "No Data" );
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if ( monitorLoadingStageURL[monId] == "" ) {
|
if ( monitorLoadingStageURL[monId] == "" ) {
|
||||||
console.log("Not showing image for " + monId );
|
console.log("Not showing image for " + monId );
|
||||||
|
|
|
@ -122,7 +122,7 @@ if ( ! $monitor ) {
|
||||||
'FrameSkip' => 0,
|
'FrameSkip' => 0,
|
||||||
'MotionFrameSkip' => 0,
|
'MotionFrameSkip' => 0,
|
||||||
'EventPrefix' => 'Event-',
|
'EventPrefix' => 'Event-',
|
||||||
'AnalysisFPS' => '',
|
'AnalysisFPSLimit' => '',
|
||||||
'AnalysisUpdateDelay' => 0,
|
'AnalysisUpdateDelay' => 0,
|
||||||
'MaxFPS' => '',
|
'MaxFPS' => '',
|
||||||
'AlarmMaxFPS' => '',
|
'AlarmMaxFPS' => '',
|
||||||
|
@ -170,8 +170,8 @@ if ( isset( $_REQUEST['newMonitor'] ) ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
# What if it has less zeros? This is not robust code.
|
# What if it has less zeros? This is not robust code.
|
||||||
if ( $monitor->AnalysisFPS() == '0.00' )
|
if ( $monitor->AnalysisFPSLimit() == '0.00' )
|
||||||
$monitor->AnalysisFPS( '' );
|
$monitor->AnalysisFPSLimit( '' );
|
||||||
if ( $monitor->MaxFPS() == '0.00' )
|
if ( $monitor->MaxFPS() == '0.00' )
|
||||||
$monitor->MaxFPS( '' );
|
$monitor->MaxFPS( '' );
|
||||||
if ( $monitor->AlarmMaxFPS() == '0.00' )
|
if ( $monitor->AlarmMaxFPS() == '0.00' )
|
||||||
|
@ -538,7 +538,7 @@ if ( $tab != 'general' ) {
|
||||||
<input type="hidden" name="newMonitor[Enabled]" value="<?php echo validHtmlStr($monitor->Enabled()) ?>"/>
|
<input type="hidden" name="newMonitor[Enabled]" value="<?php echo validHtmlStr($monitor->Enabled()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[RefBlendPerc]" value="<?php echo validHtmlStr($monitor->RefBlendPerc()) ?>"/>
|
<input type="hidden" name="newMonitor[RefBlendPerc]" value="<?php echo validHtmlStr($monitor->RefBlendPerc()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[AlarmRefBlendPerc]" value="<?php echo validHtmlStr($monitor->AlarmRefBlendPerc()) ?>"/>
|
<input type="hidden" name="newMonitor[AlarmRefBlendPerc]" value="<?php echo validHtmlStr($monitor->AlarmRefBlendPerc()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[AnalysisFPS]" value="<?php echo validHtmlStr($monitor->AnalysisFPS()) ?>"/>
|
<input type="hidden" name="newMonitor[AnalysisFPSLimit]" value="<?php echo validHtmlStr($monitor->AnalysisFPSLimit()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[MaxFPS]" value="<?php echo validHtmlStr($monitor->MaxFPS()) ?>"/>
|
<input type="hidden" name="newMonitor[MaxFPS]" value="<?php echo validHtmlStr($monitor->MaxFPS()) ?>"/>
|
||||||
<input type="hidden" name="newMonitor[AlarmMaxFPS]" value="<?php echo validHtmlStr($monitor->AlarmMaxFPS()) ?>"/>
|
<input type="hidden" name="newMonitor[AlarmMaxFPS]" value="<?php echo validHtmlStr($monitor->AlarmMaxFPS()) ?>"/>
|
||||||
<?php
|
<?php
|
||||||
|
@ -727,7 +727,7 @@ switch ( $tab ) {
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr><td><?php echo translate('AnalysisFPS') ?></td><td><input type="text" name="newMonitor[AnalysisFPS]" value="<?php echo validHtmlStr($monitor->AnalysisFPS()) ?>" size="6"/></td></tr>
|
<tr><td><?php echo translate('AnalysisFPS') ?></td><td><input type="text" name="newMonitor[AnalysisFPSLimit]" value="<?php echo validHtmlStr($monitor->AnalysisFPSLimit()) ?>" size="6"/></td></tr>
|
||||||
<?php
|
<?php
|
||||||
if ( $monitor->Type() != 'Local' && $monitor->Type() != 'File' && $monitor->Type() != 'NVSocket' ) {
|
if ( $monitor->Type() != 'Local' && $monitor->Type() != 'File' && $monitor->Type() != 'NVSocket' ) {
|
||||||
?>
|
?>
|
||||||
|
|
Loading…
Reference in New Issue