2010-02-23 09:12:12 +00:00
< ? php
//
// ZoneMinder web image view file, $Date: 2008-09-29 14:15:13 +0100 (Mon, 29 Sep 2008) $, $Revision: 2640 $
// Copyright (C) 2001-2008 Philip Coombes
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
2016-12-26 15:23:16 +00:00
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2010-02-23 09:12:12 +00:00
//
2015-08-09 16:02:13 +00:00
// Calling sequence: ... /zm/index.php?view=image&path=/monid/path/image.jpg&scale=nnn&width=wwww&height=hhhhh
//
// Path is physical path to the image starting at the monitor id
//
// Scale is optional and between 1 and 400 (percent),
// Omitted or 100 = no scaling done, image passed through directly
// Scaling will increase response time slightly
//
2015-08-09 22:19:12 +00:00
// width and height are each optional, ideally supply both, but if only one is supplied the other is calculated
// These are in pixels
2015-08-09 16:02:13 +00:00
//
2015-08-09 22:19:12 +00:00
// If both scale and either width or height are specified, scale is ignored
2015-08-09 16:02:13 +00:00
//
2021-04-12 19:57:39 +00:00
if ( ! canView ( 'Events' ) and ( $_REQUEST [ 'fid' ] != 'snapshot' or ! canView ( 'Snapshots' ))) {
2017-05-18 16:49:59 +00:00
$view = 'error' ;
return ;
2010-02-23 09:12:12 +00:00
}
2016-01-14 18:35:30 +00:00
require_once ( 'includes/Event.php' );
2016-04-25 18:59:55 +00:00
require_once ( 'includes/Frame.php' );
2016-01-14 18:35:30 +00:00
2015-11-15 21:40:25 +00:00
// Compatibility for PHP 5.4
2018-08-31 15:57:47 +00:00
if ( ! function_exists ( 'imagescale' ) ) {
2016-05-16 16:23:19 +00:00
function imagescale ( $image , $new_width , $new_height = - 1 , $mode = 0 ) {
$mode ; // Not supported
2015-11-15 21:40:25 +00:00
2016-05-16 16:23:19 +00:00
$new_height = ( $new_height == - 1 ) ? imagesy ( $image ) : $new_height ;
$imageNew = imagecreatetruecolor ( $new_width , $new_height );
imagecopyresampled ( $imageNew , $image , 0 , 0 , 0 , 0 , ( int ) $new_width , ( int ) $new_height , imagesx ( $image ), imagesy ( $image ));
2016-05-02 17:36:19 +00:00
2016-05-16 16:23:19 +00:00
return $imageNew ;
}
2015-11-15 21:40:25 +00:00
}
2023-04-22 14:29:27 +00:00
if ( ! empty ( $_REQUEST [ 'proxy' ])) {
$url = $_REQUEST [ 'proxy' ];
if ( ! $url ) {
ZM\Warning ( 'No url passed to image proxy' );
return ;
}
2023-07-25 16:32:13 +00:00
2023-04-22 14:29:27 +00:00
$url_parts = parse_url ( $url );
$username = $url_parts [ 'user' ];
2023-07-05 19:51:16 +00:00
$password = isset ( $url_parts [ 'pass' ]) ? $url_parts [ 'pass' ] : '' ;
2023-04-22 14:29:27 +00:00
$method = 'GET' ;
// preparing http options:
$opts = array (
'http' => array (
'method' => $method ,
#'header'=>"Accept-language: en\r\n" .
'ignore_errors' => true
#"Cookie: foo=bar\r\n"
),
'ssl' => array (
" verify_peer " => false ,
" verify_peer_name " => false ,
)
);
$context = stream_context_create ( $opts );
// set no time limit and disable compression:
set_time_limit ( 5 );
@ apache_setenv ( 'no-gzip' , 1 );
@ ini_set ( 'zlib.output_compression' , 0 );
/* Sends an http request with additional headers shown above */
2023-05-15 14:05:06 +00:00
$fp = @ fopen ( $url , 'r' , false , $context );
2023-04-22 14:29:27 +00:00
$r = '' ;
if ( $fp ) {
$meta_data = stream_get_meta_data ( $fp );
ZM\Debug ( print_r ( $meta_data , true ));
foreach ( $meta_data [ 'wrapper_data' ] as $header ) {
2023-07-05 19:51:16 +00:00
preg_match ( '/WWW-Authenticate: Digest (.*)/i' , $header , $matches );
2023-04-22 14:29:27 +00:00
$nc = 1 ;
if ( ! empty ( $matches )) {
ZM\Debug ( " Matched $header " );
$auth_header = $matches [ 1 ];
$auth_header_array = explode ( ',' , $auth_header );
$parsed = array ();
2023-07-25 16:32:13 +00:00
2023-04-22 14:29:27 +00:00
foreach ( $auth_header_array as $pair ) {
2023-07-25 16:32:13 +00:00
preg_match ( '/^\s*(\w+)="?(.+)"?\s*$/' , $pair , $vals );
if ( ! empty ( $vals )) {
$parsed [ $vals [ 1 ]] = trim ( $vals [ 2 ], '"' );
} else {
ZM\Debug ( " DIdn't match preg $pair " );
}
2023-04-22 14:29:27 +00:00
}
ZM\Debug ( print_r ( $parsed , true ));
2023-07-25 16:32:13 +00:00
$cnonce = uniqid ();
2023-04-22 14:29:27 +00:00
$response_realm = ( isset ( $parsed [ 'realm' ])) ? $parsed [ 'realm' ] : '' ;
$response_nonce = ( isset ( $parsed [ 'nonce' ])) ? $parsed [ 'nonce' ] : '' ;
$response_opaque = ( isset ( $parsed [ 'opaque' ])) ? $parsed [ 'opaque' ] : '' ;
$authenticate1 = md5 ( $username . ':' . $response_realm . ':' . $password );
$authenticate2 = md5 ( $method . ':' . $url );
2023-07-25 16:32:13 +00:00
$digestData = $authenticate1 . ':' . $response_nonce ;
2023-04-22 14:29:27 +00:00
if ( ! empty ( $parsed [ 'qop' ])) {
$digestData .= ':' . sprintf ( '%08x' , $nc ) . ':' . $cnonce . ':' . $parsed [ 'qop' ];
}
$authenticate_response = md5 ( $digestData . ':' . $authenticate2 );
$request = sprintf ( 'Authorization: Digest username="%s", realm="%s", nonce="%s", uri="%s", response="%s"' ,
$username , $response_realm , $response_nonce , $url , $authenticate_response );
if ( ! empty ( $parsed [ 'opaque' ])) $request .= ', opaque="' . $parsed [ 'opaque' ] . '"' ;
if ( ! empty ( $parsed [ 'qop' ])) {
$request .= ', qop="' . $parsed [ 'qop' ] . '"' ;
$request .= ', nc="' . sprintf ( '%08x' , $nc ) . '"' ;
$nc ++ ;
$request .= ', cnonce="' . $cnonce . '"' ;
}
$request .= ', algorithm="MD5"' ;
ZM\Debug ( $request );
$request_header = array ( $request );
$opts [ 'http' ][ 'header' ] = $request ;
$context = stream_context_create ( $opts );
$fp = fopen ( $url , 'r' , false , $context );
$meta_data = stream_get_meta_data ( $fp );
ZM\Debug ( print_r ( $meta_data , true ));
} # end if have auth
} # end foreach header
2023-07-25 16:32:13 +00:00
2023-04-22 14:29:27 +00:00
while ( substr_count ( $r , 'Content-Length' ) != 2 ) {
$new = fread ( $fp , 512 );
if ( ! $new ) break ;
$r .= $new ;
}
#ZM\Debug($r);
$start = strpos ( $r , " \xff " );
2023-05-15 14:05:06 +00:00
if ( false !== $start ) {
$end = strpos ( $r , " -- \n " , $start ) - 1 ;
$frame = substr ( $r , $start , $end - $start );
ZM\Debug ( " Start $start end $end " );
header ( 'Content-type: image/jpeg' );
echo $frame ;
} else {
$img = imagecreate ( 320 , 240 );
$textbgcolor = imagecolorallocate ( $img , 0 , 0 , 0 );
$textcolor = imagecolorallocate ( $img , 255 , 255 , 255 );
imagestring ( $img , 5 , 5 , 5 , 'Authentication Failed' , $textcolor );
header ( 'Content-type: image/jpeg' );
imagejpeg ( $img );
}
2023-04-22 14:29:27 +00:00
fclose ( $fp );
} else {
ZM\Debug ( " Failed to open $url " );
2023-05-15 14:05:06 +00:00
$img = imagecreate ( 320 , 200 );
$textbgcolor = imagecolorallocate ( $img , 0 , 0 , 0 );
$textcolor = imagecolorallocate ( $img , 255 , 255 , 255 );
imagestring ( $img , 5 , 5 , 5 , 'Failed to open' , $textcolor );
header ( 'Content-type: image/jpeg' );
imagejpeg ( $img );
2023-04-22 14:29:27 +00:00
}
return ;
}
2010-02-23 09:12:12 +00:00
$errorText = false ;
2016-11-21 17:28:15 +00:00
$filename = '' ;
2017-01-13 19:42:10 +00:00
$Frame = null ;
$Event = null ;
2017-04-07 17:20:54 +00:00
$path = null ;
2020-03-14 18:24:39 +00:00
$media_type = 'image/jpeg' ;
2016-11-21 17:28:15 +00:00
2016-05-16 16:23:19 +00:00
if ( empty ( $_REQUEST [ 'path' ]) ) {
2017-10-10 19:11:59 +00:00
2018-05-11 13:53:24 +00:00
$show = empty ( $_REQUEST [ 'show' ]) ? 'capture' : $_REQUEST [ 'show' ];
2018-05-11 14:37:00 +00:00
if ( empty ( $_REQUEST [ 'fid' ]) ) {
header ( 'HTTP/1.0 404 Not Found' );
2022-02-08 23:11:33 +00:00
ZM\Error ( 'No Frame ID specified' );
2018-05-11 14:37:00 +00:00
return ;
}
if ( ! empty ( $_REQUEST [ 'eid' ]) ) {
2019-02-22 14:19:07 +00:00
$Event = ZM\Event :: find_one ( array ( 'Id' => $_REQUEST [ 'eid' ]));
2018-08-31 15:57:47 +00:00
if ( ! $Event ) {
2018-05-11 14:37:00 +00:00
header ( 'HTTP/1.0 404 Not Found' );
2022-02-08 23:11:33 +00:00
ZM\Error ( 'Event ' . $_REQUEST [ 'eid' ] . ' Not found' );
2018-05-11 14:37:00 +00:00
return ;
}
2020-03-10 17:51:55 +00:00
if ( $_REQUEST [ 'fid' ] == 'objdetect' ) {
2022-02-08 23:11:33 +00:00
// if animation file is found, return that, else return image
// we are only looking for GIF or jpg here, not mp4
// as most often, browsers asking for this link will be expecting
// media types that can be rendered as <img src=>
$path_anim_gif = $Event -> Path () . '/objdetect.gif' ;
$path_image = $Event -> Path () . '/objdetect.jpg' ;
if ( file_exists ( $path_anim_gif )) {
// we found the animation gif file
$media_type = 'image/gif' ;
ZM\Debug ( " Animation file found at $path " );
$path = $path_anim_gif ;
} else if ( file_exists ( $path_image )) {
// animation not found, but image found
ZM\Debug ( " Image file found at $path " );
$path = $path_image ;
} else {
// neither animation nor image found
header ( 'HTTP/1.0 404 Not Found' );
ZM\Error ( 'Object detection animation and image not found for this event' );
return ;
}
$Frame = new ZM\Frame ();
$Frame -> Id ( 'objdetect' );
} else if ( $_REQUEST [ 'fid' ] == 'objdetect_mp4' ) {
$path = $Event -> Path () . '/objdetect.mp4' ;
if ( ! file_exists ( $path ) ) {
header ( 'HTTP/1.0 404 Not Found' );
ZM\Error ( " File $path does not exist. You might not have enabled create_animation in objectconfig.ini. If you have, inspect debug logs for errors during creation " );
return ;
}
$Frame = new ZM\Frame ();
$Frame -> Id ( 'objdetect' );
$media_type = 'video/mp4' ;
} else if ( $_REQUEST [ 'fid' ] == 'objdetect_gif' ) {
$path = $Event -> Path () . '/objdetect.gif' ;
if ( ! file_exists ( $path ) ) {
header ( 'HTTP/1.0 404 Not Found' );
ZM\Error ( " File $path does not exist. You might not have enabled create_animation in objectconfig.ini. If you have, inspect debug logs for errors during creation " );
return ;
2020-03-14 18:18:25 +00:00
}
$Frame = new ZM\Frame ();
$Frame -> Id ( 'objdetect' );
2020-03-14 18:24:39 +00:00
$media_type = 'image/gif' ;
2020-03-14 18:18:25 +00:00
} else if ( $_REQUEST [ 'fid' ] == 'objdetect_jpg' ) {
2019-02-11 21:29:19 +00:00
$path = $Event -> Path () . '/objdetect.jpg' ;
2019-02-11 21:37:22 +00:00
if ( ! file_exists ( $path ) ) {
2019-02-08 18:49:00 +00:00
header ( 'HTTP/1.0 404 Not Found' );
2022-02-08 23:11:33 +00:00
ZM\Error ( " File $path does not exist. Please make sure store_frame_in_zm is enabled in the object detection config " );
return ;
2019-02-08 18:49:00 +00:00
}
2019-02-22 14:19:07 +00:00
$Frame = new ZM\Frame ();
2019-02-11 21:37:22 +00:00
$Frame -> Id ( 'objdetect' );
2019-02-11 21:29:19 +00:00
} else if ( $_REQUEST [ 'fid' ] == 'alarm' ) {
2019-10-30 20:57:45 +00:00
$path = $Event -> Path () . '/alarm.jpg' ;
if ( ! file_exists ( $path ) ) {
# legacy support
# look for first alarmed frame
$Frame = ZM\Frame :: find_one (
array ( 'EventId' => $_REQUEST [ 'eid' ], 'Type' => 'Alarm' ),
array ( 'order' => 'FrameId ASC' ));
if ( ! $Frame ) { # no alarms, get first one I find
$Frame = ZM\Frame :: find_one ( array ( 'EventId' => $_REQUEST [ 'eid' ]));
if ( ! $Frame ) {
ZM\Warning ( 'No frame found for event ' . $_REQUEST [ 'eid' ]);
$Frame = new ZM\Frame ();
$Frame -> Delta ( 1 );
$Frame -> FrameId ( 1 );
}
}
if ( $Event -> SaveJPEGs () & 1 ) {
# If we store Frames as jpgs, then we don't store an alarmed snapshot
$path = $Event -> Path () . '/' . sprintf ( '%0' . ZM_EVENT_IMAGE_DIGITS . 'd' , $Frame -> FrameId ()) . '-' . $show . '.jpg' ;
} else {
header ( 'HTTP/1.0 404 Not Found' );
2023-06-09 14:40:34 +00:00
ZM\Error ( 'No alarm jpg found for event ' . $_REQUEST [ 'eid' ] . ' at ' . $path );
2019-10-30 20:57:45 +00:00
return ;
2018-11-12 17:43:20 +00:00
}
} else {
2019-02-22 14:19:07 +00:00
$Frame = new ZM\Frame ();
2018-05-11 14:37:00 +00:00
$Frame -> Delta ( 1 );
2019-10-30 20:57:45 +00:00
$Frame -> FrameId ( 'alarm' );
} # alarm.jpg found
} else if ( $_REQUEST [ 'fid' ] == 'snapshot' ) {
$path = $Event -> Path () . '/snapshot.jpg' ;
if ( ! file_exists ( $path ) ) {
$Frame = ZM\Frame :: find_one ( array ( 'EventId' => $_REQUEST [ 'eid' ], 'Score' => $Event -> MaxScore ()));
if ( ! $Frame )
$Frame = ZM\Frame :: find_one ( array ( 'EventId' => $_REQUEST [ 'eid' ]));
if ( ! $Frame ) {
ZM\Warning ( 'No frame found for event ' . $_REQUEST [ 'eid' ]);
$Frame = new ZM\Frame ();
$Frame -> Delta ( 1 );
if ( $Event -> SaveJPEGs () & 1 ) {
$Frame -> FrameId ( 0 );
} else {
$Frame -> FrameId ( 'snapshot' );
}
}
2019-08-26 19:04:59 +00:00
if ( $Event -> SaveJPEGs () & 1 ) {
2019-10-30 20:57:45 +00:00
# If we store Frames as jpgs, then we don't store a snapshot
$path = $Event -> Path () . '/' . sprintf ( '%0' . ZM_EVENT_IMAGE_DIGITS . 'd' , $Frame -> FrameId ()) . '-' . $show . '.jpg' ;
2019-04-04 16:18:46 +00:00
} else {
2021-01-26 17:35:17 +00:00
if ( $Event -> DefaultVideo () ) {
2023-04-22 14:30:25 +00:00
$file_path = $Event -> Path () . '/' . $Event -> DefaultVideo ();
if ( ! file_exists ( $file_path )) {
if ( $file = find_video ( $Event -> Path ())) {
$file_path = $Event -> Path () . '/' . $file ;
}
}
$command = ZM_PATH_FFMPEG . ' -ss ' . $Frame -> Delta () . ' -i ' . $file_path . ' -frames:v 1 ' . $path . ' 2>&1' ;
2021-01-26 17:35:17 +00:00
#$command ='ffmpeg -ss '. $Frame->Delta() .' -i '.$Event->Path().'/'.$Event->DefaultVideo().' -vf "select=gte(n\\,'.$Frame->FrameId().'),setpts=PTS-STARTPTS" '.$path;
#$command ='ffmpeg -v 0 -i '.$Storage->Path().'/'.$Event->Path().'/'.$Event->DefaultVideo().' -vf "select=gte(n\\,'.$Frame->FrameId().'),setpts=PTS-STARTPTS" '.$path;
ZM\Debug ( " Running $command " );
$output = array ();
$retval = 0 ;
exec ( $command , $output , $retval );
ZM\Debug ( " Command: $command , retval: $retval , output: " . implode ( " \n " , $output ));
if ( ! file_exists ( $path ) ) {
header ( 'HTTP/1.0 404 Not Found' );
2022-02-08 23:11:33 +00:00
ZM\Error ( 'Can\'t create frame images from video for this event ' . $Event -> DefaultVideo () . '
2021-01-26 17:35:17 +00:00
Command was : '.$command.'
Output was : ' . implode ( PHP_EOL , $output ) );
2022-02-08 23:11:33 +00:00
return ;
2021-01-26 17:35:17 +00:00
}
# Generating an image file will use up more disk space, so update the Event record.
2021-03-15 19:02:43 +00:00
if ( $Event -> EndDateTime () ) {
$Event -> DiskSpace ( null );
}
2021-01-26 17:35:17 +00:00
} else {
header ( 'HTTP/1.0 404 Not Found' );
2022-02-08 23:11:33 +00:00
ZM\Error ( 'No snapshot jpg found for event ' . $_REQUEST [ 'eid' ]);
2021-01-26 17:35:17 +00:00
return ;
}
2019-10-30 20:57:45 +00:00
} # end if stored jpgs
2018-06-06 15:41:28 +00:00
} else {
2019-10-30 20:57:45 +00:00
$Frame = new ZM\Frame ();
$Frame -> Delta ( 1 );
$Frame -> FrameId ( 'snapshot' );
} # end if found snapshot.jpg
2017-11-02 13:00:01 +00:00
} else {
2019-02-22 14:19:07 +00:00
$Frame = ZM\Frame :: find_one ( array ( 'EventId' => $_REQUEST [ 'eid' ], 'FrameId' => $_REQUEST [ 'fid' ]));
2021-05-03 19:20:11 +00:00
if ( ! $Frame ) {
2018-05-11 14:37:00 +00:00
$previousBulkFrame = dbFetchOne (
'SELECT * FROM Frames WHERE EventId=? AND FrameId < ? ORDER BY FrameID DESC LIMIT 1' ,
NULL , array ( $_REQUEST [ 'eid' ], $_REQUEST [ 'fid' ])
);
$nextBulkFrame = dbFetchOne (
'SELECT * FROM Frames WHERE EventId=? AND FrameId > ? ORDER BY FrameID ASC LIMIT 1' ,
NULL , array ( $_REQUEST [ 'eid' ], $_REQUEST [ 'fid' ])
);
2021-05-03 19:20:11 +00:00
if ( $previousBulkFrame and $nextBulkFrame ) {
2019-02-22 14:19:07 +00:00
$Frame = new ZM\Frame ( $previousBulkFrame );
2018-05-11 14:37:00 +00:00
$Frame -> FrameId ( $_REQUEST [ 'fid' ]);
2017-10-10 19:11:59 +00:00
2018-05-11 14:37:00 +00:00
$percentage = ( $Frame -> FrameId () - $previousBulkFrame [ 'FrameId' ]) / ( $nextBulkFrame [ 'FrameId' ] - $previousBulkFrame [ 'FrameId' ]);
2017-10-17 18:53:34 +00:00
2018-05-11 14:37:00 +00:00
$Frame -> Delta ( $previousBulkFrame [ 'Delta' ] + floor ( 100 * ( $nextBulkFrame [ 'Delta' ] - $previousBulkFrame [ 'Delta' ] ) * $percentage ) / 100 );
2021-05-03 19:20:11 +00:00
ZM\Debug ( 'Got virtual frame from Bulk Frames previous delta: ' . $previousBulkFrame [ 'Delta' ] . ' + nextdelta:' . $nextBulkFrame [ 'Delta' ] . ' - ' . $previousBulkFrame [ 'Delta' ] . ' * ' . $percentage );
2023-06-09 14:40:34 +00:00
} else if ( $previousBulkFrame ) {
2022-11-21 16:23:08 +00:00
//If no next Frame we have to pull data from the Event itself
$Frame = new ZM\Frame ( $previousBulkFrame );
$Frame -> FrameId ( $_REQUEST [ 'fid' ]);
$percentage = ( $Frame -> FrameId () / $Event -> Frames ());
$Frame -> Delta ( floor ( $Event -> Length () * $percentage ));
2023-06-09 14:40:34 +00:00
} else {
2022-11-18 21:27:52 +00:00
header ( 'HTTP/1.0 404 Not Found' );
2022-02-08 23:11:33 +00:00
ZM\Error ( 'No Frame found for event(' . $_REQUEST [ 'eid' ] . ') and frame id(' . $_REQUEST [ 'fid' ] . ')' );
return ;
2018-04-20 18:25:54 +00:00
}
2021-05-03 19:20:11 +00:00
} # end if !Frame
2018-05-11 14:37:00 +00:00
// Frame can be non-existent. We have Bulk frames. So now we should try to load the bulk frame
2017-10-10 19:11:59 +00:00
$path = $Event -> Path () . '/' . sprintf ( '%0' . ZM_EVENT_IMAGE_DIGITS . 'd' , $Frame -> FrameId ()) . '-' . $show . '.jpg' ;
2021-05-03 19:20:11 +00:00
} # if special frame (snapshot, alarm etc) or identified by id
2018-05-11 14:37:00 +00:00
2016-05-12 15:55:48 +00:00
} else {
2018-05-11 14:37:00 +00:00
# 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 #
2019-02-22 14:19:07 +00:00
$Frame = ZM\Frame :: find_one ( array ( 'Id' => $_REQUEST [ 'fid' ]));
2018-08-31 15:57:47 +00:00
if ( ! $Frame ) {
2018-05-11 14:37:00 +00:00
header ( 'HTTP/1.0 404 Not Found' );
2022-02-08 23:11:33 +00:00
ZM\Error ( 'Frame ' . $_REQUEST [ 'fid' ] . ' Not Found' );
2018-05-11 14:37:00 +00:00
return ;
}
2016-05-05 18:49:40 +00:00
2019-02-22 14:19:07 +00:00
$Event = ZM\Event :: find_one ( array ( 'Id' => $Frame -> EventId ()));
2018-08-31 15:57:47 +00:00
if ( ! $Event ) {
2018-05-11 14:37:00 +00:00
header ( 'HTTP/1.0 404 Not Found' );
2022-02-08 23:11:33 +00:00
ZM\Error ( 'Event ' . $Frame -> EventId () . ' Not Found' );
2018-05-11 14:37:00 +00:00
return ;
}
$path = $Event -> Path () . '/' . sprintf ( '%0' . ZM_EVENT_IMAGE_DIGITS . 'd' , $Frame -> FrameId ()) . '-' . $show . '.jpg' ;
} # end if have eid
2018-08-31 15:57:47 +00:00
if ( ! file_exists ( $path ) ) {
2020-10-14 14:39:25 +00:00
ZM\Debug ( " $path does not exist " );
2018-05-11 13:53:24 +00:00
# Generate the frame JPG
2018-08-31 15:57:47 +00:00
if ( ( $show == 'capture' ) and $Event -> DefaultVideo () ) {
2023-04-22 14:30:25 +00:00
$file_path = $Event -> Path () . '/' . $Event -> DefaultVideo ();
if ( ! file_exists ( $file_path )) {
if ( $file = find_video ( $Event -> Path ())) {
$file_path = $Event -> Path () . '/' . $file ;
}
}
if ( ! file_exists ( $file_path )) {
2018-03-28 20:01:58 +00:00
header ( 'HTTP/1.0 404 Not Found' );
2022-02-08 23:11:33 +00:00
ZM\Error ( " Can't create frame images from video because there is no video file for this event at ( " . $Event -> Path () . '/' . $Event -> DefaultVideo () );
return ;
2018-03-28 20:01:58 +00:00
}
2023-04-22 14:30:25 +00:00
$command = ZM_PATH_FFMPEG . ' -ss ' . $Frame -> Delta () . ' -i ' . $file_path . ' -frames:v 1 ' . $path . ' 2>&1' ;
2017-04-12 20:17:19 +00:00
#$command ='ffmpeg -ss '. $Frame->Delta() .' -i '.$Event->Path().'/'.$Event->DefaultVideo().' -vf "select=gte(n\\,'.$Frame->FrameId().'),setpts=PTS-STARTPTS" '.$path;
2016-05-16 16:23:19 +00:00
#$command ='ffmpeg -v 0 -i '.$Storage->Path().'/'.$Event->Path().'/'.$Event->DefaultVideo().' -vf "select=gte(n\\,'.$Frame->FrameId().'),setpts=PTS-STARTPTS" '.$path;
2020-10-14 14:39:25 +00:00
ZM\Debug ( " Running $command " );
2016-05-16 16:23:19 +00:00
$output = array ();
$retval = 0 ;
2020-09-28 20:13:06 +00:00
exec ( $command , $output , $retval );
2020-10-14 14:39:25 +00:00
ZM\Debug ( " Command: $command , retval: $retval , output: " . implode ( " \n " , $output ));
2020-09-28 20:13:06 +00:00
if ( ! file_exists ( $path ) ) {
2017-05-18 16:49:59 +00:00
header ( 'HTTP/1.0 404 Not Found' );
2022-02-08 23:11:33 +00:00
ZM\Error ( 'Can\'t create frame images from video for this event ' . $Event -> DefaultVideo () . '
2020-09-28 20:13:06 +00:00
Command was : '.$command.'
Output was : ' . implode ( PHP_EOL , $output ) );
2022-02-08 23:11:33 +00:00
return ;
2016-09-20 19:55:28 +00:00
}
2018-05-11 14:37:00 +00:00
# Generating an image file will use up more disk space, so update the Event record.
2021-03-15 19:02:43 +00:00
if ( $Event -> EndDateTime () ) {
$Event -> DiskSpace ( null );
}
2016-05-16 16:23:19 +00:00
} else {
2017-05-18 16:49:59 +00:00
header ( 'HTTP/1.0 404 Not Found' );
2022-02-08 23:11:33 +00:00
ZM\Error ( " Can't create frame $show images from video because there is no video file for this event at " .
2018-05-11 14:37:00 +00:00
$Event -> Path () . '/' . $Event -> DefaultVideo () );
2022-02-08 23:11:33 +00:00
return ;
2010-02-23 09:12:12 +00:00
}
2018-05-11 14:37:00 +00:00
} # end if ! file_exists($path)
2010-02-23 09:12:12 +00:00
}
2019-02-08 18:49:00 +00:00
# we now load the actual image to send
2018-08-31 15:57:47 +00:00
$scale = 0 ;
2018-05-11 14:37:00 +00:00
if ( ! empty ( $_REQUEST [ 'scale' ]) ) {
if ( is_numeric ( $_REQUEST [ 'scale' ]) ) {
2016-05-16 16:23:19 +00:00
$x = $_REQUEST [ 'scale' ];
2018-05-11 14:37:00 +00:00
if ( $x >= 1 and $x <= 400 )
2018-08-31 15:57:47 +00:00
$scale = $x ;
2016-05-16 16:23:19 +00:00
}
2016-05-02 17:36:19 +00:00
}
2010-02-23 09:12:12 +00:00
2018-08-31 15:57:47 +00:00
$width = 0 ;
2016-05-16 16:23:19 +00:00
if ( ! empty ( $_REQUEST [ 'width' ]) ) {
2018-05-11 14:37:00 +00:00
if ( is_numeric ( $_REQUEST [ 'width' ]) ) {
2016-05-16 16:23:19 +00:00
$x = $_REQUEST [ 'width' ];
2018-05-11 14:37:00 +00:00
if ( $x >= 10 and $x <= 8000 )
2018-08-31 15:57:47 +00:00
$width = $x ;
2016-05-16 16:23:19 +00:00
}
2016-05-02 17:36:19 +00:00
}
2018-10-22 20:13:12 +00:00
2018-08-31 15:57:47 +00:00
$height = 0 ;
2018-05-11 14:37:00 +00:00
if ( ! empty ( $_REQUEST [ 'height' ]) ) {
if ( is_numeric ( $_REQUEST [ 'height' ]) ) {
2016-05-16 16:23:19 +00:00
$x = $_REQUEST [ 'height' ];
2018-05-11 14:37:00 +00:00
if ( $x >= 10 and $x <= 8000 )
2018-08-31 15:57:47 +00:00
$height = $x ;
2016-05-16 16:23:19 +00:00
}
2016-05-02 17:36:19 +00:00
}
2015-08-09 16:02:13 +00:00
2016-05-02 17:36:19 +00:00
if ( $errorText ) {
2019-02-22 14:19:07 +00:00
ZM\Error ( $errorText );
2016-05-02 17:36:19 +00:00
} else {
2023-07-13 21:20:18 +00:00
# Must lock it because zmc may be still writing the jpg and will have a lock on it.
$fp_path = fopen ( $path , 'r' );
$lock = flock ( $fp_path , LOCK_SH );
2023-08-02 17:36:26 +00:00
if ( ! $lock ) ZM\Warning ( " Unable to get a read lock on $path , continuing. " );
2023-07-13 21:20:18 +00:00
2021-08-05 17:30:40 +00:00
header ( 'Content-type: ' . $media_type );
2023-02-13 21:15:12 +00:00
header ( 'Cache-Control: max-age=86400' );
header ( 'Expires: ' . gmdate ( 'D, d M Y H:i:s \G\M\T' , time () + ( 60 * 60 ))); // Default set to 1 hour
header ( 'Pragma: cache' );
2023-08-02 17:36:26 +00:00
if (( $scale == 0 || $scale == 100 ) && ( $width == 0 ) && ( $height == 0 )) {
2018-08-31 15:57:47 +00:00
# This is so that Save Image As give a useful filename
2023-08-02 17:36:26 +00:00
if ( $Event ) {
2018-08-31 15:57:47 +00:00
$filename = $Event -> MonitorId () . '_' . $Event -> Id () . '_' . $Frame -> FrameId () . '.jpg' ;
header ( 'Content-Disposition: inline; filename="' . $filename . '"' );
}
2023-08-02 17:36:26 +00:00
if ( ! readfile ( $path )) {
2019-02-22 14:19:07 +00:00
ZM\Error ( 'No bytes read from ' . $path );
2010-02-23 09:12:12 +00:00
}
2016-05-02 17:36:19 +00:00
} else {
2020-10-14 14:39:25 +00:00
ZM\Debug ( " Doing a scaled image: scale( $scale ) width( $width ) height( $height ) " );
2021-08-05 17:30:40 +00:00
$i = null ;
2016-05-16 16:23:19 +00:00
if ( ! ( $width && $height ) ) {
2018-05-11 14:37:00 +00:00
$i = imagecreatefromjpeg ( $path );
$oldWidth = imagesx ( $i );
$oldHeight = imagesy ( $i );
2016-05-16 16:23:19 +00:00
if ( $width == 0 && $height == 0 ) { // scale has to be set to get here with both zero
2022-09-21 17:23:16 +00:00
$width = intval ( $oldWidth * $scale / 100.0 );
$height = intval ( $oldHeight * $scale / 100.0 );
2016-05-16 16:23:19 +00:00
} elseif ( $width == 0 && $height != 0 ) {
2022-09-21 17:23:16 +00:00
$width = intval (( $height * $oldWidth ) / $oldHeight );
2016-05-16 16:23:19 +00:00
} elseif ( $width != 0 && $height == 0 ) {
2022-09-21 17:23:16 +00:00
$height = intval (( $width * $oldHeight ) / $oldWidth );
2020-10-14 14:39:25 +00:00
ZM\Debug ( " Figuring out height using width: $height = ( $width * $oldHeight ) / $oldWidth " );
2016-05-16 16:23:19 +00:00
}
2018-08-31 15:57:47 +00:00
if ( $width == $oldWidth && $height == $oldHeight ) {
2019-02-22 14:19:07 +00:00
ZM\Warning ( 'No change to width despite scaling.' );
2016-05-16 16:23:19 +00:00
}
}
# Slight optimisation, thumbnails always specify width and height, so we can cache them.
2018-08-31 15:57:47 +00:00
$scaled_path = preg_replace ( '/\.jpg$/' , " - ${ width}x${height } .jpg " , $path );
2021-08-05 17:30:40 +00:00
if ( $Event ) {
2018-08-31 15:57:47 +00:00
$filename = $Event -> MonitorId () . '_' . $Event -> Id () . '_' . $Frame -> FrameId () . " - ${ width}x${height } .jpg " ;
header ( 'Content-Disposition: inline; filename="' . $filename . '"' );
}
2023-07-13 21:20:18 +00:00
if ( ! file_exists ( $scaled_path )) {
ZM\Debug ( " Cached scaled image does not exist at $scaled_path . Creating it " );
if ( ! $i ) {
2018-05-11 14:37:00 +00:00
$i = imagecreatefromjpeg ( $path );
2023-07-13 21:20:18 +00:00
}
if ( ! $i ) {
2021-08-05 17:30:40 +00:00
ZM\Error ( 'Unable to load jpeg from ' . $scaled_path );
$i = imagecreatetruecolor ( $width , $height );
$bg_colour = imagecolorallocate ( $i , 255 , 255 , 255 );
$fg_colour = imagecolorallocate ( $i , 0 , 0 , 0 );
imagefilledrectangle ( $im , 0 , 0 , $width , $height , $bg_colour );
imagestring ( $i , 1 , 5 , 5 , 'Unable to load jpeg from ' . $scaled_path , $fg_colour );
imagejpeg ( $i );
} else {
ZM\Debug ( " Have image scaling to $width x $height " );
ob_start ();
$iScale = imagescale ( $i , $width , $height );
imagejpeg ( $iScale );
imagedestroy ( $i );
imagedestroy ( $iScale );
$scaled_jpeg_data = ob_get_contents ();
2023-07-13 21:20:18 +00:00
file_put_contents ( $scaled_path , $scaled_jpeg_data , LOCK_EX );
2021-08-05 17:30:40 +00:00
echo $scaled_jpeg_data ;
}
2018-08-31 15:57:47 +00:00
} else {
2023-07-13 21:20:18 +00:00
$fp_scaled_path = fopen ( $scaled_path , 'r' );
$lock = flock ( $fp_scaled_path , LOCK_SH );
if ( ! $lock ) Warning ( " Unable to get a read lock on $scaled_path , trying to send anyways. " );
2020-10-14 14:39:25 +00:00
ZM\Debug ( " Sending $scaled_path " );
2018-08-31 15:57:47 +00:00
$bytes = readfile ( $scaled_path );
if ( ! $bytes ) {
2019-02-22 14:19:07 +00:00
ZM\Error ( 'No bytes read from ' . $scaled_path );
2018-08-31 15:57:47 +00:00
} else {
2020-10-14 14:39:25 +00:00
ZM\Debug ( " $bytes sent " );
2018-08-31 15:57:47 +00:00
}
2023-07-13 21:20:18 +00:00
flock ( $fp_scaled_path , LOCK_UN );
fclose ( $fp_scaled_path );
} # end if scaled image doesn't exist or failed sending it
} # end if scaled or not
flock ( $fp_path , LOCK_UN );
fclose ( $fp_path );
2016-05-02 17:36:19 +00:00
}
2023-04-22 14:30:25 +00:00
function find_video ( $path ) {
# Look for other mp4s
2023-08-30 18:38:43 +00:00
if ( file_exists ( $path )) {
$files = scandir ( $path );
foreach ( $files as $file ) {
if ( preg_match ( '/.mp4$/i' , $file )) {
return $file ;
}
2023-04-22 14:30:25 +00:00
}
}
}
2018-08-31 15:57:47 +00:00
exit ();