wip
This commit is contained in:
commit
b58c9d87c3
|
@ -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 */;
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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',
|
||||
|
|
|
@ -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__
|
||||
|
||||
|
|
|
@ -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() );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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, ×tamp );
|
||||
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 ) {
|
||||
|
|
|
@ -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 ¬es );
|
||||
|
||||
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();
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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 );
|
||||
};
|
||||
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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() );
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -24,12 +24,29 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
ZMPacket::ZMPacket( ) {
|
||||
keyframe = 0;
|
||||
image = NULL;
|
||||
frame = NULL;
|
||||
av_init_packet( &packet );
|
||||
gettimeofday( ×tamp, NULL );
|
||||
}
|
||||
|
||||
ZMPacket::ZMPacket( Image *i ) {
|
||||
keyframe = 1;
|
||||
image = i;
|
||||
frame = NULL;
|
||||
av_init_packet( &packet );
|
||||
gettimeofday( ×tamp, NULL );
|
||||
}
|
||||
|
||||
ZMPacket::ZMPacket( AVPacket *p ) {
|
||||
av_init_packet( &packet );
|
||||
if ( zm_av_packet_ref( &packet, p ) < 0 ) {
|
||||
Error("error refing packet");
|
||||
}
|
||||
gettimeofday( ×tamp, 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
};
|
||||
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);};
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 );
|
||||
};
|
||||
|
||||
|
|
47
src/zma.cpp
47
src/zma.cpp
|
@ -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();
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
?>
|
||||
|
|
|
@ -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" );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
?>
|
||||
|
|
|
@ -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'};
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -38,3 +38,7 @@
|
|||
input[type=range]::-ms-tooltip {
|
||||
display: none;
|
||||
}
|
||||
#timeline {
|
||||
color: white;
|
||||
font-size: 40px;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
||||
?>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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&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.'&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.'&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.'&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.'&page=1'.$eventCounts[$i]['filter']['query'], $eventsWindow, ZM_WEB_EVENTS_VIEW, $eventCounts[$i]['total'], canView( 'Events' ) ) ?></td>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -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="<+" id="prevBtnVjs" title="<?php echo translate('Prev') ?>" class="active" onclick="streamPrev( true );"/>
|
||||
<input type="button" value="+>" id="nextBtnVjs" title="<?php echo translate('Next') ?>" class="active" onclick="streamNext( true );"/>
|
||||
<p id="dvrControls" class="dvrControls">
|
||||
<input type="button" value="<+" id="prevBtn" title="<?php echo translate('Prev') ?>" class="inactive" onclick="streamPrev( true );"/>
|
||||
<input type="button" value="+>" 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="<+" id="prevBtn" title="<?php echo translate('Prev') ?>" class="inactive" onclick="streamPrev( true );"/>
|
||||
<input type="button" value="<<" 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">
|
||||
|
|
|
@ -154,10 +154,12 @@ if ( $pagination ) {
|
|||
<a id="filterLink" href="#" onclick="createPopup( '?view=filter&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&mid='.$event->MonitorId(), 'zmMonitor'.$event-
|
|||
<td class="colMaxScore"><?php echo makePopupLink( '?view=frame&eid='.$event->Id().'&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&mid='.$event->MonitorId(), 'zmMonitor'.$event-
|
|||
?>
|
||||
<td class="colThumbnail">
|
||||
<?php
|
||||
if ( file_exists( $event->Path().'/snapshot.jpg' ) ) {
|
||||
Warning("Using snapshot");
|
||||
$imgSrc = '?view=image&eid='.$event->Id().'&fid=snapshot&width='.$thumbData['Width'].'&height='.$thumbData['Height'];
|
||||
} else {
|
||||
Warning("Not Using snapshot" . $event->Path().'/snapshot.jpg' );
|
||||
$imgSrc = '?view=image&eid='.$event->Id().'&fid='.$thumbData['FrameId'].'&width='.$thumbData['Width'].'&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&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 ) {
|
||||
|
|
|
@ -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'];
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -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);' ));
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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') ?>";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++)
|
||||
|
|
|
@ -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']]) ) {
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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') ?> ></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() ?>
|
||||
|
|
|
@ -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']]:' ' ?></td>
|
||||
<td class="colBandwidth"><?php echo $row['MaxBandwidth']?$bandwidth_options[$row['MaxBandwidth']]:' ' ?></td>
|
||||
<td class="colMonitor"><?php echo $row['MonitorIds']?(join( ", ", $userMonitors )):" " ?></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&id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Id']), $canEdit ) ?></td>
|
||||
<td class="colName"><?php echo makePopupLink( '?view=storage&id='.$row['Id'], 'zmStorage', 'storage', validHtmlStr($row['Name']), $canEdit ) ?></td>
|
||||
<td class="colPath"><?php echo makePopupLink( '?view=storage&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&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"' ?>/>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in New Issue