add a scale element to the frame view. Include some bits from StorageAreas to make it work

This commit is contained in:
Isaac Connor 2016-10-26 13:34:28 -04:00
parent b723e08b17
commit 98cde11e86
4 changed files with 287 additions and 84 deletions

View File

@ -1,6 +1,4 @@
<?php <?php
require_once( 'database.php' );
require_once( 'Storage.php' );
class Event { class Event {
public function __construct( $IdOrRow ) { public function __construct( $IdOrRow ) {
@ -9,11 +7,15 @@ class Event {
if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) { if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) {
$row = dbFetchOne( 'SELECT *,unix_timestamp(StartTime) as Time FROM Events WHERE Id=?', NULL, array( $IdOrRow ) ); $row = dbFetchOne( 'SELECT *,unix_timestamp(StartTime) as Time FROM Events WHERE Id=?', NULL, array( $IdOrRow ) );
if ( ! $row ) { if ( ! $row ) {
Error("Unable to load Event record for Id=" . $IdOrRow ); Error('Unable to load Event record for Id=' . $IdOrRow );
} }
} elseif ( is_array( $IdOrRow ) ) { } elseif ( is_array( $IdOrRow ) ) {
$row = $IdOrRow; $row = $IdOrRow;
} else { } else {
$backTrace = debug_backtrace();
$file = $backTrace[1]['file'];
$line = $backTrace[1]['line'];
Error("Unknown argument passed to Event Constructor from $file:$line)");
Error("Unknown argument passed to Event Constructor ($IdOrRow)"); Error("Unknown argument passed to Event Constructor ($IdOrRow)");
return; return;
} }
@ -24,14 +26,17 @@ class Event {
$this->{$k} = $v; $this->{$k} = $v;
} }
} else { } else {
Error("No row for Event " . $IdOrRow ); Error('No row for Event ' . $IdOrRow );
} }
} // end function __construct } // end function __construct
public function Storage() { public function Storage() {
return new Storage( isset($this->{'StorageId'}) ? $this->{'StorageId'} : NULL ); return new Storage( isset($this->{'StorageId'}) ? $this->{'StorageId'} : NULL );
} }
public function Monitor() {
return new Monitor( isset($this->{'MonitorId'}) ? $this->{'MonitorId'} : NULL );
}
public function __call( $fn, array $args){ public function __call( $fn, array $args){
if(isset($this->{$fn})){ if ( array_key_exists( $fn, $this ) ) {
return $this->{$fn}; return $this->{$fn};
#array_unshift($args, $this); #array_unshift($args, $this);
#call_user_func_array( $this->{$fn}, $args); #call_user_func_array( $this->{$fn}, $args);
@ -50,38 +55,27 @@ class Event {
return $Storage->Path().'/'.$this->Relative_Path(); return $Storage->Path().'/'.$this->Relative_Path();
} }
public function Relative_Path() { public function Relative_Path() {
$event_path = ""; $event_path = '';
if ( ZM_USE_DEEP_STORAGE ) if ( ZM_USE_DEEP_STORAGE ) {
{ $event_path = $this->{'MonitorId'} .'/'.strftime( '%y/%m/%d/%H/%M/%S', $this->Time()) ;
$event_path = } else {
$this->{'MonitorId'} $event_path = $this->{'MonitorId'} .'/'.$this->{'Id'};
.'/'.strftime( "%y/%m/%d/%H/%M/%S",
$this->Time()
)
;
}
else
{
$event_path =
$this->{'MonitorId'}
.'/'.$this->{'Id'}
;
} }
return( $event_path ); return( $event_path );
} // end function Relative_Path()
} public function Link_Path() {
public function LinkPath() {
if ( ZM_USE_DEEP_STORAGE ) { if ( ZM_USE_DEEP_STORAGE ) {
return $this->{'MonitorId'} .'/'.strftime( "%y/%m/%d/.", $this->Time()).$this->{'Id'}; return $this->{'MonitorId'} .'/'.strftime( '%y/%m/%d/.', $this->Time()).$this->{'Id'};
} }
Error("Calling Link_Path when not using deep storage"); Error('Calling Link_Path when not using deep storage');
return ''; return '';
} }
public function delete() { public function delete() {
# This wouldn't work with foreign keys
dbQuery( 'DELETE FROM Events WHERE Id = ?', array($this->{'Id'}) ); dbQuery( 'DELETE FROM Events WHERE Id = ?', array($this->{'Id'}) );
if ( !ZM_OPT_FAST_DELETE ) { if ( !ZM_OPT_FAST_DELETE ) {
dbQuery( 'DELETE FROM Stats WHERE EventId = ?', array($this->{'Id'}) ); dbQuery( 'DELETE FROM Stats WHERE EventId = ?', array($this->{'Id'}) );
@ -90,15 +84,29 @@ class Event {
# Assumption: All events haev a start time # Assumption: All events haev a start time
$start_date = date_parse( $this->{'StartTime'} ); $start_date = date_parse( $this->{'StartTime'} );
if ( ! $start_date ) {
Error('Unable to parse start time for event ' . $this->{'Id'} . ' not deleting files.' );
return;
}
$start_date['year'] = $start_date['year'] % 100; $start_date['year'] = $start_date['year'] % 100;
$Storage = $this->Storage();
# So this is because ZM creates a link under teh day pointing to the time that the event happened. # So this is because ZM creates a link under teh day pointing to the time that the event happened.
$eventlink_path = $Storage->Path().'/'.$this->Link_Path(); $link_path = $this->Link_Path();
if ( ! $link_path ) {
Error('Unable to determine link path for event ' . $this->{'Id'} . ' not deleting files.' );
return;
}
$Storage = $this->Storage();
$eventlink_path = $Storage->Path().'/'.$link_path;
if ( $id_files = glob( $eventlink_path ) ) { if ( $id_files = glob( $eventlink_path ) ) {
if ( ! $eventPath = readlink($id_files[0]) ) {
Error("Unable to read link at $id_files[0]");
return;
}
# I know we are using arrays here, but really there can only ever be 1 in the array # I know we are using arrays here, but really there can only ever be 1 in the array
$eventPath = preg_replace( '/\.'.$event['Id'].'$/', readlink($id_files[0]), $id_files[0] ); $eventPath = preg_replace( '/\.'.$this->{'Id'}.'$/', $eventPath, $id_files[0] );
deletePath( $eventPath ); deletePath( $eventPath );
deletePath( $id_files[0] ); deletePath( $id_files[0] );
$pathParts = explode( '/', $eventPath ); $pathParts = explode( '/', $eventPath );
@ -119,34 +127,182 @@ class Event {
} # end Event->delete } # end Event->delete
public function getStreamSrc( $args, $querySep='&amp;' ) { public function getStreamSrc( $args, $querySep='&amp;' ) {
return ZM_BASE_URL.'/index.php?view=view_video&eid='.$this->{'Id'}; return ( ZM_BASE_PATH != '/' ? ZM_BASE_PATH : '' ).'/index.php?view=view_video&eid='.$this->{'Id'};
$streamSrc = ZM_BASE_URL.ZM_PATH_ZMS; $streamSrc = ZM_BASE_URL.ZM_PATH_ZMS;
$args[] = "source=event&event=".$this->{'Id'}; $args[] = 'source=event&event='.$this->{'Id'};
if ( ZM_OPT_USE_AUTH ) { if ( ZM_OPT_USE_AUTH ) {
if ( ZM_AUTH_RELAY == "hashed" ) { if ( ZM_AUTH_RELAY == 'hashed' ) {
$args[] = "auth=".generateAuthHash( ZM_AUTH_HASH_IPS ); $args[] = 'auth='.generateAuthHash( ZM_AUTH_HASH_IPS );
} elseif ( ZM_AUTH_RELAY == "plain" ) { } elseif ( ZM_AUTH_RELAY == 'plain' ) {
$args[] = "user=".$_SESSION['username']; $args[] = 'user='.$_SESSION['username'];
$args[] = "pass=".$_SESSION['password']; $args[] = 'pass='.$_SESSION['password'];
} elseif ( ZM_AUTH_RELAY == "none" ) { } elseif ( ZM_AUTH_RELAY == "none" ) {
$args[] = "user=".$_SESSION['username']; $args[] = 'user='.$_SESSION['username'];
} }
} }
if ( !in_array( "mode=single", $args ) && !empty($GLOBALS['connkey']) ) { if ( !in_array( 'mode=single', $args ) && !empty($GLOBALS['connkey']) ) {
$args[] = "connkey=".$GLOBALS['connkey']; $args[] = 'connkey='.$GLOBALS['connkey'];
} }
if ( ZM_RAND_STREAM ) { if ( ZM_RAND_STREAM ) {
$args[] = "rand=".time(); $args[] = 'rand='.time();
} }
if ( count($args) ) { if ( count($args) ) {
$streamSrc .= "?".join( $querySep, $args ); $streamSrc .= '?'.join( $querySep, $args );
} }
return( $streamSrc ); return( $streamSrc );
} // end function getStreamSrc } // end function getStreamSrc
function DiskSpace() {
return folder_size( $this->Path() );
}
function createListThumbnail( $overwrite=false ) {
# Load the frame with the highest score to use as a thumbnail
if ( !($frame = dbFetchOne( 'SELECT * FROM Frames WHERE EventId=? AND Score=? ORDER BY FrameId LIMIT 1', NULL, array( $this->{'Id'}, $this->{'MaxScore'} ) )) ) {
Error("Unable to find a Frame matching max score " . $this->{'MaxScore'} . ' for event ' . $this->{'Id'} );
// FIXME: What if somehow the db frame was lost or score was changed? Should probably try another search for any frame.
return( false );
}
$frameId = $frame['FrameId'];
if ( ZM_WEB_LIST_THUMB_WIDTH ) {
$thumbWidth = ZM_WEB_LIST_THUMB_WIDTH;
$scale = (SCALE_BASE*ZM_WEB_LIST_THUMB_WIDTH)/$this->{'Width'};
$thumbHeight = reScale( $this->{'Height'}, $scale );
} elseif ( ZM_WEB_LIST_THUMB_HEIGHT ) {
$thumbHeight = ZM_WEB_LIST_THUMB_HEIGHT;
$scale = (SCALE_BASE*ZM_WEB_LIST_THUMB_HEIGHT)/$this->{'Height'};
$thumbWidth = reScale( $this->{'Width'}, $scale );
} else {
Fatal( "No thumbnail width or height specified, please check in Options->Web" );
}
$imageData = $this->getImageSrc( $frame, $scale, false, $overwrite );
if ( ! $imageData ) {
return ( false );
}
$thumbData = $frame;
$thumbData['Path'] = $imageData['thumbPath'];
$thumbData['Width'] = (int)$thumbWidth;
$thumbData['Height'] = (int)$thumbHeight;
return( $thumbData );
} // end function createListThumbnail
function getImageSrc( $frame, $scale=SCALE_BASE, $captureOnly=false, $overwrite=false ) {
$Storage = new Storage( $this->{'StorageId'} );
$Event = $this;
$eventPath = $Event->Path();
if ( !is_array($frame) )
$frame = array( 'FrameId'=>$frame, 'Type'=>'' );
if ( file_exists( $eventPath.'/snapshot.jpg' ) ) {
$captImage = "snapshot.jpg";
} else {
$captImage = sprintf( '%0'.ZM_EVENT_IMAGE_DIGITS.'d-capture.jpg', $frame['FrameId'] );
if ( ! file_exists( $eventPath.'/'.$captImage ) ) {
# Generate the frame JPG
if ( $Event->DefaultVideo() ) {
$videoPath = $eventPath.'/'.$Event->DefaultVideo();
if ( ! file_exists( $videoPath ) ) {
Error("Event claims to have a video file, but it does not seem to exist at $videoPath" );
return '';
}
$command ='ffmpeg -v 0 -i '.$videoPath.' -vf "select=gte(n\\,'.$frame['FrameId'].'),setpts=PTS-STARTPTS" '.$eventPath.'/'.$captImage;
Debug( "Running $command" );
$output = array();
$retval = 0;
exec( $command, $output, $retval );
Debug("Retval: $retval, output: " . implode("\n", $output));
} else {
Error("Can't create frame images from video becuase there is no video file for this event (".$Event->DefaultVideo() );
}
}
}
$captPath = $eventPath.'/'.$captImage;
if ( ! file_exists( $captPath ) ) {
Error( "Capture file does not exist at $captPath" );
return '';
}
$thumbCaptPath = ZM_DIR_IMAGES.'/'.$this->{'Id'}.'-'.$captImage;
//echo "CI:$captImage, CP:$captPath, TCP:$thumbCaptPath<br>";
$analImage = sprintf( '%0'.ZM_EVENT_IMAGE_DIGITS.'d-analyse.jpg', $frame['FrameId'] );
$analPath = $eventPath.'/'.$analImage;
$thumbAnalPath = ZM_DIR_IMAGES.'/'.$this->{'Id'}.'-'.$analImage;
//echo "AI:$analImage, AP:$analPath, TAP:$thumbAnalPath<br>";
$alarmFrame = $frame['Type']=='Alarm';
$hasAnalImage = $alarmFrame && file_exists( $analPath ) && filesize( $analPath );
$isAnalImage = $hasAnalImage && !$captureOnly;
if ( !ZM_WEB_SCALE_THUMBS || $scale >= SCALE_BASE || !function_exists( 'imagecreatefromjpeg' ) ) {
$imagePath = $thumbPath = $isAnalImage?$analPath:$captPath;
$imageFile = $imagePath;
$thumbFile = $thumbPath;
} else {
if ( version_compare( phpversion(), '4.3.10', '>=') )
$fraction = sprintf( '%.3F', $scale/SCALE_BASE );
else
$fraction = sprintf( '%.3f', $scale/SCALE_BASE );
$scale = (int)round( $scale );
$thumbCaptPath = preg_replace( '/\.jpg$/', "-$scale.jpg", $thumbCaptPath );
$thumbAnalPath = preg_replace( '/\.jpg$/', "-$scale.jpg", $thumbAnalPath );
if ( $isAnalImage ) {
$imagePath = $analPath;
$thumbPath = $thumbAnalPath;
} else {
$imagePath = $captPath;
$thumbPath = $thumbCaptPath;
}
$thumbFile = $thumbPath;
if ( $overwrite || !file_exists( $thumbFile ) || !filesize( $thumbFile ) )
{
// Get new dimensions
list( $imageWidth, $imageHeight ) = getimagesize( $imagePath );
$thumbWidth = $imageWidth * $fraction;
$thumbHeight = $imageHeight * $fraction;
// Resample
$thumbImage = imagecreatetruecolor( $thumbWidth, $thumbHeight );
$image = imagecreatefromjpeg( $imagePath );
imagecopyresampled( $thumbImage, $image, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $imageWidth, $imageHeight );
if ( !imagejpeg( $thumbImage, $thumbPath ) )
Error( "Can't create thumbnail '$thumbPath'" );
}
}
$imageData = array(
'eventPath' => $eventPath,
'imagePath' => $imagePath,
'thumbPath' => $thumbPath,
'imageFile' => $imagePath,
'thumbFile' => $thumbFile,
'imageClass' => $alarmFrame?"alarm":"normal",
'isAnalImage' => $isAnalImage,
'hasAnalImage' => $hasAnalImage,
);
return( $imageData );
}
} # end class } # end class
?> ?>

View File

@ -27,10 +27,26 @@ class Storage {
if ( isset( $this->{'Path'} ) and ( $this->{'Path'} != '' ) ) { if ( isset( $this->{'Path'} ) and ( $this->{'Path'} != '' ) ) {
return $this->{'Path'}; return $this->{'Path'};
} else if ( ! isset($this->{'Id'}) ) { } else if ( ! isset($this->{'Id'}) ) {
return ZM_DIR_EVENTS; $path = ZM_DIR_EVENTS;
if ( $path[0] != '/' ) {
$this->{'Path'} = ZM_PATH_WEB.'/'.ZM_DIR_EVENTS;
} else {
$this->{'Path'} = ZM_DIR_EVENTS;
}
return $this->{'Path'};
} }
return $this->{'Name'}; return $this->{'Name'};
} }
public function Name() {
if ( isset( $this->{'Name'} ) and ( $this->{'Name'} != '' ) ) {
return $this->{'Name'};
} else if ( ! isset($this->{'Id'}) ) {
return 'Default';
}
return $this->{'Name'};
}
public function __call( $fn, array $args= NULL){ public function __call( $fn, array $args= NULL){
if(isset($this->{$fn})){ if(isset($this->{$fn})){
return $this->{$fn}; return $this->{$fn};
@ -47,5 +63,19 @@ class Storage {
} }
return $storage_areas; return $storage_areas;
} }
public function disk_usage_percent() {
$path = $this->Path();
$total = disk_total_space( $path );
if ( ! $total ) {
Error("disk_total_space returned false for " . $path );
return 0;
}
$free = disk_free_space( $path );
if ( ! $free ) {
Error("disk_free_space returned false for " . $path );
}
$usage = round(($total - $free) / $total * 100);
return $usage;
}
} }
?> ?>

View File

@ -0,0 +1,15 @@
function changeScale() {
var scale = $('scale').get('value');
var img = $('frameImg');
if ( img ) {
var baseWidth = $('base_width').value;
var baseHeight = $('base_height').value;
var newWidth = ( baseWidth * scale ) / SCALE_BASE;
var newHeight = ( baseHeight * scale ) / SCALE_BASE;
img.style.width = newWidth + "px";
img.style.height = newHeight + "px";
}
Cookie.write( 'zmWatchScale', scale, { duration: 10*365 } );
}

View File

@ -0,0 +1,2 @@
var SCALE_BASE = <?php echo SCALE_BASE ?>;