- * ws : {
- * uri : URI to conntect to,
- * useSockJS : true (use SockJS) / false (use WebSocket) by default,
- * onconnected : callback method to invoke when connection is successful,
- * ondisconnect : callback method to invoke when the connection is lost,
- * onreconnecting : callback method to invoke when the client is reconnecting,
- * onreconnected : callback method to invoke when the client succesfully reconnects,
- * },
- * rpc : {
- * requestTimeout : timeout for a request,
- * sessionStatusChanged: callback method for changes in session status,
- * mediaRenegotiation: mediaRenegotiation
- * }
- *
- */
-function JsonRpcClient(configuration) {
-
- var self = this;
-
- var wsConfig = configuration.ws;
-
- var notReconnectIfNumLessThan = -1;
-
- var pingNextNum = 0;
- var enabledPings = true;
- var pingPongStarted = false;
- var pingInterval;
-
- var status = DISCONNECTED;
-
- var onreconnecting = wsConfig.onreconnecting;
- var onreconnected = wsConfig.onreconnected;
- var onconnected = wsConfig.onconnected;
-
- configuration.rpc.pull = function(params, request) {
- request.reply(null, "push");
- }
-
- wsConfig.onreconnecting = function() {
- console.log("--------- ONRECONNECTING -----------");
- if (status === RECONNECTING) {
- console.error("Websocket already in RECONNECTING state when receiving a new ONRECONNECTING message. Ignoring it");
- return;
- }
-
- status = RECONNECTING;
- if (onreconnecting) {
- onreconnecting();
- }
- }
-
- wsConfig.onreconnected = function() {
- console.log("--------- ONRECONNECTED -----------");
- if (status === CONNECTED) {
- console.error("Websocket already in CONNECTED state when receiving a new ONRECONNECTED message. Ignoring it");
- return;
- }
- status = CONNECTED;
-
- enabledPings = true;
- updateNotReconnectIfLessThan();
- usePing();
-
- if (onreconnected) {
- onreconnected();
- }
- }
-
- wsConfig.onconnected = function() {
- console.log("--------- ONCONNECTED -----------");
- if (status === CONNECTED) {
- console.error("Websocket already in CONNECTED state when receiving a new ONCONNECTED message. Ignoring it");
- return;
- }
- status = CONNECTED;
-
- enabledPings = true;
- usePing();
-
- if (onconnected) {
- onconnected();
- }
- }
-
- var ws = new WebSocketWithReconnection(wsConfig);
-
- console.log('Connecting websocket to URI: ' + wsConfig.uri);
-
- var rpcBuilderOptions = {
- request_timeout: configuration.rpc.requestTimeout
- };
-
- var rpc = new RpcBuilder(RpcBuilder.packers.JsonRPC, rpcBuilderOptions, ws,
- function(request) {
-
- console.log('Received request: ' + JSON.stringify(request));
-
- try {
- var func = configuration.rpc[request.method];
-
- if (func === undefined) {
- console.error("Method " + request.method + " not registered in client");
- } else {
- func(request.params, request);
- }
- } catch (err) {
- console.error('Exception processing request: ' + JSON.stringify(request));
- console.error(err);
- }
- });
-
- this.send = function(method, params, callback) {
- if (method !== 'ping') {
- console.log('Request: method:' + method + " params:" + JSON.stringify(params));
- }
-
- var requestTime = Date.now();
-
- rpc.encode(method, params, function(error, result) {
- if (error) {
- try {
- console.error("ERROR:" + error.message + " in Request: method:" + method + " params:" + JSON.stringify(params));
- if (error.data) {
- console.error("ERROR DATA:" + JSON.stringify(error.data));
- }
- } catch (e) {}
- error.requestTime = requestTime;
- }
- if (callback) {
- if (result != undefined && result.value !== 'pong') {
- console.log('Response: ' + JSON.stringify(result));
- }
- callback(error, result);
- }
- });
- }
-
- function updateNotReconnectIfLessThan() {
- notReconnectIfNumLessThan = pingNextNum;
- console.log("notReconnectIfNumLessThan = " + notReconnectIfNumLessThan);
- }
-
- function sendPing() {
- if (enabledPings) {
- var params = null;
-
- if (pingNextNum == 0 || pingNextNum == notReconnectIfNumLessThan) {
- params = {
- interval: PING_INTERVAL
- };
- }
-
- pingNextNum++;
-
- self.send('ping', params, (function(pingNum) {
- return function(error, result) {
- if (error) {
- if (pingNum > notReconnectIfNumLessThan) {
- enabledPings = false;
- updateNotReconnectIfLessThan();
- console.log("DSS did not respond to ping message " + pingNum + ". Reconnecting... ");
- ws.reconnectWs();
- }
- }
- }
- })(pingNextNum));
- } else {
- console.log("Trying to send ping, but ping is not enabled");
- }
- }
-
- /*
- * If configuration.hearbeat has any value, the ping-pong will work with the interval
- * of configuration.hearbeat
- */
- function usePing() {
- if (!pingPongStarted) {
- console.log("Starting ping (if configured)")
- pingPongStarted = true;
-
- if (configuration.heartbeat != undefined) {
- pingInterval = setInterval(sendPing, configuration.heartbeat);
- sendPing();
- }
- }
- }
-
- this.close = function() {
- console.log("Closing jsonRpcClient explicitely by client");
-
- if (pingInterval != undefined) {
- clearInterval(pingInterval);
- }
- pingPongStarted = false;
- enabledPings = false;
-
- if (configuration.sendCloseMessage) {
- this.send('closeSession', null, function(error, result) {
- if (error) {
- console.error("Error sending close message: " + JSON.stringify(error));
- }
-
- ws.close();
- });
- } else {
- ws.close();
- }
- }
-
- // This method is only for testing
- this.forceClose = function(millis) {
- ws.forceClose(millis);
- }
-
- this.reconnect = function() {
- ws.reconnectWs();
- }
-}
-
-
-module.exports = JsonRpcClient;
-
-},{"../..":14,"./transports/webSocketWithReconnection":13}],12:[function(require,module,exports){
-/*
- * (C) Copyright 2014 Kurento (http://kurento.org/)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-var WebSocketWithReconnection = require('./webSocketWithReconnection');
-
-
-exports.WebSocketWithReconnection = WebSocketWithReconnection;
-},{"./webSocketWithReconnection":13}],13:[function(require,module,exports){
-/*
- * (C) Copyright 2013-2015 Kurento (http://kurento.org/)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-"use strict";
-
-var WebSocket = require('ws');
-var SockJS = require('sockjs-client');
-
-var MAX_RETRIES = 2000; // Forever...
-var RETRY_TIME_MS = 3000; // FIXME: Implement exponential wait times...
-var PING_INTERVAL = 5000;
-var PING_MSG = JSON.stringify({
- 'method': 'ping'
-});
-
-var CONNECTING = 0;
-var OPEN = 1;
-var CLOSING = 2;
-var CLOSED = 3;
-
-/*
-config = {
- uri : wsUri,
- useSockJS : true (use SockJS) / false (use WebSocket) by default,
- onconnected : callback method to invoke when connection is successful,
- ondisconnect : callback method to invoke when the connection is lost,
- onreconnecting : callback method to invoke when the client is reconnecting,
- onreconnected : callback method to invoke when the client succesfully reconnects,
- };
-*/
-function WebSocketWithReconnection(config) {
-
- var closing = false;
- var registerMessageHandler;
- var wsUri = config.uri;
- var useSockJS = config.useSockJS;
- var reconnecting = false;
-
- var forcingDisconnection = false;
-
- var ws;
-
- if (useSockJS) {
- ws = new SockJS(wsUri);
- } else {
- ws = new WebSocket(wsUri);
- }
-
- ws.onopen = function() {
- logConnected(ws, wsUri);
- config.onconnected();
- };
-
- ws.onerror = function(evt) {
- config.onconnected(evt.data);
- };
-
- function logConnected(ws, wsUri) {
- try {
- console.log("WebSocket connected to " + wsUri);
- } catch (e) {
- console.error(e);
- }
- }
-
- var reconnectionOnClose = function() {
- if (ws.readyState === CLOSED) {
- if (closing) {
- console.log("Connection Closed by user");
- } else {
- console.log("Connection closed unexpectecly. Reconnecting...");
- reconnectInNewUri(MAX_RETRIES, 1);
- }
- } else {
- console.log("Close callback from previous websocket. Ignoring it");
- }
- };
-
- ws.onclose = reconnectionOnClose;
-
- function reconnectInNewUri(maxRetries, numRetries) {
- console.log("reconnectInNewUri");
-
- if (numRetries === 1) {
- if (reconnecting) {
- console
- .warn("Trying to reconnect when reconnecting... Ignoring this reconnection.")
- return;
- } else {
- reconnecting = true;
- }
-
- if (config.onreconnecting) {
- config.onreconnecting();
- }
- }
-
- if (forcingDisconnection) {
- reconnect(maxRetries, numRetries, wsUri);
-
- } else {
- if (config.newWsUriOnReconnection) {
- config.newWsUriOnReconnection(function(error, newWsUri) {
-
- if (error) {
- console.log(error);
- setTimeout(function() {
- reconnectInNewUri(maxRetries, numRetries + 1);
- }, RETRY_TIME_MS);
- } else {
- reconnect(maxRetries, numRetries, newWsUri);
- }
- })
- } else {
- reconnect(maxRetries, numRetries, wsUri);
- }
- }
- }
-
- // TODO Test retries. How to force not connection?
- function reconnect(maxRetries, numRetries, reconnectWsUri) {
-
- console.log("Trying to reconnect " + numRetries + " times");
-
- var newWs;
- if (useSockJS) {
- newWs = new SockJS(wsUri);
- } else {
- newWs = new WebSocket(wsUri);
- }
-
- newWs.onopen = function() {
- console.log("Reconnected in " + numRetries + " retries...");
- logConnected(newWs, reconnectWsUri);
- reconnecting = false;
- registerMessageHandler();
- if (config.onreconnected()) {
- config.onreconnected();
- }
-
- newWs.onclose = reconnectionOnClose;
- };
-
- var onErrorOrClose = function(error) {
- console.log("Reconnection error: ", error);
-
- if (numRetries === maxRetries) {
- if (config.ondisconnect) {
- config.ondisconnect();
- }
- } else {
- setTimeout(function() {
- reconnectInNewUri(maxRetries, numRetries + 1);
- }, RETRY_TIME_MS);
- }
- };
-
- newWs.onerror = onErrorOrClose;
-
- ws = newWs;
- }
-
- this.close = function() {
- closing = true;
- ws.close();
- };
-
-
- // This method is only for testing
- this.forceClose = function(millis) {
- console.log("Testing: Force WebSocket close");
-
- if (millis) {
- console.log("Testing: Change wsUri for " + millis + " millis to simulate net failure");
- var goodWsUri = wsUri;
- wsUri = "wss://21.234.12.34.4:443/";
-
- forcingDisconnection = true;
-
- setTimeout(function() {
- console.log("Testing: Recover good wsUri " + goodWsUri);
- wsUri = goodWsUri;
-
- forcingDisconnection = false;
-
- }, millis);
- }
-
- ws.close();
- };
-
- this.reconnectWs = function() {
- console.log("reconnectWs");
- reconnectInNewUri(MAX_RETRIES, 1, wsUri);
- };
-
- this.send = function(message) {
- ws.send(message);
- };
-
- this.addEventListener = function(type, callback) {
- registerMessageHandler = function() {
- ws.addEventListener(type, callback);
- };
-
- registerMessageHandler();
- };
-}
-
-module.exports = WebSocketWithReconnection;
-},{"sockjs-client":34,"ws":104}],14:[function(require,module,exports){
-/*
- * (C) Copyright 2014 Kurento (http://kurento.org/)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-
-var defineProperty_IE8 = false
-if(Object.defineProperty)
-{
- try
- {
- Object.defineProperty({}, "x", {});
- }
- catch(e)
- {
- defineProperty_IE8 = true
- }
-}
-
-// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
-if (!Function.prototype.bind) {
- Function.prototype.bind = function(oThis) {
- if (typeof this !== 'function') {
- // closest thing possible to the ECMAScript 5
- // internal IsCallable function
- throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
- }
-
- var aArgs = Array.prototype.slice.call(arguments, 1),
- fToBind = this,
- fNOP = function() {},
- fBound = function() {
- return fToBind.apply(this instanceof fNOP && oThis
- ? this
- : oThis,
- aArgs.concat(Array.prototype.slice.call(arguments)));
- };
-
- fNOP.prototype = this.prototype;
- fBound.prototype = new fNOP();
-
- return fBound;
- };
-}
-
-
-var EventEmitter = require('events').EventEmitter;
-
-var inherits = require('inherits');
-
-var packers = require('./packers');
-var Mapper = require('./Mapper');
-
-
-var BASE_TIMEOUT = 5000;
-
-
-function unifyResponseMethods(responseMethods)
-{
- if(!responseMethods) return {};
-
- for(var key in responseMethods)
- {
- var value = responseMethods[key];
-
- if(typeof value == 'string')
- responseMethods[key] =
- {
- response: value
- }
- };
-
- return responseMethods;
-};
-
-function unifyTransport(transport)
-{
- if(!transport) return;
-
- // Transport as a function
- if(transport instanceof Function)
- return {send: transport};
-
- // WebSocket & DataChannel
- if(transport.send instanceof Function)
- return transport;
-
- // Message API (Inter-window & WebWorker)
- if(transport.postMessage instanceof Function)
- {
- transport.send = transport.postMessage;
- return transport;
- }
-
- // Stream API
- if(transport.write instanceof Function)
- {
- transport.send = transport.write;
- return transport;
- }
-
- // Transports that only can receive messages, but not send
- if(transport.onmessage !== undefined) return;
- if(transport.pause instanceof Function) return;
-
- throw new SyntaxError("Transport is not a function nor a valid object");
-};
-
-
-/**
- * Representation of a RPC notification
- *
- * @class
- *
- * @constructor
- *
- * @param {String} method -method of the notification
- * @param params - parameters of the notification
- */
-function RpcNotification(method, params)
-{
- if(defineProperty_IE8)
- {
- this.method = method
- this.params = params
- }
- else
- {
- Object.defineProperty(this, 'method', {value: method, enumerable: true});
- Object.defineProperty(this, 'params', {value: params, enumerable: true});
- }
-};
-
-
-/**
- * @class
- *
- * @constructor
- *
- * @param {object} packer
- *
- * @param {object} [options]
- *
- * @param {object} [transport]
- *
- * @param {Function} [onRequest]
- */
-function RpcBuilder(packer, options, transport, onRequest)
-{
- var self = this;
-
- if(!packer)
- throw new SyntaxError('Packer is not defined');
-
- if(!packer.pack || !packer.unpack)
- throw new SyntaxError('Packer is invalid');
-
- var responseMethods = unifyResponseMethods(packer.responseMethods);
-
-
- if(options instanceof Function)
- {
- if(transport != undefined)
- throw new SyntaxError("There can't be parameters after onRequest");
-
- onRequest = options;
- transport = undefined;
- options = undefined;
- };
-
- if(options && options.send instanceof Function)
- {
- if(transport && !(transport instanceof Function))
- throw new SyntaxError("Only a function can be after transport");
-
- onRequest = transport;
- transport = options;
- options = undefined;
- };
-
- if(transport instanceof Function)
- {
- if(onRequest != undefined)
- throw new SyntaxError("There can't be parameters after onRequest");
-
- onRequest = transport;
- transport = undefined;
- };
-
- if(transport && transport.send instanceof Function)
- if(onRequest && !(onRequest instanceof Function))
- throw new SyntaxError("Only a function can be after transport");
-
- options = options || {};
-
-
- EventEmitter.call(this);
-
- if(onRequest)
- this.on('request', onRequest);
-
-
- if(defineProperty_IE8)
- this.peerID = options.peerID
- else
- Object.defineProperty(this, 'peerID', {value: options.peerID});
-
- var max_retries = options.max_retries || 0;
-
-
- function transportMessage(event)
- {
- self.decode(event.data || event);
- };
-
- this.getTransport = function()
- {
- return transport;
- }
- this.setTransport = function(value)
- {
- // Remove listener from old transport
- if(transport)
- {
- // W3C transports
- if(transport.removeEventListener)
- transport.removeEventListener('message', transportMessage);
-
- // Node.js Streams API
- else if(transport.removeListener)
- transport.removeListener('data', transportMessage);
- };
-
- // Set listener on new transport
- if(value)
- {
- // W3C transports
- if(value.addEventListener)
- value.addEventListener('message', transportMessage);
-
- // Node.js Streams API
- else if(value.addListener)
- value.addListener('data', transportMessage);
- };
-
- transport = unifyTransport(value);
- }
-
- if(!defineProperty_IE8)
- Object.defineProperty(this, 'transport',
- {
- get: this.getTransport.bind(this),
- set: this.setTransport.bind(this)
- })
-
- this.setTransport(transport);
-
-
- var request_timeout = options.request_timeout || BASE_TIMEOUT;
- var response_timeout = options.response_timeout || BASE_TIMEOUT;
- var duplicates_timeout = options.duplicates_timeout || BASE_TIMEOUT;
-
-
- var requestID = 0;
-
- var requests = new Mapper();
- var responses = new Mapper();
- var processedResponses = new Mapper();
-
- var message2Key = {};
-
-
- /**
- * Store the response to prevent to process duplicate request later
- */
- function storeResponse(message, id, dest)
- {
- var response =
- {
- message: message,
- /** Timeout to auto-clean old responses */
- timeout: setTimeout(function()
- {
- responses.remove(id, dest);
- },
- response_timeout)
- };
-
- responses.set(response, id, dest);
- };
-
- /**
- * Store the response to ignore duplicated messages later
- */
- function storeProcessedResponse(ack, from)
- {
- var timeout = setTimeout(function()
- {
- processedResponses.remove(ack, from);
- },
- duplicates_timeout);
-
- processedResponses.set(timeout, ack, from);
- };
-
-
- /**
- * Representation of a RPC request
- *
- * @class
- * @extends RpcNotification
- *
- * @constructor
- *
- * @param {String} method -method of the notification
- * @param params - parameters of the notification
- * @param {Integer} id - identifier of the request
- * @param [from] - source of the notification
- */
- function RpcRequest(method, params, id, from, transport)
- {
- RpcNotification.call(this, method, params);
-
- this.getTransport = function()
- {
- return transport;
- }
- this.setTransport = function(value)
- {
- transport = unifyTransport(value);
- }
-
- if(!defineProperty_IE8)
- Object.defineProperty(this, 'transport',
- {
- get: this.getTransport.bind(this),
- set: this.setTransport.bind(this)
- })
-
- var response = responses.get(id, from);
-
- /**
- * @constant {Boolean} duplicated
- */
- if(!(transport || self.getTransport()))
- {
- if(defineProperty_IE8)
- this.duplicated = Boolean(response)
- else
- Object.defineProperty(this, 'duplicated',
- {
- value: Boolean(response)
- });
- }
-
- var responseMethod = responseMethods[method];
-
- this.pack = packer.pack.bind(packer, this, id)
-
- /**
- * Generate a response to this request
- *
- * @param {Error} [error]
- * @param {*} [result]
- *
- * @returns {string}
- */
- this.reply = function(error, result, transport)
- {
- // Fix optional parameters
- if(error instanceof Function || error && error.send instanceof Function)
- {
- if(result != undefined)
- throw new SyntaxError("There can't be parameters after callback");
-
- transport = error;
- result = null;
- error = undefined;
- }
-
- else if(result instanceof Function
- || result && result.send instanceof Function)
- {
- if(transport != undefined)
- throw new SyntaxError("There can't be parameters after callback");
-
- transport = result;
- result = null;
- };
-
- transport = unifyTransport(transport);
-
- // Duplicated request, remove old response timeout
- if(response)
- clearTimeout(response.timeout);
-
- if(from != undefined)
- {
- if(error)
- error.dest = from;
-
- if(result)
- result.dest = from;
- };
-
- var message;
-
- // New request or overriden one, create new response with provided data
- if(error || result != undefined)
- {
- if(self.peerID != undefined)
- {
- if(error)
- error.from = self.peerID;
- else
- result.from = self.peerID;
- }
-
- // Protocol indicates that responses has own request methods
- if(responseMethod)
- {
- if(responseMethod.error == undefined && error)
- message =
- {
- error: error
- };
-
- else
- {
- var method = error
- ? responseMethod.error
- : responseMethod.response;
-
- message =
- {
- method: method,
- params: error || result
- };
- }
- }
- else
- message =
- {
- error: error,
- result: result
- };
-
- message = packer.pack(message, id);
- }
-
- // Duplicate & not-overriden request, re-send old response
- else if(response)
- message = response.message;
-
- // New empty reply, response null value
- else
- message = packer.pack({result: null}, id);
-
- // Store the response to prevent to process a duplicated request later
- storeResponse(message, id, from);
-
- // Return the stored response so it can be directly send back
- transport = transport || this.getTransport() || self.getTransport();
-
- if(transport)
- return transport.send(message);
-
- return message;
- }
- };
- inherits(RpcRequest, RpcNotification);
-
-
- function cancel(message)
- {
- var key = message2Key[message];
- if(!key) return;
-
- delete message2Key[message];
-
- var request = requests.pop(key.id, key.dest);
- if(!request) return;
-
- clearTimeout(request.timeout);
-
- // Start duplicated responses timeout
- storeProcessedResponse(key.id, key.dest);
- };
-
- /**
- * Allow to cancel a request and don't wait for a response
- *
- * If `message` is not given, cancel all the request
- */
- this.cancel = function(message)
- {
- if(message) return cancel(message);
-
- for(var message in message2Key)
- cancel(message);
- };
-
-
- this.close = function()
- {
- // Prevent to receive new messages
- var transport = this.getTransport();
- if(transport && transport.close)
- transport.close();
-
- // Request & processed responses
- this.cancel();
-
- processedResponses.forEach(clearTimeout);
-
- // Responses
- responses.forEach(function(response)
- {
- clearTimeout(response.timeout);
- });
- };
-
-
- /**
- * Generates and encode a JsonRPC 2.0 message
- *
- * @param {String} method -method of the notification
- * @param params - parameters of the notification
- * @param [dest] - destination of the notification
- * @param {object} [transport] - transport where to send the message
- * @param [callback] - function called when a response to this request is
- * received. If not defined, a notification will be send instead
- *
- * @returns {string} A raw JsonRPC 2.0 request or notification string
- */
- this.encode = function(method, params, dest, transport, callback)
- {
- // Fix optional parameters
- if(params instanceof Function)
- {
- if(dest != undefined)
- throw new SyntaxError("There can't be parameters after callback");
-
- callback = params;
- transport = undefined;
- dest = undefined;
- params = undefined;
- }
-
- else if(dest instanceof Function)
- {
- if(transport != undefined)
- throw new SyntaxError("There can't be parameters after callback");
-
- callback = dest;
- transport = undefined;
- dest = undefined;
- }
-
- else if(transport instanceof Function)
- {
- if(callback != undefined)
- throw new SyntaxError("There can't be parameters after callback");
-
- callback = transport;
- transport = undefined;
- };
-
- if(self.peerID != undefined)
- {
- params = params || {};
-
- params.from = self.peerID;
- };
-
- if(dest != undefined)
- {
- params = params || {};
-
- params.dest = dest;
- };
-
- // Encode message
- var message =
- {
- method: method,
- params: params
- };
-
- if(callback)
- {
- var id = requestID++;
- var retried = 0;
-
- message = packer.pack(message, id);
-
- function dispatchCallback(error, result)
- {
- self.cancel(message);
-
- callback(error, result);
- };
-
- var request =
- {
- message: message,
- callback: dispatchCallback,
- responseMethods: responseMethods[method] || {}
- };
-
- var encode_transport = unifyTransport(transport);
-
- function sendRequest(transport)
- {
- request.timeout = setTimeout(timeout,
- request_timeout*Math.pow(2, retried++));
- message2Key[message] = {id: id, dest: dest};
- requests.set(request, id, dest);
-
- transport = transport || encode_transport || self.getTransport();
- if(transport)
- return transport.send(message);
-
- return message;
- };
-
- function retry(transport)
- {
- transport = unifyTransport(transport);
-
- console.warn(retried+' retry for request message:',message);
-
- var timeout = processedResponses.pop(id, dest);
- clearTimeout(timeout);
-
- return sendRequest(transport);
- };
-
- function timeout()
- {
- if(retried < max_retries)
- return retry(transport);
-
- var error = new Error('Request has timed out');
- error.request = message;
-
- error.retry = retry;
-
- dispatchCallback(error)
- };
-
- return sendRequest(transport);
- };
-
- // Return the packed message
- message = packer.pack(message);
-
- transport = transport || this.getTransport();
- if(transport)
- return transport.send(message);
-
- return message;
- };
-
- /**
- * Decode and process a JsonRPC 2.0 message
- *
- * @param {string} message - string with the content of the message
- *
- * @returns {RpcNotification|RpcRequest|undefined} - the representation of the
- * notification or the request. If a response was processed, it will return
- * `undefined` to notify that it was processed
- *
- * @throws {TypeError} - Message is not defined
- */
- this.decode = function(message, transport)
- {
- if(!message)
- throw new TypeError("Message is not defined");
-
- try
- {
- message = packer.unpack(message);
- }
- catch(e)
- {
- // Ignore invalid messages
- return console.log(e, message);
- };
-
- var id = message.id;
- var ack = message.ack;
- var method = message.method;
- var params = message.params || {};
-
- var from = params.from;
- var dest = params.dest;
-
- // Ignore messages send by us
- if(self.peerID != undefined && from == self.peerID) return;
-
- // Notification
- if(id == undefined && ack == undefined)
- {
- var notification = new RpcNotification(method, params);
-
- if(self.emit('request', notification)) return;
- return notification;
- };
-
-
- function processRequest()
- {
- // If we have a transport and it's a duplicated request, reply inmediatly
- transport = unifyTransport(transport) || self.getTransport();
- if(transport)
- {
- var response = responses.get(id, from);
- if(response)
- return transport.send(response.message);
- };
-
- var idAck = (id != undefined) ? id : ack;
- var request = new RpcRequest(method, params, idAck, from, transport);
-
- if(self.emit('request', request)) return;
- return request;
- };
-
- function processResponse(request, error, result)
- {
- request.callback(error, result);
- };
-
- function duplicatedResponse(timeout)
- {
- console.warn("Response already processed", message);
-
- // Update duplicated responses timeout
- clearTimeout(timeout);
- storeProcessedResponse(ack, from);
- };
-
-
- // Request, or response with own method
- if(method)
- {
- // Check if it's a response with own method
- if(dest == undefined || dest == self.peerID)
- {
- var request = requests.get(ack, from);
- if(request)
- {
- var responseMethods = request.responseMethods;
-
- if(method == responseMethods.error)
- return processResponse(request, params);
-
- if(method == responseMethods.response)
- return processResponse(request, null, params);
-
- return processRequest();
- }
-
- var processed = processedResponses.get(ack, from);
- if(processed)
- return duplicatedResponse(processed);
- }
-
- // Request
- return processRequest();
- };
-
- var error = message.error;
- var result = message.result;
-
- // Ignore responses not send to us
- if(error && error.dest && error.dest != self.peerID) return;
- if(result && result.dest && result.dest != self.peerID) return;
-
- // Response
- var request = requests.get(ack, from);
- if(!request)
- {
- var processed = processedResponses.get(ack, from);
- if(processed)
- return duplicatedResponse(processed);
-
- return console.warn("No callback was defined for this message", message);
- };
-
- // Process response
- processResponse(request, error, result);
- };
-};
-inherits(RpcBuilder, EventEmitter);
-
-
-RpcBuilder.RpcNotification = RpcNotification;
-
-
-module.exports = RpcBuilder;
-
-var clients = require('./clients');
-var transports = require('./clients/transports');
-
-RpcBuilder.clients = clients;
-RpcBuilder.clients.transports = transports;
-RpcBuilder.packers = packers;
-
-},{"./Mapper":9,"./clients":10,"./clients/transports":12,"./packers":17,"events":114,"inherits":7}],15:[function(require,module,exports){
-/**
- * JsonRPC 2.0 packer
- */
-
-/**
- * Pack a JsonRPC 2.0 message
- *
- * @param {Object} message - object to be packaged. It requires to have all the
- * fields needed by the JsonRPC 2.0 message that it's going to be generated
- *
- * @return {String} - the stringified JsonRPC 2.0 message
- */
-function pack(message, id)
-{
- var result =
- {
- jsonrpc: "2.0"
- };
-
- // Request
- if(message.method)
- {
- result.method = message.method;
-
- if(message.params)
- result.params = message.params;
-
- // Request is a notification
- if(id != undefined)
- result.id = id;
- }
-
- // Response
- else if(id != undefined)
- {
- if(message.error)
- {
- if(message.result !== undefined)
- throw new TypeError("Both result and error are defined");
-
- result.error = message.error;
- }
- else if(message.result !== undefined)
- result.result = message.result;
- else
- throw new TypeError("No result or error is defined");
-
- result.id = id;
- };
-
- return JSON.stringify(result);
-};
-
-/**
- * Unpack a JsonRPC 2.0 message
- *
- * @param {String} message - string with the content of the JsonRPC 2.0 message
- *
- * @throws {TypeError} - Invalid JsonRPC version
- *
- * @return {Object} - object filled with the JsonRPC 2.0 message content
- */
-function unpack(message)
-{
- var result = message;
-
- if(typeof message === 'string' || message instanceof String)
- result = JSON.parse(message);
-
- // Check if it's a valid message
-
- var version = result.jsonrpc;
- if(version !== '2.0')
- throw new TypeError("Invalid JsonRPC version '" + version + "': " + message);
-
- // Response
- if(result.method == undefined)
- {
- if(result.id == undefined)
- throw new TypeError("Invalid message: "+message);
-
- var result_defined = result.result !== undefined;
- var error_defined = result.error !== undefined;
-
- // Check only result or error is defined, not both or none
- if(result_defined && error_defined)
- throw new TypeError("Both result and error are defined: "+message);
-
- if(!result_defined && !error_defined)
- throw new TypeError("No result or error is defined: "+message);
-
- result.ack = result.id;
- delete result.id;
- }
-
- // Return unpacked message
- return result;
-};
-
-
-exports.pack = pack;
-exports.unpack = unpack;
-
-},{}],16:[function(require,module,exports){
-function pack(message)
-{
- throw new TypeError("Not yet implemented");
-};
-
-function unpack(message)
-{
- throw new TypeError("Not yet implemented");
-};
-
-
-exports.pack = pack;
-exports.unpack = unpack;
-
-},{}],17:[function(require,module,exports){
-var JsonRPC = require('./JsonRPC');
-var XmlRPC = require('./XmlRPC');
-
-
-exports.JsonRPC = JsonRPC;
-exports.XmlRPC = XmlRPC;
-
-},{"./JsonRPC":15,"./XmlRPC":16}],18:[function(require,module,exports){
-/*
- * (C) Copyright 2014-2015 Kurento (http://kurento.org/)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-var freeice = require('freeice')
-var inherits = require('inherits')
-var UAParser = require('ua-parser-js')
-var uuid = require('uuid')
-var hark = require('hark')
-
-var EventEmitter = require('events').EventEmitter
-var recursive = require('merge').recursive.bind(undefined, true)
-var sdpTranslator = require('sdp-translator')
-var logger = window.Logger || console
-
-// var gUM = navigator.mediaDevices.getUserMedia || function (constraints) {
-// return new Promise(navigator.getUserMedia(constraints, function (stream) {
-// videoStream = stream
-// start()
-// }).eror(callback));
-// }
-
-try {
- require('kurento-browser-extensions')
-} catch (error) {
- if (typeof getScreenConstraints === 'undefined') {
- logger.warn('screen sharing is not available')
-
- getScreenConstraints = function getScreenConstraints(sendSource, callback) {
- callback(new Error("This library is not enabled for screen sharing"))
- }
- }
-}
-
-var MEDIA_CONSTRAINTS = {
- audio: true,
- video: {
- width: 640,
- framerate: 15
- }
-}
-
-// Somehow, the UAParser constructor gets an empty window object.
-// We need to pass the user agent string in order to get information
-var ua = (window && window.navigator) ? window.navigator.userAgent : ''
-var parser = new UAParser(ua)
-var browser = parser.getBrowser()
-
-var usePlanB = false
-if (browser.name === 'Chrome' || browser.name === 'Chromium') {
- logger.info(browser.name + ": using SDP PlanB")
- usePlanB = true
-}
-
-function noop(error) {
- if (error) logger.error(error)
-}
-
-function trackStop(track) {
- track.stop && track.stop()
-}
-
-function streamStop(stream) {
- stream.getTracks().forEach(trackStop)
-}
-
-/**
- * Returns a string representation of a SessionDescription object.
- */
-var dumpSDP = function (description) {
- if (typeof description === 'undefined' || description === null) {
- return ''
- }
-
- return 'type: ' + description.type + '\r\n' + description.sdp
-}
-
-function bufferizeCandidates(pc, onerror) {
- var candidatesQueue = []
-
- pc.addEventListener('signalingstatechange', function () {
- if (this.signalingState === 'stable') {
- while (candidatesQueue.length) {
- var entry = candidatesQueue.shift()
-
- this.addIceCandidate(entry.candidate, entry.callback, entry.callback)
- }
- }
- })
-
- return function (candidate, callback) {
- callback = callback || onerror
-
- switch (pc.signalingState) {
- case 'closed':
- callback(new Error('PeerConnection object is closed'))
- break
- case 'stable':
- if (pc.remoteDescription) {
- pc.addIceCandidate(candidate, callback, callback)
- break
- }
- default:
- candidatesQueue.push({
- candidate: candidate,
- callback: callback
- })
- }
- }
-}
-
-/* Simulcast utilities */
-
-function removeFIDFromOffer(sdp) {
- var n = sdp.indexOf("a=ssrc-group:FID");
-
- if (n > 0) {
- return sdp.slice(0, n);
- } else {
- return sdp;
- }
-}
-
-function getSimulcastInfo(videoStream) {
- var videoTracks = videoStream.getVideoTracks();
- if (!videoTracks.length) {
- logger.warn('No video tracks available in the video stream')
- return ''
- }
- var lines = [
- 'a=x-google-flag:conference',
- 'a=ssrc-group:SIM 1 2 3',
- 'a=ssrc:1 cname:localVideo',
- 'a=ssrc:1 msid:' + videoStream.id + ' ' + videoTracks[0].id,
- 'a=ssrc:1 mslabel:' + videoStream.id,
- 'a=ssrc:1 label:' + videoTracks[0].id,
- 'a=ssrc:2 cname:localVideo',
- 'a=ssrc:2 msid:' + videoStream.id + ' ' + videoTracks[0].id,
- 'a=ssrc:2 mslabel:' + videoStream.id,
- 'a=ssrc:2 label:' + videoTracks[0].id,
- 'a=ssrc:3 cname:localVideo',
- 'a=ssrc:3 msid:' + videoStream.id + ' ' + videoTracks[0].id,
- 'a=ssrc:3 mslabel:' + videoStream.id,
- 'a=ssrc:3 label:' + videoTracks[0].id
- ];
-
- lines.push('');
-
- return lines.join('\n');
-}
-
-/**
- * Wrapper object of an RTCPeerConnection. This object is aimed to simplify the
- * development of WebRTC-based applications.
- *
- * @constructor module:kurentoUtils.WebRtcPeer
- *
- * @param {String} mode Mode in which the PeerConnection will be configured.
- * Valid values are: 'recv', 'send', and 'sendRecv'
- * @param localVideo Video tag for the local stream
- * @param remoteVideo Video tag for the remote stream
- * @param {MediaStream} videoStream Stream to be used as primary source
- * (typically video and audio, or only video if combined with audioStream) for
- * localVideo and to be added as stream to the RTCPeerConnection
- * @param {MediaStream} audioStream Stream to be used as second source
- * (typically for audio) for localVideo and to be added as stream to the
- * RTCPeerConnection
- */
-function WebRtcPeer(mode, options, callback) {
- if (!(this instanceof WebRtcPeer)) {
- return new WebRtcPeer(mode, options, callback)
- }
-
- WebRtcPeer.super_.call(this)
-
- if (options instanceof Function) {
- callback = options
- options = undefined
- }
-
- options = options || {}
- callback = (callback || noop).bind(this)
-
- var self = this
- var localVideo = options.localVideo
- var remoteVideo = options.remoteVideo
- var videoStream = options.videoStream
- var audioStream = options.audioStream
- var mediaConstraints = options.mediaConstraints
-
- var connectionConstraints = options.connectionConstraints
- var pc = options.peerConnection
- var sendSource = options.sendSource || 'webcam'
-
- var dataChannelConfig = options.dataChannelConfig
- var useDataChannels = options.dataChannels || false
- var dataChannel
-
- var guid = uuid.v4()
- var configuration = recursive({
- iceServers: freeice()
- },
- options.configuration)
-
- var onicecandidate = options.onicecandidate
- if (onicecandidate) this.on('icecandidate', onicecandidate)
-
- var oncandidategatheringdone = options.oncandidategatheringdone
- if (oncandidategatheringdone) {
- this.on('candidategatheringdone', oncandidategatheringdone)
- }
-
- var simulcast = options.simulcast
- var multistream = options.multistream
- var interop = new sdpTranslator.Interop()
- var candidatesQueueOut = []
- var candidategatheringdone = false
-
- Object.defineProperties(this, {
- 'peerConnection': {
- get: function () {
- return pc
- }
- },
-
- 'id': {
- value: options.id || guid,
- writable: false
- },
-
- 'remoteVideo': {
- get: function () {
- return remoteVideo
- }
- },
-
- 'localVideo': {
- get: function () {
- return localVideo
- }
- },
-
- 'dataChannel': {
- get: function () {
- return dataChannel
- }
- },
-
- /**
- * @member {(external:ImageData|undefined)} currentFrame
- */
- 'currentFrame': {
- get: function () {
- // [ToDo] Find solution when we have a remote stream but we didn't set
- // a remoteVideo tag
- if (!remoteVideo) return;
-
- if (remoteVideo.readyState < remoteVideo.HAVE_CURRENT_DATA)
- throw new Error('No video stream data available')
-
- var canvas = document.createElement('canvas')
- canvas.width = remoteVideo.videoWidth
- canvas.height = remoteVideo.videoHeight
-
- canvas.getContext('2d').drawImage(remoteVideo, 0, 0)
-
- return canvas
- }
- }
- })
-
- // Init PeerConnection
- if (!pc) {
- pc = new RTCPeerConnection(configuration);
- if (useDataChannels && !dataChannel) {
- var dcId = 'WebRtcPeer-' + self.id
- var dcOptions = undefined
- if (dataChannelConfig) {
- dcId = dataChannelConfig.id || dcId
- dcOptions = dataChannelConfig.options
- }
- dataChannel = pc.createDataChannel(dcId, dcOptions);
- if (dataChannelConfig) {
- dataChannel.onopen = dataChannelConfig.onopen;
- dataChannel.onclose = dataChannelConfig.onclose;
- dataChannel.onmessage = dataChannelConfig.onmessage;
- dataChannel.onbufferedamountlow = dataChannelConfig.onbufferedamountlow;
- dataChannel.onerror = dataChannelConfig.onerror || noop;
- }
- }
- }
-
- pc.addEventListener('icecandidate', function (event) {
- var candidate = event.candidate
-
- if (EventEmitter.listenerCount(self, 'icecandidate') ||
- EventEmitter.listenerCount(
- self, 'candidategatheringdone')) {
- if (candidate) {
- var cand
-
- if (multistream && usePlanB) {
- cand = interop.candidateToUnifiedPlan(candidate)
- } else {
- cand = candidate
- }
-
- self.emit('icecandidate', cand)
- candidategatheringdone = false
- } else if (!candidategatheringdone) {
- self.emit('candidategatheringdone')
- candidategatheringdone = true
- }
- } else if (!candidategatheringdone) {
- // Not listening to 'icecandidate' or 'candidategatheringdone' events, queue
- // the candidate until one of them is listened
- candidatesQueueOut.push(candidate)
-
- if (!candidate) candidategatheringdone = true
- }
- })
-
- pc.onaddstream = options.onaddstream
- pc.onnegotiationneeded = options.onnegotiationneeded
- this.on('newListener', function (event, listener) {
- if (event === 'icecandidate' || event === 'candidategatheringdone') {
- while (candidatesQueueOut.length) {
- var candidate = candidatesQueueOut.shift()
-
- if (!candidate === (event === 'candidategatheringdone')) {
- listener(candidate)
- }
- }
- }
- })
-
- var addIceCandidate = bufferizeCandidates(pc)
-
- /**
- * Callback function invoked when an ICE candidate is received. Developers are
- * expected to invoke this function in order to complete the SDP negotiation.
- *
- * @function module:kurentoUtils.WebRtcPeer.prototype.addIceCandidate
- *
- * @param iceCandidate - Literal object with the ICE candidate description
- * @param callback - Called when the ICE candidate has been added.
- */
- this.addIceCandidate = function (iceCandidate, callback) {
- var candidate
-
- if (multistream && usePlanB) {
- candidate = interop.candidateToPlanB(iceCandidate)
- } else {
- candidate = new RTCIceCandidate(iceCandidate)
- }
-
- logger.debug('Remote ICE candidate received', iceCandidate)
- callback = (callback || noop).bind(this)
- addIceCandidate(candidate, callback)
- }
-
- this.generateOffer = function (callback) {
- callback = callback.bind(this)
-
- var offerAudio = true
- var offerVideo = true
- // Constraints must have both blocks
- if (mediaConstraints) {
- offerAudio = (typeof mediaConstraints.audio === 'boolean') ?
- mediaConstraints.audio : true
- offerVideo = (typeof mediaConstraints.video === 'boolean') ?
- mediaConstraints.video : true
- }
-
- var browserDependantConstraints = (browser.name === 'Firefox' &&
- browser.version > 34) ? {
- offerToReceiveAudio: (mode !== 'sendonly' && offerAudio),
- offerToReceiveVideo: (mode !== 'sendonly' && offerVideo)
- } : {
- mandatory: {
- OfferToReceiveAudio: (mode !== 'sendonly' && offerAudio),
- OfferToReceiveVideo: (mode !== 'sendonly' && offerVideo)
- },
- optional: [{
- DtlsSrtpKeyAgreement: true
- }]
- }
- var constraints = recursive(browserDependantConstraints,
- connectionConstraints)
-
- logger.info('constraints: ' + JSON.stringify(constraints))
-
- pc.createOffer(constraints).then(function (offer) {
- logger.info('Created SDP offer')
- offer = mangleSdpToAddSimulcast(offer)
- return pc.setLocalDescription(offer)
- }).then(function () {
- var localDescription = pc.localDescription
- logger.info('Local description set', localDescription.sdp)
- if (multistream && usePlanB) {
- localDescription = interop.toUnifiedPlan(localDescription)
- logger.info('offer::origPlanB->UnifiedPlan', dumpSDP(
- localDescription))
- }
- callback(null, localDescription.sdp, self.processAnswer.bind(
- self))
- }).catch(callback)
- }
-
- this.getLocalSessionDescriptor = function () {
- return pc.localDescription
- }
-
- this.getRemoteSessionDescriptor = function () {
- return pc.remoteDescription
- }
-
- function setRemoteVideo() {
- if (remoteVideo) {
- var stream = pc.getRemoteStreams()[0]
- var url = stream ? URL.createObjectURL(stream) : ''
-
- remoteVideo.pause()
- remoteVideo.src = url
- remoteVideo.load()
-
- logger.info('Remote URL:', url)
- }
- }
-
- this.showLocalVideo = function () {
- localVideo.src = URL.createObjectURL(videoStream)
- localVideo.muted = true
- }
-
- this.send = function (data) {
- if (dataChannel && dataChannel.readyState === 'open') {
- dataChannel.send(data)
- } else {
- logger.warn(
- 'Trying to send data over a non-existing or closed data channel')
- }
- }
-
- /**
- * Callback function invoked when a SDP answer is received. Developers are
- * expected to invoke this function in order to complete the SDP negotiation.
- *
- * @function module:kurentoUtils.WebRtcPeer.prototype.processAnswer
- *
- * @param sdpAnswer - Description of sdpAnswer
- * @param callback -
- * Invoked after the SDP answer is processed, or there is an error.
- */
- this.processAnswer = function (sdpAnswer, callback) {
- callback = (callback || noop).bind(this)
-
- var answer = new RTCSessionDescription({
- type: 'answer',
- sdp: sdpAnswer
- })
-
- if (multistream && usePlanB) {
- var planBAnswer = interop.toPlanB(answer)
- logger.info('asnwer::planB', dumpSDP(planBAnswer))
- answer = planBAnswer
- }
-
- logger.info('SDP answer received, setting remote description')
-
- if (pc.signalingState === 'closed') {
- return callback('PeerConnection is closed')
- }
-
- pc.setRemoteDescription(answer, function () {
- setRemoteVideo()
-
- callback()
- },
- callback)
- }
-
- /**
- * Callback function invoked when a SDP offer is received. Developers are
- * expected to invoke this function in order to complete the SDP negotiation.
- *
- * @function module:kurentoUtils.WebRtcPeer.prototype.processOffer
- *
- * @param sdpOffer - Description of sdpOffer
- * @param callback - Called when the remote description has been set
- * successfully.
- */
- this.processOffer = function (sdpOffer, callback) {
- callback = callback.bind(this)
-
- var offer = new RTCSessionDescription({
- type: 'offer',
- sdp: sdpOffer
- })
-
- if (multistream && usePlanB) {
- var planBOffer = interop.toPlanB(offer)
- logger.info('offer::planB', dumpSDP(planBOffer))
- offer = planBOffer
- }
-
- logger.info('SDP offer received, setting remote description')
-
- if (pc.signalingState === 'closed') {
- return callback('PeerConnection is closed')
- }
-
- pc.setRemoteDescription(offer).then(function () {
- return setRemoteVideo()
- }).then(function () {
- return pc.createAnswer()
- }).then(function (answer) {
- answer = mangleSdpToAddSimulcast(answer)
- logger.info('Created SDP answer')
- return pc.setLocalDescription(answer)
- }).then(function () {
- var localDescription = pc.localDescription
- if (multistream && usePlanB) {
- localDescription = interop.toUnifiedPlan(localDescription)
- logger.info('answer::origPlanB->UnifiedPlan', dumpSDP(
- localDescription))
- }
- logger.info('Local description set', localDescription.sdp)
- callback(null, localDescription.sdp)
- }).catch(callback)
- }
-
- function mangleSdpToAddSimulcast(answer) {
- if (simulcast) {
- if (browser.name === 'Chrome' || browser.name === 'Chromium') {
- logger.info('Adding multicast info')
- answer = new RTCSessionDescription({
- 'type': answer.type,
- 'sdp': removeFIDFromOffer(answer.sdp) + getSimulcastInfo(
- videoStream)
- })
- } else {
- logger.warn('Simulcast is only available in Chrome browser.')
- }
- }
-
- return answer
- }
-
- /**
- * This function creates the RTCPeerConnection object taking into account the
- * properties received in the constructor. It starts the SDP negotiation
- * process: generates the SDP offer and invokes the onsdpoffer callback. This
- * callback is expected to send the SDP offer, in order to obtain an SDP
- * answer from another peer.
- */
- function start() {
- if (pc.signalingState === 'closed') {
- callback(
- 'The peer connection object is in "closed" state. This is most likely due to an invocation of the dispose method before accepting in the dialogue'
- )
- }
-
- if (videoStream && localVideo) {
- self.showLocalVideo()
- }
-
- if (videoStream) {
- pc.addStream(videoStream)
- }
-
- if (audioStream) {
- pc.addStream(audioStream)
- }
-
- // [Hack] https://code.google.com/p/chromium/issues/detail?id=443558
- var browser = parser.getBrowser()
- if (mode === 'sendonly' &&
- (browser.name === 'Chrome' || browser.name === 'Chromium') &&
- browser.major === 39) {
- mode = 'sendrecv'
- }
-
- callback()
- }
-
- if (mode !== 'recvonly' && !videoStream && !audioStream) {
- function getMedia(constraints) {
- if (constraints === undefined) {
- constraints = MEDIA_CONSTRAINTS
- }
-
- navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
- videoStream = stream
- start()
- }).catch(callback);
- }
- if (sendSource === 'webcam') {
- getMedia(mediaConstraints)
- } else {
- getScreenConstraints(sendSource, function (error, constraints_) {
- if (error)
- return callback(error)
-
- constraints = [mediaConstraints]
- constraints.unshift(constraints_)
- getMedia(recursive.apply(undefined, constraints))
- }, guid)
- }
- } else {
- setTimeout(start, 0)
- }
-
- this.on('_dispose', function () {
- if (localVideo) {
- localVideo.pause()
- localVideo.src = ''
- localVideo.load()
- //Unmute local video in case the video tag is later used for remote video
- localVideo.muted = false
- }
- if (remoteVideo) {
- remoteVideo.pause()
- remoteVideo.src = ''
- remoteVideo.load()
- }
- self.removeAllListeners()
-
- if (window.cancelChooseDesktopMedia !== undefined) {
- window.cancelChooseDesktopMedia(guid)
- }
- })
-}
-inherits(WebRtcPeer, EventEmitter)
-
-function createEnableDescriptor(type) {
- var method = 'get' + type + 'Tracks'
-
- return {
- enumerable: true,
- get: function () {
- // [ToDo] Should return undefined if not all tracks have the same value?
-
- if (!this.peerConnection) return
-
- var streams = this.peerConnection.getLocalStreams()
- if (!streams.length) return
-
- for (var i = 0, stream; stream = streams[i]; i++) {
- var tracks = stream[method]()
- for (var j = 0, track; track = tracks[j]; j++)
- if (!track.enabled) return false
- }
-
- return true
- },
- set: function (value) {
- function trackSetEnable(track) {
- track.enabled = value
- }
-
- this.peerConnection.getLocalStreams().forEach(function (stream) {
- stream[method]().forEach(trackSetEnable)
- })
- }
- }
-}
-
-Object.defineProperties(WebRtcPeer.prototype, {
- 'enabled': {
- enumerable: true,
- get: function () {
- return this.audioEnabled && this.videoEnabled
- },
- set: function (value) {
- this.audioEnabled = this.videoEnabled = value
- }
- },
- 'audioEnabled': createEnableDescriptor('Audio'),
- 'videoEnabled': createEnableDescriptor('Video')
-})
-
-WebRtcPeer.prototype.getLocalStream = function (index) {
- if (this.peerConnection) {
- return this.peerConnection.getLocalStreams()[index || 0]
- }
-}
-
-WebRtcPeer.prototype.getRemoteStream = function (index) {
- if (this.peerConnection) {
- return this.peerConnection.getRemoteStreams()[index || 0]
- }
-}
-
-/**
- * @description This method frees the resources used by WebRtcPeer.
- *
- * @function module:kurentoUtils.WebRtcPeer.prototype.dispose
- */
-WebRtcPeer.prototype.dispose = function () {
- logger.info('Disposing WebRtcPeer')
-
- var pc = this.peerConnection
- var dc = this.dataChannel
- try {
- if (dc) {
- if (dc.signalingState === 'closed') return
-
- dc.close()
- }
-
- if (pc) {
- if (pc.signalingState === 'closed') return
-
- pc.getLocalStreams().forEach(streamStop)
-
- // FIXME This is not yet implemented in firefox
- // if(videoStream) pc.removeStream(videoStream);
- // if(audioStream) pc.removeStream(audioStream);
-
- pc.close()
- }
- } catch (err) {
- logger.warn('Exception disposing webrtc peer ' + err)
- }
-
- this.emit('_dispose')
-}
-
-//
-// Specialized child classes
-//
-
-function WebRtcPeerRecvonly(options, callback) {
- if (!(this instanceof WebRtcPeerRecvonly)) {
- return new WebRtcPeerRecvonly(options, callback)
- }
-
- WebRtcPeerRecvonly.super_.call(this, 'recvonly', options, callback)
-}
-inherits(WebRtcPeerRecvonly, WebRtcPeer)
-
-function WebRtcPeerSendonly(options, callback) {
- if (!(this instanceof WebRtcPeerSendonly)) {
- return new WebRtcPeerSendonly(options, callback)
- }
-
- WebRtcPeerSendonly.super_.call(this, 'sendonly', options, callback)
-}
-inherits(WebRtcPeerSendonly, WebRtcPeer)
-
-function WebRtcPeerSendrecv(options, callback) {
- if (!(this instanceof WebRtcPeerSendrecv)) {
- return new WebRtcPeerSendrecv(options, callback)
- }
-
- WebRtcPeerSendrecv.super_.call(this, 'sendrecv', options, callback)
-}
-inherits(WebRtcPeerSendrecv, WebRtcPeer)
-
-function harkUtils(stream, options) {
- return hark(stream, options);
-}
-
-exports.bufferizeCandidates = bufferizeCandidates
-
-exports.WebRtcPeerRecvonly = WebRtcPeerRecvonly
-exports.WebRtcPeerSendonly = WebRtcPeerSendonly
-exports.WebRtcPeerSendrecv = WebRtcPeerSendrecv
-exports.hark = harkUtils
-
-},{"events":114,"freeice":3,"hark":6,"inherits":7,"kurento-browser-extensions":undefined,"merge":20,"sdp-translator":30,"ua-parser-js":87,"uuid":91}],19:[function(require,module,exports){
-/*
- * (C) Copyright 2014 Kurento (http://kurento.org/)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-/**
- * This module contains a set of reusable components that have been found useful
- * during the development of the WebRTC applications with Kurento.
- *
- * @module kurentoUtils
- *
- * @copyright 2014 Kurento (http://kurento.org/)
- * @license ALv2
- */
-
-var WebRtcPeer = require('./WebRtcPeer');
-
-exports.WebRtcPeer = WebRtcPeer;
-
-},{"./WebRtcPeer":18}],20:[function(require,module,exports){
-/*!
- * @name JavaScript/NodeJS Merge v1.2.0
- * @author yeikos
- * @repository https://github.com/yeikos/js.merge
-
- * Copyright 2014 yeikos - MIT license
- * https://raw.github.com/yeikos/js.merge/master/LICENSE
- */
-
-;(function(isNode) {
-
- /**
- * Merge one or more objects
- * @param bool? clone
- * @param mixed,... arguments
- * @return object
- */
-
- var Public = function(clone) {
-
- return merge(clone === true, false, arguments);
-
- }, publicName = 'merge';
-
- /**
- * Merge two or more objects recursively
- * @param bool? clone
- * @param mixed,... arguments
- * @return object
- */
-
- Public.recursive = function(clone) {
-
- return merge(clone === true, true, arguments);
-
- };
-
- /**
- * Clone the input removing any reference
- * @param mixed input
- * @return mixed
- */
-
- Public.clone = function(input) {
-
- var output = input,
- type = typeOf(input),
- index, size;
-
- if (type === 'array') {
-
- output = [];
- size = input.length;
-
- for (index=0;index