]>
Commit | Line | Data |
---|---|---|
d595e656 JM |
1 | /* |
2 | * noVNC: HTML5 VNC client | |
d58f8b51 | 3 | * Copyright (C) 2012 Joel Martin |
1d728ace | 4 | * Licensed under MPL 2.0 (see LICENSE.txt) |
d595e656 JM |
5 | */ |
6 | ||
d6c17390 SR |
7 | import RFB from '../core/rfb.js'; |
8 | import * as Log from '../core/util/logging.js'; | |
9 | import Base64 from '../core/base64.js'; | |
b8bd88d0 | 10 | |
e12e2759 | 11 | // Immediate polyfill |
2b5f94fa JD |
12 | if (window.setImmediate === undefined) { |
13 | let _immediateIdCounter = 1; | |
14 | const _immediateFuncs = {}; | |
e12e2759 | 15 | |
651c23ec | 16 | window.setImmediate = (func) => { |
2b5f94fa | 17 | const index = _immediateIdCounter++; |
e12e2759 PO |
18 | _immediateFuncs[index] = func; |
19 | window.postMessage("noVNC immediate trigger:" + index, "*"); | |
20 | return index; | |
21 | }; | |
22 | ||
651c23ec | 23 | window.clearImmediate = (id) => { |
e12e2759 PO |
24 | _immediateFuncs[id]; |
25 | }; | |
26 | ||
651c23ec | 27 | const _onMessage = (event) => { |
e12e2759 PO |
28 | if ((typeof event.data !== "string") || |
29 | (event.data.indexOf("noVNC immediate trigger:") !== 0)) { | |
30 | return; | |
31 | } | |
32 | ||
2b5f94fa | 33 | const index = event.data.slice("noVNC immediate trigger:".length); |
e12e2759 | 34 | |
2b5f94fa | 35 | const callback = _immediateFuncs[index]; |
e12e2759 PO |
36 | if (callback === undefined) { |
37 | return; | |
38 | } | |
39 | ||
40 | delete _immediateFuncs[index]; | |
41 | ||
42 | callback(); | |
43 | }; | |
44 | window.addEventListener("message", _onMessage); | |
45 | } | |
46 | ||
0e4808bf JD |
47 | export default class RecordingPlayer { |
48 | constructor(frames, encoding, disconnected) { | |
49 | this._frames = frames; | |
50 | this._encoding = encoding; | |
efed2eea | 51 | |
0e4808bf | 52 | this._disconnected = disconnected; |
efed2eea | 53 | |
0e4808bf | 54 | if (this._encoding === undefined) { |
2b5f94fa JD |
55 | const frame = this._frames[0]; |
56 | const start = frame.indexOf('{', 1) + 1; | |
0e4808bf JD |
57 | if (frame.slice(start).startsWith('UkZC')) { |
58 | this._encoding = 'base64'; | |
59 | } else { | |
60 | this._encoding = 'binary'; | |
61 | } | |
d951b409 | 62 | } |
d951b409 | 63 | |
0e4808bf JD |
64 | this._rfb = undefined; |
65 | this._frame_length = this._frames.length; | |
b8bd88d0 | 66 | |
0e4808bf JD |
67 | this._frame_index = 0; |
68 | this._start_time = undefined; | |
69 | this._realtime = true; | |
70 | this._trafficManagement = true; | |
b8bd88d0 | 71 | |
0e4808bf | 72 | this._running = false; |
b8bd88d0 | 73 | |
651c23ec | 74 | this.onfinish = () => {}; |
0e4808bf | 75 | } |
b8bd88d0 | 76 | |
0e4808bf | 77 | run(realtime, trafficManagement) { |
d6c17390 | 78 | // initialize a new RFB |
a92c3317 | 79 | this._rfb = new RFB(document.getElementById('VNC_screen'), 'wss://test'); |
747b4623 | 80 | this._rfb.viewOnly = true; |
d472f3f1 SM |
81 | this._rfb.addEventListener("disconnect", |
82 | this._handleDisconnect.bind(this)); | |
d6c17390 SR |
83 | this._enablePlaybackMode(); |
84 | ||
85 | // reset the frame index and timer | |
86 | this._frame_index = 0; | |
87 | this._start_time = (new Date()).getTime(); | |
88 | ||
89 | this._realtime = realtime; | |
90 | this._trafficManagement = (trafficManagement === undefined) ? !realtime : trafficManagement; | |
91 | ||
92 | this._running = true; | |
93 | ||
d6c17390 | 94 | this._queueNextPacket(); |
0e4808bf | 95 | } |
d6c17390 SR |
96 | |
97 | // _enablePlaybackMode mocks out things not required for running playback | |
0e4808bf | 98 | _enablePlaybackMode() { |
651c23ec JD |
99 | this._rfb._sock.send = () => {}; |
100 | this._rfb._sock.close = () => {}; | |
101 | this._rfb._sock.flush = () => {}; | |
a92c3317 PO |
102 | this._rfb._sock.open = function () { |
103 | this.init(); | |
104 | this._eventHandlers.open(); | |
d6c17390 | 105 | }; |
0e4808bf | 106 | } |
d6c17390 | 107 | |
0e4808bf | 108 | _queueNextPacket() { |
d6c17390 SR |
109 | if (!this._running) { return; } |
110 | ||
2b5f94fa | 111 | let frame = this._frames[this._frame_index]; |
d6c17390 SR |
112 | |
113 | // skip send frames | |
114 | while (this._frame_index < this._frame_length && frame.charAt(0) === "}") { | |
115 | this._frame_index++; | |
116 | frame = this._frames[this._frame_index]; | |
117 | } | |
18e96092 | 118 | |
d6c17390 SR |
119 | if (frame === 'EOF') { |
120 | Log.Debug('Finished, found EOF'); | |
121 | this._finish(); | |
122 | return; | |
123 | } | |
b8bd88d0 | 124 | |
d6c17390 SR |
125 | if (this._frame_index >= this._frame_length) { |
126 | Log.Debug('Finished, no more frames'); | |
127 | this._finish(); | |
128 | return; | |
129 | } | |
b8bd88d0 | 130 | |
d6c17390 | 131 | if (this._realtime) { |
2b5f94fa JD |
132 | const foffset = frame.slice(1, frame.indexOf('{', 1)); |
133 | const toffset = (new Date()).getTime() - this._start_time; | |
d6c17390 SR |
134 | let delay = foffset - toffset; |
135 | if (delay < 1) delay = 1; | |
b8bd88d0 | 136 | |
d6c17390 SR |
137 | setTimeout(this._doPacket.bind(this), delay); |
138 | } else { | |
139 | setImmediate(this._doPacket.bind(this)); | |
140 | } | |
0e4808bf | 141 | } |
d6c17390 | 142 | |
0e4808bf | 143 | _doPacket() { |
d6c17390 | 144 | // Avoid having excessive queue buildup in non-realtime mode |
255fbe0c | 145 | if (this._trafficManagement && this._rfb._flushing) { |
2b5f94fa | 146 | const orig = this._rfb._display.onflush; |
651c23ec JD |
147 | this._rfb._display.onflush = () => { |
148 | this._rfb._display.onflush = orig; | |
149 | this._rfb._onFlush(); | |
150 | this._doPacket(); | |
747b4623 | 151 | }; |
d6c17390 | 152 | return; |
b8bd88d0 JM |
153 | } |
154 | ||
d6c17390 | 155 | const frame = this._frames[this._frame_index]; |
2b5f94fa JD |
156 | let start = frame.indexOf('{', 1) + 1; |
157 | let u8; | |
d6c17390 | 158 | if (this._encoding === 'base64') { |
8727f598 | 159 | u8 = Base64.decode(frame.slice(start)); |
d6c17390 SR |
160 | start = 0; |
161 | } else { | |
8727f598 | 162 | u8 = new Uint8Array(frame.length - start); |
d6c17390 SR |
163 | for (let i = 0; i < frame.length - start; i++) { |
164 | u8[i] = frame.charCodeAt(start + i); | |
165 | } | |
166 | } | |
18e96092 | 167 | |
d6c17390 SR |
168 | this._rfb._sock._recv_message({'data': u8}); |
169 | this._frame_index++; | |
170 | ||
171 | this._queueNextPacket(); | |
0e4808bf | 172 | } |
d6c17390 SR |
173 | |
174 | _finish() { | |
175 | if (this._rfb._display.pending()) { | |
651c23ec JD |
176 | this._rfb._display.onflush = () => { |
177 | if (this._rfb._flushing) { | |
178 | this._rfb._onFlush(); | |
d6c17390 | 179 | } |
651c23ec | 180 | this._finish(); |
747b4623 | 181 | }; |
d6c17390 SR |
182 | this._rfb._display.flush(); |
183 | } else { | |
184 | this._running = false; | |
609a3fac PO |
185 | this._rfb._sock._eventHandlers.close({code: 1000, reason: ""}); |
186 | delete this._rfb; | |
d6c17390 | 187 | this.onfinish((new Date()).getTime() - this._start_time); |
e7e66602 | 188 | } |
0e4808bf | 189 | } |
b8bd88d0 | 190 | |
a92c3317 | 191 | _handleDisconnect(evt) { |
d6c17390 | 192 | this._running = false; |
a92c3317 | 193 | this._disconnected(evt.detail.clean, this._frame_index); |
d6c17390 | 194 | } |
0e4808bf | 195 | } |