/* global VNC_frame_data, VNC_frame_encoding */ import * as WebUtil from '../app/webutil.js'; import RecordingPlayer from './playback.js'; import Base64 from '../core/base64.js'; let frames = null; function message(str) { const cell = document.getElementById('messages'); 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."); } message("Loading " + fname + "..."); return new Promise((resolve, reject) => { const script = document.createElement("script"); script.onload = resolve; script.onerror = reject; document.body.appendChild(script); script.src = "../recordings/" + fname; }); } function enableUI() { const iterations = WebUtil.getQueryVar('iterations', 3); document.getElementById('iterations').value = iterations; const mode = WebUtil.getQueryVar('mode', 3); if (mode === 'realtime') { document.getElementById('mode2').checked = true; } else { document.getElementById('mode1').checked = true; } message("Loaded " + VNC_frame_data.length + " frames"); const startButton = document.getElementById('startButton'); startButton.disabled = false; startButton.addEventListener('click', start); message("Converting..."); frames = VNC_frame_data; let encoding; // Only present in older recordings if (window.VNC_frame_encoding) { encoding = VNC_frame_encoding; } else { let frame = frames[0]; let start = frame.indexOf('{', 1) + 1; if (frame.slice(start, start+4) === 'UkZC') { 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 }; } message("Ready"); } class IterationPlayer { constructor(iterations, frames) { this._iterations = iterations; this._iteration = undefined; this._player = undefined; this._start_time = undefined; this._frames = frames; this._state = 'running'; this.onfinish = () => {}; this.oniterationfinish = () => {}; this.rfbdisconnected = () => {}; } start(realtime) { this._iteration = 0; this._start_time = (new Date()).getTime(); this._realtime = realtime; this._nextIteration(); } _nextIteration() { const player = new RecordingPlayer(this._frames, this._disconnected.bind(this)); player.onfinish = this._iterationFinish.bind(this); if (this._state !== 'running') { return; } this._iteration++; if (this._iteration > this._iterations) { this._finish(); return; } player.run(this._realtime, false); } _finish() { const endTime = (new Date()).getTime(); const totalDuration = endTime - this._start_time; const evt = new CustomEvent('finish', { detail: { duration: totalDuration, iterations: this._iterations } } ); this.onfinish(evt); } _iterationFinish(duration) { const evt = new CustomEvent('iterationfinish', { detail: { duration: duration, number: this._iteration } } ); this.oniterationfinish(evt); this._nextIteration(); } _disconnected(clean, frame) { if (!clean) { this._state = 'failed'; } const evt = new CustomEvent('rfbdisconnected', { detail: { clean: clean, frame: frame, iteration: this._iteration } } ); this.onrfbdisconnected(evt); } } function start() { document.getElementById('startButton').value = "Running"; document.getElementById('startButton').disabled = true; const iterations = document.getElementById('iterations').value; let realtime; if (document.getElementById('mode1').checked) { message(`Starting performance playback (fullspeed) [${iterations} iteration(s)]`); realtime = false; } else { message(`Starting realtime playback [${iterations} iteration(s)]`); realtime = true; } const player = new IterationPlayer(iterations, frames); player.oniterationfinish = (evt) => { message(`Iteration ${evt.detail.number} took ${evt.detail.duration}ms`); }; player.onrfbdisconnected = (evt) => { if (!evt.detail.clean) { message(`noVNC sent disconnected during iteration ${evt.detail.iteration} frame ${evt.detail.frame}`); } }; player.onfinish = (evt) => { const iterTime = parseInt(evt.detail.duration / evt.detail.iterations, 10); message(`${evt.detail.iterations} iterations took ${evt.detail.duration}ms (average ${iterTime}ms / iteration)`); document.getElementById('startButton').disabled = false; document.getElementById('startButton').value = "Start"; }; player.start(realtime); } loadFile().then(enableUI).catch(e => message("Error loading recording: " + e));