From d87bf5a15663b7f1181ca74d103799cb0bed420f Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Fri, 20 Dec 2019 11:02:12 -0500
Subject: [PATCH 1/3] replace the rate display with a dropdown
---
web/skins/classic/views/event.php | 7 ++++++-
web/skins/classic/views/js/event.js | 31 ++++++++++++++++++++++-------
2 files changed, 30 insertions(+), 8 deletions(-)
diff --git a/web/skins/classic/views/event.php b/web/skins/classic/views/event.php
index 947bd3972..a17e48b65 100644
--- a/web/skins/classic/views/event.php
+++ b/web/skins/classic/views/event.php
@@ -257,7 +257,12 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) {
: Replay
- : x
+ :
+translate('Stopped'), 100 => '1x', 200=>'2x', 400=>'4x', 800=>'8x' );
+echo htmlSelect('rate', $rates, $rate, array('id'=>'rateValue'));
+?>
+
: 0s
: 1x
diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js
index 76fc47c79..96f56dbd4 100644
--- a/web/skins/classic/views/js/event.js
+++ b/web/skins/classic/views/js/event.js
@@ -200,6 +200,19 @@ function changeReplayMode() {
refreshWindow();
}
+function changeRate() {
+ var rate = $j('select[name="rate"]').val();
+ if ( ! rate ) {
+ pauseClicked();
+ } else {
+ if ( vid ) {
+ vid.playbackRate(rate/100);
+ Cookie.write('zmEventRate', rate, {duration: 10*365});
+ }
+ }
+}
+
+
var streamParms = "view=request&request=stream&connkey="+connKey;
if ( auth_hash ) {
streamParms += '&auth='+auth_hash;
@@ -245,7 +258,7 @@ function getCmdResponse( respObj, respText ) {
if ( streamStatus.paused == true ) {
streamPause( );
} else {
- $j('#rateValue').html(streamStatus.rate);
+ $j('select[name="rate"]').val(streamStatus.rate*100);
Cookie.write('zmEventRate', streamStatus.rate*100, {duration: 10*365});
streamPlay( );
}
@@ -296,7 +309,7 @@ function vjsPause() {
function streamPause( ) {
$j('#modeValue').html('Paused');
- $j('#rateValue').html('0');
+ $j('select[name="rate"]').val('0');
setButtonState( $('pauseBtn'), 'active' );
setButtonState( $('playBtn'), 'inactive' );
setButtonState( $('fastFwdBtn'), 'unavail' );
@@ -322,7 +335,7 @@ function vjsPlay() { //catches if we change mode programatically
if ( intervalRewind ) {
stopFastRev();
}
- $j('#rateValue').html(vid.playbackRate());
+ $j('select[name="rate"]').val(vid.playbackRate());
Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365});
streamPlay();
}
@@ -350,7 +363,7 @@ function streamFastFwd( action ) {
if ( rates.indexOf(vid.playbackRate()*100)-1 == -1 ) {
setButtonState($('fastFwdBtn'), 'unavail');
}
- $j('#rateValue').html(vid.playbackRate());
+ $j('#rate').val(vid.playbackRate());
Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365});
} else {
streamReq.send(streamParms+"&command="+CMD_FASTFWD);
@@ -397,7 +410,7 @@ function streamFastRev( action ) {
setButtonState( $('fastRevBtn'), 'unavail' );
}
clearInterval(intervalRewind);
- $j('#rateValue').html(-revSpeed);
+ $j('#rate').val(-revSpeed);
Cookie.write('zmEventRate', vid.playbackRate()*100, {duration: 10*365});
intervalRewind = setInterval(function() {
if (vid.currentTime() <= 0) {
@@ -588,7 +601,7 @@ function getEventResponse( respObj, respText ) {
CurEventDefVideoPath = null;
$j('#modeValue').html('Replay');
$j('#zoomValue').html('1');
- $j('#rateValue').html('1');
+ $j('#rate').val('100');
vjsPanZoom('zoomOut');
} else {
drawProgressBar();
@@ -1066,7 +1079,8 @@ function initPage() {
$j('#progressValue').html(secsToTime(Math.floor(vid.currentTime())));
});
- if ( rate > 1 ) {
+ // rate is in % so 100 would be 1x
+ if ( rate > 0 ) {
// rate should be 100 = 1x, etc.
vid.playbackRate(rate/100);
}
@@ -1091,6 +1105,9 @@ function initPage() {
nearEventsQuery(eventData.Id);
initialAlarmCues(eventData.Id); //call ajax+renderAlarmCues
if (scale == "auto") changeScale();
+ document.querySelectorAll('select[name="rate"]').forEach(function(el) {
+ el.onchange = window['changeRate'];
+ });
}
// Kick everything off
From 1bdabefb2d06ef343782cb9c723fb9b506a8bb3e Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Tue, 7 Jan 2020 17:07:35 -0500
Subject: [PATCH 2/3] fix single stepping in reverse but actually modifying
curr_frame_id
---
src/zm_eventstream.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp
index e2f1862ab..2ed2dc167 100644
--- a/src/zm_eventstream.cpp
+++ b/src/zm_eventstream.cpp
@@ -383,6 +383,8 @@ void EventStream::processCommand(const CmdMsg *msg) {
paused = true;
replay_rate = ZM_RATE_BASE;
step = -1;
+ curr_frame_id -= 1;
+ if ( curr_frame_id < 1 ) curr_frame_id = 1;
break;
case CMD_FASTREV :
Debug(1, "Got FAST REV command");
@@ -904,7 +906,7 @@ void EventStream::runStream() {
send_frame = true;
}
} else if ( step != 0 ) {
- Debug(2, "Paused with step");
+ Debug(2, "Paused with step %d", step);
// We are paused and are just stepping forward or backward one frame
step = 0;
send_frame = true;
From 59bd658a96671b5fdda4b1a250544fd38d0fccbe Mon Sep 17 00:00:00 2001
From: Isaac Connor
Date: Tue, 10 Mar 2020 18:46:20 -0400
Subject: [PATCH 3/3] replace vjsPause with pauseCLicked. Fix behaviour with
rate dropdown and reverse/pause etc.
---
web/skins/classic/views/js/event.js | 72 +++++++++++++++++------------
1 file changed, 43 insertions(+), 29 deletions(-)
diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js
index 30cc5057c..c569e08e2 100644
--- a/web/skins/classic/views/js/event.js
+++ b/web/skins/classic/views/js/event.js
@@ -1,4 +1,7 @@
var vid = null;
+var spf = Math.round((eventData.Length / eventData.Frames)*1000000 )/1000000;//Seconds per frame for videojs frame by frame.
+var intervalRewind;
+var revSpeed = .5;
// Function called when video.js hits the end of the video
function vjsReplay() {
@@ -39,7 +42,7 @@ function vjsReplay() {
streamNext(true);
break;
}
-}
+} // end function vjsReplay
$j.ajaxSetup({timeout: AJAX_TIMEOUT}); //sets timeout for all getJSON.
@@ -67,14 +70,14 @@ function renderAlarmCues(containerEl) {
var spanTimeStart = 0;
var spanTimeEnd = 0;
var alarmed = 0;
- var alarmHtml = "";
+ var alarmHtml = '';
var pixSkew = 0;
var skip = 0;
var num_cueFrames = cueFrames.length;
for ( var i = 0; i < num_cueFrames; i++ ) {
skip = 0;
frame = cueFrames[i];
- if (frame.Type == "Alarm" && alarmed == 0) { //From nothing to alarm. End nothing and start alarm.
+ 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;
@@ -88,24 +91,24 @@ function renderAlarmCues(containerEl) {
}
alarmHtml += '';
spanTimeStart = spanTimeEnd;
- } else if (frame.Type !== "Alarm" && alarmed == 1) { //from alarm to nothing. End alarm and start nothing.
+ } 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 < num_cueFrames) {
//alarm is too short and there is more event
continue;
}
- while (futNone < minAlarm) { //check ahead to see if there's enough for a nonespan
- if (indexPlus >= cueFrames.length) break; //check if end of 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") {
+ if ( cueFrames[indexPlus].Type == 'Alarm' ) {
i = --indexPlus;
skip = 1;
break;
}
indexPlus++;
}
- if (skip == 1) continue; //javascript doesn't support continue 2;
+ if ( skip == 1 ) continue; //javascript doesn't support continue 2;
spanTimeEnd = frame.Delta *100;
spanTime = spanTimeEnd - spanTimeStart;
alarmed = 0;
@@ -118,7 +121,7 @@ function renderAlarmCues(containerEl) {
}
alarmHtml += '';
spanTimeStart = spanTimeEnd;
- } else if (frame.Type == "Alarm" && alarmed == 1 && i + 1 >= cueFrames.length) { //event ends on an alarm
+ } 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;
@@ -155,12 +158,12 @@ function changeScale() {
var eventViewer;
var alarmCue = $j('div.alarmCue');
var bottomEl = streamMode == 'stills' ? $j('#eventImageNav') : $j('#replayStatus');
- if (streamMode == 'stills') {
+ if ( streamMode == 'stills' ) {
eventViewer = $j('#eventThumbs');
} else {
eventViewer = $j(vid ? '#videoobj' : '#evtStream');
}
- if ( scale == "auto" ) {
+ if ( scale == 'auto' ) {
var newSize = scaleToFit(eventData.Width, eventData.Height, eventViewer, bottomEl);
newWidth = newSize.width;
newHeight = newSize.height;
@@ -201,17 +204,31 @@ function changeReplayMode() {
}
function changeRate() {
- var rate = $j('select[name="rate"]').val();
+ var rate = parseInt($j('select[name="rate"]').val());
if ( ! rate ) {
pauseClicked();
- } else {
+ } else if ( rate < 0 ) {
+ if ( vid ) { //There is no reverse play with mp4. Set the speed to 0 and manually set the time back.
+ revSpeed = rates[rates.indexOf(-1*rate)-1]/100;
+ clearInterval(intervalRewind);
+ 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
+ } // end if vid
+
+ } else { // Forward rate
if ( vid ) {
vid.playbackRate(rate/100);
- Cookie.write('zmEventRate', rate, {duration: 10*365});
}
}
-}
-
+ Cookie.write('zmEventRate', rate, {duration: 10*365});
+} // end function changeRate
var streamParms = "view=request&request=stream&connkey="+connKey;
if ( auth_hash ) {
@@ -294,21 +311,18 @@ var streamReq = new Request.JSON( {
function pauseClicked() {
if ( vid ) {
+ if ( intervalRewind ) {
+ stopFastRev();
+ }
vid.pause();
} else {
streamReq.send(streamParms+"&command="+CMD_PAUSE);
- streamPause();
- }
-}
-
-function vjsPause() {
- if ( intervalRewind ) {
- stopFastRev();
}
streamPause();
}
function streamPause( ) {
+
$j('#modeValue').html('Paused');
setButtonState( $('pauseBtn'), 'active' );
setButtonState( $('playBtn'), 'inactive' );
@@ -319,6 +333,10 @@ function streamPause( ) {
}
function playClicked( ) {
+ var rate_select = $j('select[name="rate"]');
+ if ( ! rate_select.val() ) {
+ $j('select[name="rate"]').val(100);
+ }
if ( vid ) {
if ( vid.paused() ) {
vid.play();
@@ -327,8 +345,8 @@ function playClicked( ) {
}
} else {
streamReq.send(streamParms+"&command="+CMD_PLAY);
- streamPlay();
}
+ streamPlay();
}
function vjsPlay() { //catches if we change mode programatically
@@ -341,7 +359,6 @@ function vjsPlay() { //catches if we change mode programatically
}
function streamPlay( ) {
- $j('#modeValue').html('Replay');
setButtonState( $('pauseBtn'), 'inactive' );
setButtonState( $('playBtn'), 'active' );
setButtonState( $('fastFwdBtn'), 'inactive' );
@@ -370,9 +387,6 @@ function streamFastFwd( action ) {
}
}
-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 ) {
if ( vid ) {
@@ -1073,7 +1087,7 @@ function initPage() {
$j('.vjs-progress-control').append('');//add a place for videojs only on first load
vid.on('ended', vjsReplay);
vid.on('play', vjsPlay);
- vid.on('pause', vjsPause);
+ vid.on('pause', pauseClicked);
vid.on('click', function(event) {
handleClick(event);
});