]> git.proxmox.com Git - mirror_novnc.git/blame - tests/playback-ui.js
Add deflator helper class for deflating data
[mirror_novnc.git] / tests / playback-ui.js
CommitLineData
8727f598
JD
1/* global VNC_frame_data, VNC_frame_encoding */
2
d6c17390
SR
3import * as WebUtil from '../app/webutil.js';
4import RecordingPlayer from './playback.js';
527a1fd0 5import Base64 from '../core/base64.js';
d6c17390 6
2b5f94fa 7let frames = null;
d6c17390
SR
8
9function message(str) {
2b5f94fa 10 const cell = document.getElementById('messages');
d6c17390
SR
11 cell.textContent += str + "\n";
12 cell.scrollTop = cell.scrollHeight;
13}
14
15function loadFile() {
16 const fname = WebUtil.getQueryVar('data', null);
17
18 if (!fname) {
19 return Promise.reject("Must specify data=FOO in query string.");
20 }
21
527a1fd0 22 message("Loading " + fname + "...");
7c1f1a9c 23
651c23ec 24 return new Promise((resolve, reject) => {
2b5f94fa 25 const script = document.createElement("script");
7c1f1a9c
PO
26 script.onload = resolve;
27 script.onerror = reject;
28 document.body.appendChild(script);
29 script.src = "../recordings/" + fname;
30 });
d6c17390
SR
31}
32
7c1f1a9c 33function enableUI() {
2b5f94fa 34 const iterations = WebUtil.getQueryVar('iterations', 3);
d6c17390
SR
35 document.getElementById('iterations').value = iterations;
36
2b5f94fa 37 const mode = WebUtil.getQueryVar('mode', 3);
d6c17390
SR
38 if (mode === 'realtime') {
39 document.getElementById('mode2').checked = true;
40 } else {
41 document.getElementById('mode1').checked = true;
42 }
43
527a1fd0 44 message("Loaded " + VNC_frame_data.length + " frames");
d6c17390
SR
45
46 const startButton = document.getElementById('startButton');
afb621d5 47 startButton.disabled = false;
d6c17390
SR
48 startButton.addEventListener('click', start);
49
527a1fd0
PO
50 message("Converting...");
51
7c1f1a9c 52 frames = VNC_frame_data;
527a1fd0
PO
53
54 let encoding;
b05a7b1d 55 // Only present in older recordings
426a8c92 56 if (window.VNC_frame_encoding) {
b05a7b1d 57 encoding = VNC_frame_encoding;
527a1fd0
PO
58 } else {
59 let frame = frames[0];
60 let start = frame.indexOf('{', 1) + 1;
568f6567 61 if (frame.slice(start, start+4) === 'UkZC') {
527a1fd0
PO
62 encoding = 'base64';
63 } else {
64 encoding = 'binary';
65 }
66 }
67
68 for (let i = 0;i < frames.length;i++) {
69 let frame = frames[i];
70
71 if (frame === "EOF") {
72 frames.splice(i);
73 break;
74 }
75
76 let dataIdx = frame.indexOf('{', 1) + 1;
77
78 let time = parseInt(frame.slice(1, dataIdx - 1));
79
80 let u8;
81 if (encoding === 'base64') {
82 u8 = Base64.decode(frame.slice(dataIdx));
83 } else {
84 u8 = new Uint8Array(frame.length - dataIdx);
85 for (let j = 0; j < frame.length - dataIdx; j++) {
86 u8[j] = frame.charCodeAt(dataIdx + j);
87 }
88 }
89
90 frames[i] = { fromClient: frame[0] === '}',
91 timestamp: time,
92 data: u8 };
426a8c92 93 }
527a1fd0
PO
94
95 message("Ready");
d6c17390
SR
96}
97
0e4808bf 98class IterationPlayer {
527a1fd0 99 constructor(iterations, frames) {
0e4808bf 100 this._iterations = iterations;
d6c17390 101
0e4808bf
JD
102 this._iteration = undefined;
103 this._player = undefined;
d6c17390 104
0e4808bf 105 this._start_time = undefined;
d6c17390 106
0e4808bf 107 this._frames = frames;
d6c17390 108
0e4808bf 109 this._state = 'running';
d6c17390 110
651c23ec
JD
111 this.onfinish = () => {};
112 this.oniterationfinish = () => {};
113 this.rfbdisconnected = () => {};
0e4808bf 114 }
d6c17390 115
568f6567 116 start(realtime) {
d6c17390
SR
117 this._iteration = 0;
118 this._start_time = (new Date()).getTime();
119
568f6567 120 this._realtime = realtime;
d6c17390
SR
121
122 this._nextIteration();
0e4808bf 123 }
d6c17390 124
0e4808bf 125 _nextIteration() {
527a1fd0 126 const player = new RecordingPlayer(this._frames, this._disconnected.bind(this));
d6c17390
SR
127 player.onfinish = this._iterationFinish.bind(this);
128
129 if (this._state !== 'running') { return; }
130
131 this._iteration++;
132 if (this._iteration > this._iterations) {
133 this._finish();
134 return;
135 }
136
568f6567 137 player.run(this._realtime, false);
0e4808bf 138 }
d6c17390 139
0e4808bf 140 _finish() {
d6c17390
SR
141 const endTime = (new Date()).getTime();
142 const totalDuration = endTime - this._start_time;
143
e3557022
PO
144 const evt = new CustomEvent('finish',
145 { detail:
146 { duration: totalDuration,
147 iterations: this._iterations } } );
d6c17390 148 this.onfinish(evt);
0e4808bf 149 }
d6c17390 150
0e4808bf 151 _iterationFinish(duration) {
e3557022
PO
152 const evt = new CustomEvent('iterationfinish',
153 { detail:
154 { duration: duration,
155 number: this._iteration } } );
d6c17390
SR
156 this.oniterationfinish(evt);
157
158 this._nextIteration();
0e4808bf 159 }
d6c17390 160
0e4808bf 161 _disconnected(clean, frame) {
d472f3f1 162 if (!clean) {
d6c17390
SR
163 this._state = 'failed';
164 }
165
e3557022
PO
166 const evt = new CustomEvent('rfbdisconnected',
167 { detail:
168 { clean: clean,
169 frame: frame,
170 iteration: this._iteration } } );
d6c17390 171 this.onrfbdisconnected(evt);
0e4808bf
JD
172 }
173}
d6c17390
SR
174
175function start() {
176 document.getElementById('startButton').value = "Running";
177 document.getElementById('startButton').disabled = true;
178
179 const iterations = document.getElementById('iterations').value;
180
568f6567 181 let realtime;
d6c17390
SR
182
183 if (document.getElementById('mode1').checked) {
184 message(`Starting performance playback (fullspeed) [${iterations} iteration(s)]`);
568f6567 185 realtime = false;
d6c17390
SR
186 } else {
187 message(`Starting realtime playback [${iterations} iteration(s)]`);
568f6567 188 realtime = true;
d6c17390
SR
189 }
190
527a1fd0 191 const player = new IterationPlayer(iterations, frames);
651c23ec 192 player.oniterationfinish = (evt) => {
e3557022 193 message(`Iteration ${evt.detail.number} took ${evt.detail.duration}ms`);
d6c17390 194 };
651c23ec 195 player.onrfbdisconnected = (evt) => {
e3557022
PO
196 if (!evt.detail.clean) {
197 message(`noVNC sent disconnected during iteration ${evt.detail.iteration} frame ${evt.detail.frame}`);
d6c17390
SR
198 }
199 };
651c23ec 200 player.onfinish = (evt) => {
e3557022
PO
201 const iterTime = parseInt(evt.detail.duration / evt.detail.iterations, 10);
202 message(`${evt.detail.iterations} iterations took ${evt.detail.duration}ms (average ${iterTime}ms / iteration)`);
d6c17390
SR
203
204 document.getElementById('startButton').disabled = false;
205 document.getElementById('startButton').value = "Start";
206 };
568f6567 207 player.start(realtime);
d6c17390
SR
208}
209
651c23ec 210loadFile().then(enableUI).catch(e => message("Error loading recording: " + e));