- * 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