]>
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 | ||
5b20d338 | 47 | export default function RecordingPlayer (frames, encoding, disconnected) { |
d6c17390 SR |
48 | this._frames = frames; |
49 | this._encoding = encoding; | |
efed2eea | 50 | |
d6c17390 | 51 | this._disconnected = disconnected; |
efed2eea | 52 | |
d6c17390 SR |
53 | if (this._encoding === undefined) { |
54 | let frame = this._frames[0]; | |
55 | let start = frame.indexOf('{', 1) + 1; | |
d951b409 | 56 | if (frame.slice(start).startsWith('UkZC')) { |
d6c17390 | 57 | this._encoding = 'base64'; |
d951b409 | 58 | } else { |
d6c17390 | 59 | this._encoding = 'binary'; |
d951b409 | 60 | } |
d951b409 PO |
61 | } |
62 | ||
d6c17390 SR |
63 | this._rfb = undefined; |
64 | this._frame_length = this._frames.length; | |
b8bd88d0 | 65 | |
d6c17390 SR |
66 | this._frame_index = 0; |
67 | this._start_time = undefined; | |
68 | this._realtime = true; | |
69 | this._trafficManagement = true; | |
b8bd88d0 | 70 | |
d6c17390 | 71 | this._running = false; |
b8bd88d0 | 72 | |
d6c17390 SR |
73 | this.onfinish = function () {}; |
74 | } | |
b8bd88d0 | 75 | |
d6c17390 SR |
76 | RecordingPlayer.prototype = { |
77 | run: function (realtime, trafficManagement) { | |
78 | // initialize a new RFB | |
2f4516f2 | 79 | this._rfb = new RFB(document.getElementById('VNC_canvas'), '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 SR |
94 | this._queueNextPacket(); |
95 | }, | |
96 | ||
97 | // _enablePlaybackMode mocks out things not required for running playback | |
98 | _enablePlaybackMode: function () { | |
99 | this._rfb._sock.send = function (arr) {}; | |
100 | this._rfb._sock.close = function () {}; | |
101 | this._rfb._sock.flush = function () {}; | |
102 | this._rfb._checkEvents = function () {}; | |
2f4516f2 | 103 | this._rfb._connect = function () { |
d6c17390 | 104 | this._sock.init('binary', 'ws'); |
d6c17390 SR |
105 | }; |
106 | }, | |
107 | ||
108 | _queueNextPacket: function () { | |
109 | if (!this._running) { return; } | |
110 | ||
111 | var frame = this._frames[this._frame_index]; | |
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 SR |
131 | if (this._realtime) { |
132 | let foffset = frame.slice(1, frame.indexOf('{', 1)); | |
133 | let toffset = (new Date()).getTime() - this._start_time; | |
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 | } | |
141 | }, | |
142 | ||
143 | _doPacket: function () { | |
144 | // Avoid having excessive queue buildup in non-realtime mode | |
255fbe0c | 145 | if (this._trafficManagement && this._rfb._flushing) { |
d6c17390 | 146 | let player = this; |
747b4623 PO |
147 | let orig = this._rfb._display.onflush; |
148 | this._rfb._display.onflush = function () { | |
149 | player._rfb._display.onflush = orig; | |
255fbe0c | 150 | player._rfb._onFlush(); |
d6c17390 | 151 | player._doPacket(); |
747b4623 | 152 | }; |
d6c17390 | 153 | return; |
b8bd88d0 JM |
154 | } |
155 | ||
d6c17390 SR |
156 | const frame = this._frames[this._frame_index]; |
157 | var start = frame.indexOf('{', 1) + 1; | |
158 | if (this._encoding === 'base64') { | |
159 | var u8 = Base64.decode(frame.slice(start)); | |
160 | start = 0; | |
161 | } else { | |
162 | var u8 = new Uint8Array(frame.length - start); | |
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(); | |
172 | }, | |
173 | ||
174 | _finish() { | |
175 | if (this._rfb._display.pending()) { | |
176 | var player = this; | |
747b4623 | 177 | this._rfb._display.onflush = function () { |
d6c17390 SR |
178 | if (player._rfb._flushing) { |
179 | player._rfb._onFlush(); | |
180 | } | |
181 | player._finish(); | |
747b4623 | 182 | }; |
d6c17390 SR |
183 | this._rfb._display.flush(); |
184 | } else { | |
185 | this._running = false; | |
186 | this.onfinish((new Date()).getTime() - this._start_time); | |
e7e66602 | 187 | } |
d6c17390 | 188 | }, |
b8bd88d0 | 189 | |
d472f3f1 | 190 | _handleDisconnect(rfb, clean) { |
d6c17390 | 191 | this._running = false; |
d472f3f1 | 192 | this._disconnected(rfb, clean, this._frame_index); |
d6c17390 | 193 | } |
d595e656 | 194 | }; |