]>
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 |
d6c17390 | 12 | if (setImmediate === undefined) { |
e12e2759 PO |
13 | var _immediateIdCounter = 1; |
14 | var _immediateFuncs = {}; | |
15 | ||
d6c17390 SR |
16 | var setImmediate = function (func) { |
17 | var index = _immediateIdCounter++; | |
e12e2759 PO |
18 | _immediateFuncs[index] = func; |
19 | window.postMessage("noVNC immediate trigger:" + index, "*"); | |
20 | return index; | |
21 | }; | |
22 | ||
23 | window.clearImmediate = function (id) { | |
24 | _immediateFuncs[id]; | |
25 | }; | |
26 | ||
27 | var _onMessage = function (event) { | |
28 | if ((typeof event.data !== "string") || | |
29 | (event.data.indexOf("noVNC immediate trigger:") !== 0)) { | |
30 | return; | |
31 | } | |
32 | ||
33 | var index = event.data.slice("noVNC immediate trigger:".length); | |
34 | ||
35 | var callback = _immediateFuncs[index]; | |
36 | if (callback === undefined) { | |
37 | return; | |
38 | } | |
39 | ||
40 | delete _immediateFuncs[index]; | |
41 | ||
42 | callback(); | |
43 | }; | |
44 | window.addEventListener("message", _onMessage); | |
45 | } | |
46 | ||
d6c17390 SR |
47 | export default function RecordingPlayer (frames, encoding, disconnected, notification) { |
48 | this._frames = frames; | |
49 | this._encoding = encoding; | |
efed2eea | 50 | |
d6c17390 SR |
51 | this._disconnected = disconnected; |
52 | this._notification = notification; | |
efed2eea | 53 | |
d6c17390 SR |
54 | if (this._encoding === undefined) { |
55 | let frame = this._frames[0]; | |
56 | let start = frame.indexOf('{', 1) + 1; | |
d951b409 | 57 | if (frame.slice(start).startsWith('UkZC')) { |
d6c17390 | 58 | this._encoding = 'base64'; |
d951b409 | 59 | } else { |
d6c17390 | 60 | this._encoding = 'binary'; |
d951b409 | 61 | } |
d951b409 PO |
62 | } |
63 | ||
d6c17390 SR |
64 | this._rfb = undefined; |
65 | this._frame_length = this._frames.length; | |
b8bd88d0 | 66 | |
d6c17390 SR |
67 | this._frame_index = 0; |
68 | this._start_time = undefined; | |
69 | this._realtime = true; | |
70 | this._trafficManagement = true; | |
b8bd88d0 | 71 | |
d6c17390 | 72 | this._running = false; |
b8bd88d0 | 73 | |
d6c17390 SR |
74 | this.onfinish = function () {}; |
75 | } | |
b8bd88d0 | 76 | |
d6c17390 SR |
77 | RecordingPlayer.prototype = { |
78 | run: function (realtime, trafficManagement) { | |
79 | // initialize a new RFB | |
80 | this._rfb = new RFB({'target': document.getElementById('VNC_canvas'), | |
81 | 'view_only': true, | |
82 | 'onDisconnected': this._handleDisconnect.bind(this), | |
83 | 'onNotification': this._notification}); | |
84 | this._enablePlaybackMode(); | |
85 | ||
86 | // reset the frame index and timer | |
87 | this._frame_index = 0; | |
88 | this._start_time = (new Date()).getTime(); | |
89 | ||
90 | this._realtime = realtime; | |
91 | this._trafficManagement = (trafficManagement === undefined) ? !realtime : trafficManagement; | |
92 | ||
93 | this._running = true; | |
94 | ||
95 | // launch the tests | |
96 | this._rfb.connect('test', 0, 'bogus'); | |
97 | ||
98 | this._queueNextPacket(); | |
99 | }, | |
100 | ||
101 | // _enablePlaybackMode mocks out things not required for running playback | |
102 | _enablePlaybackMode: function () { | |
103 | this._rfb._sock.send = function (arr) {}; | |
104 | this._rfb._sock.close = function () {}; | |
105 | this._rfb._sock.flush = function () {}; | |
106 | this._rfb._checkEvents = function () {}; | |
107 | this._rfb.connect = function (host, port, password, path) { | |
108 | this._rfb_host = host; | |
109 | this._rfb_port = port; | |
110 | this._rfb_password = (password !== undefined) ? password : ""; | |
111 | this._rfb_path = (path !== undefined) ? path : ""; | |
112 | this._sock.init('binary', 'ws'); | |
113 | this._rfb_connection_state = 'connecting'; | |
114 | this._rfb_init_state = 'ProtocolVersion'; | |
115 | }; | |
116 | }, | |
117 | ||
118 | _queueNextPacket: function () { | |
119 | if (!this._running) { return; } | |
120 | ||
121 | var frame = this._frames[this._frame_index]; | |
122 | ||
123 | // skip send frames | |
124 | while (this._frame_index < this._frame_length && frame.charAt(0) === "}") { | |
125 | this._frame_index++; | |
126 | frame = this._frames[this._frame_index]; | |
127 | } | |
18e96092 | 128 | |
d6c17390 SR |
129 | if (frame === 'EOF') { |
130 | Log.Debug('Finished, found EOF'); | |
131 | this._finish(); | |
132 | return; | |
133 | } | |
b8bd88d0 | 134 | |
d6c17390 SR |
135 | if (this._frame_index >= this._frame_length) { |
136 | Log.Debug('Finished, no more frames'); | |
137 | this._finish(); | |
138 | return; | |
139 | } | |
b8bd88d0 | 140 | |
d6c17390 SR |
141 | if (this._realtime) { |
142 | let foffset = frame.slice(1, frame.indexOf('{', 1)); | |
143 | let toffset = (new Date()).getTime() - this._start_time; | |
144 | let delay = foffset - toffset; | |
145 | if (delay < 1) delay = 1; | |
b8bd88d0 | 146 | |
d6c17390 SR |
147 | setTimeout(this._doPacket.bind(this), delay); |
148 | } else { | |
149 | setImmediate(this._doPacket.bind(this)); | |
150 | } | |
151 | }, | |
152 | ||
153 | _doPacket: function () { | |
154 | // Avoid having excessive queue buildup in non-realtime mode | |
155 | if (!this._trafficManagement && this._rfb._flushing) { | |
156 | let player = this; | |
157 | this._rfb.display.set_onFlush(function () { | |
158 | this._rfb._display.set_onFlush(this._rfb._onFlush.bind(this._rfb)); | |
159 | this._rfb._onFlush(); | |
160 | player._doPacket(); | |
161 | }); | |
162 | return; | |
b8bd88d0 JM |
163 | } |
164 | ||
d6c17390 SR |
165 | const frame = this._frames[this._frame_index]; |
166 | var start = frame.indexOf('{', 1) + 1; | |
167 | if (this._encoding === 'base64') { | |
168 | var u8 = Base64.decode(frame.slice(start)); | |
169 | start = 0; | |
170 | } else { | |
171 | var u8 = new Uint8Array(frame.length - start); | |
172 | for (let i = 0; i < frame.length - start; i++) { | |
173 | u8[i] = frame.charCodeAt(start + i); | |
174 | } | |
175 | } | |
18e96092 | 176 | |
d6c17390 SR |
177 | this._rfb._sock._recv_message({'data': u8}); |
178 | this._frame_index++; | |
179 | ||
180 | this._queueNextPacket(); | |
181 | }, | |
182 | ||
183 | _finish() { | |
184 | if (this._rfb._display.pending()) { | |
185 | var player = this; | |
186 | this._rfb._display.set_onFlush(function () { | |
187 | if (player._rfb._flushing) { | |
188 | player._rfb._onFlush(); | |
189 | } | |
190 | player._finish(); | |
191 | }); | |
192 | this._rfb._display.flush(); | |
193 | } else { | |
194 | this._running = false; | |
195 | this.onfinish((new Date()).getTime() - this._start_time); | |
e7e66602 | 196 | } |
d6c17390 | 197 | }, |
b8bd88d0 | 198 | |
d6c17390 SR |
199 | _handleDisconnect(rfb, reason) { |
200 | this._running = false; | |
201 | this._disconnected(rfb, reason, this._frame_index); | |
202 | } | |
d595e656 | 203 | }; |