Merge branch 'smarter_packetqueue' into storageareas

This commit is contained in:
Isaac Connor 2018-10-15 10:59:50 -04:00
commit 7149576a7c
18 changed files with 577 additions and 97 deletions

View File

@ -781,6 +781,7 @@ INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,1
INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'PSIA','Remote','PSIA',0,0,0,1,0,0,1,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,-100,100,0,0,1,0,0,0,0,1,-100,100,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Dahua','Remote','Dahua',0,0,0,1,0,0,1,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,0,0,0,0);
--

View File

@ -0,0 +1,365 @@
package ZoneMinder::Control::PSIA;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Control;
our @ISA = qw(ZoneMinder::Control);
our $REALM = 'TV-IP450PI';
our $USERNAME = 'admin';
our $PASSWORD = '';
our $ADDRESS = '';
our $PROTOCOL = 'http://';
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Config qw(:all);
use ZoneMinder::Database qw(zmDbConnect);
sub open
{
my $self = shift;
$self->loadMonitor();
if ( ( $self->{Monitor}->{ControlAddress} =~ /^(?<PROTOCOL>https?:\/\/)?(?<USERNAME>[^:@]+)?:?(?<PASSWORD>[^\/@]+)?@?(?<ADDRESS>.*)$/ ) ) {
$PROTOCOL = $+{PROTOCOL} if $+{PROTOCOL};
$USERNAME = $+{USERNAME} if $+{USERNAME};
$PASSWORD = $+{PASSWORD} if $+{PASSWORD};
$ADDRESS = $+{ADDRESS} if $+{ADDRESS};
} else {
Error('Failed to parse auth from address ' . $self->{Monitor}->{ControlAddress});
$ADDRESS = $self->{Monitor}->{ControlAddress};
}
if ( !($ADDRESS =~ /:/) ) {
Error('You generally need to also specify the port. I will append :80');
$ADDRESS .= ':80';
}
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".$ZoneMinder::Base::ZM_VERSION );
$self->{state} = 'closed';
Debug( "sendCmd credentials control address:'".$ADDRESS
."' realm:'" . $REALM
. "' username:'" . $USERNAME
. "' password:'".$PASSWORD
."'"
);
$self->{ua}->credentials($ADDRESS, $REALM, $USERNAME, $PASSWORD);
# Detect REALM
my $req = HTTP::Request->new(GET=>$PROTOCOL . $ADDRESS . "/PSIA/PTZ/channels");
my $res = $self->{ua}->request($req);
if ($res->is_success) {
$self->{state} = 'open';
return;
} elsif (! $res->is_success) {
Debug("Need newer REALM");
if ( $res->status_line() eq '401 Unauthorized' ) {
my $headers = $res->headers();
foreach my $k ( keys %$headers ) {
Debug("Initial Header $k => $$headers{$k}");
} # end foreach
if ( $$headers{'www-authenticate'} ) {
my ($auth, $tokens) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
if ($tokens =~ /\w+="([^"]+)"/i) {
$REALM = $1;
Debug("Changing REALM to $REALM");
$self->{ua}->credentials($ADDRESS, $REALM, $USERNAME, $PASSWORD);
} # end if
} else {
Debug("No WWW-Authenticate header");
} # end if www-authenticate header
} # end if $res->status_line() eq '401 Unauthorized'
} # end elsif ! $res->is_success
}
sub close
{
my $self = shift;
$self->{state} = 'closed';
}
sub printMsg
{
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
}
sub sendGetRequest {
my $self = shift;
my $url_path = shift;
my $result = undef;
my $url = $PROTOCOL . $ADDRESS . $url_path;
my $req = HTTP::Request->new(GET=>$url);
my $res = $self->{ua}->request($req);
if ($res->is_success) {
$result = !undef;
} else {
if ( $res->status_line() eq '401 Unauthorized' ) {
Error( "Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
Error("Content was " . $res->content() );
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
$result = !undef;
} else {
Error("Content was " . $res->content() );
}
}
if ( ! $result ) {
Error("Error check failed: '".$res->status_line());
}
}
return($result);
}
sub sendPutRequest {
my $self = shift;
my $url_path = shift;
my $content = shift;
my $result = undef;
my $url = $PROTOCOL . $ADDRESS . $url_path;
my $req = HTTP::Request->new(PUT=>$url);
if(defined($content)) {
$req->content_type("application/x-www-form-urlencoded; charset=UTF-8");
$req->content('<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $content);
}
my $res = $self->{ua}->request($req);
if ($res->is_success) {
$result = !undef;
} else {
if ( $res->status_line() eq '401 Unauthorized' ) {
Error( "Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
Error("Content was " . $res->content() );
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
$result = !undef;
} else {
Error("Content was " . $res->content() );
}
}
if ( ! $result ) {
Error( "Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" );
}
}
return($result);
}
sub sendDeleteRequest {
my $self = shift;
my $url_path = shift;
my $result = undef;
my $url = $PROTOCOL . $ADDRESS . $url_path;
my $req = HTTP::Request->new(DELETE=>$url);
my $res = $self->{ua}->request($req);
if ($res->is_success) {
$result = !undef;
} else {
if ( $res->status_line() eq '401 Unauthorized' ) {
Error( "Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD );
Error("Content was " . $res->content() );
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
$result = !undef;
} else {
Error("Content was " . $res->content() );
}
}
if ( ! $result ) {
Error( "Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" );
}
}
return($result);
}
sub move
{
my $self = shift;
my $panPercentage = shift;
my $tiltPercentage = shift;
my $zoomPercentage = shift;
my $cmd = "set_relative_pos&posX=$panSteps&posY=$tiltSteps";
my $ptzdata = '<PTZData version="1.0" xmlns="urn:psialliance-org">';
$ptzdata .= '<pan>' . $panPercentage . '</pan>';
$ptzdata .= '<tilt>' . $tiltPercentage . '</tilt>';
$ptzdata .= '<zoom>' . $zoomPercentage . '</zoom>';
$ptzdata .= '<Momentary><duration>500</duration></Momentary>';
$ptzdata .= '</PTZData>';
$self->sendPutRequest("/PSIA/PTZ/channels/1/momentary", $ptzdata);
}
sub moveRelUpLeft
{
my $self = shift;
Debug( "Move Up Left" );
$self->move(-50, 50, 0);
}
sub moveRelUp
{
my $self = shift;
Debug( "Move Up" );
$self->move(0, 50, 0);
}
sub moveRelUpRight
{
my $self = shift;
Debug( "Move Up Right" );
$self->move(50, 50, 0);
}
sub moveRelLeft
{
my $self = shift;
Debug( "Move Left" );
$self->move(-50, 0, 0);
}
sub moveRelRight
{
my $self = shift;
Debug( "Move Right" );
$self->move(50, 0, 0);
}
sub moveRelDownLeft
{
my $self = shift;
Debug( "Move Down Left" );
$self->move(-50, -50, 0);
}
sub moveRelDown
{
my $self = shift;
Debug( "Move Down" );
$self->move(0, -50, 0);
}
sub moveRelDownRight
{
my $self = shift;
Debug( "Move Down Right" );
$self->move(50, -50, 0);
}
sub zoomRelTele
{
my $self = shift;
Debug("Zoom Relative Tele");
$self->move(0, 0, 50);
}
sub zoomRelWide
{
my $self = shift;
Debug("Zoom Relative Wide");
$self->move(0, 0, -50);
}
sub presetClear
{
my $self = shift;
my $params = shift;
my $preset_id = $self->getParam($params, 'preset');
my $url_path = "/PSIA/PTZ/channels/1/presets/" . $preset_id;
$self->sendDeleteRequest($url_path);
}
sub presetSet
{
my $self = shift;
my $params = shift;
my $preset_id = $self->getParam($params, 'preset');
my $dbh = zmDbConnect(1);
my $sql = 'SELECT * FROM ControlPresets WHERE MonitorId = ? AND Preset = ?';
my $sth = $dbh->prepare($sql)
or Fatal("Can't prepare sql '$sql': " . $dbh->errstr());
my $res = $sth->execute($self->{Monitor}->{Id}, $preset_id)
or Fatal("Can't execute sql '$sql': " . $sth->errstr());
my $control_preset_row = $sth->fetchrow_hashref();
my $new_label_name = $control_preset_row->{'Label'};
my $url_path = "/PSIA/PTZ/channels/1/presets/" . $preset_id;
my $ptz_preset_data = '<PTZPreset>';
$ptz_preset_data .= '<id>' . $preset_id . '</id>';
$ptz_preset_data .= '<presetName>' . $new_label_name . '</presetName>';
$ptz_preset_data .= '</PTZPreset>';
$self->sendPutRequest($url_path, $ptz_preset_data);
}
sub presetGoto
{
my $self = shift;
my $params = shift;
my $preset_id = $self->getParam($params, 'preset');
my $url_path = '/PSIA/PTZ/channels/1/presets/' . $preset_id . '/goto';
$self->sendPutRequest($url_path);
}
1;
__END__
=head1 NAME
ZoneMinder::Control::PSIA - Perl module for cameras implementing the PSIA
(Physical Security Interoperability Alliance), IP Media Devices API
specification
=head1 SYNOPSIS
use ZoneMinder::Control::PSIA;
place this in /usr/share/perl5/ZoneMinder/Control
=head1 DESCRIPTION
This has so far been tested with:
- Trendnet TV-IP450PI
=head2 EXPORT
None by default.
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2018 ZoneMinder LLC
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
=cut

View File

@ -162,7 +162,7 @@ my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL};
my $event_id = 0;
if ( ! EVENT_PATH ) {
Error("No event path defined. Config was $Config{ZM_DIR_EVENTS}\n");
Error("No event path defined. Config was $Config{ZM_DIR_EVENTS}");
die;
}
@ -173,11 +173,11 @@ chdir( EVENT_PATH );
my $dbh = zmDbConnect();
if ( $filter_name ) {
Info("Scanning for events using filter '$filter_name'\n");
Info("Scanning for events using filter '$filter_name'");
} elsif ( $filter_id ) {
Info("Scanning for events using filter id '$filter_id'\n");
Info("Scanning for events using filter id '$filter_id'");
} else {
Info("Scanning for events using all filters\n");
Info("Scanning for events using all filters");
}
if ( ! ( $filter_name or $filter_id ) ) {
@ -191,7 +191,7 @@ my $last_action = 0;
while( !$zm_terminate ) {
my $now = time;
if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) {
Debug("Reloading filters\n");
Debug("Reloading filters");
$last_action = $now;
@filters = getFilters({ Name=>$filter_name, Id=>$filter_id });
}
@ -211,7 +211,7 @@ while( !$zm_terminate ) {
last if (!$daemon and ($filter_name or $filter_id)) or $zm_terminate;
Debug("Sleeping for $delay seconds\n");
Debug("Sleeping for $delay seconds");
sleep($delay);
}
@ -405,10 +405,10 @@ sub generateVideo {
chomp($output);
my $status = $? >> 8;
if ( $status || logDebugging() ) {
Debug("Output: $output\n");
Debug("Output: $output");
}
if ( $status ) {
Error("Video generation '$command' failed with status: $status\n");
Error("Video generation '$command' failed with status: $status");
if ( wantarray() ) {
return( undef, undef );
}
@ -453,7 +453,7 @@ sub generateImage {
chomp( $output );
my $status = $? >> 8;
if ( $status || logDebugging() ) {
Debug("Output: $output\n");
Debug("Output: $output");
}
if ( $status ) {
Error("Failed $command status $status");
@ -479,10 +479,9 @@ sub uploadArchFile {
.'/'
.(
( $Config{ZM_UPLOAD_ARCH_ANALYSE} )
? '{*analyse,*capture}'
: '*capture'
? '{*analyse.jpg,*capture.jpg,snapshot.jpg,*.mp4}'
: '*capture.jpg,snapshot.jpg,*.mp4'
)
.'.jpg'
;
my @archImageFiles = glob($archImagePath);
my $archLocPath;
@ -492,11 +491,11 @@ sub uploadArchFile {
$archFile .= '.zip';
$archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile;
my $zip = Archive::Zip->new();
Info("Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n");
Info("Creating upload file '$archLocPath', ".int(@archImageFiles).' files');
my $status = &AZ_OK;
foreach my $imageFile ( @archImageFiles ) {
Debug("Adding $imageFile\n");
Debug("Adding $imageFile");
my $member = $zip->addFile($imageFile);
if ( !$member ) {
Error("Unable toto add image file $imageFile to zip archive $archLocPath");
@ -524,7 +523,7 @@ sub uploadArchFile {
$archFile .= '.tar';
}
$archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile;
Info("Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n");
Info("Creating upload file '$archLocPath', ".int(@archImageFiles).' files');
if ( $archError = !Archive::Tar->create_archive(
$archLocPath,

View File

@ -755,6 +755,12 @@ void EventStream::runStream() {
// commands may set send_frame to true
while(checkCommandQueue());
// Update modified time of the socket .lock file so that we can tell which ones are stale.
if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) {
touch(sock_path_lock);
last_comm_update = now;
}
if ( step != 0 )
curr_frame_id += step;

View File

@ -82,8 +82,35 @@ static AVPixelFormat get_format(AVCodecContext *avctx, const enum AVPixelFormat
}
#endif
FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ),
FfmpegCamera::FfmpegCamera(
int p_id,
const std::string &p_path,
const std::string &p_method,
const std::string &p_options,
int p_width,
int p_height,
int p_colours,
int p_brightness,
int p_contrast,
int p_hue,
int p_colour,
bool p_capture,
bool p_record_audio
) :
Camera(
p_id,
FFMPEG_SRC,
p_width,
p_height,
p_colours,
ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours),
p_brightness,
p_contrast,
p_hue,
p_colour,
p_capture,
p_record_audio
),
mPath( p_path ),
mMethod( p_method ),
mOptions( p_options )
@ -113,6 +140,7 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
videoStore = NULL;
video_last_pts = 0;
have_video_keyframe = false;
packetqueue = NULL;
#if HAVE_LIBSWSCALE
mConvertContext = NULL;
@ -151,12 +179,12 @@ void FfmpegCamera::Terminate() {
int FfmpegCamera::PrimeCapture() {
if ( mCanCapture ) {
Info( "Priming capture from %s, CLosing", mPath.c_str() );
Info("Priming capture from %s, Closing", mPath.c_str());
Close();
}
mVideoStreamId = -1;
mAudioStreamId = -1;
Info( "Priming capture from %s", mPath.c_str() );
Info("Priming capture from %s", mPath.c_str());
return OpenFfmpeg();
}
@ -166,7 +194,7 @@ int FfmpegCamera::PreCapture() {
if ( ! mCanCapture )
return OpenFfmpeg();
// Nothing to do here
return( 0 );
return 0;
}
int FfmpegCamera::Capture( Image &image ) {
@ -433,6 +461,7 @@ int FfmpegCamera::OpenFfmpeg() {
Debug(3, "Found video stream at index %d", mVideoStreamId);
Debug(3, "Found audio stream at index %d", mAudioStreamId);
packetqueue = new zm_packetqueue( mVideoStreamId > mAudioStreamId ? mVideoStreamId : mAudioStreamId );
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
mVideoCodecContext = avcodec_alloc_context3(NULL);
@ -678,6 +707,10 @@ int FfmpegCamera::Close() {
delete videoStore;
videoStore = NULL;
}
if ( packetqueue ) {
delete packetqueue;
packetqueue = NULL;
}
return 0;
} // end FfmpegCamera::Close
@ -795,14 +828,14 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
ZMPacket *queued_packet;
// Clear all packets that predate the moment when the recording began
packetqueue.clear_unwanted_packets( &recording, mVideoStreamId );
packetqueue->clear_unwanted_packets( &recording, mVideoStreamId );
while ( ( queued_packet = packetqueue.popPacket() ) ) {
while ( ( queued_packet = packetqueue->popPacket() ) ) {
AVPacket *avp = queued_packet->av_packet();
packet_count += 1;
//Write the packet to our video store
Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, packetqueue.size() );
Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, packetqueue->size() );
if ( avp->stream_index == mVideoStreamId ) {
ret = videoStore->writeVideoFramePacket( avp );
have_video_keyframe = true;
@ -836,18 +869,18 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event
if ( packet.stream_index == mVideoStreamId ) {
if ( keyframe ) {
Debug(3, "Clearing queue");
packetqueue.clearQueue(monitor->GetPreEventCount(), mVideoStreamId);
packetqueue.queuePacket(&packet);
} else if ( packetqueue.size() ) {
packetqueue->clearQueue(monitor->GetPreEventCount(), mVideoStreamId);
packetqueue->queuePacket(&packet);
} else if ( packetqueue->size() ) {
// it's a keyframe or we already have something in the queue
packetqueue.queuePacket(&packet);
packetqueue->queuePacket(&packet);
}
} else if ( packet.stream_index == mAudioStreamId ) {
// The following lines should ensure that the queue always begins with a video keyframe
//Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", record_audio, packetqueue.size() );
if ( record_audio && packetqueue.size() ) {
if ( record_audio && packetqueue->size() ) {
// if it's audio, and we are doing audio, and there is already something in the queue
packetqueue.queuePacket(&packet);
packetqueue->queuePacket(&packet);
}
}
} // end if recording or not

View File

@ -79,7 +79,7 @@ class FfmpegCamera : public Camera {
#endif // HAVE_LIBAVFORMAT
VideoStore *videoStore;
zm_packetqueue packetqueue;
zm_packetqueue *packetqueue;
bool have_video_keyframe;
#if HAVE_LIBSWSCALE

View File

@ -531,6 +531,12 @@ void MonitorStream::runStream() {
Debug(2, "Have checking command Queue for connkey: %d", connkey );
got_command = true;
}
// Update modified time of the socket .lock file so that we can tell which ones are stale.
if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) {
touch(sock_path_lock);
last_comm_update = now;
}
}
if ( paused ) {

View File

@ -21,27 +21,31 @@
#include "zm_ffmpeg.h"
#include <sys/time.h>
#define VIDEO_QUEUESIZE 200
#define AUDIO_QUEUESIZE 50
zm_packetqueue::zm_packetqueue(){
zm_packetqueue::zm_packetqueue( int p_max_stream_id ) {
max_stream_id = p_max_stream_id;
packet_counts = new int[max_stream_id+1];
for ( int i=0; i <= max_stream_id; ++i )
packet_counts = 0;
}
zm_packetqueue::~zm_packetqueue() {
clearQueue();
delete[] packet_counts;
packet_counts = NULL;
}
bool zm_packetqueue::queuePacket( ZMPacket* zm_packet ) {
pktQueue.push_back( zm_packet );
bool zm_packetqueue::queuePacket(ZMPacket* zm_packet) {
pktQueue.push_back(zm_packet);
packet_counts[zm_packet->packet.stream_index] += 1;
return true;
}
bool zm_packetqueue::queuePacket( AVPacket* av_packet ) {
bool zm_packetqueue::queuePacket(AVPacket* av_packet) {
ZMPacket *zm_packet = new ZMPacket( av_packet );
ZMPacket *zm_packet = new ZMPacket(av_packet);
pktQueue.push_back( zm_packet );
pktQueue.push_back(zm_packet);
packet_counts[zm_packet->packet.stream_index] += 1;
return true;
}
@ -53,13 +57,14 @@ ZMPacket* zm_packetqueue::popPacket( ) {
ZMPacket *packet = pktQueue.front();
pktQueue.pop_front();
packet_counts[packet->packet.stream_index] -= 1;
return packet;
}
unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream_id ) {
unsigned int zm_packetqueue::clearQueue(unsigned int frames_to_keep, int stream_id) {
Debug(3, "Clearing all but %d frames, queue has %d", frames_to_keep, pktQueue.size() );
Debug(3, "Clearing all but %d frames, queue has %d", frames_to_keep, pktQueue.size());
frames_to_keep += 1;
if ( pktQueue.empty() ) {
@ -74,7 +79,8 @@ unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream
ZMPacket *zm_packet = *it;
AVPacket *av_packet = &(zm_packet->packet);
Debug(4, "Looking at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
Debug(4, "Looking at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)",
av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
// Want frames_to_keep video keyframes. Otherwise, we may not have enough
if ( ( av_packet->stream_index == stream_id) ) {
@ -83,21 +89,23 @@ unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream
}
}
// Make sure we start on a keyframe
// Make sure we start on a keyframe
for ( ; it != pktQueue.rend(); ++it ) {
ZMPacket *zm_packet = *it;
AVPacket *av_packet = &(zm_packet->packet);
Debug(5, "Looking for keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
Debug(5, "Looking for keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)",
av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
// Want frames_to_keep video keyframes. Otherwise, we may not have enough
if ( ( av_packet->stream_index == stream_id) && ( av_packet->flags & AV_PKT_FLAG_KEY ) ) {
Debug(4, "Found keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)", av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
Debug(4, "Found keyframe at packet with stream index (%d) with keyframe (%d), frames_to_keep is (%d)",
av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), frames_to_keep );
break;
}
}
if ( frames_to_keep ) {
Debug(3, "Hit end of queue, still need (%d) video frames", frames_to_keep );
Debug(3, "Hit end of queue, still need (%d) video frames", frames_to_keep);
}
if ( it != pktQueue.rend() ) {
// We want to keep this packet, so advance to the next
@ -105,22 +113,25 @@ unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream
}
unsigned int delete_count = 0;
while ( it != pktQueue.rend() ) {
Debug(4, "Deleting a packet from the front, count is (%d)", delete_count );
Debug(4, "Deleting a packet from the front, count is (%d)", delete_count);
packet = pktQueue.front();
pktQueue.pop_front();
packet_counts[packet->packet.stream_index] -= 1;
delete packet;
delete_count += 1;
}
Debug(3, "Deleted (%d) packets", delete_count );
packet = NULL; // tidy up for valgrind
Debug(3, "Deleted %d packets, %d remaining", delete_count, pktQueue.size());
return delete_count;
} // end unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream_id )
void zm_packetqueue::clearQueue() {
ZMPacket *packet = NULL;
while(!pktQueue.empty()) {
while (!pktQueue.empty()) {
packet = pktQueue.front();
packet_counts[packet->packet.stream_index] -= 1;
pktQueue.pop_front();
delete packet;
}
@ -130,18 +141,20 @@ unsigned int zm_packetqueue::size() {
return pktQueue.size();
}
int zm_packetqueue::packet_count( int stream_id ) {
return packet_counts[stream_id];
} // end int zm_packetqueue::packet_count( int stream_id )
void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVideoStreamId ) {
// Need to find the keyframe <= recording_started. Can get rid of audio packets.
if ( pktQueue.empty() ) {
if ( pktQueue.empty() )
return;
}
// Step 1 - find keyframe < recording_started.
// Step 2 - pop packets until we get to the packet in step 2
std::list<ZMPacket *>::reverse_iterator it;
Debug(3, "Looking for keyframe after start recording stream id (%d)", mVideoStreamId );
Debug(3, "Looking for keyframe after start recording stream id (%d)", mVideoStreamId);
for ( it = pktQueue.rbegin(); it != pktQueue.rend(); ++ it ) {
ZMPacket *zm_packet = *it;
AVPacket *av_packet = &(zm_packet->packet);
@ -175,9 +188,11 @@ void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVi
//while ( pktQueue.rend() != it ) {
packet = pktQueue.front();
pktQueue.pop_front();
packet_counts[packet->packet.stream_index] -= 1;
delete packet;
deleted_frames += 1;
}
packet = NULL; // tidy up for valgrind
zm_packet = pktQueue.front();
av_packet = &(zm_packet->packet);
@ -186,4 +201,4 @@ void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVi
} else {
Debug(1, "Done looking for keyframe. Deleted %d frames. Remaining frames in queue: %d stream of head packet is (%d), keyframe (%d), distance(%d), packets(%d)", deleted_frames, pktQueue.size(), av_packet->stream_index, ( av_packet->flags & AV_PKT_FLAG_KEY ), distance( it, pktQueue.rend() ), pktQueue.size() );
}
}
} // end void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVideoStreamId )

View File

@ -29,23 +29,25 @@
extern "C" {
#include <libavformat/avformat.h>
}
class zm_packetqueue {
public:
zm_packetqueue();
zm_packetqueue(int max_stream_id);
virtual ~zm_packetqueue();
bool queuePacket( AVPacket* packet, struct timeval *timestamp );
bool queuePacket( ZMPacket* packet );
bool queuePacket( AVPacket* packet );
ZMPacket * popPacket( );
bool queuePacket(AVPacket* packet, struct timeval *timestamp);
bool queuePacket(ZMPacket* packet);
bool queuePacket(AVPacket* packet);
ZMPacket * popPacket();
bool popVideoPacket(ZMPacket* packet);
bool popAudioPacket(ZMPacket* packet);
unsigned int clearQueue( unsigned int video_frames_to_keep, int stream_id );
void clearQueue( );
unsigned int clearQueue(unsigned int video_frames_to_keep, int stream_id);
void clearQueue();
unsigned int size();
void clear_unwanted_packets( timeval *recording, int mVideoStreamId );
void clear_unwanted_packets(timeval *recording, int mVideoStreamId);
int packet_count(int stream_id);
private:
std::list<ZMPacket *> pktQueue;
int max_stream_id;
int *packet_counts; /* packet count for each stream_id, to keep track of how many video vs audio packets are in the queue */
};

View File

@ -332,6 +332,8 @@ void StreamBase::openComms() {
snprintf(rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", staticConfig.PATH_SOCKS.c_str(), connkey);
strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path)-1);
rem_addr.sun_family = AF_UNIX;
gettimeofday(&last_comm_update, NULL);
} // end if connKey > 0
Debug(2, "comms open");
} // end void StreamBase::openComms()

View File

@ -85,6 +85,7 @@ protected:
int step;
struct timeval now;
struct timeval last_comm_update;
double base_fps;
double effective_fps;

View File

@ -24,6 +24,8 @@
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <fcntl.h> /* Definition of AT_* constants */
#include <sys/stat.h>
#if defined(__arm__)
#include <sys/auxv.h>
#endif
@ -414,3 +416,22 @@ Warning("ZM Compiled without LIBCURL. UriDecoding not implemented.");
#endif
}
void touch(const char *pathname) {
int fd = open(pathname,
O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK,
0666);
if ( fd < 0 ) {
// Couldn't open that path.
Error("Couldn't open() path \"%s in touch", pathname);
return;
}
int rc = utimensat(AT_FDCWD,
pathname,
nullptr,
0);
if ( rc ) {
Error("Couldn't utimensat() path %s in touch", pathname);
return;
}
}

View File

@ -63,5 +63,5 @@ extern unsigned int neonversion;
char *timeval_to_string( struct timeval tv );
std::string UriDecode( const std::string &encoded );
void touch( const char *pathname );
#endif // ZM_UTILS_H

View File

@ -55,7 +55,7 @@ class Frame {
#return $_SERVER['PHP_SELF'].'?view=image&fid='.$this->{'Id'}.'&show='.$show.'&filename='.$this->Event()->MonitorId().'_'.$this->{'EventId'}.'_'.$this->{'FrameId'}.'.jpg';
} // end function getImageSrc
public static function find( $parameters = array(), $limit = NULL ) {
public static function find( $parameters = array(), $options = NULL ) {
$sql = 'SELECT * FROM Frames';
$values = array();
if ( sizeof($parameters) ) {
@ -65,17 +65,23 @@ class Frame {
) );
$values = array_values( $parameters );
}
if ( $limit ) {
if ( is_integer( $limit ) or ctype_digit( $limit ) ) {
$sql .= ' LIMIT ' . $limit;
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Invalid value for limit($limit) passed to Frame::find from $file:$line");
return array();
}
}
if ( $options ) {
if ( isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
if ( isset($options['limit']) ) {
if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) {
$sql .= ' LIMIT ' . $options['limit'];
} else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Invalid value for limit(".$options['limit'].") passed to Frame::find from $file:$line");
return array();
}
}
}
$results = dbFetchAll($sql, NULL, $values);
if ( $results ) {
return array_map( function($id){ return new Frame($id); }, $results );
@ -83,8 +89,9 @@ class Frame {
return array();
}
public static function find_one( $parameters = array() ) {
$results = Frame::find( $parameters, 1 );
public static function find_one( $parameters = array(), $options = null ) {
$options['limit'] = 1;
$results = Frame::find($parameters, $options);
if ( ! sizeof($results) ) {
return;
}

View File

@ -152,19 +152,23 @@ private $control_fields = array(
}
if ( $this->{'Controllable'} ) {
$s = dbFetchOne('SELECT * FROM Controls WHERE Id=?', NULL, array($this->{'ControlId'}) );
foreach ($s as $k => $v) {
if ( $k == 'Id' ) {
continue;
# The reason for these is that the name overlaps Monitor fields.
} else if ( $k == 'Protocol' ) {
$this->{'ControlProtocol'} = $v;
} else if ( $k == 'Name' ) {
$this->{'ControlName'} = $v;
} else if ( $k == 'Type' ) {
$this->{'ControlType'} = $v;
} else {
$this->{$k} = $v;
if ( $s ) {
foreach ($s as $k => $v) {
if ( $k == 'Id' ) {
continue;
# The reason for these is that the name overlaps Monitor fields.
} else if ( $k == 'Protocol' ) {
$this->{'ControlProtocol'} = $v;
} else if ( $k == 'Name' ) {
$this->{'ControlName'} = $v;
} else if ( $k == 'Type' ) {
$this->{'ControlType'} = $v;
} else {
$this->{$k} = $v;
}
}
} else {
Warning('No Controls found for monitor '.$this->{'Id'} . ' ' . $this->{'Name'}.' althrough it is marked as controllable');
}
}
global $monitor_cache;

View File

@ -195,11 +195,12 @@ if ( currentView != 'none' && currentView != 'login' ) {
$j.getJSON(thisUrl + '?view=request&request=status&entity=navBar')
.done(setNavBar)
.fail(function( jqxhr, textStatus, error ) {
var err = textStatus + ", " + error;
console.log( "Request Failed: " + err );
// The idea is that this should only fail due to auth, so reload the page
// which should go to login if it can't stay logged in.
window.location.reload( true );
console.log( "Request Failed: " + textStatus + ", " + error);
if ( textStatus != "timeout" ) {
// The idea is that this should only fail due to auth, so reload the page
// which should go to login if it can't stay logged in.
window.location.reload( true );
}
});
}

View File

@ -253,7 +253,7 @@ for ( $i=0; $i < count($terms); $i++ ) {
<td><?php echo htmlSelect( "filter[Query][terms][$i][op]", $opTypes, $term['op'] ); ?></td>
<td>
<input type="text" name="filter[Query][terms][<?php echo $i ?>][val]" id="filter[Query][terms][<?php echo $i ?>][val]" value="<?php echo isset($term['val'])?validHtmlStr(str_replace('T', ' ', $term['val'])):'' ?>"/>
<script type="text/javascript">$j("[name$='\\[<?php echo $i ?>\\]\\[val\\]']").timepicker({timeFormat: "HH:mm:ss", constrainInput: falsepi}); </script>
<script type="text/javascript">$j("[name$='\\[<?php echo $i ?>\\]\\[val\\]']").timepicker({timeFormat: "HH:mm:ss", constrainInput: false}); </script>
</td>
<?php
} elseif ( $term['attr'] == 'StateId' ) {

View File

@ -76,6 +76,23 @@ if ( empty($_REQUEST['path']) ) {
return;
}
# if alarm, get the fid of the first alarmed frame if available and let the
# fid= code continue processing it. Sort it to get the first alarmed frame
if ( $_REQUEST['fid'] == 'alarm' ) {
$Frame = Frame::find_one(array('EventId'=>$_REQUEST['eid'], 'Type'=>'Alarm'),
array('order'=>'FrameId ASC'));
if ( !$Frame ) # no alarms
$Frame = Frame::find_one(array('EventId'=>$_REQUEST['eid'])); # first frame
if ( !$Frame ) {
Warning("No frame found for event " + $_REQUEST['eid']);
$Frame = new Frame();
$Frame->Delta(1);
$Frame->FrameId('snapshot');
}
$_REQUEST['fid']=$Frame->FrameId();
}
if ( $_REQUEST['fid'] == 'snapshot' ) {
$Frame = Frame::find_one(array('EventId'=>$_REQUEST['eid'], 'Score'=>$Event->MaxScore()));
if ( !$Frame )