This commit is contained in:
Isaac Connor 2017-10-23 09:51:41 -04:00
commit b58c9d87c3
87 changed files with 2152 additions and 1988 deletions

View File

@ -207,6 +207,7 @@ CREATE TABLE `Events` (
`Notes` text,
`StateId` int(10) unsigned NOT NULL,
`Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0',
`DiskSpace` bigint unsigned default NULL,
PRIMARY KEY (`Id`,`MonitorId`),
KEY `MonitorId` (`MonitorId`),
KEY `StartTime` (`StartTime`),
@ -592,13 +593,14 @@ CREATE TABLE `Storage` (
`Id` smallint(5) unsigned NOT NULL auto_increment,
`Path` varchar(64) NOT NULL default '',
`Name` varchar(64) NOT NULL default '',
`Type` enum('local','s3fs') NOT NULL default 'local',
PRIMARY KEY (`Id`)
) ENGINE=@ZM_MYSQL_ENGINE@;
--
-- Create a default storage location
--
insert into Storage VALUES (NULL, '/var/cache/zoneminder/events', 'Default' );
insert into Storage VALUES (NULL, '/var/cache/zoneminder/events', 'Default', 'local' );
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;

17
db/zm_update-1.31.8.sql Normal file
View File

@ -0,0 +1,17 @@
--
-- Add StorageId column to Monitors
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Events'
AND table_schema = DATABASE()
AND column_name = 'DiskSpace'
) > 0,
"SELECT 'Column DiskSpace exists in Events'",
"ALTER TABLE Events ADD `DiskSpace` bigint unsigned default null AFTER `Orientation`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

17
db/zm_update-1.31.9.sql Normal file
View File

@ -0,0 +1,17 @@
--
-- Add Type column to Storage
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Storage'
AND table_schema = DATABASE()
AND column_name = 'Type'
) > 0,
"SELECT 'Column Type already exists in Storage'",
"ALTER TABLE Storage ADD `Type` enum('local','s3fs') NOT NULL default 'local' AFTER `Name`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

@ -193,6 +193,16 @@ our @options = (
type => $types{string},
category => 'system',
},
{
name => 'ZM_BANDWIDTH_DEFAULT',
default => 'high',
description => 'Default setting for bandwidth profile used by web interface',
help => q`The classic skin for ZoneMinder has different
profiles to use for low medium or high bandwidth connections.
`,
type => $types{string},
category => 'system',
},
{
name => 'ZM_LANG_DEFAULT',
default => 'en_gb',

View File

@ -32,6 +32,7 @@ require ZoneMinder::Base;
require ZoneMinder::Object;
require ZoneMinder::Storage;
require Date::Manip;
require File::Find;
#our @ISA = qw(ZoneMinder::Object);
use parent qw(ZoneMinder::Object);
@ -358,6 +359,17 @@ sub age {
return $_[0]{age};
}
sub DiskUsage {
if ( @_ > 1 ) {
$_[0]{DiskUsage} = $_[1];
}
if ( ! defined $_[0]{DiskUsage} ) {
my $size = 0;
File::Find::find( { wanted=>sub { $size += -f $_ ? -s _ : 0 }, untaint=>1 }, $_[0]->Path() );
$_[0]{DiskUsage} = $size;
}
}
1;
__END__

View File

@ -246,6 +246,7 @@ MAIN: while( $loop ) {
$$Event{Path} = join('/', $Storage->Path(), $day_dir,$event_path);
$Event->MonitorId( $monitor_dir );
$Event->StorageId( $Storage->Id() );
$Event->DiskUsage( undef );
} # event path exists
} # end foreach event_link
chdir( $Storage->Path() );

View File

@ -1,25 +0,0 @@
snprintf( swap_path, sizeof(swap_path), "%s/zmswap-m%d/zmswap-q%06d", staticConfig.PATH_SWAP.c_str(), monitor->Id(), connkey );
int len = snprintf(NULL, 0, "/zmswap-m%d", monitor->Id());
int swap_path_length = strlen(staticConfig.PATH_SWAP.c_str()) + snprintf(NULL, 0, "/zmswap-m%d", monitor->Id() ) + snprintf(NULL, 0, "/zmswap-q%06d", connkey ) + 1; // +1 for NULL terminator
if ( connkey && playback_buffer > 0 ) {
if ( swap_path_length + max_swap_len_suffix > PATH_MAX ) {
Error( "Swap Path is too long. %d > %d ", swap_path_length+max_swap_len_suffix, PATH_MAX );
} else {
swap_path = (char *)malloc( swap_path_length+max_swap_len_suffix );
Debug( 3, "Checking swap image path %s", staticConfig.PATH_SWAP.c_str() );
strncpy( swap_path, staticConfig.PATH_SWAP.c_str(), swap_path_length );
if ( checkSwapPath( swap_path, false ) ) {
snprintf( &(swap_path[swap_path_length]), max_swap_len_suffix, "/zmswap-m%d", monitor->Id() );
if ( checkSwapPath( swap_path, true ) ) {
snprintf( &(swap_path[swap_path_length]), max_swap_len_suffix, "/zmswap-q%06d", connkey );
if ( checkSwapPath( swap_path, true ) ) {
buffered_playback = true;
}
}
}

View File

@ -34,61 +34,50 @@ protected:
unsigned char *mTail;
public:
Buffer() : mStorage( 0 ), mAllocation( 0 ), mSize( 0 ), mHead( 0 ), mTail( 0 )
{
Buffer() : mStorage( 0 ), mAllocation( 0 ), mSize( 0 ), mHead( 0 ), mTail( 0 ) {
}
Buffer( unsigned int pSize ) : mAllocation( pSize ), mSize( 0 )
{
Buffer( unsigned int pSize ) : mAllocation( pSize ), mSize( 0 ) {
mHead = mStorage = new unsigned char[mAllocation];
mTail = mHead;
}
Buffer( const unsigned char *pStorage, unsigned int pSize ) : mAllocation( pSize ), mSize( pSize )
{
Buffer( const unsigned char *pStorage, unsigned int pSize ) : mAllocation( pSize ), mSize( pSize ) {
mHead = mStorage = new unsigned char[mSize];
memcpy( mStorage, pStorage, mSize );
mTail = mHead + mSize;
}
Buffer( const Buffer &buffer ) : mAllocation( buffer.mSize ), mSize( buffer.mSize )
{
Buffer( const Buffer &buffer ) : mAllocation( buffer.mSize ), mSize( buffer.mSize ) {
mHead = mStorage = new unsigned char[mSize];
memcpy( mStorage, buffer.mHead, mSize );
mTail = mHead + mSize;
}
~Buffer()
{
~Buffer() {
delete[] mStorage;
}
unsigned char *head() const { return( mHead ); }
unsigned char *tail() const { return( mTail ); }
unsigned int size() const { return( mSize ); }
bool empty() const { return( mSize == 0 ); }
unsigned int size( unsigned int pSize )
{
if ( mSize < pSize )
{
unsigned int size( unsigned int pSize ) {
if ( mSize < pSize ) {
expand( pSize-mSize );
}
return( mSize );
}
//unsigned int Allocation() const { return( mAllocation ); }
void clear()
{
void clear() {
mSize = 0;
mHead = mTail = mStorage;
}
unsigned int assign( const unsigned char *pStorage, unsigned int pSize );
unsigned int assign( const Buffer &buffer )
{
unsigned int assign( const Buffer &buffer ) {
return( assign( buffer.mHead, buffer.mSize ) );
}
// Trim from the front of the buffer
unsigned int consume( unsigned int count )
{
if ( count > mSize )
{
unsigned int consume( unsigned int count ) {
if ( count > mSize ) {
Warning( "Attempt to consume %d bytes of buffer, size is only %d bytes", count, mSize );
count = mSize;
}
@ -98,10 +87,8 @@ public:
return( count );
}
// Trim from the end of the buffer
unsigned int shrink( unsigned int count )
{
if ( count > mSize )
{
unsigned int shrink( unsigned int count ) {
if ( count > mSize ) {
Warning( "Attempt to shrink buffer by %d bytes, size is only %d bytes", count, mSize );
count = mSize;
}
@ -115,10 +102,8 @@ public:
unsigned int expand( unsigned int count );
// Return pointer to the first pSize bytes and advance the head
unsigned char *extract( unsigned int pSize )
{
if ( pSize > mSize )
{
unsigned char *extract( unsigned int pSize ) {
if ( pSize > mSize ) {
Warning( "Attempt to extract %d bytes of buffer, size is only %d bytes", pSize, mSize );
pSize = mSize;
}
@ -129,32 +114,25 @@ public:
return( oldHead );
}
// Add bytes to the end of the buffer
unsigned int append( const unsigned char *pStorage, unsigned int pSize )
{
unsigned int append( const unsigned char *pStorage, unsigned int pSize ) {
expand( pSize );
memcpy( mTail, pStorage, pSize );
mTail += pSize;
mSize += pSize;
return( mSize );
}
unsigned int append( const char *pStorage, unsigned int pSize )
{
unsigned int append( const char *pStorage, unsigned int pSize ) {
return( append( (const unsigned char *)pStorage, pSize ) );
}
unsigned int append( const Buffer &buffer )
{
unsigned int append( const Buffer &buffer ) {
return( append( buffer.mHead, buffer.mSize ) );
}
void tidy( bool level=0 )
{
if ( mHead != mStorage )
{
void tidy( bool level=0 ) {
if ( mHead != mStorage ) {
if ( mSize == 0 )
mHead = mTail = mStorage;
else if ( level )
{
if ( ((uintptr_t)mHead-(uintptr_t)mStorage) > mSize )
{
else if ( level ) {
if ( ((uintptr_t)mHead-(uintptr_t)mStorage) > mSize ) {
memcpy( mStorage, mHead, mSize );
mHead = mStorage;
mTail = mHead + mSize;
@ -163,44 +141,35 @@ public:
}
}
Buffer &operator=( const Buffer &buffer )
{
Buffer &operator=( const Buffer &buffer ) {
assign( buffer );
return( *this );
}
Buffer &operator+=( const Buffer &buffer )
{
Buffer &operator+=( const Buffer &buffer ) {
append( buffer );
return( *this );
}
Buffer &operator+=( unsigned int count )
{
Buffer &operator+=( unsigned int count ) {
expand( count );
return( *this );
}
Buffer &operator-=( unsigned int count )
{
Buffer &operator-=( unsigned int count ) {
consume( count );
return( *this );
}
operator unsigned char *() const
{
operator unsigned char *() const {
return( mHead );
}
operator char *() const
{
operator char *() const {
return( (char *)mHead );
}
unsigned char *operator+(int offset) const
{
unsigned char *operator+(int offset) const {
return( (unsigned char *)(mHead+offset) );
}
unsigned char operator[](int index) const
{
unsigned char operator[](int index) const {
return( *(mHead+index) );
}
operator int () const
{
operator int () const {
return( (int)mSize );
}
int read_into( int sd, unsigned int bytes );

View File

@ -24,6 +24,7 @@
#include <sys/ioctl.h>
#include "zm_image.h"
#include "zm_packet.h"
class Camera;
@ -33,8 +34,7 @@ class Camera;
// Abstract base class for cameras. This is intended just to express
// common attributes
//
class Camera
{
class Camera {
protected:
typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType;
@ -54,8 +54,26 @@ protected:
bool capture;
bool record_audio;
int mVideoStreamId;
int mAudioStreamId;
AVCodecContext *mVideoCodecContext;
AVCodecContext *mAudioCodecContext;
public:
Camera( unsigned int p_monitor_id, SourceType p_type, unsigned int p_width, unsigned int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio );
Camera(
unsigned int p_monitor_id,
SourceType p_type,
unsigned int p_width,
unsigned int p_height,
int p_colours,
int p_subpixelorder,
int p_brightness,
int p_contrast,
int p_hue,
int p_colour,
bool p_capture,
bool p_record_audio
);
virtual ~Camera();
unsigned int getId() const { return( monitor_id ); }
@ -86,9 +104,12 @@ public:
virtual int PrimeCapture() { return( 0 ); }
virtual int PreCapture()=0;
virtual int Capture( Image &image )=0;
virtual ZMPacket * Capture( Image &image )=0;
virtual int PostCapture()=0;
virtual int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) = 0;
AVStream *get_VideoStream() { return NULL; };
AVStream *get_AudioStream() { return NULL; };
int get_VideoStreamId() { return mVideoStreamId; };
int get_AudioStreamId() { return mAudioStreamId; };
};
#endif // ZM_CAMERA_H

View File

@ -69,7 +69,7 @@ void zmLoadConfig() {
Debug( 1, "Fetching ZM_SERVER_ID For Name = %s", staticConfig.SERVER_NAME.c_str() );
std::string sql = stringtf("SELECT Id FROM Servers WHERE Name='%s'", staticConfig.SERVER_NAME.c_str() );
zmDbRow dbrow;
zmDbRow dbrow;
if ( dbrow.fetch( sql.c_str() ) ) {
staticConfig.SERVER_ID = atoi(dbrow[0]);
} else {
@ -81,12 +81,12 @@ void zmLoadConfig() {
Debug( 1, "Fetching ZM_SERVER_NAME For Id = %d", staticConfig.SERVER_ID );
std::string sql = stringtf("SELECT Name FROM Servers WHERE Id='%d'", staticConfig.SERVER_ID );
zmDbRow dbrow;
if ( dbrow.fetch( sql.c_str() ) ) {
staticConfig.SERVER_NAME = std::string(dbrow[0]);
} else {
Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID );
}
zmDbRow dbrow;
if ( dbrow.fetch( sql.c_str() ) ) {
staticConfig.SERVER_NAME = std::string(dbrow[0]);
} else {
Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID );
}
if ( staticConfig.SERVER_ID ) {
Debug( 3, "Multi-server configuration detected. Server is %d.", staticConfig.SERVER_ID );
@ -94,6 +94,11 @@ void zmLoadConfig() {
Debug( 3, "Single server configuration assumed because no Server ID or Name was specified." );
}
}
snprintf( staticConfig.capture_file_format, sizeof(staticConfig.capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits );
snprintf( staticConfig.analyse_file_format, sizeof(staticConfig.analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits );
snprintf( staticConfig.general_file_format, sizeof(staticConfig.general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits );
snprintf( staticConfig.video_file_format, sizeof(staticConfig.video_file_format), "%%s/%%s");
}
void process_configfile( char* configFile) {

View File

@ -22,6 +22,7 @@
#include "config.h"
#include "zm_config_defines.h"
#include "zm.h"
#include <string>
@ -61,8 +62,7 @@ extern void zmLoadConfig();
extern void process_configfile( char* configFile );
struct StaticConfig
{
struct StaticConfig {
std::string DB_HOST;
std::string DB_NAME;
std::string DB_USER;
@ -83,20 +83,22 @@ struct StaticConfig
std::string PATH_LOGS;
std::string PATH_SWAP;
std::string PATH_ARP;
char capture_file_format[PATH_MAX];
char analyse_file_format[PATH_MAX];
char general_file_format[PATH_MAX];
char video_file_format[PATH_MAX];
};
extern StaticConfig staticConfig;
class ConfigItem
{
class ConfigItem {
private:
char *name;
char *value;
char *type;
mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type;
mutable union
{
mutable union {
bool boolean_value;
int integer_value;
double decimal_value;
@ -113,26 +115,21 @@ public:
double DecimalValue() const;
const char *StringValue() const;
inline operator bool() const
{
inline operator bool() const {
return( BooleanValue() );
}
inline operator int() const
{
inline operator int() const {
return( IntegerValue() );
}
inline operator double() const
{
inline operator double() const {
return( DecimalValue() );
}
inline operator const char *() const
{
inline operator const char *() const {
return( StringValue() );
}
};
class Config
{
class Config {
public:
ZM_CFG_DECLARE_LIST

View File

@ -37,11 +37,6 @@
//#define USE_PREPARED_SQL 1
bool Event::initialised = false;
char Event::capture_file_format[PATH_MAX];
char Event::analyse_file_format[PATH_MAX];
char Event::general_file_format[PATH_MAX];
char Event::video_file_format[PATH_MAX];
const char * Event::frame_type_names[3] = { "Normal", "Bulk", "Alarm" };
int Event::pre_alarm_count = 0;
@ -56,8 +51,6 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
videoEvent( p_videoEvent ),
videowriter( NULL )
{
if ( !initialised )
Initialise();
std::string notes;
createNotes( notes );
@ -72,7 +65,7 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
unsigned int state_id = 0;
zmDbRow dbrow;
if ( dbrow.fetch( "SELECT Id FROM States WHERE IsActive=1") ) {
if ( dbrow.fetch("SELECT Id FROM States WHERE IsActive=1") ) {
state_id = atoi(dbrow[0]);
}
@ -173,7 +166,8 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
if ( monitor->GetOptVideoWriter() != 0 ) {
snprintf( video_name, sizeof(video_name), "%d-%s", id, "video.mp4" );
snprintf( video_file, sizeof(video_file), 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 );
/* X264 MP4 video writer */
if ( monitor->GetOptVideoWriter() == Monitor::X264ENCODE ) {
@ -194,7 +188,7 @@ Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string
}
snprintf( timecodes_name, sizeof(timecodes_name), "%d-%s", id, "video.timecodes" );
snprintf( timecodes_file, sizeof(timecodes_file), video_file_format, path, timecodes_name );
snprintf( timecodes_file, sizeof(timecodes_file), staticConfig.video_file_format, path, timecodes_name );
/* Create timecodes file */
timecodes_fd = fopen(timecodes_file, "wb");
@ -227,7 +221,7 @@ Event::~Event() {
/* Close the video file */
if ( videowriter != NULL ) {
int nRet = videowriter->Close();
if(nRet != 0) {
if ( nRet != 0 ) {
Error("Failed closing video stream");
}
delete videowriter;
@ -291,7 +285,7 @@ bool Event::WriteFrameVideo( const Image *image, const struct timeval timestamp,
}
/* If the image does not contain a timestamp, add the timestamp */
if (!config.timestamp_on_capture) {
if ( !config.timestamp_on_capture ) {
ts_image = *image;
monitor->TimestampImage( &ts_image, &timestamp );
frameimg = &ts_image;
@ -303,7 +297,7 @@ bool Event::WriteFrameVideo( const Image *image, const struct timeval timestamp,
unsigned int timeMS = (delta_time3.sec * delta_time3.prec) + delta_time3.fsec;
/* Encode and write the frame */
if(videowriter->Encode(frameimg, timeMS) != 0) {
if ( videowriter->Encode(frameimg, timeMS) != 0 ) {
Error("Failed encoding video frame");
}
@ -438,7 +432,7 @@ void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, st
frames++;
static char event_file[PATH_MAX];
snprintf( event_file, sizeof(event_file), capture_file_format, path, frames );
snprintf( event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames );
if ( monitor->GetOptSaveJPEGs() & 4 ) {
//If this is the first frame, we should add a thumbnail to the event directory
// ICON: We are working through the pre-event frames so this snapshot won't
@ -489,9 +483,10 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *
frames++;
static char event_file[PATH_MAX];
snprintf( event_file, sizeof(event_file), capture_file_format, path, frames );
snprintf( event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames );
if ( monitor->GetOptSaveJPEGs() & 4 ) {
// Only snapshots
//If this is the first frame, we should add a thumbnail to the event directory
if ( frames == 10 ) {
char snapshot_file[PATH_MAX];
@ -514,6 +509,7 @@ Debug(3, "Writing video");
DELTA_TIMEVAL( delta_time, timestamp, start_time, DT_PREC_2 );
FrameType frame_type = score>0?ALARM:(score<0?BULK:NORMAL);
// < 0 means no motion detection is being done.
if ( score < 0 )
score = 0;
@ -559,7 +555,7 @@ Debug(3, "Writing video");
max_score = score;
if ( alarm_image ) {
snprintf( event_file, sizeof(event_file), analyse_file_format, path, frames );
snprintf( event_file, sizeof(event_file), staticConfig.analyse_file_format, path, frames );
Debug( 1, "Writing analysis frame %d", frames );
if ( monitor->GetOptSaveJPEGs() & 2 ) {

View File

@ -51,13 +51,6 @@ class EventStream;
class Event {
friend class EventStream;
protected:
static bool initialised;
static char capture_file_format[PATH_MAX];
static char analyse_file_format[PATH_MAX];
static char general_file_format[PATH_MAX];
static char video_file_format[PATH_MAX];
protected:
static int sd;
@ -79,7 +72,6 @@ class Event {
static int pre_alarm_count;
static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES];
protected:
unsigned int id;
Monitor *monitor;
struct timeval start_time;
@ -98,30 +90,14 @@ class Event {
char video_file[PATH_MAX];
char timecodes_name[PATH_MAX];
char timecodes_file[PATH_MAX];
protected:
int last_db_frame;
protected:
static void Initialise() {
if ( initialised )
return;
snprintf( capture_file_format, sizeof(capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits );
snprintf( analyse_file_format, sizeof(analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits );
snprintf( general_file_format, sizeof(general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits );
snprintf( video_file_format, sizeof(video_file_format), "%%s/%%s");
initialised = true;
}
void createNotes( std::string &notes );
public:
static bool OpenFrameSocket( int );
static bool ValidateFrameSocket( int );
public:
Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent=false );
~Event();

View File

@ -577,11 +577,10 @@ void EventStream::checkEventLoaded() {
}
Image * EventStream::getImage( ) {
Event::Initialise();
static char filepath[PATH_MAX];
Debug( 2, "EventStream::getImage path(%s) frame(%d)", event_data->path, curr_frame_id );
snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id );
snprintf( filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id );
Debug( 2, "EventStream::getImage path(%s) ", filepath, curr_frame_id );
Image *image = new Image( filepath );
return image;
@ -596,12 +595,12 @@ bool EventStream::sendFrame( int delta_us ) {
// This needs to be abstracted. If we are saving jpgs, then load the capture file. If we are only saving analysis frames, then send that.
if ( monitor->GetOptSaveJPEGs() & 1 ) {
snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id );
snprintf( filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id );
} else if ( monitor->GetOptSaveJPEGs() & 2 ) {
snprintf( filepath, sizeof(filepath), Event::analyse_file_format, event_data->path, curr_frame_id );
snprintf( filepath, sizeof(filepath), staticConfig.analyse_file_format, event_data->path, curr_frame_id );
if ( stat( filepath, &filestat ) < 0 ) {
Debug(1, "analyze file %s not found will try to stream from other", filepath);
snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id );
snprintf( filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id );
filepath[0] = 0;
}
@ -752,9 +751,6 @@ bool EventStream::sendFrame( int delta_us ) {
}
void EventStream::runStream() {
Event::Initialise();
Debug(3, "Initialized");
openComms();
Debug(3, "Comms open");

View File

@ -112,9 +112,6 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
mCanCapture = false;
mOpenStart = 0;
mReopenThread = 0;
videoStore = NULL;
video_last_pts = 0;
have_video_keyframe = false;
#if HAVE_LIBSWSCALE
mConvertContext = NULL;
@ -132,14 +129,10 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
} else {
Panic("Unexpected colours: %d",colours);
}
}
} // end FFmpegCamera::FFmpegCamera
FfmpegCamera::~FfmpegCamera() {
if ( videoStore ) {
delete videoStore;
}
CloseFfmpeg();
if ( capture ) {
@ -184,15 +177,17 @@ int FfmpegCamera::PreCapture() {
return( 0 );
}
int FfmpegCamera::Capture( Image &image ) {
ZMPacket * FfmpegCamera::Capture( Image &image ) {
if ( ! mCanCapture ) {
return -1;
return NULL;
}
int ret;
char errbuf[AV_ERROR_MAX_STRING_SIZE];
// If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread.
if ( mReopenThread != 0 ) {
void *retval = 0;
int ret;
ret = pthread_join(mReopenThread, &retval);
if ( ret != 0 ) {
@ -203,32 +198,27 @@ int FfmpegCamera::Capture( Image &image ) {
mReopenThread = 0;
}
int frameComplete = false;
while ( !frameComplete ) {
int ret;
int avResult = av_read_frame( mFormatContext, &packet );
char errbuf[AV_ERROR_MAX_STRING_SIZE];
if ( avResult < 0 ) {
av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE);
if (
// Check if EOF.
(avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
// Check for Connection failure.
(avResult == -110)
) {
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf );
ReopenFfmpeg();
}
ZMPacket *zm_packet = NULL;
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf );
return( -1 );
ret = av_read_frame( mFormatContext, &packet );
if ( ret < 0 ) {
av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
if (
// Check if EOF.
(ret == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
// Check for Connection failure.
(ret == -110)
) {
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf );
ReopenFfmpeg();
}
int keyframe = packet.flags & AV_PKT_FLAG_KEY;
if ( keyframe )
have_video_keyframe = true;
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf );
return NULL;
}
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)
@ -315,9 +305,10 @@ int FfmpegCamera::Capture( Image &image ) {
} else {
Debug( 4, "Different stream_index %d", packet.stream_index );
} // end if packet.stream_index == mVideoStreamId
zm_av_packet_unref( &packet );
} // end while ! frameComplete
return (0);
#endif
zm_packet = new ZMPacket( &packet );
zm_av_packet_unref( &packet );
return zm_packet;
} // FfmpegCamera::Capture
int FfmpegCamera::PostCapture() {
@ -333,7 +324,6 @@ int FfmpegCamera::OpenFfmpeg() {
mOpenStart = time(NULL);
mIsOpening = true;
have_video_keyframe = false;
// Open the input, not necessarily a file
#if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0)
@ -714,339 +704,4 @@ void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){
}
}
//Function to handle capture and store
int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event_file ) {
if ( ! mCanCapture ) {
return -1;
}
int ret;
static char errbuf[AV_ERROR_MAX_STRING_SIZE];
// If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread.
if ( mReopenThread != 0 ) {
void *retval = 0;
ret = pthread_join(mReopenThread, &retval);
if (ret != 0){
Error("Could not join reopen thread.");
}
Info( "Successfully reopened stream." );
mReopenThread = 0;
}
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
Error( "Input stream is not h264. The stored event file may not be viewable in browser." );
#ifdef AV_CODEC_ID_H265
}
#endif
}
int frameComplete = false;
while ( ! frameComplete ) {
av_init_packet( &packet );
ret = av_read_frame( mFormatContext, &packet );
if ( ret < 0 ) {
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
if (
// Check if EOF.
(ret == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
// Check for Connection failure.
(ret == -110)
) {
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf);
ReopenFfmpeg();
}
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf );
return( -1 );
}
int keyframe = packet.flags & AV_PKT_FLAG_KEY;
Debug( 4, "Got packet from stream %d packet pts (%u) dts(%u), key?(%d)",
packet.stream_index, packet.pts, packet.dts,
keyframe
);
//Video recording
if ( recording.tv_sec ) {
uint32_t last_event_id = monitor->GetLastEventId() ;
if ( last_event_id != monitor->GetVideoWriterEventId() ) {
Debug(2, "Have change of event. last_event(%d), our current (%d)", last_event_id, monitor->GetVideoWriterEventId() );
if ( videoStore ) {
Info("Re-starting video storage module");
// I don't know if this is important or not... but I figure we might as well write this last packet out to the store before closing it.
// Also don't know how much it matters for audio.
if ( packet.stream_index == mVideoStreamId ) {
//Write the packet to our video store
int ret = videoStore->writeVideoFramePacket( &packet );
if ( ret < 0 ) { //Less than zero and we skipped a frame
Warning("Error writing last packet to videostore.");
}
} // end if video
delete videoStore;
videoStore = NULL;
have_video_keyframe = false;
monitor->SetVideoWriterEventId( 0 );
} // end if videoStore
} // end if end of recording
if ( last_event_id and ! videoStore ) {
//Instantiate the video storage module
if ( record_audio ) {
if ( mAudioStreamId == -1 ) {
Debug(3, "Record Audio on but no audio stream found");
videoStore = new VideoStore((const char *) event_file, "mp4",
mFormatContext->streams[mVideoStreamId],
NULL,
startTime,
this->getMonitor());
} else {
Debug(3, "Video module initiated with audio stream");
videoStore = new VideoStore((const char *) event_file, "mp4",
mFormatContext->streams[mVideoStreamId],
mFormatContext->streams[mAudioStreamId],
startTime,
this->getMonitor());
}
} else {
Debug(3, "Record_audio is false so exclude audio stream");
videoStore = new VideoStore((const char *) event_file, "mp4",
mFormatContext->streams[mVideoStreamId],
NULL,
startTime,
this->getMonitor());
} // end if record_audio
if ( ! videoStore->open() ) {
delete videoStore;
videoStore = NULL;
} else {
strcpy(oldDirectory, event_file);
monitor->SetVideoWriterEventId( last_event_id );
// Need to write out all the frames from the last keyframe?
// No... need to write out all frames from when the event began. Due to PreEventFrames, this could be more than since the last keyframe.
unsigned int packet_count = 0;
ZMPacket *queued_packet;
// Clear all packets that predate the moment when the recording began
packetqueue.clear_unwanted_packets( &recording, mVideoStreamId );
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() );
if ( avp->stream_index == mVideoStreamId ) {
ret = videoStore->writeVideoFramePacket( avp );
have_video_keyframe = true;
} else if ( avp->stream_index == mAudioStreamId ) {
ret = videoStore->writeAudioFramePacket( avp );
} else {
Warning("Unknown stream id in queued packet (%d)", avp->stream_index );
ret = -1;
}
if ( ret < 0 ) {
//Less than zero and we skipped a frame
}
delete queued_packet;
} // end while packets in the packetqueue
Debug(2, "Wrote %d queued packets", packet_count );
}
} // end if ! was recording
} else {
// Not recording
if ( videoStore ) {
Info("Deleting videoStore instance");
delete videoStore;
videoStore = NULL;
have_video_keyframe = false;
monitor->SetVideoWriterEventId( 0 );
}
// Buffer video packets, since we are not recording.
// All audio packets are keyframes, so only if it's a video keyframe
if ( packet.stream_index == mVideoStreamId ) {
if ( keyframe ) {
Debug(3, "Clearing queue");
packetqueue.clearQueue( monitor->GetPreEventCount(), mVideoStreamId );
}
#if 0
// Not sure this is valid. While a camera will PROBABLY always have an increasing pts... it doesn't have to.
// Also, I think there are integer wrap-around issues.
else if ( packet.pts && video_last_pts > packet.pts ) {
Warning( "Clearing queue due to out of order pts packet.pts(%d) < video_last_pts(%d)");
packetqueue.clearQueue();
}
#endif
}
// The following lines should ensure that the queue always begins with a video keyframe
if ( packet.stream_index == mAudioStreamId ) {
//Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", 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 );
}
} else if ( packet.stream_index == mVideoStreamId ) {
if ( keyframe || packetqueue.size() ) // it's a keyframe or we already have something in the queue
packetqueue.queuePacket( &packet );
}
} // end if recording or not
if ( packet.stream_index == mVideoStreamId ) {
// only do decode if we have had a keyframe, should save a few cycles.
if ( have_video_keyframe || keyframe ) {
if ( videoStore ) {
//Write the packet to our video store
int ret = videoStore->writeVideoFramePacket( &packet );
if ( ret < 0 ) { //Less than zero and we skipped a frame
zm_av_packet_unref( &packet );
return 0;
}
have_video_keyframe = true;
}
} // end if keyframe or have_video_keyframe
Debug(4, "about to decode video" );
#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.");
zm_av_packet_unref( &packet );
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 (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);
}
frameCount++;
} else {
Debug( 3, "Not framecomplete after av_read_frame" );
} // end if frameComplete
} else if ( packet.stream_index == mAudioStreamId ) { //FIXME best way to copy all other streams
if ( videoStore ) {
if ( record_audio ) {
if ( have_video_keyframe ) {
Debug(3, "Recording audio packet streamindex(%d) packetstreamindex(%d)", mAudioStreamId, packet.stream_index );
//Write the packet to our video store
//FIXME no relevance of last key frame
int ret = videoStore->writeAudioFramePacket( &packet );
if ( ret < 0 ) {//Less than zero and we skipped a frame
Warning("Failure to write audio packet.");
zm_av_packet_unref( &packet );
return 0;
}
} else {
Debug(3, "Not recording audio yet because we don't have a video keyframe yet");
}
} else {
Debug(4, "Not doing recording of audio packet" );
}
} else {
Debug(4, "Have audio packet, but not recording atm" );
}
} else {
#if LIBAVUTIL_VERSION_CHECK(56, 23, 0, 23, 0)
Debug( 3, "Some other stream index %d, %s", packet.stream_index, av_get_media_type_string( mFormatContext->streams[packet.stream_index]->codecpar->codec_type) );
#else
Debug( 3, "Some other stream index %d", packet.stream_index );
#endif
} // end if is video or audio or something else
// the packet contents are ref counted... when queuing, we allocate another packet and reference it with that one, so we should always need to unref here, which should not affect the queued version.
zm_av_packet_unref( &packet );
} // end while ! frameComplete
return (frameCount);
} // end FfmpegCamera::CaptureAndRecord
#endif // HAVE_LIBAVFORMAT

View File

@ -25,7 +25,6 @@
#include "zm_buffer.h"
#include "zm_ffmpeg.h"
#include "zm_videostore.h"
#include "zm_packetqueue.h"
#if HAVE_AVUTIL_HWCONTEXT_H
typedef struct DecodeContext {
@ -48,8 +47,6 @@ class FfmpegCamera : public Camera {
AVFormatContext *mFormatContext;
int mVideoStreamId;
int mAudioStreamId;
AVCodecContext *mVideoCodecContext;
AVCodecContext *mAudioCodecContext;
AVCodec *mVideoCodec;
AVCodec *mAudioCodec;
AVFrame *mRawFrame;
@ -84,11 +81,6 @@ class FfmpegCamera : public Camera {
pthread_t mReopenThread;
#endif // HAVE_LIBAVFORMAT
VideoStore *videoStore;
char oldDirectory[4096];
unsigned int old_event_id;
zm_packetqueue packetqueue;
bool have_video_keyframe;
#if HAVE_LIBSWSCALE
struct SwsContext *mConvertContext;
@ -109,9 +101,19 @@ class FfmpegCamera : public Camera {
int PrimeCapture();
int PreCapture();
int Capture( Image &image );
ZMPacket * Capture( Image &image );
int CaptureAndRecord( Image &image, timeval recording, char* event_directory );
int PostCapture();
AVStream *get_VideoStream() {
if ( mVideoStreamId != -1 )
return mFormatContext->streams[mVideoStreamId];
return NULL;
}
AVStream *get_AudioStream() {
if ( mAudioStreamId != -1 )
return mFormatContext->streams[mAudioStreamId];
return NULL;
}
};
#endif // ZM_FFMPEG_CAMERA_H

View File

@ -37,55 +37,48 @@
FileCamera::FileCamera( int p_id, const char *p_path, 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, FILE_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 )
{
strncpy( path, p_path, sizeof(path) );
if ( capture )
{
if ( capture ) {
Initialise();
}
}
FileCamera::~FileCamera()
{
if ( capture )
{
FileCamera::~FileCamera() {
if ( capture ) {
Terminate();
}
}
void FileCamera::Initialise()
{
if ( !path[0] )
{
void FileCamera::Initialise() {
if ( !path[0] ) {
Error( "No path specified for file image" );
exit( -1 );
}
}
void FileCamera::Terminate()
{
void FileCamera::Terminate() {
}
int FileCamera::PreCapture()
{
int FileCamera::PreCapture() {
struct stat statbuf;
if ( stat( path, &statbuf ) < 0 )
{
if ( stat( path, &statbuf ) < 0 ) {
Error( "Can't stat %s: %s", path, strerror(errno) );
return( -1 );
}
while ( (time( 0 ) - statbuf.st_mtime) < 1 )
{
// I think this is waiting for file change...
while ( (time( 0 ) - statbuf.st_mtime) < 1 ) {
usleep( 100000 );
}
return( 0 );
}
int FileCamera::Capture( Image &image )
{
return( image.ReadJpeg( path, colours, subpixelorder )?0:-1 );
ZMPacket * FileCamera::Capture( Image &image ) {
ZMPacket * packet = NULL;
if ( image.ReadJpeg( path, colours, subpixelorder ) )
packet = new ZMPacket( &image );
return packet;
}
int FileCamera::PostCapture()
{
return( 0 );
int FileCamera::PostCapture() {
return( 0 );
}

View File

@ -23,7 +23,7 @@
#include "zm_camera.h"
#include "zm_buffer.h"
#include "zm_regexp.h"
#include "zm_packetqueue.h"
#include "zm_packet.h"
#include <sys/param.h>
@ -31,13 +31,24 @@
// Class representing 'file' cameras, i.e. those which are
// accessed using a single file which contains the latest jpeg data
//
class FileCamera : public Camera
{
class FileCamera : public Camera {
protected:
char path[PATH_MAX];
public:
FileCamera( int p_id, const char *p_path, 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 );
FileCamera(
int p_id,
const char *p_path,
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
);
~FileCamera();
const char *Path() const { return( path ); }
@ -45,9 +56,8 @@ public:
void Initialise();
void Terminate();
int PreCapture();
int Capture( Image &image );
ZMPacket * Capture( Image &image );
int PostCapture();
int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) {return(0);};
};
#endif // ZM_FILE_CAMERA_H

View File

@ -95,14 +95,15 @@ protected:
double _1_m;
static int CompareYX( const void *p1, const void *p2 ) {
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
// This is because these functions are passed to qsort
const Edge *e1 = reinterpret_cast<const Edge *>(p1), *e2 = reinterpret_cast<const Edge *>(p2);
if ( e1->min_y == e2->min_y )
return( int(e1->min_x - e2->min_x) );
else
return( int(e1->min_y - e2->min_y) );
}
static int CompareX( const void *p1, const void *p2 ) {
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
const Edge *e1 = reinterpret_cast<const Edge *>(p1), *e2 = reinterpret_cast<const Edge *>(p2);
return( int(e1->min_x - e2->min_x) );
}
};
@ -145,6 +146,7 @@ protected:
unsigned int size;
unsigned int subpixelorder;
unsigned long allocation;
_AVPIXELFORMAT imagePixFormat;
uint8_t *buffer;
int buffertype; /* 0=not ours, no need to call free(), 1=malloc() buffer, 2=new buffer */
int holdbuffer; /* Hold the buffer instead of replacing it with new one */
@ -166,12 +168,32 @@ public:
inline unsigned int Colours() const { return( colours ); }
inline unsigned int SubpixelOrder() const { return( subpixelorder ); }
inline unsigned int Size() const { return( size ); }
inline unsigned int AVPixFormat() {
if ( ! imagePixFormat ) {
/* 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;
};
}
return imagePixFormat;
}
/* Internal buffer should not be modified from functions outside of this class */
inline const uint8_t* Buffer() const { return( buffer ); }
inline const uint8_t* Buffer( unsigned int x, unsigned int y= 0 ) const { return( &buffer[colours*((y*width)+x)] ); }
/* Request writeable buffer */
uint8_t* WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder);
// Is only acceptable on a pre-allocated buffer
uint8_t* WriteBuffer() { if ( holdbuffer ) return buffer; return NULL; };
inline int IsBufferHeld() const { return holdbuffer; }
inline void HoldBuffer(int tohold) { holdbuffer = tohold; }

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@
#include "zm.h"
#include "zm_camera.h"
#include "zm_image.h"
#include "zm_packetqueue.h"
#include "zm_packet.h"
#if ZM_HAS_V4L
@ -49,18 +49,15 @@
// directly connect to the host machine and which are accessed
// via a video interface.
//
class LocalCamera : public Camera
{
class LocalCamera : public Camera {
protected:
#if ZM_HAS_V4L2
struct V4L2MappedBuffer
{
struct V4L2MappedBuffer {
void *start;
size_t length;
};
struct V4L2Data
{
struct V4L2Data {
v4l2_cropcap cropcap;
v4l2_crop crop;
v4l2_format fmt;
@ -71,8 +68,7 @@ protected:
#endif // ZM_HAS_V4L2
#if ZM_HAS_V4L1
struct V4L1Data
{
struct V4L1Data {
int active_frame;
video_mbuf frames;
video_mmap *buffers;
@ -160,10 +156,8 @@ public:
int PrimeCapture();
int PreCapture();
int Capture( Image &image );
ZMPacket *Capture( Image &image );
int PostCapture();
int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) {return(0);};
static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose );
};

View File

@ -131,16 +131,17 @@ void Logger::initialise( const std::string &id, const Options &options ) {
this->id( id );
std::string tempLogFile;
if ( options.mLogPath.size() ) {
mLogPath = options.mLogPath;
tempLogFile = mLogPath+"/"+mId+".log";
}
if ( options.mLogFile.size() )
tempLogFile = options.mLogFile;
else
tempLogFile = mLogPath+"/"+mId+".log";
if ( (envPtr = getTargettedEnv( "LOG_FILE" )) )
tempLogFile = envPtr;
else if ( options.mLogFile.size() )
tempLogFile = options.mLogFile;
else {
if ( options.mLogPath.size() ) {
mLogPath = options.mLogPath;
}
tempLogFile = mLogPath+"/"+mId+".log";
}
Level tempLevel = INFO;
Level tempTermLevel = mTermLevel;
@ -270,7 +271,7 @@ int Logger::intEnv( const std::string &name, bool defaultValue ) {
return( envPtr ? atoi( envPtr ) : defaultValue );
}
std::string Logger::strEnv( const std::string &name, const std::string defaultValue ) {
std::string Logger::strEnv( const std::string &name, const std::string &defaultValue ) {
const char *envPtr = getenv( name.c_str() );
return( envPtr ? envPtr : defaultValue );
}
@ -560,7 +561,7 @@ void logInit( const char *name, const Logger::Options &options ) {
if ( !Logger::smInstance )
Logger::smInstance = new Logger();
Logger::Options tempOptions = options;
tempOptions.mLogPath = staticConfig.PATH_LOGS.c_str();
tempOptions.mLogPath = staticConfig.PATH_LOGS;
Logger::smInstance->initialise( name, tempOptions );
}

View File

@ -56,8 +56,7 @@ public:
typedef std::map<Level,std::string> StringMap;
typedef std::map<Level,int> IntMap;
class Options
{
class Options {
public:
int mTermLevel;
int mDatabaseLevel;
@ -147,7 +146,7 @@ private:
bool boolEnv( const std::string &name, bool defaultValue=false );
int intEnv( const std::string &name, bool defaultValue=0 );
std::string strEnv( const std::string &name, const std::string defaultValue="" );
std::string strEnv( const std::string &name, const std::string &defaultValue="" );
char *getTargettedEnv( const std::string &name );
void loadEnv();
@ -166,8 +165,7 @@ public:
}
Level level( Level=NOOPT );
bool debugOn()
{
bool debugOn() {
return( mEffectiveLevel >= DEBUG1 );
}
@ -190,20 +188,16 @@ public:
void logInit( const char *name, const Logger::Options &options=Logger::Options() );
void logTerm();
inline const std::string &logId()
{
inline const std::string &logId() {
return( Logger::fetch()->id() );
}
inline Logger::Level logLevel()
{
inline Logger::Level logLevel() {
return( Logger::fetch()->level() );
}
inline void logCapLevel( Logger::Level level )
{
inline void logCapLevel( Logger::Level level ) {
Logger::fetch()->level( level );
}
inline Logger::Level logDebugging()
{
inline Logger::Level logDebugging() {
return( Logger::fetch()->debugOn() );
}

View File

@ -75,7 +75,7 @@ Monitor::MonitorLink::MonitorLink( int p_id, const char *p_name ) : id( p_id ) {
mem_size = 0;
mem_ptr = 0;
last_event = 0;
last_event_id = 0;
last_state = IDLE;
last_connect_time = 0;
@ -155,7 +155,7 @@ bool Monitor::MonitorLink::connect() {
}
last_state = shared_data->state;
last_event = shared_data->last_event;
last_event_id = shared_data->last_event_id;
connected = true;
return( true );
@ -221,9 +221,11 @@ bool Monitor::MonitorLink::inAlarm() {
bool Monitor::MonitorLink::hasAlarmed() {
if ( shared_data->state == ALARM ) {
return( true );
} else if ( shared_data->last_event != (unsigned int)last_event ) {
last_event = shared_data->last_event;
}
//} else if ( shared_data->last_event_id != (unsigned int)last_event ) {
// Why test for it, just set it...
last_event_id = shared_data->last_event_id;
//}
return( false );
}
@ -401,7 +403,7 @@ Monitor::Monitor(
shared_data->last_write_index = image_buffer_count;
shared_data->last_read_index = image_buffer_count;
shared_data->last_write_time = 0;
shared_data->last_event = 0;
shared_data->last_event_id = 0;
shared_data->action = (Action)0;
shared_data->brightness = -1;
shared_data->hue = -1;
@ -436,12 +438,14 @@ Monitor::Monitor(
event = 0;
Debug( 1, "Monitor %s has function %d", name, function );
Debug( 1, "Monitor %s LBF = '%s', LBX = %d, LBY = %d, LBS = %d", name, label_format, label_coord.X(), label_coord.Y(), label_size );
Debug( 1, "Monitor %s IBC = %d, WUC = %d, pEC = %d, PEC = %d, EAF = %d, FRI = %d, RBP = %d, ARBP = %d, FM = %d", name, image_buffer_count, warmup_count, pre_event_count, post_event_count, alarm_frame_count, fps_report_interval, ref_blend_perc, alarm_ref_blend_perc, track_motion );
//Set video recording flag for event start constructor and easy reference in code
videoRecording = ((GetOptVideoWriter() == H264PASSTHROUGH) && camera->SupportsNativeVideo());
Debug( 1, "Monitor %s\
function: %d\
label_format: '%s', label_x = %d, label_y = %d, label size = %d \
IBC = %d, WUC = %d, pEC = %d, PEC = %d, EAF = %d, FRI = %d, RBP = %d, ARBP = %d, FM = %d",
name,
function, label_format, label_coord.X(), label_coord.Y(), label_size,
image_buffer_count, warmup_count, pre_event_count, post_event_count, alarm_frame_count, fps_report_interval, ref_blend_perc, alarm_ref_blend_perc, track_motion
);
n_linked_monitors = 0;
linked_monitors = 0;
@ -449,7 +453,7 @@ Monitor::Monitor(
adaptive_skip = true;
ReloadLinkedMonitors( p_linked_monitors );
}
} // Monitor::Monitor
bool Monitor::connect() {
Debug(3, "Connecting to monitor. Purpose is %d", purpose );
@ -560,6 +564,10 @@ bool Monitor::connect() {
} // Monitor::connect
Monitor::~Monitor() {
if ( videoStore ) {
delete videoStore;
videoStore = NULL;
}
if ( timestamps ) {
delete[] timestamps;
timestamps = 0;
@ -738,7 +746,7 @@ unsigned int Monitor::GetLastWriteIndex() const {
}
unsigned int Monitor::GetLastEvent() const {
return( shared_data->last_event );
return( shared_data->last_event_id );
}
double Monitor::GetFPS() const {
@ -1164,7 +1172,7 @@ bool Monitor::Analyse() {
int pending_frames = shared_data->last_write_index - shared_data->last_read_index;
if ( pending_frames < 0 ) pending_frames += image_buffer_count;
Debug( 4, "RI:%d, WI: %d, PF = %d, RM = %d, Step = %d", shared_data->last_read_index, shared_data->last_write_index, pending_frames, read_margin, step );
Debug( 4, "ReadIndex:%d, WriteIndex: %d, PendingFrames = %d, ReadMargin = %d, Step = %d", shared_data->last_read_index, shared_data->last_write_index, pending_frames, read_margin, step );
if ( step <= pending_frames ) {
index = (shared_data->last_read_index+step)%image_buffer_count;
} else {
@ -1375,7 +1383,7 @@ Debug(3,"before DetectMotion");
// Create event
event = new Event( this, *timestamp, "Continuous", noteSetMap, videoRecording );
shared_data->last_event = event->Id();
shared_data->last_event_id = event->Id();
//set up video store data
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
video_store_data->recording = event->StartTime();
@ -1483,7 +1491,7 @@ Debug(3, "Pre Index = (%d) = image_count(%d) %% pre_event_buffer_count (%d)", pr
event = new Event( this, *(image_buffer[pre_index].timestamp), cause, noteSetMap );
}
shared_data->last_event = event->Id();
shared_data->last_event_id = event->Id();
//set up video store data
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
video_store_data->recording = event->StartTime();
@ -2832,13 +2840,15 @@ Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) {
*/
int Monitor::Capture() {
static int FirstCapture = 1; // Used in de-interlacing to indicate whether this is the even or odd image
int captureResult;
unsigned int index = image_count%image_buffer_count;
Image* capture_image = image_buffer[index].image;
unsigned int deinterlacing_value = deinterlacing & 0xff;
ZMPacket *packet;
int captureResult = 0;
if ( deinterlacing_value == 4 ) {
if ( FirstCapture != 1 ) {
/* Copy the next image into the shared memory */
@ -2847,15 +2857,7 @@ int Monitor::Capture() {
/* Capture a new next image */
//Check if FFMPEG camera
// Icon: I don't think we can support de-interlacing on ffmpeg input.... most of the time it will be h264 or mpeg4
if ( ( videowriter == H264PASSTHROUGH ) && camera->SupportsNativeVideo() ) {
captureResult = camera->CaptureAndRecord(*(next_buffer.image),
video_store_data->recording,
video_store_data->event_file );
} else {
captureResult = camera->Capture(*(next_buffer.image));
}
packet = camera->Capture(*(next_buffer.image));
if ( FirstCapture ) {
FirstCapture = 0;
@ -2863,125 +2865,208 @@ int Monitor::Capture() {
}
} else {
//Check if FFMPEG camera
if ( (videowriter == H264PASSTHROUGH ) && camera->SupportsNativeVideo() ) {
//Warning("ZMC: Recording: %d", video_store_data->recording);
captureResult = camera->CaptureAndRecord(*capture_image, video_store_data->recording, video_store_data->event_file);
packet = camera->Capture(*capture_image);
if ( ! packet ) {
packet = new ZMPacket(capture_image);
// Unable to capture image for temporary reason
// Fake a signal loss image
Rgb signalcolor;
signalcolor = rgb_convert(signal_check_colour, ZM_SUBPIX_ORDER_BGR); /* HTML colour code is actually BGR in memory, we want RGB */
capture_image->Fill(signalcolor);
} else {
/* Capture directly into image buffer, avoiding the need to memcpy() */
captureResult = camera->Capture(*capture_image);
captureResult = 1;
}
}
// CaptureAndRecord returns # of frames captured I think
if ( ( videowriter == H264PASSTHROUGH ) && ( captureResult > 0 ) ) {
//video_store_data->frameNumber = captureResult;
captureResult = 0;
}
if ( captureResult != 0 ) {
// Unable to capture image for temporary reason
// Fake a signal loss image
Rgb signalcolor;
signalcolor = rgb_convert(signal_check_colour, ZM_SUBPIX_ORDER_BGR); /* HTML colour code is actually BGR in memory, we want RGB */
capture_image->Fill(signalcolor);
captureResult = 0;
} else {
captureResult = 1;
}
if ( captureResult == 1 ) {
/* Deinterlacing */
if ( deinterlacing_value == 1 ) {
capture_image->Deinterlace_Discard();
} else if ( deinterlacing_value == 2 ) {
capture_image->Deinterlace_Linear();
} else if ( deinterlacing_value == 3 ) {
capture_image->Deinterlace_Blend();
} else if ( deinterlacing_value == 4 ) {
capture_image->Deinterlace_4Field( next_buffer.image, (deinterlacing>>8)&0xff );
} else if ( deinterlacing_value == 5 ) {
capture_image->Deinterlace_Blend_CustomRatio( (deinterlacing>>8)&0xff );
}
if ( orientation != ROTATE_0 ) {
switch ( orientation ) {
case ROTATE_0 : {
// No action required
break;
}
case ROTATE_90 :
case ROTATE_180 :
case ROTATE_270 : {
capture_image->Rotate( (orientation-1)*90 );
break;
}
case FLIP_HORI :
case FLIP_VERT : {
capture_image->Flip( orientation==FLIP_HORI );
break;
int video_stream_id = camera->get_VideoStreamId();
// Should maybe not write to videowriter when no signal FIXME
//Video recording
if ( video_store_data->recording.tv_sec ) {
if ( shared_data->last_event_id != this->GetVideoWriterEventId() ) {
Debug(2, "Have change of event. last_event(%d), our current (%d)", shared_data->last_event_id, this->GetVideoWriterEventId() );
if ( videoStore ) {
Info("Re-starting video storage module");
// I don't know if this is important or not... but I figure we might as well write this last packet out to the store before closing it.
// Also don't know how much it matters for audio.
int ret = videoStore->writePacket( packet );
if ( ret < 0 ) { //Less than zero and we skipped a frame
Warning("Error writing last packet to videostore.");
}
delete videoStore;
videoStore = NULL;
this->SetVideoWriterEventId( 0 );
} // end if videoStore
} // end if end of recording
if ( shared_data->last_event_id and ! videoStore ) {
//Instantiate the video storage module
videoStore = new VideoStore((const char *) video_store_data->event_file, "mp4",
camera->get_VideoStream(),
( record_audio ? camera->get_AudioStream() : NULL ),
this );
if ( ! videoStore->open() ) {
delete videoStore;
videoStore = NULL;
} else {
this->SetVideoWriterEventId( shared_data->last_event_id );
// Need to write out all the frames from the last keyframe?
// No... need to write out all frames from when the event began. Due to PreEventFrames, this could be more than since the last keyframe.
unsigned int packet_count = 0;
ZMPacket *queued_packet;
// Clear all packets that predate the moment when the recording began
packetqueue.clear_unwanted_packets( &video_store_data->recording, video_stream_id );
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() );
int ret = videoStore->writePacket( queued_packet );
if ( ret < 0 ) {
//Less than zero and we skipped a frame
}
delete queued_packet;
} // end while packets in the packetqueue
Debug(2, "Wrote %d queued packets", packet_count );
} // success opening
} // end if ! was recording
} else {
// Not recording
if ( videoStore ) {
Info("Deleting videoStore instance");
delete videoStore;
videoStore = NULL;
this->SetVideoWriterEventId( 0 );
}
// Buffer video packets, since we are not recording.
// All audio packets are keyframes, so only if it's a video keyframe
if ( packet->packet.stream_index == video_stream_id ) {
if ( packet->keyframe ) {
Debug(3, "Clearing queue");
packetqueue.clearQueue( this->GetPreEventCount(), video_stream_id );
}
}
// The following lines should ensure that the queue always begins with a video keyframe
if ( packet->packet.stream_index == camera->get_AudioStreamId() ) {
//Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", 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 );
}
} else if ( packet->packet.stream_index == video_stream_id ) {
if ( packet->keyframe || packetqueue.size() ) // it's a keyframe or we already have something in the queue
packetqueue.queuePacket( packet );
}
} // end if recording or not
if ( videoStore ) {
//Write the packet to our video store, it will be smart enough to know what to do
int ret = videoStore->writePacket( packet );
if ( ret < 0 ) { //Less than zero and we skipped a frame
Warning("problem writing packet");
}
}
if ( capture_image->Size() > camera->ImageSize() ) {
Error( "Captured image %d does not match expected size %d check width, height and colour depth",capture_image->Size(),camera->ImageSize() );
return( -1 );
}
if ( (index == shared_data->last_read_index) && (function > MONITOR) ) {
Warning( "Buffer overrun at index %d, image %d, slow down capture, speed up analysis or increase ring buffer size", index, image_count );
time_t now = time(0);
double approxFps = double(image_buffer_count)/double(now-image_buffer[index].timestamp->tv_sec);
time_t last_read_delta = now - shared_data->last_read_time;
if ( last_read_delta > (image_buffer_count/approxFps) ) {
Warning( "Last image read from shared memory %ld seconds ago, zma may have gone away", last_read_delta )
shared_data->last_read_index = image_buffer_count;
}
}
if ( privacy_bitmask )
capture_image->MaskPrivacy( privacy_bitmask );
gettimeofday( image_buffer[index].timestamp, NULL );
if ( config.timestamp_on_capture ) {
TimestampImage( capture_image, image_buffer[index].timestamp );
}
shared_data->signal = CheckSignal(capture_image);
shared_data->last_write_index = index;
shared_data->last_write_time = image_buffer[index].timestamp->tv_sec;
image_count++;
if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) {
time_t now = image_buffer[index].timestamp->tv_sec;
fps = double(fps_report_interval)/(now-last_fps_time);
//Info( "%d -> %d -> %d", fps_report_interval, now, last_fps_time );
//Info( "%d -> %d -> %lf -> %lf", now-last_fps_time, fps_report_interval/(now-last_fps_time), double(fps_report_interval)/(now-last_fps_time), fps );
Info( "%s: %d - Capturing at %.2lf fps", name, image_count, fps );
last_fps_time = now;
}
// Icon: I'm not sure these should be here. They have nothing to do with capturing
if ( shared_data->action & GET_SETTINGS ) {
shared_data->brightness = camera->Brightness();
shared_data->hue = camera->Hue();
shared_data->colour = camera->Colour();
shared_data->contrast = camera->Contrast();
shared_data->action &= ~GET_SETTINGS;
}
if ( shared_data->action & SET_SETTINGS ) {
camera->Brightness( shared_data->brightness );
camera->Hue( shared_data->hue );
camera->Colour( shared_data->colour );
camera->Contrast( shared_data->contrast );
shared_data->action &= ~SET_SETTINGS;
}
return( 0 );
} // end if de-interlacing or not
if ( ! captureResult ) {
shared_data->signal = false;
return -1;
} // end if captureResults == 1 which is success I think
shared_data->signal = false;
return( -1 );
/* Deinterlacing */
if ( deinterlacing_value == 1 ) {
capture_image->Deinterlace_Discard();
} else if ( deinterlacing_value == 2 ) {
capture_image->Deinterlace_Linear();
} else if ( deinterlacing_value == 3 ) {
capture_image->Deinterlace_Blend();
} else if ( deinterlacing_value == 4 ) {
capture_image->Deinterlace_4Field( next_buffer.image, (deinterlacing>>8)&0xff );
} else if ( deinterlacing_value == 5 ) {
capture_image->Deinterlace_Blend_CustomRatio( (deinterlacing>>8)&0xff );
}
if ( orientation != ROTATE_0 ) {
switch ( orientation ) {
case ROTATE_0 : {
// No action required
break;
}
case ROTATE_90 :
case ROTATE_180 :
case ROTATE_270 : {
capture_image->Rotate( (orientation-1)*90 );
break;
}
case FLIP_HORI :
case FLIP_VERT : {
capture_image->Flip( orientation==FLIP_HORI );
break;
}
}
}
if ( (index == shared_data->last_read_index) && (function > MONITOR) ) {
Warning( "Buffer overrun at index %d, image %d, slow down capture, speed up analysis or increase ring buffer size", index, image_count );
time_t now = time(0);
double approxFps = double(image_buffer_count)/double(now-image_buffer[index].timestamp->tv_sec);
time_t last_read_delta = now - shared_data->last_read_time;
if ( last_read_delta > (image_buffer_count/approxFps) ) {
Warning( "Last image read from shared memory %ld seconds ago, zma may have gone away", last_read_delta )
shared_data->last_read_index = image_buffer_count;
}
}
if ( privacy_bitmask )
capture_image->MaskPrivacy( privacy_bitmask );
gettimeofday( image_buffer[index].timestamp, NULL );
if ( config.timestamp_on_capture ) {
TimestampImage( capture_image, image_buffer[index].timestamp );
}
shared_data->signal = CheckSignal(capture_image);
shared_data->last_write_index = index;
shared_data->last_write_time = image_buffer[index].timestamp->tv_sec;
image_count++;
if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) {
time_t now = image_buffer[index].timestamp->tv_sec;
fps = double(fps_report_interval)/(now-last_fps_time);
//Info( "%d -> %d -> %d", fps_report_interval, now, last_fps_time );
//Info( "%d -> %d -> %lf -> %lf", now-last_fps_time, fps_report_interval/(now-last_fps_time), double(fps_report_interval)/(now-last_fps_time), fps );
Info( "%s: %d - Capturing at %.2lf fps", name, image_count, fps );
last_fps_time = now;
}
// Icon: I'm not sure these should be here. They have nothing to do with capturing
if ( shared_data->action & GET_SETTINGS ) {
shared_data->brightness = camera->Brightness();
shared_data->hue = camera->Hue();
shared_data->colour = camera->Colour();
shared_data->contrast = camera->Contrast();
shared_data->action &= ~GET_SETTINGS;
}
if ( shared_data->action & SET_SETTINGS ) {
camera->Brightness( shared_data->brightness );
camera->Hue( shared_data->hue );
camera->Colour( shared_data->colour );
camera->Contrast( shared_data->contrast );
shared_data->action &= ~SET_SETTINGS;
}
return( 0 );
}
void Monitor::TimestampImage( Image *ts_image, const struct timeval *ts_time ) const {

View File

@ -29,6 +29,9 @@
#include "zm_rgb.h"
#include "zm_zone.h"
#include "zm_event.h"
#include "zm_videostore.h"
#include "zm_packetqueue.h"
class Monitor;
#include "zm_camera.h"
#include "zm_storage.h"
@ -102,7 +105,7 @@ protected:
uint32_t last_write_index; /* +4 */
uint32_t last_read_index; /* +8 */
uint32_t state; /* +12 */
uint32_t last_event; /* +16 */
uint32_t last_event_id; /* +16 */
uint32_t action; /* +20 */
int32_t brightness; /* +24 */
int32_t hue; /* +28 */
@ -158,17 +161,16 @@ protected:
};
//TODO: Technically we can't exclude this struct when people don't have avformat as the Memory.pm module doesn't know about avformat
#if 1
//sizeOf(VideoStoreData) expected to be 4104 bytes on 32bit and 64bit
typedef struct {
uint32_t size;
uint32_t current_event;
char event_file[4096];
timeval recording; // used as both bool and a pointer to the timestamp when recording should begin
//uint32_t frameNumber;
} VideoStoreData;
#endif // HAVE_LIBAVFORMAT
VideoStore *videoStore;
zm_packetqueue packetqueue;
class MonitorLink {
protected:
@ -192,7 +194,7 @@ protected:
volatile VideoStoreData *video_store_data;
int last_state;
int last_event;
int last_event_id;
public:
@ -432,7 +434,7 @@ public:
int GetOptSaveJPEGs() const { return( savejpegspref ); }
VideoWriter GetOptVideoWriter() const { return( videowriter ); }
const std::vector<EncoderParameter_t>* GetOptEncoderParams() const { return( &encoderparamsvec ); }
uint32_t GetLastEventId() const { return shared_data->last_event; }
uint32_t GetLastEventId() const { return shared_data->last_event_id; }
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; }

View File

@ -565,6 +565,7 @@ void MonitorStream::runStream() {
gettimeofday( &now, NULL );
if ( connkey ) {
Debug(2, "checking command Queue");
while(checkCommandQueue()) {
got_command = true;
}
@ -689,7 +690,7 @@ void MonitorStream::runStream() {
} else {
Warning( "Unable to store frame as shared memory invalid" );
}
}
} // end if buffered playback
frame_count++;
}
usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) );
@ -702,7 +703,7 @@ void MonitorStream::runStream() {
Error( "Terminating, last frame sent time %f secs more than maximum of %f", TV_2_FLOAT( now ) - last_frame_sent, max_secs_since_last_sent_frame );
break;
}
}
} // end while
if ( buffered_playback ) {
Debug( 1, "Cleaning swap files from %s", swap_path );
struct stat stat_buf;

View File

@ -24,12 +24,29 @@
using namespace std;
ZMPacket::ZMPacket( ) {
keyframe = 0;
image = NULL;
frame = NULL;
av_init_packet( &packet );
gettimeofday( &timestamp, NULL );
}
ZMPacket::ZMPacket( Image *i ) {
keyframe = 1;
image = i;
frame = NULL;
av_init_packet( &packet );
gettimeofday( &timestamp, NULL );
}
ZMPacket::ZMPacket( AVPacket *p ) {
av_init_packet( &packet );
if ( zm_av_packet_ref( &packet, p ) < 0 ) {
Error("error refing packet");
}
gettimeofday( &timestamp, NULL );
keyframe = p->flags & AV_PKT_FLAG_KEY;
}
ZMPacket::ZMPacket( AVPacket *p, struct timeval *t ) {
@ -38,6 +55,7 @@ ZMPacket::ZMPacket( AVPacket *p, struct timeval *t ) {
Error("error refing packet");
}
timestamp = *t;
keyframe = p->flags & AV_PKT_FLAG_KEY;
}
ZMPacket::~ZMPacket() {
@ -45,5 +63,103 @@ ZMPacket::~ZMPacket() {
}
int ZMPacket::decode( AVCodecContext *ctx ) {
Debug(4, "about to decode video" );
if ( frame ) {
Error("Already have a frame?");
} else {
frame = zm_av_frame_alloc();
}
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
int ret = avcodec_send_packet( ctx, &packet );
if ( ret < 0 ) {
Error( "Unable to send packet: %s", av_make_error_string( ret ) );
av_frame_free( &frame );
return 0;
}
#if HAVE_AVUTIL_HWCONTEXT_H
if ( hwaccel ) {
ret = avcodec_receive_frame( ctx, hwFrame );
if ( ret < 0 ) {
Error( "Unable to receive frame: %s", av_make_error_string( ret ) );
av_frame_free( &frame );
return 0;
}
ret = av_hwframe_transfer_data(frame, hwFrame, 0);
if ( ret < 0 ) {
Error( "Unable to transfer frame: %s", av_make_error_string( ret ) );
av_frame_free( &frame );
return 0;
}
} else {
#endif
ret = avcodec_receive_frame( ctx, frame );
if ( ret < 0 ) {
Error( "Unable to receive frame: %s", av_make_error_string( ret ) );
av_frame_free( &frame );
return 0;
}
#if HAVE_AVUTIL_HWCONTEXT_H
}
#endif
# else
int frameComplete = 0;
ret = zm_avcodec_decode_video( ctx, frame, &frameComplete, &packet );
if ( ret < 0 ) {
Error( "Unable to decode frame at frame %s", av_make_error_string( ret ) );
av_frame_free( &frame );
return 0;
}
if ( ! frameComplete ) {
Debug(1, "incomplete frame?");
av_frame_free( &frame );
return 0;
}
#endif
return 1;
} // 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;
}
colours = ZM_COLOUR_RGB32;
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
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;
}

View File

@ -28,18 +28,26 @@ extern "C" {
#include <sys/time.h>
#endif // __FreeBSD__
#include "zm_image.h"
class ZMPacket {
public:
AVPacket packet;
AVFrame *frame; // Theoretically only filled if needed.
int keyframe;
AVPacket packet; // Input packet, undecoded
AVFrame *frame; // Input image, decoded Theoretically only filled if needed.
Image *image; // Our internal image oject representing this frame
struct timeval timestamp;
public:
AVPacket *av_packet() { return &packet; }
AVFrame *av_frame() { return frame; }
Image *get_image( Image * );
int is_keyframe() { return keyframe; };
int decode( AVCodecContext *ctx );
ZMPacket( AVPacket *packet, struct timeval *timestamp );
ZMPacket( AVPacket *packet );
ZMPacket( Image *image );
ZMPacket();
~ZMPacket();
};

View File

@ -30,45 +30,40 @@
// Class used for storing a box, which is defined as a region
// defined by two coordinates
//
class Polygon
{
class Polygon {
protected:
struct Edge
{
struct Edge {
int min_y;
int max_y;
double min_x;
double _1_m;
static int CompareYX( const void *p1, const void *p2 )
{
static int CompareYX( const void *p1, const void *p2 ) {
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
if ( e1->min_y == e2->min_y )
return( int(e1->min_x - e2->min_x) );
else
return( int(e1->min_y - e2->min_y) );
}
static int CompareX( const void *p1, const void *p2 )
{
static int CompareX( const void *p1, const void *p2 ) {
const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2;
return( int(e1->min_x - e2->min_x) );
}
};
struct Slice
{
struct Slice {
int min_x;
int max_x;
int n_edges;
int *edges;
Slice()
{
Slice() {
min_x = 0;
max_x = 0;
n_edges = 0;
edges = 0;
}
~Slice()
{
~Slice() {
delete edges;
}
};
@ -88,21 +83,18 @@ protected:
void calcCentre();
public:
inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 )
{
inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 ), edges(0), slices(0) {
}
Polygon( int p_n_coords, const Coord *p_coords );
Polygon( const Polygon &p_polygon );
~Polygon()
{
~Polygon() {
delete[] coords;
}
Polygon &operator=( const Polygon &p_polygon );
inline int getNumCoords() const { return( n_coords ); }
inline const Coord &getCoord( int index ) const
{
inline const Coord &getCoord( int index ) const {
return( coords[index] );
}
@ -115,8 +107,7 @@ public:
inline int Height() const { return( extent.Height() ); }
inline int Area() const { return( area ); }
inline const Coord &Centre() const
{
inline const Coord &Centre() const {
return( centre );
}
bool isInside( const Coord &coord ) const;

View File

@ -88,9 +88,8 @@ public:
virtual int Connect() = 0;
virtual int Disconnect() = 0;
virtual int PreCapture() = 0;
virtual int Capture( Image &image ) = 0;
virtual ZMPacket *Capture( Image &image ) = 0;
virtual int PostCapture() = 0;
virtual int CaptureAndRecord( Image &image, timeval recording, char* event_directory )=0;
int Read( int fd, char*buf, int size );
};

View File

@ -281,12 +281,10 @@ int RemoteCameraHttp::ReadData( Buffer &buffer, unsigned int bytes_expected ) {
return( total_bytes_read );
}
int RemoteCameraHttp::GetResponse()
{
int RemoteCameraHttp::GetResponse() {
int buffer_len;
#if HAVE_LIBPCRE
if ( method == REGEXP )
{
if ( method == REGEXP ) {
const char *header = 0;
int header_len = 0;
const char *http_version = 0;
@ -301,10 +299,8 @@ int RemoteCameraHttp::GetResponse()
//int subcontent_length = 0;
//const char *subcontent_type = "";
while ( true )
{
switch( state )
{
while ( true ) {
switch( state ) {
case HEADER :
{
static RegExpr *header_expr = 0;
@ -322,16 +318,14 @@ int RemoteCameraHttp::GetResponse()
}
if ( !header_expr )
header_expr = new RegExpr( "^(.+?\r?\n\r?\n)", PCRE_DOTALL );
if ( header_expr->Match( (char*)buffer, buffer.size() ) == 2 )
{
if ( header_expr->Match( (char*)buffer, buffer.size() ) == 2 ) {
header = header_expr->MatchString( 1 );
header_len = header_expr->MatchLength( 1 );
Debug( 4, "Captured header (%d bytes):\n'%s'", header_len, header );
if ( !status_expr )
status_expr = new RegExpr( "^HTTP/(1\\.[01]) +([0-9]+) +(.+?)\r?\n", PCRE_CASELESS );
if ( status_expr->Match( header, header_len ) < 4 )
{
if ( status_expr->Match( header, header_len ) < 4 ) {
Error( "Unable to extract HTTP status from header" );
return( -1 );
}
@ -370,59 +364,47 @@ int RemoteCameraHttp::GetResponse()
if ( !connection_expr )
connection_expr = new RegExpr( "Connection: ?(.+?)\r?\n", PCRE_CASELESS );
if ( connection_expr->Match( header, header_len ) == 2 )
{
if ( connection_expr->Match( header, header_len ) == 2 ) {
connection_type = connection_expr->MatchString( 1 );
Debug( 3, "Got connection '%s'", connection_type );
}
if ( !content_length_expr )
content_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS );
if ( content_length_expr->Match( header, header_len ) == 2 )
{
if ( content_length_expr->Match( header, header_len ) == 2 ) {
content_length = atoi( content_length_expr->MatchString( 1 ) );
Debug( 3, "Got content length '%d'", content_length );
}
if ( !content_type_expr )
content_type_expr = new RegExpr( "Content-type: ?(.+?)(?:; ?boundary=\x22?(.+?)\x22?)?\r?\n", PCRE_CASELESS );
if ( content_type_expr->Match( header, header_len ) >= 2 )
{
if ( content_type_expr->Match( header, header_len ) >= 2 ) {
content_type = content_type_expr->MatchString( 1 );
Debug( 3, "Got content type '%s'\n", content_type );
if ( content_type_expr->MatchCount() > 2 )
{
if ( content_type_expr->MatchCount() > 2 ) {
content_boundary = content_type_expr->MatchString( 2 );
Debug( 3, "Got content boundary '%s'", content_boundary );
}
}
if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) )
{
if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) {
// Single image
mode = SINGLE_IMAGE;
format = JPEG;
state = CONTENT;
}
else if ( !strcasecmp( content_type, "image/x-rgb" ) )
{
} else if ( !strcasecmp( content_type, "image/x-rgb" ) ) {
// Single image
mode = SINGLE_IMAGE;
format = X_RGB;
state = CONTENT;
}
else if ( !strcasecmp( content_type, "image/x-rgbz" ) )
{
} else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) {
// Single image
mode = SINGLE_IMAGE;
format = X_RGBZ;
state = CONTENT;
}
else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) )
{
} else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) ) {
// Image stream, so start processing
if ( !content_boundary[0] )
{
if ( !content_boundary[0] ) {
Error( "No content boundary found in header '%s'", header );
return( -1 );
}
@ -433,15 +415,12 @@ int RemoteCameraHttp::GetResponse()
//{
//// MPEG stream, coming soon!
//}
else
{
else {
Error( "Unrecognised content type '%s'", content_type );
return( -1 );
}
buffer.consume( header_len );
}
else
{
} else {
Debug( 3, "Unable to extract header from stream, retrying" );
//return( -1 );
}
@ -453,39 +432,33 @@ int RemoteCameraHttp::GetResponse()
static RegExpr *subcontent_length_expr = 0;
static RegExpr *subcontent_type_expr = 0;
if ( !subheader_expr )
{
if ( !subheader_expr ) {
char subheader_pattern[256] = "";
snprintf( subheader_pattern, sizeof(subheader_pattern), "^((?:\r?\n){0,2}?(?:--)?%s\r?\n.+?\r?\n\r?\n)", content_boundary );
subheader_expr = new RegExpr( subheader_pattern, PCRE_DOTALL );
}
if ( subheader_expr->Match( (char *)buffer, (int)buffer ) == 2 )
{
if ( subheader_expr->Match( (char *)buffer, (int)buffer ) == 2 ) {
subheader = subheader_expr->MatchString( 1 );
subheader_len = subheader_expr->MatchLength( 1 );
Debug( 4, "Captured subheader (%d bytes):'%s'", subheader_len, subheader );
if ( !subcontent_length_expr )
subcontent_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS );
if ( subcontent_length_expr->Match( subheader, subheader_len ) == 2 )
{
if ( subcontent_length_expr->Match( subheader, subheader_len ) == 2 ) {
content_length = atoi( subcontent_length_expr->MatchString( 1 ) );
Debug( 3, "Got subcontent length '%d'", content_length );
}
if ( !subcontent_type_expr )
subcontent_type_expr = new RegExpr( "Content-type: ?(.+?)\r?\n", PCRE_CASELESS );
if ( subcontent_type_expr->Match( subheader, subheader_len ) == 2 )
{
if ( subcontent_type_expr->Match( subheader, subheader_len ) == 2 ) {
content_type = subcontent_type_expr->MatchString( 1 );
Debug( 3, "Got subcontent type '%s'", content_type );
}
buffer.consume( subheader_len );
state = CONTENT;
}
else
{
} else {
Debug( 3, "Unable to extract subheader from stream, retrying" );
while ( ! ( buffer_len = ReadData( buffer ) ) ) {
Debug(4, "Timeout waiting to extract subheader");
@ -506,28 +479,19 @@ int RemoteCameraHttp::GetResponse()
*semicolon = '\0';
}
if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) )
{
if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) {
format = JPEG;
}
else if ( !strcasecmp( content_type, "image/x-rgb" ) )
{
} else if ( !strcasecmp( content_type, "image/x-rgb" ) ) {
format = X_RGB;
}
else if ( !strcasecmp( content_type, "image/x-rgbz" ) )
{
} else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) {
format = X_RGBZ;
}
else
{
} else {
Error( "Found unsupported content type '%s'", content_type );
return( -1 );
}
if ( content_length )
{
while ( (long)buffer.size() < content_length )
{
if ( content_length ) {
while ( (long)buffer.size() < content_length ) {
Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length );
if ( ReadData( buffer ) < 0 ) {
Error( "Unable to read content" );
@ -535,42 +499,33 @@ int RemoteCameraHttp::GetResponse()
}
}
Debug( 3, "Got end of image by length, content-length = %d", content_length );
}
else
{
while ( !content_length )
{
} else {
while ( !content_length ) {
while ( ! ( buffer_len = ReadData( buffer ) ) ) {
Debug(4, "Timeout waiting for content");
Debug(4, "Timeout waiting for content");
}
if ( buffer_len < 0 ) {
Error( "Unable to read content" );
return( -1 );
}
static RegExpr *content_expr = 0;
if ( mode == MULTI_IMAGE )
{
if ( !content_expr )
{
if ( mode == MULTI_IMAGE ) {
if ( !content_expr ) {
char content_pattern[256] = "";
snprintf( content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary );
content_expr = new RegExpr( content_pattern, PCRE_DOTALL );
}
if ( content_expr->Match( buffer, buffer.size() ) == 2 )
{
if ( content_expr->Match( buffer, buffer.size() ) == 2 ) {
content_length = content_expr->MatchLength( 1 );
Debug( 3, "Got end of image by pattern, content-length = %d", content_length );
}
}
}
}
if ( mode == SINGLE_IMAGE )
{
if ( mode == SINGLE_IMAGE ) {
state = HEADER;
Disconnect();
}
else
{
} else {
state = SUBHEADER;
}
Debug( 3, "Returning %d (%d) bytes of captured content", content_length, buffer.size() );
@ -584,12 +539,10 @@ int RemoteCameraHttp::GetResponse()
}
}
}
}
else
} else
#endif // HAVE_LIBPCRE
{
if ( method == REGEXP )
{
if ( method == REGEXP ) {
Warning( "Unable to use netcam regexps as not compiled with libpcre" );
}
static const char *http_match = "HTTP/";
@ -1061,77 +1014,66 @@ int RemoteCameraHttp::GetResponse()
}
}
return( 0 );
}
} // end RemoteCameraHttp::GetResponse
int RemoteCameraHttp::PreCapture()
{
if ( sd < 0 )
{
int RemoteCameraHttp::PreCapture() {
if ( sd < 0 ) {
Connect();
if ( sd < 0 )
{
if ( sd < 0 ) {
Error( "Unable to connect to camera" );
return( -1 );
}
mode = SINGLE_IMAGE;
buffer.clear();
}
if ( mode == SINGLE_IMAGE )
{
if ( SendRequest() < 0 )
{
if ( mode == SINGLE_IMAGE ) {
if ( SendRequest() < 0 ) {
Error( "Unable to send request" );
Disconnect();
return( -1 );
}
}
return( 0 );
}
} // end int RemoteCameraHttp::PreCapture()
int RemoteCameraHttp::Capture( Image &image )
{
ZMPacket * RemoteCameraHttp::Capture( Image &image ) {
int content_length = GetResponse();
if ( content_length == 0 )
{
if ( content_length == 0 ) {
Warning( "Unable to capture image, retrying" );
return( 1 );
return NULL;
}
if ( content_length < 0 )
{
if ( content_length < 0 ) {
Error( "Unable to get response, disconnecting" );
Disconnect();
return( -1 );
return NULL;
}
switch( format )
{
switch( format ) {
case JPEG :
{
if ( !image.DecodeJpeg( buffer.extract( content_length ), content_length, colours, subpixelorder ) )
{
if ( !image.DecodeJpeg( buffer.extract( content_length ), content_length, colours, subpixelorder ) ) {
Error( "Unable to decode jpeg" );
Disconnect();
return( -1 );
return NULL;
}
break;
}
case X_RGB :
{
if ( content_length != (long)image.Size() )
{
if ( content_length != (long)image.Size() ) {
Error( "Image length mismatch, expected %d bytes, content length was %d", image.Size(), content_length );
Disconnect();
return( -1 );
return NULL;
}
image.Assign( width, height, colours, subpixelorder, buffer, imagesize );
break;
}
case X_RGBZ :
{
if ( !image.Unzip( buffer.extract( content_length ), content_length ) )
{
if ( !image.Unzip( buffer.extract( content_length ), content_length ) ) {
Error( "Unable to unzip RGB image" );
Disconnect();
return( -1 );
return NULL;
}
image.Assign( width, height, colours, subpixelorder, buffer, imagesize );
break;
@ -1140,13 +1082,13 @@ int RemoteCameraHttp::Capture( Image &image )
{
Error( "Unexpected image format encountered" );
Disconnect();
return( -1 );
return NULL;
}
}
return( 0 );
}
ZMPacket *packet = new ZMPacket( &image );
return packet;
} // end ZmPacket *RmoteCameraHttp::Capture( &image );
int RemoteCameraHttp::PostCapture()
{
int RemoteCameraHttp::PostCapture() {
return( 0 );
}

View File

@ -44,7 +44,22 @@ protected:
enum { SIMPLE, REGEXP } method;
public:
RemoteCameraHttp( unsigned int p_monitor_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, 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 );
RemoteCameraHttp(
unsigned int p_monitor_id,
const std::string &method,
const std::string &host,
const std::string &port,
const std::string &path,
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
);
~RemoteCameraHttp();
void Initialise();
@ -55,9 +70,8 @@ public:
int ReadData( Buffer &buffer, unsigned int bytes_expected=0 );
int GetResponse();
int PreCapture();
int Capture( Image &image );
ZMPacket *Capture( Image &image );
int PostCapture();
int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) {return(0);};
};
#endif // ZM_REMOTE_CAMERA_HTTP_H

View File

@ -288,18 +288,19 @@ struct image_def image_def;
return 0;
}
int RemoteCameraNVSocket::Capture( Image &image ) {
ZMPacket * RemoteCameraNVSocket::Capture( Image &image ) {
if ( SendRequest("GetNextImage") < 0 ) {
Warning( "Unable to capture image, retrying" );
return( 1 );
return NULL;
}
if ( Read( sd, buffer, imagesize ) < imagesize ) {
Warning( "Unable to capture image, retrying" );
return( 1 );
return NULL;
}
image.Assign( width, height, colours, subpixelorder, buffer, imagesize );
return( 0 );
ZMPacket *packet = new ZMPacket( &image );
return packet;
}
int RemoteCameraNVSocket::PostCapture()

View File

@ -68,7 +68,7 @@ bool p_record_audio );
int ReadData( Buffer &buffer, unsigned int bytes_expected=0 );
int GetResponse();
int PreCapture();
int Capture( Image &image );
ZMPacket * Capture( Image &image );
int PostCapture();
int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) {return(0);};
};

View File

@ -63,13 +63,13 @@ RemoteCameraRtsp::RemoteCameraRtsp( unsigned int p_monitor_id, const std::string
mConvertContext = NULL;
#endif
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
if(colours == ZM_COLOUR_RGB32) {
if ( colours == ZM_COLOUR_RGB32 ) {
subpixelorder = ZM_SUBPIX_ORDER_RGBA;
imagePixFormat = AV_PIX_FMT_RGBA;
} else if(colours == ZM_COLOUR_RGB24) {
} else if ( colours == ZM_COLOUR_RGB24 ) {
subpixelorder = ZM_SUBPIX_ORDER_RGB;
imagePixFormat = AV_PIX_FMT_RGB24;
} else if(colours == ZM_COLOUR_GRAY8) {
} else if ( colours == ZM_COLOUR_GRAY8 ) {
subpixelorder = ZM_SUBPIX_ORDER_NONE;
imagePixFormat = AV_PIX_FMT_GRAY8;
} else {
@ -219,7 +219,7 @@ int RemoteCameraRtsp::PrimeCapture() {
mFrame = avcodec_alloc_frame();
#endif
if(mRawFrame == NULL || mFrame == NULL)
if ( mRawFrame == NULL || mFrame == NULL )
Fatal( "Unable to allocate frame(s)");
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
@ -259,16 +259,17 @@ int RemoteCameraRtsp::PreCapture() {
return( 0 );
}
int RemoteCameraRtsp::Capture( Image &image ) {
ZMPacket * RemoteCameraRtsp::Capture( Image &image ) {
AVPacket packet;
ZMPacket *zmpacket = NULL;
uint8_t* directbuffer;
int frameComplete = false;
/* Request a writeable buffer of the target image */
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
if(directbuffer == NULL) {
if ( directbuffer == NULL ) {
Error("Failed requesting writeable buffer for the captured image.");
return (-1);
return NULL;
}
while ( true ) {
@ -284,19 +285,19 @@ int RemoteCameraRtsp::Capture( Image &image ) {
if ( !buffer.size() )
return( -1 );
if(mCodecContext->codec_id == AV_CODEC_ID_H264) {
if ( mCodecContext->codec_id == AV_CODEC_ID_H264 ) {
// SPS and PPS frames should be saved and appended to IDR frames
int nalType = (buffer.head()[3] & 0x1f);
// SPS The SPS NAL unit contains parameters that apply to a series of consecutive coded video pictures
if(nalType == 7) {
if ( nalType == 7 ) {
lastSps = buffer;
continue;
} else if(nalType == 8) {
} else if ( nalType == 8 ) {
// PPS The PPS NAL unit contains parameters that apply to the decoding of one or more individual pictures inside a coded video sequence
lastPps = buffer;
continue;
} else if(nalType == 5) {
} else if ( nalType == 5 ) {
// IDR
buffer += lastSps;
buffer += lastPps;
@ -337,10 +338,10 @@ int RemoteCameraRtsp::Capture( Image &image ) {
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height );
#if HAVE_LIBSWSCALE
if(mConvertContext == NULL) {
if ( mConvertContext == NULL ) {
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
if(mConvertContext == NULL)
if ( mConvertContext == NULL )
Fatal( "Unable to create conversion context");
}
@ -352,18 +353,18 @@ int RemoteCameraRtsp::Capture( Image &image ) {
frameCount++;
zm_packet = new ZMPacket( &packet, mFrame, &image );
} /* frame complete */
zm_av_packet_unref( &packet );
} /* getFrame() */
if(frameComplete)
return (0);
if ( frameComplete ) break;
} // end while true
// can never get here.
return (0);
return zm_packet;
}
//Function to handle capture and store

View File

@ -84,9 +84,8 @@ public:
int PrimeCapture();
int PreCapture();
int Capture( Image &image );
ZMPacket * Capture( Image &image );
int PostCapture();
int CaptureAndRecord( Image &image, timeval recording, char* event_directory );
};
#endif // ZM_REMOTE_CAMERA_RTSP_H

View File

@ -265,6 +265,7 @@ void StreamBase::openComms() {
Warning("Socket lock path was truncated.");
length = sizeof(sock_path_lock)-1;
}
Debug( 1, "Trying to open the lock on %s", sock_path_lock );
lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR);
if ( lock_fd <= 0 ) {

View File

@ -132,6 +132,7 @@ public:
memset( &loc_addr, 0, sizeof(loc_addr) );
memset( &rem_sock_path, 0, sizeof(rem_sock_path) );
memset( &rem_addr, 0, sizeof(rem_addr) );
memset( &sock_path_lock, 0, sizeof(sock_path_lock) );
base_fps = 0.0;
effective_fps = 0.0;

View File

@ -33,7 +33,7 @@ extern "C" {
VideoStore::VideoStore(const char *filename_in, const char *format_in,
AVStream *p_video_in_stream,
AVStream *p_audio_in_stream, int64_t nStartTime,
AVStream *p_audio_in_stream,
Monitor *monitor) {
video_in_stream = p_video_in_stream;
audio_in_stream = p_audio_in_stream;
@ -673,6 +673,18 @@ void VideoStore::dumpPacket(AVPacket *pkt) {
Debug(1, "%s:%d:DEBUG: %s", __FILE__, __LINE__, b);
}
int VideoStore::writePacket( ZMPacket *ipkt ) {
if ( ipkt->stream_index == video_in_stream.index ) {
return writeVideoFramePacket( &ipkt.packet );
} else if ( ipkt->stream_index == audio_in_stream.index ) {
return writeAudioFramePacket( &ipkt.packet );
} else {
Error("Unknown stream type in packet (%d) out input video stream is (%d) and audio is (%d)",
ipkt->packet.stream_index, video_in_stream->index, ( audio_in_stream ? audio_in_stream->index : -1 )
);
}
}
int VideoStore::writeVideoFramePacket(AVPacket *ipkt) {
av_init_packet(&opkt);

View File

@ -13,6 +13,7 @@ extern "C" {
#if HAVE_LIBAVCODEC
#include "zm_monitor.h"
#include "zm_packet.h"
class VideoStore {
private:
@ -76,13 +77,13 @@ public:
const char *format_in,
AVStream *video_in_stream,
AVStream *audio_in_stream,
int64_t nStartTime,
Monitor * p_monitor);
bool open();
~VideoStore();
int writeVideoFramePacket( AVPacket *pkt );
int writeAudioFramePacket( AVPacket *pkt );
int writePacket( ZMPacket *pkt );
void dumpPacket( AVPacket *pkt );
};

View File

@ -57,8 +57,7 @@ behind.
#include "zm_signal.h"
#include "zm_monitor.h"
void Usage()
{
void Usage() {
fprintf( stderr, "zma -m <monitor_id>\n" );
fprintf( stderr, "Options:\n" );
fprintf( stderr, " -m, --monitor <monitor_id> : Specify which monitor to use\n" );
@ -67,8 +66,7 @@ void Usage()
exit( 0 );
}
int main( int argc, char *argv[] )
{
int main( int argc, char *argv[] ) {
self = argv[0];
srand( getpid() * time( 0 ) );
@ -82,18 +80,15 @@ int main( int argc, char *argv[] )
{0, 0, 0, 0}
};
while (1)
{
while (1) {
int option_index = 0;
int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index);
if (c == -1)
{
if ( c == -1 ) {
break;
}
switch (c)
{
switch (c) {
case 'm':
id = atoi(optarg);
break;
@ -110,8 +105,7 @@ int main( int argc, char *argv[] )
}
}
if (optind < argc)
{
if (optind < argc) {
fprintf( stderr, "Extraneous options, " );
while (optind < argc)
printf ("%s ", argv[optind++]);
@ -119,8 +113,7 @@ int main( int argc, char *argv[] )
Usage();
}
if ( id < 0 )
{
if ( id < 0 ) {
fprintf( stderr, "Bogus monitor %d\n", id );
Usage();
exit( 0 );
@ -137,8 +130,7 @@ int main( int argc, char *argv[] )
Monitor *monitor = Monitor::Load( id, true, Monitor::ANALYSIS );
if ( monitor )
{
if ( monitor ) {
Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() );
zmSetDefaultHupHandler();
@ -154,43 +146,34 @@ int main( int argc, char *argv[] )
monitor->UpdateAdaptiveSkip();
last_analysis_update_time = time( 0 );
while( !zm_terminate )
{
while( !zm_terminate ) {
// Process the next image
sigprocmask( SIG_BLOCK, &block_set, 0 );
// Some periodic updates are required for variable capturing framerate
if ( analysis_update_delay )
{
if ( analysis_update_delay ) {
cur_time = time( 0 );
if ( (unsigned int)( cur_time - last_analysis_update_time ) > analysis_update_delay )
{
if ( (unsigned int)( cur_time - last_analysis_update_time ) > analysis_update_delay ) {
analysis_rate = monitor->GetAnalysisRate();
monitor->UpdateAdaptiveSkip();
last_analysis_update_time = cur_time;
}
}
if ( !monitor->Analyse() )
{
if ( !monitor->Analyse() ) {
usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE );
}
else if ( analysis_rate )
{
} else if ( analysis_rate ) {
usleep( analysis_rate );
}
if ( zm_reload )
{
if ( zm_reload ) {
monitor->Reload();
zm_reload = false;
}
sigprocmask( SIG_UNBLOCK, &block_set, 0 );
}
delete monitor;
}
else
{
} else {
fprintf( stderr, "Can't find monitor with id of %d\n", id );
}
Image::Deinitialise();

View File

@ -6,7 +6,7 @@ use Getopt::Long ();
my $opts = {};
Getopt::Long::GetOptions($opts, 'help', 'output=s',
'pid_file=s', 'db_port=s', 'db_name=s', 'db_host=s', 'db_user=s', 'db_pass=s',
'pid_file=s',
'min_port=s','max_port=s', 'debug=s',
'server_name=s','error_log=s','protocol=s',
);
@ -100,5 +100,19 @@ if ( open( F, "> $$opts{output}" ) ) {
die "Error opening $$opts{output}, Reason: $!";
} # end if
sub usage {
print "
Usage: generate-apache-config.pl
--help output this help.
--output=file the file to output the config to,
--min_port= The starting port. port 80 or 443 will be added as appropriate depending on protocol.
--max_port= The ending port.
--debug= more verbose output
--server_name=[servername]
--error_log
--protocol=[http|https] Whether to turn on https for this host. Will assume a letsencrypt setup for keys.
";
exit 1;
}
1;
__END__

View File

@ -1 +1 @@
1.31.7
1.31.9

View File

@ -391,8 +391,8 @@ function getNearEvents() {
$result['NextEventId'] = empty($nextEvent)?0:$nextEvent['Id'];
$result['PrevEventStartTime'] = empty($prevEvent)?0:$prevEvent['StartTime'];
$result['NextEventStartTime'] = empty($nextEvent)?0:$nextEvent['StartTime'];
$result['PrevEventDefVideoPath'] = empty($prevEvent)?0:(getEventDefaultVideoPath($prevEvent));
$result['NextEventDefVideoPath'] = empty($nextEvent)?0:(getEventDefaultVideoPath($nextEvent));
$result['PrevEventDefVideoPath'] = empty($prevEvent)?0:(getEventDefaultVideoPath($prevEvent['Id']));
$result['NextEventDefVideoPath'] = empty($nextEvent)?0:(getEventDefaultVideoPath($nextEvent['Id']));
return( $result );
}

View File

@ -1,7 +1,7 @@
<?php
class Event {
public function __construct( $IdOrRow ) {
public function __construct( $IdOrRow = null ) {
$row = NULL;
if ( $IdOrRow ) {
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
@ -19,19 +19,28 @@ class Event {
Error("Unknown argument passed to Event Constructor ($IdOrRow)");
return;
}
} # end if isset($IdOrRow)
if ( $row ) {
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
} else {
Error('No row for Event ' . $IdOrRow );
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error('No row for Event ' . $IdOrRow . " from $file:$line");
}
} # end if isset($IdOrRow)
} // end function __construct
public function Storage() {
return new Storage( isset($this->{'StorageId'}) ? $this->{'StorageId'} : NULL );
public function Storage( $new = null ) {
if ( $new ) {
$this->{'Storage'} = $new;
}
if ( ! ( array_key_exists( 'Storage', $this ) and $this->{'Storage'} ) ) {
$this->{'Storage'} = new Storage( isset($this->{'StorageId'}) ? $this->{'StorageId'} : NULL );
}
return $this->{'Storage'};
}
public function Monitor() {
@ -39,10 +48,16 @@ class Event {
}
public function __call( $fn, array $args){
if ( count( $args ) ) {
$this->{$fn} = $args[0];
}
if ( array_key_exists( $fn, $this ) ) {
return $this->{$fn};
#array_unshift($args, $this);
#call_user_func_array( $this->{$fn}, $args);
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Warning( "Unknown function call Event->$fn from $file:$line" );
}
}
@ -163,7 +178,11 @@ class Event {
} // end function getStreamSrc
function DiskSpace() {
return folder_size( $this->Path() );
if ( null === $this->{'DiskSpace'} ) {
$this->{'DiskSpace'} = folder_size( $this->Path() );
dbQuery( 'UPDATE Events SET DiskSpace=? WHERE Id=?', array( $this->{'DiskSpace'}, $this->{'Id'} ) );
}
return $this->{'DiskSpace'};
}
function createListThumbnail( $overwrite=false ) {
@ -317,6 +336,41 @@ class Event {
return( $imageData );
}
public static function find_all( $parameters = null, $options = null ) {
$filters = array();
$sql = 'SELECT * FROM Events ';
$values = array();
if ( $parameters ) {
$fields = array();
$sql .= 'WHERE ';
foreach ( $parameters as $field => $value ) {
if ( $value == null ) {
$fields[] = $field.' IS NULL';
} else if ( is_array( $value ) ) {
$func = function(){return '?';};
$fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')';
$values += $value;
} else {
$fields[] = $field.'=?';
$values[] = $value;
}
}
$sql .= implode(' AND ', $fields );
}
if ( $options and isset($options['order']) ) {
$sql .= ' ORDER BY ' . $options['order'];
}
$result = dbQuery($sql, $values);
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Event');
foreach ( $results as $row => $obj ) {
$filters[] = $obj;
}
return $filters;
}
} # end class
?>

View File

@ -3,7 +3,7 @@ require_once( 'database.php' );
require_once( 'Event.php' );
class Frame {
public function __construct( $IdOrRow ) {
public function __construct( $IdOrRow=null ) {
$row = NULL;
if ( $IdOrRow ) {
if ( is_integer( $IdOrRow ) or ctype_digit($IdOrRow) ) {
@ -17,15 +17,15 @@ class Frame {
Error("Unknown argument passed to Frame Constructor ($IdOrRow)");
return;
}
} # end if isset($IdOrRow)
if ( $row ) {
foreach ($row as $k => $v) {
$this->{$k} = $v;
if ( $row ) {
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
} else {
Error("No row for Frame " . $IdOrRow );
}
} else {
Error("No row for Frame " . $IdOrRow );
}
} # end if isset($IdOrRow)
} // end function __construct
public function Storage() {
@ -36,10 +36,16 @@ class Frame {
return new Event( $this->{'EventId'} );
}
public function __call( $fn, array $args){
if( array_key_exists( $fn, $this ) ) {
if ( count( $args ) ) {
$this->{$fn} = $args[0];
}
if ( array_key_exists( $fn, $this ) ) {
return $this->{$fn};
#array_unshift($args, $this);
#call_user_func_array( $this->{$fn}, $args);
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Warning( "Unknown function call Frame->$fn from $file:$line" );
}
}

View File

@ -87,7 +87,15 @@ public $defaults = array(
}
public function delete() {
dbQuery( 'DELETE FROM Groups WHERE Id = ?', array($this->{'Id'}) );
if ( array_key_exists( 'Id', $this ) ) {
dbQuery( 'DELETE FROM Groups WHERE Id = ?', array($this->{'Id'}) );
if ( isset($_COOKIE['zmGroup']) ) {
if ( $this->{'Id'} == $_COOKIE['zmGroup'] ) {
unset( $_COOKIE['zmGroup'] );
setcookie( 'zmGroup', '', time()-3600*24*2 );
}
}
}
} # end function delete()
public function set( $data ) {
@ -172,5 +180,34 @@ public $defaults = array(
}
return $groupSql;
} # end public static function get_group_sql( $group_id )
public static function get_monitors_dropdown( $options = null ) {
$monitor_id = 0;
if ( isset( $_REQUEST['monitor_id'] ) ) {
$monitor_id = $_REQUEST['monitor_id'];
} else if ( isset($_COOKIE['zmMonitorId']) ) {
$monitor_id = $_COOKIE['zmMonitorId'];
}
$sql = 'SELECT * FROM Monitors';
if ( $options ) {
$sql .= ' WHERE '. implode(' AND ', array(
( isset($options['groupSql']) ? $options['groupSql']:'')
) ).' ORDER BY Sequence ASC';
}
$monitors_dropdown = array(''=>'All');
foreach ( dbFetchAll( $sql ) as $monitor ) {
if ( !visibleMonitor( $monitor['Id'] ) ) {
continue;
}
$monitors_dropdown[$monitor['Id']] = $monitor['Name'];
}
echo htmlSelect( 'monitor_id', $monitors_dropdown, $monitor_id, array('onchange'=>'changeMonitor(this);') );
return $monitor_id;
}
} # end class Group
?>

View File

@ -1,10 +1,18 @@
<?php
require_once( 'database.php' );
#$storage_cache = array();
class Storage {
public function __construct( $IdOrRow = NULL ) {
$row = NULL;
if ( $IdOrRow ) {
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
#if ( isset( $storage_cache[$IdOrRow] ) ) {
#Warning("using cached object for $dOrRow");
#return $storage_cache[$IdOrRow];
#} else {
#Warning("Not using cached object for $dOrRow");
$row = dbFetchOne( 'SELECT * FROM Storage WHERE Id=?', NULL, array( $IdOrRow ) );
if ( ! $row ) {
Error("Unable to load Storage record for Id=" . $IdOrRow );
@ -17,9 +25,11 @@ class Storage {
foreach ($row as $k => $v) {
$this->{$k} = $v;
}
#$storage_cache[$IdOrRow] = $this;
} else {
$this->{'Name'} = '';
$this->{'Path'} = '';
$this->{'Type'} = 'local';
}
}
@ -48,10 +58,16 @@ class Storage {
}
public function __call( $fn, array $args= NULL){
if(isset($this->{$fn})){
if ( count( $args ) ) {
$this->{$fn} = $args[0];
}
if ( array_key_exists( $fn, $this ) ) {
return $this->{$fn};
#array_unshift($args, $this);
#call_user_func_array( $this->{$fn}, $args);
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Warning( "Unknown function call Storage->$fn from $file:$line" );
}
}
public static function find_all() {
@ -60,6 +76,7 @@ class Storage {
$results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Storage' );
foreach ( $results as $row => $obj ) {
$storage_areas[] = $obj;
$storage_cache[$obj->Id()] = $obj;
}
return $storage_areas;
}
@ -73,7 +90,7 @@ class Storage {
return 0;
}
$total = disk_total_space( $path );
$total = $this->disk_total_space();
if ( ! $total ) {
Error("disk_total_space returned false for " . $path );
return 0;
@ -85,5 +102,29 @@ class Storage {
$usage = round(($total - $free) / $total * 100);
return $usage;
}
public function disk_total_space() {
if ( ! array_key_exists( 'disk_total_space', $this ) ) {
$this->{'disk_total_space'} = disk_total_space( $this->Path() );
}
return $this->{'disk_total_space'};
}
public function disk_used_space() {
# This isn't a function like this in php, so we have to add up the space used in each event.
if ( ! array_key_exists( 'disk_used_space', $this ) ) {
$used = 0;
if ( $this->{'Type'} == 's3fs' ) {
foreach ( Event::find_all( array( 'StorageId'=>$this->Id() ) ) as $Event ) {
$Event->Storage( $this ); // Prevent further db hit
$used += $Event->DiskSpace();
}
} else {
$path = $this->Path();
$used = disk_total_space( $path ) - disk_free_space( $path );;
}
$this->{'disk_used_space'} = $used;
}
return $this->{'disk_used_space'};
}
}
?>

View File

@ -602,10 +602,10 @@ Warning("Addterm");
}
// Group edit actions
# Should probably verify that each monitor id is a valid monitor, that we have access to. However at the moment, you have to have System permissions to do this
if ( canEdit( 'Groups' ) ) {
if ( $action == 'group' ) {
# Should probably verfy that each monitor id is a valid monitor, that we have access to. HOwever at the moment, you have to have System permissions to do this
$monitors = empty( $_POST['newGroup']['MonitorIds'] ) ? '' : implode(',', $_POST['newGroup']['MonitorIds'] );
$monitors = empty( $_POST['newGroup']['MonitorIds'] ) ? '' : implode(',', $_POST['newGroup']['MonitorIds']);
if ( !empty($_POST['gid']) ) {
dbQuery( 'UPDATE Groups SET Name=?, ParentId=?, MonitorIds=? WHERE Id=?',
array($_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), $monitors, $_POST['gid']) );
@ -614,18 +614,21 @@ Warning("Addterm");
array( $_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), $monitors ) );
}
$view = 'none';
}
if ( !empty($_REQUEST['gid']) && $action == 'delete' ) {
dbQuery( 'DELETE FROM Groups WHERE Id = ?', array($_REQUEST['gid']) );
if ( isset($_COOKIE['zmGroup']) ) {
if ( $_REQUEST['gid'] == $_COOKIE['zmGroup'] ) {
unset( $_COOKIE['zmGroup'] );
setcookie( 'zmGroup', '', time()-3600*24*2 );
$refreshParent = true;
$refreshParent = true;
} else if ( $action == 'delete' ) {
if ( !empty($_REQUEST['gid']) ) {
if ( is_array( $_REQUEST['gid'] ) ) {
foreach( $_REQUEST['gid'] as $gid ) {
$Group = new Group( $gid );
$Group->delete();
}
} else {
$Group = new Group( $_REQUEST['gid'] );
$Group->delete();
}
}
}
$refreshParent = true;
$refreshParent = true;
} # end if action
} // end if can edit groups
// System edit actions

View File

@ -132,7 +132,7 @@ function dbQuery( $sql, $params=NULL ) {
//else
//Warning("SQL: $sql" );
} catch(PDOException $e) {
Fatal( "SQL-ERR '".$e->getMessage()."', statement was '".$sql."'" );
Error( "SQL-ERR '".$e->getMessage()."', statement was '".$sql."' params:" . implode(',',$params) );
}
return( $result );
}

View File

@ -161,7 +161,10 @@ function generateAuthHash( $useRemoteAddr ) {
}
$auth = md5( $authKey );
if ( session_status() == PHP_SESSION_NONE ) {
Warning("Session is not active. AuthHash will not be cached.");
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Warning("Session is not active. AuthHash will not be cached. called from $file:$line");
}
$_SESSION['AuthHash'] = $auth;
$_SESSION['AuthHashGeneratedAt'] = time();
@ -471,7 +474,7 @@ function getEventPath( $event ) {
function getEventDefaultVideoPath( $event ) {
$Event = new Event( $event );
return $Event->getStreamSrc( array( "mode=mpeg&format=h264" ) );
return $Event->getStreamSrc( array( "mode"=>"mpeg", "format"=>"h264" ) );
//$Event->Path().'/'.$event['DefaultVideo'];
}
@ -557,10 +560,15 @@ function htmlSelect( $name, $contents, $values, $behaviours=false ) {
$html = "<select name=\"$name\" id=\"$name\"$behaviourText>";
foreach ( $contents as $value=>$text ) {
if ( is_array( $text ) )
$text = $text['Name'];
else if ( is_object( $text ) )
$text = $text->Name();
//for ( $i = 0; $i < count($contents); $i +=2 ) {
//$value = $contents[$i];
//$text = $contents[$i+1];
$selected = is_array( $values ) ? in_array( $value, $values ) : $value==$values;
$selected = is_array( $values ) ? in_array( $value, $values ) : !strcmp($value, $values);
//Warning("Selected is $selected from $value and $values");
$html .= "<option value=\"$value\"".($selected?" selected=\"selected\"":'').">$text</option>";
}
$html .= '</select>';
@ -867,8 +875,17 @@ function zmcControl( $monitor, $mode=false ) {
} else {
$Server = new Server( $monitor['ServerId'] );
#$url = $Server->Url() . '/zm/api/monitors.json?auth='.generateAuthHash( $_SESSION['remoteAddr'] );
$url = $Server->Url() . '/zm/api/monitors.json?user='.$_SESSION['username'].'&pass='.$_SESSION['passwordHash'];
$url = $Server->Url() . '/zm/api/monitors/'.$monitor['Id'].'.json';
if ( ZM_OPT_USE_AUTH ) {
if ( ZM_AUTH_RELAY == 'hashed' ) {
$url .= '&auth='.generateAuthHash( ZM_AUTH_HASH_IPS );
} elseif ( ZM_AUTH_RELAY == 'plain' ) {
$url = '&user='.$_SESSION['username'];
$url = '&pass='.$_SESSION['password'];
} elseif ( ZM_AUTH_RELAY == 'none' ) {
$url = '&user='.$_SESSION['username'];
}
}
$data = array('Monitor[Function]' => $monitor['Function'] );
// use key 'http' even if you send the request to https://...
@ -2194,15 +2211,20 @@ function getStreamMode( ) {
function folder_size($dir) {
$size = 0;
foreach (glob(rtrim($dir, '/').'/*', GLOB_NOSORT) as $each) {
$size += is_file($each) ? filesize($each) : folderSize($each);
$size += is_file($each) ? filesize($each) : folder_size($each);
}
return $size;
} // end function folder_size
function human_filesize($bytes, $decimals = 2) {
$sz = 'BKMGTP';
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
function human_filesize($size, $precision = 2) {
$units = array('B','kB','MB','GB','TB','PB','EB','ZB','YB');
$step = 1024;
$i = 0;
while (($size / $step) > 0.9) {
$size = $size / $step;
$i++;
}
return round($size, $precision).$units[$i];
}
function csrf_startup() {

View File

@ -1,3 +1,28 @@
.vjsMessage {
font-size: 2em;
line-height: 1.5;
color: white;
background-color: black;
display: inline-block;
}
.alarmCue {
background-color: #222222;
height: 1.5em;
text-align: left;
margin: 0 auto 0 auto;
}
.alarmCue span {
background-color:red;
height: 1.5em;
display: inline-block;
}
span.noneCue {
background: none;
}
#dataBar {
width: 100%;
margin: 2px auto;
@ -13,6 +38,10 @@
padding: 2px;
}
#eventVideo {
display: inline-block;
}
#menuBar1 {
width: 100%;
padding: 3px 0;
@ -147,22 +176,14 @@
#progressBar {
position: relative;
border: 1px solid #666666;
height: 15px;
margin: 0 auto;
top: -1.5em;
height: 1.5em;
margin: 0 auto -1.5em auto;
}
#progressBar .progressBox {
position: absolute;
top: 0px;
left: 0px;
height: 15px;
background: #eeeeee;
border-left: 1px solid #999999;
}
#progressBar .complete {
background: #aaaaaa;
height: 1.5em;
background: rgba(170, 170, 170, .7);
}
#eventStills {

View File

@ -1,3 +1,32 @@
.vjsMessage {
font-size: 2em;
line-height: 1.5;
color: white;
background-color: black;
display: inline-block;
}
.alarmCue {
background-color: #222222;
height: 1.5em;
text-align: left;
margin: 0 auto 0 auto;
}
.alarmCue span {
background-color:red;
height: 1.5em;
display: inline-block;
}
span.noneCue {
background: none;
}
#eventVideo {
display: inline-block;
}
#dataBar {
width: 100%;
margin: 2px auto;
@ -130,22 +159,14 @@
#progressBar {
position: relative;
border: 1px solid #666666;
height: 15px;
margin: 0 auto;
top: -1.5em;
height: 1.5em;
margin: 0 auto -1.5em auto;
}
#progressBar .progressBox {
position: absolute;
top: 0px;
left: 0px;
height: 15px;
background: #eeeeee;
border-left: 1px solid #999999;
}
#progressBar .complete {
background: #aaaaaa;
height: 1.5em;
background: rgba(170, 170, 170, .7);
}
#eventStills {
@ -233,6 +254,10 @@
background: #dddddd;
}
#eventVideo {
display: inline-block;
}
#thumbsKnob {
width: 8px;
height: 10px;

View File

@ -21,12 +21,7 @@
* Primary look and feel styles
*/
html {
height: 100%;
}
body {
height: 100%;
font-family: "Open Sans", Verdana, Arial, Helvetica, sans-serif;
font-size: 13px;
color: #333333;

View File

@ -1,3 +1,28 @@
.vjsMessage {
font-size: 2em;
line-height: 1.5;
color: white;
background-color: black;
display: inline-block;
}
.alarmCue {
background-color: #222222;
height: 1.5em;
text-align: left;
margin: 0 auto 0 auto;
}
.alarmCue span {
background-color:red;
height: 1.5em;
display: inline-block;
}
span.noneCue {
background: none;
}
#dataBar {
width: 100%;
margin: 2px auto;
@ -145,22 +170,14 @@
#progressBar {
position: relative;
border: 1px solid #666666;
height: 15px;
margin: 0 auto;
top: -1.5em;
height: 1.5em;
margin: 0 auto -1.5em auto;
}
#progressBar .progressBox {
position: absolute;
top: 0px;
left: 0px;
height: 15px;
background: #eeeeee;
border-left: 1px solid #999999;
}
#progressBar .complete {
background: #aaaaaa;
height: 1.5em;
background: rgba(170, 170, 170, .7);
}
#eventStills {
@ -254,7 +271,7 @@
background-color: #444444;
}
#eventVideo {
position: relative;
display: inline-block;
}
#video-controls {

View File

@ -38,3 +38,7 @@
input[type=range]::-ms-tooltip {
display: none;
}
#timeline {
color: white;
font-size: 40px;
}

View File

@ -44,16 +44,14 @@ $scales = array(
'12.5' => '1/8x',
);
$bwArray = array(
$bandwidth_options = array(
'high' => translate('High'),
'medium' => translate('Medium'),
'low' => translate('Low')
);
switch ( $_COOKIE['zmBandwidth'] )
{
case 'high' :
{
switch ( $_COOKIE['zmBandwidth'] ) {
case 'high' : {
define( 'ZM_WEB_REFRESH_MAIN', ZM_WEB_H_REFRESH_MAIN ); // How often (in seconds) the main console window refreshes
define( 'ZM_WEB_REFRESH_CYCLE', ZM_WEB_H_REFRESH_CYCLE ); // How often the cycle watch windows swaps to the next monitor
define( 'ZM_WEB_REFRESH_IMAGE', ZM_WEB_H_REFRESH_IMAGE ); // How often the watched image is refreshed (if not streaming)
@ -70,9 +68,7 @@ switch ( $_COOKIE['zmBandwidth'] )
define( 'ZM_WEB_SHOW_PROGRESS', ZM_WEB_H_SHOW_PROGRESS ); // Whether to show the progress of replay in event view.
define( 'ZM_WEB_AJAX_TIMEOUT', ZM_WEB_H_AJAX_TIMEOUT ); // Timeout to use for Ajax requests, no timeout used if unset
break;
}
case 'medium' :
{
} case 'medium' : {
define( 'ZM_WEB_REFRESH_MAIN', ZM_WEB_M_REFRESH_MAIN ); // How often (in seconds) the main console window refreshes
define( 'ZM_WEB_REFRESH_CYCLE', ZM_WEB_M_REFRESH_CYCLE ); // How often the cycle watch windows swaps to the next monitor
define( 'ZM_WEB_REFRESH_IMAGE', ZM_WEB_M_REFRESH_IMAGE ); // How often the watched image is refreshed (if not streaming)
@ -89,9 +85,7 @@ switch ( $_COOKIE['zmBandwidth'] )
define( 'ZM_WEB_SHOW_PROGRESS', ZM_WEB_M_SHOW_PROGRESS ); // Whether to show the progress of replay in event view.
define( 'ZM_WEB_AJAX_TIMEOUT', ZM_WEB_M_AJAX_TIMEOUT ); // Timeout to use for Ajax requests, no timeout used if unset
break;
}
case 'low' :
{
} case 'low' : {
define( 'ZM_WEB_REFRESH_MAIN', ZM_WEB_L_REFRESH_MAIN ); // How often (in seconds) the main console window refreshes
define( 'ZM_WEB_REFRESH_CYCLE', ZM_WEB_L_REFRESH_CYCLE ); // How often the cycle watch windows swaps to the next monitor
define( 'ZM_WEB_REFRESH_IMAGE', ZM_WEB_L_REFRESH_IMAGE ); // How often the watched image is refreshed (if not streaming)

View File

@ -48,11 +48,13 @@ function xhtmlHeaders( $file, $title ) {
<title><?php echo ZM_WEB_TITLE_PREFIX ?> - <?php echo validHtmlStr($title) ?></title>
<?php
if ( file_exists( "skins/$skin/css/$css/graphics/favicon.ico" ) ) {
echo "<link rel=\"icon\" type=\"image/ico\" href=\"skins/$skin/css/$css/graphics/favicon.ico\"/>";
echo "<link rel=\"shortcut icon\" href=\"skins/$skin/css/$css/graphics/favicon.ico\"/>";
echo "<link rel=\"icon\" type=\"image/ico\" href=\"skins/$skin/css/$css/graphics/favicon.ico\"/>\n";
echo "<link rel=\"shortcut icon\" href=\"skins/$skin/css/$css/graphics/favicon.ico\"/>\n";
} else {
echo '<link rel="icon" type="image/ico" href="graphics/favicon.ico"/>';
echo '<link rel="shortcut icon" href="graphics/favicon.ico"/>';
echo '
<link rel="icon" type="image/ico" href="graphics/favicon.ico"/>
<link rel="shortcut icon" href="graphics/favicon.ico"/>
';
}
?>
<link rel="stylesheet" href="css/reset.css" type="text/css"/>
@ -101,11 +103,13 @@ if ( file_exists( "skins/$skin/css/$css/graphics/favicon.ico" ) ) {
} else if ( $title == 'Event' ) {
?>
<link href="skins/<?php echo $skin ?>/js/video-js.css" rel="stylesheet">
<link href="skins/<?php echo $skin ?>/js/video-js-skin.css" rel="stylesheet">
<script src="skins/<?php echo $skin ?>/js/video.js"></script>
<script src="./js/videojs.zoomrotate.js"></script>
<script src="skins/<?php echo $skin ?>/js/moment.min.js"></script>
<?php
}
if ( $skinJsPhpFile ) {
?>
<script type="text/javascript">
@ -164,7 +168,7 @@ function getNavBarHTML() {
$running = daemonCheck();
$status = $running?translate('Running'):translate('Stopped');
global $user;
global $bwArray;
global $bandwidth_options;
global $view;
?>
<noscript>
@ -229,7 +233,7 @@ ZoneMinder requires Javascript. Please enable Javascript in your browser for thi
</div> <!-- End .container-fluid -->
<div class="container-fluid">
<div class="pull-left">
<?php echo makePopupLink( '?view=bandwidth', 'zmBandwidth', 'bandwidth', $bwArray[$_COOKIE['zmBandwidth']] . ' ' . translate('BandwidthHead'), ($user && $user['MaxBandwidth'] != 'low' ) ) ?>
<?php echo makePopupLink( '?view=bandwidth', 'zmBandwidth', 'bandwidth', $bandwidth_options[$_COOKIE['zmBandwidth']] . ' ' . translate('BandwidthHead'), ($user && $user['MaxBandwidth'] != 'low' ) ) ?>
</div>
<div class="pull-right">
<?php echo makePopupLink( '?view=version', 'zmVersion', 'version', '<span class="'.$versionClass.'">v'.ZM_VERSION.'</span>', canEdit( 'System' ) ) ?>
@ -255,7 +259,8 @@ ZoneMinder requires Javascript. Please enable Javascript in your browser for thi
if ( ! isset($storage_paths[ZM_DIR_EVENTS]) ) {
array_push( $storage_areas, new Storage() );
}
$func = function($S){ return $S->Name() . ': ' . $S->disk_usage_percent().'%'; };
$func = function($S){ return '<span title="'.human_filesize($S->disk_used_space()) . ' of ' . human_filesize($S->disk_total_space()).'">'.$S->Name() . ': ' . $S->disk_usage_percent().'%' . '</span>'; };
#$func = function($S){ return '<span title="">'.$S->Name() . ': ' . $S->disk_usage_percent().'%' . '</span>'; };
echo implode( ', ', array_map ( $func, $storage_areas ) );
echo ' ' . ZM_PATH_MAP .': '. getDiskPercent(ZM_PATH_MAP).'%';
?></li>

View File

@ -1,37 +1,30 @@
<?php
function getDateScale( $scales, $range, $minLines, $maxLines )
{
foreach ( $scales as $scale )
{
function getDateScale( $scales, $range, $minLines, $maxLines ) {
foreach ( $scales as $scale ) {
$align = isset($scale['align'])?$scale['align']:1;
$scaleRange = (int)($range/($scale['factor']*$align));
//echo "S:".$scale['name'].", A:$align, SR:$scaleRange<br>";
if ( $scaleRange >= $minLines )
{
if ( $scaleRange >= $minLines ) {
$scale['range'] = $scaleRange;
break;
}
}
if ( !isset($scale['range']) )
{
if ( !isset($scale['range']) ) {
$scale['range'] = (int)($range/($scale['factor']*$align));
}
$scale['divisor'] = 1;
while ( ($scale['range']/$scale['divisor']) > $maxLines )
{
while ( ($scale['range']/$scale['divisor']) > $maxLines ) {
$scale['divisor']++;
}
$scale['lines'] = (int)($scale['range']/$scale['divisor']);
return( $scale );
}
function getYScale( $range, $minLines, $maxLines )
{
function getYScale( $range, $minLines, $maxLines ) {
$scale['range'] = $range;
$scale['divisor'] = 1;
while ( $scale['range']/$scale['divisor'] > $maxLines )
{
while ( $scale['range']/$scale['divisor'] > $maxLines ) {
$scale['divisor']++;
}
$scale['lines'] = (int)(($scale['range']-1)/$scale['divisor'])+1;
@ -39,11 +32,9 @@ function getYScale( $range, $minLines, $maxLines )
return( $scale );
}
function getSlotFrame( $slot )
{
function getSlotFrame( $slot ) {
$slotFrame = isset($slot['frame'])?$slot['frame']['FrameId']:1;
if ( false && $slotFrame )
{
if ( false && $slotFrame ) {
$slotFrame -= $monitor['PreEventCount'];
if ( $slotFrame < 1 )
$slotFrame = 1;
@ -51,10 +42,8 @@ function getSlotFrame( $slot )
return( $slotFrame );
}
function parseFilterToTree( $filter )
{
if ( count($filter['terms']) > 0 )
{
function parseFilterToTree( $filter ) {
if ( count($filter['terms']) > 0 ) {
$postfixExpr = array();
$postfixStack = array();
@ -73,29 +62,19 @@ function parseFilterToTree( $filter )
'or' => 4,
);
for ( $i = 0; $i <= count($filter['terms']); $i++ )
{
if ( !empty($filter['terms'][$i]['cnj']) )
{
while( true )
{
if ( !count($postfixStack) )
{
for ( $i = 0; $i <= count($filter['terms']); $i++ ) {
if ( !empty($filter['terms'][$i]['cnj']) ) {
while( true ) {
if ( !count($postfixStack) ) {
$postfixStack[] = array( 'type'=>"cnj", 'value'=>$filter['terms'][$i]['cnj'], 'sqlValue'=>$filter['terms'][$i]['cnj']);
break;
}
elseif ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' )
{
} elseif ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' ) {
$postfixStack[] = array( 'type'=>"cnj", 'value'=>$filter['terms'][$i]['cnj'], 'sqlValue'=>$filter['terms'][$i]['cnj']);
break;
}
elseif ( $priorities[$filter['terms'][$i]['cnj']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] )
{
} elseif ( $priorities[$filter['terms'][$i]['cnj']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) {
$postfixStack[] = array( 'type'=>"cnj", 'value'=>$filter['terms'][$i]['cnj'], 'sqlValue'=>$filter['terms'][$i]['cnj']);
break;
}
else
{
} else {
$postfixExpr[] = array_pop( $postfixStack );
}
}
@ -157,23 +136,17 @@ function parseFilterToTree( $filter )
$sqlValue = $filter['terms'][$i]['attr'];
break;
}
if ( $dtAttr )
{
if ( $dtAttr ) {
$postfixExpr[] = array( 'type'=>"attr", 'value'=>$filter['terms'][$i]['attr'], 'sqlValue'=>$sqlValue, 'dtAttr'=>true );
}
else
{
} else {
$postfixExpr[] = array( 'type'=>"attr", 'value'=>$filter['terms'][$i]['attr'], 'sqlValue'=>$sqlValue );
}
}
if ( isset($filter['terms'][$i]['op']) )
{
if ( empty($filter['terms'][$i]['op']) )
{
if ( isset($filter['terms'][$i]['op']) ) {
if ( empty($filter['terms'][$i]['op']) ) {
$filter['terms'][$i]['op' ]= '=';
}
switch ( $filter['terms'][$i]['op' ])
{
switch ( $filter['terms'][$i]['op' ]) {
case '=' :
case '!=' :
case '>=' :
@ -195,36 +168,25 @@ function parseFilterToTree( $filter )
$sqlValue = 'not in (';
break;
}
while( true )
{
if ( !count($postfixStack) )
{
while( true ) {
if ( !count($postfixStack) ) {
$postfixStack[] = array( 'type'=>"op", 'value'=>$filter['terms'][$i]['op'], 'sqlValue'=>$sqlValue );
break;
}
elseif ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' )
{
} elseif ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' ) {
$postfixStack[] = array( 'type'=>"op", 'value'=>$filter['terms'][$i]['op'], 'sqlValue'=>$sqlValue );
break;
}
elseif ( $priorities[$filter['terms'][$i]['op']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] )
{
} elseif ( $priorities[$filter['terms'][$i]['op']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) {
$postfixStack[] = array( 'type'=>"op", 'value'=>$filter['terms'][$i]['op'], 'sqlValue'=>$sqlValue );
break;
}
else
{
} else {
$postfixExpr[] = array_pop( $postfixStack );
}
}
}
if ( isset($filter['terms'][$i]['val']) )
{
if ( isset($filter['terms'][$i]['val']) ) {
$valueList = array();
foreach ( preg_split( '/["\'\s]*?,["\'\s]*?/', preg_replace( '/^["\']+?(.+)["\']+?$/', '$1', $filter['terms'][$i]['val' ]) ) as $value )
{
switch ( $filter['terms'][$i]['attr'])
{
foreach ( preg_split( '/["\'\s]*?,["\'\s]*?/', preg_replace( '/^["\']+?(.+)["\']+?$/', '$1', $filter['terms'][$i]['val' ]) ) as $value ) {
switch ( $filter['terms'][$i]['attr']) {
case 'MonitorName':
case 'Name':
case 'Cause':

View File

@ -24,7 +24,7 @@
// Javascript window sizes
var popupSizes = {
'bandwidth': { 'width': 300, 'height': 120 },
'bandwidth': { 'width': 300, 'height': 220 },
'console': { 'width': 750, 'height': 312 },
'control': { 'width': 380, 'height': 480 },
'controlcaps': { 'width': 780, 'height': 320 },
@ -35,13 +35,13 @@ var popupSizes = {
'donate': { 'width': 500, 'height': 280 },
'event': { 'addWidth': 108, 'minWidth': 496, 'addHeight': 230, 'minHeight': 540 },
'eventdetail': { 'width': 600, 'height': 420 },
'events': { 'width': 960, 'height': 780 },
'events': { 'width': 1020, 'height': 780 },
'export': { 'width': 400, 'height': 340 },
'filter': { 'width': 820, 'height': 700 },
'frame': { 'addWidth': 32, 'minWidth': 384, 'addHeight': 200 },
'frames': { 'width': 600, 'height': 700 },
'function': { 'width': 300, 'height': 200 },
'group': { 'width': 360, 'height': 180 },
'function': { 'width': 400, 'height': 250 },
'group': { 'width': 360, 'height': 320 },
'groups': { 'width': 440, 'height': 220 },
'image': { 'addWidth': 48, 'addHeight': 80 },
'log': { 'width': 1080, 'height': 720 },

View File

@ -315,3 +315,7 @@ function changeMonitor( e ) {
Cookie.write( 'zmMonitorId', monitor_id, { duration: 10*365 } );
window.location = window.location;
}
function changeFilter( e ) {
Cookie.write( e.name, e.value, { duration: 10*365 } );
window.location = window.location;
}

View File

@ -0,0 +1,56 @@
.vjs-tt-cue {
margin-bottom: .75em;
}
.vjs-menu {
z-index: 5;
}
.vjs-default-skin .vjs-playback-rate.vjs-menu-button .vjs-menu .vjs-menu-content {
width: 5em;
}
.vjs-default-skin.vjs-user-inactive:hover .vjs-progress-control {
font-size: .3em;
}
.vjs-default-skin.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar.vjs-zm {
visibility: visible;
opacity: 1;
bottom: -2em;
-webkit-transition: all .2s;
-moz-transition: all .2s;
-o-transition: all .2s;
transition: all .2s
}
.vjs-default-skin.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control,
.vjs-default-skin.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-time-divider {
display: none;
}
.vjs-default-skin .vjs-progress-holder .vjs-play-progress, .vjs-default-skin .vjs-progress-holder .vjs-load-progress {
height: 1em;
}
.vjs-progress-holder.vjs-slider {
background-color: transparent;
z-index: 1;
}
.vjs-progress-control.vjs-control {
background-color: rgba(7, 20, 30, 0.8);
height: 1.3em;
top: -1.3em;
display: block !important;
}
.vjs-control div.alarmCue {
position: relative;
top: -1.3em;
background: none;
}
.vjs-control .alarmCue {
height: 1.3em;
}

View File

@ -19,7 +19,9 @@
//
if ( empty($_COOKIE['zmBandwidth']) )
$_COOKIE['zmBandwidth'] = "low";
$_COOKIE['zmBandwidth'] = ZM_BANDWIDTH_DEFAULT;
if ( empty($_COOKIE['zmBandwidth']) )
$_COOKIE['zmBandwidth'] = 'low';
//ini_set( "magic_quotes_gpc", "Off" );
@ -28,39 +30,33 @@ if ( empty($_COOKIE['zmBandwidth']) )
//require_once( $skinLangFile );
foreach ( getSkinIncludes( 'includes/config.php' ) as $includeFile )
require_once $includeFile;
require_once $includeFile;
foreach ( getSkinIncludes( 'includes/functions.php' ) as $includeFile )
require_once $includeFile;
require_once $includeFile;
if ( empty($view) )
$view = isset($user)?'console':'login';
$view = isset($user)?'console':'login';
if ( !isset($user) && ZM_OPT_USE_AUTH && ZM_AUTH_TYPE == "remote" && !empty( $_SERVER['REMOTE_USER']) )
{
$view = "postlogin";
$action = "login";
$_REQUEST['username'] = $_SERVER['REMOTE_USER'];
if ( !isset($user) && ZM_OPT_USE_AUTH && ZM_AUTH_TYPE == 'remote' && !empty( $_SERVER['REMOTE_USER']) ) {
$view = 'postlogin';
$action = 'login';
$_REQUEST['username'] = $_SERVER['REMOTE_USER'];
}
if ( isset($user) )
{
// Bandwidth Limiter
if ( !empty($user['MaxBandwidth']) )
{
if ( $user['MaxBandwidth'] == "low" )
{
$_COOKIE['zmBandwidth'] = "low";
}
elseif ( $user['MaxBandwidth'] == "medium" && $_COOKIE['zmBandwidth'] == "high" )
{
$_COOKIE['zmBandwidth'] = "medium";
}
if ( isset($user) ) {
// Bandwidth Limiter
if ( !empty($user['MaxBandwidth']) ) {
if ( $user['MaxBandwidth'] == 'low' ) {
$_COOKIE['zmBandwidth'] = 'low';
} elseif ( $user['MaxBandwidth'] == 'medium' && $_COOKIE['zmBandwidth'] == 'high' ) {
$_COOKIE['zmBandwidth'] = 'medium';
}
}
}
// If there are additional actions
foreach ( getSkinIncludes( 'includes/actions.php' ) as $includeFile )
require_once $includeFile;
require_once $includeFile;
?>

View File

@ -24,12 +24,12 @@ if ( $user && !empty($user['MaxBandwidth']) )
{
if ( $user['MaxBandwidth'] == "low" )
{
unset( $bwArray['high'] );
unset( $bwArray['medium'] );
unset( $bandwidth_options['high'] );
unset( $bandwidth_options['medium'] );
}
elseif ( $user['MaxBandwidth'] == "medium" )
{
unset( $bwArray['high'] );
unset( $bandwidth_options['high'] );
}
}
@ -47,7 +47,7 @@ xhtmlHeaders(__FILE__, translate('Bandwidth') );
<input type="hidden" name="view" value="none"/>
<input type="hidden" name="action" value="bandwidth"/>
<p><?php echo translate('SetNewBandwidth') ?></p>
<p><?php echo buildSelect( "newBandwidth", $bwArray ) ?></p>
<p><?php echo buildSelect( "newBandwidth", $bandwidth_options ) ?></p>
<div id="contentButtons">
<input type="submit" value="<?php echo translate('Save') ?>"/><input type="button" value="<?php echo translate('Cancel') ?>" onclick="closeWindow();"/>
</div>

View File

@ -19,8 +19,30 @@
//
$servers = Server::find_all();
$ServersById = array(''=>'All');
foreach ( $servers as $S ) {
$ServersById[$S->Id()] = $S;
}
session_start();
foreach ( array('ServerFilter','StorageFilter') as $var ) {
if ( isset( $_REQUEST[$var] ) ) {
if ( $_REQUEST[$var] != '' ) {
$_SESSION[$var] = $_REQUEST[$var];
} else {
unset( $_SESSION[$var] );
}
} else if ( isset( $_COOKIE[$var] ) ) {
if ( $_COOKIE[$var] != '' ) {
$_SESSION[$var] = $_COOKIE[$var];
} else {
unset($_SESSION[$var]);
}
}
}
session_write_close();
$storage_areas = Storage::find_all();
$StorageById = array();
$StorageById = array(''=>'All');
foreach ( $storage_areas as $S ) {
$StorageById[$S->Id()] = $S;
}
@ -101,7 +123,6 @@ $navbar = getNavBarHTML();
noCacheHeaders();
$eventsView = ZM_WEB_EVENTS_VIEW;
$eventsWindow = 'zm'.ucfirst(ZM_WEB_EVENTS_VIEW);
$left_columns = 3;
if ( count($servers) ) $left_columns += 1;
@ -138,7 +159,21 @@ $groupSql = Group::get_group_sql( $group_id );
$maxHeight = 0;
# Used to determine if the Cycle button should be made available
$monitors = dbFetchAll( 'SELECT * FROM Monitors'.($groupSql?' WHERE '.$groupSql:'').' ORDER BY Sequence ASC' );
$conditions = array();
$values = array();
if ( $groupSql )
$conditions[] = $groupSql;
if ( isset($_SESSION['ServerFilter']) ) {
$conditions[] = 'ServerId=?';
$values[] = $_SESSION['ServerFilter'];
}
if ( isset($_SESSION['StorageFilter']) ) {
$conditions[] = 'StorageId=?';
$values[] = $_SESSION['StorageFilter'];
}
$sql = 'SELECT * FROM Monitors' . ( count($conditions) ? ' WHERE ' . implode(' AND ', $conditions ) : '' ).' ORDER BY Sequence ASC';
$monitors = dbFetchAll( $sql, null, $values );
$displayMonitors = array();
$monitors_dropdown = array(''=>'All');
@ -188,6 +223,24 @@ for( $i = 0; $i < count($displayMonitors); $i += 1 ) {
}
?>
</span>
<?php if ( count($ServersById) > 0 ) { ?>
<span id="ServerFilter"><label><?php echo translate('Server')?>:</label>
<?php
echo htmlSelect( 'ServerFilter', $ServersById, (isset($_SESSION['ServerFilter'])?$_SESSION['ServerFilter']:''), array('onchange'=>'changeFilter(this);') );
?>
</span>
<?php
}
if ( count($StorageById) > 0 ) { ?>
<span id="StorageFilter"><label><?php echo translate('Storage')?>:</label>
<?php
echo htmlSelect( 'StorageFilter', $StorageById, (isset($_SESSION['StorageFilter'])?$_SESSION['StorageFilter']:''), array('onchange'=>'changeFilter(this);') );
?>
</span>
<?php
}
?>
</div>
</div>
<div class="container-fluid">
@ -252,7 +305,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
<td class="colFunction"><?php echo makePopupLink( '?view=function&amp;mid='.$monitor['Id'], 'zmFunction', 'function', '<span class="'.$fclass.'">'.translate('Fn'.$monitor['Function']).( empty($monitor['Enabled']) ? ', disabled' : '' ) .'</span>', canEdit( 'Monitors' ) ) ?></td>
<?php
if ( count($servers) ) { ?>
<td class="colServer"><?php $Server = new Server( $monitor['ServerId'] ); echo $Server->Name(); ?></td>
<td class="colServer"><?php $Server = isset($ServersById[$monitor['ServerId']]) ? $ServersById[$monitor['ServerId']] : new Server( $monitor['ServerId'] ); echo $Server->Name(); ?></td>
<?php
}
$source = '';
@ -278,7 +331,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
for ( $i = 0; $i < count($eventCounts); $i++ ) {
?>
<td class="colEvents"><?php echo makePopupLink( '?view='.$eventsView.'&amp;page=1'.$monitor['eventCounts'][$i]['filter']['query'], $eventsWindow, $eventsView, $monitor['EventCount'.$i], canView( 'Events' ) ) ?></td>
<td class="colEvents"><?php echo makePopupLink( '?view='.ZM_WEB_EVENTS_VIEW.'&amp;page=1'.$monitor['eventCounts'][$i]['filter']['query'], $eventsWindow, ZM_WEB_EVENTS_VIEW, $monitor['EventCount'.$i], canView( 'Events' ) ) ?></td>
<?php
}
?>
@ -300,7 +353,8 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
</tbody>
<tfoot>
<tr>
<td class="colLeftButtons" colspan="<?php echo $left_columns ?>">
<td class="colId"><?php echo count($displayMonitors) ?></td>
<td class="colLeftButtons" colspan="<?php echo $left_columns -1?>">
<input type="button" value="<?php echo translate('Refresh') ?>" onclick="location.reload(true);"/>
<input type="button" name="addBtn" value="<?php echo translate('AddNewMonitor') ?>" onclick="addMonitor( this )"/>
<!-- <?php echo makePopupButton( '?view=monitor', 'zmMonitor0', 'monitor', translate('AddNewMonitor'), (canEdit( 'Monitors' ) && !$user['MonitorIds']) ) ?> -->
@ -312,7 +366,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) {
for ( $i = 0; $i < count($eventCounts); $i++ ) {
parseFilter( $eventCounts[$i]['filter'] );
?>
<td class="colEvents"><?php echo makePopupLink( '?view='.$eventsView.'&amp;page=1'.$eventCounts[$i]['filter']['query'], $eventsWindow, $eventsView, $eventCounts[$i]['total'], canView( 'Events' ) ) ?></td>
<td class="colEvents"><?php echo makePopupLink( '?view='.ZM_WEB_EVENTS_VIEW.'&amp;page=1'.$eventCounts[$i]['filter']['query'], $eventsWindow, ZM_WEB_EVENTS_VIEW, $eventCounts[$i]['total'], canView( 'Events' ) ) ?></td>
<?php
}
?>

View File

@ -85,10 +85,6 @@ parseSort();
parseFilter( $_REQUEST['filter'] );
$filterQuery = $_REQUEST['filter']['query'];
$panelSections = 40;
$panelSectionWidth = (int)ceil(reScale($Event->Width(),$scale)/$panelSections);
$panelWidth = ($panelSections*$panelSectionWidth-1);
$connkey = generateConnKey();
$focusWindow = true;
@ -150,10 +146,10 @@ if ( $Event->SaveJPEGs() & 3 ) { // Analysis or Jpegs
<div id="videoEvent"><a href="#" onclick="videoEvent();"><?php echo translate('Video') ?></a></div>
<div id="exportEvent"><a href="#" onclick="exportEvent();"><?php echo translate('Export') ?></a></div>
</div>
<div id="eventVideo" class="">
<?php
if ( $Event->DefaultVideo() ) {
?>
<div id="eventVideo" class="">
<div id="videoFeed">
<video id="videoobj" class="video-js vjs-default-skin" width="<?php echo reScale( $Event->Width(), $scale ) ?>" height="<?php echo reScale( $Event->Height(), $scale ) ?>" data-setup='{ "controls": true, "playbackRates": [0.5, 1, 1.5, 2, 4, 8, 16, 32, 64, 128, 256], "autoplay": true, "preload": "auto", "plugins": { "zoomrotate": { "zoom": "<?php echo $Zoom ?>"}}}'>
<source src="<?php echo $Event->getStreamSrc( array( 'mode'=>'mpeg','format'=>'h264' ) ); ?>" type="video/mp4">
@ -172,16 +168,14 @@ if ( $Event->DefaultVideo() ) {
vjsReplay(<?php echo (strtotime($Event->StartTime()) + $Event->Length())*1000 ?>);
</script>
<p id="replayAllCountDown"></p>
<p id="dvrControlsVjs" class="dvrControls">
<input type="button" value="&lt;+" id="prevBtnVjs" title="<?php echo translate('Prev') ?>" class="active" onclick="streamPrev( true );"/>
<input type="button" value="+&gt;" id="nextBtnVjs" title="<?php echo translate('Next') ?>" class="active" onclick="streamNext( true );"/>
<p id="dvrControls" class="dvrControls">
<input type="button" value="&lt;+" id="prevBtn" title="<?php echo translate('Prev') ?>" class="inactive" onclick="streamPrev( true );"/>
<input type="button" value="+&gt;" id="nextBtn" title="<?php echo translate('Next') ?>" class="inactive" onclick="streamNext( true );"/>
</p>
</div><!--eventVideo-->
<?php
} // end if DefaultVideo
?>
</div><!--eventVideo-->
<?php if (!$Event->DefaultVideo()) { ?>
<div id="imageFeed">
<?php
@ -197,6 +191,10 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
}
} // end if stream method
?>
<div id="alarmCueJpeg" class="alarmCue" style="width: <?php echo reScale($Event->Width(), $scale);?>px;"></div>
<div id="progressBar" style="width: <?php echo reScale($Event->Width(), $scale);?>px;">
<div class="progressBox" id="progressBox" title="" style="width: 0%;"></div>
</div><!--progressBar-->
<p id="dvrControls" class="dvrControls">
<input type="button" value="&lt;+" id="prevBtn" title="<?php echo translate('Prev') ?>" class="inactive" onclick="streamPrev( true );"/>
<input type="button" value="&lt;&lt;" id="fastRevBtn" title="<?php echo translate('Rewind') ?>" class="inactive" disabled="disabled" onclick="streamFastRev( true );"/>
@ -214,15 +212,10 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
<span id="progress"><?php echo translate('Progress') ?>: <span id="progressValue"></span>s</span>
<span id="zoom"><?php echo translate('Zoom') ?>: <span id="zoomValue"></span>x</span>
</div>
<div id="progressBar" class="invisible">
<?php for ( $i = 0; $i < $panelSections; $i++ ) { ?>
<div class="progressBox" id="progressBox<?php echo $i ?>" title=""></div>
<?php } ?>
</div><!--progressBar-->
</div><!--imageFeed-->
</div>
<?php } /*end if !DefaultVideo*/ ?>
<?php
<?php
if ( $Event->SaveJPEGs() & 3 ) { // frames or analysis
?>
<div id="eventStills" class="hidden">

View File

@ -154,10 +154,12 @@ if ( $pagination ) {
<a id="filterLink" href="#" onclick="createPopup( '?view=filter&amp;page=<?php echo $page ?><?php echo $filterQuery ?>', 'zmFilter', 'filter' );"><?php echo translate('ShowFilterWindow') ?></a>
<a id="timelineLink" href="#" onclick="createPopup( '?view=timeline<?php echo $filterQuery ?>', 'zmTimeline', 'timeline' );"><?php echo translate('ShowTimeline') ?></a>
</p>
<table id="contentTable" class="major" cellspacing="0">
<table id="contentTable" class="major">
<tbody>
<?php
$count = 0;
$disk_space_total = 0;
foreach ( $events as $event ) {
if ( ($count++%ZM_WEB_EVENTS_PER_PAGE) == 0 ) {
?>
@ -174,7 +176,8 @@ foreach ( $events as $event ) {
<th class="colAvgScore"><a href="<?php echo sortHeader( 'AvgScore' ) ?>"><?php echo translate('AvgBrScore') ?><?php echo sortTag( 'AvgScore' ) ?></a></th>
<th class="colMaxScore"><a href="<?php echo sortHeader( 'MaxScore' ) ?>"><?php echo translate('MaxBrScore') ?><?php echo sortTag( 'MaxScore' ) ?></a></th>
<?php
if ( ZM_WEB_EVENT_DISK_SPACE ) { ?>
if ( ZM_WEB_EVENT_DISK_SPACE ) {
?>
<th class="colDiskSpace"><a href="<?php echo sortHeader( 'DiskSpace' ) ?>"><?php echo translate('DiskSpace') ?><?php echo sortTag( 'DiskSpace' ) ?></a></th>
<?php
}
@ -206,6 +209,7 @@ makePopupLink( '?view=monitor&amp;mid='.$event->MonitorId(), 'zmMonitor'.$event-
<td class="colMaxScore"><?php echo makePopupLink( '?view=frame&amp;eid='.$event->Id().'&amp;fid=0', 'zmImage', array( 'image', reScale( $event->Width(), $scale ), reScale( $event->Height(), $scale ) ), $event->MaxScore() ) ?></td>
<?php
if ( ZM_WEB_EVENT_DISK_SPACE ) {
$disk_space_total += $event->DiskSpace();
?>
<td class="colDiskSpace"><?php echo human_filesize( $event->DiskSpace() ) ?></td>
<?php
@ -215,7 +219,13 @@ makePopupLink( '?view=monitor&amp;mid='.$event->MonitorId(), 'zmMonitor'.$event-
?>
<td class="colThumbnail">
<?php
if ( file_exists( $event->Path().'/snapshot.jpg' ) ) {
Warning("Using snapshot");
$imgSrc = '?view=image&amp;eid='.$event->Id().'&amp;fid=snapshot&amp;width='.$thumbData['Width'].'&amp;height='.$thumbData['Height'];
} else {
Warning("Not Using snapshot" . $event->Path().'/snapshot.jpg' );
$imgSrc = '?view=image&amp;eid='.$event->Id().'&amp;fid='.$thumbData['FrameId'].'&amp;width='.$thumbData['Width'].'&amp;height='.$thumbData['Height'];
}
$streamSrc = $event->getStreamSrc( array( 'mode'=>'jpeg', 'scale'=>$scale, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>'single') );
$imgHtml = '<img id="thumbnail'.$event->id().'" src="'.$imgSrc.'" alt="'. validHtmlStr('Event '.$event->Id()) .'" style="width:'. validInt($thumbData['Width']) .'px;height:'. validInt( $thumbData['Height'] ).'px;" onmouseover="this.src=\''.$streamSrc.'\';" onmouseout="this.src=\''.$imgSrc.'\';"/>';
@ -242,6 +252,24 @@ makePopupLink( '?view=monitor&amp;mid='.$event->MonitorId(), 'zmMonitor'.$event-
}
?>
</tbody>
<?php
if ( ZM_WEB_EVENT_DISK_SPACE ) {
?>
<tfoot>
<tr>
<td colspan="11">Totals:</td>
<td class="colDiskSpace"><?php echo human_filesize( $disk_space_total ) ?></td>
<?php
if ( ZM_WEB_LIST_THUMBS ) {
?><td></td>
<?php
}
?>
</tr>
</tfoot>
<?php
}
?>
</table>
<?php
if ( $pagination ) {

View File

@ -142,7 +142,8 @@ if ( (null !== $filter->Concurrent()) and $filter->Concurrent() )
<hr/>
<p>
<label for="filter[Name]"><?php echo translate('Name') ?></label><input type="text" id="filter[Name]" name="filter[Name]" value="<?php echo $filter->Name() ?>"/>
<label for="filter[Name]"><?php echo translate('Name') ?></label>
<input type="text" id="filter[Name]" name="filter[Name]" value="<?php echo $filter->Name() ?>"/>
</p>
<table id="fieldsTable" class="filterTable">
<tbody>
@ -245,7 +246,7 @@ for ( $i = 0; $i < count($terms); $i++ ) {
} elseif ( $term['attr'] == 'StorageId' ) {
$storageareas = array();
$storageareas[0] = 'Default ' . ZM_DIR_EVENTS;
foreach ( dbFetchAll( "SELECT Id,Name FROM Storage ORDER BY lower(Name) ASC" ) as $storage ) {
foreach ( dbFetchAll( 'SELECT Id,Name FROM Storage ORDER BY lower(Name) ASC' ) as $storage ) {
$storageareas[$storage['Id']] = $storage['Name'];
}
?>

View File

@ -88,11 +88,8 @@ function get_children($Group) {
$kids = get_children($newGroup);
$kids[] = $newGroup->Id();
function get_question_marks() {
return '?';
}
$options = array(''=>'None');
foreach ( dbFetchAll( 'SELECT Id,Name from Groups WHERE Id NOT IN ('.implode(',',array_map('get_question_marks', $kids )).') ORDER BY Name', null, $kids ) as $option ) {
foreach ( dbFetchAll( 'SELECT Id,Name from Groups WHERE Id NOT IN ('.implode(',',array_map(function(){return '?';}, $kids )).') ORDER BY Name', null, $kids ) as $option ) {
$options[$option['Id']] = $option['Name'];
}
echo htmlSelect( 'newGroup[ParentId]', $options, $newGroup->ParentId(), array('onchange'=>'configureButtons(this);' ));

View File

@ -23,11 +23,10 @@ if ( !canView( 'Groups' ) ) {
return;
}
# This will end up with the group_id of the deepest selection
# This will end up with the group_id of the deepest selection
$group_id = 0;
$max_depth = 0;
$Groups = array();
foreach ( Group::find_all( ) as $Group ) {
$Groups[$Group->Id()] = $Group;
@ -42,7 +41,6 @@ foreach ( $Groups as $id=>$Group ) {
if ( $max_depth < $Group->depth() )
$max_depth = $Group->depth();
}
Warning("Max depth $max_depth");
xhtmlHeaders(__FILE__, translate('Groups') );
?>
<body>
@ -75,7 +73,7 @@ function group_line( $Group ) {
$html .= validHtmlStr($Group->Name());
}
$html .= '</td><td class="colIds">'. monitorIdsToNames( $Group->MonitorIds(), 30 ).'</td>
<td class="colSelect"><input type="checkbox" name="gid" value="'. $Group->Id() .'" onclick="configureButtons(this);"/></td>
<td class="colSelect"><input type="checkbox" name="gid[]" value="'. $Group->Id() .'" onclick="configureButtons(this);"/></td>
</tr>
';
if ( isset( $children[$Group->Id()] ) ) {
@ -85,8 +83,9 @@ function group_line( $Group ) {
}
return $html;
}
foreach ( $children[null] as $Group )
echo group_line( $Group );
if ( isset( $children[null] ) )
foreach ( $children[null] as $Group )
echo group_line( $Group );
?>
</tbody>
</table>

View File

@ -11,18 +11,27 @@ function vjsReplay(endTime) {
player.play();
break;
case 'all':
// nextEventStartTime.getTime() is a mootools workaround, highjacks Date.parse
var gapDuration = (new Date().getTime()) + (nextEventStartTime.getTime() - endTime);
var x = setInterval(function() {
var now = new Date().getTime();
var remainder = new Date(Math.round(gapDuration - now)).toISOString().substr(11,8);;
$j("#replayAllCountDown").html(remainder + " to next event.");
if (remainder < 0) {
clearInterval(x);
streamNext( true );
if (nextEventId == 0) {
$j("#videoobj").html('<p class="vjsMessage">No more events</p>');
} else {
var nextStartTime = nextEventStartTime.getTime(); //nextEventStartTime.getTime() is a mootools workaround, highjacks Date.parse
if (nextStartTime <= endTime) {
streamNext( true );
return;
}
}, 1000);
break;
$j("#videoobj").html('<p class="vjsMessage"></p>');
var gapDuration = (new Date().getTime()) + (nextStartTime - endTime);
var x = setInterval(function() {
var now = new Date().getTime();
var remainder = new Date(Math.round(gapDuration - now)).toISOString().substr(11,8);
$j(".vjsMessage").html(remainder + ' to next event.');
if (remainder < 0) {
clearInterval(x);
streamNext( true );
}
}, 1000);
}
break;
case 'gapless':
streamNext( true );
break;
@ -31,6 +40,91 @@ function vjsReplay(endTime) {
});
}
$j.ajaxSetup ({timeout: AJAX_TIMEOUT }); //sets timeout for all getJSON.
var cueFrames = null; //make cueFrames availaible even if we don't send another ajax query
function initialAlarmCues (eventId) {
$j.getJSON("api/events/"+eventId+".json", setAlarmCues); //get frames data for alarmCues and inserts into html
}
function setAlarmCues (data) {
cueFrames = data.event.Frame;
alarmSpans = renderAlarmCues();
if ( vid ) {
$j(".vjs-progress-control").append('<div class="alarmCue">' + alarmSpans + '</div>');
$j(".vjs-control-bar").addClass("vjs-zm");
} else {
$j("#alarmCueJpeg").html(alarmSpans);
}
}
function renderAlarmCues () {
if (cueFrames) {
var cueRatio = (vid ? $j("#videoobj").width() : $j("#evtStream").width()) / (cueFrames[cueFrames.length - 1].Delta * 100);//use videojs width or nph-zms width
var minAlarm = Math.ceil(1/cueRatio);
var spanTimeStart = 0;
var spanTimeEnd = 0;
var alarmed = 0;
var alarmHtml = "";
var pixSkew = 0;
var skip = 0;
for (let i = 0; i < cueFrames.length; i++) {
skip = 0;
frame = cueFrames[i];
if (frame.Type == "Alarm" && alarmed == 0) { //From nothing to alarm. End nothing and start alarm.
alarmed = 1;
if (frame.Delta == 0) continue; //If event starts with an alarm or too few for a nonespan
spanTimeEnd = frame.Delta * 100;
spanTime = spanTimeEnd - spanTimeStart;
let pix = cueRatio * spanTime;
pixSkew += pix - Math.round(pix);//average out the rounding errors.
pix = Math.round(pix);
if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) { //add skew if it's a pixel and won't zero out span.
pix += Math.round(pixSkew);
pixSkew = pixSkew - Math.round(pixSkew);
}
alarmHtml += '<span class="alarmCue noneCue" style="width: ' + pix + 'px;"></span>';
spanTimeStart = spanTimeEnd;
} else if (frame.Type !== "Alarm" && alarmed == 1) { //from alarm to nothing. End alarm and start nothing.
futNone = 0;
indexPlus = i+1;
if (((frame.Delta * 100) - spanTimeStart) < minAlarm && indexPlus < cueFrames.length) continue; //alarm is too short and there is more event
while (futNone < minAlarm) { //check ahead to see if there's enough for a nonespan
if (indexPlus >= cueFrames.length) break; //check if end of event.
futNone = (cueFrames[indexPlus].Delta *100) - (frame.Delta *100);
if (cueFrames[indexPlus].Type == "Alarm") {
i = --indexPlus;
skip = 1;
break;
}
indexPlus++;
}
if (skip == 1) continue; //javascript doesn't support continue 2;
spanTimeEnd = frame.Delta *100;
spanTime = spanTimeEnd - spanTimeStart;
alarmed = 0;
pix = cueRatio * spanTime;
pixSkew += pix - Math.round(pix);
pix = Math.round(pix);
if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) {
pix += Math.round(pixSkew);
pixSkew = pixSkew - Math.round(pixSkew);
}
alarmHtml += '<span class="alarmCue" style="width: ' + pix + 'px;"></span>';
spanTimeStart = spanTimeEnd;
} else if (frame.Type == "Alarm" && alarmed == 1 && i + 1 >= cueFrames.length) { //event ends on an alarm
spanTimeEnd = frame.Delta * 100;
spanTime = spanTimeEnd - spanTimeStart;
alarmed = 0;
pix = Math.round(cueRatio * spanTime);
alarmHtml += '<span class="alarmCue" style="width: ' + pix + 'px;"></span>';
}
}
return alarmHtml;
}
}
function setButtonState( element, butClass ) {
if ( element ) {
element.className = butClass;
@ -46,16 +140,20 @@ function changeScale() {
var baseHeight = eventData.Height;
var newWidth = ( baseWidth * scale ) / SCALE_BASE;
var newHeight = ( baseHeight * scale ) / SCALE_BASE;
if ( vid ) {
// Using video.js
vid.width = newWidth;
vid.height = newHeight;
} else {
if ( vid ) {
// Using video.js
$j("#videoobj").width(newWidth);
$j("#videoobj").height(newHeight);
$j("div.alarmCue").html(renderAlarmCues());//just re-render alarmCues. skip ajax call
Cookie.write( 'zmEventScale'+eventData.MonitorId, scale, { duration: 10*365 } );
} else {
streamScale( scale );
var streamImg = document.getElementById('evtStream');
streamImg.style.width = newWidth + "px";
streamImg.style.height = newHeight + "px";
var streamImg = document.getElementById('evtStream');
streamImg.style.width = newWidth + "px";
streamImg.style.height = newHeight + "px";
$j("#alarmCueJpeg").width(newWidth);
drawProgressBar();
$j("#alarmCueJpeg").html(renderAlarmCues());
Cookie.write( 'zmEventScale'+eventData.MonitorId, scale, { duration: 10*365 } );
}
}
@ -73,23 +171,30 @@ var streamCmdTimer = null;
var streamStatus = null;
var lastEventId = 0;
var zmsBroke = false; //Use alternate navigation if zms has crashed
function getCmdResponse( respObj, respText ) {
if ( checkStreamForErrors( "getCmdResponse", respObj ) ) {
console.log('Got an error from getCmdResponse');
var zmsBroke = true;
return;
}
zmsBroke = false;
if ( streamCmdTimer )
streamCmdTimer = clearTimeout( streamCmdTimer );
streamStatus = respObj.status;
if (streamStatus.progress > parseFloat(eventData.Length)) streamStatus.progress = parseFloat(eventData.Length); //Limit progress to reality
var eventId = streamStatus.event;
if ( eventId != lastEventId ) {
if ( eventId != lastEventId && lastEventId != 0) { //Doesn't run on first load, prevents a double hit on event and nearEvents ajax
eventQuery( eventId );
initialAlarmCues(eventId); //zms uses this instead of a page reload, must call ajax+render
lastEventId = eventId;
}
if (lastEventId == 0) lastEventId = eventId; //Only fires on first load.
if ( streamStatus.paused == true ) {
$('modeValue').set( 'text', 'Paused' );
$('rate').addClass( 'hidden' );
@ -117,7 +222,7 @@ function getCmdResponse( respObj, respText ) {
streamImg.src = streamImg.src.replace( /auth=\w+/i, 'auth='+streamStatus.auth );
} // end if haev a new auth hash
streamCmdTimer = streamQuery.delay( streamTimeout );
streamCmdTimer = streamQuery.delay( streamTimeout ); //Timeout is refresh rate for progressBox and time display
}
var streamReq = new Request.JSON( { url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getCmdResponse } );
@ -194,23 +299,26 @@ function streamFastRev( action ) {
streamReq.send( streamParms+"&command="+CMD_FASTREV );
}
function streamPrev( action ) {
if ( action ) {
if ( vid ) {
function streamPrev(action) {
if (action) {
if (vid || PrevEventDefVideoPath.indexOf("view_video") >=0 || $j("#vjsMessage").length || zmsBroke) { //handles this or prev being video.js, or end of events
location.replace(thisUrl + '?view=event&eid=' + prevEventId + filterQuery + sortQuery);
return;
} else {
streamReq.send(streamParms+"&command="+CMD_PREV);
}
streamReq.send( streamParms+"&command="+CMD_PREV );
}
}
function streamNext( action ) {
if ( action ) {
if ( vid ) {
function streamNext(action) {
if (action) {
if (nextEventId == 0) { //handles deleting last event.
let replaceStream = $j(vid ? "#videoobj" : "#evtStream");
replaceStream.replaceWith('<p class="vjsMessage" style="width:' + replaceStream.css("width") + '; height: ' + replaceStream.css("height") + ';line-height: ' + replaceStream.css("height") + ';">No more events</p>');
} else if (vid || NextEventDefVideoPath.indexOf("view_video") >=0 || zmsBroke) { //handles current or next switch to video.js
location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery);
return;
} else {
streamReq.send( streamParms+"&command="+CMD_NEXT );
}
streamReq.send( streamParms+"&command="+CMD_NEXT );
}
}
@ -311,8 +419,8 @@ function getNearEventsResponse( respObj, respText ) {
if ( prevEventBtn ) prevEventBtn.disabled = !prevEventId;
var nextEventBtn = $('nextEventBtn');
if ( nextEventBtn ) nextEventBtn.disabled = !nextEventId;
if (prevEventId == 0) $j('#prevBtnVjs').prop('disabled', true).attr('class', 'unavail');
if (nextEventId == 0) $j('#nextBtnVjs').prop('disabled', true).attr('class', 'unavail');
$j('#prevBtn').prop('disabled', prevEventId == 0 ? true : false).attr('class', prevEventId == 0 ? 'unavail' : 'inactive');
$j('#nextBtn').prop('disabled', nextEventId == 0 ? true : false).attr('class', nextEventId == 0 ? 'unavail' : 'inactive');
}
var nearEventsReq = new Request.JSON( { url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getNearEventsResponse } );
@ -683,63 +791,40 @@ function videoEvent() {
createPopup( '?view=video&eid='+eventData.Id, 'zmVideo', 'video', eventData.Width, eventData.Height );
}
// Called on each event load, because each event can be a different length.
// Called on each event load because each event can be a different width
function drawProgressBar() {
// Make it invisible so we don't see the update happen
$('progressBar').addClass( 'invisible' );
var barWidth = 0;
var cells = $('progressBar').getElements( 'div' );
var cells_length = cells.length;
var cellWidth = parseInt( eventData.Width / cells_length );
for ( var index = 0; index < cells_length; index += 1 ) {
var cell = $( cells[index] );
if ( index == 0 )
cell.setStyles( { 'left': barWidth, 'width': cellWidth, 'borderLeft': 0 } );
else
cell.setStyles( { 'left': barWidth, 'width': cellWidth } );
var offset = parseInt( (index*eventData.Length)/cells_length );
cell.setProperty( 'title', '+'+secsToTime(offset)+'s' );
cell.removeEvent( 'click' );
cell.addEvent( 'click', function() { streamSeek( offset ); } );
barWidth += cell.getCoordinates().width;
}
$('progressBar').setStyle( 'width', barWidth );
$('progressBar').removeClass( 'invisible' );
var barWidth = (eventData.Width * $j('#scale').val()) / SCALE_BASE;
$j('#progressBar').css( 'width', barWidth );
}
// Changes the colour of the cells making up the completed progress bar
// Shows current stream progress.
function updateProgressBar() {
if ( ! ( eventData && streamStatus ) ) {
return;
} // end if ! eventData && streamStatus
var cells = $('progressBar').getElements( 'div' );
var cells_length = cells.length;
var completeIndex = parseInt(((cells_length+1)*streamStatus.progress)/eventData.Length);
for ( var index = 0; index < cells_length; index += 1 ) {
var cell = $( cells[index] );
if ( index < completeIndex ) {
if ( !cell.hasClass( 'complete' ) ) {
cell.addClass( 'complete' );
}
} else {
if ( cell.hasClass( 'complete' ) ) {
cell.removeClass( 'complete' );
}
} // end if
} // end function
var curWidth = (streamStatus.progress / parseFloat(eventData.Length)) * 100;
$j("#progressBox").css('width', curWidth + '%');
} // end function updateProgressBar()
// Handles seeking when clicking on the progress bar.
function progressBarSeek (){
$j('#progressBar').click(function(e){
var x = e.pageX - $j(this).offset().left;
var seekTime = (x / $j('#progressBar').width()) * parseFloat(eventData.Length);
streamSeek (seekTime);
});
}
function handleClick( event ) {
var target = event.target;
var x = event.page.x - $(target).getLeft();
var y = event.page.y - $(target).getTop();
if ( event.shift )
streamPan( x, y );
else
streamZoomIn( x, y );
if (event.shift) {
streamPan(x, y);
} else {
streamZoomIn(x, y);
}
}
function setupListener() {
@ -841,6 +926,7 @@ function initPage() {
//FIXME prevent blocking...not sure what is happening or best way to unblock
if ( $('videoobj') ) {
vid = videojs("videoobj");
initialAlarmCues(eventData.Id); //call ajax+renderAlarmCues after videojs is. should be only call to initialAlarmCues on vjs streams
}
if ( vid ) {
/*
@ -861,8 +947,10 @@ function initPage() {
window.videoobj.load();
streamPlay(); */
} else {
progressBarSeek ();
streamCmdTimer = streamQuery.delay( 250 );
eventQuery.pass( eventData.Id ).delay( 500 );
initialAlarmCues(eventData.Id); //call ajax+renderAlarmCues for nph-zms.
if ( canStreamNative ) {
var streamImg = $('imageFeed').getElement('img');

View File

@ -60,6 +60,7 @@ function saveFilter( element ) {
var form = element.form;
//form.target = 'zmFilter';
form.target = window.name;
form.elements['action'].value = 'save';
form.action = thisUrl + '?view=filter';
form.submit();

View File

@ -50,7 +50,7 @@ monitorNames['<?php echo validJsStr($name) ?>'] = true;
function validateForm( form ) {
var errors = new Array();
if ( form.elements['newMonitor[Name]'].value.search( /[^\w- ]/ ) >= 0 )
if ( form.elements['newMonitor[Name]'].value.search( /[^\w\-\.\(\)\:\/ ]/ ) >= 0 )
errors[errors.length] = "<?php echo translate('BadNameChars') ?>";
else if ( form.elements.mid.value == 0 && monitorNames[form.elements['newMonitor[Name]'].value] )
errors[errors.length] = "<?php echo translate('DuplicateMonitorName') ?>";

View File

@ -30,7 +30,7 @@ function Monitor( monitorData ) {
if ( this.streamCmdTimer )
this.streamCmdTimer = clearTimeout( this.streamCmdTimer );
var stream = document.getElementById( "liveStream"+this.id );
var stream = $j('#liveStream'+this.id );
if ( respObj.result == 'Ok' ) {
this.status = respObj.status;
this.alarmState = this.status.state;

View File

@ -35,7 +35,7 @@ monitorData[monitorData.length] = {
'connKey': <?php echo $monitor->connKey() ?>,
'width': <?php echo $monitor->Width() ?>,
'height':<?php echo $monitor->Height() ?>,
'server_url': '<?php echo $monitor->Server()->Url().$_SERVER['PHP_SELF'] ?>'
'server_url': '<?php echo $monitor->Server()->Url().(ZM_MIN_STREAMING_PORT?':'.(ZM_MIN_STREAMING_PORT+$monitor->Id()):'').$_SERVER['PHP_SELF'] ?>'
};
<?php
}

View File

@ -1,3 +1,5 @@
var date = new Date();
function evaluateLoadTimes() {
// Only consider it a completed event if we load ALL monitors, then zero all and start again
var start=0;
@ -5,7 +7,7 @@ function evaluateLoadTimes() {
if ( liveMode != 1 && currentSpeed == 0 ) return; // don't evaluate when we are not moving as we can do nothing really fast.
for ( var i = 0; i < monitorIndex.length; i++ ) {
if ( monitorName[i] > "" ) {
if ( monitorLoadEndTimems[i] ==0 ) return; // if we have a monitor with no time yet just wait
if ( monitorLoadEndTimems[i] == 0 ) return; // if we have a monitor with no time yet just wait
if ( start == 0 || start > monitorLoadStartTimems[i] ) start = monitorLoadStartTimems[i];
if ( end == 0 || end < monitorLoadEndTimems[i] ) end = monitorLoadEndTimems[i];
}
@ -16,8 +18,9 @@ function evaluateLoadTimes() {
monitorLoadStartTimems[monId] = 0;
monitorLoadEndTimems[monId] = 0;
}
freeTimeLastIntervals[imageLoadTimesEvaluated++] = 1 - ((end - start)/currentDisplayInterval);
if( imageLoadTimesEvaluated < imageLoadTimesNeeded ) return;
if ( imageLoadTimesEvaluated < imageLoadTimesNeeded ) return;
var avgFrac=0;
for ( var i=0; i < imageLoadTimesEvaluated; i++ )
avgFrac += freeTimeLastIntervals[i];
@ -35,21 +38,24 @@ function evaluateLoadTimes() {
else if (avgFrac >= 0.2) currentDisplayInterval = (currentDisplayInterval * 1.50).toFixed(1);
else if (avgFrac >= 0.1) currentDisplayInterval = (currentDisplayInterval * 2.00).toFixed(1);
else currentDisplayInterval = (currentDisplayInterval * 2.50).toFixed(1);
currentDisplayInterval=Math.min(Math.max(currentDisplayInterval, 30),10000); // limit this from about 30fps to .1 fps
currentDisplayInterval = Math.min(Math.max(currentDisplayInterval, 30), 10000); // limit this from about 30fps to .1 fps
imageLoadTimesEvaluated=0;
setSpeed(speedIndex);
$('fps').innerHTML="Display refresh rate is " + (1000 / currentDisplayInterval).toFixed(1) + " per second, avgFrac=" + avgFrac.toFixed(3) + ".";
}
function SetImageSource( monId, val ) {
// time is seconds since epoch
function SetImageSource( monId, time ) {
if ( liveMode == 1 ) {
return monitorImageObject[monId].src.replace(/rand=\d+/i, 'rand='+Math.floor((Math.random() * 1000000) ));
} else {
for ( var i=0, eIdlength = eId.length; i < eIdlength; i++ ) {
// Search for a match
if ( eMonId[i] == monId && val >= eStartSecs[i] && val <= eEndSecs[i] ) {
var frame = parseInt((val - eStartSecs[i])/(eEndSecs[i]-eStartSecs[i])*eventFrames[i])+1;
// Search for the event matching this time. Would be more efficient if we had events indexed by monitor
if ( eMonId[i] == monId && time >= eStartSecs[i] && time <= eEndSecs[i] ) {
var duration = eEndSecs[i]-eStartSecs[i];
var frame = parseInt((time - eStartSecs[i])/(duration)*eventFrames[i])+1;
console.log("SetImageSource: " + time + " duration: " + duration + " frame: " + frame );
return "index.php?view=image&eid=" + eId[i] + '&fid='+frame + "&width=" + monitorCanvasObj[monId].width + "&height=" + monitorCanvasObj[monId].height;
}
} // end for
@ -127,166 +133,174 @@ function loadImage2Monitor( monId, url ) {
}
function timerFire() {
// See if we need to reschedule
if(currentDisplayInterval != timerInterval || currentSpeed == 0) {
if ( currentDisplayInterval != timerInterval || currentSpeed == 0 ) {
// zero just turn off interrupts
clearInterval(timerObj);
timerInterval=currentDisplayInterval;
if(currentSpeed>0 || liveMode!=0) timerObj=setInterval(timerFire,timerInterval); // don't fire out of live mode if speed is zero
if ( currentSpeed>0 || liveMode!=0 ) timerObj=setInterval(timerFire, timerInterval); // don't fire out of live mode if speed is zero
}
if (liveMode) outputUpdate(currentTimeSecs); // In live mode we basically do nothing but redisplay
else if (currentTimeSecs + playSecsperInterval >= maxTimeSecs) // beyond the end just stop
{
if ( liveMode ) {
outputUpdate(currentTimeSecs); // In live mode we basically do nothing but redisplay
} else if (currentTimeSecs + playSecsperInterval >= maxTimeSecs) {
// beyond the end just stop
console.log("Current time " + currentTimeSecs + " + " + playSecsperInterval + " >= " + maxTimeSecs + " so stopping");
setSpeed(0);
outputUpdate(currentTimeSecs);
} else {
outputUpdate(playSecsperInterval + currentTimeSecs);
}
else outputUpdate(currentTimeSecs + playSecsperInterval);
return;
}
// val is seconds?
function drawSliderOnGraph(val) {
var sliderWidth=10;
var sliderLineWidth=1;
var sliderHeight=cHeight;
var sliderWidth=10;
var sliderLineWidth=1;
var sliderHeight=cHeight;
if(liveMode==1) {
val=Math.floor( Date.now() / 1000);
if ( liveMode == 1 ) {
val = Math.floor( Date.now() / 1000);
}
// Set some sizes
var labelpx = Math.max( 6, Math.min( 20, parseInt(cHeight * timeLabelsFractOfRow / (numMonitors+1)) ) );
var labbottom = parseInt(cHeight * 0.2 / (numMonitors+1)).toString() + "px"; // This is positioning same as row labels below, but from bottom so 1-position
var labfont = labelpx + "px"; // set this like below row labels
if ( numMonitors > 0 ) {
// if we have no data to display don't do the slider itself
var sliderX = parseInt( (val - minTimeSecs) / rangeTimeSecs * cWidth - sliderWidth/2); // position left side of slider
if ( sliderX < 0 ) sliderX = 0;
if ( sliderX+sliderWidth > cWidth )
sliderX=cWidth-sliderWidth-1;
// If we have data already saved first restore it from LAST time
if ( typeof underSlider !== 'undefined' ) {
ctx.putImageData(underSlider,underSliderX, 0, 0, 0, sliderWidth, sliderHeight);
underSlider = undefined;
}
// Set some sizes
var labelpx = Math.max( 6, Math.min( 20, parseInt(cHeight * timeLabelsFractOfRow / (numMonitors+1)) ) );
var labbottom=parseInt(cHeight * 0.2 / (numMonitors+1)).toString() + "px"; // This is positioning same as row labels below, but from bottom so 1-position
var labfont=labelpx + "px Georgia"; // set this like below row labels
if(numMonitors>0) {
// if we have no data to display don't do the slider itself
var sliderX=parseInt( (val - minTimeSecs) / rangeTimeSecs * cWidth - sliderWidth/2); // position left side of slider
if(sliderX < 0) sliderX=0;
if(sliderX+sliderWidth > cWidth) sliderX=cWidth-sliderWidth-1;
// If we have data already saved first restore it from LAST time
if(typeof underSlider !== 'undefined') {
ctx.putImageData(underSlider,underSliderX, 0, 0, 0, sliderWidth, sliderHeight);
underSlider=undefined;
}
if ( liveMode == 0 ) {
// we get rid of the slider if we switch to live (since it may not be in the "right" place)
// Now save where we are putting it THIS time
underSlider=ctx.getImageData(sliderX, 0, sliderWidth, sliderHeight);
// And add in the slider'
ctx.lineWidth=sliderLineWidth;
ctx.strokeStyle='black';
// looks like strokes are on the outside (or could be) so shrink it by the line width so we replace all the pixels
ctx.strokeRect(sliderX+sliderLineWidth,sliderLineWidth,sliderWidth - 2*sliderLineWidth, sliderHeight - 2*sliderLineWidth);
underSliderX=sliderX;
}
var o = $('scruboutput');
if(liveMode==1) {
o.innerHTML="Live Feed @ " + (1000 / currentDisplayInterval).toFixed(1) + " fps";
o.style.color="red";
} else {
o.innerHTML=secs2dbstr(val);
o.style.color="blue";
}
o.style.position="absolute";
o.style.bottom=labbottom;
o.style.font=labfont;
// try to get length and then when we get too close to the right switch to the left
var len = o.offsetWidth;
var x;
if(sliderX > cWidth/2)
x=sliderX - len - 10;
else
x=sliderX + 10;
o.style.left=x.toString() + "px";
if ( liveMode == 0 ) {
// we get rid of the slider if we switch to live (since it may not be in the "right" place)
// Now save where we are putting it THIS time
underSlider = ctx.getImageData(sliderX, 0, sliderWidth, sliderHeight);
// And add in the slider'
ctx.lineWidth = sliderLineWidth;
ctx.strokeStyle = 'black';
// looks like strokes are on the outside (or could be) so shrink it by the line width so we replace all the pixels
ctx.strokeRect(sliderX+sliderLineWidth,sliderLineWidth,sliderWidth - 2*sliderLineWidth, sliderHeight - 2*sliderLineWidth);
underSliderX = sliderX;
}
var o = $('scruboutput');
if ( liveMode == 1 ) {
o.innerHTML = "Live Feed @ " + (1000 / currentDisplayInterval).toFixed(1) + " fps";
o.style.color = "red";
} else {
o.innerHTML = secs2dbstr(val);
o.style.color="blue";
}
// This displays (or not) the left/right limits depending on how close the slider is.
// Because these change widths if the slider is too close, use the slider width as an estimate for the left/right label length (i.e. don't recalculate len from above)
// If this starts to collide increase some of the extra space
var o = $('scrubleft');
o.innerHTML=secs2dbstr(minTimeSecs);
o.style.position="absolute";
o.style.bottom=labbottom;
o.style.font=labfont;
o.style.left="5px";
if(numMonitors==0) // we need a len calculation if we skipped the slider
len = o.offsetWidth;
// If the slider will overlay part of this suppress (this is the left side)
if(len + 10 > sliderX || cWidth < len * 4 ) // that last check is for very narrow browsers
o.style.display="none";
o.style.bottom = labbottom;
o.style.font = labfont;
// try to get length and then when we get too close to the right switch to the left
var len = o.offsetWidth;
var x;
if(sliderX > cWidth/2)
x=sliderX - len - 10;
else
{
o.style.display="inline";
o.style.display="inline-flex"; // safari won't take this but will just ignore
}
x=sliderX + 10;
o.style.left=x.toString() + "px";
}
var o = $('scrubright');
o.innerHTML=secs2dbstr(maxTimeSecs);
o.style.position="absolute";
o.style.bottom=labbottom;
o.style.font=labfont;
// If the slider will overlay part of this suppress (this is the right side)
o.style.left=(cWidth - len - 15).toString() + "px";
if(sliderX > cWidth - len - 20 || cWidth < len * 4 )
o.style.display="none";
else
{
o.style.display="inline";
o.style.display="inline-flex";
}
// This displays (or not) the left/right limits depending on how close the slider is.
// Because these change widths if the slider is too close, use the slider width as an estimate for the left/right label length (i.e. don't recalculate len from above)
// If this starts to collide increase some of the extra space
var o = $('scrubleft');
o.innerHTML=secs2dbstr(minTimeSecs);
o.style.position="absolute";
o.style.bottom=labbottom;
o.style.font=labfont;
o.style.left="5px";
if ( numMonitors == 0 ) // we need a len calculation if we skipped the slider
len = o.offsetWidth;
// If the slider will overlay part of this suppress (this is the left side)
if ( len + 10 > sliderX || cWidth < len * 4 ) {
// that last check is for very narrow browsers
o.style.display="none";
} else {
o.style.display="inline";
o.style.display="inline-flex"; // safari won't take this but will just ignore
}
var o = $('scrubright');
o.innerHTML=secs2dbstr(maxTimeSecs);
o.style.position="absolute";
o.style.bottom=labbottom;
o.style.font=labfont;
// If the slider will overlay part of this suppress (this is the right side)
o.style.left=(cWidth - len - 15).toString() + "px";
if ( sliderX > cWidth - len - 20 || cWidth < len * 4 ) {
o.style.display="none";
} else {
o.style.display="inline";
o.style.display="inline-flex";
}
}
function drawGraph()
{
var divWidth=$('timelinediv').clientWidth
canvas.width = cWidth = divWidth; // Let it float and determine width (it should be sized a bit smaller percentage of window)
canvas.height=cHeight = parseInt(window.innerHeight * 0.10);
if(eId.length==0)
{
ctx.font="40px Georgia";
ctx.fillStyle="Black";
ctx.globalAlpha=1;
var t="No data found in range - choose differently";
var l=ctx.measureText(t).width;
ctx.fillText(t,(cWidth - l)/2, cHeight-10);
underSlider=undefined;
return;
}
var rowHeight=parseInt(cHeight / (numMonitors + 1) ); // Leave room for a scale of some sort
function drawGraph() {
var divWidth=$('timelinediv').clientWidth
canvas.width = cWidth = divWidth; // Let it float and determine width (it should be sized a bit smaller percentage of window)
cHeight = parseInt(window.innerHeight * 0.10);
if ( cHeight < numMonitors * 20 ) {
cHeight = numMonitors * 20;
}
// first fill in the bars for the events (not alarms)
canvas.height = cHeight;
for(var i=0; i<eId.length; i++) // Display all we loaded
{
var x1=parseInt( (eStartSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth) ; // round low end down
var x2=parseInt( (eEndSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ) ; // round high end up to be sure consecutive ones connect
ctx.fillStyle=monitorColour[eMonId[i]];
ctx.globalAlpha = 0.2; // light color for background
ctx.clearRect(x1,monitorIndex[eMonId[i]]*rowHeight,x2-x1,rowHeight); // Erase any overlap so it doesn't look artificially darker
ctx.fillRect (x1,monitorIndex[eMonId[i]]*rowHeight,x2-x1,rowHeight);
}
for(var i=0; (i<fScore.length) && (maxScore>0); i++) // Now put in scored frames (if any)
{
var x1=parseInt( (fTimeFromSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth) ; // round low end down
var x2=parseInt( (fTimeToSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ) ; // round up
if(x2-x1 < 2) x2=x1+2; // So it is visible make them all at least this number of seconds wide
ctx.fillStyle=monitorColour[fMonId[i]];
ctx.globalAlpha = 0.4 + 0.6 * (1 - fScore[i]/maxScore); // Background is scaled but even lowest is twice as dark as the background
ctx.fillRect(x1,monitorIndex[fMonId[i]]*rowHeight,x2-x1,rowHeight);
}
for(var i=0; i<numMonitors; i++) // Note that this may be a sparse array
{
ctx.font= parseInt(rowHeight * timeLabelsFractOfRow).toString() + "px Georgia";
ctx.fillStyle="Black";
ctx.globalAlpha=1;
ctx.fillText(monitorName[monitorPtr[i]], 0, (i + 1 - (1 - timeLabelsFractOfRow)/2 ) * rowHeight ); // This should roughly center font in row
}
underSlider=undefined; // flag we don't have a slider cached
drawSliderOnGraph(currentTimeSecs);
if ( eId.length == 0 ) {
ctx.globalAlpha=1;
ctx.font= "40px Georgia";
ctx.fillStyle="white";
var t="No data found in range - choose differently";
var l=ctx.measureText(t).width;
ctx.fillText(t,(cWidth - l)/2, cHeight-10);
underSlider=undefined;
return;
}
var rowHeight=parseInt(cHeight / (numMonitors + 1) ); // Leave room for a scale of some sort
// first fill in the bars for the events (not alarms)
for(var i=0; i<eId.length; i++) { // Display all we loaded
var x1=parseInt( (eStartSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth) ; // round low end down
var x2=parseInt( (eEndSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ) ; // round high end up to be sure consecutive ones connect
ctx.fillStyle=monitorColour[eMonId[i]];
ctx.globalAlpha = 0.2; // light color for background
ctx.clearRect(x1,monitorIndex[eMonId[i]]*rowHeight,x2-x1,rowHeight); // Erase any overlap so it doesn't look artificially darker
ctx.fillRect (x1,monitorIndex[eMonId[i]]*rowHeight,x2-x1,rowHeight);
}
for(var i=0; (i<fScore.length) && (maxScore>0); i++) {
// Now put in scored frames (if any)
var x1=parseInt( (fTimeFromSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth) ; // round low end down
var x2=parseInt( (fTimeToSecs[i] - minTimeSecs) / rangeTimeSecs * cWidth + 0.5 ) ; // round up
if(x2-x1 < 2) x2=x1+2; // So it is visible make them all at least this number of seconds wide
ctx.fillStyle=monitorColour[fMonId[i]];
ctx.globalAlpha = 0.4 + 0.6 * (1 - fScore[i]/maxScore); // Background is scaled but even lowest is twice as dark as the background
ctx.fillRect(x1,monitorIndex[fMonId[i]]*rowHeight,x2-x1,rowHeight);
}
for(var i=0; i<numMonitors; i++) {
// Note that this may be a sparse array
ctx.font= parseInt(rowHeight * timeLabelsFractOfRow).toString() + "px Georgia";
ctx.fillStyle="white";
ctx.globalAlpha=1;
ctx.fillText(monitorName[monitorPtr[i]], 0, (i + 1 - (1 - timeLabelsFractOfRow)/2 ) * rowHeight ); // This should roughly center font in row
}
underSlider=undefined; // flag we don't have a slider cached
drawSliderOnGraph(currentTimeSecs);
return;
}
function redrawScreen() {
@ -344,17 +358,14 @@ function redrawScreen() {
timerFire(); // force a fire in case it's not timing
}
function outputUpdate(val) {
drawSliderOnGraph(val);
for(var i=0; i<numMonitors; i++) {
loadImage2Monitor(monitorPtr[i],SetImageSource(monitorPtr[i],val));
function outputUpdate(time) {
drawSliderOnGraph(time);
for ( var i=0; i < numMonitors; i++ ) {
loadImage2Monitor(monitorPtr[i],SetImageSource(monitorPtr[i],time));
}
var currentTimeMS = new Date(val*1000);
currentTimeSecs=val;
currentTimeSecs = time;
}
/// Found this here: http://stackoverflow.com/questions/55677/how-do-i-get-the-coordinates-of-a-mouse-click-on-a-canvas-element
function relMouseCoords(event){
var totalOffsetX = 0;
@ -363,11 +374,10 @@ function relMouseCoords(event){
var canvasY = 0;
var currentElement = this;
do{
do {
totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
}
while(currentElement = currentElement.offsetParent)
} while(currentElement = currentElement.offsetParent);
canvasX = event.pageX - totalOffsetX;
canvasY = event.pageY - totalOffsetY;
@ -385,9 +395,9 @@ function mout(event) {mouseisdown=false;} // if we go outside treat it as relea
function tmove(event) {mouseisdown=true; mmove(event);}
function mmove(event) {
if(mouseisdown) {
if ( mouseisdown ) {
// only do anything if the mouse is depressed while on the sheet
var sec = minTimeSecs + rangeTimeSecs / event.target.width * event.target.relMouseCoords(event).x;
var sec = Math.floor(minTimeSecs + rangeTimeSecs / event.target.width * event.target.relMouseCoords(event).x);
outputUpdate(sec);
}
}
@ -406,21 +416,20 @@ function setFit(value) {
redrawScreen();
}
function showScale(newscale) // updates slider only
{
function showScale(newscale) {
// updates slider only
$('scaleslideroutput').innerHTML = parseFloat(newscale).toFixed(2).toString() + " x";
return;
}
function setScale(newscale) // makes actual change
{
showScale(newscale);
for(var i=0; i<numMonitors; i++)
{
monitorCanvasObj[monitorPtr[i]].width=monitorWidth[monitorPtr[i]]*monitorNormalizeScale[monitorPtr[i]]*monitorZoomScale[monitorPtr[i]]*newscale;
monitorCanvasObj[monitorPtr[i]].height=monitorHeight[monitorPtr[i]]*monitorNormalizeScale[monitorPtr[i]]*monitorZoomScale[monitorPtr[i]]*newscale;
}
currentScale=newscale;
function setScale(newscale) {
// makes actual change
showScale(newscale);
for(var i=0; i<numMonitors; i++) {
monitorCanvasObj[monitorPtr[i]].width=monitorWidth[monitorPtr[i]]*monitorNormalizeScale[monitorPtr[i]]*monitorZoomScale[monitorPtr[i]]*newscale;
monitorCanvasObj[monitorPtr[i]].height=monitorHeight[monitorPtr[i]]*monitorNormalizeScale[monitorPtr[i]]*monitorZoomScale[monitorPtr[i]]*newscale;
}
currentScale=newscale;
}
function showSpeed(val) {
@ -428,32 +437,36 @@ function showSpeed(val) {
$('speedslideroutput').innerHTML = parseFloat(speeds[val]).toFixed(2).toString() + " x";
}
function setSpeed(val) { // Note parameter is the index not the speed
var t;
if(liveMode==1) return; // we shouldn't actually get here but just in case
currentSpeed=parseFloat(speeds[val]);
speedIndex=val;
playSecsperInterval = currentSpeed * currentDisplayInterval / 1000;
showSpeed(val);
if( timerInterval != currentDisplayInterval || currentSpeed == 0 ) timerFire(); // if the timer isn't firing we need to trigger it to update
function setSpeed( speed_index ) {
if ( liveMode == 1 ) return; // we shouldn't actually get here but just in case
currentSpeed = parseFloat(speeds[speed_index]);
speedIndex = speed_index;
playSecsperInterval = Math.floor( 1000 * currentSpeed * currentDisplayInterval ) / 1000000;
console.log("playSecsPerInterval: " + playSecsperInterval + " = currentspeed:" + currentSpeed + " * " + currentDisplayInterval + " /1000");
showSpeed(speed_index);
if ( timerInterval != currentDisplayInterval || currentSpeed == 0 ) timerFire(); // if the timer isn't firing we need to trigger it to update
}
function setLive(value) {
liveMode=value;
redrawScreen();
liveMode = value;
redrawScreen();
}
//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// The section below are to reload this program with new parameters
function clicknav(minSecs,maxSecs,arch,live) {// we use the current time if we can
var now = new Date() / 1000;
function clicknav(minSecs,maxSecs,live) {// we use the current time if we can
var now = Math.floor( date.getTime() / 1000 );
var tz_difference = ( -1 * date.getTimezoneOffset() * 60 ) - server_utc_offset;
now -= tz_difference;
var minStr = "";
var maxStr = "";
var currentStr = "";
if ( minSecs > 0 ) {
if(maxSecs > now)
if ( maxSecs > now )
maxSecs = parseInt(now);
maxStr="&maxTime=" + secs2inputstr(maxSecs);
$('maxTime').value = secs2inputstr(maxSecs);
@ -487,44 +500,47 @@ function clicknav(minSecs,maxSecs,arch,live) {// we use the current time if we c
var uri = "?view=" + currentView + fitStr + groupStr + minStr + maxStr + currentStr + intervalStr + liveStr + zoomStr + "&scale=" + $j("#scaleslider")[0].value + "&speed=" + speeds[$j("#speedslider")[0].value];
window.location = uri;
} // end function clickNav
} // end function clicknav
function click_lastHour() {
var now = new Date() / 1000;
clicknav(now - 3600 + 1, now,1,0);
var now = Math.floor( date.getTime() / 1000 );
now -= -1 * date.getTimezoneOffset() * 60;
now += server_utc_offset;
clicknav(now - 3599, now, 0);
}
function click_lastEight() {
var now = new Date() / 1000;
clicknav(now - 3600*8 + 1, now,1,0);
var now = Math.floor( date.getTime() / 1000 );
now -= -1 * date.getTimezoneOffset() * 60 - server_utc_offset;
clicknav(now - 3600*8 + 1, now, 0);
}
function click_zoomin() {
rangeTimeSecs = parseInt(rangeTimeSecs / 2);
minTimeSecs = parseInt(currentTimeSecs - rangeTimeSecs/2); // this is the slider current time, we center on that
maxTimeSecs = parseInt(currentTimeSecs + rangeTimeSecs/2);
clicknav(minTimeSecs,maxTimeSecs,1,0);
clicknav(minTimeSecs,maxTimeSecs,0);
}
function click_zoomout() {
rangeTimeSecs = parseInt(rangeTimeSecs * 2);
minTimeSecs = parseInt(currentTimeSecs - rangeTimeSecs/2); // this is the slider current time, we center on that
maxTimeSecs = parseInt(currentTimeSecs + rangeTimeSecs/2);
clicknav(minTimeSecs,maxTimeSecs,1,0);
clicknav(minTimeSecs,maxTimeSecs,0);
}
function click_panleft() {
minTimeSecs = parseInt(minTimeSecs - rangeTimeSecs/2);
maxTimeSecs = minTimeSecs + rangeTimeSecs - 1;
clicknav(minTimeSecs,maxTimeSecs,1,0);
clicknav(minTimeSecs,maxTimeSecs,0);
}
function click_panright() {
minTimeSecs = parseInt(minTimeSecs + rangeTimeSecs/2);
maxTimeSecs = minTimeSecs + rangeTimeSecs - 1;
clicknav(minTimeSecs,maxTimeSecs,1,0);
clicknav(minTimeSecs,maxTimeSecs,0);
}
function click_all_events() {
clicknav(0,0,1,0);
clicknav(0,0,0);
}
function allnon() {
clicknav(0,0,0,0);
clicknav(0,0,0);
}
/// >>>>>>>>>>>>>>>>> handles packing different size/aspect monitors on screen <<<<<<<<<<<<<<<<<<<<<<<<
@ -551,7 +567,7 @@ function maxfit2(divW, divH) {
var borders=-1;
monitorPtr.sort(compSize);
//monitorPtr.sort(compSize); //Sorts monitors by size in viewport. If enabled makes captions not line up with graphs.
while(1) {
if( maxScale - minScale < 0.01 ) break;
@ -684,7 +700,19 @@ function clickMonitor(event,monId) {
}
function changeDateTime(e) {
e.form.submit();
var minStr = "&minTime="+($j('#minTime')[0].value);
var maxStr = "&maxTime="+($j('#maxTime')[0].value);
var liveStr="&live="+(liveMode?"1":"0");
var fitStr ="&fit="+(fitMode?"1":"0");
var zoomStr="";
for ( var i=0; i < numMonitors; i++ )
if ( monitorZoomScale[monitorPtr[i]] < 0.99 || monitorZoomScale[monitorPtr[i]] > 1.01 ) // allow for some up/down changes and just treat as 1 of almost 1
zoomStr += "&z" + monitorPtr[i].toString() + "=" + monitorZoomScale[monitorPtr[i]].toFixed(2);
var uri = "?view=" + currentView + fitStr + groupStr + minStr + maxStr + liveStr + zoomStr + "&scale=" + $j("#scaleslider")[0].value + "&speed=" + speeds[$j("#speedslider")[0].value];
window.location = uri;
}
// >>>>>>>>> Initialization that runs on window load by being at the bottom
@ -709,8 +737,9 @@ function initPage() {
}
drawGraph();
setSpeed(speedIndex);
setFit(fitMode); // will redraw
setLive(liveMode); // will redraw
//setFit(fitMode); // will redraw
//setLive(liveMode); // will redraw
redrawScreen();
}
window.addEventListener("resize",redrawScreen);
// Kick everything off

View File

@ -1,13 +1,21 @@
var server_utc_offset = <?php
$TimeZone = new DateTimeZone( ini_get('date.timezone') );
$now = new DateTime('now', $TimeZone);
$offset = $TimeZone->getOffset($now);
echo $offset . '; // ' . floor($offset / 3600) . ' hours ';
?>
var currentScale=<?php echo $defaultScale?>;
var liveMode=<?php echo $initialModeIsLive?>;
var fitMode=<?php echo $fitMode?>;
var currentSpeed=<?php echo $speeds[$speedIndex]?>; // slider scale, which is only for replay and relative to real time
var speedIndex=<?php echo $speedIndex?>;
var currentDisplayInterval=<?php echo $initialDisplayInterval?>; // will be set based on performance, this is the display interval in milliseconds for history, and fps for live, and dynamically determined (in ms)
// will be set based on performance, this is the display interval in milliseconds for history, and fps for live, and dynamically determined (in ms)
var currentDisplayInterval=<?php echo $initialDisplayInterval?>;
var playSecsperInterval=1; // How many seconds of recorded image we play per refresh determined by speed (replay rate) and display interval; (default=1 if coming from live)
var timerInterval; // milliseconds between interrupts
var timerObj; // object to hold timer interval;
var timerObj; // object to hold timer interval;
var freeTimeLastIntervals=[]; // Percentage of current interval used in loading most recent image
var imageLoadTimesEvaluated=0; // running count
var imageLoadTimesNeeded=15; // and how many we need
@ -28,12 +36,12 @@ $maxTimeSecs = strtotime('1950-01-01 01:01:01');
// This builds the list of events that are eligible from this range
$index=0;
$anyAlarms=false;
$index = 0;
$anyAlarms = false;
$result = dbQuery( $eventsSql );
if ( ! $result ) {
Fatal( "SQL-ERR");
Fatal('SQL-ERR');
return;
}
@ -159,7 +167,7 @@ if ( $numMonitors > 0 ) $avgArea = $avgArea / $numMonitors;
$numMonitors = 0;
foreach ( $monitors as $m ) {
echo " monitorLoading[" . $m->Id() . "]=false;\n";
echo " monitorImageURL[" . $m->Id() . "]='".$m->getStreamSrc( array('mode'=>'single','scale'=>$defaultScale*100), '&' )."';\n";
echo " monitorImageURL[" . $m->Id() . "]='".$m->getStreamSrc( array('mode'=>'single','scale'=>$defaultScale*100), '&' )."';\n";
echo " monitorLoadingStageURL[" . $m->Id() . "] = '';\n";
echo " monitorColour[" . $m->Id() . "]=\"" . $m->WebColour() . "\";\n";
echo " monitorWidth[" . $m->Id() . "]=" . $m->Width() . ";\n";
@ -177,13 +185,13 @@ foreach ( $monitors as $m ) {
$numMonitors += 1;
}
echo "var numMonitors = $numMonitors;\n";
echo "var minTimeSecs='" . $minTimeSecs . "';\n";
echo "var maxTimeSecs='" . $maxTimeSecs . "';\n";
echo "var minTimeSecs=parseInt(" . $minTimeSecs . ");\n";
echo "var maxTimeSecs=parseInt(" . $maxTimeSecs . ");\n";
echo "var rangeTimeSecs=" . ( $maxTimeSecs - $minTimeSecs + 1) . ";\n";
if(isset($defaultCurrentTime))
echo "var currentTimeSecs=" . strtotime($defaultCurrentTime) . ";\n";
echo "var currentTimeSecs=parseInt(" . strtotime($defaultCurrentTime) . ");\n";
else
echo "var currentTimeSecs=" . ($minTimeSecs + $maxTimeSecs)/2 . ";\n";
echo "var currentTimeSecs=parseInt(" . ($minTimeSecs + $maxTimeSecs)/2 . ");\n";
echo 'var speeds=[';
for ($i=0; $i<count($speeds); $i++)

View File

@ -2,6 +2,7 @@ var filterQuery = '<?php echo validJsStr($filterQuery) ?>';
<?php
$jsMonitors = array();
$fields = array('Name', 'LabelFormat', 'SaveJPEGs', 'VideoWriter');
foreach ( $monitors as $monitor ) {
if ( !empty($monitorIds[$monitor['Id']]) ) {

View File

@ -672,7 +672,7 @@ switch ( $tab ) {
case 'general' :
{
?>
<tr><td><?php echo translate('Name') ?></td><td><input type="text" name="newMonitor[Name]" value="<?php echo validHtmlStr($monitor->Name()) ?>" size="16"/></td></tr>
<tr class="Name"><td><?php echo translate('Name') ?></td><td><input type="text" name="newMonitor[Name]" value="<?php echo validHtmlStr($monitor->Name()) ?>" /></td></tr>
<tr><td><?php echo translate('Server') ?></td><td>
<?php
$servers = array(''=>'None');

View File

@ -90,10 +90,21 @@ $group_dropdowns = ob_get_contents();
ob_end_clean();
$groupSql = Group::get_group_sql( $group_id );
$monitor_id = 0;
if ( isset( $_REQUEST['monitor_id'] ) ) {
$monitor_id = $_REQUEST['monitor_id'];
} else if ( isset($_COOKIE['zmMonitorId']) ) {
$monitor_id = $_COOKIE['zmMonitorId'];
}
$monitors = array();
$monitors_dropdown = array( '' => 'All' );
$sql = "SELECT * FROM Monitors WHERE Function != 'None'";
if ( $groupSql ) { $sql .= ' AND ' . $groupSql; };
$sql .= 'ORDER BY Sequence';
if ( $monitor_id ) { $sql .= ' AND Id='.$monitor_id; };
$sql .= ' ORDER BY Sequence';
foreach( dbFetchAll( $sql ) as $row ) {
if ( !visibleMonitor( $row['Id'] ) ) {
continue;
@ -105,7 +116,8 @@ foreach( dbFetchAll( $sql ) as $row ) {
if ( ZM_OPT_CONTROL && $row['ControlId'] && $row['Controllable'] )
$showControl = true;
$row['connKey'] = generateConnKey();
$monitors[] = new Monitor( $row );
$Monitor = $monitors[] = new Monitor( $row );
$monitors_dropdown[$Monitor->Id()] = $Monitor->Name();
if ( ! isset( $widths[$row['Width']] ) ) {
$widths[$row['Width']] = $row['Width'];
}
@ -140,10 +152,11 @@ if ( $showZones ) {
</div>
<div id="headerControl">
<span id="groupControl"><label><?php echo translate('Group') ?>:</label>
<?php
echo $group_dropdowns;
?>
<?php echo $group_dropdowns; ?>
</span>
<span id="monitorControl"><label><?php echo translate('Monitor') ?>:</label>
<?php echo htmlSelect( 'monitor_id', $monitors_dropdown, $monitor_id, array('onchange'=>'changeMonitor(this);') ); ?>
</span>
<span id="widthControl"><label><?php echo translate('Width') ?>:</label><?php echo htmlSelect( 'width', $widths, $options['width'], 'changeSize(this);' ); ?></span>
<span id="heightControl"><label><?php echo translate('Height') ?>:</label><?php echo htmlSelect( 'height', $heights, $options['height'], 'changeSize(this);' ); ?></span>
<span id="scaleControl"><label><?php echo translate('Scale') ?>:</label><?php echo htmlSelect( 'scale', $scales, $scale, 'changeScale(this);' ); ?></span>

View File

@ -64,12 +64,19 @@ $groupSql = Group::get_group_sql( $group_id );
session_start();
foreach ( array('minTime','maxTime') as $var ) {
if ( isset( $_REQUEST[$var] ) ) {
$_SESSION[$var] = $_REQUEST[$var];
}
if ( isset( $_REQUEST[$var] ) ) {
$_SESSION[$var] = $_REQUEST[$var];
}
}
session_write_close();
$monitor_id = 0;
if ( isset( $_REQUEST['monitor_id'] ) ) {
$monitor_id = $_REQUEST['monitor_id'];
} else if ( isset($_COOKIE['zmMonitorId']) ) {
$monitor_id = $_COOKIE['zmMonitorId'];
}
$monitorsSql = "SELECT * FROM Monitors WHERE Function != 'None'" . ( $groupSql ? ' AND ' . $groupSql : '');
// Note that this finds incomplete events as well, and any frame records written, but still cannot "see" to the end frame
@ -106,6 +113,11 @@ if ( ! empty( $user['MonitorIds'] ) ) {
$monitorsSql .= ' AND Id IN ('.$user['MonitorIds'].')';
$frameSql .= ' AND E.MonitorId IN ('.$user['MonitorIds'].')';
}
if ( $monitor_id ) {
$monitorsSql .= ' AND Id='.$monitor_id;
$eventsSql .= ' AND M.Id='.$monitor_id;
$frameSql .= ' AND E.MonitorId='.$monitor_id;
}
// Parse input parameters -- note for future, validate/clean up better in case we don't get called from self.
// Live overrides all the min/max stuff but it is still processed
@ -177,14 +189,12 @@ if ( isset($minTime) && isset($maxTime) ) {
}
$frameSql .= ' GROUP BY E.Id, E.MonitorId, F.TimeStamp, F.Delta ORDER BY E.MonitorId, F.TimeStamp ASC';
// This loads all monitors the user can see - even if we don't have data for one we still show all for switch to live.
$monitors = array();
$monitorsSql .= ' ORDER BY Sequence ASC';
$index=0;
foreach( dbFetchAll( $monitorsSql ) as $row ) {
$monitors[$index] = new Monitor( $row );
$index = $index + 1;
$Monitor = new Monitor( $row );
$monitors[] = $Monitor;
}
// These are zoom ranges per visible monitor
@ -201,6 +211,9 @@ xhtmlHeaders(__FILE__, translate('MontageReview') );
<span id="groupControl"><label><?php echo translate('Group') ?>:</label>
<?php echo $group_dropdowns; ?>
</span>
<span id="monitorControl"><label><?php echo translate('Monitor') ?>:</label>
<?php Group::get_monitors_dropdown( $groupSql ? array( 'groupSql'=>$groupSql) : null ); ?>
</span>
<div id="DateTimeDiv">
<input type="datetime-local" name="minTime" id="minTime" value="<?php echo preg_replace('/ /', 'T', $minTime ) ?>" onchange="changeDateTime(this);"> to
<input type="datetime-local" name="maxTime" id="maxTime" value="<?php echo preg_replace('/ /', 'T', $maxTime ) ?>" onchange="changeDateTime(this);">
@ -221,7 +234,7 @@ xhtmlHeaders(__FILE__, translate('MontageReview') );
<button type="button" id="zoomout" onclick="click_zoomout();" ><?php echo translate('Out -') ?></button>
<button type="button" id="lasteight" onclick="click_lastEight();" ><?php echo translate('8 Hour') ?></button>
<button type="button" id="lasthour" onclick="click_lastHour();" ><?php echo translate('1 Hour') ?></button>
<button type="button" id="allof" onclick="click_all_events();" ><?php echo translate('All Events') ?></button>
<button type="button" id="allof" onclick="click_all_events();" ><?php echo translate('All Events') ?></button>
<button type="button" id="live" onclick="setLive(1-liveMode);"><?php echo translate('Live') ?></button>
<button type="button" id="fit" onclick="setFit(1-fitMode);" ><?php echo translate('Fit') ?></button>
<button type="button" id="panright" onclick="click_panright();" ><?php echo translate('Pan') ?> &gt;</button>
@ -239,11 +252,10 @@ xhtmlHeaders(__FILE__, translate('MontageReview') );
<?php
// Monitor images - these had to be loaded after the monitors used were determined (after loading events)
foreach ($monitors as $m) {
echo '<canvas width="' . $m->Width() * $defaultScale . '" height="' . $m->Height() * $defaultScale . '" id="Monitor' . $m->Id() . '" style="border:3px solid ' . $m->WebColour() . '" onclick="clickMonitor(event,' . $m->Id() . ')">No Canvas Support!!</canvas>';
echo '<canvas width="' . $m->Width() * $defaultScale . '" height="' . $m->Height() * $defaultScale . '" id="Monitor' . $m->Id() . '" style="border:1px solid ' . $m->WebColour() . '" onclick="clickMonitor(event,' . $m->Id() . ')">No Canvas Support!!</canvas>';
}
?>
</div>
<p id="fps">evaluating fps</p>
</div>
<?php xhtmlFooter() ?>

View File

@ -178,7 +178,7 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<td class="colMonitors"><?php echo validHtmlStr($row['Monitors']) ?></td>
<td class="colGroups"><?php echo validHtmlStr($row['Groups']) ?></td>
<td class="colSystem"><?php echo validHtmlStr($row['System']) ?></td>
<td class="colBandwidth"><?php echo $row['MaxBandwidth']?$bwArray[$row['MaxBandwidth']]:'&nbsp;' ?></td>
<td class="colBandwidth"><?php echo $row['MaxBandwidth']?$bandwidth_options[$row['MaxBandwidth']]:'&nbsp;' ?></td>
<td class="colMonitor"><?php echo $row['MonitorIds']?(join( ", ", $userMonitors )):"&nbsp;" ?></td>
<td class="colMark"><input type="checkbox" name="markUids[]" value="<?php echo $row['Id'] ?>" onclick="configureDeleteButton( this );"<?php if ( !$canEdit ) { ?> disabled="disabled"<?php } ?>/></td>
</tr>
@ -199,10 +199,10 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<input type="hidden" name="tab" value="<?php echo $tab ?>"/>
<input type="hidden" name="action" value="delete"/>
<input type="hidden" name="object" value="server"/>
<table id="contentTable" class="table table-striped" cellspacing="0">
<table id="contentTable" class="table table-striped">
<thead>
<tr>
<th class="colName"><?php echo translate('name') ?></th>
<th class="colName"><?php echo translate('Name') ?></th>
<th class="colHostname"><?php echo translate('Hostname') ?></th>
<th class="colMark"><?php echo translate('Mark') ?></th>
</tr>
@ -223,7 +223,7 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
</div>
</form>
<?php
} else if ( $tab == "storage" ) { ?>
} else if ( $tab == 'storage' ) { ?>
<form name="storageForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
<input type="hidden" name="view" value="<?php echo $view ?>"/>
<input type="hidden" name="tab" value="<?php echo $tab ?>"/>
@ -235,6 +235,7 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<th class="colId"><?php echo translate('Id') ?></th>
<th class="colName"><?php echo translate('name') ?></th>
<th class="colPath"><?php echo translate('path') ?></th>
<th class="colType"><?php echo translate('Type') ?></th>
<th class="colMark"><?php echo translate('Mark') ?></th>
</tr>
</thead>
@ -244,7 +245,8 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<td class="colId"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Id']), $canEdit ) ?></td>
<td class="colName"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Name']), $canEdit ) ?></td>
<td class="colPath"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Path']), $canEdit ) ?></td>
<td class="colMark"><input type="checkbox" name="markIds[]" value="<?php echo $row['Id'] ?>" onclick="configureDeleteButton( this );"<?php if ( !$canEdit ) { ?> disabled="disabled"<?php } ?>/></td>
<td class="colType"><?php echo makePopupLink( '?view=storage&amp;id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Type']), $canEdit ) ?></td>
<td class="colMark"><input type="checkbox" name="markIds[]" value="<?php echo $row['Id'] ?>" onclick="configureDeleteButton(this);"<?php if ( !$canEdit ) { ?> disabled="disabled"<?php } ?>/></td>
</tr>
<?php } #end foreach Server ?>
</tbody>
@ -257,11 +259,11 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
</form>
<?php
} else {
if ( $tab == "system" ) {
if ( $tab == 'system' ) {
$configCats[$tab]['ZM_LANG_DEFAULT']['Hint'] = join( '|', getLanguages() );
$configCats[$tab]['ZM_SKIN_DEFAULT']['Hint'] = join( '|', $skin_options );
$configCats[$tab]['ZM_CSS_DEFAULT']['Hint'] = join( '|', array_map ( 'basename', glob('skins/'.ZM_SKIN_DEFAULT.'/css/*',GLOB_ONLYDIR) ) );
$configCats[$tab]['ZM_BANDWIDTH_DEFAULT']['Hint'] = $bandwidth_options;
}
?>
<form name="optionsForm" class="form-horizontal" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>">
@ -270,8 +272,7 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<input type="hidden" name="action" value="options"/>
<?php
$configCat = $configCats[$tab];
foreach ( $configCat as $name=>$value )
{
foreach ( $configCat as $name=>$value ) {
$shortName = preg_replace( '/^ZM_/', '', $name );
$optionPromptText = !empty($OLANG[$shortName])?$OLANG[$shortName]['Prompt']:$value['Prompt'];
?>
@ -279,12 +280,15 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<label for="<?php echo $name ?>" class="col-sm-3 control-label"><?php echo $shortName ?></label>
<div class="col-sm-6">
<?php
if ( $value['Type'] == "boolean" ) {
if ( $value['Type'] == 'boolean' ) {
?>
<input type="checkbox" id="<?php echo $name ?>" name="newConfig[<?php echo $name ?>]" value="1"<?php if ( $value['Value'] ) { ?> checked="checked"<?php } ?><?php echo $canEdit?'':' disabled="disabled"' ?>/>
<?php
} elseif ( preg_match( "/\|/", $value['Hint'] ) ) {
} elseif ( is_array( $value['Hint'] ) ) {
echo htmlSelect( "newConfig[$name]", $value['Hint'], $value['Value'] );
} elseif ( preg_match( '/\|/', $value['Hint'] ) ) {
?>
<?php
$options = explode( '|', $value['Hint'] );
if ( count( $options ) > 3 ) {
@ -307,12 +311,12 @@ foreach( array_map( 'basename', glob('skins/'.$current_skin.'/css/*',GLOB_ONLYDI
<?php
} else {
foreach ( $options as $option ) {
if ( preg_match( '/^([^=]+)=(.+)$/', $option ) ) {
$optionLabel = $matches[1];
$optionValue = $matches[2];
} else {
$optionLabel = $optionValue = $option;
}
if ( preg_match( '/^([^=]+)=(.+)$/', $option ) ) {
$optionLabel = $matches[1];
$optionValue = $matches[2];
} else {
$optionLabel = $optionValue = $option;
}
?>
<label>
<input type="radio" id="<?php echo $name.'_'.preg_replace( '/[^a-zA-Z0-9]/', '', $optionValue ) ?>" name="newConfig[<?php echo $name ?>]" value="<?php echo $optionValue ?>"<?php if ( $value['Value'] == $optionValue ) { ?> checked="checked"<?php } ?><?php echo $canEdit?'':' disabled="disabled"' ?>/>

View File

@ -18,15 +18,14 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
if ( !canEdit( 'System' ) )
{
$view = "error";
return;
if ( !canEdit( 'System' ) ) {
$view = 'error';
return;
}
if ( $_REQUEST['id'] ) {
if ( !($newServer = dbFetchOne( 'SELECT * FROM Servers WHERE Id = ?', NULL, ARRAY($_REQUEST['id'])) ) ) {
$view = "error";
$view = 'error';
return;
}
} else {
@ -37,19 +36,19 @@ if ( $_REQUEST['id'] ) {
$focusWindow = true;
xhtmlHeaders(__FILE__, translate('Server')." - ".$newServer['Name'] );
xhtmlHeaders(__FILE__, translate('Server').' - '.$newServer['Name'] );
?>
<body>
<div id="page">
<div id="header">
<h2><?php echo translate('Server')." - ".$newServer['Name'] ?></h2>
<h2><?php echo translate('Server').' - '.$newServer['Name'] ?></h2>
</div>
<div id="content">
<form name="contentForm" method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>" onsubmit="return validateForm( this, <?php echo empty($newServer['Name'])?'true':'false' ?> )">
<input type="hidden" name="view" value="<?php echo $view ?>"/>
<input type="hidden" name="object" value="server"/>
<input type="hidden" name="id" value="<?php echo validHtmlStr($_REQUEST['id']) ?>"/>
<table id="contentTable" class="major" cellspacing="0">
<table id="contentTable" class="major">
<tbody>
<tr>
<th scope="row"><?php echo translate('Name') ?></th>
@ -62,7 +61,7 @@ xhtmlHeaders(__FILE__, translate('Server')." - ".$newServer['Name'] );
</tbody>
</table>
<div id="contentButtons">
<input type="hidden" name="action" value="Save"/>
<input type="hidden" name="action" value="Save"/>
<input type="submit" value="<?php echo translate('Save') ?>"/>
<input type="button" value="<?php echo translate('Cancel') ?>" onclick="closeWindow();"/>
</div>

View File

@ -24,7 +24,7 @@ if ( !canEdit( 'System' ) ) {
}
if ( $_REQUEST['id'] ) {
if ( !($newStorage = dbFetchOne( 'SELECT * FROM Storage WHERE Id = ?', NULL, ARRAY($_REQUEST['id'])) ) ) {
if ( !($newStorage = dbFetchOne('SELECT * FROM Storage WHERE Id=?', NULL, ARRAY($_REQUEST['id'])) ) ) {
$view = 'error';
return;
}
@ -32,8 +32,11 @@ if ( $_REQUEST['id'] ) {
$newStorage = array();
$newStorage['Name'] = translate('NewStorage');
$newStorage['Path'] = '';
$newStorage['Type'] = 'local';
}
$type_options = array( 'local' => translate('Local'), 's3fs' => translate('s3fs') );
$focusWindow = true;
xhtmlHeaders(__FILE__, translate('Storage')." - ".$newStorage['Name'] );
@ -48,7 +51,7 @@ xhtmlHeaders(__FILE__, translate('Storage')." - ".$newStorage['Name'] );
<input type="hidden" name="view" value="<?php echo $view ?>"/>
<input type="hidden" name="object" value="storage"/>
<input type="hidden" name="id" value="<?php echo validHtmlStr($_REQUEST['id']) ?>"/>
<table id="contentTable" class="major" cellspacing="0">
<table id="contentTable" class="major">
<tbody>
<tr>
<th scope="row"><?php echo translate('Name') ?></th>
@ -58,6 +61,10 @@ xhtmlHeaders(__FILE__, translate('Storage')." - ".$newStorage['Name'] );
<th scope="row"><?php echo translate('Path') ?></th>
<td><input type="text" name="newStorage[Path]" value="<?php echo $newStorage['Path'] ?>"/></td>
</tr>
<tr>
<th scope="row"><?php echo translate('Type') ?></th>
<td><?php echo htmlSelect( 'newStorage[Type]', $type_options, $newStorage['Type'] ); ?></td>
</tr>
</tbody>
</table>
<div id="contentButtons">

View File

@ -359,7 +359,9 @@ if ( $event ) {
$currEventSlots[$i]['count']++;
}
}
if ( $event['MaxScore'] > 0 ) {
Warning("Has max Scoer");
if ( $startIndex == $endIndex ) {
$framesSql = 'SELECT FrameId,Score FROM Frames WHERE EventId = ? AND Score > 0 ORDER BY Score DESC LIMIT 1';
$frame = dbFetchOne( $framesSql, NULL, array($event['Id']) );
@ -447,7 +449,6 @@ foreach ( array_keys($monitorIds) as $monitorId ) {
$maxHeight = $monitors[$monitorId]['Height'];
}
//print_r( $monEventSlots );
// Optimise boxes
foreach( array_keys($monEventSlots) as $monitorId ) {
unset( $currEventSlots );
@ -751,7 +752,7 @@ if ( $mode == 'overlay' ) {
echo drawYGrid( $chart, $majYScale, 'majLabelY', 'majTickY', 'majGridY graphWidth' );
}
echo drawXGrid( $chart, $majXScale, 'majLabelX', 'majTickX', 'majGridX graphHeight', 'zoom graphHeight' );
Warning("Mode: $mode");
if ( $mode == 'overlay' ) {
?>
<div id="activity" class="activitySize">

View File

@ -43,7 +43,7 @@ $monitorIds = array_flip(explode( ',', $newUser['MonitorIds'] ));
$yesno = array( 0=>translate('No'), 1=>translate('Yes') );
$nv = array( 'None'=>translate('None'), 'View'=>translate('View') );
$nve = array( 'None'=>translate('None'), 'View'=>translate('View'), 'Edit'=>translate('Edit') );
$bandwidths = array_merge( array( ""=>"" ), $bwArray );
$bandwidths = array_merge( array( ""=>"" ), $bandwidth_options );
$langs = array_merge( array( ""=>"" ), getLanguages() );
$sql = "select Id,Name from Monitors order by Sequence asc";

View File

@ -59,21 +59,45 @@ $Event = null;
$path = null;
if ( empty($_REQUEST['path']) ) {
if ( ! empty($_REQUEST['fid']) ) {
$show = empty($_REQUEST['show']) ? 'capture' : $_REQUEST['show'];
if ( ! empty($_REQUEST['eid'] ) ) {
$Event = new Event( $_REQUEST['eid'] );
$Frame = Frame::find_one( array( 'EventId' => $_REQUEST['eid'], 'FrameId' => $_REQUEST['fid'] ) );
if ( ! $Frame ) {
Fatal("No Frame found for event(".$_REQUEST['eid'].") and frame id(".$_REQUEST['fid'].")");
}
} else {
if ( ! empty($_REQUEST['fid']) ) {
if ( $_REQUEST['fid'] == 'snapshot' ) {
$Event = new Event( $_REQUEST['eid'] );
$Frame = new Frame();
$Frame->FrameId('snapshot');
$path = $Event->Path().'/snapshot.jpg';
Warning("Path to snapshot: $path");
} else {
$show = empty($_REQUEST['show']) ? 'capture' : $_REQUEST['show'];
if ( ! empty($_REQUEST['eid'] ) ) {
$Event = new Event( $_REQUEST['eid'] );
$Frame = Frame::find_one( array( 'EventId' => $_REQUEST['eid'], 'FrameId' => $_REQUEST['fid'] ) );
if ( ! $Frame ) {
$previousBulkFrame = dbFetchOne( "SELECT * FROM Frames WHERE EventId=? AND FrameId < ? AND Type='BULK' ORDER BY FrameID DESC LIMIT 1", NULL, array($_REQUEST['eid'], $_REQUEST['fid'] ) );
$nextBulkFrame = dbFetchOne( "SELECT * FROM Frames WHERE EventId=? AND FrameId > ? AND Type='BULK' ORDER BY FrameID ASC LIMIT 1", NULL, array($_REQUEST['eid'], $_REQUEST['fid'] ) );
if ( $previousBulkFrame and $nextBulkFrame ) {
$Frame = new Frame( $previousBulkFrame );
$Frame->FrameId( $_REQUEST['fid'] );
$percentage = floor( ( $Frame->FrameId() - $previousBulkFrame['FrameId'] ) / ( $nextBulkFrame['FrameId'] / $previousBulkFrame['FrameId'] ) )/100;
$Frame->Delta( $previousBulkFrame['Delta'] + floor( 100* ( $nextBulkFrame['Delta'] - $previousBulkFrame['Delta'] ) * $percentage )/100 );
Logger::Debug("Got virtual frame from Bulk Frames previous delta: " . $previousBulkFrame['Delta'] . " + nextdelta:" . $nextBulkFrame['Delta'] . ' - ' . $previousBulkFrame['Delta'] . ' * ' . $percentage );
} else {
Fatal("No Frame found for event(".$_REQUEST['eid'].") and frame id(".$_REQUEST['fid'].")");
}
}
// Frame can be non-existent. We have Bulk frames. So now we should try to load the bulk frame
} else {
# If we are only specifying fid, then the fid must be the primary key into the frames table. But when the event is specified, then it is the frame #
$Frame = new Frame( $_REQUEST['fid'] );
$Event = new Event( $Frame->EventId() );
$Frame = new Frame( $_REQUEST['fid'] );
$Event = new Event( $Frame->EventId() );
}
$path = $Event->Path().'/'.sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d',$Frame->FrameId()).'-'.$show.'.jpg';
}
$path = $Event->Path().'/'.sprintf('%0'.ZM_EVENT_IMAGE_DIGITS.'d',$Frame->FrameId()).'-'.$show.'.jpg';
} else {
Fatal("No Frame ID specified");