Files
noVNC/tests/playback.js
Pierre Ossman 527a1fd0ae Pre-convert recordings for playback
Convert the recordings ahead of time instead of during the playback.
That way we aren't messing up the profiling with time spent converting
data, rather than processing it.
2019-01-08 12:24:39 +01:00

173 lines
4.9 KiB
JavaScript

/*
* noVNC: HTML5 VNC client
* Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*/
import RFB from '../core/rfb.js';
import * as Log from '../core/util/logging.js';
// Immediate polyfill
if (window.setImmediate === undefined) {
let _immediateIdCounter = 1;
const _immediateFuncs = {};
window.setImmediate = (func) => {
const index = _immediateIdCounter++;
_immediateFuncs[index] = func;
window.postMessage("noVNC immediate trigger:" + index, "*");
return index;
};
window.clearImmediate = (id) => {
_immediateFuncs[id];
};
window.addEventListener("message", (event) => {
if ((typeof event.data !== "string") ||
(event.data.indexOf("noVNC immediate trigger:") !== 0)) {
return;
}
const index = event.data.slice("noVNC immediate trigger:".length);
const callback = _immediateFuncs[index];
if (callback === undefined) {
return;
}
delete _immediateFuncs[index];
callback();
});
}
export default class RecordingPlayer {
constructor(frames, disconnected) {
this._frames = frames;
this._disconnected = disconnected;
this._rfb = undefined;
this._frame_length = this._frames.length;
this._frame_index = 0;
this._start_time = undefined;
this._realtime = true;
this._trafficManagement = true;
this._running = false;
this.onfinish = () => {};
}
run(realtime, trafficManagement) {
// initialize a new RFB
this._rfb = new RFB(document.getElementById('VNC_screen'), 'wss://test');
this._rfb.viewOnly = true;
this._rfb.addEventListener("disconnect",
this._handleDisconnect.bind(this));
this._rfb.addEventListener("credentialsrequired",
this._handleCredentials.bind(this));
this._enablePlaybackMode();
// reset the frame index and timer
this._frame_index = 0;
this._start_time = (new Date()).getTime();
this._realtime = realtime;
this._trafficManagement = (trafficManagement === undefined) ? !realtime : trafficManagement;
this._running = true;
}
// _enablePlaybackMode mocks out things not required for running playback
_enablePlaybackMode() {
const self = this;
this._rfb._sock.send = () => {};
this._rfb._sock.close = () => {};
this._rfb._sock.flush = () => {};
this._rfb._sock.open = function () {
this.init();
this._eventHandlers.open();
self._queueNextPacket();
};
}
_queueNextPacket() {
if (!this._running) { return; }
let frame = this._frames[this._frame_index];
// skip send frames
while (this._frame_index < this._frame_length && frame.fromClient) {
this._frame_index++;
frame = this._frames[this._frame_index];
}
if (this._frame_index >= this._frame_length) {
Log.Debug('Finished, no more frames');
this._finish();
return;
}
if (this._realtime) {
const toffset = (new Date()).getTime() - this._start_time;
let delay = frame.timestamp - toffset;
if (delay < 1) delay = 1;
setTimeout(this._doPacket.bind(this), delay);
} else {
setImmediate(this._doPacket.bind(this));
}
}
_doPacket() {
// Avoid having excessive queue buildup in non-realtime mode
if (this._trafficManagement && this._rfb._flushing) {
const orig = this._rfb._display.onflush;
this._rfb._display.onflush = () => {
this._rfb._display.onflush = orig;
this._rfb._onFlush();
this._doPacket();
};
return;
}
const frame = this._frames[this._frame_index];
this._rfb._sock._recv_message({'data': frame.data});
this._frame_index++;
this._queueNextPacket();
}
_finish() {
if (this._rfb._display.pending()) {
this._rfb._display.onflush = () => {
if (this._rfb._flushing) {
this._rfb._onFlush();
}
this._finish();
};
this._rfb._display.flush();
} else {
this._running = false;
this._rfb._sock._eventHandlers.close({code: 1000, reason: ""});
delete this._rfb;
this.onfinish((new Date()).getTime() - this._start_time);
}
}
_handleDisconnect(evt) {
this._running = false;
this._disconnected(evt.detail.clean, this._frame_index);
}
_handleCredentials(evt) {
this._rfb.sendCredentials({"username": "Foo",
"password": "Bar",
"target": "Baz"});
}
}