GUACAMOLE-462: Merge correct recording playback artifacts during seek().

This commit is contained in:
Virtually Nick
2022-03-02 16:25:33 -05:00
committed by GitHub
3 changed files with 91 additions and 38 deletions

View File

@@ -228,11 +228,14 @@ Guacamole.Client = function(tunnel) {
currentState = state.currentState;
currentTimestamp = state.currentTimestamp;
// Cancel any pending display operations/frames
display.cancel();
// Dispose of all layers
for (key in layers) {
index = parseInt(key);
if (index > 0)
display.dispose(layers[key]);
layers[key].dispose();
}
layers = {};

View File

@@ -201,6 +201,22 @@ Guacamole.Display = function() {
*/
function Frame(callback, tasks) {
/**
* Cancels rendering of this frame and all associated tasks. The
* callback provided at construction time, if any, is not invoked.
*/
this.cancel = function cancel() {
callback = null;
tasks.forEach(function cancelTask(task) {
task.cancel();
});
tasks = [];
};
/**
* Returns whether this frame is ready to be rendered. This function
* returns true if and only if ALL underlying tasks are unblocked.
@@ -271,6 +287,16 @@ Guacamole.Display = function() {
*/
this.blocked = blocked;
/**
* Cancels this task such that it will not run. The task handler
* provided at construction time, if any, is not invoked. Calling
* execute() after calling this function has no effect.
*/
this.cancel = function cancel() {
task.blocked = false;
taskHandler = null;
};
/**
* Unblocks this Task, allowing it to run.
*/
@@ -417,6 +443,27 @@ Guacamole.Display = function() {
};
/**
* Cancels rendering of all pending frames and associated rendering
* operations. The callbacks provided to outstanding past calls to flush(),
* if any, are not invoked.
*/
this.cancel = function cancel() {
frames.forEach(function cancelFrame(frame) {
frame.cancel();
});
frames = [];
tasks.forEach(function cancelTask(task) {
task.cancel();
});
tasks = [];
};
/**
* Sets the hotspot and image of the mouse cursor displayed within the
* Guacamole display.

View File

@@ -577,11 +577,12 @@ Guacamole.SessionRecording = function SessionRecording(source) {
* @param {function} callback
* The callback to invoke once the seek operation has completed.
*
* @param {number} [delay=0]
* The number of milliseconds that the seek operation should be
* scheduled to take.
* @param {number} [nextRealTimestamp]
* The timestamp of the point in time that the given frame should be
* displayed, as would be returned by new Date().getTime(). If omitted,
* the frame will be displayed as soon as possible.
*/
var seekToFrame = function seekToFrame(index, callback, delay) {
var seekToFrame = function seekToFrame(index, callback, nextRealTimestamp) {
// Abort any in-progress seek
abortSeek();
@@ -591,29 +592,7 @@ Guacamole.SessionRecording = function SessionRecording(source) {
aborted : false
};
var startIndex;
// Back up until startIndex represents current state
for (startIndex = index; startIndex >= 0; startIndex--) {
var frame = frames[startIndex];
// If we've reached the current frame, startIndex represents
// current state by definition
if (startIndex === currentFrame)
break;
// If frame has associated absolute state, make that frame the
// current state
if (frame.clientState) {
frame.clientState.text().then(function textReady(text) {
playbackClient.importState(JSON.parse(text));
currentFrame = index;
});
break;
}
}
var startIndex = index;
// Replay any applicable incremental frames
var continueReplay = function continueReplay() {
@@ -629,7 +608,7 @@ Guacamole.SessionRecording = function SessionRecording(source) {
return;
// If frames remain, replay the next frame
if (!thisSeek.aborted && currentFrame < index)
if (currentFrame < index)
replayFrame(currentFrame + 1, continueReplay);
// Otherwise, the seek operation is completed
@@ -640,11 +619,39 @@ Guacamole.SessionRecording = function SessionRecording(source) {
// Continue replay after requested delay has elapsed, or
// immediately if no delay was requested
if (delay)
window.setTimeout(continueReplay, delay);
else
continueReplay();
var continueAfterRequiredDelay = function continueAfterRequiredDelay() {
var delay = nextRealTimestamp ? Math.max(nextRealTimestamp - new Date().getTime(), 0) : 0;
if (delay)
window.setTimeout(continueReplay, delay);
else
continueReplay();
};
// Back up until startIndex represents current state
for (; startIndex >= 0; startIndex--) {
var frame = frames[startIndex];
// If we've reached the current frame, startIndex represents
// current state by definition
if (startIndex === currentFrame)
break;
// If frame has associated absolute state, make that frame the
// current state
if (frame.clientState) {
frame.clientState.text().then(function textReady(text) {
playbackClient.importState(JSON.parse(text));
currentFrame = startIndex;
continueAfterRequiredDelay();
});
return;
}
}
continueAfterRequiredDelay();
};
/**
@@ -679,14 +686,10 @@ Guacamole.SessionRecording = function SessionRecording(source) {
// frame begins
var nextRealTimestamp = next.timestamp - startVideoTimestamp + startRealTimestamp;
// Calculate the relative delay between the current time and
// the next frame start
var delay = Math.max(nextRealTimestamp - new Date().getTime(), 0);
// Advance to next frame after enough time has elapsed
seekToFrame(currentFrame + 1, function frameDelayElapsed() {
continuePlayback();
}, delay);
}, nextRealTimestamp);
}