pax_global_header00006660000000000000000000000064125442644430014522gustar00rootroot0000000000000052 comment=9ddf6664289d9ab9da786edcd2f8b61b0633f013 rtcninja.js-0.6.2/000077500000000000000000000000001254426444300137525ustar00rootroot00000000000000rtcninja.js-0.6.2/.gitignore000066400000000000000000000003031254426444300157360ustar00rootroot00000000000000# Node and bower deps node_modules bower_components # Logs and databases *.log dump.rdb output # OS .DS_Store .DS_Store? ._* .Spotlight-V100 .Trashes Icon? ehthumbs.db Thumbs.db # Others html rtcninja.js-0.6.2/.jscsrc000066400000000000000000000000771254426444300152460ustar00rootroot00000000000000{ "preset": "crockford", "validateIndentation": "\t" } rtcninja.js-0.6.2/.jshintrc000066400000000000000000000010101254426444300155670ustar00rootroot00000000000000{ "bitwise": true, "curly": true, "eqeqeq": true, "forin": true, "freeze": true, "latedef": "function", "noarg": true, "nonbsp": true, "nonew": true, "plusplus": true, "undef": true, "unused": true, "strict": true, "maxparams": 3, "maxdepth": 4, "maxstatements": 70, "maxlen": 140, "validthis": true, "browser": true, "browserify": true, "devel": false, "jquery": false, "mocha": false, "node": true, "worker": false } rtcninja.js-0.6.2/.npmignore000066400000000000000000000000521254426444300157460ustar00rootroot00000000000000.gitignore /node_modules/ /NO_GIT/ /html/ rtcninja.js-0.6.2/LICENSE000066400000000000000000000020731254426444300147610ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015, eFace2Face Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rtcninja.js-0.6.2/README.md000066400000000000000000000062701254426444300152360ustar00rootroot00000000000000# rtcninja.js WebRTC API wrapper to deal with different browsers transparently, [eventually](http://iswebrtcreadyyet.com/) this library shouldn't be needed. We only have to wait until W3C group in charge [finishes the specification](https://tools.ietf.org/wg/rtcweb/) and the different browsers implement it correctly :sweat_smile:. Supported environments: * [Google Chrome](https://www.google.com/chrome/browser/desktop/index.html) (desktop & mobile) * [Google Canary](https://www.google.com/chrome/browser/canary.html) (desktop & mobile) * [Mozilla Firefox](https://www.mozilla.org/en-GB/firefox/new) (desktop & mobile) * [Firefox Nigthly](https://nightly.mozilla.org/) (desktop & mobile) * [Opera](http://www.opera.com/) * [Vivaldi](https://vivaldi.com/) * [CrossWalk](https://crosswalk-project.org/) * [Cordova](cordova.apache.org): iOS support, you only have to use our plugin [following these steps](https://github.com/eface2face/cordova-plugin-iosrtc#usage). * [Node-webkit](https://github.com/nwjs/nw.js/) ## Installation ### **npm**: ```bash $ npm install rtcninja ``` and then: ```javascript var rtcninja = require('rtcninja'); ``` ### **bower**: ```bash $ bower install rtcninja ``` ## Browserified library Take a browserified version of the library from the `dist/` folder: * `dist/rtcninja-X.Y.Z.js`: The uncompressed version. * `dist/rtcninja-X.Y.Z.min.js`: The compressed production-ready version. * `dist/rtcninja.js`: A copy of the uncompressed version. * `dist/rtcninja.min.js`: A copy of the compressed version. They expose the global `window.rtcninja` module. ## Usage In the [examples](./examples/) folder we provide a complete one. ```javascript // Must first call it. rtcninja(); // Then check. if (rtcninja.hasWebRTC()) { // Do something. } else { // Do something. } ``` ## Documentation You can read the full [API documentation](docs/index.md) in the docs folder. ## Issues https://github.com/eface2face/rtcninja.js/issues ## Developer guide * Create a branch with a name including your user and a meaningful word about the fix/feature you're going to implement, ie: "jesusprubio/fixstuff" * Use [GitHub pull requests](https://help.github.com/articles/using-pull-requests). * Conventions: * We use [JSHint](http://jshint.com/) and [Crockford's Styleguide](http://javascript.crockford.com/code.html). * Please run `grunt lint` to be sure your code fits with them. ### Debugging The library includes the Node [debug](https://github.com/visionmedia/debug) module. In order to enable debugging: In Node set the `DEBUG=rtcninja*` environment variable before running the application, or set it at the top of the script: ```javascript process.env.DEBUG = 'rtcninja*'; ``` In the browser run `rtcninja.debug.enable('rtcninja*');` and reload the page. Note that the debugging settings are stored into the browser LocalStorage. To disable it run `rtcninja.debug.disable('rtcninja*');`. ## Copyright & License * eFace2Face Inc. * [MIT](./LICENSE) rtcninja.js-0.6.2/banner.txt000066400000000000000000000002341254426444300157570ustar00rootroot00000000000000/* * <%= pkg.name %>.js v<%= pkg.version %> * <%= pkg.description %> * Copyright <%= currentYear %> <%= pkg.author %> * License <%= pkg.license %> */ rtcninja.js-0.6.2/bower.json000066400000000000000000000010001254426444300157520ustar00rootroot00000000000000{ "name": "rtcninja", "version": "0.6.2", "description": "WebRTC API wrapper to deal with different browsers", "author": "Iñaki Baz Castillo (http://eface2face.com)", "license": "MIT", "main": "dist/rtcninja.js", "homepage": "https://github.com/eface2face/rtcninja.js", "repository": { "type": "git", "url": "https://github.com/eface2face/rtcninja.js.git" }, "keywords": [ "webrtc" ], "ignore": [ "node_modules", "NO_GIT", "test" ] } rtcninja.js-0.6.2/dist/000077500000000000000000000000001254426444300147155ustar00rootroot00000000000000rtcninja.js-0.6.2/dist/rtcninja-0.6.2.js000066400000000000000000001476501254426444300175410ustar00rootroot00000000000000/* * rtcninja.js v0.6.2 * WebRTC API wrapper to deal with different browsers * Copyright 2015 Iñaki Baz Castillo (http://eface2face.com) * License MIT */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.rtcninja = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 32) || (browser.android && browser.chrome && browserVersion >= 39) || (isDesktop && browser.opera && browserVersion >= 27) || (browser.android && browser.opera && browserVersion >= 24) || (browser.android && browser.webkit && !browser.chrome && browserVersion >= 37) || (virtNavigator.webkitGetUserMedia && virtGlobal.webkitRTCPeerConnection) ) { hasWebRTC = true; getUserMedia = virtNavigator.webkitGetUserMedia.bind(virtNavigator); RTCPeerConnection = virtGlobal.webkitRTCPeerConnection; RTCSessionDescription = virtGlobal.RTCSessionDescription; RTCIceCandidate = virtGlobal.RTCIceCandidate; MediaStreamTrack = virtGlobal.MediaStreamTrack; if (MediaStreamTrack && MediaStreamTrack.getSources) { getMediaDevices = MediaStreamTrack.getSources.bind(MediaStreamTrack); } else if (virtNavigator.getMediaDevices) { getMediaDevices = virtNavigator.getMediaDevices.bind(virtNavigator); } attachMediaStream = function (element, stream) { element.src = URL.createObjectURL(stream); return element; }; canRenegotiate = true; oldSpecRTCOfferOptions = false; // Firefox desktop, Firefox Android. } else if ( (isDesktop && browser.firefox && browserVersion >= 22) || (browser.android && browser.firefox && browserVersion >= 33) || (virtNavigator.mozGetUserMedia && virtGlobal.mozRTCPeerConnection) ) { hasWebRTC = true; getUserMedia = virtNavigator.mozGetUserMedia.bind(virtNavigator); RTCPeerConnection = virtGlobal.mozRTCPeerConnection; RTCSessionDescription = virtGlobal.mozRTCSessionDescription; RTCIceCandidate = virtGlobal.mozRTCIceCandidate; MediaStreamTrack = virtGlobal.MediaStreamTrack; attachMediaStream = function (element, stream) { element.src = URL.createObjectURL(stream); return element; }; canRenegotiate = false; oldSpecRTCOfferOptions = false; // WebRTC plugin required. For example IE or Safari with the Temasys plugin. } else if ( options.plugin && typeof options.plugin.isRequired === 'function' && options.plugin.isRequired() && typeof options.plugin.isInstalled === 'function' && options.plugin.isInstalled() ) { var pluginiface = options.plugin.interface; hasWebRTC = true; getUserMedia = pluginiface.getUserMedia; RTCPeerConnection = pluginiface.RTCPeerConnection; RTCSessionDescription = pluginiface.RTCSessionDescription; RTCIceCandidate = pluginiface.RTCIceCandidate; MediaStreamTrack = pluginiface.MediaStreamTrack; if (MediaStreamTrack && MediaStreamTrack.getSources) { getMediaDevices = MediaStreamTrack.getSources.bind(MediaStreamTrack); } else if (virtNavigator.getMediaDevices) { getMediaDevices = virtNavigator.getMediaDevices.bind(virtNavigator); } attachMediaStream = pluginiface.attachMediaStream; canRenegotiate = pluginiface.canRenegotiate; oldSpecRTCOfferOptions = true; // TODO: Update when fixed in the plugin. // Best effort (may be adater.js is loaded). } else if (virtNavigator.getUserMedia && virtGlobal.RTCPeerConnection) { hasWebRTC = true; getUserMedia = virtNavigator.getUserMedia.bind(virtNavigator); RTCPeerConnection = virtGlobal.RTCPeerConnection; RTCSessionDescription = virtGlobal.RTCSessionDescription; RTCIceCandidate = virtGlobal.RTCIceCandidate; MediaStreamTrack = virtGlobal.MediaStreamTrack; if (MediaStreamTrack && MediaStreamTrack.getSources) { getMediaDevices = MediaStreamTrack.getSources.bind(MediaStreamTrack); } else if (virtNavigator.getMediaDevices) { getMediaDevices = virtNavigator.getMediaDevices.bind(virtNavigator); } attachMediaStream = virtGlobal.attachMediaStream || function (element, stream) { element.src = URL.createObjectURL(stream); return element; }; canRenegotiate = false; oldSpecRTCOfferOptions = false; } function throwNonSupported(item) { return function () { throw new Error('rtcninja: WebRTC not supported, missing ' + item + ' [browser: ' + browser.name + ' ' + browser.version + ']'); }; } // Public API. // Expose a WebRTC checker. Adapter.hasWebRTC = function () { return hasWebRTC; }; // Expose getUserMedia. if (getUserMedia) { Adapter.getUserMedia = function (constraints, successCallback, errorCallback) { debug('getUserMedia() | constraints: %o', constraints); try { getUserMedia(constraints, function (stream) { debug('getUserMedia() | success'); if (successCallback) { successCallback(stream); } }, function (error) { debug('getUserMedia() | error:', error); if (errorCallback) { errorCallback(error); } } ); } catch (error) { debugerror('getUserMedia() | error:', error); if (errorCallback) { errorCallback(error); } } }; } else { Adapter.getUserMedia = function (constraints, successCallback, errorCallback) { debugerror('getUserMedia() | WebRTC not supported'); if (errorCallback) { errorCallback(new Error('rtcninja: WebRTC not supported, missing ' + 'getUserMedia [browser: ' + browser.name + ' ' + browser.version + ']')); } else { throwNonSupported('getUserMedia'); } }; } // Expose RTCPeerConnection. Adapter.RTCPeerConnection = RTCPeerConnection || throwNonSupported('RTCPeerConnection'); // Expose RTCSessionDescription. Adapter.RTCSessionDescription = RTCSessionDescription || throwNonSupported('RTCSessionDescription'); // Expose RTCIceCandidate. Adapter.RTCIceCandidate = RTCIceCandidate || throwNonSupported('RTCIceCandidate'); // Expose MediaStreamTrack. Adapter.MediaStreamTrack = MediaStreamTrack || throwNonSupported('MediaStreamTrack'); // Expose getMediaDevices. Adapter.getMediaDevices = getMediaDevices; // Expose MediaStreamTrack. Adapter.attachMediaStream = attachMediaStream || throwNonSupported('attachMediaStream'); // Expose canRenegotiate attribute. Adapter.canRenegotiate = canRenegotiate; // Expose closeMediaStream. Adapter.closeMediaStream = function (stream) { if (!stream) { return; } // Latest spec states that MediaStream has no stop() method and instead must // call stop() on every MediaStreamTrack. if (MediaStreamTrack && MediaStreamTrack.prototype && MediaStreamTrack.prototype.stop) { debug('closeMediaStream() | calling stop() on all the MediaStreamTrack'); var tracks, i, len; if (stream.getTracks) { tracks = stream.getTracks(); for (i = 0, len = tracks.length; i < len; i += 1) { tracks[i].stop(); } } else { tracks = stream.getAudioTracks(); for (i = 0, len = tracks.length; i < len; i += 1) { tracks[i].stop(); } tracks = stream.getVideoTracks(); for (i = 0, len = tracks.length; i < len; i += 1) { tracks[i].stop(); } } // Deprecated by the spec, but still in use. } else if (typeof stream.stop === 'function') { debug('closeMediaStream() | calling stop() on the MediaStream'); stream.stop(); } }; // Expose fixPeerConnectionConfig. Adapter.fixPeerConnectionConfig = function (pcConfig) { var i, len, iceServer, hasUrls, hasUrl; if (!Array.isArray(pcConfig.iceServers)) { pcConfig.iceServers = []; } for (i = 0, len = pcConfig.iceServers.length; i < len; i += 1) { iceServer = pcConfig.iceServers[i]; hasUrls = iceServer.hasOwnProperty('urls'); hasUrl = iceServer.hasOwnProperty('url'); if (typeof iceServer === 'object') { // Has .urls but not .url, so add .url with a single string value. if (hasUrls && !hasUrl) { iceServer.url = (Array.isArray(iceServer.urls) ? iceServer.urls[0] : iceServer.urls); // Has .url but not .urls, so add .urls with same value. } else if (!hasUrls && hasUrl) { iceServer.urls = (Array.isArray(iceServer.url) ? iceServer.url.slice() : iceServer.url); } // Ensure .url is a single string. if (hasUrl && Array.isArray(iceServer.url)) { iceServer.url = iceServer.url[0]; } } } }; // Expose fixRTCOfferOptions. Adapter.fixRTCOfferOptions = function (options) { options = options || {}; // New spec. if (!oldSpecRTCOfferOptions) { if (options.mandatory && options.mandatory.OfferToReceiveAudio) { options.offerToReceiveAudio = 1; } if (options.mandatory && options.mandatory.OfferToReceiveVideo) { options.offerToReceiveVideo = 1; } delete options.mandatory; // Old spec. } else { if (options.offerToReceiveAudio) { options.mandatory = options.mandatory || {}; options.mandatory.OfferToReceiveAudio = true; } if (options.offerToReceiveVideo) { options.mandatory = options.mandatory || {}; options.mandatory.OfferToReceiveVideo = true; } } }; return Adapter; } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"bowser":5,"debug":6}],2:[function(require,module,exports){ 'use strict'; // Expose the RTCPeerConnection class. module.exports = RTCPeerConnection; // Dependencies. var merge = require('merge'), debug = require('debug')('rtcninja:RTCPeerConnection'), debugerror = require('debug')('rtcninja:ERROR:RTCPeerConnection'), Adapter = require('./Adapter'), // Internal constants. C = { REGEXP_NORMALIZED_CANDIDATE: new RegExp(/^candidate:/i), REGEXP_FIX_CANDIDATE: new RegExp(/(^a=|\r|\n)/gi), REGEXP_RELAY_CANDIDATE: new RegExp(/ relay /i), REGEXP_SDP_CANDIDATES: new RegExp(/^a=candidate:.*\r\n/igm), REGEXP_SDP_NON_RELAY_CANDIDATES: new RegExp(/^a=candidate:(.(?!relay ))*\r\n/igm) }, // Internal variables. VAR = { normalizeCandidate: null }; debugerror.log = console.warn.bind(console); // Constructor function RTCPeerConnection(pcConfig, pcConstraints) { debug('new | pcConfig: %o', pcConfig); // Set this.pcConfig and this.options. setConfigurationAndOptions.call(this, pcConfig); // NOTE: Deprecated pcConstraints argument. this.pcConstraints = pcConstraints; // Own version of the localDescription. this.ourLocalDescription = null; // Latest values of PC attributes to avoid events with same value. this.ourSignalingState = null; this.ourIceConnectionState = null; this.ourIceGatheringState = null; // Timer for options.gatheringTimeout. this.timerGatheringTimeout = null; // Timer for options.gatheringTimeoutAfterRelay. this.timerGatheringTimeoutAfterRelay = null; // Flag to ignore new gathered ICE candidates. this.ignoreIceGathering = false; // Flag set when closed. this.closed = false; // Set RTCPeerConnection. setPeerConnection.call(this); // Set properties. setProperties.call(this); } // Public API. RTCPeerConnection.prototype.createOffer = function (successCallback, failureCallback, options) { debug('createOffer()'); var self = this; Adapter.fixRTCOfferOptions(options); this.pc.createOffer( function (offer) { if (isClosed.call(self)) { return; } debug('createOffer() | success'); if (successCallback) { successCallback(offer); } }, function (error) { if (isClosed.call(self)) { return; } debugerror('createOffer() | error:', error); if (failureCallback) { failureCallback(error); } }, options ); }; RTCPeerConnection.prototype.createAnswer = function (successCallback, failureCallback, options) { debug('createAnswer()'); var self = this; this.pc.createAnswer( function (answer) { if (isClosed.call(self)) { return; } debug('createAnswer() | success'); if (successCallback) { successCallback(answer); } }, function (error) { if (isClosed.call(self)) { return; } debugerror('createAnswer() | error:', error); if (failureCallback) { failureCallback(error); } }, options ); }; RTCPeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) { debug('setLocalDescription()'); var self = this; this.pc.setLocalDescription( description, // success. function () { if (isClosed.call(self)) { return; } debug('setLocalDescription() | success'); // Clear gathering timers. clearTimeout(self.timerGatheringTimeout); delete self.timerGatheringTimeout; clearTimeout(self.timerGatheringTimeoutAfterRelay); delete self.timerGatheringTimeoutAfterRelay; runTimerGatheringTimeout(); if (successCallback) { successCallback(); } }, // failure function (error) { if (isClosed.call(self)) { return; } debugerror('setLocalDescription() | error:', error); if (failureCallback) { failureCallback(error); } } ); // Enable (again) ICE gathering. this.ignoreIceGathering = false; // Handle gatheringTimeout. function runTimerGatheringTimeout() { if (typeof self.options.gatheringTimeout !== 'number') { return; } // If setLocalDescription was already called, it may happen that // ICE gathering is not needed, so don't run this timer. if (self.pc.iceGatheringState === 'complete') { return; } debug('setLocalDescription() | ending gathering in %d ms (gatheringTimeout option)', self.options.gatheringTimeout); self.timerGatheringTimeout = setTimeout(function () { if (isClosed.call(self)) { return; } debug('forced end of candidates after gatheringTimeout timeout'); // Clear gathering timers. delete self.timerGatheringTimeout; clearTimeout(self.timerGatheringTimeoutAfterRelay); delete self.timerGatheringTimeoutAfterRelay; // Ignore new candidates. self.ignoreIceGathering = true; if (self.onicecandidate) { self.onicecandidate({ candidate: null }, null); } }, self.options.gatheringTimeout); } }; RTCPeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) { debug('setRemoteDescription()'); var self = this; this.pc.setRemoteDescription( description, function () { if (isClosed.call(self)) { return; } debug('setRemoteDescription() | success'); if (successCallback) { successCallback(); } }, function (error) { if (isClosed.call(self)) { return; } debugerror('setRemoteDescription() | error:', error); if (failureCallback) { failureCallback(error); } } ); }; RTCPeerConnection.prototype.updateIce = function (pcConfig) { debug('updateIce() | pcConfig: %o', pcConfig); // Update this.pcConfig and this.options. setConfigurationAndOptions.call(this, pcConfig); this.pc.updateIce(this.pcConfig); // Enable (again) ICE gathering. this.ignoreIceGathering = false; }; RTCPeerConnection.prototype.addIceCandidate = function (candidate, successCallback, failureCallback) { debug('addIceCandidate() | candidate: %o', candidate); var self = this; this.pc.addIceCandidate( candidate, function () { if (isClosed.call(self)) { return; } debug('addIceCandidate() | success'); if (successCallback) { successCallback(); } }, function (error) { if (isClosed.call(self)) { return; } debugerror('addIceCandidate() | error:', error); if (failureCallback) { failureCallback(error); } } ); }; RTCPeerConnection.prototype.getConfiguration = function () { debug('getConfiguration()'); return this.pc.getConfiguration(); }; RTCPeerConnection.prototype.getLocalStreams = function () { debug('getLocalStreams()'); return this.pc.getLocalStreams(); }; RTCPeerConnection.prototype.getRemoteStreams = function () { debug('getRemoteStreams()'); return this.pc.getRemoteStreams(); }; RTCPeerConnection.prototype.getStreamById = function (streamId) { debug('getStreamById() | streamId: %s', streamId); return this.pc.getStreamById(streamId); }; RTCPeerConnection.prototype.addStream = function (stream) { debug('addStream() | stream: %s', stream); this.pc.addStream(stream); }; RTCPeerConnection.prototype.removeStream = function (stream) { debug('removeStream() | stream: %o', stream); this.pc.removeStream(stream); }; RTCPeerConnection.prototype.close = function () { debug('close()'); this.closed = true; // Clear gathering timers. clearTimeout(this.timerGatheringTimeout); delete this.timerGatheringTimeout; clearTimeout(this.timerGatheringTimeoutAfterRelay); delete this.timerGatheringTimeoutAfterRelay; this.pc.close(); }; RTCPeerConnection.prototype.createDataChannel = function () { debug('createDataChannel()'); return this.pc.createDataChannel.apply(this.pc, arguments); }; RTCPeerConnection.prototype.createDTMFSender = function (track) { debug('createDTMFSender()'); return this.pc.createDTMFSender(track); }; RTCPeerConnection.prototype.getStats = function () { debug('getStats()'); return this.pc.getStats.apply(this.pc, arguments); }; RTCPeerConnection.prototype.setIdentityProvider = function () { debug('setIdentityProvider()'); return this.pc.setIdentityProvider.apply(this.pc, arguments); }; RTCPeerConnection.prototype.getIdentityAssertion = function () { debug('getIdentityAssertion()'); return this.pc.getIdentityAssertion(); }; RTCPeerConnection.prototype.reset = function (pcConfig) { debug('reset() | pcConfig: %o', pcConfig); var pc = this.pc; // Remove events in the old PC. pc.onnegotiationneeded = null; pc.onicecandidate = null; pc.onaddstream = null; pc.onremovestream = null; pc.ondatachannel = null; pc.onsignalingstatechange = null; pc.oniceconnectionstatechange = null; pc.onicegatheringstatechange = null; pc.onidentityresult = null; pc.onpeeridentity = null; pc.onidpassertionerror = null; pc.onidpvalidationerror = null; // Clear gathering timers. clearTimeout(this.timerGatheringTimeout); delete this.timerGatheringTimeout; clearTimeout(this.timerGatheringTimeoutAfterRelay); delete this.timerGatheringTimeoutAfterRelay; // Silently close the old PC. debug('reset() | closing current peerConnection'); pc.close(); // Set this.pcConfig and this.options. setConfigurationAndOptions.call(this, pcConfig); // Create a new PC. setPeerConnection.call(this); }; // Private Helpers. function setConfigurationAndOptions(pcConfig) { // Clone pcConfig. this.pcConfig = merge(true, pcConfig); // Fix pcConfig. Adapter.fixPeerConnectionConfig(this.pcConfig); this.options = { iceTransportsRelay: (this.pcConfig.iceTransports === 'relay'), iceTransportsNone: (this.pcConfig.iceTransports === 'none'), gatheringTimeout: this.pcConfig.gatheringTimeout, gatheringTimeoutAfterRelay: this.pcConfig.gatheringTimeoutAfterRelay }; // Remove custom rtcninja.RTCPeerConnection options from pcConfig. delete this.pcConfig.gatheringTimeout; delete this.pcConfig.gatheringTimeoutAfterRelay; debug('setConfigurationAndOptions | processed pcConfig: %o', this.pcConfig); } function isClosed() { return ((this.closed) || (this.pc && this.pc.iceConnectionState === 'closed')); } function setEvents() { var self = this, pc = this.pc; pc.onnegotiationneeded = function (event) { if (isClosed.call(self)) { return; } debug('onnegotiationneeded()'); if (self.onnegotiationneeded) { self.onnegotiationneeded(event); } }; pc.onicecandidate = function (event) { var candidate, isRelay, newCandidate; if (isClosed.call(self)) { return; } if (self.ignoreIceGathering) { return; } // Ignore any candidate (event the null one) if iceTransports:'none' is set. if (self.options.iceTransportsNone) { return; } candidate = event.candidate; if (candidate) { isRelay = C.REGEXP_RELAY_CANDIDATE.test(candidate.candidate); // Ignore if just relay candidates are requested. if (self.options.iceTransportsRelay && !isRelay) { return; } // Handle gatheringTimeoutAfterRelay. if (isRelay && !self.timerGatheringTimeoutAfterRelay && (typeof self.options.gatheringTimeoutAfterRelay === 'number')) { debug('onicecandidate() | first relay candidate found, ending gathering in %d ms', self.options.gatheringTimeoutAfterRelay); self.timerGatheringTimeoutAfterRelay = setTimeout(function () { if (isClosed.call(self)) { return; } debug('forced end of candidates after timeout'); // Clear gathering timers. delete self.timerGatheringTimeoutAfterRelay; clearTimeout(self.timerGatheringTimeout); delete self.timerGatheringTimeout; // Ignore new candidates. self.ignoreIceGathering = true; if (self.onicecandidate) { self.onicecandidate({candidate: null}, null); } }, self.options.gatheringTimeoutAfterRelay); } newCandidate = new Adapter.RTCIceCandidate({ sdpMid: candidate.sdpMid, sdpMLineIndex: candidate.sdpMLineIndex, candidate: candidate.candidate }); // Force correct candidate syntax (just check it once). if (VAR.normalizeCandidate === null) { if (C.REGEXP_NORMALIZED_CANDIDATE.test(candidate.candidate)) { VAR.normalizeCandidate = false; } else { debug('onicecandidate() | normalizing ICE candidates syntax (remove "a=" and "\\r\\n")'); VAR.normalizeCandidate = true; } } if (VAR.normalizeCandidate) { newCandidate.candidate = candidate.candidate.replace(C.REGEXP_FIX_CANDIDATE, ''); } debug( 'onicecandidate() | m%d(%s) %s', newCandidate.sdpMLineIndex, newCandidate.sdpMid || 'no mid', newCandidate.candidate); if (self.onicecandidate) { self.onicecandidate(event, newCandidate); } // Null candidate (end of candidates). } else { debug('onicecandidate() | end of candidates'); // Clear gathering timers. clearTimeout(self.timerGatheringTimeout); delete self.timerGatheringTimeout; clearTimeout(self.timerGatheringTimeoutAfterRelay); delete self.timerGatheringTimeoutAfterRelay; if (self.onicecandidate) { self.onicecandidate(event, null); } } }; pc.onaddstream = function (event) { if (isClosed.call(self)) { return; } debug('onaddstream() | stream: %o', event.stream); if (self.onaddstream) { self.onaddstream(event, event.stream); } }; pc.onremovestream = function (event) { if (isClosed.call(self)) { return; } debug('onremovestream() | stream: %o', event.stream); if (self.onremovestream) { self.onremovestream(event, event.stream); } }; pc.ondatachannel = function (event) { if (isClosed.call(self)) { return; } debug('ondatachannel() | datachannel: %o', event.channel); if (self.ondatachannel) { self.ondatachannel(event, event.channel); } }; pc.onsignalingstatechange = function (event) { if (pc.signalingState === self.ourSignalingState) { return; } debug('onsignalingstatechange() | signalingState: %s', pc.signalingState); self.ourSignalingState = pc.signalingState; if (self.onsignalingstatechange) { self.onsignalingstatechange(event, pc.signalingState); } }; pc.oniceconnectionstatechange = function (event) { if (pc.iceConnectionState === self.ourIceConnectionState) { return; } debug('oniceconnectionstatechange() | iceConnectionState: %s', pc.iceConnectionState); self.ourIceConnectionState = pc.iceConnectionState; if (self.oniceconnectionstatechange) { self.oniceconnectionstatechange(event, pc.iceConnectionState); } }; pc.onicegatheringstatechange = function (event) { if (isClosed.call(self)) { return; } if (pc.iceGatheringState === self.ourIceGatheringState) { return; } debug('onicegatheringstatechange() | iceGatheringState: %s', pc.iceGatheringState); self.ourIceGatheringState = pc.iceGatheringState; if (self.onicegatheringstatechange) { self.onicegatheringstatechange(event, pc.iceGatheringState); } }; pc.onidentityresult = function (event) { if (isClosed.call(self)) { return; } debug('onidentityresult()'); if (self.onidentityresult) { self.onidentityresult(event); } }; pc.onpeeridentity = function (event) { if (isClosed.call(self)) { return; } debug('onpeeridentity()'); if (self.onpeeridentity) { self.onpeeridentity(event); } }; pc.onidpassertionerror = function (event) { if (isClosed.call(self)) { return; } debug('onidpassertionerror()'); if (self.onidpassertionerror) { self.onidpassertionerror(event); } }; pc.onidpvalidationerror = function (event) { if (isClosed.call(self)) { return; } debug('onidpvalidationerror()'); if (self.onidpvalidationerror) { self.onidpvalidationerror(event); } }; } function setPeerConnection() { // Create a RTCPeerConnection. if (!this.pcConstraints) { this.pc = new Adapter.RTCPeerConnection(this.pcConfig); } else { // NOTE: Deprecated. this.pc = new Adapter.RTCPeerConnection(this.pcConfig, this.pcConstraints); } // Set RTC events. setEvents.call(this); } function getLocalDescription() { var pc = this.pc, options = this.options, sdp = null; if (!pc.localDescription) { this.ourLocalDescription = null; return null; } // Mangle the SDP string. if (options.iceTransportsRelay) { sdp = pc.localDescription.sdp.replace(C.REGEXP_SDP_NON_RELAY_CANDIDATES, ''); } else if (options.iceTransportsNone) { sdp = pc.localDescription.sdp.replace(C.REGEXP_SDP_CANDIDATES, ''); } this.ourLocalDescription = new Adapter.RTCSessionDescription({ type: pc.localDescription.type, sdp: sdp || pc.localDescription.sdp }); return this.ourLocalDescription; } function setProperties() { var self = this; Object.defineProperties(this, { peerConnection: { get: function () { return self.pc; } }, signalingState: { get: function () { return self.pc.signalingState; } }, iceConnectionState: { get: function () { return self.pc.iceConnectionState; } }, iceGatheringState: { get: function () { return self.pc.iceGatheringState; } }, localDescription: { get: function () { return getLocalDescription.call(self); } }, remoteDescription: { get: function () { return self.pc.remoteDescription; } }, peerIdentity: { get: function () { return self.pc.peerIdentity; } } }); } },{"./Adapter":1,"debug":6,"merge":9}],3:[function(require,module,exports){ 'use strict'; module.exports = rtcninja; // Dependencies. var browser = require('bowser').browser, debug = require('debug')('rtcninja'), debugerror = require('debug')('rtcninja:ERROR'), version = require('./version'), Adapter = require('./Adapter'), RTCPeerConnection = require('./RTCPeerConnection'), // Internal vars. called = false; debugerror.log = console.warn.bind(console); debug('version %s', version); debug('detected browser: %s %s [mobile:%s, tablet:%s, android:%s, ios:%s]', browser.name, browser.version, !!browser.mobile, !!browser.tablet, !!browser.android, !!browser.ios); // Constructor. function rtcninja(options) { // Load adapter var iface = Adapter(options || {}); // jshint ignore:line called = true; // Expose RTCPeerConnection class. rtcninja.RTCPeerConnection = RTCPeerConnection; // Expose WebRTC API and utils. rtcninja.getUserMedia = iface.getUserMedia; rtcninja.RTCSessionDescription = iface.RTCSessionDescription; rtcninja.RTCIceCandidate = iface.RTCIceCandidate; rtcninja.MediaStreamTrack = iface.MediaStreamTrack; rtcninja.getMediaDevices = iface.getMediaDevices; rtcninja.attachMediaStream = iface.attachMediaStream; rtcninja.closeMediaStream = iface.closeMediaStream; rtcninja.canRenegotiate = iface.canRenegotiate; // Log WebRTC support. if (iface.hasWebRTC()) { debug('WebRTC supported'); return true; } else { debugerror('WebRTC not supported'); return false; } } // Public API. // If called without calling rtcninja(), call it. rtcninja.hasWebRTC = function () { if (!called) { rtcninja(); } return Adapter.hasWebRTC(); }; // Expose version property. Object.defineProperty(rtcninja, 'version', { get: function () { return version; } }); // Expose called property. Object.defineProperty(rtcninja, 'called', { get: function () { return called; } }); // Exposing stuff. rtcninja.debug = require('debug'); rtcninja.browser = browser; },{"./Adapter":1,"./RTCPeerConnection":2,"./version":4,"bowser":5,"debug":6}],4:[function(require,module,exports){ 'use strict'; // Expose the 'version' field of package.json. module.exports = require('../package.json').version; },{"../package.json":10}],5:[function(require,module,exports){ /*! * Bowser - a browser detector * https://github.com/ded/bowser * MIT License | (c) Dustin Diaz 2014 */ !function (name, definition) { if (typeof module != 'undefined' && module.exports) module.exports['browser'] = definition() else if (typeof define == 'function' && define.amd) define(definition) else this[name] = definition() }('bowser', function () { /** * See useragents.js for examples of navigator.userAgent */ var t = true function detect(ua) { function getFirstMatch(regex) { var match = ua.match(regex); return (match && match.length > 1 && match[1]) || ''; } function getSecondMatch(regex) { var match = ua.match(regex); return (match && match.length > 1 && match[2]) || ''; } var iosdevice = getFirstMatch(/(ipod|iphone|ipad)/i).toLowerCase() , likeAndroid = /like android/i.test(ua) , android = !likeAndroid && /android/i.test(ua) , edgeVersion = getFirstMatch(/edge\/(\d+(\.\d+)?)/i) , versionIdentifier = getFirstMatch(/version\/(\d+(\.\d+)?)/i) , tablet = /tablet/i.test(ua) , mobile = !tablet && /[^-]mobi/i.test(ua) , result if (/opera|opr/i.test(ua)) { result = { name: 'Opera' , opera: t , version: versionIdentifier || getFirstMatch(/(?:opera|opr)[\s\/](\d+(\.\d+)?)/i) } } else if (/windows phone/i.test(ua)) { result = { name: 'Windows Phone' , windowsphone: t } if (edgeVersion) { result.msedge = t result.version = edgeVersion } else { result.msie = t result.version = getFirstMatch(/iemobile\/(\d+(\.\d+)?)/i) } } else if (/msie|trident/i.test(ua)) { result = { name: 'Internet Explorer' , msie: t , version: getFirstMatch(/(?:msie |rv:)(\d+(\.\d+)?)/i) } } else if (/chrome.+? edge/i.test(ua)) { result = { name: 'Microsoft Edge' , msedge: t , version: edgeVersion } } else if (/chrome|crios|crmo/i.test(ua)) { result = { name: 'Chrome' , chrome: t , version: getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i) } } else if (iosdevice) { result = { name : iosdevice == 'iphone' ? 'iPhone' : iosdevice == 'ipad' ? 'iPad' : 'iPod' } // WTF: version is not part of user agent in web apps if (versionIdentifier) { result.version = versionIdentifier } } else if (/sailfish/i.test(ua)) { result = { name: 'Sailfish' , sailfish: t , version: getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i) } } else if (/seamonkey\//i.test(ua)) { result = { name: 'SeaMonkey' , seamonkey: t , version: getFirstMatch(/seamonkey\/(\d+(\.\d+)?)/i) } } else if (/firefox|iceweasel/i.test(ua)) { result = { name: 'Firefox' , firefox: t , version: getFirstMatch(/(?:firefox|iceweasel)[ \/](\d+(\.\d+)?)/i) } if (/\((mobile|tablet);[^\)]*rv:[\d\.]+\)/i.test(ua)) { result.firefoxos = t } } else if (/silk/i.test(ua)) { result = { name: 'Amazon Silk' , silk: t , version : getFirstMatch(/silk\/(\d+(\.\d+)?)/i) } } else if (android) { result = { name: 'Android' , version: versionIdentifier } } else if (/phantom/i.test(ua)) { result = { name: 'PhantomJS' , phantom: t , version: getFirstMatch(/phantomjs\/(\d+(\.\d+)?)/i) } } else if (/blackberry|\bbb\d+/i.test(ua) || /rim\stablet/i.test(ua)) { result = { name: 'BlackBerry' , blackberry: t , version: versionIdentifier || getFirstMatch(/blackberry[\d]+\/(\d+(\.\d+)?)/i) } } else if (/(web|hpw)os/i.test(ua)) { result = { name: 'WebOS' , webos: t , version: versionIdentifier || getFirstMatch(/w(?:eb)?osbrowser\/(\d+(\.\d+)?)/i) }; /touchpad\//i.test(ua) && (result.touchpad = t) } else if (/bada/i.test(ua)) { result = { name: 'Bada' , bada: t , version: getFirstMatch(/dolfin\/(\d+(\.\d+)?)/i) }; } else if (/tizen/i.test(ua)) { result = { name: 'Tizen' , tizen: t , version: getFirstMatch(/(?:tizen\s?)?browser\/(\d+(\.\d+)?)/i) || versionIdentifier }; } else if (/safari/i.test(ua)) { result = { name: 'Safari' , safari: t , version: versionIdentifier } } else { result = { name: getFirstMatch(/^(.*)\/(.*) /), version: getSecondMatch(/^(.*)\/(.*) /) }; } // set webkit or gecko flag for browsers based on these engines if (!result.msedge && /(apple)?webkit/i.test(ua)) { result.name = result.name || "Webkit" result.webkit = t if (!result.version && versionIdentifier) { result.version = versionIdentifier } } else if (!result.opera && /gecko\//i.test(ua)) { result.name = result.name || "Gecko" result.gecko = t result.version = result.version || getFirstMatch(/gecko\/(\d+(\.\d+)?)/i) } // set OS flags for platforms that have multiple browsers if (!result.msedge && (android || result.silk)) { result.android = t } else if (iosdevice) { result[iosdevice] = t result.ios = t } // OS version extraction var osVersion = ''; if (result.windowsphone) { osVersion = getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i); } else if (iosdevice) { osVersion = getFirstMatch(/os (\d+([_\s]\d+)*) like mac os x/i); osVersion = osVersion.replace(/[_\s]/g, '.'); } else if (android) { osVersion = getFirstMatch(/android[ \/-](\d+(\.\d+)*)/i); } else if (result.webos) { osVersion = getFirstMatch(/(?:web|hpw)os\/(\d+(\.\d+)*)/i); } else if (result.blackberry) { osVersion = getFirstMatch(/rim\stablet\sos\s(\d+(\.\d+)*)/i); } else if (result.bada) { osVersion = getFirstMatch(/bada\/(\d+(\.\d+)*)/i); } else if (result.tizen) { osVersion = getFirstMatch(/tizen[\/\s](\d+(\.\d+)*)/i); } if (osVersion) { result.osversion = osVersion; } // device type extraction var osMajorVersion = osVersion.split('.')[0]; if (tablet || iosdevice == 'ipad' || (android && (osMajorVersion == 3 || (osMajorVersion == 4 && !mobile))) || result.silk) { result.tablet = t } else if (mobile || iosdevice == 'iphone' || iosdevice == 'ipod' || android || result.blackberry || result.webos || result.bada) { result.mobile = t } // Graded Browser Support // http://developer.yahoo.com/yui/articles/gbs if (result.msedge || (result.msie && result.version >= 10) || (result.chrome && result.version >= 20) || (result.firefox && result.version >= 20.0) || (result.safari && result.version >= 6) || (result.opera && result.version >= 10.0) || (result.ios && result.osversion && result.osversion.split(".")[0] >= 6) || (result.blackberry && result.version >= 10.1) ) { result.a = t; } else if ((result.msie && result.version < 10) || (result.chrome && result.version < 20) || (result.firefox && result.version < 20.0) || (result.safari && result.version < 6) || (result.opera && result.version < 10.0) || (result.ios && result.osversion && result.osversion.split(".")[0] < 6) ) { result.c = t } else result.x = t return result } var bowser = detect(typeof navigator !== 'undefined' ? navigator.userAgent : '') bowser.test = function (browserList) { for (var i = 0; i < browserList.length; ++i) { var browserItem = browserList[i]; if (typeof browserItem=== 'string') { if (browserItem in bowser) { return true; } } } return false; } /* * Set our detect method to the main bowser object so we can * reuse it to test other user agents. * This is needed to implement future tests. */ bowser._detect = detect; return bowser }); },{}],6:[function(require,module,exports){ /** * This is the web browser implementation of `debug()`. * * Expose `debug()` as the module. */ exports = module.exports = require('./debug'); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; exports.load = load; exports.useColors = useColors; exports.storage = 'undefined' != typeof chrome && 'undefined' != typeof chrome.storage ? chrome.storage.local : localstorage(); /** * Colors. */ exports.colors = [ 'lightseagreen', 'forestgreen', 'goldenrod', 'dodgerblue', 'darkorchid', 'crimson' ]; /** * Currently only WebKit-based Web Inspectors, Firefox >= v31, * and the Firebug extension (any Firefox version) are known * to support "%c" CSS customizations. * * TODO: add a `localStorage` variable to explicitly enable/disable colors */ function useColors() { // is webkit? http://stackoverflow.com/a/16459606/376773 return ('WebkitAppearance' in document.documentElement.style) || // is firebug? http://stackoverflow.com/a/398120/376773 (window.console && (console.firebug || (console.exception && console.table))) || // is firefox >= v31? // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); } /** * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. */ exports.formatters.j = function(v) { return JSON.stringify(v); }; /** * Colorize log arguments if enabled. * * @api public */ function formatArgs() { var args = arguments; var useColors = this.useColors; args[0] = (useColors ? '%c' : '') + this.namespace + (useColors ? ' %c' : ' ') + args[0] + (useColors ? '%c ' : ' ') + '+' + exports.humanize(this.diff); if (!useColors) return args; var c = 'color: ' + this.color; args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); // the final "%c" is somewhat tricky, because there could be other // arguments passed either before or after the %c, so we need to // figure out the correct index to insert the CSS into var index = 0; var lastC = 0; args[0].replace(/%[a-z%]/g, function(match) { if ('%%' === match) return; index++; if ('%c' === match) { // we only are interested in the *last* %c // (the user may have provided their own) lastC = index; } }); args.splice(lastC, 0, c); return args; } /** * Invokes `console.log()` when available. * No-op when `console.log` is not a "function". * * @api public */ function log() { // this hackery is required for IE8/9, where // the `console.log` function doesn't have 'apply' return 'object' === typeof console && console.log && Function.prototype.apply.call(console.log, console, arguments); } /** * Save `namespaces`. * * @param {String} namespaces * @api private */ function save(namespaces) { try { if (null == namespaces) { exports.storage.removeItem('debug'); } else { exports.storage.debug = namespaces; } } catch(e) {} } /** * Load `namespaces`. * * @return {String} returns the previously persisted debug modes * @api private */ function load() { var r; try { r = exports.storage.debug; } catch(e) {} return r; } /** * Enable namespaces listed in `localStorage.debug` initially. */ exports.enable(load()); /** * Localstorage attempts to return the localstorage. * * This is necessary because safari throws * when a user disables cookies/localstorage * and you attempt to access it. * * @return {LocalStorage} * @api private */ function localstorage(){ try { return window.localStorage; } catch (e) {} } },{"./debug":7}],7:[function(require,module,exports){ /** * This is the common logic for both the Node.js and web browser * implementations of `debug()`. * * Expose `debug()` as the module. */ exports = module.exports = debug; exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; exports.humanize = require('ms'); /** * The currently active debug mode names, and names to skip. */ exports.names = []; exports.skips = []; /** * Map of special "%n" handling functions, for the debug "format" argument. * * Valid key names are a single, lowercased letter, i.e. "n". */ exports.formatters = {}; /** * Previously assigned color. */ var prevColor = 0; /** * Previous log timestamp. */ var prevTime; /** * Select a color. * * @return {Number} * @api private */ function selectColor() { return exports.colors[prevColor++ % exports.colors.length]; } /** * Create a debugger with the given `namespace`. * * @param {String} namespace * @return {Function} * @api public */ function debug(namespace) { // define the `disabled` version function disabled() { } disabled.enabled = false; // define the `enabled` version function enabled() { var self = enabled; // set `diff` timestamp var curr = +new Date(); var ms = curr - (prevTime || curr); self.diff = ms; self.prev = prevTime; self.curr = curr; prevTime = curr; // add the `color` if not set if (null == self.useColors) self.useColors = exports.useColors(); if (null == self.color && self.useColors) self.color = selectColor(); var args = Array.prototype.slice.call(arguments); args[0] = exports.coerce(args[0]); if ('string' !== typeof args[0]) { // anything else let's inspect with %o args = ['%o'].concat(args); } // apply any `formatters` transformations var index = 0; args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { // if we encounter an escaped % then don't increase the array index if (match === '%%') return match; index++; var formatter = exports.formatters[format]; if ('function' === typeof formatter) { var val = args[index]; match = formatter.call(self, val); // now we need to remove `args[index]` since it's inlined in the `format` args.splice(index, 1); index--; } return match; }); if ('function' === typeof exports.formatArgs) { args = exports.formatArgs.apply(self, args); } var logFn = enabled.log || exports.log || console.log.bind(console); logFn.apply(self, args); } enabled.enabled = true; var fn = exports.enabled(namespace) ? enabled : disabled; fn.namespace = namespace; return fn; } /** * Enables a debug mode by namespaces. This can include modes * separated by a colon and wildcards. * * @param {String} namespaces * @api public */ function enable(namespaces) { exports.save(namespaces); var split = (namespaces || '').split(/[\s,]+/); var len = split.length; for (var i = 0; i < len; i++) { if (!split[i]) continue; // ignore empty strings namespaces = split[i].replace(/\*/g, '.*?'); if (namespaces[0] === '-') { exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); } else { exports.names.push(new RegExp('^' + namespaces + '$')); } } } /** * Disable debug output. * * @api public */ function disable() { exports.enable(''); } /** * Returns true if the given mode name is enabled, false otherwise. * * @param {String} name * @return {Boolean} * @api public */ function enabled(name) { var i, len; for (i = 0, len = exports.skips.length; i < len; i++) { if (exports.skips[i].test(name)) { return false; } } for (i = 0, len = exports.names.length; i < len; i++) { if (exports.names[i].test(name)) { return true; } } return false; } /** * Coerce `val`. * * @param {Mixed} val * @return {Mixed} * @api private */ function coerce(val) { if (val instanceof Error) return val.stack || val.message; return val; } },{"ms":8}],8:[function(require,module,exports){ /** * Helpers. */ var s = 1000; var m = s * 60; var h = m * 60; var d = h * 24; var y = d * 365.25; /** * Parse or format the given `val`. * * Options: * * - `long` verbose formatting [false] * * @param {String|Number} val * @param {Object} options * @return {String|Number} * @api public */ module.exports = function(val, options){ options = options || {}; if ('string' == typeof val) return parse(val); return options.long ? long(val) : short(val); }; /** * Parse the given `str` and return milliseconds. * * @param {String} str * @return {Number} * @api private */ function parse(str) { str = '' + str; if (str.length > 10000) return; var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str); if (!match) return; var n = parseFloat(match[1]); var type = (match[2] || 'ms').toLowerCase(); switch (type) { case 'years': case 'year': case 'yrs': case 'yr': case 'y': return n * y; case 'days': case 'day': case 'd': return n * d; case 'hours': case 'hour': case 'hrs': case 'hr': case 'h': return n * h; case 'minutes': case 'minute': case 'mins': case 'min': case 'm': return n * m; case 'seconds': case 'second': case 'secs': case 'sec': case 's': return n * s; case 'milliseconds': case 'millisecond': case 'msecs': case 'msec': case 'ms': return n; } } /** * Short format for `ms`. * * @param {Number} ms * @return {String} * @api private */ function short(ms) { if (ms >= d) return Math.round(ms / d) + 'd'; if (ms >= h) return Math.round(ms / h) + 'h'; if (ms >= m) return Math.round(ms / m) + 'm'; if (ms >= s) return Math.round(ms / s) + 's'; return ms + 'ms'; } /** * Long format for `ms`. * * @param {Number} ms * @return {String} * @api private */ function long(ms) { return plural(ms, d, 'day') || plural(ms, h, 'hour') || plural(ms, m, 'minute') || plural(ms, s, 'second') || ms + ' ms'; } /** * Pluralization helper. */ function plural(ms, n, name) { if (ms < n) return; if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; return Math.ceil(ms / n) + ' ' + name + 's'; } },{}],9:[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 (http://eface2face.com)", "contributors": [ "Jesús Pérez " ], "license": "MIT", "main": "lib/rtcninja.js", "homepage": "https://github.com/eface2face/rtcninja.js", "repository": { "type": "git", "url": "https://github.com/eface2face/rtcninja.js.git" }, "keywords": [ "webrtc" ], "engines": { "node": ">=0.10.32" }, "dependencies": { "bowser": "^0.7.3", "debug": "^2.2.0", "merge": "^1.2.0" }, "devDependencies": { "browserify": "^10.2.3", "gulp": "git+https://github.com/gulpjs/gulp.git#4.0", "gulp-expect-file": "0.0.7", "gulp-filelog": "^0.4.1", "gulp-header": "^1.2.2", "gulp-jscs": "^1.6.0", "gulp-jscs-stylish": "^1.1.0", "gulp-jshint": "^1.11.0", "gulp-rename": "^1.2.2", "gulp-uglify": "^1.2.0", "jshint-stylish": "^1.0.2", "retire": "^1.1.0", "shelljs": "^0.5.0", "vinyl-source-stream": "^1.1.0" } } },{}]},{},[3])(3) });rtcninja.js-0.6.2/dist/rtcninja-0.6.2.min.js000066400000000000000000000613541254426444300203170ustar00rootroot00000000000000/* * rtcninja.js v0.6.2 * WebRTC API wrapper to deal with different browsers * Copyright 2015 Iñaki Baz Castillo (http://eface2face.com) * License MIT */ !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.rtcninja=e()}}(function(){var e;return function t(e,n,i){function o(a,s){if(!n[a]){if(!e[a]){var c="function"==typeof require&&require;if(!s&&c)return c(a,!0);if(r)return r(a,!0);var d=new Error("Cannot find module '"+a+"'");throw d.code="MODULE_NOT_FOUND",d}var l=n[a]={exports:{}};e[a][0].call(l.exports,function(t){var n=e[a][1][t];return o(n?n:t)},l,l.exports,t,e,n,i)}return n[a].exports}for(var r="function"==typeof require&&require,a=0;a=32||a.android&&a.chrome&&v>=39||b&&a.opera&&v>=27||a.android&&a.opera&&v>=24||a.android&&a.webkit&&!a.chrome&&v>=37||r.webkitGetUserMedia&&o.webkitRTCPeerConnection)C=!0,d=r.webkitGetUserMedia.bind(r),l=o.webkitRTCPeerConnection,u=o.RTCSessionDescription,p=o.RTCIceCandidate,g=o.MediaStreamTrack,g&&g.getSources?f=g.getSources.bind(g):r.getMediaDevices&&(f=r.getMediaDevices.bind(r)),m=function(e,t){return e.src=URL.createObjectURL(t),e},h=!0,y=!1;else if(b&&a.firefox&&v>=22||a.android&&a.firefox&&v>=33||r.mozGetUserMedia&&o.mozRTCPeerConnection)C=!0,d=r.mozGetUserMedia.bind(r),l=o.mozRTCPeerConnection,u=o.mozRTCSessionDescription,p=o.mozRTCIceCandidate,g=o.MediaStreamTrack,m=function(e,t){return e.src=URL.createObjectURL(t),e},h=!1,y=!1;else if(e.plugin&&"function"==typeof e.plugin.isRequired&&e.plugin.isRequired()&&"function"==typeof e.plugin.isInstalled&&e.plugin.isInstalled()){var n=e.plugin["interface"];C=!0,d=n.getUserMedia,l=n.RTCPeerConnection,u=n.RTCSessionDescription,p=n.RTCIceCandidate,g=n.MediaStreamTrack,g&&g.getSources?f=g.getSources.bind(g):r.getMediaDevices&&(f=r.getMediaDevices.bind(r)),m=n.attachMediaStream,h=n.canRenegotiate,y=!0}else r.getUserMedia&&o.RTCPeerConnection&&(C=!0,d=r.getUserMedia.bind(r),l=o.RTCPeerConnection,u=o.RTCSessionDescription,p=o.RTCIceCandidate,g=o.MediaStreamTrack,g&&g.getSources?f=g.getSources.bind(g):r.getMediaDevices&&(f=r.getMediaDevices.bind(r)),m=o.attachMediaStream||function(e,t){return e.src=URL.createObjectURL(t),e},h=!1,y=!1);return i.hasWebRTC=function(){return C},i.getUserMedia=d?function(e,t,n){s("getUserMedia() | constraints: %o",e);try{d(e,function(e){s("getUserMedia() | success"),t&&t(e)},function(e){s("getUserMedia() | error:",e),n&&n(e)})}catch(i){c("getUserMedia() | error:",i),n&&n(i)}}:function(e,n,i){c("getUserMedia() | WebRTC not supported"),i?i(new Error("rtcninja: WebRTC not supported, missing getUserMedia [browser: "+a.name+" "+a.version+"]")):t("getUserMedia")},i.RTCPeerConnection=l||t("RTCPeerConnection"),i.RTCSessionDescription=u||t("RTCSessionDescription"),i.RTCIceCandidate=p||t("RTCIceCandidate"),i.MediaStreamTrack=g||t("MediaStreamTrack"),i.getMediaDevices=f,i.attachMediaStream=m||t("attachMediaStream"),i.canRenegotiate=h,i.closeMediaStream=function(e){if(e)if(g&&g.prototype&&g.prototype.stop){s("closeMediaStream() | calling stop() on all the MediaStreamTrack");var t,n,i;if(e.getTracks)for(t=e.getTracks(),n=0,i=t.length;i>n;n+=1)t[n].stop();else{for(t=e.getAudioTracks(),n=0,i=t.length;i>n;n+=1)t[n].stop();for(t=e.getVideoTracks(),n=0,i=t.length;i>n;n+=1)t[n].stop()}}else"function"==typeof e.stop&&(s("closeMediaStream() | calling stop() on the MediaStream"),e.stop())},i.fixPeerConnectionConfig=function(e){var t,n,i,o,r;for(Array.isArray(e.iceServers)||(e.iceServers=[]),t=0,n=e.iceServers.length;n>t;t+=1)i=e.iceServers[t],o=i.hasOwnProperty("urls"),r=i.hasOwnProperty("url"),"object"==typeof i&&(o&&!r?i.url=Array.isArray(i.urls)?i.urls[0]:i.urls:!o&&r&&(i.urls=Array.isArray(i.url)?i.url.slice():i.url),r&&Array.isArray(i.url)&&(i.url=i.url[0]))},i.fixRTCOfferOptions=function(e){e=e||{},y?(e.offerToReceiveAudio&&(e.mandatory=e.mandatory||{},e.mandatory.OfferToReceiveAudio=!0),e.offerToReceiveVideo&&(e.mandatory=e.mandatory||{},e.mandatory.OfferToReceiveVideo=!0)):(e.mandatory&&e.mandatory.OfferToReceiveAudio&&(e.offerToReceiveAudio=1),e.mandatory&&e.mandatory.OfferToReceiveVideo&&(e.offerToReceiveVideo=1),delete e.mandatory)},i}t.exports=i;var o,r,a=e("bowser").browser,s=e("debug")("rtcninja:Adapter"),c=e("debug")("rtcninja:ERROR:Adapter"),d=null,l=null,u=null,p=null,g=null,f=null,m=null,h=!1,y=!1,v=Number(a.version)||0,b=!(a.mobile&&a.tablet),C=!1;c.log=console.warn.bind(console),o=n.window||n,r=o.navigator||{}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{bowser:5,debug:6}],2:[function(e,t,n){"use strict";function i(e,t){u("new | pcConfig: %o",e),o.call(this,e),this.pcConstraints=t,this.ourLocalDescription=null,this.ourSignalingState=null,this.ourIceConnectionState=null,this.ourIceGatheringState=null,this.timerGatheringTimeout=null,this.timerGatheringTimeoutAfterRelay=null,this.ignoreIceGathering=!1,this.closed=!1,s.call(this),d.call(this)}function o(e){this.pcConfig=l(!0,e),g.fixPeerConnectionConfig(this.pcConfig),this.options={iceTransportsRelay:"relay"===this.pcConfig.iceTransports,iceTransportsNone:"none"===this.pcConfig.iceTransports,gatheringTimeout:this.pcConfig.gatheringTimeout,gatheringTimeoutAfterRelay:this.pcConfig.gatheringTimeoutAfterRelay},delete this.pcConfig.gatheringTimeout,delete this.pcConfig.gatheringTimeoutAfterRelay,u("setConfigurationAndOptions | processed pcConfig: %o",this.pcConfig)}function r(){return this.closed||this.pc&&"closed"===this.pc.iceConnectionState}function a(){var e=this,t=this.pc;t.onnegotiationneeded=function(t){r.call(e)||(u("onnegotiationneeded()"),e.onnegotiationneeded&&e.onnegotiationneeded(t))},t.onicecandidate=function(t){var n,i,o;if(!r.call(e)&&!e.ignoreIceGathering&&!e.options.iceTransportsNone)if(n=t.candidate){if(i=f.REGEXP_RELAY_CANDIDATE.test(n.candidate),e.options.iceTransportsRelay&&!i)return;i&&!e.timerGatheringTimeoutAfterRelay&&"number"==typeof e.options.gatheringTimeoutAfterRelay&&(u("onicecandidate() | first relay candidate found, ending gathering in %d ms",e.options.gatheringTimeoutAfterRelay),e.timerGatheringTimeoutAfterRelay=setTimeout(function(){r.call(e)||(u("forced end of candidates after timeout"),delete e.timerGatheringTimeoutAfterRelay,clearTimeout(e.timerGatheringTimeout),delete e.timerGatheringTimeout,e.ignoreIceGathering=!0,e.onicecandidate&&e.onicecandidate({candidate:null},null))},e.options.gatheringTimeoutAfterRelay)),o=new g.RTCIceCandidate({sdpMid:n.sdpMid,sdpMLineIndex:n.sdpMLineIndex,candidate:n.candidate}),null===m.normalizeCandidate&&(f.REGEXP_NORMALIZED_CANDIDATE.test(n.candidate)?m.normalizeCandidate=!1:(u('onicecandidate() | normalizing ICE candidates syntax (remove "a=" and "\\r\\n")'),m.normalizeCandidate=!0)),m.normalizeCandidate&&(o.candidate=n.candidate.replace(f.REGEXP_FIX_CANDIDATE,"")),u("onicecandidate() | m%d(%s) %s",o.sdpMLineIndex,o.sdpMid||"no mid",o.candidate),e.onicecandidate&&e.onicecandidate(t,o)}else u("onicecandidate() | end of candidates"),clearTimeout(e.timerGatheringTimeout),delete e.timerGatheringTimeout,clearTimeout(e.timerGatheringTimeoutAfterRelay),delete e.timerGatheringTimeoutAfterRelay,e.onicecandidate&&e.onicecandidate(t,null)},t.onaddstream=function(t){r.call(e)||(u("onaddstream() | stream: %o",t.stream),e.onaddstream&&e.onaddstream(t,t.stream))},t.onremovestream=function(t){r.call(e)||(u("onremovestream() | stream: %o",t.stream),e.onremovestream&&e.onremovestream(t,t.stream))},t.ondatachannel=function(t){r.call(e)||(u("ondatachannel() | datachannel: %o",t.channel),e.ondatachannel&&e.ondatachannel(t,t.channel))},t.onsignalingstatechange=function(n){t.signalingState!==e.ourSignalingState&&(u("onsignalingstatechange() | signalingState: %s",t.signalingState),e.ourSignalingState=t.signalingState,e.onsignalingstatechange&&e.onsignalingstatechange(n,t.signalingState))},t.oniceconnectionstatechange=function(n){t.iceConnectionState!==e.ourIceConnectionState&&(u("oniceconnectionstatechange() | iceConnectionState: %s",t.iceConnectionState),e.ourIceConnectionState=t.iceConnectionState,e.oniceconnectionstatechange&&e.oniceconnectionstatechange(n,t.iceConnectionState))},t.onicegatheringstatechange=function(n){r.call(e)||t.iceGatheringState!==e.ourIceGatheringState&&(u("onicegatheringstatechange() | iceGatheringState: %s",t.iceGatheringState),e.ourIceGatheringState=t.iceGatheringState,e.onicegatheringstatechange&&e.onicegatheringstatechange(n,t.iceGatheringState))},t.onidentityresult=function(t){r.call(e)||(u("onidentityresult()"),e.onidentityresult&&e.onidentityresult(t))},t.onpeeridentity=function(t){r.call(e)||(u("onpeeridentity()"),e.onpeeridentity&&e.onpeeridentity(t))},t.onidpassertionerror=function(t){r.call(e)||(u("onidpassertionerror()"),e.onidpassertionerror&&e.onidpassertionerror(t))},t.onidpvalidationerror=function(t){r.call(e)||(u("onidpvalidationerror()"),e.onidpvalidationerror&&e.onidpvalidationerror(t))}}function s(){this.pc=this.pcConstraints?new g.RTCPeerConnection(this.pcConfig,this.pcConstraints):new g.RTCPeerConnection(this.pcConfig),a.call(this)}function c(){var e=this.pc,t=this.options,n=null;return e.localDescription?(t.iceTransportsRelay?n=e.localDescription.sdp.replace(f.REGEXP_SDP_NON_RELAY_CANDIDATES,""):t.iceTransportsNone&&(n=e.localDescription.sdp.replace(f.REGEXP_SDP_CANDIDATES,"")),this.ourLocalDescription=new g.RTCSessionDescription({type:e.localDescription.type,sdp:n||e.localDescription.sdp}),this.ourLocalDescription):(this.ourLocalDescription=null,null)}function d(){var e=this;Object.defineProperties(this,{peerConnection:{get:function(){return e.pc}},signalingState:{get:function(){return e.pc.signalingState}},iceConnectionState:{get:function(){return e.pc.iceConnectionState}},iceGatheringState:{get:function(){return e.pc.iceGatheringState}},localDescription:{get:function(){return c.call(e)}},remoteDescription:{get:function(){return e.pc.remoteDescription}},peerIdentity:{get:function(){return e.pc.peerIdentity}}})}t.exports=i;var l=e("merge"),u=e("debug")("rtcninja:RTCPeerConnection"),p=e("debug")("rtcninja:ERROR:RTCPeerConnection"),g=e("./Adapter"),f={REGEXP_NORMALIZED_CANDIDATE:new RegExp(/^candidate:/i),REGEXP_FIX_CANDIDATE:new RegExp(/(^a=|\r|\n)/gi),REGEXP_RELAY_CANDIDATE:new RegExp(/ relay /i),REGEXP_SDP_CANDIDATES:new RegExp(/^a=candidate:.*\r\n/gim),REGEXP_SDP_NON_RELAY_CANDIDATES:new RegExp(/^a=candidate:(.(?!relay ))*\r\n/gim)},m={normalizeCandidate:null};p.log=console.warn.bind(console),i.prototype.createOffer=function(e,t,n){u("createOffer()");var i=this;g.fixRTCOfferOptions(n),this.pc.createOffer(function(t){r.call(i)||(u("createOffer() | success"),e&&e(t))},function(e){r.call(i)||(p("createOffer() | error:",e),t&&t(e))},n)},i.prototype.createAnswer=function(e,t,n){u("createAnswer()");var i=this;this.pc.createAnswer(function(t){r.call(i)||(u("createAnswer() | success"),e&&e(t))},function(e){r.call(i)||(p("createAnswer() | error:",e),t&&t(e))},n)},i.prototype.setLocalDescription=function(e,t,n){function i(){"number"==typeof o.options.gatheringTimeout&&"complete"!==o.pc.iceGatheringState&&(u("setLocalDescription() | ending gathering in %d ms (gatheringTimeout option)",o.options.gatheringTimeout),o.timerGatheringTimeout=setTimeout(function(){r.call(o)||(u("forced end of candidates after gatheringTimeout timeout"),delete o.timerGatheringTimeout,clearTimeout(o.timerGatheringTimeoutAfterRelay),delete o.timerGatheringTimeoutAfterRelay,o.ignoreIceGathering=!0,o.onicecandidate&&o.onicecandidate({candidate:null},null))},o.options.gatheringTimeout))}u("setLocalDescription()");var o=this;this.pc.setLocalDescription(e,function(){r.call(o)||(u("setLocalDescription() | success"),clearTimeout(o.timerGatheringTimeout),delete o.timerGatheringTimeout,clearTimeout(o.timerGatheringTimeoutAfterRelay),delete o.timerGatheringTimeoutAfterRelay,i(),t&&t())},function(e){r.call(o)||(p("setLocalDescription() | error:",e),n&&n(e))}),this.ignoreIceGathering=!1},i.prototype.setRemoteDescription=function(e,t,n){u("setRemoteDescription()");var i=this;this.pc.setRemoteDescription(e,function(){r.call(i)||(u("setRemoteDescription() | success"),t&&t())},function(e){r.call(i)||(p("setRemoteDescription() | error:",e),n&&n(e))})},i.prototype.updateIce=function(e){u("updateIce() | pcConfig: %o",e),o.call(this,e),this.pc.updateIce(this.pcConfig),this.ignoreIceGathering=!1},i.prototype.addIceCandidate=function(e,t,n){u("addIceCandidate() | candidate: %o",e);var i=this;this.pc.addIceCandidate(e,function(){r.call(i)||(u("addIceCandidate() | success"),t&&t())},function(e){r.call(i)||(p("addIceCandidate() | error:",e),n&&n(e))})},i.prototype.getConfiguration=function(){return u("getConfiguration()"),this.pc.getConfiguration()},i.prototype.getLocalStreams=function(){return u("getLocalStreams()"),this.pc.getLocalStreams()},i.prototype.getRemoteStreams=function(){return u("getRemoteStreams()"),this.pc.getRemoteStreams()},i.prototype.getStreamById=function(e){return u("getStreamById() | streamId: %s",e),this.pc.getStreamById(e)},i.prototype.addStream=function(e){u("addStream() | stream: %s",e),this.pc.addStream(e)},i.prototype.removeStream=function(e){u("removeStream() | stream: %o",e),this.pc.removeStream(e)},i.prototype.close=function(){u("close()"),this.closed=!0,clearTimeout(this.timerGatheringTimeout),delete this.timerGatheringTimeout,clearTimeout(this.timerGatheringTimeoutAfterRelay),delete this.timerGatheringTimeoutAfterRelay,this.pc.close()},i.prototype.createDataChannel=function(){return u("createDataChannel()"),this.pc.createDataChannel.apply(this.pc,arguments)},i.prototype.createDTMFSender=function(e){return u("createDTMFSender()"),this.pc.createDTMFSender(e)},i.prototype.getStats=function(){return u("getStats()"),this.pc.getStats.apply(this.pc,arguments)},i.prototype.setIdentityProvider=function(){return u("setIdentityProvider()"),this.pc.setIdentityProvider.apply(this.pc,arguments)},i.prototype.getIdentityAssertion=function(){return u("getIdentityAssertion()"),this.pc.getIdentityAssertion()},i.prototype.reset=function(e){u("reset() | pcConfig: %o",e);var t=this.pc;t.onnegotiationneeded=null,t.onicecandidate=null,t.onaddstream=null,t.onremovestream=null,t.ondatachannel=null,t.onsignalingstatechange=null,t.oniceconnectionstatechange=null,t.onicegatheringstatechange=null,t.onidentityresult=null,t.onpeeridentity=null,t.onidpassertionerror=null,t.onidpvalidationerror=null,clearTimeout(this.timerGatheringTimeout),delete this.timerGatheringTimeout,clearTimeout(this.timerGatheringTimeoutAfterRelay),delete this.timerGatheringTimeoutAfterRelay,u("reset() | closing current peerConnection"),t.close(),o.call(this,e),s.call(this)}},{"./Adapter":1,debug:6,merge:9}],3:[function(e,t,n){"use strict";function i(e){var t=c(e||{});return l=!0,i.RTCPeerConnection=d,i.getUserMedia=t.getUserMedia,i.RTCSessionDescription=t.RTCSessionDescription,i.RTCIceCandidate=t.RTCIceCandidate,i.MediaStreamTrack=t.MediaStreamTrack,i.getMediaDevices=t.getMediaDevices,i.attachMediaStream=t.attachMediaStream,i.closeMediaStream=t.closeMediaStream,i.canRenegotiate=t.canRenegotiate,t.hasWebRTC()?(r("WebRTC supported"),!0):(a("WebRTC not supported"),!1)}t.exports=i;var o=e("bowser").browser,r=e("debug")("rtcninja"),a=e("debug")("rtcninja:ERROR"),s=e("./version"),c=e("./Adapter"),d=e("./RTCPeerConnection"),l=!1;a.log=console.warn.bind(console),r("version %s",s),r("detected browser: %s %s [mobile:%s, tablet:%s, android:%s, ios:%s]",o.name,o.version,!!o.mobile,!!o.tablet,!!o.android,!!o.ios),i.hasWebRTC=function(){return l||i(),c.hasWebRTC()},Object.defineProperty(i,"version",{get:function(){return s}}),Object.defineProperty(i,"called",{get:function(){return l}}),i.debug=e("debug"),i.browser=o},{"./Adapter":1,"./RTCPeerConnection":2,"./version":4,bowser:5,debug:6}],4:[function(e,t,n){"use strict";t.exports=e("../package.json").version},{"../package.json":10}],5:[function(t,n,i){!function(t,i){"undefined"!=typeof n&&n.exports?n.exports.browser=i():"function"==typeof e&&e.amd?e(i):this[t]=i()}("bowser",function(){function e(e){function n(t){var n=e.match(t);return n&&n.length>1&&n[1]||""}function i(t){var n=e.match(t);return n&&n.length>1&&n[2]||""}var o,r=n(/(ipod|iphone|ipad)/i).toLowerCase(),a=/like android/i.test(e),s=!a&&/android/i.test(e),c=n(/edge\/(\d+(\.\d+)?)/i),d=n(/version\/(\d+(\.\d+)?)/i),l=/tablet/i.test(e),u=!l&&/[^-]mobi/i.test(e);/opera|opr/i.test(e)?o={name:"Opera",opera:t,version:d||n(/(?:opera|opr)[\s\/](\d+(\.\d+)?)/i)}:/windows phone/i.test(e)?(o={name:"Windows Phone",windowsphone:t},c?(o.msedge=t,o.version=c):(o.msie=t,o.version=n(/iemobile\/(\d+(\.\d+)?)/i))):/msie|trident/i.test(e)?o={name:"Internet Explorer",msie:t,version:n(/(?:msie |rv:)(\d+(\.\d+)?)/i)}:/chrome.+? edge/i.test(e)?o={name:"Microsoft Edge",msedge:t,version:c}:/chrome|crios|crmo/i.test(e)?o={name:"Chrome",chrome:t,version:n(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i)}:r?(o={name:"iphone"==r?"iPhone":"ipad"==r?"iPad":"iPod"},d&&(o.version=d)):/sailfish/i.test(e)?o={name:"Sailfish",sailfish:t,version:n(/sailfish\s?browser\/(\d+(\.\d+)?)/i)}:/seamonkey\//i.test(e)?o={name:"SeaMonkey",seamonkey:t,version:n(/seamonkey\/(\d+(\.\d+)?)/i)}:/firefox|iceweasel/i.test(e)?(o={name:"Firefox",firefox:t,version:n(/(?:firefox|iceweasel)[ \/](\d+(\.\d+)?)/i)},/\((mobile|tablet);[^\)]*rv:[\d\.]+\)/i.test(e)&&(o.firefoxos=t)):/silk/i.test(e)?o={name:"Amazon Silk",silk:t,version:n(/silk\/(\d+(\.\d+)?)/i)}:s?o={name:"Android",version:d}:/phantom/i.test(e)?o={name:"PhantomJS",phantom:t,version:n(/phantomjs\/(\d+(\.\d+)?)/i)}:/blackberry|\bbb\d+/i.test(e)||/rim\stablet/i.test(e)?o={name:"BlackBerry",blackberry:t,version:d||n(/blackberry[\d]+\/(\d+(\.\d+)?)/i)}:/(web|hpw)os/i.test(e)?(o={name:"WebOS",webos:t,version:d||n(/w(?:eb)?osbrowser\/(\d+(\.\d+)?)/i)},/touchpad\//i.test(e)&&(o.touchpad=t)):o=/bada/i.test(e)?{name:"Bada",bada:t,version:n(/dolfin\/(\d+(\.\d+)?)/i)}:/tizen/i.test(e)?{name:"Tizen",tizen:t,version:n(/(?:tizen\s?)?browser\/(\d+(\.\d+)?)/i)||d}:/safari/i.test(e)?{name:"Safari",safari:t,version:d}:{name:n(/^(.*)\/(.*) /),version:i(/^(.*)\/(.*) /)},!o.msedge&&/(apple)?webkit/i.test(e)?(o.name=o.name||"Webkit",o.webkit=t,!o.version&&d&&(o.version=d)):!o.opera&&/gecko\//i.test(e)&&(o.name=o.name||"Gecko",o.gecko=t,o.version=o.version||n(/gecko\/(\d+(\.\d+)?)/i)),o.msedge||!s&&!o.silk?r&&(o[r]=t,o.ios=t):o.android=t;var p="";o.windowsphone?p=n(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i):r?(p=n(/os (\d+([_\s]\d+)*) like mac os x/i),p=p.replace(/[_\s]/g,".")):s?p=n(/android[ \/-](\d+(\.\d+)*)/i):o.webos?p=n(/(?:web|hpw)os\/(\d+(\.\d+)*)/i):o.blackberry?p=n(/rim\stablet\sos\s(\d+(\.\d+)*)/i):o.bada?p=n(/bada\/(\d+(\.\d+)*)/i):o.tizen&&(p=n(/tizen[\/\s](\d+(\.\d+)*)/i)),p&&(o.osversion=p);var g=p.split(".")[0];return l||"ipad"==r||s&&(3==g||4==g&&!u)||o.silk?o.tablet=t:(u||"iphone"==r||"ipod"==r||s||o.blackberry||o.webos||o.bada)&&(o.mobile=t),o.msedge||o.msie&&o.version>=10||o.chrome&&o.version>=20||o.firefox&&o.version>=20||o.safari&&o.version>=6||o.opera&&o.version>=10||o.ios&&o.osversion&&o.osversion.split(".")[0]>=6||o.blackberry&&o.version>=10.1?o.a=t:o.msie&&o.version<10||o.chrome&&o.version<20||o.firefox&&o.version<20||o.safari&&o.version<6||o.opera&&o.version<10||o.ios&&o.osversion&&o.osversion.split(".")[0]<6?o.c=t:o.x=t,o}var t=!0,n=e("undefined"!=typeof navigator?navigator.userAgent:"");return n.test=function(e){for(var t=0;t=31}function o(){var e=arguments,t=this.useColors;if(e[0]=(t?"%c":"")+this.namespace+(t?" %c":" ")+e[0]+(t?"%c ":" ")+"+"+n.humanize(this.diff),!t)return e;var i="color: "+this.color;e=[e[0],i,"color: inherit"].concat(Array.prototype.slice.call(e,1));var o=0,r=0;return e[0].replace(/%[a-z%]/g,function(e){"%%"!==e&&(o++,"%c"===e&&(r=o))}),e.splice(r,0,i),e}function r(){return"object"==typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function a(e){try{null==e?n.storage.removeItem("debug"):n.storage.debug=e}catch(t){}}function s(){var e;try{e=n.storage.debug}catch(t){}return e}function c(){try{return window.localStorage}catch(e){}}n=t.exports=e("./debug"),n.log=r,n.formatArgs=o,n.save=a,n.load=s,n.useColors=i,n.storage="undefined"!=typeof chrome&&"undefined"!=typeof chrome.storage?chrome.storage.local:c(),n.colors=["lightseagreen","forestgreen","goldenrod","dodgerblue","darkorchid","crimson"],n.formatters.j=function(e){return JSON.stringify(e)},n.enable(s())},{"./debug":7}],7:[function(e,t,n){function i(){return n.colors[l++%n.colors.length]}function o(e){function t(){}function o(){var e=o,t=+new Date,r=t-(d||t);e.diff=r,e.prev=d,e.curr=t,d=t,null==e.useColors&&(e.useColors=n.useColors()),null==e.color&&e.useColors&&(e.color=i());var a=Array.prototype.slice.call(arguments);a[0]=n.coerce(a[0]),"string"!=typeof a[0]&&(a=["%o"].concat(a));var s=0;a[0]=a[0].replace(/%([a-z%])/g,function(t,i){if("%%"===t)return t;s++;var o=n.formatters[i];if("function"==typeof o){var r=a[s];t=o.call(e,r),a.splice(s,1),s--}return t}),"function"==typeof n.formatArgs&&(a=n.formatArgs.apply(e,a));var c=o.log||n.log||console.log.bind(console);c.apply(e,a)}t.enabled=!1,o.enabled=!0;var r=n.enabled(e)?o:t;return r.namespace=e,r}function r(e){n.save(e);for(var t=(e||"").split(/[\s,]+/),i=t.length,o=0;i>o;o++)t[o]&&(e=t[o].replace(/\*/g,".*?"),"-"===e[0]?n.skips.push(new RegExp("^"+e.substr(1)+"$")):n.names.push(new RegExp("^"+e+"$")))}function a(){n.enable("")}function s(e){var t,i;for(t=0,i=n.skips.length;i>t;t++)if(n.skips[t].test(e))return!1;for(t=0,i=n.names.length;i>t;t++)if(n.names[t].test(e))return!0;return!1}function c(e){return e instanceof Error?e.stack||e.message:e}n=t.exports=o,n.coerce=c,n.disable=a,n.enable=r,n.enabled=s,n.humanize=e("ms"),n.names=[],n.skips=[],n.formatters={};var d,l=0},{ms:8}],8:[function(e,t,n){function i(e){if(e=""+e,!(e.length>1e4)){var t=/^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(e);if(t){var n=parseFloat(t[1]),i=(t[2]||"ms").toLowerCase();switch(i){case"years":case"year":case"yrs":case"yr":case"y":return n*u;case"days":case"day":case"d":return n*l;case"hours":case"hour":case"hrs":case"hr":case"h":return n*d;case"minutes":case"minute":case"mins":case"min":case"m":return n*c;case"seconds":case"second":case"secs":case"sec":case"s":return n*s;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return n}}}}function o(e){return e>=l?Math.round(e/l)+"d":e>=d?Math.round(e/d)+"h":e>=c?Math.round(e/c)+"m":e>=s?Math.round(e/s)+"s":e+"ms"}function r(e){return a(e,l,"day")||a(e,d,"hour")||a(e,c,"minute")||a(e,s,"second")||e+" ms"}function a(e,t,n){return t>e?void 0:1.5*t>e?Math.floor(e/t)+" "+n:Math.ceil(e/t)+" "+n+"s"}var s=1e3,c=60*s,d=60*c,l=24*d,u=365.25*l;t.exports=function(e,t){return t=t||{},"string"==typeof e?i(e):t["long"]?r(e):o(e)}},{}],9:[function(e,t,n){!function(e){function n(e,t){if("object"!==o(e))return t;for(var i in t)e[i]="object"===o(e[i])&&"object"===o(t[i])?n(e[i],t[i]):t[i];return e}function i(e,t,i){var a=i[0],s=i.length;(e||"object"!==o(a))&&(a={});for(var c=0;s>c;++c){var d=i[c],l=o(d);if("object"===l)for(var u in d){var p=e?r.clone(d[u]):d[u];a[u]=t?n(a[u],p):p}}return a}function o(e){return{}.toString.call(e).slice(8,-1).toLowerCase()}var r=function(e){return i(e===!0,!1,arguments)},a="merge";r.recursive=function(e){return i(e===!0,!0,arguments)},r.clone=function(e){var t,n,i=e,a=o(e);if("array"===a)for(i=[],n=e.length,t=0;n>t;++t)i[t]=r.clone(e[t]);else if("object"===a){i={};for(t in e)i[t]=r.clone(e[t])}return i},e?t.exports=r:window[a]=r}("object"==typeof t&&t&&"object"==typeof t.exports&&t.exports)},{}],10:[function(e,t,n){t.exports={name:"rtcninja",version:"0.6.2",description:"WebRTC API wrapper to deal with different browsers",author:"Iñaki Baz Castillo (http://eface2face.com)",contributors:["Jesús Pérez "],license:"MIT",main:"lib/rtcninja.js",homepage:"https://github.com/eface2face/rtcninja.js",repository:{type:"git",url:"https://github.com/eface2face/rtcninja.js.git"},keywords:["webrtc"],engines:{node:">=0.10.32"},dependencies:{bowser:"^0.7.3",debug:"^2.2.0",merge:"^1.2.0"},devDependencies:{browserify:"^10.2.3",gulp:"git+https://github.com/gulpjs/gulp.git#4.0","gulp-expect-file":"0.0.7","gulp-filelog":"^0.4.1","gulp-header":"^1.2.2","gulp-jscs":"^1.6.0","gulp-jscs-stylish":"^1.1.0","gulp-jshint":"^1.11.0","gulp-rename":"^1.2.2","gulp-uglify":"^1.2.0","jshint-stylish":"^1.0.2",retire:"^1.1.0",shelljs:"^0.5.0","vinyl-source-stream":"^1.1.0"}}},{}]},{},[3])(3)});rtcninja.js-0.6.2/dist/rtcninja.js000066400000000000000000001476501254426444300171000ustar00rootroot00000000000000/* * rtcninja.js v0.6.2 * WebRTC API wrapper to deal with different browsers * Copyright 2015 Iñaki Baz Castillo (http://eface2face.com) * License MIT */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.rtcninja = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 32) || (browser.android && browser.chrome && browserVersion >= 39) || (isDesktop && browser.opera && browserVersion >= 27) || (browser.android && browser.opera && browserVersion >= 24) || (browser.android && browser.webkit && !browser.chrome && browserVersion >= 37) || (virtNavigator.webkitGetUserMedia && virtGlobal.webkitRTCPeerConnection) ) { hasWebRTC = true; getUserMedia = virtNavigator.webkitGetUserMedia.bind(virtNavigator); RTCPeerConnection = virtGlobal.webkitRTCPeerConnection; RTCSessionDescription = virtGlobal.RTCSessionDescription; RTCIceCandidate = virtGlobal.RTCIceCandidate; MediaStreamTrack = virtGlobal.MediaStreamTrack; if (MediaStreamTrack && MediaStreamTrack.getSources) { getMediaDevices = MediaStreamTrack.getSources.bind(MediaStreamTrack); } else if (virtNavigator.getMediaDevices) { getMediaDevices = virtNavigator.getMediaDevices.bind(virtNavigator); } attachMediaStream = function (element, stream) { element.src = URL.createObjectURL(stream); return element; }; canRenegotiate = true; oldSpecRTCOfferOptions = false; // Firefox desktop, Firefox Android. } else if ( (isDesktop && browser.firefox && browserVersion >= 22) || (browser.android && browser.firefox && browserVersion >= 33) || (virtNavigator.mozGetUserMedia && virtGlobal.mozRTCPeerConnection) ) { hasWebRTC = true; getUserMedia = virtNavigator.mozGetUserMedia.bind(virtNavigator); RTCPeerConnection = virtGlobal.mozRTCPeerConnection; RTCSessionDescription = virtGlobal.mozRTCSessionDescription; RTCIceCandidate = virtGlobal.mozRTCIceCandidate; MediaStreamTrack = virtGlobal.MediaStreamTrack; attachMediaStream = function (element, stream) { element.src = URL.createObjectURL(stream); return element; }; canRenegotiate = false; oldSpecRTCOfferOptions = false; // WebRTC plugin required. For example IE or Safari with the Temasys plugin. } else if ( options.plugin && typeof options.plugin.isRequired === 'function' && options.plugin.isRequired() && typeof options.plugin.isInstalled === 'function' && options.plugin.isInstalled() ) { var pluginiface = options.plugin.interface; hasWebRTC = true; getUserMedia = pluginiface.getUserMedia; RTCPeerConnection = pluginiface.RTCPeerConnection; RTCSessionDescription = pluginiface.RTCSessionDescription; RTCIceCandidate = pluginiface.RTCIceCandidate; MediaStreamTrack = pluginiface.MediaStreamTrack; if (MediaStreamTrack && MediaStreamTrack.getSources) { getMediaDevices = MediaStreamTrack.getSources.bind(MediaStreamTrack); } else if (virtNavigator.getMediaDevices) { getMediaDevices = virtNavigator.getMediaDevices.bind(virtNavigator); } attachMediaStream = pluginiface.attachMediaStream; canRenegotiate = pluginiface.canRenegotiate; oldSpecRTCOfferOptions = true; // TODO: Update when fixed in the plugin. // Best effort (may be adater.js is loaded). } else if (virtNavigator.getUserMedia && virtGlobal.RTCPeerConnection) { hasWebRTC = true; getUserMedia = virtNavigator.getUserMedia.bind(virtNavigator); RTCPeerConnection = virtGlobal.RTCPeerConnection; RTCSessionDescription = virtGlobal.RTCSessionDescription; RTCIceCandidate = virtGlobal.RTCIceCandidate; MediaStreamTrack = virtGlobal.MediaStreamTrack; if (MediaStreamTrack && MediaStreamTrack.getSources) { getMediaDevices = MediaStreamTrack.getSources.bind(MediaStreamTrack); } else if (virtNavigator.getMediaDevices) { getMediaDevices = virtNavigator.getMediaDevices.bind(virtNavigator); } attachMediaStream = virtGlobal.attachMediaStream || function (element, stream) { element.src = URL.createObjectURL(stream); return element; }; canRenegotiate = false; oldSpecRTCOfferOptions = false; } function throwNonSupported(item) { return function () { throw new Error('rtcninja: WebRTC not supported, missing ' + item + ' [browser: ' + browser.name + ' ' + browser.version + ']'); }; } // Public API. // Expose a WebRTC checker. Adapter.hasWebRTC = function () { return hasWebRTC; }; // Expose getUserMedia. if (getUserMedia) { Adapter.getUserMedia = function (constraints, successCallback, errorCallback) { debug('getUserMedia() | constraints: %o', constraints); try { getUserMedia(constraints, function (stream) { debug('getUserMedia() | success'); if (successCallback) { successCallback(stream); } }, function (error) { debug('getUserMedia() | error:', error); if (errorCallback) { errorCallback(error); } } ); } catch (error) { debugerror('getUserMedia() | error:', error); if (errorCallback) { errorCallback(error); } } }; } else { Adapter.getUserMedia = function (constraints, successCallback, errorCallback) { debugerror('getUserMedia() | WebRTC not supported'); if (errorCallback) { errorCallback(new Error('rtcninja: WebRTC not supported, missing ' + 'getUserMedia [browser: ' + browser.name + ' ' + browser.version + ']')); } else { throwNonSupported('getUserMedia'); } }; } // Expose RTCPeerConnection. Adapter.RTCPeerConnection = RTCPeerConnection || throwNonSupported('RTCPeerConnection'); // Expose RTCSessionDescription. Adapter.RTCSessionDescription = RTCSessionDescription || throwNonSupported('RTCSessionDescription'); // Expose RTCIceCandidate. Adapter.RTCIceCandidate = RTCIceCandidate || throwNonSupported('RTCIceCandidate'); // Expose MediaStreamTrack. Adapter.MediaStreamTrack = MediaStreamTrack || throwNonSupported('MediaStreamTrack'); // Expose getMediaDevices. Adapter.getMediaDevices = getMediaDevices; // Expose MediaStreamTrack. Adapter.attachMediaStream = attachMediaStream || throwNonSupported('attachMediaStream'); // Expose canRenegotiate attribute. Adapter.canRenegotiate = canRenegotiate; // Expose closeMediaStream. Adapter.closeMediaStream = function (stream) { if (!stream) { return; } // Latest spec states that MediaStream has no stop() method and instead must // call stop() on every MediaStreamTrack. if (MediaStreamTrack && MediaStreamTrack.prototype && MediaStreamTrack.prototype.stop) { debug('closeMediaStream() | calling stop() on all the MediaStreamTrack'); var tracks, i, len; if (stream.getTracks) { tracks = stream.getTracks(); for (i = 0, len = tracks.length; i < len; i += 1) { tracks[i].stop(); } } else { tracks = stream.getAudioTracks(); for (i = 0, len = tracks.length; i < len; i += 1) { tracks[i].stop(); } tracks = stream.getVideoTracks(); for (i = 0, len = tracks.length; i < len; i += 1) { tracks[i].stop(); } } // Deprecated by the spec, but still in use. } else if (typeof stream.stop === 'function') { debug('closeMediaStream() | calling stop() on the MediaStream'); stream.stop(); } }; // Expose fixPeerConnectionConfig. Adapter.fixPeerConnectionConfig = function (pcConfig) { var i, len, iceServer, hasUrls, hasUrl; if (!Array.isArray(pcConfig.iceServers)) { pcConfig.iceServers = []; } for (i = 0, len = pcConfig.iceServers.length; i < len; i += 1) { iceServer = pcConfig.iceServers[i]; hasUrls = iceServer.hasOwnProperty('urls'); hasUrl = iceServer.hasOwnProperty('url'); if (typeof iceServer === 'object') { // Has .urls but not .url, so add .url with a single string value. if (hasUrls && !hasUrl) { iceServer.url = (Array.isArray(iceServer.urls) ? iceServer.urls[0] : iceServer.urls); // Has .url but not .urls, so add .urls with same value. } else if (!hasUrls && hasUrl) { iceServer.urls = (Array.isArray(iceServer.url) ? iceServer.url.slice() : iceServer.url); } // Ensure .url is a single string. if (hasUrl && Array.isArray(iceServer.url)) { iceServer.url = iceServer.url[0]; } } } }; // Expose fixRTCOfferOptions. Adapter.fixRTCOfferOptions = function (options) { options = options || {}; // New spec. if (!oldSpecRTCOfferOptions) { if (options.mandatory && options.mandatory.OfferToReceiveAudio) { options.offerToReceiveAudio = 1; } if (options.mandatory && options.mandatory.OfferToReceiveVideo) { options.offerToReceiveVideo = 1; } delete options.mandatory; // Old spec. } else { if (options.offerToReceiveAudio) { options.mandatory = options.mandatory || {}; options.mandatory.OfferToReceiveAudio = true; } if (options.offerToReceiveVideo) { options.mandatory = options.mandatory || {}; options.mandatory.OfferToReceiveVideo = true; } } }; return Adapter; } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"bowser":5,"debug":6}],2:[function(require,module,exports){ 'use strict'; // Expose the RTCPeerConnection class. module.exports = RTCPeerConnection; // Dependencies. var merge = require('merge'), debug = require('debug')('rtcninja:RTCPeerConnection'), debugerror = require('debug')('rtcninja:ERROR:RTCPeerConnection'), Adapter = require('./Adapter'), // Internal constants. C = { REGEXP_NORMALIZED_CANDIDATE: new RegExp(/^candidate:/i), REGEXP_FIX_CANDIDATE: new RegExp(/(^a=|\r|\n)/gi), REGEXP_RELAY_CANDIDATE: new RegExp(/ relay /i), REGEXP_SDP_CANDIDATES: new RegExp(/^a=candidate:.*\r\n/igm), REGEXP_SDP_NON_RELAY_CANDIDATES: new RegExp(/^a=candidate:(.(?!relay ))*\r\n/igm) }, // Internal variables. VAR = { normalizeCandidate: null }; debugerror.log = console.warn.bind(console); // Constructor function RTCPeerConnection(pcConfig, pcConstraints) { debug('new | pcConfig: %o', pcConfig); // Set this.pcConfig and this.options. setConfigurationAndOptions.call(this, pcConfig); // NOTE: Deprecated pcConstraints argument. this.pcConstraints = pcConstraints; // Own version of the localDescription. this.ourLocalDescription = null; // Latest values of PC attributes to avoid events with same value. this.ourSignalingState = null; this.ourIceConnectionState = null; this.ourIceGatheringState = null; // Timer for options.gatheringTimeout. this.timerGatheringTimeout = null; // Timer for options.gatheringTimeoutAfterRelay. this.timerGatheringTimeoutAfterRelay = null; // Flag to ignore new gathered ICE candidates. this.ignoreIceGathering = false; // Flag set when closed. this.closed = false; // Set RTCPeerConnection. setPeerConnection.call(this); // Set properties. setProperties.call(this); } // Public API. RTCPeerConnection.prototype.createOffer = function (successCallback, failureCallback, options) { debug('createOffer()'); var self = this; Adapter.fixRTCOfferOptions(options); this.pc.createOffer( function (offer) { if (isClosed.call(self)) { return; } debug('createOffer() | success'); if (successCallback) { successCallback(offer); } }, function (error) { if (isClosed.call(self)) { return; } debugerror('createOffer() | error:', error); if (failureCallback) { failureCallback(error); } }, options ); }; RTCPeerConnection.prototype.createAnswer = function (successCallback, failureCallback, options) { debug('createAnswer()'); var self = this; this.pc.createAnswer( function (answer) { if (isClosed.call(self)) { return; } debug('createAnswer() | success'); if (successCallback) { successCallback(answer); } }, function (error) { if (isClosed.call(self)) { return; } debugerror('createAnswer() | error:', error); if (failureCallback) { failureCallback(error); } }, options ); }; RTCPeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) { debug('setLocalDescription()'); var self = this; this.pc.setLocalDescription( description, // success. function () { if (isClosed.call(self)) { return; } debug('setLocalDescription() | success'); // Clear gathering timers. clearTimeout(self.timerGatheringTimeout); delete self.timerGatheringTimeout; clearTimeout(self.timerGatheringTimeoutAfterRelay); delete self.timerGatheringTimeoutAfterRelay; runTimerGatheringTimeout(); if (successCallback) { successCallback(); } }, // failure function (error) { if (isClosed.call(self)) { return; } debugerror('setLocalDescription() | error:', error); if (failureCallback) { failureCallback(error); } } ); // Enable (again) ICE gathering. this.ignoreIceGathering = false; // Handle gatheringTimeout. function runTimerGatheringTimeout() { if (typeof self.options.gatheringTimeout !== 'number') { return; } // If setLocalDescription was already called, it may happen that // ICE gathering is not needed, so don't run this timer. if (self.pc.iceGatheringState === 'complete') { return; } debug('setLocalDescription() | ending gathering in %d ms (gatheringTimeout option)', self.options.gatheringTimeout); self.timerGatheringTimeout = setTimeout(function () { if (isClosed.call(self)) { return; } debug('forced end of candidates after gatheringTimeout timeout'); // Clear gathering timers. delete self.timerGatheringTimeout; clearTimeout(self.timerGatheringTimeoutAfterRelay); delete self.timerGatheringTimeoutAfterRelay; // Ignore new candidates. self.ignoreIceGathering = true; if (self.onicecandidate) { self.onicecandidate({ candidate: null }, null); } }, self.options.gatheringTimeout); } }; RTCPeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) { debug('setRemoteDescription()'); var self = this; this.pc.setRemoteDescription( description, function () { if (isClosed.call(self)) { return; } debug('setRemoteDescription() | success'); if (successCallback) { successCallback(); } }, function (error) { if (isClosed.call(self)) { return; } debugerror('setRemoteDescription() | error:', error); if (failureCallback) { failureCallback(error); } } ); }; RTCPeerConnection.prototype.updateIce = function (pcConfig) { debug('updateIce() | pcConfig: %o', pcConfig); // Update this.pcConfig and this.options. setConfigurationAndOptions.call(this, pcConfig); this.pc.updateIce(this.pcConfig); // Enable (again) ICE gathering. this.ignoreIceGathering = false; }; RTCPeerConnection.prototype.addIceCandidate = function (candidate, successCallback, failureCallback) { debug('addIceCandidate() | candidate: %o', candidate); var self = this; this.pc.addIceCandidate( candidate, function () { if (isClosed.call(self)) { return; } debug('addIceCandidate() | success'); if (successCallback) { successCallback(); } }, function (error) { if (isClosed.call(self)) { return; } debugerror('addIceCandidate() | error:', error); if (failureCallback) { failureCallback(error); } } ); }; RTCPeerConnection.prototype.getConfiguration = function () { debug('getConfiguration()'); return this.pc.getConfiguration(); }; RTCPeerConnection.prototype.getLocalStreams = function () { debug('getLocalStreams()'); return this.pc.getLocalStreams(); }; RTCPeerConnection.prototype.getRemoteStreams = function () { debug('getRemoteStreams()'); return this.pc.getRemoteStreams(); }; RTCPeerConnection.prototype.getStreamById = function (streamId) { debug('getStreamById() | streamId: %s', streamId); return this.pc.getStreamById(streamId); }; RTCPeerConnection.prototype.addStream = function (stream) { debug('addStream() | stream: %s', stream); this.pc.addStream(stream); }; RTCPeerConnection.prototype.removeStream = function (stream) { debug('removeStream() | stream: %o', stream); this.pc.removeStream(stream); }; RTCPeerConnection.prototype.close = function () { debug('close()'); this.closed = true; // Clear gathering timers. clearTimeout(this.timerGatheringTimeout); delete this.timerGatheringTimeout; clearTimeout(this.timerGatheringTimeoutAfterRelay); delete this.timerGatheringTimeoutAfterRelay; this.pc.close(); }; RTCPeerConnection.prototype.createDataChannel = function () { debug('createDataChannel()'); return this.pc.createDataChannel.apply(this.pc, arguments); }; RTCPeerConnection.prototype.createDTMFSender = function (track) { debug('createDTMFSender()'); return this.pc.createDTMFSender(track); }; RTCPeerConnection.prototype.getStats = function () { debug('getStats()'); return this.pc.getStats.apply(this.pc, arguments); }; RTCPeerConnection.prototype.setIdentityProvider = function () { debug('setIdentityProvider()'); return this.pc.setIdentityProvider.apply(this.pc, arguments); }; RTCPeerConnection.prototype.getIdentityAssertion = function () { debug('getIdentityAssertion()'); return this.pc.getIdentityAssertion(); }; RTCPeerConnection.prototype.reset = function (pcConfig) { debug('reset() | pcConfig: %o', pcConfig); var pc = this.pc; // Remove events in the old PC. pc.onnegotiationneeded = null; pc.onicecandidate = null; pc.onaddstream = null; pc.onremovestream = null; pc.ondatachannel = null; pc.onsignalingstatechange = null; pc.oniceconnectionstatechange = null; pc.onicegatheringstatechange = null; pc.onidentityresult = null; pc.onpeeridentity = null; pc.onidpassertionerror = null; pc.onidpvalidationerror = null; // Clear gathering timers. clearTimeout(this.timerGatheringTimeout); delete this.timerGatheringTimeout; clearTimeout(this.timerGatheringTimeoutAfterRelay); delete this.timerGatheringTimeoutAfterRelay; // Silently close the old PC. debug('reset() | closing current peerConnection'); pc.close(); // Set this.pcConfig and this.options. setConfigurationAndOptions.call(this, pcConfig); // Create a new PC. setPeerConnection.call(this); }; // Private Helpers. function setConfigurationAndOptions(pcConfig) { // Clone pcConfig. this.pcConfig = merge(true, pcConfig); // Fix pcConfig. Adapter.fixPeerConnectionConfig(this.pcConfig); this.options = { iceTransportsRelay: (this.pcConfig.iceTransports === 'relay'), iceTransportsNone: (this.pcConfig.iceTransports === 'none'), gatheringTimeout: this.pcConfig.gatheringTimeout, gatheringTimeoutAfterRelay: this.pcConfig.gatheringTimeoutAfterRelay }; // Remove custom rtcninja.RTCPeerConnection options from pcConfig. delete this.pcConfig.gatheringTimeout; delete this.pcConfig.gatheringTimeoutAfterRelay; debug('setConfigurationAndOptions | processed pcConfig: %o', this.pcConfig); } function isClosed() { return ((this.closed) || (this.pc && this.pc.iceConnectionState === 'closed')); } function setEvents() { var self = this, pc = this.pc; pc.onnegotiationneeded = function (event) { if (isClosed.call(self)) { return; } debug('onnegotiationneeded()'); if (self.onnegotiationneeded) { self.onnegotiationneeded(event); } }; pc.onicecandidate = function (event) { var candidate, isRelay, newCandidate; if (isClosed.call(self)) { return; } if (self.ignoreIceGathering) { return; } // Ignore any candidate (event the null one) if iceTransports:'none' is set. if (self.options.iceTransportsNone) { return; } candidate = event.candidate; if (candidate) { isRelay = C.REGEXP_RELAY_CANDIDATE.test(candidate.candidate); // Ignore if just relay candidates are requested. if (self.options.iceTransportsRelay && !isRelay) { return; } // Handle gatheringTimeoutAfterRelay. if (isRelay && !self.timerGatheringTimeoutAfterRelay && (typeof self.options.gatheringTimeoutAfterRelay === 'number')) { debug('onicecandidate() | first relay candidate found, ending gathering in %d ms', self.options.gatheringTimeoutAfterRelay); self.timerGatheringTimeoutAfterRelay = setTimeout(function () { if (isClosed.call(self)) { return; } debug('forced end of candidates after timeout'); // Clear gathering timers. delete self.timerGatheringTimeoutAfterRelay; clearTimeout(self.timerGatheringTimeout); delete self.timerGatheringTimeout; // Ignore new candidates. self.ignoreIceGathering = true; if (self.onicecandidate) { self.onicecandidate({candidate: null}, null); } }, self.options.gatheringTimeoutAfterRelay); } newCandidate = new Adapter.RTCIceCandidate({ sdpMid: candidate.sdpMid, sdpMLineIndex: candidate.sdpMLineIndex, candidate: candidate.candidate }); // Force correct candidate syntax (just check it once). if (VAR.normalizeCandidate === null) { if (C.REGEXP_NORMALIZED_CANDIDATE.test(candidate.candidate)) { VAR.normalizeCandidate = false; } else { debug('onicecandidate() | normalizing ICE candidates syntax (remove "a=" and "\\r\\n")'); VAR.normalizeCandidate = true; } } if (VAR.normalizeCandidate) { newCandidate.candidate = candidate.candidate.replace(C.REGEXP_FIX_CANDIDATE, ''); } debug( 'onicecandidate() | m%d(%s) %s', newCandidate.sdpMLineIndex, newCandidate.sdpMid || 'no mid', newCandidate.candidate); if (self.onicecandidate) { self.onicecandidate(event, newCandidate); } // Null candidate (end of candidates). } else { debug('onicecandidate() | end of candidates'); // Clear gathering timers. clearTimeout(self.timerGatheringTimeout); delete self.timerGatheringTimeout; clearTimeout(self.timerGatheringTimeoutAfterRelay); delete self.timerGatheringTimeoutAfterRelay; if (self.onicecandidate) { self.onicecandidate(event, null); } } }; pc.onaddstream = function (event) { if (isClosed.call(self)) { return; } debug('onaddstream() | stream: %o', event.stream); if (self.onaddstream) { self.onaddstream(event, event.stream); } }; pc.onremovestream = function (event) { if (isClosed.call(self)) { return; } debug('onremovestream() | stream: %o', event.stream); if (self.onremovestream) { self.onremovestream(event, event.stream); } }; pc.ondatachannel = function (event) { if (isClosed.call(self)) { return; } debug('ondatachannel() | datachannel: %o', event.channel); if (self.ondatachannel) { self.ondatachannel(event, event.channel); } }; pc.onsignalingstatechange = function (event) { if (pc.signalingState === self.ourSignalingState) { return; } debug('onsignalingstatechange() | signalingState: %s', pc.signalingState); self.ourSignalingState = pc.signalingState; if (self.onsignalingstatechange) { self.onsignalingstatechange(event, pc.signalingState); } }; pc.oniceconnectionstatechange = function (event) { if (pc.iceConnectionState === self.ourIceConnectionState) { return; } debug('oniceconnectionstatechange() | iceConnectionState: %s', pc.iceConnectionState); self.ourIceConnectionState = pc.iceConnectionState; if (self.oniceconnectionstatechange) { self.oniceconnectionstatechange(event, pc.iceConnectionState); } }; pc.onicegatheringstatechange = function (event) { if (isClosed.call(self)) { return; } if (pc.iceGatheringState === self.ourIceGatheringState) { return; } debug('onicegatheringstatechange() | iceGatheringState: %s', pc.iceGatheringState); self.ourIceGatheringState = pc.iceGatheringState; if (self.onicegatheringstatechange) { self.onicegatheringstatechange(event, pc.iceGatheringState); } }; pc.onidentityresult = function (event) { if (isClosed.call(self)) { return; } debug('onidentityresult()'); if (self.onidentityresult) { self.onidentityresult(event); } }; pc.onpeeridentity = function (event) { if (isClosed.call(self)) { return; } debug('onpeeridentity()'); if (self.onpeeridentity) { self.onpeeridentity(event); } }; pc.onidpassertionerror = function (event) { if (isClosed.call(self)) { return; } debug('onidpassertionerror()'); if (self.onidpassertionerror) { self.onidpassertionerror(event); } }; pc.onidpvalidationerror = function (event) { if (isClosed.call(self)) { return; } debug('onidpvalidationerror()'); if (self.onidpvalidationerror) { self.onidpvalidationerror(event); } }; } function setPeerConnection() { // Create a RTCPeerConnection. if (!this.pcConstraints) { this.pc = new Adapter.RTCPeerConnection(this.pcConfig); } else { // NOTE: Deprecated. this.pc = new Adapter.RTCPeerConnection(this.pcConfig, this.pcConstraints); } // Set RTC events. setEvents.call(this); } function getLocalDescription() { var pc = this.pc, options = this.options, sdp = null; if (!pc.localDescription) { this.ourLocalDescription = null; return null; } // Mangle the SDP string. if (options.iceTransportsRelay) { sdp = pc.localDescription.sdp.replace(C.REGEXP_SDP_NON_RELAY_CANDIDATES, ''); } else if (options.iceTransportsNone) { sdp = pc.localDescription.sdp.replace(C.REGEXP_SDP_CANDIDATES, ''); } this.ourLocalDescription = new Adapter.RTCSessionDescription({ type: pc.localDescription.type, sdp: sdp || pc.localDescription.sdp }); return this.ourLocalDescription; } function setProperties() { var self = this; Object.defineProperties(this, { peerConnection: { get: function () { return self.pc; } }, signalingState: { get: function () { return self.pc.signalingState; } }, iceConnectionState: { get: function () { return self.pc.iceConnectionState; } }, iceGatheringState: { get: function () { return self.pc.iceGatheringState; } }, localDescription: { get: function () { return getLocalDescription.call(self); } }, remoteDescription: { get: function () { return self.pc.remoteDescription; } }, peerIdentity: { get: function () { return self.pc.peerIdentity; } } }); } },{"./Adapter":1,"debug":6,"merge":9}],3:[function(require,module,exports){ 'use strict'; module.exports = rtcninja; // Dependencies. var browser = require('bowser').browser, debug = require('debug')('rtcninja'), debugerror = require('debug')('rtcninja:ERROR'), version = require('./version'), Adapter = require('./Adapter'), RTCPeerConnection = require('./RTCPeerConnection'), // Internal vars. called = false; debugerror.log = console.warn.bind(console); debug('version %s', version); debug('detected browser: %s %s [mobile:%s, tablet:%s, android:%s, ios:%s]', browser.name, browser.version, !!browser.mobile, !!browser.tablet, !!browser.android, !!browser.ios); // Constructor. function rtcninja(options) { // Load adapter var iface = Adapter(options || {}); // jshint ignore:line called = true; // Expose RTCPeerConnection class. rtcninja.RTCPeerConnection = RTCPeerConnection; // Expose WebRTC API and utils. rtcninja.getUserMedia = iface.getUserMedia; rtcninja.RTCSessionDescription = iface.RTCSessionDescription; rtcninja.RTCIceCandidate = iface.RTCIceCandidate; rtcninja.MediaStreamTrack = iface.MediaStreamTrack; rtcninja.getMediaDevices = iface.getMediaDevices; rtcninja.attachMediaStream = iface.attachMediaStream; rtcninja.closeMediaStream = iface.closeMediaStream; rtcninja.canRenegotiate = iface.canRenegotiate; // Log WebRTC support. if (iface.hasWebRTC()) { debug('WebRTC supported'); return true; } else { debugerror('WebRTC not supported'); return false; } } // Public API. // If called without calling rtcninja(), call it. rtcninja.hasWebRTC = function () { if (!called) { rtcninja(); } return Adapter.hasWebRTC(); }; // Expose version property. Object.defineProperty(rtcninja, 'version', { get: function () { return version; } }); // Expose called property. Object.defineProperty(rtcninja, 'called', { get: function () { return called; } }); // Exposing stuff. rtcninja.debug = require('debug'); rtcninja.browser = browser; },{"./Adapter":1,"./RTCPeerConnection":2,"./version":4,"bowser":5,"debug":6}],4:[function(require,module,exports){ 'use strict'; // Expose the 'version' field of package.json. module.exports = require('../package.json').version; },{"../package.json":10}],5:[function(require,module,exports){ /*! * Bowser - a browser detector * https://github.com/ded/bowser * MIT License | (c) Dustin Diaz 2014 */ !function (name, definition) { if (typeof module != 'undefined' && module.exports) module.exports['browser'] = definition() else if (typeof define == 'function' && define.amd) define(definition) else this[name] = definition() }('bowser', function () { /** * See useragents.js for examples of navigator.userAgent */ var t = true function detect(ua) { function getFirstMatch(regex) { var match = ua.match(regex); return (match && match.length > 1 && match[1]) || ''; } function getSecondMatch(regex) { var match = ua.match(regex); return (match && match.length > 1 && match[2]) || ''; } var iosdevice = getFirstMatch(/(ipod|iphone|ipad)/i).toLowerCase() , likeAndroid = /like android/i.test(ua) , android = !likeAndroid && /android/i.test(ua) , edgeVersion = getFirstMatch(/edge\/(\d+(\.\d+)?)/i) , versionIdentifier = getFirstMatch(/version\/(\d+(\.\d+)?)/i) , tablet = /tablet/i.test(ua) , mobile = !tablet && /[^-]mobi/i.test(ua) , result if (/opera|opr/i.test(ua)) { result = { name: 'Opera' , opera: t , version: versionIdentifier || getFirstMatch(/(?:opera|opr)[\s\/](\d+(\.\d+)?)/i) } } else if (/windows phone/i.test(ua)) { result = { name: 'Windows Phone' , windowsphone: t } if (edgeVersion) { result.msedge = t result.version = edgeVersion } else { result.msie = t result.version = getFirstMatch(/iemobile\/(\d+(\.\d+)?)/i) } } else if (/msie|trident/i.test(ua)) { result = { name: 'Internet Explorer' , msie: t , version: getFirstMatch(/(?:msie |rv:)(\d+(\.\d+)?)/i) } } else if (/chrome.+? edge/i.test(ua)) { result = { name: 'Microsoft Edge' , msedge: t , version: edgeVersion } } else if (/chrome|crios|crmo/i.test(ua)) { result = { name: 'Chrome' , chrome: t , version: getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i) } } else if (iosdevice) { result = { name : iosdevice == 'iphone' ? 'iPhone' : iosdevice == 'ipad' ? 'iPad' : 'iPod' } // WTF: version is not part of user agent in web apps if (versionIdentifier) { result.version = versionIdentifier } } else if (/sailfish/i.test(ua)) { result = { name: 'Sailfish' , sailfish: t , version: getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i) } } else if (/seamonkey\//i.test(ua)) { result = { name: 'SeaMonkey' , seamonkey: t , version: getFirstMatch(/seamonkey\/(\d+(\.\d+)?)/i) } } else if (/firefox|iceweasel/i.test(ua)) { result = { name: 'Firefox' , firefox: t , version: getFirstMatch(/(?:firefox|iceweasel)[ \/](\d+(\.\d+)?)/i) } if (/\((mobile|tablet);[^\)]*rv:[\d\.]+\)/i.test(ua)) { result.firefoxos = t } } else if (/silk/i.test(ua)) { result = { name: 'Amazon Silk' , silk: t , version : getFirstMatch(/silk\/(\d+(\.\d+)?)/i) } } else if (android) { result = { name: 'Android' , version: versionIdentifier } } else if (/phantom/i.test(ua)) { result = { name: 'PhantomJS' , phantom: t , version: getFirstMatch(/phantomjs\/(\d+(\.\d+)?)/i) } } else if (/blackberry|\bbb\d+/i.test(ua) || /rim\stablet/i.test(ua)) { result = { name: 'BlackBerry' , blackberry: t , version: versionIdentifier || getFirstMatch(/blackberry[\d]+\/(\d+(\.\d+)?)/i) } } else if (/(web|hpw)os/i.test(ua)) { result = { name: 'WebOS' , webos: t , version: versionIdentifier || getFirstMatch(/w(?:eb)?osbrowser\/(\d+(\.\d+)?)/i) }; /touchpad\//i.test(ua) && (result.touchpad = t) } else if (/bada/i.test(ua)) { result = { name: 'Bada' , bada: t , version: getFirstMatch(/dolfin\/(\d+(\.\d+)?)/i) }; } else if (/tizen/i.test(ua)) { result = { name: 'Tizen' , tizen: t , version: getFirstMatch(/(?:tizen\s?)?browser\/(\d+(\.\d+)?)/i) || versionIdentifier }; } else if (/safari/i.test(ua)) { result = { name: 'Safari' , safari: t , version: versionIdentifier } } else { result = { name: getFirstMatch(/^(.*)\/(.*) /), version: getSecondMatch(/^(.*)\/(.*) /) }; } // set webkit or gecko flag for browsers based on these engines if (!result.msedge && /(apple)?webkit/i.test(ua)) { result.name = result.name || "Webkit" result.webkit = t if (!result.version && versionIdentifier) { result.version = versionIdentifier } } else if (!result.opera && /gecko\//i.test(ua)) { result.name = result.name || "Gecko" result.gecko = t result.version = result.version || getFirstMatch(/gecko\/(\d+(\.\d+)?)/i) } // set OS flags for platforms that have multiple browsers if (!result.msedge && (android || result.silk)) { result.android = t } else if (iosdevice) { result[iosdevice] = t result.ios = t } // OS version extraction var osVersion = ''; if (result.windowsphone) { osVersion = getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i); } else if (iosdevice) { osVersion = getFirstMatch(/os (\d+([_\s]\d+)*) like mac os x/i); osVersion = osVersion.replace(/[_\s]/g, '.'); } else if (android) { osVersion = getFirstMatch(/android[ \/-](\d+(\.\d+)*)/i); } else if (result.webos) { osVersion = getFirstMatch(/(?:web|hpw)os\/(\d+(\.\d+)*)/i); } else if (result.blackberry) { osVersion = getFirstMatch(/rim\stablet\sos\s(\d+(\.\d+)*)/i); } else if (result.bada) { osVersion = getFirstMatch(/bada\/(\d+(\.\d+)*)/i); } else if (result.tizen) { osVersion = getFirstMatch(/tizen[\/\s](\d+(\.\d+)*)/i); } if (osVersion) { result.osversion = osVersion; } // device type extraction var osMajorVersion = osVersion.split('.')[0]; if (tablet || iosdevice == 'ipad' || (android && (osMajorVersion == 3 || (osMajorVersion == 4 && !mobile))) || result.silk) { result.tablet = t } else if (mobile || iosdevice == 'iphone' || iosdevice == 'ipod' || android || result.blackberry || result.webos || result.bada) { result.mobile = t } // Graded Browser Support // http://developer.yahoo.com/yui/articles/gbs if (result.msedge || (result.msie && result.version >= 10) || (result.chrome && result.version >= 20) || (result.firefox && result.version >= 20.0) || (result.safari && result.version >= 6) || (result.opera && result.version >= 10.0) || (result.ios && result.osversion && result.osversion.split(".")[0] >= 6) || (result.blackberry && result.version >= 10.1) ) { result.a = t; } else if ((result.msie && result.version < 10) || (result.chrome && result.version < 20) || (result.firefox && result.version < 20.0) || (result.safari && result.version < 6) || (result.opera && result.version < 10.0) || (result.ios && result.osversion && result.osversion.split(".")[0] < 6) ) { result.c = t } else result.x = t return result } var bowser = detect(typeof navigator !== 'undefined' ? navigator.userAgent : '') bowser.test = function (browserList) { for (var i = 0; i < browserList.length; ++i) { var browserItem = browserList[i]; if (typeof browserItem=== 'string') { if (browserItem in bowser) { return true; } } } return false; } /* * Set our detect method to the main bowser object so we can * reuse it to test other user agents. * This is needed to implement future tests. */ bowser._detect = detect; return bowser }); },{}],6:[function(require,module,exports){ /** * This is the web browser implementation of `debug()`. * * Expose `debug()` as the module. */ exports = module.exports = require('./debug'); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; exports.load = load; exports.useColors = useColors; exports.storage = 'undefined' != typeof chrome && 'undefined' != typeof chrome.storage ? chrome.storage.local : localstorage(); /** * Colors. */ exports.colors = [ 'lightseagreen', 'forestgreen', 'goldenrod', 'dodgerblue', 'darkorchid', 'crimson' ]; /** * Currently only WebKit-based Web Inspectors, Firefox >= v31, * and the Firebug extension (any Firefox version) are known * to support "%c" CSS customizations. * * TODO: add a `localStorage` variable to explicitly enable/disable colors */ function useColors() { // is webkit? http://stackoverflow.com/a/16459606/376773 return ('WebkitAppearance' in document.documentElement.style) || // is firebug? http://stackoverflow.com/a/398120/376773 (window.console && (console.firebug || (console.exception && console.table))) || // is firefox >= v31? // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31); } /** * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. */ exports.formatters.j = function(v) { return JSON.stringify(v); }; /** * Colorize log arguments if enabled. * * @api public */ function formatArgs() { var args = arguments; var useColors = this.useColors; args[0] = (useColors ? '%c' : '') + this.namespace + (useColors ? ' %c' : ' ') + args[0] + (useColors ? '%c ' : ' ') + '+' + exports.humanize(this.diff); if (!useColors) return args; var c = 'color: ' + this.color; args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1)); // the final "%c" is somewhat tricky, because there could be other // arguments passed either before or after the %c, so we need to // figure out the correct index to insert the CSS into var index = 0; var lastC = 0; args[0].replace(/%[a-z%]/g, function(match) { if ('%%' === match) return; index++; if ('%c' === match) { // we only are interested in the *last* %c // (the user may have provided their own) lastC = index; } }); args.splice(lastC, 0, c); return args; } /** * Invokes `console.log()` when available. * No-op when `console.log` is not a "function". * * @api public */ function log() { // this hackery is required for IE8/9, where // the `console.log` function doesn't have 'apply' return 'object' === typeof console && console.log && Function.prototype.apply.call(console.log, console, arguments); } /** * Save `namespaces`. * * @param {String} namespaces * @api private */ function save(namespaces) { try { if (null == namespaces) { exports.storage.removeItem('debug'); } else { exports.storage.debug = namespaces; } } catch(e) {} } /** * Load `namespaces`. * * @return {String} returns the previously persisted debug modes * @api private */ function load() { var r; try { r = exports.storage.debug; } catch(e) {} return r; } /** * Enable namespaces listed in `localStorage.debug` initially. */ exports.enable(load()); /** * Localstorage attempts to return the localstorage. * * This is necessary because safari throws * when a user disables cookies/localstorage * and you attempt to access it. * * @return {LocalStorage} * @api private */ function localstorage(){ try { return window.localStorage; } catch (e) {} } },{"./debug":7}],7:[function(require,module,exports){ /** * This is the common logic for both the Node.js and web browser * implementations of `debug()`. * * Expose `debug()` as the module. */ exports = module.exports = debug; exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; exports.humanize = require('ms'); /** * The currently active debug mode names, and names to skip. */ exports.names = []; exports.skips = []; /** * Map of special "%n" handling functions, for the debug "format" argument. * * Valid key names are a single, lowercased letter, i.e. "n". */ exports.formatters = {}; /** * Previously assigned color. */ var prevColor = 0; /** * Previous log timestamp. */ var prevTime; /** * Select a color. * * @return {Number} * @api private */ function selectColor() { return exports.colors[prevColor++ % exports.colors.length]; } /** * Create a debugger with the given `namespace`. * * @param {String} namespace * @return {Function} * @api public */ function debug(namespace) { // define the `disabled` version function disabled() { } disabled.enabled = false; // define the `enabled` version function enabled() { var self = enabled; // set `diff` timestamp var curr = +new Date(); var ms = curr - (prevTime || curr); self.diff = ms; self.prev = prevTime; self.curr = curr; prevTime = curr; // add the `color` if not set if (null == self.useColors) self.useColors = exports.useColors(); if (null == self.color && self.useColors) self.color = selectColor(); var args = Array.prototype.slice.call(arguments); args[0] = exports.coerce(args[0]); if ('string' !== typeof args[0]) { // anything else let's inspect with %o args = ['%o'].concat(args); } // apply any `formatters` transformations var index = 0; args[0] = args[0].replace(/%([a-z%])/g, function(match, format) { // if we encounter an escaped % then don't increase the array index if (match === '%%') return match; index++; var formatter = exports.formatters[format]; if ('function' === typeof formatter) { var val = args[index]; match = formatter.call(self, val); // now we need to remove `args[index]` since it's inlined in the `format` args.splice(index, 1); index--; } return match; }); if ('function' === typeof exports.formatArgs) { args = exports.formatArgs.apply(self, args); } var logFn = enabled.log || exports.log || console.log.bind(console); logFn.apply(self, args); } enabled.enabled = true; var fn = exports.enabled(namespace) ? enabled : disabled; fn.namespace = namespace; return fn; } /** * Enables a debug mode by namespaces. This can include modes * separated by a colon and wildcards. * * @param {String} namespaces * @api public */ function enable(namespaces) { exports.save(namespaces); var split = (namespaces || '').split(/[\s,]+/); var len = split.length; for (var i = 0; i < len; i++) { if (!split[i]) continue; // ignore empty strings namespaces = split[i].replace(/\*/g, '.*?'); if (namespaces[0] === '-') { exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); } else { exports.names.push(new RegExp('^' + namespaces + '$')); } } } /** * Disable debug output. * * @api public */ function disable() { exports.enable(''); } /** * Returns true if the given mode name is enabled, false otherwise. * * @param {String} name * @return {Boolean} * @api public */ function enabled(name) { var i, len; for (i = 0, len = exports.skips.length; i < len; i++) { if (exports.skips[i].test(name)) { return false; } } for (i = 0, len = exports.names.length; i < len; i++) { if (exports.names[i].test(name)) { return true; } } return false; } /** * Coerce `val`. * * @param {Mixed} val * @return {Mixed} * @api private */ function coerce(val) { if (val instanceof Error) return val.stack || val.message; return val; } },{"ms":8}],8:[function(require,module,exports){ /** * Helpers. */ var s = 1000; var m = s * 60; var h = m * 60; var d = h * 24; var y = d * 365.25; /** * Parse or format the given `val`. * * Options: * * - `long` verbose formatting [false] * * @param {String|Number} val * @param {Object} options * @return {String|Number} * @api public */ module.exports = function(val, options){ options = options || {}; if ('string' == typeof val) return parse(val); return options.long ? long(val) : short(val); }; /** * Parse the given `str` and return milliseconds. * * @param {String} str * @return {Number} * @api private */ function parse(str) { str = '' + str; if (str.length > 10000) return; var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str); if (!match) return; var n = parseFloat(match[1]); var type = (match[2] || 'ms').toLowerCase(); switch (type) { case 'years': case 'year': case 'yrs': case 'yr': case 'y': return n * y; case 'days': case 'day': case 'd': return n * d; case 'hours': case 'hour': case 'hrs': case 'hr': case 'h': return n * h; case 'minutes': case 'minute': case 'mins': case 'min': case 'm': return n * m; case 'seconds': case 'second': case 'secs': case 'sec': case 's': return n * s; case 'milliseconds': case 'millisecond': case 'msecs': case 'msec': case 'ms': return n; } } /** * Short format for `ms`. * * @param {Number} ms * @return {String} * @api private */ function short(ms) { if (ms >= d) return Math.round(ms / d) + 'd'; if (ms >= h) return Math.round(ms / h) + 'h'; if (ms >= m) return Math.round(ms / m) + 'm'; if (ms >= s) return Math.round(ms / s) + 's'; return ms + 'ms'; } /** * Long format for `ms`. * * @param {Number} ms * @return {String} * @api private */ function long(ms) { return plural(ms, d, 'day') || plural(ms, h, 'hour') || plural(ms, m, 'minute') || plural(ms, s, 'second') || ms + ' ms'; } /** * Pluralization helper. */ function plural(ms, n, name) { if (ms < n) return; if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; return Math.ceil(ms / n) + ' ' + name + 's'; } },{}],9:[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 (http://eface2face.com)", "contributors": [ "Jesús Pérez " ], "license": "MIT", "main": "lib/rtcninja.js", "homepage": "https://github.com/eface2face/rtcninja.js", "repository": { "type": "git", "url": "https://github.com/eface2face/rtcninja.js.git" }, "keywords": [ "webrtc" ], "engines": { "node": ">=0.10.32" }, "dependencies": { "bowser": "^0.7.3", "debug": "^2.2.0", "merge": "^1.2.0" }, "devDependencies": { "browserify": "^10.2.3", "gulp": "git+https://github.com/gulpjs/gulp.git#4.0", "gulp-expect-file": "0.0.7", "gulp-filelog": "^0.4.1", "gulp-header": "^1.2.2", "gulp-jscs": "^1.6.0", "gulp-jscs-stylish": "^1.1.0", "gulp-jshint": "^1.11.0", "gulp-rename": "^1.2.2", "gulp-uglify": "^1.2.0", "jshint-stylish": "^1.0.2", "retire": "^1.1.0", "shelljs": "^0.5.0", "vinyl-source-stream": "^1.1.0" } } },{}]},{},[3])(3) });rtcninja.js-0.6.2/dist/rtcninja.min.js000066400000000000000000000613541254426444300176560ustar00rootroot00000000000000/* * rtcninja.js v0.6.2 * WebRTC API wrapper to deal with different browsers * Copyright 2015 Iñaki Baz Castillo (http://eface2face.com) * License MIT */ !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.rtcninja=e()}}(function(){var e;return function t(e,n,i){function o(a,s){if(!n[a]){if(!e[a]){var c="function"==typeof require&&require;if(!s&&c)return c(a,!0);if(r)return r(a,!0);var d=new Error("Cannot find module '"+a+"'");throw d.code="MODULE_NOT_FOUND",d}var l=n[a]={exports:{}};e[a][0].call(l.exports,function(t){var n=e[a][1][t];return o(n?n:t)},l,l.exports,t,e,n,i)}return n[a].exports}for(var r="function"==typeof require&&require,a=0;a=32||a.android&&a.chrome&&v>=39||b&&a.opera&&v>=27||a.android&&a.opera&&v>=24||a.android&&a.webkit&&!a.chrome&&v>=37||r.webkitGetUserMedia&&o.webkitRTCPeerConnection)C=!0,d=r.webkitGetUserMedia.bind(r),l=o.webkitRTCPeerConnection,u=o.RTCSessionDescription,p=o.RTCIceCandidate,g=o.MediaStreamTrack,g&&g.getSources?f=g.getSources.bind(g):r.getMediaDevices&&(f=r.getMediaDevices.bind(r)),m=function(e,t){return e.src=URL.createObjectURL(t),e},h=!0,y=!1;else if(b&&a.firefox&&v>=22||a.android&&a.firefox&&v>=33||r.mozGetUserMedia&&o.mozRTCPeerConnection)C=!0,d=r.mozGetUserMedia.bind(r),l=o.mozRTCPeerConnection,u=o.mozRTCSessionDescription,p=o.mozRTCIceCandidate,g=o.MediaStreamTrack,m=function(e,t){return e.src=URL.createObjectURL(t),e},h=!1,y=!1;else if(e.plugin&&"function"==typeof e.plugin.isRequired&&e.plugin.isRequired()&&"function"==typeof e.plugin.isInstalled&&e.plugin.isInstalled()){var n=e.plugin["interface"];C=!0,d=n.getUserMedia,l=n.RTCPeerConnection,u=n.RTCSessionDescription,p=n.RTCIceCandidate,g=n.MediaStreamTrack,g&&g.getSources?f=g.getSources.bind(g):r.getMediaDevices&&(f=r.getMediaDevices.bind(r)),m=n.attachMediaStream,h=n.canRenegotiate,y=!0}else r.getUserMedia&&o.RTCPeerConnection&&(C=!0,d=r.getUserMedia.bind(r),l=o.RTCPeerConnection,u=o.RTCSessionDescription,p=o.RTCIceCandidate,g=o.MediaStreamTrack,g&&g.getSources?f=g.getSources.bind(g):r.getMediaDevices&&(f=r.getMediaDevices.bind(r)),m=o.attachMediaStream||function(e,t){return e.src=URL.createObjectURL(t),e},h=!1,y=!1);return i.hasWebRTC=function(){return C},i.getUserMedia=d?function(e,t,n){s("getUserMedia() | constraints: %o",e);try{d(e,function(e){s("getUserMedia() | success"),t&&t(e)},function(e){s("getUserMedia() | error:",e),n&&n(e)})}catch(i){c("getUserMedia() | error:",i),n&&n(i)}}:function(e,n,i){c("getUserMedia() | WebRTC not supported"),i?i(new Error("rtcninja: WebRTC not supported, missing getUserMedia [browser: "+a.name+" "+a.version+"]")):t("getUserMedia")},i.RTCPeerConnection=l||t("RTCPeerConnection"),i.RTCSessionDescription=u||t("RTCSessionDescription"),i.RTCIceCandidate=p||t("RTCIceCandidate"),i.MediaStreamTrack=g||t("MediaStreamTrack"),i.getMediaDevices=f,i.attachMediaStream=m||t("attachMediaStream"),i.canRenegotiate=h,i.closeMediaStream=function(e){if(e)if(g&&g.prototype&&g.prototype.stop){s("closeMediaStream() | calling stop() on all the MediaStreamTrack");var t,n,i;if(e.getTracks)for(t=e.getTracks(),n=0,i=t.length;i>n;n+=1)t[n].stop();else{for(t=e.getAudioTracks(),n=0,i=t.length;i>n;n+=1)t[n].stop();for(t=e.getVideoTracks(),n=0,i=t.length;i>n;n+=1)t[n].stop()}}else"function"==typeof e.stop&&(s("closeMediaStream() | calling stop() on the MediaStream"),e.stop())},i.fixPeerConnectionConfig=function(e){var t,n,i,o,r;for(Array.isArray(e.iceServers)||(e.iceServers=[]),t=0,n=e.iceServers.length;n>t;t+=1)i=e.iceServers[t],o=i.hasOwnProperty("urls"),r=i.hasOwnProperty("url"),"object"==typeof i&&(o&&!r?i.url=Array.isArray(i.urls)?i.urls[0]:i.urls:!o&&r&&(i.urls=Array.isArray(i.url)?i.url.slice():i.url),r&&Array.isArray(i.url)&&(i.url=i.url[0]))},i.fixRTCOfferOptions=function(e){e=e||{},y?(e.offerToReceiveAudio&&(e.mandatory=e.mandatory||{},e.mandatory.OfferToReceiveAudio=!0),e.offerToReceiveVideo&&(e.mandatory=e.mandatory||{},e.mandatory.OfferToReceiveVideo=!0)):(e.mandatory&&e.mandatory.OfferToReceiveAudio&&(e.offerToReceiveAudio=1),e.mandatory&&e.mandatory.OfferToReceiveVideo&&(e.offerToReceiveVideo=1),delete e.mandatory)},i}t.exports=i;var o,r,a=e("bowser").browser,s=e("debug")("rtcninja:Adapter"),c=e("debug")("rtcninja:ERROR:Adapter"),d=null,l=null,u=null,p=null,g=null,f=null,m=null,h=!1,y=!1,v=Number(a.version)||0,b=!(a.mobile&&a.tablet),C=!1;c.log=console.warn.bind(console),o=n.window||n,r=o.navigator||{}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{bowser:5,debug:6}],2:[function(e,t,n){"use strict";function i(e,t){u("new | pcConfig: %o",e),o.call(this,e),this.pcConstraints=t,this.ourLocalDescription=null,this.ourSignalingState=null,this.ourIceConnectionState=null,this.ourIceGatheringState=null,this.timerGatheringTimeout=null,this.timerGatheringTimeoutAfterRelay=null,this.ignoreIceGathering=!1,this.closed=!1,s.call(this),d.call(this)}function o(e){this.pcConfig=l(!0,e),g.fixPeerConnectionConfig(this.pcConfig),this.options={iceTransportsRelay:"relay"===this.pcConfig.iceTransports,iceTransportsNone:"none"===this.pcConfig.iceTransports,gatheringTimeout:this.pcConfig.gatheringTimeout,gatheringTimeoutAfterRelay:this.pcConfig.gatheringTimeoutAfterRelay},delete this.pcConfig.gatheringTimeout,delete this.pcConfig.gatheringTimeoutAfterRelay,u("setConfigurationAndOptions | processed pcConfig: %o",this.pcConfig)}function r(){return this.closed||this.pc&&"closed"===this.pc.iceConnectionState}function a(){var e=this,t=this.pc;t.onnegotiationneeded=function(t){r.call(e)||(u("onnegotiationneeded()"),e.onnegotiationneeded&&e.onnegotiationneeded(t))},t.onicecandidate=function(t){var n,i,o;if(!r.call(e)&&!e.ignoreIceGathering&&!e.options.iceTransportsNone)if(n=t.candidate){if(i=f.REGEXP_RELAY_CANDIDATE.test(n.candidate),e.options.iceTransportsRelay&&!i)return;i&&!e.timerGatheringTimeoutAfterRelay&&"number"==typeof e.options.gatheringTimeoutAfterRelay&&(u("onicecandidate() | first relay candidate found, ending gathering in %d ms",e.options.gatheringTimeoutAfterRelay),e.timerGatheringTimeoutAfterRelay=setTimeout(function(){r.call(e)||(u("forced end of candidates after timeout"),delete e.timerGatheringTimeoutAfterRelay,clearTimeout(e.timerGatheringTimeout),delete e.timerGatheringTimeout,e.ignoreIceGathering=!0,e.onicecandidate&&e.onicecandidate({candidate:null},null))},e.options.gatheringTimeoutAfterRelay)),o=new g.RTCIceCandidate({sdpMid:n.sdpMid,sdpMLineIndex:n.sdpMLineIndex,candidate:n.candidate}),null===m.normalizeCandidate&&(f.REGEXP_NORMALIZED_CANDIDATE.test(n.candidate)?m.normalizeCandidate=!1:(u('onicecandidate() | normalizing ICE candidates syntax (remove "a=" and "\\r\\n")'),m.normalizeCandidate=!0)),m.normalizeCandidate&&(o.candidate=n.candidate.replace(f.REGEXP_FIX_CANDIDATE,"")),u("onicecandidate() | m%d(%s) %s",o.sdpMLineIndex,o.sdpMid||"no mid",o.candidate),e.onicecandidate&&e.onicecandidate(t,o)}else u("onicecandidate() | end of candidates"),clearTimeout(e.timerGatheringTimeout),delete e.timerGatheringTimeout,clearTimeout(e.timerGatheringTimeoutAfterRelay),delete e.timerGatheringTimeoutAfterRelay,e.onicecandidate&&e.onicecandidate(t,null)},t.onaddstream=function(t){r.call(e)||(u("onaddstream() | stream: %o",t.stream),e.onaddstream&&e.onaddstream(t,t.stream))},t.onremovestream=function(t){r.call(e)||(u("onremovestream() | stream: %o",t.stream),e.onremovestream&&e.onremovestream(t,t.stream))},t.ondatachannel=function(t){r.call(e)||(u("ondatachannel() | datachannel: %o",t.channel),e.ondatachannel&&e.ondatachannel(t,t.channel))},t.onsignalingstatechange=function(n){t.signalingState!==e.ourSignalingState&&(u("onsignalingstatechange() | signalingState: %s",t.signalingState),e.ourSignalingState=t.signalingState,e.onsignalingstatechange&&e.onsignalingstatechange(n,t.signalingState))},t.oniceconnectionstatechange=function(n){t.iceConnectionState!==e.ourIceConnectionState&&(u("oniceconnectionstatechange() | iceConnectionState: %s",t.iceConnectionState),e.ourIceConnectionState=t.iceConnectionState,e.oniceconnectionstatechange&&e.oniceconnectionstatechange(n,t.iceConnectionState))},t.onicegatheringstatechange=function(n){r.call(e)||t.iceGatheringState!==e.ourIceGatheringState&&(u("onicegatheringstatechange() | iceGatheringState: %s",t.iceGatheringState),e.ourIceGatheringState=t.iceGatheringState,e.onicegatheringstatechange&&e.onicegatheringstatechange(n,t.iceGatheringState))},t.onidentityresult=function(t){r.call(e)||(u("onidentityresult()"),e.onidentityresult&&e.onidentityresult(t))},t.onpeeridentity=function(t){r.call(e)||(u("onpeeridentity()"),e.onpeeridentity&&e.onpeeridentity(t))},t.onidpassertionerror=function(t){r.call(e)||(u("onidpassertionerror()"),e.onidpassertionerror&&e.onidpassertionerror(t))},t.onidpvalidationerror=function(t){r.call(e)||(u("onidpvalidationerror()"),e.onidpvalidationerror&&e.onidpvalidationerror(t))}}function s(){this.pc=this.pcConstraints?new g.RTCPeerConnection(this.pcConfig,this.pcConstraints):new g.RTCPeerConnection(this.pcConfig),a.call(this)}function c(){var e=this.pc,t=this.options,n=null;return e.localDescription?(t.iceTransportsRelay?n=e.localDescription.sdp.replace(f.REGEXP_SDP_NON_RELAY_CANDIDATES,""):t.iceTransportsNone&&(n=e.localDescription.sdp.replace(f.REGEXP_SDP_CANDIDATES,"")),this.ourLocalDescription=new g.RTCSessionDescription({type:e.localDescription.type,sdp:n||e.localDescription.sdp}),this.ourLocalDescription):(this.ourLocalDescription=null,null)}function d(){var e=this;Object.defineProperties(this,{peerConnection:{get:function(){return e.pc}},signalingState:{get:function(){return e.pc.signalingState}},iceConnectionState:{get:function(){return e.pc.iceConnectionState}},iceGatheringState:{get:function(){return e.pc.iceGatheringState}},localDescription:{get:function(){return c.call(e)}},remoteDescription:{get:function(){return e.pc.remoteDescription}},peerIdentity:{get:function(){return e.pc.peerIdentity}}})}t.exports=i;var l=e("merge"),u=e("debug")("rtcninja:RTCPeerConnection"),p=e("debug")("rtcninja:ERROR:RTCPeerConnection"),g=e("./Adapter"),f={REGEXP_NORMALIZED_CANDIDATE:new RegExp(/^candidate:/i),REGEXP_FIX_CANDIDATE:new RegExp(/(^a=|\r|\n)/gi),REGEXP_RELAY_CANDIDATE:new RegExp(/ relay /i),REGEXP_SDP_CANDIDATES:new RegExp(/^a=candidate:.*\r\n/gim),REGEXP_SDP_NON_RELAY_CANDIDATES:new RegExp(/^a=candidate:(.(?!relay ))*\r\n/gim)},m={normalizeCandidate:null};p.log=console.warn.bind(console),i.prototype.createOffer=function(e,t,n){u("createOffer()");var i=this;g.fixRTCOfferOptions(n),this.pc.createOffer(function(t){r.call(i)||(u("createOffer() | success"),e&&e(t))},function(e){r.call(i)||(p("createOffer() | error:",e),t&&t(e))},n)},i.prototype.createAnswer=function(e,t,n){u("createAnswer()");var i=this;this.pc.createAnswer(function(t){r.call(i)||(u("createAnswer() | success"),e&&e(t))},function(e){r.call(i)||(p("createAnswer() | error:",e),t&&t(e))},n)},i.prototype.setLocalDescription=function(e,t,n){function i(){"number"==typeof o.options.gatheringTimeout&&"complete"!==o.pc.iceGatheringState&&(u("setLocalDescription() | ending gathering in %d ms (gatheringTimeout option)",o.options.gatheringTimeout),o.timerGatheringTimeout=setTimeout(function(){r.call(o)||(u("forced end of candidates after gatheringTimeout timeout"),delete o.timerGatheringTimeout,clearTimeout(o.timerGatheringTimeoutAfterRelay),delete o.timerGatheringTimeoutAfterRelay,o.ignoreIceGathering=!0,o.onicecandidate&&o.onicecandidate({candidate:null},null))},o.options.gatheringTimeout))}u("setLocalDescription()");var o=this;this.pc.setLocalDescription(e,function(){r.call(o)||(u("setLocalDescription() | success"),clearTimeout(o.timerGatheringTimeout),delete o.timerGatheringTimeout,clearTimeout(o.timerGatheringTimeoutAfterRelay),delete o.timerGatheringTimeoutAfterRelay,i(),t&&t())},function(e){r.call(o)||(p("setLocalDescription() | error:",e),n&&n(e))}),this.ignoreIceGathering=!1},i.prototype.setRemoteDescription=function(e,t,n){u("setRemoteDescription()");var i=this;this.pc.setRemoteDescription(e,function(){r.call(i)||(u("setRemoteDescription() | success"),t&&t())},function(e){r.call(i)||(p("setRemoteDescription() | error:",e),n&&n(e))})},i.prototype.updateIce=function(e){u("updateIce() | pcConfig: %o",e),o.call(this,e),this.pc.updateIce(this.pcConfig),this.ignoreIceGathering=!1},i.prototype.addIceCandidate=function(e,t,n){u("addIceCandidate() | candidate: %o",e);var i=this;this.pc.addIceCandidate(e,function(){r.call(i)||(u("addIceCandidate() | success"),t&&t())},function(e){r.call(i)||(p("addIceCandidate() | error:",e),n&&n(e))})},i.prototype.getConfiguration=function(){return u("getConfiguration()"),this.pc.getConfiguration()},i.prototype.getLocalStreams=function(){return u("getLocalStreams()"),this.pc.getLocalStreams()},i.prototype.getRemoteStreams=function(){return u("getRemoteStreams()"),this.pc.getRemoteStreams()},i.prototype.getStreamById=function(e){return u("getStreamById() | streamId: %s",e),this.pc.getStreamById(e)},i.prototype.addStream=function(e){u("addStream() | stream: %s",e),this.pc.addStream(e)},i.prototype.removeStream=function(e){u("removeStream() | stream: %o",e),this.pc.removeStream(e)},i.prototype.close=function(){u("close()"),this.closed=!0,clearTimeout(this.timerGatheringTimeout),delete this.timerGatheringTimeout,clearTimeout(this.timerGatheringTimeoutAfterRelay),delete this.timerGatheringTimeoutAfterRelay,this.pc.close()},i.prototype.createDataChannel=function(){return u("createDataChannel()"),this.pc.createDataChannel.apply(this.pc,arguments)},i.prototype.createDTMFSender=function(e){return u("createDTMFSender()"),this.pc.createDTMFSender(e)},i.prototype.getStats=function(){return u("getStats()"),this.pc.getStats.apply(this.pc,arguments)},i.prototype.setIdentityProvider=function(){return u("setIdentityProvider()"),this.pc.setIdentityProvider.apply(this.pc,arguments)},i.prototype.getIdentityAssertion=function(){return u("getIdentityAssertion()"),this.pc.getIdentityAssertion()},i.prototype.reset=function(e){u("reset() | pcConfig: %o",e);var t=this.pc;t.onnegotiationneeded=null,t.onicecandidate=null,t.onaddstream=null,t.onremovestream=null,t.ondatachannel=null,t.onsignalingstatechange=null,t.oniceconnectionstatechange=null,t.onicegatheringstatechange=null,t.onidentityresult=null,t.onpeeridentity=null,t.onidpassertionerror=null,t.onidpvalidationerror=null,clearTimeout(this.timerGatheringTimeout),delete this.timerGatheringTimeout,clearTimeout(this.timerGatheringTimeoutAfterRelay),delete this.timerGatheringTimeoutAfterRelay,u("reset() | closing current peerConnection"),t.close(),o.call(this,e),s.call(this)}},{"./Adapter":1,debug:6,merge:9}],3:[function(e,t,n){"use strict";function i(e){var t=c(e||{});return l=!0,i.RTCPeerConnection=d,i.getUserMedia=t.getUserMedia,i.RTCSessionDescription=t.RTCSessionDescription,i.RTCIceCandidate=t.RTCIceCandidate,i.MediaStreamTrack=t.MediaStreamTrack,i.getMediaDevices=t.getMediaDevices,i.attachMediaStream=t.attachMediaStream,i.closeMediaStream=t.closeMediaStream,i.canRenegotiate=t.canRenegotiate,t.hasWebRTC()?(r("WebRTC supported"),!0):(a("WebRTC not supported"),!1)}t.exports=i;var o=e("bowser").browser,r=e("debug")("rtcninja"),a=e("debug")("rtcninja:ERROR"),s=e("./version"),c=e("./Adapter"),d=e("./RTCPeerConnection"),l=!1;a.log=console.warn.bind(console),r("version %s",s),r("detected browser: %s %s [mobile:%s, tablet:%s, android:%s, ios:%s]",o.name,o.version,!!o.mobile,!!o.tablet,!!o.android,!!o.ios),i.hasWebRTC=function(){return l||i(),c.hasWebRTC()},Object.defineProperty(i,"version",{get:function(){return s}}),Object.defineProperty(i,"called",{get:function(){return l}}),i.debug=e("debug"),i.browser=o},{"./Adapter":1,"./RTCPeerConnection":2,"./version":4,bowser:5,debug:6}],4:[function(e,t,n){"use strict";t.exports=e("../package.json").version},{"../package.json":10}],5:[function(t,n,i){!function(t,i){"undefined"!=typeof n&&n.exports?n.exports.browser=i():"function"==typeof e&&e.amd?e(i):this[t]=i()}("bowser",function(){function e(e){function n(t){var n=e.match(t);return n&&n.length>1&&n[1]||""}function i(t){var n=e.match(t);return n&&n.length>1&&n[2]||""}var o,r=n(/(ipod|iphone|ipad)/i).toLowerCase(),a=/like android/i.test(e),s=!a&&/android/i.test(e),c=n(/edge\/(\d+(\.\d+)?)/i),d=n(/version\/(\d+(\.\d+)?)/i),l=/tablet/i.test(e),u=!l&&/[^-]mobi/i.test(e);/opera|opr/i.test(e)?o={name:"Opera",opera:t,version:d||n(/(?:opera|opr)[\s\/](\d+(\.\d+)?)/i)}:/windows phone/i.test(e)?(o={name:"Windows Phone",windowsphone:t},c?(o.msedge=t,o.version=c):(o.msie=t,o.version=n(/iemobile\/(\d+(\.\d+)?)/i))):/msie|trident/i.test(e)?o={name:"Internet Explorer",msie:t,version:n(/(?:msie |rv:)(\d+(\.\d+)?)/i)}:/chrome.+? edge/i.test(e)?o={name:"Microsoft Edge",msedge:t,version:c}:/chrome|crios|crmo/i.test(e)?o={name:"Chrome",chrome:t,version:n(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i)}:r?(o={name:"iphone"==r?"iPhone":"ipad"==r?"iPad":"iPod"},d&&(o.version=d)):/sailfish/i.test(e)?o={name:"Sailfish",sailfish:t,version:n(/sailfish\s?browser\/(\d+(\.\d+)?)/i)}:/seamonkey\//i.test(e)?o={name:"SeaMonkey",seamonkey:t,version:n(/seamonkey\/(\d+(\.\d+)?)/i)}:/firefox|iceweasel/i.test(e)?(o={name:"Firefox",firefox:t,version:n(/(?:firefox|iceweasel)[ \/](\d+(\.\d+)?)/i)},/\((mobile|tablet);[^\)]*rv:[\d\.]+\)/i.test(e)&&(o.firefoxos=t)):/silk/i.test(e)?o={name:"Amazon Silk",silk:t,version:n(/silk\/(\d+(\.\d+)?)/i)}:s?o={name:"Android",version:d}:/phantom/i.test(e)?o={name:"PhantomJS",phantom:t,version:n(/phantomjs\/(\d+(\.\d+)?)/i)}:/blackberry|\bbb\d+/i.test(e)||/rim\stablet/i.test(e)?o={name:"BlackBerry",blackberry:t,version:d||n(/blackberry[\d]+\/(\d+(\.\d+)?)/i)}:/(web|hpw)os/i.test(e)?(o={name:"WebOS",webos:t,version:d||n(/w(?:eb)?osbrowser\/(\d+(\.\d+)?)/i)},/touchpad\//i.test(e)&&(o.touchpad=t)):o=/bada/i.test(e)?{name:"Bada",bada:t,version:n(/dolfin\/(\d+(\.\d+)?)/i)}:/tizen/i.test(e)?{name:"Tizen",tizen:t,version:n(/(?:tizen\s?)?browser\/(\d+(\.\d+)?)/i)||d}:/safari/i.test(e)?{name:"Safari",safari:t,version:d}:{name:n(/^(.*)\/(.*) /),version:i(/^(.*)\/(.*) /)},!o.msedge&&/(apple)?webkit/i.test(e)?(o.name=o.name||"Webkit",o.webkit=t,!o.version&&d&&(o.version=d)):!o.opera&&/gecko\//i.test(e)&&(o.name=o.name||"Gecko",o.gecko=t,o.version=o.version||n(/gecko\/(\d+(\.\d+)?)/i)),o.msedge||!s&&!o.silk?r&&(o[r]=t,o.ios=t):o.android=t;var p="";o.windowsphone?p=n(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i):r?(p=n(/os (\d+([_\s]\d+)*) like mac os x/i),p=p.replace(/[_\s]/g,".")):s?p=n(/android[ \/-](\d+(\.\d+)*)/i):o.webos?p=n(/(?:web|hpw)os\/(\d+(\.\d+)*)/i):o.blackberry?p=n(/rim\stablet\sos\s(\d+(\.\d+)*)/i):o.bada?p=n(/bada\/(\d+(\.\d+)*)/i):o.tizen&&(p=n(/tizen[\/\s](\d+(\.\d+)*)/i)),p&&(o.osversion=p);var g=p.split(".")[0];return l||"ipad"==r||s&&(3==g||4==g&&!u)||o.silk?o.tablet=t:(u||"iphone"==r||"ipod"==r||s||o.blackberry||o.webos||o.bada)&&(o.mobile=t),o.msedge||o.msie&&o.version>=10||o.chrome&&o.version>=20||o.firefox&&o.version>=20||o.safari&&o.version>=6||o.opera&&o.version>=10||o.ios&&o.osversion&&o.osversion.split(".")[0]>=6||o.blackberry&&o.version>=10.1?o.a=t:o.msie&&o.version<10||o.chrome&&o.version<20||o.firefox&&o.version<20||o.safari&&o.version<6||o.opera&&o.version<10||o.ios&&o.osversion&&o.osversion.split(".")[0]<6?o.c=t:o.x=t,o}var t=!0,n=e("undefined"!=typeof navigator?navigator.userAgent:"");return n.test=function(e){for(var t=0;t=31}function o(){var e=arguments,t=this.useColors;if(e[0]=(t?"%c":"")+this.namespace+(t?" %c":" ")+e[0]+(t?"%c ":" ")+"+"+n.humanize(this.diff),!t)return e;var i="color: "+this.color;e=[e[0],i,"color: inherit"].concat(Array.prototype.slice.call(e,1));var o=0,r=0;return e[0].replace(/%[a-z%]/g,function(e){"%%"!==e&&(o++,"%c"===e&&(r=o))}),e.splice(r,0,i),e}function r(){return"object"==typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function a(e){try{null==e?n.storage.removeItem("debug"):n.storage.debug=e}catch(t){}}function s(){var e;try{e=n.storage.debug}catch(t){}return e}function c(){try{return window.localStorage}catch(e){}}n=t.exports=e("./debug"),n.log=r,n.formatArgs=o,n.save=a,n.load=s,n.useColors=i,n.storage="undefined"!=typeof chrome&&"undefined"!=typeof chrome.storage?chrome.storage.local:c(),n.colors=["lightseagreen","forestgreen","goldenrod","dodgerblue","darkorchid","crimson"],n.formatters.j=function(e){return JSON.stringify(e)},n.enable(s())},{"./debug":7}],7:[function(e,t,n){function i(){return n.colors[l++%n.colors.length]}function o(e){function t(){}function o(){var e=o,t=+new Date,r=t-(d||t);e.diff=r,e.prev=d,e.curr=t,d=t,null==e.useColors&&(e.useColors=n.useColors()),null==e.color&&e.useColors&&(e.color=i());var a=Array.prototype.slice.call(arguments);a[0]=n.coerce(a[0]),"string"!=typeof a[0]&&(a=["%o"].concat(a));var s=0;a[0]=a[0].replace(/%([a-z%])/g,function(t,i){if("%%"===t)return t;s++;var o=n.formatters[i];if("function"==typeof o){var r=a[s];t=o.call(e,r),a.splice(s,1),s--}return t}),"function"==typeof n.formatArgs&&(a=n.formatArgs.apply(e,a));var c=o.log||n.log||console.log.bind(console);c.apply(e,a)}t.enabled=!1,o.enabled=!0;var r=n.enabled(e)?o:t;return r.namespace=e,r}function r(e){n.save(e);for(var t=(e||"").split(/[\s,]+/),i=t.length,o=0;i>o;o++)t[o]&&(e=t[o].replace(/\*/g,".*?"),"-"===e[0]?n.skips.push(new RegExp("^"+e.substr(1)+"$")):n.names.push(new RegExp("^"+e+"$")))}function a(){n.enable("")}function s(e){var t,i;for(t=0,i=n.skips.length;i>t;t++)if(n.skips[t].test(e))return!1;for(t=0,i=n.names.length;i>t;t++)if(n.names[t].test(e))return!0;return!1}function c(e){return e instanceof Error?e.stack||e.message:e}n=t.exports=o,n.coerce=c,n.disable=a,n.enable=r,n.enabled=s,n.humanize=e("ms"),n.names=[],n.skips=[],n.formatters={};var d,l=0},{ms:8}],8:[function(e,t,n){function i(e){if(e=""+e,!(e.length>1e4)){var t=/^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(e);if(t){var n=parseFloat(t[1]),i=(t[2]||"ms").toLowerCase();switch(i){case"years":case"year":case"yrs":case"yr":case"y":return n*u;case"days":case"day":case"d":return n*l;case"hours":case"hour":case"hrs":case"hr":case"h":return n*d;case"minutes":case"minute":case"mins":case"min":case"m":return n*c;case"seconds":case"second":case"secs":case"sec":case"s":return n*s;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return n}}}}function o(e){return e>=l?Math.round(e/l)+"d":e>=d?Math.round(e/d)+"h":e>=c?Math.round(e/c)+"m":e>=s?Math.round(e/s)+"s":e+"ms"}function r(e){return a(e,l,"day")||a(e,d,"hour")||a(e,c,"minute")||a(e,s,"second")||e+" ms"}function a(e,t,n){return t>e?void 0:1.5*t>e?Math.floor(e/t)+" "+n:Math.ceil(e/t)+" "+n+"s"}var s=1e3,c=60*s,d=60*c,l=24*d,u=365.25*l;t.exports=function(e,t){return t=t||{},"string"==typeof e?i(e):t["long"]?r(e):o(e)}},{}],9:[function(e,t,n){!function(e){function n(e,t){if("object"!==o(e))return t;for(var i in t)e[i]="object"===o(e[i])&&"object"===o(t[i])?n(e[i],t[i]):t[i];return e}function i(e,t,i){var a=i[0],s=i.length;(e||"object"!==o(a))&&(a={});for(var c=0;s>c;++c){var d=i[c],l=o(d);if("object"===l)for(var u in d){var p=e?r.clone(d[u]):d[u];a[u]=t?n(a[u],p):p}}return a}function o(e){return{}.toString.call(e).slice(8,-1).toLowerCase()}var r=function(e){return i(e===!0,!1,arguments)},a="merge";r.recursive=function(e){return i(e===!0,!0,arguments)},r.clone=function(e){var t,n,i=e,a=o(e);if("array"===a)for(i=[],n=e.length,t=0;n>t;++t)i[t]=r.clone(e[t]);else if("object"===a){i={};for(t in e)i[t]=r.clone(e[t])}return i},e?t.exports=r:window[a]=r}("object"==typeof t&&t&&"object"==typeof t.exports&&t.exports)},{}],10:[function(e,t,n){t.exports={name:"rtcninja",version:"0.6.2",description:"WebRTC API wrapper to deal with different browsers",author:"Iñaki Baz Castillo (http://eface2face.com)",contributors:["Jesús Pérez "],license:"MIT",main:"lib/rtcninja.js",homepage:"https://github.com/eface2face/rtcninja.js",repository:{type:"git",url:"https://github.com/eface2face/rtcninja.js.git"},keywords:["webrtc"],engines:{node:">=0.10.32"},dependencies:{bowser:"^0.7.3",debug:"^2.2.0",merge:"^1.2.0"},devDependencies:{browserify:"^10.2.3",gulp:"git+https://github.com/gulpjs/gulp.git#4.0","gulp-expect-file":"0.0.7","gulp-filelog":"^0.4.1","gulp-header":"^1.2.2","gulp-jscs":"^1.6.0","gulp-jscs-stylish":"^1.1.0","gulp-jshint":"^1.11.0","gulp-rename":"^1.2.2","gulp-uglify":"^1.2.0","jshint-stylish":"^1.0.2",retire:"^1.1.0",shelljs:"^0.5.0","vinyl-source-stream":"^1.1.0"}}},{}]},{},[3])(3)});rtcninja.js-0.6.2/docs/000077500000000000000000000000001254426444300147025ustar00rootroot00000000000000rtcninja.js-0.6.2/docs/RTCPeerConnection.md000066400000000000000000000064411254426444300205150ustar00rootroot00000000000000# `rtcninja.RTCPeerConnection` Class API The `rtcninja.RTCPeerConnection` class wrappes a native `(webkit|moz)RTCPeerConnection` and attempts to provide a uniform behavior across different WebRTC implementations. The provided API mimics the `RTCPeerConnection` API in the latest [WebRTC draft](http://w3c.github.io/webrtc-pc/). ### Features * Normalization of the `pcConfig.iceServers` field (it deals with both the deprecated `url` field and the new `urls` one). * Filtering of certain events once the `RTCPeerConnection` is closed. For example, old versions of Firefox fire `onicecandidate` or `onaddstream` events even after `close()` is called. * Filtering of duplicate events. For example, some old browsers fire consecutive `oniceconnectionstatechange` events having the `iceConnectionState` attribute the same value in them. * Normalization of the ICE candidate syntax. In some old browsers the `candidate` String in a `RTCIceCandidate` object contains incorrect "a=" and "\r\n". Those chars are removed. * Addition of the `pcConfig.iceTransports` field in browsers not supporting it. ### `new rtcninja.RTCPeerConnection(pcConfig)` constructor #### `pcConfig` {Object} param Mandatory Object holding the [`RTCConfiguration`](http://w3c.github.io/webrtc-pc/#idl-def-RTCConfiguration) dictionary of the `RTCPeerConnection`. Some features not implemented in some browsers (such a Firefox) are implemented in JavaScript (such as `iceTransports`). There are also custom options not present in the WebRTC specification: * `gatheringTimeout` {Number}: ICE gathering is terminated after the given time (milliseconds) and a faked `onicecandidate` event with `candidate = null` is fired. No more `onicecandidate` events are fired once this timeout. * `gatheringTimeoutAfterRelay` {Number}: Once the first "relay" (TURN) candidate is gathered, ICE gathering is terminated after the given time (milliseconds) and a faked `onicecandidate` event with `candidate = null` is fired. No more `onicecandidate` events are fired once this timeout. ### RTCPeerConnection API `rtcninja.RTCPeerConnection` provides the same functions and attributes as described in the latest WebRTC draft for the `RTCPeerConnection` class. There are a few exceptions: #### Events All the native `RTCPeerConnection` events are fired with a single `event` argument of type `Event`. Events in `rtcninja.RTCPeerConnection` are full compatible with the original ones, but some of them also provide a second argument: * `onicecandidate(event, candidate)`: Second argument is the normalized `event.candidate` field. * `onaddstream(event, stream)` * `onremovestream(event, stream)` * `ondatachannel(event, channel)` * `onsignalingstatechange(event, signalingState)` * `oniceconnectionstatechange(event, iceConnectionState)` * `onicegatheringstatechange(event, iceGatheringState)` ### `reset(pcConfig)` function By calling `reset()` on a `rtcninja.RTCPeerConnection` instance the native `RTCPeerConnection` is silently closed (no events are fired) and a new `RTCPeerConnection` is created. No local stream is attached to it (must be done by the application). *NOTE:* This is just useful in case the remote peer does **also** reset its `RTCPeerConnection`. Otherwise a new SDP renegotiation would fail. #### `pcConfig` {Object} param Same as in the `RTCPeerConnection` construtor. rtcninja.js-0.6.2/docs/index.md000066400000000000000000000001761254426444300163370ustar00rootroot00000000000000# API Documentation * [`rtcninja` Module API](rtcninja.md) * [`rtcninja.RTCPeerConnection` Class API](RTCPeerConnection.md) rtcninja.js-0.6.2/docs/rtcninja.md000066400000000000000000000042471254426444300170430ustar00rootroot00000000000000# `rtcninja` Module API ### `rtcninja()` function By calling the library as a function it performs the WebRTC check in the browser. This function MUST be called before attempting to use the library. It returns `true` if the browser supports WebRTC. ### `rtcninja.hasWebRTC()` function Returns `true` if the browser supports WebRTC. ### `rtcninja.called` read attribute Returns `true` if `rtcninja()` was already called. ### `rtcninja.version` read attribute Returns a String with the version of the library. ```javascript rtcninja.version => "X.Y.Z" ``` ### `rtcninja.debug` Provides access to the [debug](https://github.com/visionmedia/debug) module. ### `rtcninja.getUserMedia(constraints, successCallback, errorCallback)` function Provides a wrapper over the native `navigator.(webkit|moz)getUserMedia()` function. As a feature, if WebRTC is not supported this function fires the given `errorCallback` instead of throwing an error. ### `rtcninja.getMediaDevices(successCallback)` function Provides a wrapper over the native `navigator.getMediaDevices()` or `MediaStreamTrack.getSources()`. **NOTE:** This method does not exist (`rtcninja.getMediaDevices === undefined`) if the browser does not support any of the native implementations to get the list of media devices. ### `rtcninja.RTCPeerConnection` class Provides access to the [`rtcninja.RTCPeerConnection`](RTCPeerConnection.md) class, which wrappes a native `(webkit|moz)RTCPeerConnection`. ### `rtcninja.RTCSessionDescription` class Wrapper for the native `RTCSessionDescription` class. ### `rtcninja.RTCIceCandidate` class Wrapper for the native `RTCIceCandidate` class. ### `rtcninja.MediaStreamTrack` class Wrapper for the native `MediaStreamTrack` class. ### `rtcninja.attachMediaStream(element, stream)` function Sets the given `stream` (of type `MediaStream`) as the source of the `