Add h264 event view functionality and new feature alarmCues (#2012)
* Fix nearEventsQuery Removed dbEscape from getNearEvents previous event because it only returns 0. Now matches next. Changed getEventDefaultVideoPath function to return a web path rather than the absolute path based on ic0ns branch. Also added start times to allow for videoJS replaymode. * Unescape filters Filters need to be unescaped * Add initial values to page load * Add replay modes to videojs * rough in figuring out a frame in between bulk frames * Add alarmCues Add a graphical indication of where alarm frames happened in an event. Similar to what zmNinja shows. * Add remaining buttons to videojs Functionality for all buttons on videojs streams. FF/RW buttons show as active when they are active. * Whitespace and fix Bulkframe calcs * Fix zms events trying to generate with mp4 code ZMS events would attempt to generate frames as though they were an mp4/passthrough type because the full eventpath wasn't passed * ZMS scrub bar Move zms scrub bar to bottom of image feed. Make it simpler and more like videojs style. * Wrap event feeds properly * Fix dvrControls on watch view * Add scaleToFit Add a scaleToFit option to event view * Add navigation for videoJS streams Disables nav buttons at beginning and end of events. Handles switching from zms to videojs. If zms crashes changes next event function to reload page instead of ajax. * Add scaleToFit to watch and frame view Adds scaleToFit to watch view. Since frame view uses the watch cookie this required changes to frame view * Add transition to zoom * Change stills view to match stream Move stills slider bar to match scrub bar on streams. Allow it to resize, make it larger. Add alarmcues. * Add Stills for every event Add stills for every event. Match size to stream size * Progressbox transitions
This commit is contained in:
parent
fb952179cd
commit
f8d3c07586
|
@ -136,6 +136,17 @@ $statusData = array(
|
|||
//"Path" => array( "postFunc" => "getEventPath" ),
|
||||
),
|
||||
),
|
||||
"frames" => array(
|
||||
"permission" => "Events",
|
||||
"table" => "Frames",
|
||||
"selector" => "EventId",
|
||||
"elements" => array(
|
||||
"EventId" => true,
|
||||
"FrameId" => true,
|
||||
"Type" => true,
|
||||
"Delta" => true,
|
||||
),
|
||||
),
|
||||
"frame" => array(
|
||||
"permission" => "Events",
|
||||
"table" => "Frames",
|
||||
|
@ -367,7 +378,7 @@ function getNearEvents() {
|
|||
else
|
||||
$midSql = '';
|
||||
|
||||
$sql = "select E.Id as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where ".dbEscape($sortColumn)." ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn ".($sortOrder=='asc'?'desc':'asc');
|
||||
$sql = "SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE $sortColumn ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." ORDER BY $sortColumn ".($sortOrder=='asc'?'desc':'asc') . ' LIMIT 2';
|
||||
$result = dbQuery( $sql );
|
||||
while ( $id = dbFetchNext( $result, 'Id' ) ) {
|
||||
if ( $id == $eventId ) {
|
||||
|
@ -376,7 +387,7 @@ function getNearEvents() {
|
|||
}
|
||||
}
|
||||
|
||||
$sql = "select E.Id as Id from Events as E inner join Monitors as M on E.MonitorId = M.Id where $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn $sortOrder";
|
||||
$sql = "SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." ORDER BY $sortColumn $sortOrder LIMIT 2";
|
||||
$result = dbQuery( $sql );
|
||||
while ( $id = dbFetchNext( $result, 'Id' ) ) {
|
||||
if ( $id == $eventId ) {
|
||||
|
@ -388,8 +399,10 @@ function getNearEvents() {
|
|||
$result = array( 'EventId'=>$eventId );
|
||||
$result['PrevEventId'] = empty($prevEvent)?0:$prevEvent['Id'];
|
||||
$result['NextEventId'] = empty($nextEvent)?0:$nextEvent['Id'];
|
||||
$result['PrevEventDefVideoPath'] = empty($prevEvent)?0:(getEventDefaultVideoPath($prevEvent));
|
||||
$result['NextEventDefVideoPath'] = empty($nextEvent)?0:(getEventDefaultVideoPath($nextEvent));
|
||||
$result['PrevEventStartTime'] = empty($prevEvent)?0:$prevEvent['StartTime'];
|
||||
$result['NextEventStartTime'] = empty($nextEvent)?0:$nextEvent['StartTime'];
|
||||
$result['PrevEventDefVideoPath'] = empty($prevEvent)?0:(getEventDefaultVideoPath($prevEvent['Id']));
|
||||
$result['NextEventDefVideoPath'] = empty($nextEvent)?0:(getEventDefaultVideoPath($nextEvent['Id']));
|
||||
return( $result );
|
||||
}
|
||||
|
||||
|
|
|
@ -452,7 +452,8 @@ function getEventPath( $event ) {
|
|||
}
|
||||
|
||||
function getEventDefaultVideoPath( $event ) {
|
||||
return ZM_DIR_EVENTS . '/' . getEventPath($event) . '/' . $event['DefaultVideo'];
|
||||
$Event = new Event( $event );
|
||||
return $Event->getStreamSrc( array( "mode"=>"mpeg", "format"=>"h264" ) );
|
||||
}
|
||||
|
||||
function deletePath( $path ) {
|
||||
|
@ -992,7 +993,7 @@ function zmaCheck( $monitor ) {
|
|||
}
|
||||
|
||||
function getImageSrc( $event, $frame, $scale=SCALE_BASE, $captureOnly=false, $overwrite=false ) {
|
||||
$eventPath = getEventPath( $event );
|
||||
$eventPath = ZM_DIR_EVENTS . '/' . getEventPath( $event );
|
||||
|
||||
if ( !is_array($frame) )
|
||||
$frame = array( 'FrameId'=>$frame, 'Type'=>'' );
|
||||
|
@ -2305,6 +2306,7 @@ function getStreamMode( ) {
|
|||
$streamMode = 'single';
|
||||
Info( 'The system has fallen back to single jpeg mode for streaming. Consider enabling Cambozola or upgrading the client browser.' );
|
||||
}
|
||||
return $streamMode;
|
||||
} // end function getStreamMode
|
||||
|
||||
function folder_size($dir) {
|
||||
|
|
|
@ -1,3 +1,34 @@
|
|||
#content .vjsMessage {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
margin: 0;
|
||||
font-size: 200%;
|
||||
color: white;
|
||||
background-color: black;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.alarmCue {
|
||||
background-color: #222222;
|
||||
height: 1.25em;
|
||||
text-align: left;
|
||||
margin: 0 auto 0 auto;
|
||||
border-radius: 0 0 .3em .3em;
|
||||
}
|
||||
|
||||
.alarmCue span {
|
||||
background-color:red;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
span.noneCue {
|
||||
background: none;
|
||||
}
|
||||
|
||||
#dataBar {
|
||||
width: 100%;
|
||||
margin: 2px auto;
|
||||
|
@ -6,6 +37,7 @@
|
|||
|
||||
#dataBar #dataTable {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
#dataBar #dataTable td {
|
||||
|
@ -13,6 +45,11 @@
|
|||
padding: 2px;
|
||||
}
|
||||
|
||||
#eventVideo {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#menuBar1 {
|
||||
width: 100%;
|
||||
padding: 3px 0;
|
||||
|
@ -83,6 +120,8 @@
|
|||
}
|
||||
|
||||
#imageFeed {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
@ -147,39 +186,29 @@
|
|||
|
||||
#progressBar {
|
||||
position: relative;
|
||||
border: 1px solid #666666;
|
||||
height: 15px;
|
||||
margin: 0 auto;
|
||||
top: -1.25em;
|
||||
height: 1.25em;
|
||||
margin: 0 auto -1.25em auto;
|
||||
}
|
||||
|
||||
#progressBar .progressBox {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
height: 15px;
|
||||
background: #eeeeee;
|
||||
border-left: 1px solid #999999;
|
||||
}
|
||||
|
||||
#progressBar .complete {
|
||||
background: #aaaaaa;
|
||||
transition: width .1s;
|
||||
height: 100%;
|
||||
background: rgba(170, 170, 170, .7);
|
||||
border-radius: 0 0 .3em .3em;
|
||||
}
|
||||
|
||||
#eventStills {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#eventThumbsPanel {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin: 4px auto;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#eventThumbs {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
height: 300px;
|
||||
}
|
||||
|
@ -198,14 +227,19 @@
|
|||
}
|
||||
|
||||
#eventImagePanel {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#eventImageFrame {
|
||||
border: 2px solid gray;
|
||||
background-color: white;
|
||||
padding: 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#eventImage {
|
||||
|
@ -215,6 +249,14 @@
|
|||
margin-top: 2px;
|
||||
}
|
||||
|
||||
#eventImageBar::after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
content: "";
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
#eventImageStats {
|
||||
float: left;
|
||||
}
|
||||
|
@ -230,6 +272,7 @@
|
|||
|
||||
#eventImageNav {
|
||||
position: relative;
|
||||
margin: 0 0 4px 0;
|
||||
}
|
||||
|
||||
#eventImageNav input {
|
||||
|
@ -238,20 +281,20 @@
|
|||
}
|
||||
|
||||
#thumbsSliderPanel {
|
||||
width: 400px;
|
||||
margin: 4px auto 0;
|
||||
background: #888888;
|
||||
padding: 1px;
|
||||
width: 80%;
|
||||
margin: 0px auto 4px auto;
|
||||
}
|
||||
|
||||
#thumbsSlider {
|
||||
width: 400px;
|
||||
height: 10px;
|
||||
background: #dddddd;
|
||||
width: 100%;
|
||||
height: 1.25em;
|
||||
position: relative;
|
||||
top: -1.25em;
|
||||
margin: 0 0 -1.25em 0;
|
||||
}
|
||||
|
||||
#thumbsKnob {
|
||||
width: 8px;
|
||||
height: 10px;
|
||||
background-color: #444444;
|
||||
width: 1em;
|
||||
height: 100%;
|
||||
background-color: #999999;
|
||||
}
|
||||
|
|
|
@ -1,33 +1,16 @@
|
|||
#scaleControl {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#controls {
|
||||
width: 80%;
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#controls a {
|
||||
width: 40px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
#firstLink {
|
||||
position: absolute;
|
||||
left: 13%;
|
||||
}
|
||||
|
||||
#prevLink {
|
||||
position: absolute;
|
||||
left: 37%;
|
||||
}
|
||||
|
||||
#nextLink {
|
||||
position: absolute;
|
||||
left: 63%;
|
||||
}
|
||||
|
||||
#lastLink {
|
||||
position: absolute;
|
||||
left: 87%;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,39 @@
|
|||
#content .vjsMessage {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
margin: 0;
|
||||
font-size: 200%;
|
||||
color: white;
|
||||
background-color: black;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.alarmCue {
|
||||
background-color: #222222;
|
||||
height: 1.25em;
|
||||
text-align: left;
|
||||
margin: 0 auto 0 auto;
|
||||
border-radius: 0 0 .3em .3em;
|
||||
}
|
||||
|
||||
.alarmCue span {
|
||||
background-color:red;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
span.noneCue {
|
||||
background: none;
|
||||
}
|
||||
|
||||
#eventVideo {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#dataBar {
|
||||
width: 100%;
|
||||
margin: 2px auto;
|
||||
|
@ -6,6 +42,7 @@
|
|||
|
||||
#dataBar #dataTable {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
#dataBar #dataTable td {
|
||||
|
@ -66,6 +103,8 @@
|
|||
}
|
||||
|
||||
#imageFeed {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
@ -130,39 +169,30 @@
|
|||
|
||||
#progressBar {
|
||||
position: relative;
|
||||
border: 1px solid #666666;
|
||||
height: 15px;
|
||||
margin: 0 auto;
|
||||
top: -1.25em;
|
||||
height: 1.25em;
|
||||
margin: 0 auto -1.25em auto;
|
||||
}
|
||||
|
||||
#progressBar .progressBox {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
height: 15px;
|
||||
background: #eeeeee;
|
||||
border-left: 1px solid #999999;
|
||||
}
|
||||
|
||||
#progressBar .complete {
|
||||
background: #aaaaaa;
|
||||
transition: width .1s;
|
||||
height: 100%;
|
||||
background: rgba(170, 170, 170, .7);
|
||||
border-radius: 0 0 .3em .3em;
|
||||
}
|
||||
|
||||
#eventStills {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#eventThumbsPanel {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin: 4px auto;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#eventThumbs {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
height: 300px;
|
||||
}
|
||||
|
@ -181,14 +211,19 @@
|
|||
}
|
||||
|
||||
#eventImagePanel {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#eventImageFrame {
|
||||
border: 2px solid gray;
|
||||
background-color: white;
|
||||
padding: 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#eventImage {
|
||||
|
@ -198,6 +233,14 @@
|
|||
margin-top: 2px;
|
||||
}
|
||||
|
||||
#eventImageBar::after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
content: "";
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
#eventImageStats {
|
||||
float: left;
|
||||
}
|
||||
|
@ -213,6 +256,7 @@
|
|||
|
||||
#eventImageNav {
|
||||
position: relative;
|
||||
margin: 0 0 4px 0;
|
||||
}
|
||||
|
||||
#eventImageNav input {
|
||||
|
@ -222,19 +266,25 @@
|
|||
|
||||
#thumbsSliderPanel {
|
||||
width: 400px;
|
||||
margin: 4px auto 0;
|
||||
background: #888888;
|
||||
padding: 1px;
|
||||
width: 80%;
|
||||
margin: 0px auto 4px auto;
|
||||
}
|
||||
|
||||
#thumbsSlider {
|
||||
width: 400px;
|
||||
height: 10px;
|
||||
background: #dddddd;
|
||||
width: 100%;
|
||||
height: 1.25em;
|
||||
position: relative;
|
||||
top: -1.25em;
|
||||
margin: 0 0 -1.25em 0;
|
||||
}
|
||||
|
||||
#eventVideo {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#thumbsKnob {
|
||||
width: 8px;
|
||||
height: 10px;
|
||||
background-color: #444444;
|
||||
width: 1em;
|
||||
height: 100%;
|
||||
background-color: #999999;
|
||||
}
|
||||
|
|
|
@ -1,33 +1,16 @@
|
|||
#scaleControl {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#controls {
|
||||
width: 80%;
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#controls a {
|
||||
width: 40px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
#firstLink {
|
||||
position: absolute;
|
||||
left: 13%;
|
||||
}
|
||||
|
||||
#prevLink {
|
||||
position: absolute;
|
||||
left: 37%;
|
||||
}
|
||||
|
||||
#nextLink {
|
||||
position: absolute;
|
||||
left: 63%;
|
||||
}
|
||||
|
||||
#lastLink {
|
||||
position: absolute;
|
||||
left: 87%;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,34 @@
|
|||
#content .vjsMessage {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
margin: 0;
|
||||
font-size: 200%;
|
||||
color: white;
|
||||
background-color: black;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.alarmCue {
|
||||
background-color: #222222;
|
||||
height: 1.25em;
|
||||
text-align: left;
|
||||
margin: 0 auto 0 auto;
|
||||
border-radius: 0 0 .3em .3em;
|
||||
}
|
||||
|
||||
.alarmCue span {
|
||||
background-color:red;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
span.noneCue {
|
||||
background: none;
|
||||
}
|
||||
|
||||
#dataBar {
|
||||
width: 100%;
|
||||
margin: 2px auto;
|
||||
|
@ -6,6 +37,7 @@
|
|||
|
||||
#dataBar #dataTable {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
#dataBar #dataTable td {
|
||||
|
@ -64,7 +96,10 @@
|
|||
clear: both;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#imageFeed {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
@ -135,39 +170,30 @@
|
|||
|
||||
#progressBar {
|
||||
position: relative;
|
||||
border: 1px solid #666666;
|
||||
height: 15px;
|
||||
margin: 0 auto;
|
||||
top: -1.25em;
|
||||
height: 1.25em;
|
||||
margin: 0 auto -1.25em auto;
|
||||
}
|
||||
|
||||
#progressBar .progressBox {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
height: 15px;
|
||||
background: #eeeeee;
|
||||
border-left: 1px solid #999999;
|
||||
}
|
||||
|
||||
#progressBar .complete {
|
||||
background: #aaaaaa;
|
||||
transition: width .1s;
|
||||
height: 100%;
|
||||
background: rgba(170, 170, 170, .7);
|
||||
border-radius: 0 0 .3em .3em;
|
||||
}
|
||||
|
||||
#eventStills {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#eventThumbsPanel {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin: 4px auto;
|
||||
margin: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#eventThumbs {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
height: 300px;
|
||||
}
|
||||
|
@ -186,14 +212,19 @@
|
|||
}
|
||||
|
||||
#eventImagePanel {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#eventImageFrame {
|
||||
border: 2px solid gray;
|
||||
background-color: white;
|
||||
padding: 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#eventImage {
|
||||
|
@ -216,8 +247,17 @@
|
|||
float: right;
|
||||
}
|
||||
|
||||
#eventImageBar::after {
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
content: "";
|
||||
clear: both;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
#eventImageNav {
|
||||
position: relative;
|
||||
margin: 0 0 4px 0;
|
||||
}
|
||||
|
||||
#eventImageNav input {
|
||||
|
@ -226,24 +266,25 @@
|
|||
}
|
||||
|
||||
#thumbsSliderPanel {
|
||||
width: 400px;
|
||||
margin: 4px auto 0;
|
||||
background: #888888;
|
||||
padding: 1px;
|
||||
width: 80%;
|
||||
margin: 0px auto 4px auto;
|
||||
}
|
||||
|
||||
#thumbsSlider {
|
||||
width: 400px;
|
||||
height: 10px;
|
||||
background: #dddddd;
|
||||
width: 100%;
|
||||
height: 1.25em;
|
||||
position: relative;
|
||||
top: -1.25em;
|
||||
margin: 0 0 -1.25em 0;
|
||||
}
|
||||
|
||||
#thumbsKnob {
|
||||
width: 8px;
|
||||
height: 10px;
|
||||
background-color: #444444;
|
||||
width: 1em;
|
||||
height: 100%;
|
||||
background-color: #999999;
|
||||
}
|
||||
#eventVideo {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
@ -274,10 +315,6 @@
|
|||
);
|
||||
}
|
||||
|
||||
#eventVideo:hover #video-controls {
|
||||
opacity: .9;
|
||||
}
|
||||
|
||||
button {
|
||||
background: rgba(0,0,0,.5);
|
||||
border: 0;
|
||||
|
|
|
@ -6,29 +6,11 @@
|
|||
width: 80%;
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#controls a {
|
||||
width: 40px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
#firstLink {
|
||||
position: absolute;
|
||||
left: 13%;
|
||||
}
|
||||
|
||||
#prevLink {
|
||||
position: absolute;
|
||||
left: 37%;
|
||||
}
|
||||
|
||||
#nextLink {
|
||||
position: absolute;
|
||||
left: 63%;
|
||||
}
|
||||
|
||||
#lastLink {
|
||||
position: absolute;
|
||||
left: 87%;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ $rates = array(
|
|||
);
|
||||
|
||||
$scales = array(
|
||||
"auto" => translate("Scale to Fit"),
|
||||
"400" => "4x",
|
||||
"300" => "3x",
|
||||
"200" => "2x",
|
||||
|
@ -43,6 +44,8 @@ $scales = array(
|
|||
"12.5" => "1/8x",
|
||||
);
|
||||
|
||||
if (isset($_REQUEST['view']) && ($_REQUEST['view'] == 'montage')) unset($scales['auto']); //Remove auto if we aren't using event view
|
||||
|
||||
$bwArray = array(
|
||||
"high" => translate('High'),
|
||||
"medium" => translate('Medium'),
|
||||
|
|
|
@ -92,6 +92,7 @@ var $j = jQuery.noConflict();
|
|||
<?php } else if ( $title == 'Event' ) {
|
||||
?>
|
||||
<link href="skins/<?php echo $skin ?>/js/video-js.css" rel="stylesheet">
|
||||
<link href="skins/<?php echo $skin ?>/js/video-js-skin.css" rel="stylesheet">
|
||||
<script src="skins/<?php echo $skin ?>/js/video.js"></script>
|
||||
<script src="./js/videojs.zoomrotate.js"></script>
|
||||
<script src="skins/<?php echo $skin ?>/js/moment.min.js"></script>
|
||||
|
|
|
@ -262,6 +262,17 @@ function convertLabelFormat(LabelFormat, monitorName){
|
|||
}
|
||||
|
||||
function addVideoTimingTrack(video, LabelFormat, monitorName, duration, startTime){
|
||||
//This is a hacky way to handle changing the texttrack. If we ever upgrade vjs in a revamp replace this. Old method preserved because it's the right way.
|
||||
let cues = vid.textTracks()[0].cues();
|
||||
let labelFormat = convertLabelFormat(LabelFormat, monitorName);
|
||||
startTime = moment(startTime);
|
||||
|
||||
for (let i = 0; i <= duration; i++) {
|
||||
cues[i] = {id: i, index: i, startTime: i, Ca: i+1, text: startTime.format(labelFormat)};
|
||||
startTime.add(1, 's');
|
||||
}
|
||||
}
|
||||
/*
|
||||
var labelFormat = convertLabelFormat(LabelFormat, monitorName);
|
||||
var webvttformat = 'HH:mm:ss.SSS', webvttdata="WEBVTT\n\n";
|
||||
|
||||
|
@ -283,3 +294,37 @@ function addVideoTimingTrack(video, LabelFormat, monitorName, duration, startTim
|
|||
track.src = 'data:plain/text;charset=utf-8,'+encodeURIComponent(webvttdata);
|
||||
video.appendChild(track);
|
||||
}
|
||||
*/
|
||||
|
||||
var resizeTimer;
|
||||
|
||||
function endOfResize(e) {
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(changeScale, 250);
|
||||
}
|
||||
|
||||
function scaleToFit (baseWidth, baseHeight, scaleEl, bottomEl) {
|
||||
$j(window).on('resize', endOfResize) //set delayed scaling when Scale to Fit is selected
|
||||
let ratio = baseWidth / baseHeight;
|
||||
let container = $j('#content');
|
||||
let viewPort = $j(window);
|
||||
// jquery does not provide a bottom offet, and offset dows not include margins. outerHeight true minus false gives total vertical margins.
|
||||
let bottomLoc = bottomEl.offset().top + (bottomEl.outerHeight(true) - bottomEl.outerHeight()) + bottomEl.outerHeight(true);
|
||||
let newHeight = viewPort.height() - (bottomLoc - scaleEl.outerHeight(true))
|
||||
let newWidth = ratio * newHeight;
|
||||
if (newWidth > container.innerWidth()) {
|
||||
newWidth = container.innerWidth();
|
||||
newHeight = newWidth / ratio;
|
||||
}
|
||||
let autoScale = Math.round(newWidth / baseWidth * SCALE_BASE);
|
||||
let scales = $j('#scale option').map(function() {return parseInt($j(this).val());}).get();
|
||||
scales.shift();
|
||||
let closest;
|
||||
$j(scales).each(function () { //Set zms scale to nearest regular scale. Zoom does not like arbitrary scale values.
|
||||
if (closest == null || Math.abs(this - autoScale) < Math.abs(closest - autoScale)) {
|
||||
closest = this.valueOf();
|
||||
}
|
||||
});
|
||||
autoScale = closest;
|
||||
return {width: Math.floor(newWidth), height: Math.floor(newHeight), autoScale: autoScale};
|
||||
}
|
||||
|
|
|
@ -39,4 +39,4 @@ var refreshParent = <?php echo !empty($refreshParent)?'true':'false' ?>;
|
|||
|
||||
var focusWindow = <?php echo !empty($focusWindow)?'true':'false' ?>;
|
||||
|
||||
var imagePrefix = "<?php echo viewImagePath( "", '&' ) ?>";
|
||||
var imagePrefix = "<?php echo "?view=image&eid=" ?>";
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
.vjs-tech {
|
||||
pointer-events: none;
|
||||
transition: transform .25s;
|
||||
}
|
||||
|
||||
.vjs-captions-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vjs-tt-cue {
|
||||
margin-bottom: .75em;
|
||||
}
|
||||
|
||||
.vjs-menu {
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.vjs-default-skin .vjs-playback-rate.vjs-menu-button .vjs-menu .vjs-menu-content {
|
||||
width: 5em;
|
||||
}
|
||||
|
||||
.vjs-default-skin.vjs-user-inactive:hover .vjs-progress-control {
|
||||
font-size: .3em;
|
||||
}
|
||||
|
||||
.vjs-default-skin.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
bottom: -2em;
|
||||
-webkit-transition: all .2s;
|
||||
-moz-transition: all .2s;
|
||||
-o-transition: all .2s;
|
||||
transition: all .2s
|
||||
}
|
||||
|
||||
.vjs-default-skin.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control,
|
||||
.vjs-default-skin.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-time-divider {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vjs-default-skin .vjs-progress-holder .vjs-play-progress, .vjs-default-skin .vjs-progress-holder .vjs-load-progress {
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.vjs-progress-holder.vjs-slider {
|
||||
background-color: transparent;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.vjs-progress-control.vjs-control {
|
||||
background-color: rgba(7, 20, 30, 0.8);
|
||||
height: 1.3em;
|
||||
top: -1.3em;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.vjs-control div.alarmCue {
|
||||
position: relative;
|
||||
top: -1.3em;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.vjs-control .alarmCue {
|
||||
height: 1.3em;
|
||||
}
|
|
@ -43,6 +43,8 @@ else
|
|||
|
||||
if ( isset( $_REQUEST['scale'] ) ) {
|
||||
$scale = validInt($_REQUEST['scale']);
|
||||
} else if ( isset( $_COOKIE['zmEventScaleAuto'] ) ) { //If we're using scale to fit use it on all monitors
|
||||
$scale = 'auto';
|
||||
} else if ( isset( $_COOKIE['zmEventScale'.$Event->MonitorId()] ) ) {
|
||||
$scale = $_COOKIE['zmEventScale'.$Event->MonitorId()];
|
||||
} else {
|
||||
|
@ -61,6 +63,7 @@ if ( isset( $_REQUEST['streamMode'] ) )
|
|||
else
|
||||
$streamMode = 'video';
|
||||
|
||||
$replayMode = '';
|
||||
if ( isset( $_REQUEST['replayMode'] ) )
|
||||
$replayMode = validHtmlStr($_REQUEST['replayMode']);
|
||||
if ( isset( $_COOKIE['replayMode']) && preg_match('#^[a-z]+$#', $_COOKIE['replayMode']) )
|
||||
|
@ -84,10 +87,6 @@ parseSort();
|
|||
parseFilter( $_REQUEST['filter'] );
|
||||
$filterQuery = $_REQUEST['filter']['query'];
|
||||
|
||||
$panelSections = 40;
|
||||
$panelSectionWidth = (int)ceil(reScale($Event->Width(),$scale)/$panelSections);
|
||||
$panelWidth = ($panelSections*$panelSectionWidth-1);
|
||||
|
||||
$connkey = generateConnKey();
|
||||
|
||||
$focusWindow = true;
|
||||
|
@ -126,24 +125,25 @@ if ( canEdit( 'Events' ) ) {
|
|||
?>
|
||||
<div id="deleteEvent"><a href="#" onclick="deleteEvent()"><?php echo translate('Delete') ?></a></div>
|
||||
<div id="editEvent"><a href="#" onclick="editEvent()"><?php echo translate('Edit') ?></a></div>
|
||||
<div id="archiveEvent" class="hidden"><a href="#" onclick="archiveEvent()"><?php echo translate('Archive') ?></a></div>
|
||||
<div id="unarchiveEvent" class="hidden"><a href="#" onclick="unarchiveEvent()"><?php echo translate('Unarchive') ?></a></div>
|
||||
<?php
|
||||
<div id="archiveEvent"<?php echo $Event->Archived == 1 ? ' class="hidden"' : '' ?>><a href="#" onclick="archiveEvent()"><?php echo translate('Archive') ?></a></div>
|
||||
<div id="unarchiveEvent"<?php echo $Event->Archived == 0 ? ' class="hidden"' : '' ?>><a href="#" onclick="unarchiveEvent()"><?php echo translate('Unarchive') ?></a></div>
|
||||
<?php
|
||||
} // end if can edit Events
|
||||
if ( $Event->DefaultVideo() ) { ?>
|
||||
<div id="downloadEventFile"><a href="<?php echo $Event->getStreamSrc()?>">Download MP4</a></div>
|
||||
?>
|
||||
<div id="framesEvent"><a href="#" onclick="showEventFrames()"><?php echo translate('Frames') ?></a></div>
|
||||
<div id="streamEvent" class="hidden"><a href="#" onclick="showStream()"><?php echo translate('Stream') ?></a></div>
|
||||
<div id="stillsEvent"><a href="#" onclick="showStills()"><?php echo translate('Stills') ?></a></div>
|
||||
<?php
|
||||
if ( $Event->DefaultVideo() ) {
|
||||
?>
|
||||
<div id="downloadEventFile"><a href="<?php echo $Event->getStreamSrc(array('mode'=>'mp4'))?>" download>Download MP4</a></div>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<div id="videoEvent"><a href="#" onclick="videoEvent();"><?php echo translate('Video') ?></a></div>
|
||||
<?php
|
||||
} // end if Event->DefaultVideo
|
||||
?>
|
||||
<div id="framesEvent"><a href="#" onclick="showEventFrames()"><?php echo translate('Frames') ?></a></div>
|
||||
<?php
|
||||
if ( $Event->SaveJPEGs() & 3 ) { // Analysis or Jpegs
|
||||
?>
|
||||
<div id="stillsEvent"<?php if ( $streamMode == 'still' ) { ?> class="hidden"<?php } ?>><a href="#" onclick="showStills()"><?php echo translate('Stills') ?></a></div>
|
||||
<?php
|
||||
} // has frames or analysis
|
||||
?>
|
||||
<div id="videoEvent"><a href="#" onclick="videoEvent();"><?php echo translate('Video') ?></a></div>
|
||||
<div id="exportEvent"><a href="#" onclick="exportEvent();"><?php echo translate('Export') ?></a></div>
|
||||
</div>
|
||||
<div id="eventVideo" class="">
|
||||
|
@ -151,23 +151,17 @@ if ( $Event->SaveJPEGs() & 3 ) { // Analysis or Jpegs
|
|||
if ( $Event->DefaultVideo() ) {
|
||||
?>
|
||||
<div id="videoFeed">
|
||||
<video id="videoobj" class="video-js vjs-default-skin" width="<?php echo reScale( $Event->Width(), $scale ) ?>" height="<?php echo reScale( $Event->Height(), $scale ) ?>" data-setup='{ "controls": true, "playbackRates": [0.5, 1, 1.5, 2, 4, 8, 16, 32, 64, 128, 256], "autoplay": true, "preload": "auto", "plugins": { "zoomrotate": { "zoom": "<?php echo $Zoom ?>"}}}'>
|
||||
<video id="videoobj" class="video-js vjs-default-skin" style="transform: matrix(1, 0, 0, 1, 0, 0)" width="<?php echo reScale( $Event->Width(), $scale ) ?>" height="<?php echo reScale( $Event->Height(), $scale ) ?>" data-setup='{ "controls": true, "autoplay": true, "preload": "auto", "plugins": { "zoomrotate": { "zoom": "<?php echo $Zoom ?>"}}}'>
|
||||
<source src="<?php echo $Event->getStreamSrc( array( 'mode'=>'mpeg','format'=>'h264' ) ); ?>" type="video/mp4">
|
||||
<track id="monitorCaption" kind="captions" label="English" srclang="en" src='data:plain/text;charset=utf-8,"WEBVTT\n\n 00:00:00.000 --> 00:00:01.000 ZoneMinder"' default>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
<!--script>includeVideoJs();</script-->
|
||||
<script>
|
||||
var LabelFormat = "<?php echo validJsStr($Monitor->LabelFormat())?>";
|
||||
var monitorName = "<?php echo validJsStr($Monitor->Name())?>";
|
||||
var duration = <?php echo $Event->Length() ?>, startTime = '<?php echo $Event->StartTime() ?>';
|
||||
|
||||
addVideoTimingTrack(document.getElementById('videoobj'), LabelFormat, monitorName, duration, startTime);
|
||||
</script>
|
||||
</div><!--videoFeed-->
|
||||
<?php
|
||||
} // end if DefaultVideo
|
||||
?>
|
||||
<div id="imageFeed" <?php if ( $Event->DefaultVideo() ) { ?>class="hidden"<?php } ?> >
|
||||
<?php if (!$Event->DefaultVideo()) { ?>
|
||||
<div id="imageFeed">
|
||||
<?php
|
||||
if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
|
||||
$streamSrc = $Event->getStreamSrc( array( 'mode'=>'mpeg', 'scale'=>$scale, 'rate'=>$rate, 'bitrate'=>ZM_WEB_VIDEO_BITRATE, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'format'=>ZM_MPEG_REPLAY_FORMAT, 'replay'=>$replayMode ) );
|
||||
|
@ -181,34 +175,30 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
|
|||
}
|
||||
} // end if stream method
|
||||
?>
|
||||
</div>
|
||||
<div id="alarmCue" class="alarmCue"></div>
|
||||
<div id="progressBar" style="width: <?php echo reScale($Event->Width(), $scale);?>px;">
|
||||
<div class="progressBox" id="progressBox" title="" style="width: 0%;"></div>
|
||||
</div><!--progressBar-->
|
||||
</div><!--imageFeed-->
|
||||
<?php } /*end if !DefaultVideo*/ ?>
|
||||
<p id="dvrControls">
|
||||
<input type="button" value="<+" id="prevBtn" title="<?php echo translate('Prev') ?>" class="inactive" onclick="streamPrev( true );"/>
|
||||
<input type="button" value="<<" id="fastRevBtn" title="<?php echo translate('Rewind') ?>" class="inactive" disabled="disabled" onclick="streamFastRev( true );"/>
|
||||
<input type="button" value="<<" id="fastRevBtn" title="<?php echo translate('Rewind') ?>" class="inactive" onclick="streamFastRev( true );"/>
|
||||
<input type="button" value="<" id="slowRevBtn" title="<?php echo translate('StepBack') ?>" class="unavail" disabled="disabled" onclick="streamSlowRev( true );"/>
|
||||
<input type="button" value="||" id="pauseBtn" title="<?php echo translate('Pause') ?>" class="inactive" onclick="pauseClicked();"/>
|
||||
<input type="button" value="|>" id="playBtn" title="<?php echo translate('Play') ?>" class="active" disabled="disabled" onclick="playClicked();"/>
|
||||
<input type="button" value=">" id="slowFwdBtn" title="<?php echo translate('StepForward') ?>" class="unavail" disabled="disabled" onclick="streamSlowFwd( true );"/>
|
||||
<input type="button" value=">>" id="fastFwdBtn" title="<?php echo translate('FastForward') ?>" class="inactive" disabled="disabled" onclick="streamFastFwd( true );"/>
|
||||
<input type="button" value="–" id="zoomOutBtn" title="<?php echo translate('ZoomOut') ?>" class="avail" onclick="streamZoomOut();"/>
|
||||
<input type="button" value=">>" id="fastFwdBtn" title="<?php echo translate('FastForward') ?>" class="inactive" onclick="streamFastFwd( true );"/>
|
||||
<input type="button" value="–" id="zoomOutBtn" title="<?php echo translate('ZoomOut') ?>" class="unavail" disabled="disabled" onclick="streamZoomOut();"/>
|
||||
<input type="button" value="+>" id="nextBtn" title="<?php echo translate('Next') ?>" class="inactive" onclick="streamNext( true );"/>
|
||||
</p>
|
||||
<div id="replayStatus">
|
||||
<span id="mode"><?php echo translate('Mode') ?>: <span id="modeValue"> </span></span>
|
||||
<span id="rate"><?php echo translate('Rate') ?>: <span id="rateValue"></span>x</span>
|
||||
<span id="progress"><?php echo translate('Progress') ?>: <span id="progressValue"></span>s</span>
|
||||
<span id="zoom"><?php echo translate('Zoom') ?>: <span id="zoomValue"></span>x</span>
|
||||
<span id="mode"><?php echo translate('Mode') ?>: <span id="modeValue">Replay</span></span>
|
||||
<span id="rate"><?php echo translate('Rate') ?>: <span id="rateValue"><?php echo $rate/100 ?></span>x</span>
|
||||
<span id="progress"><?php echo translate('Progress') ?>: <span id="progressValue">0</span>s</span>
|
||||
<span id="zoom"><?php echo translate('Zoom') ?>: <span id="zoomValue">1</span>x</span>
|
||||
</div>
|
||||
<div id="progressBar" class="invisible">
|
||||
<?php for ( $i = 0; $i < $panelSections; $i++ ) { ?>
|
||||
<div class="progressBox" id="progressBox<?php echo $i ?>" title=""></div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
if ($Event->SaveJPEGs() & 3) { // frames or analysis
|
||||
?>
|
||||
</div><!--eventVideo-->
|
||||
<div id="eventStills" class="hidden">
|
||||
<div id="eventThumbsPanel">
|
||||
<div id="eventThumbs">
|
||||
|
@ -225,6 +215,13 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
|
|||
</div>
|
||||
</div>
|
||||
<div id="eventImageNav">
|
||||
<div id="thumbsSliderPanel">
|
||||
<div id="alarmCue" class="alarmCue"></div>
|
||||
<div id="thumbsSlider">
|
||||
<div id="thumbsKnob">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="eventImageButtons">
|
||||
<div id="prevButtonsPanel">
|
||||
<input id="prevEventBtn" type="button" value="<E" onclick="prevEvent()" disabled="disabled"/>
|
||||
|
@ -235,18 +232,12 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
|
|||
<input id="nextEventBtn" type="button" value="E>" onclick="nextEvent()" disabled="disabled"/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="thumbsSliderPanel">
|
||||
<div id="thumbsSlider">
|
||||
<div id="thumbsKnob">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
} // end if SaveJPEGs() & 3 Analysis or Jpegs
|
||||
} // end if Event exists
|
||||
?>
|
||||
</div><!--content-->
|
||||
</div><!--page-->
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -51,7 +51,7 @@ $lastFid = $maxFid;
|
|||
$alarmFrame = $Frame->Type()=='Alarm';
|
||||
|
||||
if ( isset( $_REQUEST['scale'] ) ) {
|
||||
$scale = validInt($_REQUEST['scale']);
|
||||
$scale = $_REQUEST['scale'];
|
||||
} else if ( isset( $_COOKIE['zmWatchScale'.$Monitor->Id()] ) ) {
|
||||
$scale = $_COOKIE['zmWatchScale'.$Monitor->Id()];
|
||||
} else if ( isset( $_COOKIE['zmWatchScale'] ) ) {
|
||||
|
|
|
@ -1,31 +1,179 @@
|
|||
var vid = null;
|
||||
|
||||
function vjsReplay() {
|
||||
let endTime = (Date.parse(eventData.EndTime)).getTime();
|
||||
switch(replayMode.value) {
|
||||
case 'none':
|
||||
break;
|
||||
case 'single':
|
||||
player.play();
|
||||
break;
|
||||
case 'all':
|
||||
if (nextEventId == 0) {
|
||||
let overLaid = $j("#videoobj");
|
||||
overLaid.append('<p class="vjsMessage" style="height: '+overLaid.height()+'px; line-height: '+overLaid.height()+'px;">No more events</p>');
|
||||
} else {
|
||||
let nextStartTime = nextEventStartTime.getTime(); //nextEventStartTime.getTime() is a mootools workaround, highjacks Date.parse
|
||||
if (nextStartTime <= endTime) {
|
||||
streamNext( true );
|
||||
return;
|
||||
}
|
||||
let overLaid = $j("#videoobj");
|
||||
vid.pause();
|
||||
overLaid.append('<p class="vjsMessage" style="height: '+overLaid.height()+'px; line-height: '+overLaid.height()+'px;"></p>');
|
||||
let gapDuration = (new Date().getTime()) + (nextStartTime - endTime);
|
||||
let messageP = $j(".vjsMessage");
|
||||
let x = setInterval(function() {
|
||||
let now = new Date().getTime();
|
||||
let remainder = new Date(Math.round(gapDuration - now)).toISOString().substr(11,8);
|
||||
messageP.html(remainder + ' to next event.');
|
||||
if (remainder < 0) {
|
||||
clearInterval(x);
|
||||
streamNext( true );
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
break;
|
||||
case 'gapless':
|
||||
streamNext( true );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$j.ajaxSetup ({timeout: AJAX_TIMEOUT }); //sets timeout for all getJSON.
|
||||
|
||||
var cueFrames = null; //make cueFrames availaible even if we don't send another ajax query
|
||||
|
||||
function initialAlarmCues (eventId) {
|
||||
$j.getJSON(thisUrl + '?view=request&request=status&entity=frames&id=' + eventId, setAlarmCues); //get frames data for alarmCues and inserts into html
|
||||
}
|
||||
|
||||
function setAlarmCues (data) {
|
||||
cueFrames = data.frames;
|
||||
alarmSpans = renderAlarmCues(vid ? $j("#videoobj") : $j("#evtStream"));//use videojs width or zms width
|
||||
$j(".alarmCue").html(alarmSpans);
|
||||
}
|
||||
|
||||
function renderAlarmCues (containerEl) {
|
||||
if (cueFrames) {
|
||||
var cueRatio = containerEl.width() / (cueFrames[cueFrames.length - 1].Delta * 100);
|
||||
var minAlarm = Math.ceil(1/cueRatio);
|
||||
var spanTimeStart = 0;
|
||||
var spanTimeEnd = 0;
|
||||
var alarmed = 0;
|
||||
var alarmHtml = "";
|
||||
var pixSkew = 0;
|
||||
var skip = 0;
|
||||
for (let i = 0; i < cueFrames.length; i++) {
|
||||
skip = 0;
|
||||
frame = cueFrames[i];
|
||||
if (frame.Type == "Alarm" && alarmed == 0) { //From nothing to alarm. End nothing and start alarm.
|
||||
alarmed = 1;
|
||||
if (frame.Delta == 0) continue; //If event starts with an alarm or too few for a nonespan
|
||||
spanTimeEnd = frame.Delta * 100;
|
||||
spanTime = spanTimeEnd - spanTimeStart;
|
||||
let pix = cueRatio * spanTime;
|
||||
pixSkew += pix - Math.round(pix);//average out the rounding errors.
|
||||
pix = Math.round(pix);
|
||||
if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) { //add skew if it's a pixel and won't zero out span.
|
||||
pix += Math.round(pixSkew);
|
||||
pixSkew = pixSkew - Math.round(pixSkew);
|
||||
}
|
||||
alarmHtml += '<span class="alarmCue noneCue" style="width: ' + pix + 'px;"></span>';
|
||||
spanTimeStart = spanTimeEnd;
|
||||
} else if (frame.Type !== "Alarm" && alarmed == 1) { //from alarm to nothing. End alarm and start nothing.
|
||||
futNone = 0;
|
||||
indexPlus = i+1;
|
||||
if (((frame.Delta * 100) - spanTimeStart) < minAlarm && indexPlus < cueFrames.length) continue; //alarm is too short and there is more event
|
||||
while (futNone < minAlarm) { //check ahead to see if there's enough for a nonespan
|
||||
if (indexPlus >= cueFrames.length) break; //check if end of event.
|
||||
futNone = (cueFrames[indexPlus].Delta *100) - (frame.Delta *100);
|
||||
if (cueFrames[indexPlus].Type == "Alarm") {
|
||||
i = --indexPlus;
|
||||
skip = 1;
|
||||
break;
|
||||
}
|
||||
indexPlus++;
|
||||
}
|
||||
if (skip == 1) continue; //javascript doesn't support continue 2;
|
||||
spanTimeEnd = frame.Delta *100;
|
||||
spanTime = spanTimeEnd - spanTimeStart;
|
||||
alarmed = 0;
|
||||
pix = cueRatio * spanTime;
|
||||
pixSkew += pix - Math.round(pix);
|
||||
pix = Math.round(pix);
|
||||
if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) {
|
||||
pix += Math.round(pixSkew);
|
||||
pixSkew = pixSkew - Math.round(pixSkew);
|
||||
}
|
||||
alarmHtml += '<span class="alarmCue" style="width: ' + pix + 'px;"></span>';
|
||||
spanTimeStart = spanTimeEnd;
|
||||
} else if (frame.Type == "Alarm" && alarmed == 1 && i + 1 >= cueFrames.length) { //event ends on an alarm
|
||||
spanTimeEnd = frame.Delta * 100;
|
||||
spanTime = spanTimeEnd - spanTimeStart;
|
||||
alarmed = 0;
|
||||
pix = Math.round(cueRatio * spanTime);
|
||||
if (pixSkew >= .5 || pixSkew <= -.5) pix += Math.round(pixSkew);
|
||||
alarmHtml += '<span class="alarmCue" style="width: ' + pix + 'px;"></span>';
|
||||
}
|
||||
}
|
||||
return alarmHtml;
|
||||
}
|
||||
}
|
||||
|
||||
function setButtonState( element, butClass ) {
|
||||
if ( element ) {
|
||||
element.className = butClass;
|
||||
element.disabled = (butClass != 'inactive');
|
||||
if (butClass == 'unavail' || (butClass == 'active' && (element.id == 'pauseBtn' || element.id == 'playBtn'))) {
|
||||
element.disabled = true;
|
||||
} else {
|
||||
element.disabled = false;
|
||||
}
|
||||
} else {
|
||||
console.log("Element was null in setButtonState");
|
||||
}
|
||||
}
|
||||
|
||||
function changeScale() {
|
||||
var scale = $('scale').get('value');
|
||||
var baseWidth = eventData.Width;
|
||||
var baseHeight = eventData.Height;
|
||||
var newWidth = ( baseWidth * scale ) / SCALE_BASE;
|
||||
var newHeight = ( baseHeight * scale ) / SCALE_BASE;
|
||||
|
||||
if ( vid ) {
|
||||
// Using video.js
|
||||
vid.width = newWidth;
|
||||
vid.height = newHeight;
|
||||
} else {
|
||||
streamScale( scale );
|
||||
var streamImg = document.getElementById('evtStream');
|
||||
streamImg.style.width = newWidth + "px";
|
||||
streamImg.style.height = newHeight + "px";
|
||||
Cookie.write( 'zmEventScale'+eventData.MonitorId, scale, { duration: 10*365 } );
|
||||
let scale = $j('#scale').val();
|
||||
let newWidth;
|
||||
let newHeight;
|
||||
let autoScale;
|
||||
let eventViewer;
|
||||
let alarmCue = $j('div.alarmCue');
|
||||
let bottomEl = streamMode == 'stills' ? $j('#eventImageNav') : $j('#replayStatus');
|
||||
if (streamMode == 'stills') {
|
||||
eventViewer = $j('#eventThumbs');
|
||||
} else {
|
||||
eventViewer = $j(vid ? '#videoobj' : '#evtStream');
|
||||
}
|
||||
if (scale == "auto") {
|
||||
let newSize = scaleToFit(eventData.Width, eventData.Height, eventViewer, bottomEl);
|
||||
newWidth = newSize.width;
|
||||
newHeight = newSize.height;
|
||||
autoScale = newSize.autoScale;
|
||||
} else {
|
||||
$j(window).off('resize', endOfResize); //remove resize handler when Scale to Fit is not active
|
||||
newWidth = eventData.Width * scale / SCALE_BASE;
|
||||
newHeight = eventData.Height * scale / SCALE_BASE;
|
||||
}
|
||||
if (!(streamMode == 'stills')) eventViewer.width(newWidth); //stills handles its own width
|
||||
eventViewer.height(newHeight);
|
||||
if ( !vid ) { // zms needs extra sizing
|
||||
streamScale(scale == "auto" ? autoScale : scale);
|
||||
drawProgressBar();
|
||||
}
|
||||
if (streamMode == 'stills') {
|
||||
slider.autosize();
|
||||
alarmCue.html(renderAlarmCues($j('#thumbsSliderPanel')));
|
||||
} else {
|
||||
alarmCue.html(renderAlarmCues(eventViewer));//just re-render alarmCues. skip ajax call
|
||||
}
|
||||
if (scale == "auto") {
|
||||
Cookie.write('zmEventScaleAuto', 'auto', {duration: 10*365});
|
||||
}else{
|
||||
Cookie.write('zmEventScale'+eventData.MonitorId, scale, {duration: 10*365});
|
||||
Cookie.dispose('zmEventScaleAuto');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,33 +190,38 @@ var streamCmdTimer = null;
|
|||
|
||||
var streamStatus = null;
|
||||
var lastEventId = 0;
|
||||
var zmsBroke = false; //Use alternate navigation if zms has crashed
|
||||
|
||||
function getCmdResponse( respObj, respText ) {
|
||||
if ( checkStreamForErrors( "getCmdResponse", respObj ) )
|
||||
if ( checkStreamForErrors( "getCmdResponse", respObj ) ) {
|
||||
console.log('Got an error from getCmdResponse');
|
||||
zmsBroke = true;
|
||||
return;
|
||||
}
|
||||
|
||||
zmsBroke = false;
|
||||
|
||||
if ( streamCmdTimer )
|
||||
streamCmdTimer = clearTimeout( streamCmdTimer );
|
||||
|
||||
streamStatus = respObj.status;
|
||||
if (streamStatus.progress >= Math.round(parseFloat(eventData.Length))) streamStatus.progress = parseFloat(eventData.Length); //Limit progress to reality
|
||||
|
||||
var eventId = streamStatus.event;
|
||||
if ( eventId != lastEventId ) {
|
||||
if ( eventId != lastEventId && lastEventId != 0) { //Doesn't run on first load, prevents a double hit on event and nearEvents ajax
|
||||
eventQuery( eventId );
|
||||
initialAlarmCues(eventId);
|
||||
lastEventId = eventId;
|
||||
}
|
||||
if (lastEventId == 0) lastEventId = eventId; //Only fires on first load.
|
||||
if ( streamStatus.paused == true ) {
|
||||
$('modeValue').set( 'text', 'Paused' );
|
||||
$('rate').addClass( 'hidden' );
|
||||
streamPause( );
|
||||
} else {
|
||||
$('modeValue').set( 'text', "Replay" );
|
||||
$('rateValue').set( 'text', streamStatus.rate );
|
||||
$('rate').removeClass( 'hidden' );
|
||||
$j('#rateValue').html(streamStatus.rate);
|
||||
streamPlay( );
|
||||
}
|
||||
$('progressValue').set( 'text', secsToTime( parseInt(streamStatus.progress) ) );
|
||||
$('zoomValue').set( 'text', streamStatus.zoom );
|
||||
$j('#progressValue').html(secsToTime(parseInt(streamStatus.progress)));
|
||||
$j('#zoomValue').html(streamStatus.zoom);
|
||||
if ( streamStatus.zoom == "1.0" )
|
||||
setButtonState( $('zoomOutBtn'), 'unavail' );
|
||||
else
|
||||
|
@ -83,17 +236,29 @@ function getCmdResponse( respObj, respText ) {
|
|||
streamImg.src = streamImg.src.replace( /auth=\w+/i, 'auth='+streamStatus.auth );
|
||||
} // end if haev a new auth hash
|
||||
|
||||
streamCmdTimer = streamQuery.delay( streamTimeout );
|
||||
streamCmdTimer = streamQuery.delay( streamTimeout ); //Timeout is refresh rate for progressBox and time display
|
||||
}
|
||||
|
||||
var streamReq = new Request.JSON( { url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getCmdResponse } );
|
||||
|
||||
function pauseClicked( ) {
|
||||
streamReq.send( streamParms+"&command="+CMD_PAUSE );
|
||||
function pauseClicked() {
|
||||
if (vid) {
|
||||
vid.pause();
|
||||
} else {
|
||||
streamReq.send( streamParms+"&command="+CMD_PAUSE );
|
||||
streamPause();
|
||||
}
|
||||
}
|
||||
|
||||
function vjsPause () {
|
||||
stopFastRev();
|
||||
streamPause();
|
||||
}
|
||||
|
||||
// Called when stream becomes paused, just updates the button status
|
||||
function streamPause( ) {
|
||||
$j('#modeValue').html('Paused');
|
||||
$j('#rateValue').html('0');
|
||||
setButtonState( $('pauseBtn'), 'active' );
|
||||
setButtonState( $('playBtn'), 'inactive' );
|
||||
setButtonState( $('fastFwdBtn'), 'unavail' );
|
||||
|
@ -103,13 +268,28 @@ function streamPause( ) {
|
|||
}
|
||||
|
||||
function playClicked( ) {
|
||||
streamReq.send( streamParms+"&command="+CMD_PLAY );
|
||||
if (vid) {
|
||||
if (vid.paused()) {
|
||||
vid.play();
|
||||
} else {
|
||||
vjsPlay(); //handles fast forward and rewind
|
||||
}
|
||||
} else {
|
||||
streamReq.send( streamParms+"&command="+CMD_PLAY );
|
||||
streamPlay();
|
||||
}
|
||||
}
|
||||
|
||||
function vjsPlay () { //catches if we change mode programatically
|
||||
stopFastRev();
|
||||
$j('#rateValue').html(vid.playbackRate());
|
||||
streamPlay();
|
||||
}
|
||||
|
||||
function streamPlay( ) {
|
||||
$j('#modeValue').html('Replay');
|
||||
setButtonState( $('pauseBtn'), 'inactive' );
|
||||
if (streamStatus)
|
||||
setButtonState( $('playBtn'), streamStatus.rate==1?'active':'inactive' );
|
||||
setButtonState( $('playBtn'), 'active' );
|
||||
setButtonState( $('fastFwdBtn'), 'inactive' );
|
||||
setButtonState( $('slowFwdBtn'), 'unavail' );
|
||||
setButtonState( $('slowRevBtn'), 'unavail' );
|
||||
|
@ -119,35 +299,44 @@ function streamPlay( ) {
|
|||
function streamFastFwd( action ) {
|
||||
setButtonState( $('pauseBtn'), 'inactive' );
|
||||
setButtonState( $('playBtn'), 'inactive' );
|
||||
setButtonState( $('fastFwdBtn'), 'inactive' );
|
||||
setButtonState( $('fastFwdBtn'), 'active' );
|
||||
setButtonState( $('slowFwdBtn'), 'unavail' );
|
||||
setButtonState( $('slowRevBtn'), 'unavail' );
|
||||
setButtonState( $('fastRevBtn'), 'inactive' );
|
||||
streamReq.send( streamParms+"&command="+CMD_FASTFWD );
|
||||
if (vid) {
|
||||
if (revSpeed != .5) stopFastRev();
|
||||
vid.playbackRate(rates[rates.indexOf(vid.playbackRate()*100)-1]/100);
|
||||
if (rates.indexOf(vid.playbackRate()*100)-1 == -1) setButtonState($('fastFwdBtn'), 'unavail');
|
||||
$j('#rateValue').html(vid.playbackRate());
|
||||
} else {
|
||||
streamReq.send( streamParms+"&command="+CMD_FASTFWD );
|
||||
}
|
||||
}
|
||||
|
||||
var spf = Math.round((eventData.Length / eventData.Frames)*1000000 )/1000000;//Seconds per frame for videojs frame by frame.
|
||||
var intervalRewind;
|
||||
var revSpeed = .5;
|
||||
|
||||
function streamSlowFwd( action ) {
|
||||
setButtonState( $('pauseBtn'), 'inactive' );
|
||||
setButtonState( $('playBtn'), 'inactive' );
|
||||
setButtonState( $('fastFwdBtn'), 'unavail' );
|
||||
setButtonState( $('slowFwdBtn'), 'active' );
|
||||
setButtonState( $('slowRevBtn'), 'inactive' );
|
||||
setButtonState( $('fastRevBtn'), 'unavail' );
|
||||
streamReq.send( streamParms+"&command="+CMD_SLOWFWD );
|
||||
setButtonState( $('pauseBtn'), 'inactive' );
|
||||
setButtonState( $('slowFwdBtn'), 'inactive' );
|
||||
if (vid) {
|
||||
vid.currentTime(vid.currentTime() + spf);
|
||||
} else {
|
||||
streamReq.send( streamParms+"&command="+CMD_SLOWFWD );
|
||||
}
|
||||
}
|
||||
|
||||
function streamSlowRev( action ) {
|
||||
setButtonState( $('pauseBtn'), 'inactive' );
|
||||
setButtonState( $('playBtn'), 'inactive' );
|
||||
setButtonState( $('fastFwdBtn'), 'unavail' );
|
||||
setButtonState( $('slowFwdBtn'), 'inactive' );
|
||||
setButtonState( $('slowRevBtn'), 'active' );
|
||||
setButtonState( $('fastRevBtn'), 'unavail' );
|
||||
streamReq.send( streamParms+"&command="+CMD_SLOWREV );
|
||||
setButtonState( $('pauseBtn'), 'inactive' );
|
||||
setButtonState( $('slowRevBtn'), 'inactive' );
|
||||
if (vid) {
|
||||
vid.currentTime(vid.currentTime() - spf);
|
||||
} else {
|
||||
streamReq.send( streamParms+"&command="+CMD_SLOWREV );
|
||||
}
|
||||
}
|
||||
|
||||
function stopFastRev () {
|
||||
clearInterval(intervalRewind);
|
||||
vid.playbackRate(1);
|
||||
revSpeed = .5;
|
||||
}
|
||||
|
||||
function streamFastRev( action ) {
|
||||
|
@ -156,26 +345,121 @@ function streamFastRev( action ) {
|
|||
setButtonState( $('fastFwdBtn'), 'inactive' );
|
||||
setButtonState( $('slowFwdBtn'), 'unavail' );
|
||||
setButtonState( $('slowRevBtn'), 'unavail' );
|
||||
setButtonState( $('fastRevBtn'), 'inactive' );
|
||||
streamReq.send( streamParms+"&command="+CMD_FASTREV );
|
||||
setButtonState( $('fastRevBtn'), 'active' );
|
||||
if (vid) { //There is no reverse play with mp4. Set the speed to 0 and manualy set the time back.
|
||||
revSpeed = rates[rates.indexOf(revSpeed*100)-1]/100;
|
||||
if (rates.indexOf(revSpeed*100) == 0) {
|
||||
setButtonState( $('fastRevBtn'), 'unavail' );
|
||||
}
|
||||
clearInterval(intervalRewind);
|
||||
$j('#rateValue').html(-revSpeed);
|
||||
intervalRewind = setInterval(function() {
|
||||
if (vid.currentTime() <= 0) {
|
||||
clearInterval(intervalRewind);
|
||||
vid.pause();
|
||||
} else {
|
||||
vid.playbackRate(0);
|
||||
vid.currentTime(vid.currentTime() - (revSpeed/2)); //Half of reverse speed because our interval is 500ms.
|
||||
}
|
||||
}, 500); //500ms is a compromise between smooth reverse and realistic performance
|
||||
} else {
|
||||
streamReq.send( streamParms+"&command="+CMD_FASTREV );
|
||||
}
|
||||
}
|
||||
|
||||
function streamPrev( action ) {
|
||||
if ( action )
|
||||
streamReq.send( streamParms+"&command="+CMD_PREV );
|
||||
function streamPrev(action) {
|
||||
if (action) {
|
||||
$j(".vjsMessage").remove();
|
||||
if (vid && PrevEventDefVideoPath.indexOf("view_video") > 0) {
|
||||
CurEventDefVideoPath = PrevEventDefVideoPath;
|
||||
eventQuery(prevEventId);
|
||||
} else if (zmsBroke || (vid && PrevEventDefVideoPath.indexOf("view_video") < 0) || $j("#vjsMessage").length || PrevEventDefVideoPath.indexOf("view_video") > 0) {//zms broke, leaving videojs, last event, moving to videojs
|
||||
location.replace(thisUrl + '?view=event&eid=' + prevEventId + filterQuery + sortQuery);
|
||||
} else {
|
||||
streamReq.send(streamParms+"&command="+CMD_PREV);
|
||||
streamPlay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function streamNext( action ) {
|
||||
if ( action )
|
||||
streamReq.send( streamParms+"&command="+CMD_NEXT );
|
||||
function streamNext(action) {
|
||||
if (action) {
|
||||
$j(".vjsMessage").remove();//This shouldn't happen
|
||||
if (nextEventId == 0) { //handles deleting last event.
|
||||
pauseClicked();
|
||||
let hideContainer = $j('#eventVideo');
|
||||
let hideStream = $j(vid ? "#videoobj" : "#evtStream").height() + (vid ? 0 :$j("#progressBar").height());
|
||||
hideContainer.prepend('<p class="vjsMessage" style="height: ' + hideStream + 'px; line-height: ' + hideStream + 'px;">No more events</p>');
|
||||
if (vid == null) zmsBroke = true;
|
||||
return;
|
||||
}
|
||||
if (vid && NextEventDefVideoPath.indexOf("view_video") > 0) { //on and staying with videojs
|
||||
CurEventDefVideoPath = NextEventDefVideoPath;
|
||||
eventQuery(nextEventId);
|
||||
} else if (zmsBroke || (vid && NextEventDefVideoPath.indexOf("view_video") < 0) || NextEventDefVideoPath.indexOf("view_video") > 0) {//reload zms, leaving vjs, moving to vjs
|
||||
location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery);
|
||||
} else {
|
||||
streamReq.send(streamParms+"&command="+CMD_NEXT);
|
||||
streamPlay();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function vjsPanZoom (action, x, y) { //Pan and zoom with centering where the click occurs
|
||||
let outer = $j('#videoobj');
|
||||
let video = outer.children().first();
|
||||
let zoom = parseFloat($j('#zoomValue').html());
|
||||
let zoomRate = .5;
|
||||
let matrix = video.css('transform').split(',');
|
||||
let currentPanX = parseFloat(matrix[4]);
|
||||
let currentPanY = parseFloat(matrix[5]);
|
||||
let xDist = outer.width()/2 - x //Click distance from center of view
|
||||
let yDist = outer.height()/2 - y
|
||||
if (action == 'zoomOut') {
|
||||
zoom -= zoomRate;
|
||||
if (x && y) {
|
||||
x = (xDist + currentPanX)*((zoom-zoomRate)/zoom); // if ctrl-click Pan but use ratio of old zoom to new zoom for coords
|
||||
y = (yDist + currentPanY)*((zoom-zoomRate)/zoom);
|
||||
} else {
|
||||
x = currentPanX*((zoom-zoomRate)/zoom); //Leave zoom centered where it was
|
||||
y = currentPanY*((zoom-zoomRate)/zoom);
|
||||
}
|
||||
if (zoom <= 1) {
|
||||
zoom = 1;
|
||||
$j('#zoomOutBtn').attr('class', 'unavail').attr('disabled', 'disabled');
|
||||
}
|
||||
$j('#zoomValue').html(zoom);
|
||||
} else if (action == 'zoom') {
|
||||
zoom += zoomRate;
|
||||
x = (xDist + currentPanX)*(zoom/(zoom-zoomRate)); //Pan but use ratio of new zoom to old zoom for coords. Center on mouse click.
|
||||
y = (yDist + currentPanY)*(zoom/(zoom-zoomRate));
|
||||
$j('#zoomOutBtn').attr('class', 'inactive').removeAttr('disabled');
|
||||
$j('#zoomValue').html(zoom);
|
||||
} else if (action == 'pan') {
|
||||
x = xDist + currentPanX;
|
||||
y = yDist + currentPanY;
|
||||
}
|
||||
let limitX = ((zoom*outer.width()) - outer.width())/2; //Calculate outer bounds of video
|
||||
let limitY = ((zoom*outer.height()) - outer.height())/2;
|
||||
x = Math.min(Math.max((x),-limitX),limitX); //Limit pan to outer bounds of video
|
||||
y = Math.min(Math.max((y),-limitY),limitY);
|
||||
video.css('transform', 'matrix('+zoom+', 0, 0, '+zoom+', '+x+', '+y+')');
|
||||
}
|
||||
|
||||
function streamZoomIn( x, y ) {
|
||||
streamReq.send( streamParms+"&command="+CMD_ZOOMIN+"&x="+x+"&y="+y );
|
||||
if (vid) {
|
||||
vjsPanZoom('zoom', x, y);
|
||||
} else {
|
||||
streamReq.send( streamParms+"&command="+CMD_ZOOMIN+"&x="+x+"&y="+y );
|
||||
}
|
||||
}
|
||||
|
||||
function streamZoomOut() {
|
||||
streamReq.send( streamParms+"&command="+CMD_ZOOMOUT );
|
||||
if (vid) {
|
||||
vjsPanZoom('zoomOut');
|
||||
} else {
|
||||
streamReq.send( streamParms+"&command="+CMD_ZOOMOUT );
|
||||
}
|
||||
}
|
||||
|
||||
function streamScale( scale ) {
|
||||
|
@ -183,7 +467,11 @@ function streamScale( scale ) {
|
|||
}
|
||||
|
||||
function streamPan( x, y ) {
|
||||
streamReq.send( streamParms+"&command="+CMD_PAN+"&x="+x+"&y="+y );
|
||||
if (vid) {
|
||||
vjsPanZoom('pan', x, y);
|
||||
} else {
|
||||
streamReq.send( streamParms+"&command="+CMD_PAN+"&x="+x+"&y="+y );
|
||||
}
|
||||
}
|
||||
|
||||
function streamSeek( offset ) {
|
||||
|
@ -196,6 +484,8 @@ function streamQuery() {
|
|||
|
||||
var slider = null;
|
||||
var scroll = null;
|
||||
var currEventId = null;
|
||||
var CurEventDefVideoPath = null;
|
||||
|
||||
function getEventResponse( respObj, respText ) {
|
||||
if ( checkStreamForErrors( "getEventResponse", respObj ) )
|
||||
|
@ -220,7 +510,7 @@ function getEventResponse( respObj, respText ) {
|
|||
$('dataFrames').set( 'text', eventData.Frames+"/"+eventData.AlarmFrames );
|
||||
$('dataScore').set( 'text', eventData.TotScore+"/"+eventData.AvgScore+"/"+eventData.MaxScore );
|
||||
$('eventName').setProperty( 'value', eventData.Name );
|
||||
|
||||
history.replaceState(null, null, '?view=event&eid=' + eventData.Id + filterQuery + sortQuery);//if popup removed, check if this allows forward
|
||||
if ( canEditEvents ) {
|
||||
if ( parseInt(eventData.Archived) ) {
|
||||
$('archiveEvent').addClass( 'hidden' );
|
||||
|
@ -232,7 +522,18 @@ function getEventResponse( respObj, respText ) {
|
|||
}
|
||||
//var eventImg = $('eventImage');
|
||||
//eventImg.setStyles( { 'width': eventData.width, 'height': eventData.height } );
|
||||
drawProgressBar();
|
||||
if (vid && CurEventDefVideoPath) {
|
||||
vid.src({type: 'video/mp4', src: CurEventDefVideoPath}); //Currently mp4 is all we use
|
||||
initialAlarmCues(eventData.Id);//ajax and render, new event
|
||||
addVideoTimingTrack(vid, LabelFormat, eventData.MonitorName, eventData.Length, eventData.StartTime);
|
||||
CurEventDefVideoPath = null;
|
||||
$j('#modeValue').html('Replay');
|
||||
$j('#zoomValue').html('1');
|
||||
$j('#rateValue').html('1');
|
||||
vjsPanZoom('zoomOut');
|
||||
} else {
|
||||
drawProgressBar();
|
||||
}
|
||||
nearEventsQuery( eventData.Id );
|
||||
}
|
||||
|
||||
|
@ -245,6 +546,8 @@ function eventQuery( eventId ) {
|
|||
|
||||
var prevEventId = 0;
|
||||
var nextEventId = 0;
|
||||
var prevEventStartTime = 0;
|
||||
var nextEventStartTime = 0;
|
||||
var PrevEventDefVideoPath = "";
|
||||
var NextEventDefVideoPath = "";
|
||||
|
||||
|
@ -253,6 +556,8 @@ function getNearEventsResponse( respObj, respText ) {
|
|||
return;
|
||||
prevEventId = respObj.nearevents.PrevEventId;
|
||||
nextEventId = respObj.nearevents.NextEventId;
|
||||
prevEventStartTime = Date.parse(respObj.nearevents.PrevEventStartTime);
|
||||
nextEventStartTime = Date.parse(respObj.nearevents.NextEventStartTime);
|
||||
PrevEventDefVideoPath = respObj.nearevents.PrevEventDefVideoPath;
|
||||
NextEventDefVideoPath = respObj.nearevents.NextEventDefVideoPath;
|
||||
|
||||
|
@ -260,12 +565,14 @@ function getNearEventsResponse( respObj, respText ) {
|
|||
if ( prevEventBtn ) prevEventBtn.disabled = !prevEventId;
|
||||
var nextEventBtn = $('nextEventBtn');
|
||||
if ( nextEventBtn ) nextEventBtn.disabled = !nextEventId;
|
||||
$j('#prevBtn').prop('disabled', prevEventId == 0 ? true : false).attr('class', prevEventId == 0 ? 'unavail' : 'inactive');
|
||||
$j('#nextBtn').prop('disabled', nextEventId == 0 ? true : false).attr('class', nextEventId == 0 ? 'unavail' : 'inactive');
|
||||
}
|
||||
|
||||
var nearEventsReq = new Request.JSON( { url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getNearEventsResponse } );
|
||||
|
||||
function nearEventsQuery( eventId ) {
|
||||
var parms = "view=request&request=status&entity=nearevents&id="+eventId;
|
||||
var parms = "view=request&request=status&entity=nearevents&id="+eventId+filterQuery+sortQuery;
|
||||
nearEventsReq.send( parms );
|
||||
}
|
||||
|
||||
|
@ -277,7 +584,7 @@ function loadEventThumb( event, frame, loadImage ) {
|
|||
console.error( "No holder found for frame "+frame.FrameId );
|
||||
return;
|
||||
}
|
||||
var img = new Asset.image( imagePrefix+frame.Image.imagePath,
|
||||
var img = new Asset.image( imagePrefix+frame.EventId+"&fid="+frame.FrameId,
|
||||
{
|
||||
'onload': ( function( loadImage ) {
|
||||
thumbImg.setProperty( 'src', img.getProperty( 'src' ) );
|
||||
|
@ -293,29 +600,6 @@ function loadEventThumb( event, frame, loadImage ) {
|
|||
);
|
||||
}
|
||||
|
||||
function updateStillsSizes( noDelay ) {
|
||||
var containerDim = $('eventThumbs').getSize();
|
||||
|
||||
var containerWidth = containerDim.x;
|
||||
var containerHeight = containerDim.y;
|
||||
var popupWidth = parseInt($('eventImage').getStyle( 'width' ));
|
||||
var popupHeight = parseInt($('eventImage').getStyle( 'height' ));
|
||||
|
||||
var left = (containerWidth - popupWidth)/2;
|
||||
if ( left < 0 ) left = 0;
|
||||
var top = (containerHeight - popupHeight)/2;
|
||||
if ( top < 0 ) top = 0;
|
||||
if ( popupHeight == 0 && !noDelay ) {
|
||||
// image not yet loaded lets give it another second
|
||||
updateStillsSizes.pass( true ).delay( 50 );
|
||||
return;
|
||||
}
|
||||
$('eventImagePanel').setStyles( {
|
||||
'left': left,
|
||||
'top': top
|
||||
} );
|
||||
}
|
||||
|
||||
function loadEventImage( event, frame ) {
|
||||
console.debug( "Loading "+event.Id+"/"+frame.FrameId );
|
||||
var eventImg = $('eventImage');
|
||||
|
@ -329,14 +613,6 @@ function loadEventImage( event, frame ) {
|
|||
lastThumbImg.setOpacity( 1.0 );
|
||||
}
|
||||
|
||||
eventImg.setProperties( {
|
||||
'class': frame.Type=='Alarm'?'alarm':'normal',
|
||||
'src': thumbImg.getProperty( 'src' ),
|
||||
'title': thumbImg.getProperty( 'title' ),
|
||||
'alt': thumbImg.getProperty( 'alt' ),
|
||||
'width': event.Width,
|
||||
'height': event.Height
|
||||
} );
|
||||
$('eventImageBar').setStyle( 'width', event.Width );
|
||||
if ( frame.Type=='Alarm' )
|
||||
$('eventImageStats').removeClass( 'hidden' );
|
||||
|
@ -347,11 +623,18 @@ function loadEventImage( event, frame ) {
|
|||
|
||||
if ( eventImagePanel.getStyle( 'display' ) == 'none' ) {
|
||||
eventImagePanel.setOpacity( 0 );
|
||||
updateStillsSizes();
|
||||
eventImagePanel.setStyle( 'display', 'block' );
|
||||
eventImagePanel.setStyle( 'display', 'inline-block' );
|
||||
new Fx.Tween( eventImagePanel, { duration: 500, transition: Fx.Transitions.Sine } ).start( 'opacity', 0, 1 );
|
||||
}
|
||||
|
||||
eventImg.setProperties( {
|
||||
'class': frame.Type=='Alarm'?'alarm':'normal',
|
||||
'src': thumbImg.getProperty( 'src' ),
|
||||
'title': thumbImg.getProperty( 'title' ),
|
||||
'alt': thumbImg.getProperty( 'alt' ),
|
||||
'height': $j('#eventThumbs').height() - $j('#eventImageBar').outerHeight(true)-10
|
||||
} );
|
||||
|
||||
$('eventImageNo').set( 'text', frame.FrameId );
|
||||
$('prevImageBtn').disabled = (frame.FrameId==1);
|
||||
$('nextImageBtn').disabled = (frame.FrameId==event.Frames);
|
||||
|
@ -392,13 +675,11 @@ function resetEventStills() {
|
|||
fid = 1;
|
||||
else if ( fid > eventData.Frames )
|
||||
fid = eventData.Frames;
|
||||
checkFrames( eventData.Id, fid );
|
||||
checkFrames( eventData.Id, fid, ($j('#eventImagePanel').css('display')=='none'?'':'true'));
|
||||
scroll.toElement( 'eventThumb'+fid );
|
||||
}
|
||||
} ).set( 0 );
|
||||
}
|
||||
if ( $('eventThumbs').getStyle( 'height' ).match( /^\d+/ ) < (parseInt(eventData.Height)+80) )
|
||||
$('eventThumbs').setStyle( 'height', (parseInt(eventData.Height)+80)+'px' );
|
||||
}
|
||||
|
||||
function getFrameResponse( respObj, respText ) {
|
||||
|
@ -548,6 +829,7 @@ function actQuery( action, parms ) {
|
|||
}
|
||||
|
||||
function deleteEvent() {
|
||||
pauseClicked(); //Provides visual feedback that your click happened.
|
||||
actQuery( 'delete' );
|
||||
streamNext( true );
|
||||
}
|
||||
|
@ -577,21 +859,20 @@ function showEventFrames() {
|
|||
createPopup( '?view=frames&eid='+eventData.Id, 'zmFrames', 'frames' );
|
||||
}
|
||||
|
||||
function showVideo() {
|
||||
function showStream() {
|
||||
$('eventStills').addClass( 'hidden' );
|
||||
$('imageFeed').addClass('hidden');
|
||||
$('eventVideo').removeClass( 'hidden' );
|
||||
|
||||
$('stillsEvent').removeClass( 'hidden' );
|
||||
$('videoEvent').addClass( 'hidden' );
|
||||
$('streamEvent').addClass( 'hidden' );
|
||||
|
||||
streamMode = 'video';
|
||||
if (scale == 'auto') changeScale();
|
||||
}
|
||||
|
||||
function showStills() {
|
||||
$('eventStills').removeClass( 'hidden' );
|
||||
$('imageFeed').removeClass('hidden');
|
||||
$('eventVideo').addClass( 'hidden' );
|
||||
$('eventVideo').addClass( 'hidden' );
|
||||
|
||||
if (vid && ( vid.paused != true ) ) {
|
||||
// Pause the video
|
||||
|
@ -603,11 +884,11 @@ function showStills() {
|
|||
}
|
||||
|
||||
$('stillsEvent').addClass( 'hidden' );
|
||||
$('videoEvent').removeClass( 'hidden' );
|
||||
$('streamEvent').removeClass( 'hidden' );
|
||||
|
||||
streamMode = 'stills';
|
||||
|
||||
streamPause( true );
|
||||
pauseClicked();
|
||||
if ( !scroll ) {
|
||||
scroll = new Fx.Scroll( 'eventThumbs', {
|
||||
wait: false,
|
||||
|
@ -618,7 +899,7 @@ function showStills() {
|
|||
);
|
||||
}
|
||||
resetEventStills();
|
||||
$(window).addEvent( 'resize', updateStillsSizes );
|
||||
if (scale == 'auto') changeScale();
|
||||
}
|
||||
|
||||
function showFrameStats() {
|
||||
|
@ -630,184 +911,64 @@ function videoEvent() {
|
|||
createPopup( '?view=video&eid='+eventData.Id, 'zmVideo', 'video', eventData.Width, eventData.Height );
|
||||
}
|
||||
|
||||
|
||||
// Called on each event load because each event can be a different width
|
||||
function drawProgressBar() {
|
||||
var barWidth = 0;
|
||||
$('progressBar').addClass( 'invisible' );
|
||||
var cells = $('progressBar').getElements( 'div' );
|
||||
var cellWidth = parseInt( eventData.Width/$$(cells).length );
|
||||
$$(cells).forEach(
|
||||
function( cell, index ) {
|
||||
if ( index == 0 )
|
||||
$(cell).setStyles( { 'left': barWidth, 'width': cellWidth, 'borderLeft': 0 } );
|
||||
else
|
||||
$(cell).setStyles( { 'left': barWidth, 'width': cellWidth } );
|
||||
var offset = parseInt((index*eventData.Length)/$$(cells).length);
|
||||
$(cell).setProperty( 'title', '+'+secsToTime(offset)+'s' );
|
||||
$(cell).removeEvent( 'click' );
|
||||
$(cell).addEvent( 'click', function() { streamSeek( offset ); } );
|
||||
barWidth += $(cell).getCoordinates().width;
|
||||
}
|
||||
);
|
||||
$('progressBar').setStyle( 'width', barWidth );
|
||||
$('progressBar').removeClass( 'invisible' );
|
||||
let barWidth = $j('#evtStream').width();
|
||||
$j('#progressBar').css( 'width', barWidth );
|
||||
}
|
||||
|
||||
// Shows current stream progress.
|
||||
function updateProgressBar() {
|
||||
if ( eventData && streamStatus ) {
|
||||
var cells = $('progressBar').getElements( 'div' );
|
||||
var completeIndex = parseInt((($$(cells).length+1)*streamStatus.progress)/eventData.Length);
|
||||
$$(cells).forEach(
|
||||
function( cell, index ) {
|
||||
if ( index < completeIndex ) {
|
||||
if ( !$(cell).hasClass( 'complete' ) ) {
|
||||
$(cell).addClass( 'complete' );
|
||||
}
|
||||
} else {
|
||||
if ( $(cell).hasClass( 'complete' ) ) {
|
||||
$(cell).removeClass( 'complete' );
|
||||
}
|
||||
} // end if
|
||||
} // end function
|
||||
);
|
||||
//$('progressBar').setStyle( 'width', barWidth );
|
||||
$('progressBar').removeClass( 'invisible' );
|
||||
} // end if eventData && streamStatus
|
||||
if ( ! ( eventData && streamStatus ) ) {
|
||||
return;
|
||||
} // end if ! eventData && streamStatus
|
||||
var curWidth = (streamStatus.progress / parseFloat(eventData.Length)) * 100;
|
||||
$j("#progressBox").css('width', curWidth + '%');
|
||||
} // end function updateProgressBar()
|
||||
|
||||
// Handles seeking when clicking on the progress bar.
|
||||
function progressBarNav (){
|
||||
$j('#progressBar').click(function(e){
|
||||
var x = e.offsetX;
|
||||
var seekTime = (x / $j('#progressBar').width()) * parseFloat(eventData.Length);
|
||||
streamSeek (seekTime);
|
||||
});
|
||||
}
|
||||
|
||||
function handleClick( event ) {
|
||||
var target = event.target;
|
||||
var x = event.page.x - $(target).getLeft();
|
||||
var y = event.page.y - $(target).getTop();
|
||||
if (vid) {
|
||||
if (target.id != 'videoobj') return; //ignore clicks on control bar
|
||||
var x = event.offsetX;
|
||||
var y = event.offsetY;
|
||||
} else {
|
||||
var x = event.page.x - $(target).getLeft();
|
||||
var y = event.page.y - $(target).getTop();
|
||||
}
|
||||
|
||||
if ( event.shift )
|
||||
streamPan( x, y );
|
||||
else
|
||||
streamZoomIn( x, y );
|
||||
}
|
||||
|
||||
function setupListener() {
|
||||
|
||||
// Buttons
|
||||
var playButton = document.getElementById("play-pause");
|
||||
var muteButton = document.getElementById("mute");
|
||||
var fullScreenButton = document.getElementById("full-screen");
|
||||
|
||||
// Sliders
|
||||
var seekBar = document.getElementById("seekbar");
|
||||
var volumeBar = document.getElementById("volume-bar");
|
||||
|
||||
// Event listener for the play/pause button
|
||||
playButton.addEventListener( "click", function() {
|
||||
if (vid.paused == true) {
|
||||
// Play the video
|
||||
vid.play();
|
||||
|
||||
// Update the button text to 'Pause'
|
||||
playButton.innerHTML = "Pause";
|
||||
} else {
|
||||
// Pause the video
|
||||
vid.pause();
|
||||
|
||||
// Update the button text to 'Play'
|
||||
playButton.innerHTML = "Play";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Event listener for the mute button
|
||||
muteButton.addEventListener("click", function() {
|
||||
if (vid.muted == false) {
|
||||
// Mute the video
|
||||
vid.muted = true;
|
||||
|
||||
// Update the button text
|
||||
muteButton.innerHTML = "Unmute";
|
||||
} else {
|
||||
// Unmute the video
|
||||
vid.muted = false;
|
||||
|
||||
// Update the button text
|
||||
muteButton.innerHTML = "Mute";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Event listener for the full-screen button
|
||||
fullScreenButton.addEventListener("click", function() {
|
||||
if (vid.requestFullscreen) {
|
||||
vid.requestFullscreen();
|
||||
} else if (vid.mozRequestFullScreen) {
|
||||
vid.mozRequestFullScreen(); // Firefox
|
||||
} else if (vid.webkitRequestFullscreen) {
|
||||
vid.webkitRequestFullscreen(); // Chrome and Safari
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Event listener for the seek bar
|
||||
seekBar.addEventListener("change", function() {
|
||||
// Calculate the new time
|
||||
var time = vid.duration * (seekBar.value / 100);
|
||||
|
||||
// Update the video time
|
||||
vid.currentTime = time;
|
||||
});
|
||||
|
||||
|
||||
// Update the seek bar as the video plays
|
||||
vid.addEventListener("timeupdate", function() {
|
||||
// Calculate the slider value
|
||||
var value = (100 / vid.duration) * vid.currentTime;
|
||||
|
||||
// Update the slider value
|
||||
seekBar.value = value;
|
||||
});
|
||||
|
||||
// Pause the video when the seek handle is being dragged
|
||||
seekBar.addEventListener("mousedown", function() {
|
||||
vid.pause();
|
||||
});
|
||||
|
||||
// Play the video when the seek handle is dropped
|
||||
seekBar.addEventListener("mouseup", function() {
|
||||
vid.play();
|
||||
});
|
||||
|
||||
// Event listener for the volume bar
|
||||
volumeBar.addEventListener("change", function() {
|
||||
// Update the video volume
|
||||
vid.volume = volumeBar.value;
|
||||
});
|
||||
if (event.shift || event.shiftKey) {//handle both jquery and mootools
|
||||
streamPan(x, y);
|
||||
} else if (vid && event.ctrlKey) { //allow zoom out by control click. useful in fullscreen
|
||||
vjsPanZoom('zoomOut', x, y);
|
||||
} else {
|
||||
streamZoomIn(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
function initPage() {
|
||||
//FIXME prevent blocking...not sure what is happening or best way to unblock
|
||||
if ( $('videoobj') ) {
|
||||
vid = videojs("videoobj");
|
||||
}
|
||||
if ( vid ) {
|
||||
/*
|
||||
setupListener();
|
||||
vid.removeAttribute("controls");
|
||||
/* window.videoobj.oncanplay=null;
|
||||
window.videoobj.currentTime=window.videoobj.currentTime-1;
|
||||
window.videoobj.currentTime=window.videoobj.currentTime+1;//may not be symetrical of course
|
||||
|
||||
vid.onstalled=function(){window.vid.currentTime=window.vid.currentTime-1;window.vid.currentTime=window.vid.currentTime+1;}
|
||||
vid.onwaiting=function(){window.vid.currentTime=window.vid.currentTime-1;window.vid.currentTime=window.vid.currentTime+1;}
|
||||
vid.onloadstart=function(){window.vid.currentTime=window.vid.currentTime-1;window.vid.currentTime=window.vid.currentTime+1;}
|
||||
vid.onplay=function(){window.vid.currentTime=window.vid.currentTime-1;window.vid.currentTime=window.vid.currentTime+1;}
|
||||
vid.onplaying=function(){window.vid.currentTime=window.vid.currentTime-1;window.vid.currentTime=window.vid.currentTime+1;}
|
||||
//window.vid.hide();//does not help
|
||||
var sources = window.videoobj.getElementsByTagName('source');
|
||||
sources[0].src=null;
|
||||
window.videoobj.load();
|
||||
streamPlay(); */
|
||||
if ($j('#videoobj').length) {
|
||||
vid = videojs('videoobj');
|
||||
addVideoTimingTrack(vid, LabelFormat, eventData.MonitorName, eventData.Length, eventData.StartTime);
|
||||
$j('.vjs-progress-control').append('<div class="alarmCue"></div>');//add a place for videojs only on first load
|
||||
vid.on('ended', vjsReplay);
|
||||
vid.on('play', vjsPlay);
|
||||
vid.on('pause', vjsPause);
|
||||
vid.on('click', function(event){handleClick(event);});
|
||||
vid.on('timeupdate', function (){$j('#progressValue').html(secsToTime(Math.floor(vid.currentTime())))});
|
||||
} else {
|
||||
progressBarNav ();
|
||||
streamCmdTimer = streamQuery.delay( 250 );
|
||||
eventQuery.pass( eventData.Id ).delay( 500 );
|
||||
|
||||
if ( canStreamNative ) {
|
||||
var streamImg = $('imageFeed').getElement('img');
|
||||
if ( !streamImg )
|
||||
|
@ -815,6 +976,9 @@ function initPage() {
|
|||
$(streamImg).addEvent( 'click', function( event ) { handleClick( event ); } );
|
||||
}
|
||||
}
|
||||
nearEventsQuery(eventData.Id);
|
||||
initialAlarmCues(eventData.Id); //call ajax+renderAlarmCues
|
||||
if (scale == "auto") changeScale();
|
||||
}
|
||||
|
||||
// Kick everything off
|
||||
|
|
|
@ -30,17 +30,25 @@ var eventData = {
|
|||
MonitorId: '<?php echo $Event->MonitorId() ?>',
|
||||
Width: '<?php echo $Event->Width() ?>',
|
||||
Height: '<?php echo $Event->Height() ?>',
|
||||
Length: '<?php echo $Event->Length() ?>'
|
||||
Length: '<?php echo $Event->Length() ?>',
|
||||
StartTime: '<?php echo $Event->StartTime() ?>',
|
||||
EndTime: '<?php echo $Event->EndTime() ?>',
|
||||
Frames: '<?php echo $Event->Frames() ?>',
|
||||
MonitorName: '<?php echo $Monitor->Name() ?>'
|
||||
};
|
||||
|
||||
var filterQuery = '<?php echo isset($filterQuery)?validJsStr($filterQuery):'' ?>';
|
||||
var sortQuery = '<?php echo isset($sortQuery)?validJsStr($sortQuery):'' ?>';
|
||||
var filterQuery = '<?php echo isset($filterQuery)?validJsStr(htmlspecialchars_decode($filterQuery)):'' ?>';
|
||||
var sortQuery = '<?php echo isset($sortQuery)?validJsStr(htmlspecialchars_decode($sortQuery)):'' ?>';
|
||||
|
||||
var rates = <?php echo json_encode(array_keys($rates)) ?>;
|
||||
var scale = "<?php echo $scale ?>";
|
||||
var LabelFormat = "<?php echo validJsStr($Monitor->LabelFormat())?>";
|
||||
|
||||
var scale = <?php echo $scale ?>;
|
||||
var canEditEvents = <?php echo canEdit( 'Events' )?'true':'false' ?>;
|
||||
var streamTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>;
|
||||
|
||||
var canStreamNative = <?php echo canStreamNative()?'true':'false' ?>;
|
||||
var streamMode = '<?php echo $streamMode ?>';
|
||||
|
||||
//
|
||||
// Strings
|
||||
|
|
|
@ -1,15 +1,34 @@
|
|||
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;
|
||||
let scale = $j('#scale').val();
|
||||
let img = $j('#frameImg');
|
||||
let controlsLinks = {
|
||||
next: $j('#nextLink'),
|
||||
prev: $j('#prevLink'),
|
||||
first: $j('#firstLink'),
|
||||
last: $j('#lastLink')
|
||||
}
|
||||
|
||||
img.style.width = newWidth + "px";
|
||||
img.style.height = newHeight + "px";
|
||||
if (img) {
|
||||
let baseWidth = $j('#base_width').val();
|
||||
let baseHeight = $j('#base_height').val();
|
||||
if (scale == "auto") {
|
||||
let newSize = scaleToFit(baseWidth, baseHeight, img, $j('#controls'));
|
||||
newWidth = newSize.width;
|
||||
newHeight = newSize.height;
|
||||
autoScale = newSize.autoScale;
|
||||
} else {
|
||||
$j(window).off('resize', endOfResize); //remove resize handler when Scale to Fit is not active
|
||||
newWidth = baseWidth * scale / SCALE_BASE;
|
||||
newHeight = baseHeight * scale / SCALE_BASE;
|
||||
}
|
||||
img.css('width', newWidth + "px");
|
||||
img.css('height', newHeight + "px");
|
||||
}
|
||||
Cookie.write( 'zmWatchScale', scale, { duration: 10*365 } );
|
||||
$j.each(controlsLinks, function(k, anchor) { //Make frames respect scale choices
|
||||
anchor.prop('href', anchor.prop('href').replace(/scale=.*&/, 'scale=' + scale + '&'));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
if (scale == "auto") $j(document).ready(changeScale);
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
var scale = '<?php echo $scale ?>';
|
||||
|
||||
var SCALE_BASE = <?php echo SCALE_BASE ?>;
|
||||
|
|
|
@ -25,18 +25,29 @@ function showPtzControls() {
|
|||
|
||||
function changeScale() {
|
||||
var scale = $('scale').get('value');
|
||||
var newWidth = ( monitorWidth * scale ) / SCALE_BASE;
|
||||
var newHeight = ( monitorHeight * scale ) / SCALE_BASE;
|
||||
var newWidth;
|
||||
var newHeight;
|
||||
if (scale == "auto") {
|
||||
let newSize = scaleToFit(monitorWidth, monitorHeight, $j('#liveStream'+monitorId), $j('#replayStatus'));
|
||||
newWidth = newSize.width;
|
||||
newHeight = newSize.height;
|
||||
autoScale = newSize.autoScale;
|
||||
} else {
|
||||
$j(window).off('resize', endOfResize); //remove resize handler when Scale to Fit is not active
|
||||
newWidth = monitorWidth * scale / SCALE_BASE;
|
||||
newHeight = monitorHeight * scale / SCALE_BASE;
|
||||
}
|
||||
|
||||
|
||||
Cookie.write( 'zmWatchScale'+monitorId, scale, { duration: 10*365 } );
|
||||
|
||||
/*Stream could be an applet so can't use moo tools*/
|
||||
var streamImg = document.getElementById('liveStream'+monitorId);
|
||||
var streamImg = $('liveStream'+monitorId);
|
||||
if ( streamImg ) {
|
||||
streamImg.style.width = newWidth + "px";
|
||||
streamImg.style.height = newHeight + "px";
|
||||
|
||||
streamImg.src = streamImg.src.replace(/scale=\d+/i, 'scale='+scale);
|
||||
streamImg.src = streamImg.src.replace(/scale=\d+/i, 'scale='+(scale== 'auto' ? autoScale : scale));
|
||||
} else {
|
||||
console.error("No element found for liveStream.");
|
||||
}
|
||||
|
@ -622,6 +633,7 @@ function initPage() {
|
|||
|
||||
if ( refreshApplet && appletRefreshTime )
|
||||
appletRefresh.delay( appletRefreshTime*1000 );
|
||||
if (scale == "auto") changeScale();
|
||||
}
|
||||
|
||||
// Kick everything off
|
||||
|
|
|
@ -50,7 +50,7 @@ var monitorWidth = <?php echo $monitor->Width() ?>;
|
|||
var monitorHeight = <?php echo $monitor->Height() ?>;
|
||||
var monitorUrl = '<?php echo ( $monitor->Server()->Url() ) ?>';
|
||||
|
||||
var scale = <?php echo $scale ?>;
|
||||
var scale = '<?php echo $scale ?>';
|
||||
|
||||
var statusRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_STATUS ?>;
|
||||
var eventsRefreshTimeout = <?php echo 1000*ZM_WEB_REFRESH_EVENTS ?>;
|
||||
|
|
|
@ -61,13 +61,24 @@ $path = null;
|
|||
if ( empty($_REQUEST['path']) ) {
|
||||
if ( ! empty($_REQUEST['fid']) ) {
|
||||
$show = empty($_REQUEST['show']) ? 'capture' : $_REQUEST['show'];
|
||||
|
||||
|
||||
if ( ! empty($_REQUEST['eid'] ) ) {
|
||||
$Event = new Event( $_REQUEST['eid'] );
|
||||
$Frame = Frame::find_one( array( 'EventId' => $_REQUEST['eid'], 'FrameId' => $_REQUEST['fid'] ) );
|
||||
if ( ! $Frame ) {
|
||||
Fatal("No Frame found for event(".$_REQUEST['eid'].") and frame id(".$_REQUEST['fid'].")");
|
||||
$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'] ) );
|
||||
if ( $previousBulkFrame and $nextBulkFrame ) {
|
||||
$Frame = new Frame( $previousBulkFrame );
|
||||
$Frame->FrameId = ( $_REQUEST['fid'] );
|
||||
$percentage = ($Frame->FrameId() - $previousBulkFrame['FrameId']) / ($nextBulkFrame['FrameId'] - $previousBulkFrame['FrameId']);
|
||||
$Frame->Delta = ( $previousBulkFrame['Delta'] + floor( 100* ( $nextBulkFrame['Delta'] - $previousBulkFrame['Delta'] ) * $percentage )/100 );
|
||||
Logger::Debug("Got virtual frame from Bulk Frames previous delta: " . $previousBulkFrame['Delta'] . " + nextdelta:" . $nextBulkFrame['Delta'] . ' - ' . $previousBulkFrame['Delta'] . ' * ' . $percentage );
|
||||
} else {
|
||||
Fatal("No Frame found for event(".$_REQUEST['eid'].") and frame id(".$_REQUEST['fid'].")");
|
||||
}
|
||||
}
|
||||
// Frame can be non-existent. We have Bulk frames. So now we should try to load the bulk frame
|
||||
} else {
|
||||
# If we are only specifying fid, then the fid must be the primary key into the frames table. But when the event is specified, then it is the frame #
|
||||
$Frame = new Frame( $_REQUEST['fid'] );
|
||||
|
|
Loading…
Reference in New Issue