zoneminder/web/skins/classic/views/timeline.php

872 lines
31 KiB
PHP
Raw Normal View History

<?php
//
// ZoneMinder web timeline view file, $Date$, $Revision$
//
// 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
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
if ( !canView('Events') ) {
2017-05-19 02:04:24 +08:00
$view = 'error';
return;
}
foreach ( getSkinIncludes('includes/timeline_functions.php') as $includeFile )
2017-05-19 02:04:24 +08:00
require_once $includeFile;
//
// Date/time formats used in charts
//
// These are the time axis range text. The first of each pair is the start date/time
// and the second is the last so often contains additional information
//
// When the chart range is years
define( 'STRF_TL_AXIS_RANGE_YEAR1', '%b %Y' );
define( 'STRF_TL_AXIS_RANGE_YEAR2', STRF_TL_AXIS_RANGE_YEAR1 );
// When the chart range is months
2017-07-14 05:34:45 +08:00
define( 'STRF_TL_AXIS_RANGE_MONTH1', '%b' );
define( 'STRF_TL_AXIS_RANGE_MONTH2', STRF_TL_AXIS_RANGE_MONTH1.' %Y' );
// When the chart range is days
2017-07-14 05:34:45 +08:00
define( 'STRF_TL_AXIS_RANGE_DAY1', '%d' );
define( 'STRF_TL_AXIS_RANGE_DAY2', STRF_TL_AXIS_RANGE_DAY1.' %b %Y' );
// When the chart range is less than a day
2017-07-14 05:34:45 +08:00
define( 'STRF_TL_AXIS_RANGE_TIME1', '%H:%M' );
define( 'STRF_TL_AXIS_RANGE_TIME2', STRF_TL_AXIS_RANGE_TIME1.', %d %b %Y' );
//
// These are the time axis tick labels
//
define( "STRF_TL_AXIS_LABEL_YEAR", "%Y" );
define( "STRF_TL_AXIS_LABEL_MONTH", "%M" );
define( "STRF_TL_AXIS_LABEL_WEEK", "%d/%m" );
define( "STRF_TL_AXIS_LABEL_DAY", "%d" );
define( "STRF_TL_AXIS_LABEL_4HOUR", "%H:00" );
define( "STRF_TL_AXIS_LABEL_HOUR", "%H:00" );
define( "STRF_TL_AXIS_LABEL_10MINUTE", "%H:%M" );
define( "STRF_TL_AXIS_LABEL_MINUTE", "%H:%M" );
define( "STRF_TL_AXIS_LABEL_10SECOND", "%S" );
define( "STRF_TL_AXIS_LABEL_SECOND", "%S" );
$mouseover = true;
if ( !isset($mouseover) )
2017-05-19 02:04:24 +08:00
$mouseover = true;
2017-05-19 02:04:24 +08:00
$mode = 'overlay';
if ( !isset($mode) )
2017-05-19 02:04:24 +08:00
$mode = 'overlay';
$minEventWidth = 3;
$maxEventWidth = 6;
$chart = array(
2017-07-14 05:34:45 +08:00
'width'=>700,
'height'=>460,
'image' => array(
'width'=>264,
'height'=>220,
'topOffset'=>20,
),
2017-07-14 05:34:45 +08:00
'imageText' => array(
'width'=>400,
'height'=>30,
'topOffset'=>20,
),
2017-07-14 05:34:45 +08:00
'graph' => array(
'width'=>600,
'height'=>160,
'topOffset'=>30,
),
2017-07-14 05:34:45 +08:00
'title' => array(
'topOffset'=>50
),
2017-07-14 05:34:45 +08:00
'key' => array(
'topOffset'=>50
),
2017-07-14 05:34:45 +08:00
'axes' => array(
'x' => array(
'height' => 20,
),
2017-07-14 05:34:45 +08:00
'y' => array(
'width' => 30,
),
),
2017-07-14 05:34:45 +08:00
'grid' => array(
'x' => array(
'major' => array(
'max' => 12,
'min' => 4,
),
2017-07-14 05:34:45 +08:00
'minor' => array(
'max' => 48,
'min' => 12,
),
),
2017-07-14 05:34:45 +08:00
'y' => array(
'major' => array(
'max' => 8,
'min' => 1,
),
2017-07-14 05:34:45 +08:00
'minor' => array(
'max' => 0,
'min' => 0,
),
),
),
);
$monitors = array();
2017-05-19 02:04:24 +08:00
$monitorsSql = 'SELECT * FROM Monitors ORDER BY Sequence ASC';
//srand( 97981 );
2017-05-19 02:04:24 +08:00
foreach( dbFetchAll( $monitorsSql ) as $row ) {
//if ( empty($row['WebColour']) )
//{
//$row['WebColour'] = sprintf( "#%02x%02x%02x", rand( 0, 255 ), rand( 0, 255), rand( 0, 255 ) );
//}
$monitors[$row['Id']] = $row;
}
# The as E, and joining with Monitors is required for the filterSQL filters.
$rangeSql = 'SELECT min(E.StartTime) AS MinTime, max(E.EndTime) AS MaxTime FROM Events AS E INNER JOIN Monitors AS M ON (E.MonitorId = M.Id) WHERE NOT isnull(E.StartTime) AND NOT isnull(E.EndTime)';
$eventsSql = 'SELECT E.* FROM Events AS E INNER JOIN Monitors AS M ON (E.MonitorId = M.Id) WHERE NOT isnull(StartTime)';
2018-04-21 01:10:38 +08:00
$eventsValues = array();
2017-05-19 02:04:24 +08:00
if ( !empty($user['MonitorIds']) ) {
$monFilterSql = ' AND MonitorId IN ('.$user['MonitorIds'].')';
2017-05-19 02:04:24 +08:00
$rangeSql .= $monFilterSql;
$eventsSql .= $monFilterSql;
}
if ( isset($_REQUEST['filter']) )
2018-04-21 01:10:38 +08:00
$tree = parseFilterToTree($_REQUEST['filter']['Query']);
else
2017-05-19 02:04:24 +08:00
$tree = false;
if ( isset($_REQUEST['range']) )
$range = validHtmlStr($_REQUEST['range']);
if ( isset($_REQUEST['minTime']) )
$minTime = validHtmlStr($_REQUEST['minTime']);
if ( isset($_REQUEST['midTime']) )
$midTime = validHtmlStr($_REQUEST['midTime']);
if ( isset($_REQUEST['maxTime']) )
$maxTime = validHtmlStr($_REQUEST['maxTime']);
2017-05-19 02:04:24 +08:00
if ( isset($range) ) {
$halfRange = (int)($range/2);
if ( isset($midTime) ) {
$midTimeT = strtotime($midTime);
$minTimeT = $midTimeT-$halfRange;
$maxTimeT = $midTimeT+$halfRange;
if ( !($range%1) ) {
$maxTimeT--;
}
$minTime = strftime(STRF_FMT_DATETIME_DB, $minTimeT);
$maxTime = strftime(STRF_FMT_DATETIME_DB, $maxTimeT);
} elseif ( isset($minTime) ) {
$minTimeT = strtotime($minTime);
$maxTimeT = $minTimeT + $range;
$midTimeT = $minTimeT + $halfRange;
$midTime = strftime(STRF_FMT_DATETIME_DB, $midTimeT);
$maxTime = strftime(STRF_FMT_DATETIME_DB, $maxTimeT);
} elseif ( isset($maxTime) ) {
$maxTimeT = strtotime($maxTime);
$minTimeT = $maxTimeT - $range;
$midTimeT = $minTimeT + $halfRange;
$minTime = strftime(STRF_FMT_DATETIME_DB, $minTimeT);
$midTime = strftime(STRF_FMT_DATETIME_DB, $midTimeT);
}
} elseif ( isset($minTime) && isset($maxTime) ) {
$minTimeT = strtotime($minTime);
$maxTimeT = strtotime($maxTime);
$range = ($maxTimeT - $minTimeT) + 1;
$halfRange = (int)($range/2);
$midTimeT = $minTimeT + $halfRange;
$midTime = strftime(STRF_FMT_DATETIME_DB, $midTimeT);
}
2017-05-19 02:04:24 +08:00
if ( isset($minTime) && isset($maxTime) ) {
$tempMinTime = $tempMaxTime = $tempExpandable = false;
2018-04-21 01:10:38 +08:00
extractDatetimeRange($tree, $tempMinTime, $tempMaxTime, $tempExpandable);
$filterSql = parseTreeToSQL($tree);
if ( $filterSql ) {
2018-04-21 01:10:38 +08:00
$filterSql = " AND $filterSql";
$eventsSql .= $filterSql;
}
2017-05-19 02:04:24 +08:00
} else {
2018-04-21 01:10:38 +08:00
$filterSql = parseTreeToSQL($tree);
$tempMinTime = $tempMaxTime = $tempExpandable = false;
2018-04-21 01:10:38 +08:00
extractDatetimeRange($tree, $tempMinTime, $tempMaxTime, $tempExpandable);
if ( $filterSql ) {
2018-04-21 01:10:38 +08:00
$filterSql = " AND $filterSql";
$rangeSql .= $filterSql;
$eventsSql .= $filterSql;
}
if ( !isset($minTime) || !isset($maxTime) ) {
// Dynamically determine range
2018-04-21 01:10:38 +08:00
$row = dbFetchOne($rangeSql);
if ( !isset($minTime) )
$minTime = $row['MinTime'];
if ( !isset($maxTime) )
$maxTime = $row['MaxTime'];
}
if ( empty($minTime) )
$minTime = $tempMinTime;
if ( empty($maxTime) )
$maxTime = $tempMaxTime;
if ( empty($maxTime) )
2018-04-21 01:10:38 +08:00
$maxTime = 'now';
$minTimeT = strtotime($minTime);
$maxTimeT = strtotime($maxTime);
$range = ($maxTimeT - $minTimeT) + 1;
$halfRange = (int)($range/2);
$midTimeT = $minTimeT + $halfRange;
$midTime = strftime( STRF_FMT_DATETIME_DB, $midTimeT );
}
//echo "MnT: $tempMinTime, MxT: $tempMaxTime, ExP: $tempExpandable<br>";
2017-05-19 02:04:24 +08:00
if ( $tree ) {
appendDatetimeRange($tree, $minTime, $maxTime);
$filterQuery = parseTreeToQuery($tree);
2017-05-19 02:04:24 +08:00
} else {
$filterQuery = false;
}
$scales = array(
2018-04-21 01:10:38 +08:00
array( 'name'=>'year', 'factor'=>60*60*24*365, 'align'=>1, 'zoomout'=>2, 'label'=>STRF_TL_AXIS_LABEL_YEAR ),
array( 'name'=>'month', 'factor'=>60*60*24*30, 'align'=>1, 'zoomout'=>12, 'label'=>STRF_TL_AXIS_LABEL_MONTH ),
array( 'name'=>'week', 'factor'=>60*60*24*7, 'align'=>1, 'zoomout'=>4.25, 'label'=>STRF_TL_AXIS_LABEL_WEEK, 'labelCheck'=>"%W" ),
array( 'name'=>'day', 'factor'=>60*60*24, 'align'=>1, 'zoomout'=>7, 'label'=>STRF_TL_AXIS_LABEL_DAY ),
2017-07-14 05:34:45 +08:00
array( 'name'=>"hour4", 'factor'=>60*60, 'align'=>4, 'zoomout'=>6, 'label'=>STRF_TL_AXIS_LABEL_4HOUR, 'labelCheck'=>"%H" ),
2018-04-21 01:10:38 +08:00
array( 'name'=>'hour', 'factor'=>60*60, 'align'=>1, 'zoomout'=>4, 'label'=>STRF_TL_AXIS_LABEL_HOUR, 'labelCheck'=>"%H" ),
2017-07-14 05:34:45 +08:00
array( 'name'=>"minute10", 'factor'=>60, 'align'=>10, 'zoomout'=>6, 'label'=>STRF_TL_AXIS_LABEL_10MINUTE, 'labelCheck'=>"%M" ),
2018-04-21 01:10:38 +08:00
array( 'name'=>'minute', 'factor'=>60, 'align'=>1, 'zoomout'=>10, 'label'=>STRF_TL_AXIS_LABEL_MINUTE, 'labelCheck'=>"%M" ),
2017-07-14 05:34:45 +08:00
array( 'name'=>"second10", 'factor'=>1, 'align'=>10, 'zoomout'=>6, 'label'=>STRF_TL_AXIS_LABEL_10SECOND ),
2018-04-21 01:10:38 +08:00
array( 'name'=>'second', 'factor'=>1, 'align'=>1, 'zoomout'=>10, 'label'=>STRF_TL_AXIS_LABEL_SECOND ),
);
$majXScale = getDateScale($scales, $range, $chart['grid']['x']['major']['min'], $chart['grid']['x']['major']['max']);
// Adjust the range etc for scale
$minTimeT -= $minTimeT%($majXScale['factor']*$majXScale['align']);
$minTime = strftime(STRF_FMT_DATETIME_DB, $minTimeT);
$maxTimeT += (($majXScale['factor']*$majXScale['align'])-$maxTimeT%($majXScale['factor']*$majXScale['align']))-1;
if ( $maxTimeT > time() )
$maxTimeT = time();
$maxTime = strftime(STRF_FMT_DATETIME_DB, $maxTimeT);
$range = ($maxTimeT - $minTimeT) + 1;
$halfRange = (int)($range/2);
$midTimeT = $minTimeT + $halfRange;
$midTime = strftime(STRF_FMT_DATETIME_DB, $midTimeT);
//echo "R:$range<br>";
//echo "MnT:$minTime<br>";
//echo "MnTt:$minTimeT<br>";
//echo "MdT:$midTime<br>";
//echo "MdTt:$midTimeT<br>";
//echo "MxT:$maxTime<br>";
//echo "MxTt:$maxTimeT<br>";
2017-05-19 02:04:24 +08:00
if ( isset($minTime) && isset($maxTime) ) {
2018-04-21 01:10:38 +08:00
$eventsSql .= " AND EndTime >= '$minTime' AND StartTime <= '$maxTime'";
}
2018-04-21 01:10:38 +08:00
$eventsSql .= ' ORDER BY E.Id ASC';
//echo "ESQL: $eventsSql<br>";
$chart['data'] = array(
2017-07-14 05:34:45 +08:00
'x' => array(
'lo' => strtotime($minTime),
'hi' => strtotime($maxTime),
),
2017-07-14 05:34:45 +08:00
'y' => array(
'lo' => 0,
'hi' => 0,
)
);
$chart['data']['x']['range'] = ($chart['data']['x']['hi'] - $chart['data']['x']['lo']) + 1;
$chart['data']['x']['density'] = $chart['data']['x']['range']/$chart['graph']['width'];
$monEventSlots = array();
$monFrameSlots = array();
$monitorIds = array();
2017-07-14 05:34:45 +08:00
$events_result = dbQuery( $eventsSql );
if ( ! $events_result ) {
Fatal( "SQL-ERR");
return;
}
$first_event = $event = $events_result->fetch(PDO::FETCH_ASSOC);
2017-07-14 05:34:45 +08:00
if ( $event ) {
do {
if ( !isset($monitorIds[$event['MonitorId']]) ) {
2017-07-14 05:34:45 +08:00
$monitorIds[$event['MonitorId']] = true;
$monEventSlots[$event['MonitorId']] = array();
$monFrameSlots[$event['MonitorId']] = array();
}
2017-07-14 05:34:45 +08:00
$currEventSlots = &$monEventSlots[$event['MonitorId']];
$currFrameSlots = &$monFrameSlots[$event['MonitorId']];
2017-07-14 05:34:45 +08:00
$startTimeT = strtotime($event['StartTime']);
$startIndex = $rawStartIndex = (int)(($startTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']);
if ( $startIndex < 0 )
$startIndex = 0;
2017-07-14 05:34:45 +08:00
if ( isset($event['EndTime']) )
$endTimeT = strtotime($event['EndTime']);
else
$endTimeT = time();
$endIndex = $rawEndIndex = (int)(($endTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']);
2017-07-14 05:34:45 +08:00
if ( $endIndex >= $chart['graph']['width'] )
$endIndex = $chart['graph']['width'] - 1;
2017-07-14 05:34:45 +08:00
for ( $i = $startIndex; $i <= $endIndex; $i++ ) {
if ( !isset($currEventSlots[$i]) ) {
if ( $rawStartIndex == $rawEndIndex ) {
$offset = 1;
} else {
$offset = 1 + ($event['Frames']?((int)(($event['Frames']-1)*(($i-$rawStartIndex)/($rawEndIndex-$rawStartIndex)))):0);
}
$currEventSlots[$i] = array( 'count'=>0, 'width'=>1, 'offset'=>$offset, 'event'=>$event );
} else {
2017-07-14 05:34:45 +08:00
$currEventSlots[$i]['count']++;
}
}
2017-10-17 23:50:47 +08:00
2017-07-14 05:34:45 +08:00
if ( $event['MaxScore'] > 0 ) {
if ( $startIndex == $endIndex ) {
# Only fills 1 slot, so just get the max Score
2017-07-14 05:34:45 +08:00
$framesSql = 'SELECT FrameId,Score FROM Frames WHERE EventId = ? AND Score > 0 ORDER BY Score DESC LIMIT 1';
2018-04-21 01:10:38 +08:00
$frame = dbFetchOne($framesSql, NULL, array($event['Id']));
2017-07-14 05:34:45 +08:00
$i = $startIndex;
if ( !isset($currFrameSlots[$i]) ) {
2018-04-21 01:10:38 +08:00
$currFrameSlots[$i] = array('count'=>1, 'value'=>$event['MaxScore'], 'event'=>$event, 'frame'=>$frame);
2017-05-19 02:04:24 +08:00
} else {
2017-07-14 05:34:45 +08:00
$currFrameSlots[$i]['count']++;
if ( $event['MaxScore'] > $currFrameSlots[$i]['value'] ) {
$currFrameSlots[$i]['value'] = $event['MaxScore'];
$currFrameSlots[$i]['event'] = $event;
$currFrameSlots[$i]['frame'] = $frame;
}
}
2017-07-14 05:34:45 +08:00
if ( $event['MaxScore'] > $chart['data']['y']['hi'] ) {
$chart['data']['y']['hi'] = $event['MaxScore'];
}
2017-07-14 05:34:45 +08:00
} else {
# Fills multiple Slots, so need multiple scores to generate the graph over multiple slots.
2017-07-14 05:34:45 +08:00
$framesSql = 'SELECT FrameId,Delta,unix_timestamp(TimeStamp) AS TimeT,Score FROM Frames WHERE EventId = ? AND Score > 0';
2018-04-21 01:10:38 +08:00
$result = dbQuery($framesSql, array($event['Id']));
while( $frame = dbFetchNext($result) ) {
2017-07-14 05:34:45 +08:00
if ( $frame['Score'] == 0 )
continue;
$frameTimeT = $frame['TimeT'];
$frameTimeT = $startTimeT + $frame['Delta'];
$frameIndex = (int)(($frameTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']);
if ( $frameIndex < 0 )
continue;
if ( $frameIndex >= $chart['graph']['width'] )
continue;
if ( !isset($currFrameSlots[$frameIndex]) ) {
2018-04-21 01:10:38 +08:00
$currFrameSlots[$frameIndex] = array('count'=>1, 'value'=>$frame['Score'], 'event'=>$event, 'frame'=>$frame);
2017-07-14 05:34:45 +08:00
} else {
$currFrameSlots[$frameIndex]['count']++;
if ( $frame['Score'] > $currFrameSlots[$frameIndex]['value'] ) {
$currFrameSlots[$frameIndex]['value'] = $frame['Score'];
$currFrameSlots[$frameIndex]['event'] = $event;
$currFrameSlots[$frameIndex]['frame'] = $frame;
}
}
if ( $frame['Score'] > $chart['data']['y']['hi'] ) {
$chart['data']['y']['hi'] = $frame['Score'];
}
} // end foreach frame
}
} // end if MaxScore > 0
} while( $event = $events_result->fetch( PDO::FETCH_ASSOC ) );
} # end if have first event
ksort( $monitorIds, SORT_NUMERIC );
ksort( $monEventSlots, SORT_NUMERIC );
ksort( $monFrameSlots, SORT_NUMERIC );
// No longer needed?
2017-05-19 02:04:24 +08:00
if ( false ) {
// Add on missing frames
foreach( array_keys($monFrameSlots) as $monitorId ) {
unset( $currFrameSlots );
$currFrameSlots = &$monFrameSlots[$monitorId];
for ( $i = 0; $i < $chart['graph']['width']; $i++ ) {
if ( isset($currFrameSlots[$i]) ) {
if ( !isset($currFrameSlots[$i]['frame']) ) {
2018-04-21 01:10:38 +08:00
$framesSql = "SELECT FrameId,Score FROM Frames WHERE EventId = ? AND Score > 0 ORDER BY FrameId LIMIT 1";
$currFrameSlots[$i]['frame'] = dbFetchOne( $framesSql, NULL, array( $currFrameSlots[$i]['event']['Id'] ) );
}
}
}
}
}
$chart['data']['y']['range'] = ($chart['data']['y']['hi'] - $chart['data']['y']['lo']) + 1;
$chart['data']['y']['density'] = $chart['data']['y']['range']/$chart['graph']['height'];
$majYScale = getYScale(
$chart['data']['y']['range'],
$chart['grid']['y']['major']['min'],
$chart['grid']['y']['major']['max']);
$maxWidth = 0;
$maxHeight = 0;
2017-05-19 02:04:24 +08:00
foreach ( array_keys($monitorIds) as $monitorId ) {
if ( $maxWidth < $monitors[$monitorId]['Width'] )
$maxWidth = $monitors[$monitorId]['Width'];
if ( $maxHeight < $monitors[$monitorId]['Height'] )
$maxHeight = $monitors[$monitorId]['Height'];
}
// Optimise boxes
2017-05-19 02:04:24 +08:00
foreach( array_keys($monEventSlots) as $monitorId ) {
unset( $currEventSlots );
$currEventSlots = &$monEventSlots[$monitorId];
for ( $i = 0; $i < $chart['graph']['width']; $i++ ) {
if ( isset($currEventSlots[$i]) ) {
if ( isset($currSlot) ) {
if ( $currSlot['event']['Id'] == $currEventSlots[$i]['event']['Id'] ) {
if ( $currSlot['width'] < $maxEventWidth ) {
// Merge slots for the same long event
$currSlot['width']++;
unset( $currEventSlots[$i] );
continue;
} elseif ( $currSlot['offset'] < $currEventSlots[$i]['offset'] ) {
// Split very long events
$currEventSlots[$i]['frame'] = array( 'FrameId'=>$currEventSlots[$i]['offset'] );
}
} elseif ( $currSlot['width'] < $minEventWidth ) {
// Merge multiple small events
$currSlot['width']++;
unset( $currEventSlots[$i] );
continue;
}
}
$currSlot = &$currEventSlots[$i];
} else {
unset( $currSlot );
}
}
if ( isset($currSlot) )
unset($currSlot);
} // end foreach Event Monitors
//print_r( $monEventSlots );
// Stack events
$frameSlots = array();
$frameMonitorIds = array_keys($monFrameSlots);
2017-05-19 02:04:24 +08:00
for ( $i = 0; $i < $chart['graph']['width']; $i++ ) {
foreach ( $frameMonitorIds as $frameMonitorId ) {
2018-04-21 01:10:38 +08:00
unset($currFrameSlots);
$currFrameSlots = &$monFrameSlots[$frameMonitorId];
if ( isset($currFrameSlots[$i]) ) {
if ( !isset($frameSlots[$i]) ) {
$frameSlots[$i] = array();
$frameSlots[$i][] = &$currFrameSlots[$i];
} else {
$slotCount = count($frameSlots[$i]);
for ( $j = 0; $j < $slotCount; $j++ ) {
if ( $currFrameSlots[$i]['value'] > $frameSlots[$i][$j]['value'] ) {
for ( $k = $slotCount; $k > $j; $k-- ) {
$frameSlots[$i][$k] = $frameSlots[$i][$k-1];
}
$frameSlots[$i][$j] = &$currFrameSlots[$i];
break 2;
}
}
$frameSlots[$i][] = &$currFrameSlots[$i];
}
}
} # end foreach MonitorId
}
//print_r( $monEventSlots );
//print_r( $monFrameSlots );
//print_r( $chart );
$graphHeight = $chart['graph']['height'];
if ( $mode == 'overlay' ) {
$minEventBarHeight = 10;
$maxEventBarHeight = 40;
if ( count($monitorIds) ) {
$chart['graph']['eventBarHeight'] = $minEventBarHeight;
while ( ($chart['graph']['eventsHeight'] = (($chart['graph']['eventBarHeight'] * count($monitorIds)) + (count($monitorIds)-1))) < $maxEventBarHeight ) {
$chart['graph']['eventBarHeight']++;
}
} else {
$chart['graph']['eventBarHeight'] = $maxEventBarHeight;
$chart['graph']['eventsHeight'] = $maxEventBarHeight;
}
$chart['graph']['activityHeight'] = ($graphHeight - $chart['graph']['eventsHeight']);
$chart['data']['y']['density'] = $chart['data']['y']['range']/$chart['graph']['activityHeight'];
$chart['eventBars'] = array();
$top = $chart['graph']['activityHeight'];
foreach ( array_keys($monitorIds) as $monitorId ) {
$chart['eventBars'][$monitorId] = array( 'top' => $top );
$top += $chart['graph']['eventBarHeight']+1;
}
} else if ( $mode == 'split' ) {
$minActivityBarHeight = 30;
$minEventBarHeight = 10;
$maxEventBarHeight = 40;
if ( count($monitorIds) ) {
$chart['graph']['eventBarHeight'] = $minEventBarHeight;
$chart['graph']['activityBarHeight'] = $minActivityBarHeight;
while ( ((($chart['graph']['eventBarHeight']+$chart['graph']['activityBarHeight']) * count($monitorIds)) + ((2*count($monitorIds))-1)) < $graphHeight ) {
$chart['graph']['activityBarHeight']++;
if ( $chart['graph']['eventBarHeight'] < $maxEventBarHeight ) {
$chart['graph']['eventBarHeight']++;
}
}
} else {
$chart['graph']['eventBarHeight'] = $maxEventBarHeight;
$chart['graph']['activityBarHeight'] = $graphHeight - $chart['graph']['eventBarHeight'];
}
$chart['data']['y']['density'] = $chart['data']['y']['range']/$chart['graph']['activityBarHeight'];
$chart['activityBars'] = array();
$chart['eventBars'] = array();
$top = 0;
$barCount = 1;
foreach ( array_keys($monitorIds) as $monitorId ) {
$chart['eventBars'][$monitorId] = array( 'top' => $top );
$chart['eventBars'][$monitorId] = array( 'top' => $top+$chart['graph']['activityBarHeight']+1 );
$top += $chart['graph']['activityBarHeight']+1+$chart['graph']['eventBarHeight']+1;
}
}
preg_match( '/^(\d+)-(\d+)-(\d+) (\d+):(\d+)/', $minTime, $startMatches );
preg_match( '/^(\d+)-(\d+)-(\d+) (\d+):(\d+)/', $maxTime, $endMatches );
2017-05-19 02:04:24 +08:00
if ( $startMatches[1] != $endMatches[1] ) {
// Different years
$title = strftime( STRF_TL_AXIS_RANGE_YEAR1, $chart['data']['x']['lo'] )." - ".strftime( STRF_TL_AXIS_RANGE_YEAR2, $chart['data']['x']['hi'] );
2017-05-19 02:04:24 +08:00
} elseif ( $startMatches[2] != $endMatches[2] ) {
// Different months
$title = strftime( STRF_TL_AXIS_RANGE_MONTH1, $chart['data']['x']['lo'] )." - ".strftime( STRF_TL_AXIS_RANGE_MONTH2, $chart['data']['x']['hi'] );
2017-05-19 02:04:24 +08:00
} elseif ( $startMatches[3] != $endMatches[3] ) {
// Different dates
$title = strftime( STRF_TL_AXIS_RANGE_DAY1, $chart['data']['x']['lo'] )." - ".strftime( STRF_TL_AXIS_RANGE_DAY2, $chart['data']['x']['hi'] );
2017-05-19 02:04:24 +08:00
} else {
// Different times
$title = strftime( STRF_TL_AXIS_RANGE_TIME1, $chart['data']['x']['lo'] )." - ".strftime( STRF_TL_AXIS_RANGE_TIME2, $chart['data']['x']['hi'] );
}
2017-05-19 02:04:24 +08:00
function drawXGrid( $chart, $scale, $labelClass, $tickClass, $gridClass, $zoomClass=false ) {
$html = '';
ob_start();
$labelCount = 0;
$lastTick = 0;
unset( $lastLabel );
$labelCheck = isset($scale['labelCheck'])?$scale['labelCheck']:$scale['label'];
?>
2018-04-21 01:10:38 +08:00
<div id='xScale'>
<?php
for ( $i = 0; $i < $chart['graph']['width']; $i++ ) {
$x = $i - 1;
$timeOffset = (int)($chart['data']['x']['lo'] + ($i * $chart['data']['x']['density']));
if ( $scale['align'] > 1 ) {
$label = (int)(strftime( $labelCheck, $timeOffset )/$scale['align']);
} else {
$label = strftime( $labelCheck, $timeOffset );
}
if ( !isset($lastLabel) || ($lastLabel != $label) ) {
$labelCount++;
}
if ( $labelCount >= $scale['divisor'] ) {
$labelCount = 0;
if ( isset($lastLabel) ) {
if ( $labelClass ) {
?>
<div class="<?php echo $labelClass ?>" style="left: <?php echo $x-25 ?>px;"><?php echo strftime( $scale['label'], $timeOffset ); ?></div>
<?php
}
if ( $tickClass ) {
?>
<div class="<?php echo $tickClass ?>" style="left: <?php echo $x ?>px;"></div>
<?php
}
if ( $gridClass ) {
?>
<div class="<?php echo $gridClass ?>" style="left: <?php echo $x ?>px;"></div>
<?php
}
if ( $scale['name'] != 'second' && $zoomClass ) {
$zoomMinTime = strftime( STRF_FMT_DATETIME_DB, (int)($chart['data']['x']['lo'] + ($lastTick * $chart['data']['x']['density'])) );
$zoomMaxTime = strftime( STRF_FMT_DATETIME_DB, (int)($chart['data']['x']['lo'] + ($i * $chart['data']['x']['density'])) );
?>
2015-05-10 21:10:30 +08:00
<div class="<?php echo $zoomClass ?>" style="left: <?php echo $lastTick-1 ?>px; width: <?php echo $i-$lastTick ?>px;" title="<?php echo translate('ZoomIn') ?>" onclick="tlZoomBounds( '<?php echo $zoomMinTime ?>', '<?php echo $zoomMaxTime ?>' )"></div>
<?php
}
$lastTick = $i;
} # end if $lastLabel
}
$lastLabel = $label;
} # end foreach width segment
if ( $zoomClass ) {
$zoomMinTime = strftime( STRF_FMT_DATETIME_DB, (int)($chart['data']['x']['lo'] + ($lastTick * $chart['data']['x']['density'])) );
$zoomMaxTime = strftime( STRF_FMT_DATETIME_DB, (int)($chart['data']['x']['lo'] + ($i * $chart['data']['x']['density'])) );
?>
2015-05-10 21:10:30 +08:00
<div class="<?php echo $zoomClass ?>" style="left: <?php echo $lastTick-1 ?>px; width: <?php echo $i-$lastTick ?>px;" title="<?php echo translate('ZoomIn') ?>" onclick="tlZoomBounds( '<?php echo $zoomMinTime ?>', '<?php echo $zoomMaxTime ?>' )"></div>
<?php
}
?>
</div>
<?php
return( ob_get_clean() );
} # end function drawXGrid
2017-05-19 02:04:24 +08:00
function drawYGrid( $chart, $scale, $labelClass, $tickClass, $gridClass ) {
ob_start();
?>
2018-04-21 01:10:38 +08:00
<div id='yScale'>
<?php
for ( $i = 0; $i < $scale['lines']; $i++ ) {
$label = (int)($i * $scale['divisor']);
$y = $chart['graph']['eventsHeight']+(int)(($i * $scale['divisor'])/$chart['data']['y']['density'])-1;
if ( $labelClass ) {
?>
<div class="<?php echo $labelClass ?>" style="top: <?php echo $chart['graph']['height']-($y+8) ?>px;"><?php echo $label ?></div>
<?php
}
if ( $tickClass ) {
?>
<div class="<?php echo $tickClass ?>" style="top: <?php echo $chart['graph']['height']-($y+2) ?>px;"></div>
<?php
}
if ( $gridClass ) {
?>
<div class="<?php echo $gridClass ?>" style="top: <?php echo $chart['graph']['height']-($y+2) ?>px;<?php echo $i <= 0?' border-top: solid 1px black;':'' ?>"></div>
<?php
}
} # end foreach line segment
?>
</div>
<?php
return( ob_get_clean() );
} # end function drawYGrid
2017-05-19 02:04:24 +08:00
function getSlotPreviewEventBehaviour( $slot ) {
return( "previewEvent( '".$slot['event']['Id']."', '".getSlotFrame( $slot )."' )" );
}
2017-05-19 02:04:24 +08:00
function getSlotShowEventBehaviour( $slot ) {
global $monitors;
2017-05-19 02:04:24 +08:00
$monitor = &$monitors[$slot['event']['MonitorId']];
2017-05-19 02:04:24 +08:00
return( "showEvent( '".$slot['event']['Id']."', '".getSlotFrame( $slot )."', '".reScale( $monitor['Width'], $monitor['DefaultScale'], ZM_WEB_DEFAULT_SCALE )."', '".reScale( $monitor['Height'], $monitor['DefaultScale'], ZM_WEB_DEFAULT_SCALE )."' )" );
}
$focusWindow = true;
2015-05-10 21:10:30 +08:00
xhtmlHeaders(__FILE__, translate('Timeline') );
?>
<body>
2018-04-21 01:10:38 +08:00
<div id='page'>
2017-11-28 02:43:15 +08:00
<?php echo getNavBarHTML() ?>
2018-04-21 01:10:38 +08:00
<div id='header'>
<div id='info'>
2017-11-30 00:10:28 +08:00
<h2><?php echo translate('Timeline') ?></h2>
2018-04-21 01:10:38 +08:00
<a id='refreshLink' href="#" onclick="location.reload(true);"><?php echo translate('Refresh') ?></a>
2017-11-30 00:10:28 +08:00
</div>
2018-04-21 01:10:38 +08:00
<div id='headerButtons'>
2017-11-28 02:43:15 +08:00
<a href="#" onclick="window.history.back();"><?php echo translate('Back') ?></a>
2017-12-01 22:52:46 +08:00
<a href="?view=events&amp;page=1<?php echo htmlspecialchars($filterQuery) ?>"><?php echo translate('List') ?></a>
</div>
</div>
2018-04-21 01:10:38 +08:00
<div id='content' class='chartSize'>
<div id='topPanel' class='graphWidth'>
<div id='imagePanel'>
<div id='image' class='imageHeight'>
<img id='imageSrc' class='imageWidth' src="graphics/transparent.png" alt="<?php echo translate('ViewEvent') ?>" title="<?php echo translate('ViewEvent') ?>"/>
Feature h264 videostorage (#1882) * Moved writing of configure options from Controller to Model. Fixes #191. * Initial commit for saving events as videos :) * Add zm_video.cpp to autotools * Add zm_video.h to autotools * Search for MP4V2 header file 3 times: mp4v2/mp4v2.h, mp4v2.h, mp4.h * Fix serve memory leak * Few minor code improvements * Added the ability to override preset, tune, profile and few other improvements * Correctly write SPS & PPS from x264 encoder headers * Remove unnessecary SPS & PPS writing code * Imported missing files from master to feature-h264-videostorage * Audio support including fixes for dts/pts, split on keyframe and update to mkv extension to prevent ffmpeg problems writing rtsp audio to mp4 containter (header problem) * Updates to make gcc happy * Add html5 video control to timeline and event to support mkv playback * Add zm_videostore.cpp to CMakeLists.txt * Remove Modern Branch for now * Fix minor bug * Option handled added in master, removing duplicate declaration * Add CaptureandRecord from zm_camera.h * Putting placeholder in for CaptureAndRecord function * Removed duplicate code and brackets * add digest auth file for cmake Conflicts: src/CMakeLists.txt * Add web dir back into Makefile.am Revert "Removed web from SUBDIRS in Makefile.am" This reverts commit d9bbcdf3a94cba4d8955fcd03bd965ed2772f34d. * Add CaptureAndRecord to vlc, still need to make it record * Resolve SegFault on videostore * Swap to mp4 container * mp4 changes * spaces to tabs, hide video stuff if video writer is turned off * Make timeline open event.mp4 instead of mkv * Missed mkv in timeline.js * Fix some issues from the merge conflict * Resolve post merge build issues with braces * Fix whitespace * Update Jpeg and Video options for passthrough options * Whitespace fix zm_camera.h * Fix array mkssing comma * Add support for Jpeg save options for h264 branch snapshot. Might remove altogether if snapshots not needed * Update VideoStoreData memory size comment * Change from config.use_mkv_storage to per monitor option VideoWriter from video branch * Fix bracket issues post merge * Clean up comments and add av_free_packet * Convert from event_directory to event file as per Video branch * Testing videojs for video playback * Fixed a missing bracket post merge and also SQL_values now used for EventID and Monitors * bring recent improvements in ffmpeg capture function into captureandrecord * Remove pict from writeAudioFramePacket as not used * Add translate options for h264 Storage options in Monitor and update en_gb file * Cherry-Pick from iconnor - make it compile on ubuntu 15.04. Which is libav 56.1.0 Conflicts: src/zm_ffmpeg.cpp src/zm_remote_camera_rtsp.cpp Conflicts: distros/ubuntu1204/changelog * Clean up videostore code and remove lots of unused code * proof of concept for dynamic/automatic video rotation using video-js plugin zoomrotate Conflicts: web/skins/classic/views/event.php * removed redundant field in sql query Conflicts: web/skins/classic/views/event.php * local storage of video js plugin * Beautify! Make the code somewhat readable. * added missing videojs.zoomrotate.js file added missing videojs.zoomrotate.js file * Typo added missing " * Added missing brackets * fix to display thumbnails when only storing snapshot.jpg * added control for video playback rate Conflicts: web/skins/classic/views/event.php * dynamically create jpegs from video file for viewing in browser * fix timeline view for SaveJPEGs monitors (without enabled VideoWriter) * only expose monitor info which are being used in client * fix segmentation fault in zma with ubuntu 14.04 and ffmpeg 2.5.8 (gcc 4.8) when libx264 is not installed * better way of detecting showing image or video in timeline and event view instead of Monitor.VideoWriter, Event.DefaultVideo is used, so even if VideoWriter/SaveJPEG option is changed, a valid image or video will always be displayed for historical events in both timeline and event view this also fixes loading videos in timeline view * Fixes problem of crashing zmc when bad packet arrives causing av_interleaved_write_frame() to return non-zero (-22). Prefilters common packet issues. Add metadata title to generated video file * Remove syslog.h * fixed SaveJPEGs are not working which is caused in errors introduced when merging with master * Update README.md * Fix build warnings specific to h264 branch, unused FrameImg, unused ret and int64_t snprintf issues * Fix PRId64 issue in travis, builds locally fine, but I can see a gcc version issue here * Fix PRId64 issue in travis, another try * Try "STDC_FORMAT_MACROS" to see if that helps Travis on gcc 4.6.3 * Revert space removal around PRId64 * video branch ffmpeg 2.9 fixes ffmpeg 2.9 patched removed SSE2 CPU * Add FFMPEGInit back * use webvvt to overlay timestamp (honoring Monitor.LabelFormat) to videos in timeline and event also fixed bug which prevented seeking in timeline video preview * ffmpeg 3.0 API build failure fixes * Update README.md * merge all the commits from the messed up iconnor_video branch * fix whitespace * revert * whitespace fixes * spelling fix * put back some text * add these back * fix spelling mistake * Steal some packet dumping routines from ffmpeg. Convert them to use our logging routines * add a test and error message if the codec is not h264 * these have been removed in master * add a view to check auth and just send the video * add some comments, and dump filename and AVFormatContext on failure to write header * add the toggle for RecordAudio so that the checkbox works to turn off Audio * Must init videoStore in constuctor * more debug and comments, return checking * Fix dropped part of sql query. * fix extra else and some whitespace * Fix missing } from merge that was preventing building. * fix tabs * get rid of use of separator, just use \n * Restore lost fixes for deprecation * Why are these failing * Respect record_audio flag when setting up video file so dont try and initiliase mp4 with unsupported audio * Forgot that I was trying to solve case of stream is true and record_audio is false. * Pass swscale_ctx back in to getCachedContext or it will create new context every frame and leak memory like a mofo. * Add libx264-dev and libmp4v2-dev to build requires to save hassle of ensuring they are installed before build. * Merge my Rotation/Orientation work and fixes for bad h264 streams * need arpa/inet for reverse lookups * pull in the new byte range code for viewing videos * Move our recording flag deeper into closeevent * add braces and only call closeEvent if there is an event * deprecate the z_frame_rate stuff which is deprecated in ffmpeg * remark out some debugging * fix for video on stream 1 * fix audio_stream to audio_st * Ignore bad decodes * fix problems with content-length causing viewing to not work in chrome/android * change logic of sending file contents to handle an off by one and be more readable * Some fixes pointed out by Maxim Romanov. Also simply the loading of events to not join the Monitors table * fix to sql for timeline * added RecordAudio to sql in README * Use sub queries instead of joins to fix errors when using new mysql defaults. * fix sql queries * Dockerfile to build feature-h264-videostorage * Must cast codec * add php-acpu as a dependency * require php5-acpu * fix typo * remove extra / * Add a line for out-of-tree builds to do api/lib/Cake/bootstrap.php * delete merge conflict files * delete merge conflict files
2017-05-16 10:02:48 +08:00
<?php
if ( 0 ) {
Feature h264 videostorage (#1882) * Moved writing of configure options from Controller to Model. Fixes #191. * Initial commit for saving events as videos :) * Add zm_video.cpp to autotools * Add zm_video.h to autotools * Search for MP4V2 header file 3 times: mp4v2/mp4v2.h, mp4v2.h, mp4.h * Fix serve memory leak * Few minor code improvements * Added the ability to override preset, tune, profile and few other improvements * Correctly write SPS & PPS from x264 encoder headers * Remove unnessecary SPS & PPS writing code * Imported missing files from master to feature-h264-videostorage * Audio support including fixes for dts/pts, split on keyframe and update to mkv extension to prevent ffmpeg problems writing rtsp audio to mp4 containter (header problem) * Updates to make gcc happy * Add html5 video control to timeline and event to support mkv playback * Add zm_videostore.cpp to CMakeLists.txt * Remove Modern Branch for now * Fix minor bug * Option handled added in master, removing duplicate declaration * Add CaptureandRecord from zm_camera.h * Putting placeholder in for CaptureAndRecord function * Removed duplicate code and brackets * add digest auth file for cmake Conflicts: src/CMakeLists.txt * Add web dir back into Makefile.am Revert "Removed web from SUBDIRS in Makefile.am" This reverts commit d9bbcdf3a94cba4d8955fcd03bd965ed2772f34d. * Add CaptureAndRecord to vlc, still need to make it record * Resolve SegFault on videostore * Swap to mp4 container * mp4 changes * spaces to tabs, hide video stuff if video writer is turned off * Make timeline open event.mp4 instead of mkv * Missed mkv in timeline.js * Fix some issues from the merge conflict * Resolve post merge build issues with braces * Fix whitespace * Update Jpeg and Video options for passthrough options * Whitespace fix zm_camera.h * Fix array mkssing comma * Add support for Jpeg save options for h264 branch snapshot. Might remove altogether if snapshots not needed * Update VideoStoreData memory size comment * Change from config.use_mkv_storage to per monitor option VideoWriter from video branch * Fix bracket issues post merge * Clean up comments and add av_free_packet * Convert from event_directory to event file as per Video branch * Testing videojs for video playback * Fixed a missing bracket post merge and also SQL_values now used for EventID and Monitors * bring recent improvements in ffmpeg capture function into captureandrecord * Remove pict from writeAudioFramePacket as not used * Add translate options for h264 Storage options in Monitor and update en_gb file * Cherry-Pick from iconnor - make it compile on ubuntu 15.04. Which is libav 56.1.0 Conflicts: src/zm_ffmpeg.cpp src/zm_remote_camera_rtsp.cpp Conflicts: distros/ubuntu1204/changelog * Clean up videostore code and remove lots of unused code * proof of concept for dynamic/automatic video rotation using video-js plugin zoomrotate Conflicts: web/skins/classic/views/event.php * removed redundant field in sql query Conflicts: web/skins/classic/views/event.php * local storage of video js plugin * Beautify! Make the code somewhat readable. * added missing videojs.zoomrotate.js file added missing videojs.zoomrotate.js file * Typo added missing " * Added missing brackets * fix to display thumbnails when only storing snapshot.jpg * added control for video playback rate Conflicts: web/skins/classic/views/event.php * dynamically create jpegs from video file for viewing in browser * fix timeline view for SaveJPEGs monitors (without enabled VideoWriter) * only expose monitor info which are being used in client * fix segmentation fault in zma with ubuntu 14.04 and ffmpeg 2.5.8 (gcc 4.8) when libx264 is not installed * better way of detecting showing image or video in timeline and event view instead of Monitor.VideoWriter, Event.DefaultVideo is used, so even if VideoWriter/SaveJPEG option is changed, a valid image or video will always be displayed for historical events in both timeline and event view this also fixes loading videos in timeline view * Fixes problem of crashing zmc when bad packet arrives causing av_interleaved_write_frame() to return non-zero (-22). Prefilters common packet issues. Add metadata title to generated video file * Remove syslog.h * fixed SaveJPEGs are not working which is caused in errors introduced when merging with master * Update README.md * Fix build warnings specific to h264 branch, unused FrameImg, unused ret and int64_t snprintf issues * Fix PRId64 issue in travis, builds locally fine, but I can see a gcc version issue here * Fix PRId64 issue in travis, another try * Try "STDC_FORMAT_MACROS" to see if that helps Travis on gcc 4.6.3 * Revert space removal around PRId64 * video branch ffmpeg 2.9 fixes ffmpeg 2.9 patched removed SSE2 CPU * Add FFMPEGInit back * use webvvt to overlay timestamp (honoring Monitor.LabelFormat) to videos in timeline and event also fixed bug which prevented seeking in timeline video preview * ffmpeg 3.0 API build failure fixes * Update README.md * merge all the commits from the messed up iconnor_video branch * fix whitespace * revert * whitespace fixes * spelling fix * put back some text * add these back * fix spelling mistake * Steal some packet dumping routines from ffmpeg. Convert them to use our logging routines * add a test and error message if the codec is not h264 * these have been removed in master * add a view to check auth and just send the video * add some comments, and dump filename and AVFormatContext on failure to write header * add the toggle for RecordAudio so that the checkbox works to turn off Audio * Must init videoStore in constuctor * more debug and comments, return checking * Fix dropped part of sql query. * fix extra else and some whitespace * Fix missing } from merge that was preventing building. * fix tabs * get rid of use of separator, just use \n * Restore lost fixes for deprecation * Why are these failing * Respect record_audio flag when setting up video file so dont try and initiliase mp4 with unsupported audio * Forgot that I was trying to solve case of stream is true and record_audio is false. * Pass swscale_ctx back in to getCachedContext or it will create new context every frame and leak memory like a mofo. * Add libx264-dev and libmp4v2-dev to build requires to save hassle of ensuring they are installed before build. * Merge my Rotation/Orientation work and fixes for bad h264 streams * need arpa/inet for reverse lookups * pull in the new byte range code for viewing videos * Move our recording flag deeper into closeevent * add braces and only call closeEvent if there is an event * deprecate the z_frame_rate stuff which is deprecated in ffmpeg * remark out some debugging * fix for video on stream 1 * fix audio_stream to audio_st * Ignore bad decodes * fix problems with content-length causing viewing to not work in chrome/android * change logic of sending file contents to handle an off by one and be more readable * Some fixes pointed out by Maxim Romanov. Also simply the loading of events to not join the Monitors table * fix to sql for timeline * added RecordAudio to sql in README * Use sub queries instead of joins to fix errors when using new mysql defaults. * fix sql queries * Dockerfile to build feature-h264-videostorage * Must cast codec * add php-acpu as a dependency * require php5-acpu * fix typo * remove extra / * Add a line for out-of-tree builds to do api/lib/Cake/bootstrap.php * delete merge conflict files * delete merge conflict files
2017-05-16 10:02:48 +08:00
//due to chrome bug, has to enable https://code.google.com/p/chromium/issues/detail?id=472300
//crossorigin has to be added below to make caption work in chrome
?>
<!--
2018-04-21 01:10:38 +08:00
<video id='preview' width="100%" controls crossorigin='anonymous'>
2017-07-14 05:34:45 +08:00
<source src="<?php echo getEventDefaultVideoPath($first_event); ?>" type="video/mp4">
Feature h264 videostorage (#1882) * Moved writing of configure options from Controller to Model. Fixes #191. * Initial commit for saving events as videos :) * Add zm_video.cpp to autotools * Add zm_video.h to autotools * Search for MP4V2 header file 3 times: mp4v2/mp4v2.h, mp4v2.h, mp4.h * Fix serve memory leak * Few minor code improvements * Added the ability to override preset, tune, profile and few other improvements * Correctly write SPS & PPS from x264 encoder headers * Remove unnessecary SPS & PPS writing code * Imported missing files from master to feature-h264-videostorage * Audio support including fixes for dts/pts, split on keyframe and update to mkv extension to prevent ffmpeg problems writing rtsp audio to mp4 containter (header problem) * Updates to make gcc happy * Add html5 video control to timeline and event to support mkv playback * Add zm_videostore.cpp to CMakeLists.txt * Remove Modern Branch for now * Fix minor bug * Option handled added in master, removing duplicate declaration * Add CaptureandRecord from zm_camera.h * Putting placeholder in for CaptureAndRecord function * Removed duplicate code and brackets * add digest auth file for cmake Conflicts: src/CMakeLists.txt * Add web dir back into Makefile.am Revert "Removed web from SUBDIRS in Makefile.am" This reverts commit d9bbcdf3a94cba4d8955fcd03bd965ed2772f34d. * Add CaptureAndRecord to vlc, still need to make it record * Resolve SegFault on videostore * Swap to mp4 container * mp4 changes * spaces to tabs, hide video stuff if video writer is turned off * Make timeline open event.mp4 instead of mkv * Missed mkv in timeline.js * Fix some issues from the merge conflict * Resolve post merge build issues with braces * Fix whitespace * Update Jpeg and Video options for passthrough options * Whitespace fix zm_camera.h * Fix array mkssing comma * Add support for Jpeg save options for h264 branch snapshot. Might remove altogether if snapshots not needed * Update VideoStoreData memory size comment * Change from config.use_mkv_storage to per monitor option VideoWriter from video branch * Fix bracket issues post merge * Clean up comments and add av_free_packet * Convert from event_directory to event file as per Video branch * Testing videojs for video playback * Fixed a missing bracket post merge and also SQL_values now used for EventID and Monitors * bring recent improvements in ffmpeg capture function into captureandrecord * Remove pict from writeAudioFramePacket as not used * Add translate options for h264 Storage options in Monitor and update en_gb file * Cherry-Pick from iconnor - make it compile on ubuntu 15.04. Which is libav 56.1.0 Conflicts: src/zm_ffmpeg.cpp src/zm_remote_camera_rtsp.cpp Conflicts: distros/ubuntu1204/changelog * Clean up videostore code and remove lots of unused code * proof of concept for dynamic/automatic video rotation using video-js plugin zoomrotate Conflicts: web/skins/classic/views/event.php * removed redundant field in sql query Conflicts: web/skins/classic/views/event.php * local storage of video js plugin * Beautify! Make the code somewhat readable. * added missing videojs.zoomrotate.js file added missing videojs.zoomrotate.js file * Typo added missing " * Added missing brackets * fix to display thumbnails when only storing snapshot.jpg * added control for video playback rate Conflicts: web/skins/classic/views/event.php * dynamically create jpegs from video file for viewing in browser * fix timeline view for SaveJPEGs monitors (without enabled VideoWriter) * only expose monitor info which are being used in client * fix segmentation fault in zma with ubuntu 14.04 and ffmpeg 2.5.8 (gcc 4.8) when libx264 is not installed * better way of detecting showing image or video in timeline and event view instead of Monitor.VideoWriter, Event.DefaultVideo is used, so even if VideoWriter/SaveJPEG option is changed, a valid image or video will always be displayed for historical events in both timeline and event view this also fixes loading videos in timeline view * Fixes problem of crashing zmc when bad packet arrives causing av_interleaved_write_frame() to return non-zero (-22). Prefilters common packet issues. Add metadata title to generated video file * Remove syslog.h * fixed SaveJPEGs are not working which is caused in errors introduced when merging with master * Update README.md * Fix build warnings specific to h264 branch, unused FrameImg, unused ret and int64_t snprintf issues * Fix PRId64 issue in travis, builds locally fine, but I can see a gcc version issue here * Fix PRId64 issue in travis, another try * Try "STDC_FORMAT_MACROS" to see if that helps Travis on gcc 4.6.3 * Revert space removal around PRId64 * video branch ffmpeg 2.9 fixes ffmpeg 2.9 patched removed SSE2 CPU * Add FFMPEGInit back * use webvvt to overlay timestamp (honoring Monitor.LabelFormat) to videos in timeline and event also fixed bug which prevented seeking in timeline video preview * ffmpeg 3.0 API build failure fixes * Update README.md * merge all the commits from the messed up iconnor_video branch * fix whitespace * revert * whitespace fixes * spelling fix * put back some text * add these back * fix spelling mistake * Steal some packet dumping routines from ffmpeg. Convert them to use our logging routines * add a test and error message if the codec is not h264 * these have been removed in master * add a view to check auth and just send the video * add some comments, and dump filename and AVFormatContext on failure to write header * add the toggle for RecordAudio so that the checkbox works to turn off Audio * Must init videoStore in constuctor * more debug and comments, return checking * Fix dropped part of sql query. * fix extra else and some whitespace * Fix missing } from merge that was preventing building. * fix tabs * get rid of use of separator, just use \n * Restore lost fixes for deprecation * Why are these failing * Respect record_audio flag when setting up video file so dont try and initiliase mp4 with unsupported audio * Forgot that I was trying to solve case of stream is true and record_audio is false. * Pass swscale_ctx back in to getCachedContext or it will create new context every frame and leak memory like a mofo. * Add libx264-dev and libmp4v2-dev to build requires to save hassle of ensuring they are installed before build. * Merge my Rotation/Orientation work and fixes for bad h264 streams * need arpa/inet for reverse lookups * pull in the new byte range code for viewing videos * Move our recording flag deeper into closeevent * add braces and only call closeEvent if there is an event * deprecate the z_frame_rate stuff which is deprecated in ffmpeg * remark out some debugging * fix for video on stream 1 * fix audio_stream to audio_st * Ignore bad decodes * fix problems with content-length causing viewing to not work in chrome/android * change logic of sending file contents to handle an off by one and be more readable * Some fixes pointed out by Maxim Romanov. Also simply the loading of events to not join the Monitors table * fix to sql for timeline * added RecordAudio to sql in README * Use sub queries instead of joins to fix errors when using new mysql defaults. * fix sql queries * Dockerfile to build feature-h264-videostorage * Must cast codec * add php-acpu as a dependency * require php5-acpu * fix typo * remove extra / * Add a line for out-of-tree builds to do api/lib/Cake/bootstrap.php * delete merge conflict files * delete merge conflict files
2017-05-16 10:02:48 +08:00
Your browser does not support the video tag.
</video>
o-->
<?php } ?>
Feature h264 videostorage (#1882) * Moved writing of configure options from Controller to Model. Fixes #191. * Initial commit for saving events as videos :) * Add zm_video.cpp to autotools * Add zm_video.h to autotools * Search for MP4V2 header file 3 times: mp4v2/mp4v2.h, mp4v2.h, mp4.h * Fix serve memory leak * Few minor code improvements * Added the ability to override preset, tune, profile and few other improvements * Correctly write SPS & PPS from x264 encoder headers * Remove unnessecary SPS & PPS writing code * Imported missing files from master to feature-h264-videostorage * Audio support including fixes for dts/pts, split on keyframe and update to mkv extension to prevent ffmpeg problems writing rtsp audio to mp4 containter (header problem) * Updates to make gcc happy * Add html5 video control to timeline and event to support mkv playback * Add zm_videostore.cpp to CMakeLists.txt * Remove Modern Branch for now * Fix minor bug * Option handled added in master, removing duplicate declaration * Add CaptureandRecord from zm_camera.h * Putting placeholder in for CaptureAndRecord function * Removed duplicate code and brackets * add digest auth file for cmake Conflicts: src/CMakeLists.txt * Add web dir back into Makefile.am Revert "Removed web from SUBDIRS in Makefile.am" This reverts commit d9bbcdf3a94cba4d8955fcd03bd965ed2772f34d. * Add CaptureAndRecord to vlc, still need to make it record * Resolve SegFault on videostore * Swap to mp4 container * mp4 changes * spaces to tabs, hide video stuff if video writer is turned off * Make timeline open event.mp4 instead of mkv * Missed mkv in timeline.js * Fix some issues from the merge conflict * Resolve post merge build issues with braces * Fix whitespace * Update Jpeg and Video options for passthrough options * Whitespace fix zm_camera.h * Fix array mkssing comma * Add support for Jpeg save options for h264 branch snapshot. Might remove altogether if snapshots not needed * Update VideoStoreData memory size comment * Change from config.use_mkv_storage to per monitor option VideoWriter from video branch * Fix bracket issues post merge * Clean up comments and add av_free_packet * Convert from event_directory to event file as per Video branch * Testing videojs for video playback * Fixed a missing bracket post merge and also SQL_values now used for EventID and Monitors * bring recent improvements in ffmpeg capture function into captureandrecord * Remove pict from writeAudioFramePacket as not used * Add translate options for h264 Storage options in Monitor and update en_gb file * Cherry-Pick from iconnor - make it compile on ubuntu 15.04. Which is libav 56.1.0 Conflicts: src/zm_ffmpeg.cpp src/zm_remote_camera_rtsp.cpp Conflicts: distros/ubuntu1204/changelog * Clean up videostore code and remove lots of unused code * proof of concept for dynamic/automatic video rotation using video-js plugin zoomrotate Conflicts: web/skins/classic/views/event.php * removed redundant field in sql query Conflicts: web/skins/classic/views/event.php * local storage of video js plugin * Beautify! Make the code somewhat readable. * added missing videojs.zoomrotate.js file added missing videojs.zoomrotate.js file * Typo added missing " * Added missing brackets * fix to display thumbnails when only storing snapshot.jpg * added control for video playback rate Conflicts: web/skins/classic/views/event.php * dynamically create jpegs from video file for viewing in browser * fix timeline view for SaveJPEGs monitors (without enabled VideoWriter) * only expose monitor info which are being used in client * fix segmentation fault in zma with ubuntu 14.04 and ffmpeg 2.5.8 (gcc 4.8) when libx264 is not installed * better way of detecting showing image or video in timeline and event view instead of Monitor.VideoWriter, Event.DefaultVideo is used, so even if VideoWriter/SaveJPEG option is changed, a valid image or video will always be displayed for historical events in both timeline and event view this also fixes loading videos in timeline view * Fixes problem of crashing zmc when bad packet arrives causing av_interleaved_write_frame() to return non-zero (-22). Prefilters common packet issues. Add metadata title to generated video file * Remove syslog.h * fixed SaveJPEGs are not working which is caused in errors introduced when merging with master * Update README.md * Fix build warnings specific to h264 branch, unused FrameImg, unused ret and int64_t snprintf issues * Fix PRId64 issue in travis, builds locally fine, but I can see a gcc version issue here * Fix PRId64 issue in travis, another try * Try "STDC_FORMAT_MACROS" to see if that helps Travis on gcc 4.6.3 * Revert space removal around PRId64 * video branch ffmpeg 2.9 fixes ffmpeg 2.9 patched removed SSE2 CPU * Add FFMPEGInit back * use webvvt to overlay timestamp (honoring Monitor.LabelFormat) to videos in timeline and event also fixed bug which prevented seeking in timeline video preview * ffmpeg 3.0 API build failure fixes * Update README.md * merge all the commits from the messed up iconnor_video branch * fix whitespace * revert * whitespace fixes * spelling fix * put back some text * add these back * fix spelling mistake * Steal some packet dumping routines from ffmpeg. Convert them to use our logging routines * add a test and error message if the codec is not h264 * these have been removed in master * add a view to check auth and just send the video * add some comments, and dump filename and AVFormatContext on failure to write header * add the toggle for RecordAudio so that the checkbox works to turn off Audio * Must init videoStore in constuctor * more debug and comments, return checking * Fix dropped part of sql query. * fix extra else and some whitespace * Fix missing } from merge that was preventing building. * fix tabs * get rid of use of separator, just use \n * Restore lost fixes for deprecation * Why are these failing * Respect record_audio flag when setting up video file so dont try and initiliase mp4 with unsupported audio * Forgot that I was trying to solve case of stream is true and record_audio is false. * Pass swscale_ctx back in to getCachedContext or it will create new context every frame and leak memory like a mofo. * Add libx264-dev and libmp4v2-dev to build requires to save hassle of ensuring they are installed before build. * Merge my Rotation/Orientation work and fixes for bad h264 streams * need arpa/inet for reverse lookups * pull in the new byte range code for viewing videos * Move our recording flag deeper into closeevent * add braces and only call closeEvent if there is an event * deprecate the z_frame_rate stuff which is deprecated in ffmpeg * remark out some debugging * fix for video on stream 1 * fix audio_stream to audio_st * Ignore bad decodes * fix problems with content-length causing viewing to not work in chrome/android * change logic of sending file contents to handle an off by one and be more readable * Some fixes pointed out by Maxim Romanov. Also simply the loading of events to not join the Monitors table * fix to sql for timeline * added RecordAudio to sql in README * Use sub queries instead of joins to fix errors when using new mysql defaults. * fix sql queries * Dockerfile to build feature-h264-videostorage * Must cast codec * add php-acpu as a dependency * require php5-acpu * fix typo * remove extra / * Add a line for out-of-tree builds to do api/lib/Cake/bootstrap.php * delete merge conflict files * delete merge conflict files
2017-05-16 10:02:48 +08:00
</div>
</div>
2018-04-21 01:10:38 +08:00
<div id='dataPanel'>
<div id='textPanel'>
<div id='instruction'>
2015-05-10 21:10:30 +08:00
<p><?php echo translate('TimelineTip1') ?></p>
<p><?php echo translate('TimelineTip2') ?></p>
<p><?php echo translate('TimelineTip3') ?></p>
<p><?php echo translate('TimelineTip4') ?></p>
</div>
2018-04-21 01:10:38 +08:00
<div id='eventData'>
</div>
</div>
2018-04-21 01:10:38 +08:00
<div id='navPanel'>
<input type='button' title="<?php echo translate('PanLeft') ?>" value="&lt;&lt;" onclick="tlPan( '<?php echo $minTime ?>', '<?php echo $range ?>' )"/>
<input type='button' title="<?php echo translate('ZoomOut') ?>" value="&ndash;" onclick="tlZoomRange( '<?php echo $midTime ?>', '<?php echo (int)($range*$majXScale['zoomout']) ?>' )"/>
<input type='button' title="<?php echo translate('PanRight') ?>" value="&gt;&gt;" onclick="tlPan( '<?php echo $maxTime ?>', '<?php echo $range ?>' )"/>
</div>
</div>
</div>
2018-04-21 01:10:38 +08:00
<div id='chartPanel'>
<div id='chart' class='graphSize'>
<?php
if ( $mode == 'overlay' ) {
echo drawYGrid( $chart, $majYScale, 'majLabelY', 'majTickY', 'majGridY graphWidth' );
}
echo drawXGrid( $chart, $majXScale, 'majLabelX', 'majTickX', 'majGridX graphHeight', 'zoom graphHeight' );
if ( $mode == 'overlay' ) {
?>
2018-04-21 01:10:38 +08:00
<div id='activity' class='activitySize'>
<?php
2017-05-19 02:04:24 +08:00
foreach ( $frameSlots as $index=>$slots ) {
foreach ( $slots as $slot ) {
$slotHeight = (int)($slot['value']/$chart['data']['y']['density']);
if ( $slotHeight <= 0 )
continue;
if ( $mouseover ) {
$behaviours = array(
2018-04-21 01:10:38 +08:00
'onclick="'.getSlotShowEventBehaviour($slot).'"',
'onmouseover="'.getSlotPreviewEventBehaviour($slot).'"'
);
} else {
$behaviours = array(
2018-04-21 01:10:38 +08:00
'onclick="'.getSlotPreviewEventBehaviour($slot).'"'
);
}
?>
2018-04-21 01:10:38 +08:00
<div class="activity monitorColour<?php echo $slot['event']['MonitorId'] ?>" style="left: <?php echo $index ?>px; height: <?php echo $slotHeight ?>px;" <?php echo join( ' ', $behaviours ) ?>></div>
<?php
}
}
?>
</div>
<?php
} elseif ( $mode == 'split' ) {
2017-05-19 02:04:24 +08:00
foreach( array_keys($monFrameSlots) as $monitorId ) {
?>
2017-05-19 02:04:24 +08:00
<div id="activity<?php echo $monitorId ?>">
<?php
2018-04-21 01:10:38 +08:00
unset($currFrameSlots);
2017-05-19 02:04:24 +08:00
$currFrameSlots = &$monFrameSlots[$monitorId];
foreach ( $currFrameSlots as $index=>$slot ) {
$slotHeight = (int)($slot['value']/$chart['data']['y']['density']);
if ( $slotHeight <= 0 )
continue;
if ( $mouseover ) {
$behaviours = array(
2018-04-21 01:10:38 +08:00
'onclick="'.getSlotShowEventBehaviour($slot).'"',
'onmouseover="'.getSlotPreviewEventBehaviour($slot).'"'
2017-05-19 02:04:24 +08:00
);
} else {
$behaviours = array(
2018-04-21 01:10:38 +08:00
'onclick="'.getSlotPreviewEventBehaviour($slot).'"'
2017-05-19 02:04:24 +08:00
);
}
?>
2018-04-21 01:10:38 +08:00
<div class="activity activity<?php echo $slot['event']['MonitorId'] ?>" style="left: <?php echo $index ?>px; height: <?php echo $slotHeight ?>px;" <?php echo join( ' ', $behaviours ) ?>></div>
<?php
} # end foreach $currFrameSlots
2017-05-19 02:04:24 +08:00
?>
</div>
<?php
} # end foreach $MonitorId
}
2017-05-19 02:04:24 +08:00
foreach( array_keys($monEventSlots) as $monitorId ) {
?>
<div id="events<?php echo $monitorId ?>" class="events eventsSize eventsPos<?php echo $monitorId ?>">
<?php
2017-05-19 02:04:24 +08:00
unset( $currEventSlots );
$currEventSlots = &$monEventSlots[$monitorId];
for ( $i = 0; $i < $chart['graph']['width']; $i++ ) {
if ( isset($currEventSlots[$i]) ) {
unset( $slot );
$slot = &$currEventSlots[$i];
if ( $mouseover ) {
2017-05-19 02:04:24 +08:00
$behaviours = array(
2018-04-21 01:10:38 +08:00
'onclick="'.getSlotShowEventBehaviour($slot).'"',
'onmouseover="'.getSlotPreviewEventBehaviour($slot).'"'
2017-05-19 02:04:24 +08:00
);
} else {
$behaviours = array(
2018-04-21 01:10:38 +08:00
'onclick="'.getSlotPreviewEventBehaviour($slot).'"'
2017-05-19 02:04:24 +08:00
);
}
?>
2018-04-21 01:10:38 +08:00
<div class="event eventsHeight monitorColour<?php echo $monitorId ?>" style="left: <?php echo $i ?>px; width: <?php echo $slot['width'] ?>px;" <?php echo join( ' ', $behaviours ) ?>></div>
<?php
} # end if isset($currEventSlots[$i])
} # end foreach width segment
?>
</div>
<?php
}
?>
</div>
</div>
2018-04-21 01:10:38 +08:00
<div id='chartLabels' class='graphWidth'>
<div id='key'>
<?php
2017-05-19 02:04:24 +08:00
foreach( array_keys($monEventSlots) as $monitorId ) {
?>
2018-04-21 01:10:38 +08:00
<span class='keyEntry'><?php echo $monitors[$monitorId]['Name'] ?>
<img id="keyBox<?php echo $monitorId ?>" class="keyBox monitorColour<?php echo $monitorId ?>" src="graphics/transparent.png" alt="<?php echo $monitors[$monitorId]['Name'] ?>"/>
</span>
<?php
}
?>
</div>
2018-04-21 01:10:38 +08:00
<div id='range'><?php echo $title ?></div>
</div>
</div>
</div>
</body>
</html>