Files
noVNC/tests/playback-ui.js
T

219 lines
6.4 KiB
JavaScript
Raw Normal View History

2020-09-04 10:33:35 +02:00
/* global VNC_frame_data, VNC_frame_encoding */
2018-05-24 00:25:44 +03:00
2017-03-01 21:10:09 -05:00
import * as WebUtil from '../app/webutil.js';
import RecordingPlayer from './playback.js';
2019-01-08 11:05:05 +01:00
import Base64 from '../core/base64.js';
2017-03-01 21:10:09 -05:00
2018-05-24 00:27:09 +03:00
let frames = null;
2017-03-01 21:10:09 -05:00
function message(str) {
2018-05-24 00:27:09 +03:00
const cell = document.getElementById('messages');
2017-03-01 21:10:09 -05:00
cell.textContent += str + "\n";
cell.scrollTop = cell.scrollHeight;
}
function loadFile() {
const fname = WebUtil.getQueryVar('data', null);
if (!fname) {
return Promise.reject("Must specify data=FOO in query string.");
}
2019-01-08 11:05:05 +01:00
message("Loading " + fname + "...");
2017-05-22 13:44:48 +02:00
return new Promise((resolve, reject) => {
2018-05-24 00:27:09 +03:00
const script = document.createElement("script");
2017-05-22 13:44:48 +02:00
script.onload = resolve;
2023-04-04 17:02:21 +02:00
script.onerror = () => { reject("Failed to load " + fname); };
2017-05-22 13:44:48 +02:00
document.body.appendChild(script);
script.src = "../recordings/" + fname;
});
2017-03-01 21:10:09 -05:00
}
2017-05-22 13:44:48 +02:00
function enableUI() {
2018-05-24 00:27:09 +03:00
const iterations = WebUtil.getQueryVar('iterations', 3);
2017-03-01 21:10:09 -05:00
document.getElementById('iterations').value = iterations;
2018-05-24 00:27:09 +03:00
const mode = WebUtil.getQueryVar('mode', 3);
2017-03-01 21:10:09 -05:00
if (mode === 'realtime') {
document.getElementById('mode2').checked = true;
} else {
document.getElementById('mode1').checked = true;
}
2020-09-05 11:04:11 +02:00
/* eslint-disable-next-line camelcase */
2020-09-04 10:33:35 +02:00
message("Loaded " + VNC_frame_data.length + " frames");
2017-03-01 21:10:09 -05:00
const startButton = document.getElementById('startButton');
2017-05-13 01:56:35 +02:00
startButton.disabled = false;
2017-03-01 21:10:09 -05:00
startButton.addEventListener('click', start);
2019-01-08 11:05:05 +01:00
message("Converting...");
2020-09-05 11:04:11 +02:00
/* eslint-disable-next-line camelcase */
2020-09-04 10:33:35 +02:00
frames = VNC_frame_data;
2019-01-08 11:05:05 +01:00
let encoding;
2020-09-05 11:04:11 +02:00
/* eslint-disable camelcase */
2020-09-04 10:33:35 +02:00
if (window.VNC_frame_encoding) {
2020-09-05 11:04:11 +02:00
// Only present in older recordings
2020-09-04 10:33:35 +02:00
encoding = VNC_frame_encoding;
2020-09-05 11:04:11 +02:00
/* eslint-enable camelcase */
2019-01-08 11:05:05 +01:00
} else {
let frame = frames[0];
let start = frame.indexOf('{', 1) + 1;
2019-01-08 12:25:01 +01:00
if (frame.slice(start, start+4) === 'UkZC') {
2019-01-08 11:05:05 +01:00
encoding = 'base64';
} else {
encoding = 'binary';
}
}
for (let i = 0;i < frames.length;i++) {
let frame = frames[i];
if (frame === "EOF") {
frames.splice(i);
break;
}
let dataIdx = frame.indexOf('{', 1) + 1;
let time = parseInt(frame.slice(1, dataIdx - 1));
let u8;
if (encoding === 'base64') {
u8 = Base64.decode(frame.slice(dataIdx));
} else {
u8 = new Uint8Array(frame.length - dataIdx);
for (let j = 0; j < frame.length - dataIdx; j++) {
u8[j] = frame.charCodeAt(dataIdx + j);
}
}
frames[i] = { fromClient: frame[0] === '}',
timestamp: time,
data: u8 };
2018-09-06 17:22:40 +02:00
}
2019-01-08 11:05:05 +01:00
message("Ready");
2017-03-01 21:10:09 -05:00
}
2018-07-05 21:31:56 +02:00
class IterationPlayer {
2019-01-08 11:05:05 +01:00
constructor(iterations, frames) {
2018-07-05 21:31:56 +02:00
this._iterations = iterations;
2017-03-01 21:10:09 -05:00
2018-07-05 21:31:56 +02:00
this._iteration = undefined;
this._player = undefined;
2017-03-01 21:10:09 -05:00
2020-05-31 21:03:38 +02:00
this._startTime = undefined;
2017-03-01 21:10:09 -05:00
2018-07-05 21:31:56 +02:00
this._frames = frames;
2017-03-01 21:10:09 -05:00
2018-07-05 21:31:56 +02:00
this._state = 'running';
2017-03-01 21:10:09 -05:00
this.onfinish = () => {};
this.oniterationfinish = () => {};
this.rfbdisconnected = () => {};
2018-07-05 21:31:56 +02:00
}
2017-03-01 21:10:09 -05:00
2019-01-08 12:25:01 +01:00
start(realtime) {
2017-03-01 21:10:09 -05:00
this._iteration = 0;
2020-05-31 21:03:38 +02:00
this._startTime = (new Date()).getTime();
2017-03-01 21:10:09 -05:00
2019-01-08 12:25:01 +01:00
this._realtime = realtime;
2017-03-01 21:10:09 -05:00
this._nextIteration();
2018-07-05 21:31:56 +02:00
}
2017-03-01 21:10:09 -05:00
2018-07-05 21:31:56 +02:00
_nextIteration() {
2019-01-08 11:05:05 +01:00
const player = new RecordingPlayer(this._frames, this._disconnected.bind(this));
2017-03-01 21:10:09 -05:00
player.onfinish = this._iterationFinish.bind(this);
if (this._state !== 'running') { return; }
this._iteration++;
if (this._iteration > this._iterations) {
this._finish();
return;
}
2019-01-08 12:25:01 +01:00
player.run(this._realtime, false);
2018-07-05 21:31:56 +02:00
}
2017-03-01 21:10:09 -05:00
2018-07-05 21:31:56 +02:00
_finish() {
2017-03-01 21:10:09 -05:00
const endTime = (new Date()).getTime();
2020-05-31 21:03:38 +02:00
const totalDuration = endTime - this._startTime;
2017-03-01 21:10:09 -05:00
2019-01-08 12:25:42 +01:00
const evt = new CustomEvent('finish',
{ detail:
{ duration: totalDuration,
iterations: this._iterations } } );
2017-03-01 21:10:09 -05:00
this.onfinish(evt);
2018-07-05 21:31:56 +02:00
}
2017-03-01 21:10:09 -05:00
2018-07-05 21:31:56 +02:00
_iterationFinish(duration) {
2019-01-08 12:25:42 +01:00
const evt = new CustomEvent('iterationfinish',
{ detail:
{ duration: duration,
number: this._iteration } } );
2017-03-01 21:10:09 -05:00
this.oniterationfinish(evt);
this._nextIteration();
2018-07-05 21:31:56 +02:00
}
2017-03-01 21:10:09 -05:00
2018-07-05 21:31:56 +02:00
_disconnected(clean, frame) {
if (!clean) {
2017-03-01 21:10:09 -05:00
this._state = 'failed';
}
2019-01-08 12:25:42 +01:00
const evt = new CustomEvent('rfbdisconnected',
{ detail:
{ clean: clean,
frame: frame,
iteration: this._iteration } } );
2017-03-01 21:10:09 -05:00
this.onrfbdisconnected(evt);
2018-07-05 21:31:56 +02:00
}
}
2017-03-01 21:10:09 -05:00
function start() {
document.getElementById('startButton').value = "Running";
document.getElementById('startButton').disabled = true;
const iterations = document.getElementById('iterations').value;
2019-01-08 12:25:01 +01:00
let realtime;
2017-03-01 21:10:09 -05:00
if (document.getElementById('mode1').checked) {
message(`Starting performance playback (fullspeed) [${iterations} iteration(s)]`);
2019-01-08 12:25:01 +01:00
realtime = false;
2017-03-01 21:10:09 -05:00
} else {
message(`Starting realtime playback [${iterations} iteration(s)]`);
2019-01-08 12:25:01 +01:00
realtime = true;
2017-03-01 21:10:09 -05:00
}
2019-01-08 11:05:05 +01:00
const player = new IterationPlayer(iterations, frames);
player.oniterationfinish = (evt) => {
2019-01-08 12:25:42 +01:00
message(`Iteration ${evt.detail.number} took ${evt.detail.duration}ms`);
2017-03-01 21:10:09 -05:00
};
player.onrfbdisconnected = (evt) => {
2019-01-08 12:25:42 +01:00
if (!evt.detail.clean) {
message(`noVNC sent disconnected during iteration ${evt.detail.iteration} frame ${evt.detail.frame}`);
2023-04-04 17:02:57 +02:00
document.getElementById('startButton').disabled = false;
document.getElementById('startButton').value = "Start";
2017-03-01 21:10:09 -05:00
}
};
player.onfinish = (evt) => {
2019-01-08 12:25:42 +01:00
const iterTime = parseInt(evt.detail.duration / evt.detail.iterations, 10);
message(`${evt.detail.iterations} iterations took ${evt.detail.duration}ms (average ${iterTime}ms / iteration)`);
2017-03-01 21:10:09 -05:00
document.getElementById('startButton').disabled = false;
document.getElementById('startButton').value = "Start";
};
2019-01-08 12:25:01 +01:00
player.start(realtime);
2017-03-01 21:10:09 -05:00
}
loadFile().then(enableUI).catch(e => message("Error loading recording: " + e));