View | Details | Raw Unified | Return to bug 4822
Collapse All | Expand All

(-)noVNC/include/esd.js (+349 lines)
Line 0 Link Here
1
/*
2
 * EsounD Server
3
 * Copyright 2013 Samuel Mannehed for Cendio AB
4
 */
5
6
function ESD(defaults) {
7
"use strict";
8
9
var that           = {},  // Public API methods
10
    conf           = {},  // Configuration attributes
11
12
    // Pre-declare private functions used before definitions (jslint)
13
    handle_message, connect_audio, disconnect_audio, buffer_audio,
14
    parse_stream, parse_format, parse_rate,
15
16
    //
17
    // Private ESD namespace variables
18
    //
19
    esd_host = '',
20
    esd_port = 4910,
21
    esd_path = '',
22
23
    ws = null,                // Websock object
24
25
    ws_loop_timeout   = 1000, // time until we loop the ws connection
26
    ws_loop_timer     = null, // timer for looping the ws connection
27
28
    authenticated = false,    // true when the esd connection is authenticated
29
    little_endian = false,
30
    sixteen_bits  = false,
31
    stream        = false,    // true while streaming data
32
    play          = false,    // true while playing sound
33
    nrOfChannels  = 1,
34
    rate          = 0,        // Sample rate of audio
35
    name          = "",       // name of esd stream
36
37
    context = null,           // audio context
38
    node    = null,           // audio processing node
39
    buff    = new Array();    // buffer for audio data
40
41
// Configuration attributes
42
Util.conf_defaults(conf, that, defaults, [
43
    ['encrypt',            'rw', 'bool', false, 'Use TLS/SSL/wss encryption']
44
    ]);
45
46
//
47
// Setup routines
48
//
49
50
// Create the public API interface and initialize values that stay
51
// constant across connect/disconnect
52
function constructor() {
53
54
    ws = new Websock();
55
    ws.on('message', handle_message);
56
    ws.on('open', function() {
57
        console.log("ESD: WebSocket on-open event");
58
    });
59
    ws.on('close', function(e) {
60
        var msg = "";
61
        if (e.code) {
62
            msg = " (code: " + e.code;
63
            if (e.reason)
64
                msg += ", reason: " + e.reason;
65
            msg += ")";
66
        }
67
        console.log("ESD: WebSocket on-close event, msg: " + msg);
68
69
        // Loop
70
        ws_loop_timer = setTimeout(function() {
71
            connect();
72
        }, ws_loop_timeout);
73
    });
74
    ws.on('error', function(e) {
75
        console.log("ESD: WebSocket on-error event, data: " + e.data);
76
        if (ws_loop_timer) {
77
            console.log("ESD: Clearing websocket loop timer");
78
            clearTimeout(ws_loop_timer);
79
            ws_loop_timer = null;
80
        }
81
    });
82
83
    ws.init();
84
85
    /* Check web-socket-js if no builtin WebSocket support */
86
    if (Websock_native) {
87
        console.log("ESD: Using native WebSockets");
88
    } else {
89
        console.log("ESD: Using web-socket-js bridge. Flash version: " +
90
                  Util.Flash.version);
91
    }
92
93
    try {
94
        if (window.AudioContext)
95
            context = new AudioContext();
96
        else
97
            context = new webkitAudioContext();
98
    } catch(e) {
99
        console.log("ESD: Web Audio API is not supported in this browser: " + e);
100
    }
101
102
    return that;  // Return the public API interface
103
}
104
105
// On iOS devices the sound needs to start from an user initiated event
106
window.addEventListener('touchstart', function() {
107
108
    if (context.createScriptProcessor)
109
        node = context.createScriptProcessor(4096, 0, 1);
110
    else
111
        node = context.createJavaScriptNode(4096, 0, 1);
112
113
    var source = context.createBufferSource();
114
    var buffer = context.createBuffer(1, 1024, context.sampleRate)
115
    var data = buffer.getChannelData(0);
116
    for (var i = 0; i < data.length; i++)
117
        data[i] = 0;
118
    source.buffer = buffer;
119
    source.loop = true;
120
    source.connect(node);
121
    node.connect(context.destination);
122
123
    source.start(0);
124
125
}, false);
126
127
function connect() {
128
    var uri;
129
    
130
    if (typeof UsingSocketIO !== "undefined") {
131
        uri = "http://" + esd_host + ":" + esd_port + "/" + esd_path;
132
    } else {
133
        if (conf.encrypt)
134
            uri = "wss://";
135
        else
136
            uri = "ws://";
137
        uri += esd_host + ":" + esd_port + "/" + esd_path;
138
    }
139
    console.log("ESD: connecting to " + uri);
140
    ws.open(uri, ['binary','base64','audio']);
141
}
142
143
function disconnect() {
144
    ws.close();
145
}
146
147
//
148
// Utility routines
149
//
150
151
handle_message = function() {
152
    if (ws.rQlen() === 0) { return; }
153
154
    if (stream && play) {
155
        buffer_audio();
156
        return;
157
    }
158
159
    var opcode, esdkey, endian;
160
161
    // If we are not authenticated yet
162
    if (!authenticated) {
163
        if (ws.rQwait("init", 20, 0)) { return; }
164
165
        esdkey = ws.rQshiftBytes(16);
166
        endian = ws.rQshiftStr(4);
167
        little_endian = (endian == 'NDNE');
168
        if (little_endian)
169
            ws.send([1,0,0,0]);
170
        else
171
            ws.send([0,0,0,1]);
172
        authenticated = true;
173
174
        console.log("ESD: authenticated");
175
176
    } else {
177
        opcode = ws.rQshiftBytes(4);
178
179
        switch (opcode[0]) {
180
        case 1: //lock
181
        case 2: //unlock
182
        case 3: //stream-play
183
            if (ws.rQwait("stream-play", 136, 4)) { return; }
184
185
            // Parse the header
186
            parse_format(ws.rQshiftBytes(4));
187
            parse_rate(ws.rQshiftBytes(4));
188
            name = ws.rQshiftStr(128);
189
190
            console.log("ESD: --- Audio header --- ");
191
            console.log((sixteen_bits ? "16bit " : "") +
192
                        ((nrOfChannels == 1) ? "mono " : "stereo ") +
193
                        (stream ? "stream " : "") +
194
                        (play ? "play" : ""));
195
            console.log(rate + " Hz");
196
            console.log(name);
197
198
            connect_audio();
199
        case 5: //stream-mon
200
        case 6: //sample-cache
201
        case 7: //sample-free
202
        case 8: //sample-play
203
        case 9: //sample-loop
204
        case 10: //sample-stop
205
        case 11: //sample-kill
206
        case 12: //standby
207
        case 13: //resume
208
        case 14: //sample-getid
209
        case 15: //stream-filter
210
        case 16: //server-info
211
        case 17: //server-all-info
212
        case 18: //subscribe
213
        case 19: //unsubjcribe
214
        case 20: //stream-pan
215
        case 21: //sample-pan
216
        case 22: //standby-mode
217
        case 23: //latency
218
        }
219
    }
220
};
221
222
connect_audio = function() {
223
    // Reset the node
224
    if (node != null)
225
        node.disconnect();
226
227
    if (context.createScriptProcessor)
228
        node = context.createScriptProcessor(4096, 0, nrOfChannels);
229
    else
230
        node = context.createJavaScriptNode(4096, 0, nrOfChannels);
231
    node.onaudioprocess = parse_stream;
232
    node.connect(context.destination);
233
};
234
235
disconnect_audio = function() {
236
237
    node.onaudioprocess = null;
238
    node.disconnect();
239
240
    authenticated   = false;
241
    little_endian   = false;
242
    sixteen_bits    = false;
243
    stream          = false;
244
    play            = false;
245
    nrOfChannels    = 1;
246
    rate            = 0;
247
    name            = "";
248
249
    buff = new Array();
250
251
    console.log("ESD: Closed audio connection");
252
};
253
254
buffer_audio = function() {
255
    var ws_data = ws.rQshiftBytes(ws.rQlen());
256
    var l = buff.length;
257
    buff = buff.concat(ws_data);
258
    console.log("buffered " + (buff.length - l));
259
};
260
261
parse_stream = function(e) {
262
    var audio, n, channels, chan_positions, i16;
263
264
    if (buff.length > 0) {
265
        // Take buffered data
266
        audio = buff.splice(0, e.outputBuffer.length*4);
267
        console.log("playing " + audio.length)
268
269
        n = e.outputBuffer.numberOfChannels;
270
        channels = new Array(n);
271
        chan_positions = new Array(n);
272
        for (var c = 0; c < n; c++) {
273
            channels[c] = e.outputBuffer.getChannelData(c);
274
            channels[c].sampleRate = rate;
275
            chan_positions[c] = 0;
276
        }
277
278
        for (var i = 0; i < audio.length; i += 2) {
279
280
            // handle endian and convert to 16bit
281
            if (little_endian)
282
                i16 = audio[i] + (audio[i+1] << 8);
283
            else
284
                i16 = (audio[i+1] << 8) + audio[i];
285
286
            // handle signed
287
            if (i16 > 32767)
288
                i16 -= 65536;
289
290
            // put audio on the channels
291
            for (var c = 0; c < n; c++) {
292
                if ((i/2) % n == c) {
293
                    (channels[c])[chan_positions[c]] = i16 / 32767;
294
                    chan_positions[c]++;
295
                }
296
            }
297
        }
298
    } else {
299
        disconnect_audio();
300
    }
301
};
302
303
parse_format = function(ws_data) {
304
    var format = "", length = ws_data.length;
305
    if (little_endian) {
306
        for (var i = length - 1; i >= 0; i--)
307
            format += ws_data[i].toString(16);
308
    } else {
309
        for (var i = 0; i < length; i++)
310
            format += ws_data[i].toString(16);
311
    }
312
    sixteen_bits = format.charAt(format.length - 1) == 1;
313
    nrOfChannels = format.charAt(format.length - 2);
314
    stream       = format.charAt(format.length - 3) == 0;
315
    play         = format.charAt(format.length - 4) == 1;
316
};
317
318
parse_rate = function(ws_data) {
319
    var length = ws_data.length;
320
    if (little_endian) {
321
        for (var i = length - 1; i >= 0; i--)
322
            rate += ws_data[i] * Math.pow(2, 8 * i);
323
    } else {
324
        for (var i = 0; i < length; i++)
325
            rate += ws_data[i] * Math.pow(2, 8 * i);
326
    }
327
};
328
329
//
330
// Public API interface functions
331
//
332
333
that.connect = function(host, port, path) {
334
    esd_host = host;
335
    esd_port = port;
336
    esd_path = (path !== undefined) ? path : "";
337
 
338
   if ((!esd_host) || (!esd_port))
339
        return fail("Must set host and port");
340
    connect();
341
};
342
343
that.disconnect = function() {
344
    disconnect();
345
};
346
347
return constructor();  // Return the public API interface
348
349
}  // End of ESD()
(-)noVNC/include/ui.js (-1 / +4 lines)
Lines 27-33 Link Here
27
27
28
Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
28
Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
29
                   "input.js", "display.js", "jsunzip.js", "rfb.js",
29
                   "input.js", "display.js", "jsunzip.js", "rfb.js",
30
                   "keysym.js"]);
30
                   "keysym.js", "esd.js"]);
31
31
32
var UI = {
32
var UI = {
33
33
Lines 113-118 Link Here
113
                  'onClipboard': UI.clipReceive,
113
                  'onClipboard': UI.clipReceive,
114
                  'onFBUComplete': UI.FBUComplete,
114
                  'onFBUComplete': UI.FBUComplete,
115
                  'onDesktopName': UI.updateDocumentTitle});
115
                  'onDesktopName': UI.updateDocumentTitle});
116
    UI.esd = ESD({'encrypt': (window.location.protocol === "https:")});
116
117
117
    autoconnect = WebUtil.getQueryVar('autoconnect', false);
118
    autoconnect = WebUtil.getQueryVar('autoconnect', false);
118
    if (autoconnect === 'true' || autoconnect == '1') {
119
    if (autoconnect === 'true' || autoconnect == '1') {
Lines 663-668 Link Here
663
    UI.rfb.set_repeaterID(UI.getSetting('repeaterID'));
664
    UI.rfb.set_repeaterID(UI.getSetting('repeaterID'));
664
665
665
    UI.rfb.connect(host, port, password, path);
666
    UI.rfb.connect(host, port, password, path);
667
    UI.esd.connect(host, port, path);
666
668
667
    //Close dialog.
669
    //Close dialog.
668
    setTimeout(UI.setBarPosition, 100);
670
    setTimeout(UI.setBarPosition, 100);
Lines 672-677 Link Here
672
disconnect: function() {
674
disconnect: function() {
673
    UI.closeSettingsMenu();
675
    UI.closeSettingsMenu();
674
    UI.rfb.disconnect();
676
    UI.rfb.disconnect();
677
    UI.esd.disconnect();
675
678
676
    $D('noVNC_logo').style.display = "block";
679
    $D('noVNC_logo').style.display = "block";
677
    UI.connSettingsOpen = false;
680
    UI.connSettingsOpen = false;

Return to bug 4822