kurento_rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +25 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/kurento_rails.gemspec +30 -0
- data/lib/generators/kurento_rails/install_generator.rb +45 -0
- data/lib/generators/kurento_rails/templates/config/events.rb +24 -0
- data/lib/generators/kurento_rails/templates/controllers/kurento_controller.rb +27 -0
- data/lib/generators/kurento_rails/templates/controllers/kurento_websockets_controller.rb +103 -0
- data/lib/generators/kurento_rails/templates/javascript/kurento-rails-js/adapterjs/publish/adapter.min.js +3 -0
- data/lib/generators/kurento_rails/templates/javascript/kurento-rails-js/kurento-client/js/kurento-client.map +1 -0
- data/lib/generators/kurento_rails/templates/javascript/kurento-rails-js/kurento-client/js/kurento-client.min.js +557 -0
- data/lib/generators/kurento_rails/templates/javascript/kurento-rails-js/kurento-rails.js +635 -0
- data/lib/generators/kurento_rails/templates/javascript/kurento-rails-js/kurento-utils/js/kurento-utils.map +1 -0
- data/lib/generators/kurento_rails/templates/javascript/kurento-rails-js/kurento-utils/js/kurento-utils.min.js +36 -0
- data/lib/generators/kurento_rails/templates/migrations/create_video_streams.rb +13 -0
- data/lib/generators/kurento_rails/templates/models/kurento_rails_video_stream.rb +2 -0
- data/lib/generators/kurento_rails/templates/views/kurento/broadcast.html.slim +2 -0
- data/lib/generators/kurento_rails/templates/views/kurento/index.html.slim +2 -0
- data/lib/generators/kurento_rails/templates/views/kurento/old_streams.html.slim +2 -0
- data/lib/kurento_rails.rb +5 -0
- data/lib/kurento_rails/version.rb +3 -0
- data/src/events.rb +24 -0
- data/src/kurento-rails.coffee +387 -0
- data/src/kurento_controller.rb +27 -0
- data/src/kurento_rails_video_stream.rb +2 -0
- data/src/kurento_websockets_controller.rb +103 -0
- metadata +161 -0
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../node_modules/browserify/node_modules/browser-pack/_prelude.js","../node_modules/freeice/stun.json","../node_modules/freeice/turn.json","../lib/browser.js","../lib/index.js","../lib/WebRtcPeer.js","../node_modules/browserify/node_modules/events/events.js","../node_modules/freeice/index.js","../node_modules/inherits/inherits_browser.js","../node_modules/merge/merge.js","../node_modules/uuid/uuid.js","../node_modules/ua-parser-js/src/ua-parser.js","../node_modules/uuid/rng-browser.js","../node_modules/freeice/node_modules/normalice/index.js"],"names":["window","addEventListener","module","exports","require","WebRtcPeer","noop","error","console","trackStop","track","stop","streamStop","stream","getTracks","forEach","bufferizeCandidates","pc","onerror","candidatesQueue","this","signalingState","length","entry","shift","addIceCandidate","candidate","callback","Error","remoteDescription","push","removeFIDFromOffer","sdp","n","indexOf","slice","getSimulcastInfo","videoStream","videoTracks","getVideoTracks","lines","id","join","mode","options","setRemoteVideo","remoteVideo","getRemoteStreams","url","URL","createObjectURL","pause","src","load","log","streamEndedListener","self","emit","start","localVideo","showLocalVideo","addStream","audioStream","browser","parser","getBrowser","name","major","getMedia","constraints","undefined","MEDIA_CONSTRAINTS","getUserMedia","super_","call","Function","bind","mediaConstraints","connectionConstraints","peerConnection","sendSource","guid","uuid","v4","configuration","recursive","iceServers","freeice","onstreamended","on","onicecandidate","oncandidategatheringdone","simulcast","RTCPeerConnection","Object","defineProperties","get","currentFrame","readyState","HAVE_CURRENT_DATA","canvas","document","createElement","width","videoWidth","height","videoHeight","getContext","drawImage","candidatesQueueOut","candidategatheringdone","event","EventEmitter","listenerCount","listener","iceCandidate","RTCIceCandidate","generateOffer","offerAudio","offerVideo","audio","video","browserDependantConstraints","version","offerToReceiveAudio","offerToReceiveVideo","mandatory","OfferToReceiveAudio","OfferToReceiveVideo","optional","DtlsSrtpKeyAgreement","JSON","stringify","createOffer","offer","RTCSessionDescription","type","warn","setLocalDescription","processAnswer","getLocalSessionDescriptor","localDescription","getRemoteSessionDescriptor","muted","sdpAnswer","answer","setRemoteDescription","processOffer","sdpOffer","createAnswer","setTimeout","getScreenConstraints","constraints_","unshift","apply","removeAllListeners","cancelChooseDesktopMedia","createEnableDescriptor","method","enumerable","streams","getLocalStreams","i","tracks","j","enabled","set","value","trackSetEnable","WebRtcPeerRecvonly","WebRtcPeerSendonly","WebRtcPeerSendrecv","inherits","UAParser","framerate","ua","navigator","userAgent","prototype","audioEnabled","videoEnabled","getLocalStream","index","getRemoteStream","dispose","close","err","_events","_maxListeners","isFunction","arg","isNumber","isObject","isUndefined","defaultMaxListeners","setMaxListeners","isNaN","TypeError","er","handler","len","args","listeners","arguments","Array","addListener","m","newListener","warned","trace","once","g","removeListener","fired","list","position","splice","key","ret","emitter","normalice","opts","getServers","count","idx","out","input","concat","servers","Math","random","map","String","selected","stun","turn","stunCount","turnCount","create","ctor","superCtor","constructor","writable","configurable","TempCtor","isNode","merge_recursive","base","extend","typeOf","merge","clone","argv","result","size","item","sitem","Public","toString","toLowerCase","publicName","output","parse","s","buf","offset","ii","replace","oct","_hexToByte","unparse","bth","_byteToHex","v1","b","clockseq","_clockseq","msecs","Date","getTime","nsecs","_lastNSecs","dt","_lastMSecs","tl","tmh","node","_nodeId","rnds","rng","_rng","substr","_seedBytes","LIBVERSION","EMPTY","UNKNOWN","FUNC_TYPE","UNDEF_TYPE","OBJ_TYPE","STR_TYPE","MAJOR","MODEL","NAME","TYPE","VENDOR","VERSION","ARCHITECTURE","CONSOLE","MOBILE","TABLET","SMARTTV","WEARABLE","EMBEDDED","util","regexes","extensions","has","str1","str2","lowerize","str","split","mapper","rgx","k","p","q","matches","match","regex","props","exec","getUA","test","maps","oldsafari","1.0","1.2","1.3","2.0","2.0.2","2.0.3","2.0.4","?","device","amazon","model","Fire Phone","sprint","Evo Shift 4G","vendor","HTC","Sprint","os","windows","ME","NT 3.11","NT 4.0","2000","XP","Vista","7","8","8.1","10","RT","cpu","engine","uastring","getResult","rgxmap","getCPU","getDevice","getEngine","getOS","setUA","BROWSER","CPU","DEVICE","ENGINE","OS","define","amd","$","jQuery","Zepto","prop","global","crypto","getRandomValues","_rnds8","Uint8Array","_rnds","r","protocols","protocol","parts","trim","username","credential","urls"],"mappings":"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","file":"bundle.js","sourcesContent":["(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<r.length;o++)s(r[o]);return s})","module.exports=[\"stun.l.google.com:19302\",\"stun1.l.google.com:19302\",\"stun2.l.google.com:19302\",\"stun3.l.google.com:19302\",\"stun4.l.google.com:19302\",\"stun.ekiga.net\",\"stun.ideasip.com\",\"stun.schlund.de\",\"stun.stunprotocol.org:3478\",\"stun.voiparound.com\",\"stun.voipbuster.com\",\"stun.voipstunt.com\",\"stun.voxgratia.org\",\"stun.services.mozilla.com\"]","module.exports=[]","if (window.addEventListener)\n module.exports = require('./index');","var WebRtcPeer = require('./WebRtcPeer');\nexports.WebRtcPeer = WebRtcPeer;","var freeice = require('freeice');\nvar inherits = require('inherits');\nvar UAParser = require('ua-parser-js');\nvar uuid = require('uuid');\nvar EventEmitter = require('events').EventEmitter;\nvar recursive = require('merge').recursive.bind(undefined, true);\ntry {\n require('kurento-browser-extensions');\n} catch (error) {\n if (typeof getScreenConstraints === 'undefined') {\n console.warn('screen sharing is not available');\n getScreenConstraints = function getScreenConstraints(sendSource, callback) {\n callback(new Error('This library is not enabled for screen sharing'));\n };\n }\n}\nvar MEDIA_CONSTRAINTS = {\n audio: true,\n video: {\n width: 640,\n framerate: 15\n }\n };\nvar ua = window && window.navigator ? window.navigator.userAgent : '';\nvar parser = new UAParser(ua);\nfunction noop(error) {\n if (error)\n console.error(error);\n}\nfunction trackStop(track) {\n track.stop && track.stop();\n}\nfunction streamStop(stream) {\n stream.getTracks().forEach(trackStop);\n}\nfunction bufferizeCandidates(pc, onerror) {\n var candidatesQueue = [];\n pc.addEventListener('signalingstatechange', function () {\n if (this.signalingState === 'stable') {\n while (candidatesQueue.length) {\n var entry = candidatesQueue.shift();\n this.addIceCandidate(entry.candidate, entry.callback, entry.callback);\n }\n }\n });\n return function (candidate, callback) {\n callback = callback || onerror;\n switch (pc.signalingState) {\n case 'closed':\n callback(new Error('PeerConnection object is closed'));\n break;\n case 'stable':\n if (pc.remoteDescription) {\n pc.addIceCandidate(candidate, callback, callback);\n break;\n }\n default:\n candidatesQueue.push({\n candidate: candidate,\n callback: callback\n });\n }\n };\n}\nfunction removeFIDFromOffer(sdp) {\n var n = sdp.indexOf('a=ssrc-group:FID');\n if (n > 0) {\n return sdp.slice(0, n);\n } else {\n return sdp;\n }\n}\nfunction getSimulcastInfo(videoStream) {\n var videoTracks = videoStream.getVideoTracks();\n var lines = [\n 'a=x-google-flag:conference',\n 'a=ssrc-group:SIM 1 2 3',\n 'a=ssrc:1 cname:localVideo',\n 'a=ssrc:1 msid:' + videoStream.id + ' ' + videoTracks[0].id,\n 'a=ssrc:1 mslabel:' + videoStream.id,\n 'a=ssrc:1 label:' + videoTracks[0].id,\n 'a=ssrc:2 cname:localVideo',\n 'a=ssrc:2 msid:' + videoStream.id + ' ' + videoTracks[0].id,\n 'a=ssrc:2 mslabel:' + videoStream.id,\n 'a=ssrc:2 label:' + videoTracks[0].id,\n 'a=ssrc:3 cname:localVideo',\n 'a=ssrc:3 msid:' + videoStream.id + ' ' + videoTracks[0].id,\n 'a=ssrc:3 mslabel:' + videoStream.id,\n 'a=ssrc:3 label:' + videoTracks[0].id\n ];\n lines.push('');\n return lines.join('\\n');\n}\nfunction WebRtcPeer(mode, options, callback) {\n if (!(this instanceof WebRtcPeer)) {\n return new WebRtcPeer(mode, options, callback);\n }\n WebRtcPeer.super_.call(this);\n if (options instanceof Function) {\n callback = options;\n options = undefined;\n }\n options = options || {};\n callback = (callback || noop).bind(this);\n var localVideo = options.localVideo;\n var remoteVideo = options.remoteVideo;\n var videoStream = options.videoStream;\n var audioStream = options.audioStream;\n var mediaConstraints = options.mediaConstraints;\n var connectionConstraints = options.connectionConstraints;\n var pc = options.peerConnection;\n var sendSource = options.sendSource || 'webcam';\n var guid = uuid.v4();\n var configuration = recursive({ iceServers: freeice() }, options.configuration);\n var onstreamended = options.onstreamended;\n if (onstreamended)\n this.on('streamended', onstreamended);\n var onicecandidate = options.onicecandidate;\n if (onicecandidate)\n this.on('icecandidate', onicecandidate);\n var oncandidategatheringdone = options.oncandidategatheringdone;\n if (oncandidategatheringdone) {\n this.on('candidategatheringdone', oncandidategatheringdone);\n }\n var simulcast = options.simulcast;\n if (!pc)\n pc = new RTCPeerConnection(configuration);\n Object.defineProperties(this, {\n 'peerConnection': {\n get: function () {\n return pc;\n }\n },\n 'remoteVideo': {\n get: function () {\n return remoteVideo;\n }\n },\n 'localVideo': {\n get: function () {\n return localVideo;\n }\n },\n 'currentFrame': {\n get: function () {\n if (!remoteVideo)\n return;\n if (remoteVideo.readyState < remoteVideo.HAVE_CURRENT_DATA)\n throw new Error('No video stream data available');\n var canvas = document.createElement('canvas');\n canvas.width = remoteVideo.videoWidth;\n canvas.height = remoteVideo.videoHeight;\n canvas.getContext('2d').drawImage(remoteVideo, 0, 0);\n return canvas;\n }\n }\n });\n var self = this;\n var candidatesQueueOut = [];\n var candidategatheringdone = false;\n pc.addEventListener('icecandidate', function (event) {\n var candidate = event.candidate;\n if (EventEmitter.listenerCount(self, 'icecandidate') || EventEmitter.listenerCount(self, 'candidategatheringdone')) {\n if (candidate) {\n self.emit('icecandidate', candidate);\n candidategatheringdone = false;\n } else if (!candidategatheringdone) {\n self.emit('candidategatheringdone');\n candidategatheringdone = true;\n }\n } else if (!candidategatheringdone) {\n candidatesQueueOut.push(candidate);\n if (!candidate)\n candidategatheringdone = true;\n }\n });\n this.on('newListener', function (event, listener) {\n if (event === 'icecandidate' || event === 'candidategatheringdone') {\n while (candidatesQueueOut.length) {\n var candidate = candidatesQueueOut.shift();\n if (!candidate === (event === 'candidategatheringdone')) {\n listener(candidate);\n }\n }\n }\n });\n var addIceCandidate = bufferizeCandidates(pc);\n this.addIceCandidate = function (iceCandidate, callback) {\n var candidate = new RTCIceCandidate(iceCandidate);\n console.log('ICE candidate received');\n callback = (callback || noop).bind(this);\n addIceCandidate(candidate, callback);\n };\n this.generateOffer = function (callback) {\n callback = callback.bind(this);\n var browser = parser.getBrowser();\n var offerAudio = true;\n var offerVideo = true;\n if (mediaConstraints) {\n offerAudio = typeof mediaConstraints.audio === 'boolean' ? mediaConstraints.audio : true;\n offerVideo = typeof mediaConstraints.video === 'boolean' ? mediaConstraints.video : true;\n }\n var browserDependantConstraints = browser.name === 'Firefox' && browser.version > 34 ? {\n offerToReceiveAudio: mode !== 'sendonly' && offerAudio,\n offerToReceiveVideo: mode !== 'sendonly' && offerVideo\n } : {\n mandatory: {\n OfferToReceiveAudio: mode !== 'sendonly' && offerAudio,\n OfferToReceiveVideo: mode !== 'sendonly' && offerVideo\n },\n optional: [{ DtlsSrtpKeyAgreement: true }]\n };\n var constraints = recursive(browserDependantConstraints, connectionConstraints);\n console.log('constraints: ' + JSON.stringify(constraints));\n pc.createOffer(function (offer) {\n console.log('Created SDP offer');\n if (simulcast) {\n if (browser.name === 'Chrome' || browser.name === 'Chromium') {\n console.log('Adding multicast info');\n offer = new RTCSessionDescription({\n 'type': offer.type,\n 'sdp': removeFIDFromOffer(offer.sdp) + getSimulcastInfo(videoStream)\n });\n } else {\n console.warn('Simulcast is only available in Chrome browser.');\n }\n }\n pc.setLocalDescription(offer, function () {\n console.log('Local description set', offer.sdp);\n callback(null, offer.sdp, self.processAnswer.bind(self));\n }, callback);\n }, callback, constraints);\n };\n this.getLocalSessionDescriptor = function () {\n return pc.localDescription;\n };\n this.getRemoteSessionDescriptor = function () {\n return pc.remoteDescription;\n };\n function setRemoteVideo() {\n if (remoteVideo) {\n var stream = pc.getRemoteStreams()[0];\n var url = stream ? URL.createObjectURL(stream) : '';\n remoteVideo.pause();\n remoteVideo.src = url;\n remoteVideo.load();\n console.log('Remote URL:', url);\n }\n }\n this.showLocalVideo = function () {\n localVideo.src = URL.createObjectURL(videoStream);\n localVideo.muted = true;\n };\n this.processAnswer = function (sdpAnswer, callback) {\n callback = (callback || noop).bind(this);\n var answer = new RTCSessionDescription({\n type: 'answer',\n sdp: sdpAnswer\n });\n console.log('SDP answer received, setting remote description');\n if (pc.signalingState === 'closed') {\n return callback('PeerConnection is closed');\n }\n pc.setRemoteDescription(answer, function () {\n setRemoteVideo();\n callback();\n }, callback);\n };\n this.processOffer = function (sdpOffer, callback) {\n callback = callback.bind(this);\n var offer = new RTCSessionDescription({\n type: 'offer',\n sdp: sdpOffer\n });\n console.log('SDP offer received, setting remote description');\n if (pc.signalingState === 'closed') {\n return callback('PeerConnection is closed');\n }\n pc.setRemoteDescription(offer, function () {\n setRemoteVideo();\n pc.createAnswer(function (answer) {\n console.log('Created SDP answer');\n pc.setLocalDescription(answer, function () {\n console.log('Local description set', answer.sdp);\n callback(null, answer.sdp);\n }, callback);\n }, callback);\n }, callback);\n };\n function streamEndedListener() {\n self.emit('streamended', this);\n }\n function start() {\n if (pc.signalingState === 'closed') {\n callback('The peer connection object is in \"closed\" state. This is most likely due to an invocation of the dispose method before accepting in the dialogue');\n }\n if (videoStream && localVideo) {\n self.showLocalVideo();\n }\n if (videoStream) {\n videoStream.addEventListener('ended', streamEndedListener);\n pc.addStream(videoStream);\n }\n if (audioStream) {\n audioStream.addEventListener('ended', streamEndedListener);\n pc.addStream(audioStream);\n }\n var browser = parser.getBrowser();\n if (mode === 'sendonly' && (browser.name === 'Chrome' || browser.name === 'Chromium') && browser.major === 39) {\n mode = 'sendrecv';\n }\n callback();\n }\n if (mode !== 'recvonly' && !videoStream && !audioStream) {\n function getMedia(constraints) {\n if (constraints === undefined) {\n constraints = MEDIA_CONSTRAINTS;\n }\n getUserMedia(constraints, function (stream) {\n videoStream = stream;\n start();\n }, callback);\n }\n if (sendSource === 'webcam') {\n getMedia(mediaConstraints);\n } else {\n getScreenConstraints(sendSource, function (error, constraints_) {\n if (error)\n return callback(error);\n constraints = [mediaConstraints];\n constraints.unshift(constraints_);\n getMedia(recursive.apply(undefined, constraints));\n }, guid);\n }\n } else {\n setTimeout(start, 0);\n }\n this.on('_dispose', function () {\n if (localVideo) {\n localVideo.pause();\n localVideo.src = '';\n localVideo.load();\n }\n if (remoteVideo) {\n remoteVideo.pause();\n remoteVideo.src = '';\n remoteVideo.load();\n }\n self.removeAllListeners();\n if (window.cancelChooseDesktopMedia !== undefined) {\n window.cancelChooseDesktopMedia(guid);\n }\n });\n}\ninherits(WebRtcPeer, EventEmitter);\nfunction createEnableDescriptor(type) {\n var method = 'get' + type + 'Tracks';\n return {\n enumerable: true,\n get: function () {\n if (!this.peerConnection)\n return;\n var streams = this.peerConnection.getLocalStreams();\n if (!streams.length)\n return;\n for (var i = 0, stream; stream = streams[i]; i++) {\n var tracks = stream[method]();\n for (var j = 0, track; track = tracks[j]; j++)\n if (!track.enabled)\n return false;\n }\n return true;\n },\n set: function (value) {\n function trackSetEnable(track) {\n track.enabled = value;\n }\n this.peerConnection.getLocalStreams().forEach(function (stream) {\n stream[method]().forEach(trackSetEnable);\n });\n }\n };\n}\nObject.defineProperties(WebRtcPeer.prototype, {\n 'enabled': {\n enumerable: true,\n get: function () {\n return this.audioEnabled && this.videoEnabled;\n },\n set: function (value) {\n this.audioEnabled = this.videoEnabled = value;\n }\n },\n 'audioEnabled': createEnableDescriptor('Audio'),\n 'videoEnabled': createEnableDescriptor('Video')\n});\nWebRtcPeer.prototype.getLocalStream = function (index) {\n if (this.peerConnection) {\n return this.peerConnection.getLocalStreams()[index || 0];\n }\n};\nWebRtcPeer.prototype.getRemoteStream = function (index) {\n if (this.peerConnection) {\n return this.peerConnection.getRemoteStreams()[index || 0];\n }\n};\nWebRtcPeer.prototype.dispose = function () {\n console.log('Disposing WebRtcPeer');\n var pc = this.peerConnection;\n try {\n if (pc) {\n if (pc.signalingState === 'closed')\n return;\n pc.getLocalStreams().forEach(streamStop);\n pc.close();\n }\n } catch (err) {\n console.warn('Exception disposing webrtc peer ' + err);\n }\n this.emit('_dispose');\n};\nfunction WebRtcPeerRecvonly(options, callback) {\n if (!(this instanceof WebRtcPeerRecvonly)) {\n return new WebRtcPeerRecvonly(options, callback);\n }\n WebRtcPeerRecvonly.super_.call(this, 'recvonly', options, callback);\n}\ninherits(WebRtcPeerRecvonly, WebRtcPeer);\nfunction WebRtcPeerSendonly(options, callback) {\n if (!(this instanceof WebRtcPeerSendonly)) {\n return new WebRtcPeerSendonly(options, callback);\n }\n WebRtcPeerSendonly.super_.call(this, 'sendonly', options, callback);\n}\ninherits(WebRtcPeerSendonly, WebRtcPeer);\nfunction WebRtcPeerSendrecv(options, callback) {\n if (!(this instanceof WebRtcPeerSendrecv)) {\n return new WebRtcPeerSendrecv(options, callback);\n }\n WebRtcPeerSendrecv.super_.call(this, 'sendrecv', options, callback);\n}\ninherits(WebRtcPeerSendrecv, WebRtcPeer);\nexports.bufferizeCandidates = bufferizeCandidates;\nexports.WebRtcPeerRecvonly = WebRtcPeerRecvonly;\nexports.WebRtcPeerSendonly = WebRtcPeerSendonly;\nexports.WebRtcPeerSendrecv = WebRtcPeerSendrecv;","// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nfunction EventEmitter() {\n this._events = this._events || {};\n this._maxListeners = this._maxListeners || undefined;\n}\nmodule.exports = EventEmitter;\n\n// Backwards-compat with node 0.10.x\nEventEmitter.EventEmitter = EventEmitter;\n\nEventEmitter.prototype._events = undefined;\nEventEmitter.prototype._maxListeners = undefined;\n\n// By default EventEmitters will print a warning if more than 10 listeners are\n// added to it. This is a useful default which helps finding memory leaks.\nEventEmitter.defaultMaxListeners = 10;\n\n// Obviously not all Emitters should be limited to 10. This function allows\n// that to be increased. Set to zero for unlimited.\nEventEmitter.prototype.setMaxListeners = function(n) {\n if (!isNumber(n) || n < 0 || isNaN(n))\n throw TypeError('n must be a positive number');\n this._maxListeners = n;\n return this;\n};\n\nEventEmitter.prototype.emit = function(type) {\n var er, handler, len, args, i, listeners;\n\n if (!this._events)\n this._events = {};\n\n // If there is no 'error' event listener then throw.\n if (type === 'error') {\n if (!this._events.error ||\n (isObject(this._events.error) && !this._events.error.length)) {\n er = arguments[1];\n if (er instanceof Error) {\n throw er; // Unhandled 'error' event\n }\n throw TypeError('Uncaught, unspecified \"error\" event.');\n }\n }\n\n handler = this._events[type];\n\n if (isUndefined(handler))\n return false;\n\n if (isFunction(handler)) {\n switch (arguments.length) {\n // fast cases\n case 1:\n handler.call(this);\n break;\n case 2:\n handler.call(this, arguments[1]);\n break;\n case 3:\n handler.call(this, arguments[1], arguments[2]);\n break;\n // slower\n default:\n len = arguments.length;\n args = new Array(len - 1);\n for (i = 1; i < len; i++)\n args[i - 1] = arguments[i];\n handler.apply(this, args);\n }\n } else if (isObject(handler)) {\n len = arguments.length;\n args = new Array(len - 1);\n for (i = 1; i < len; i++)\n args[i - 1] = arguments[i];\n\n listeners = handler.slice();\n len = listeners.length;\n for (i = 0; i < len; i++)\n listeners[i].apply(this, args);\n }\n\n return true;\n};\n\nEventEmitter.prototype.addListener = function(type, listener) {\n var m;\n\n if (!isFunction(listener))\n throw TypeError('listener must be a function');\n\n if (!this._events)\n this._events = {};\n\n // To avoid recursion in the case that type === \"newListener\"! Before\n // adding it to the listeners, first emit \"newListener\".\n if (this._events.newListener)\n this.emit('newListener', type,\n isFunction(listener.listener) ?\n listener.listener : listener);\n\n if (!this._events[type])\n // Optimize the case of one listener. Don't need the extra array object.\n this._events[type] = listener;\n else if (isObject(this._events[type]))\n // If we've already got an array, just append.\n this._events[type].push(listener);\n else\n // Adding the second element, need to change to array.\n this._events[type] = [this._events[type], listener];\n\n // Check for listener leak\n if (isObject(this._events[type]) && !this._events[type].warned) {\n var m;\n if (!isUndefined(this._maxListeners)) {\n m = this._maxListeners;\n } else {\n m = EventEmitter.defaultMaxListeners;\n }\n\n if (m && m > 0 && this._events[type].length > m) {\n this._events[type].warned = true;\n console.error('(node) warning: possible EventEmitter memory ' +\n 'leak detected. %d listeners added. ' +\n 'Use emitter.setMaxListeners() to increase limit.',\n this._events[type].length);\n if (typeof console.trace === 'function') {\n // not supported in IE 10\n console.trace();\n }\n }\n }\n\n return this;\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.once = function(type, listener) {\n if (!isFunction(listener))\n throw TypeError('listener must be a function');\n\n var fired = false;\n\n function g() {\n this.removeListener(type, g);\n\n if (!fired) {\n fired = true;\n listener.apply(this, arguments);\n }\n }\n\n g.listener = listener;\n this.on(type, g);\n\n return this;\n};\n\n// emits a 'removeListener' event iff the listener was removed\nEventEmitter.prototype.removeListener = function(type, listener) {\n var list, position, length, i;\n\n if (!isFunction(listener))\n throw TypeError('listener must be a function');\n\n if (!this._events || !this._events[type])\n return this;\n\n list = this._events[type];\n length = list.length;\n position = -1;\n\n if (list === listener ||\n (isFunction(list.listener) && list.listener === listener)) {\n delete this._events[type];\n if (this._events.removeListener)\n this.emit('removeListener', type, listener);\n\n } else if (isObject(list)) {\n for (i = length; i-- > 0;) {\n if (list[i] === listener ||\n (list[i].listener && list[i].listener === listener)) {\n position = i;\n break;\n }\n }\n\n if (position < 0)\n return this;\n\n if (list.length === 1) {\n list.length = 0;\n delete this._events[type];\n } else {\n list.splice(position, 1);\n }\n\n if (this._events.removeListener)\n this.emit('removeListener', type, listener);\n }\n\n return this;\n};\n\nEventEmitter.prototype.removeAllListeners = function(type) {\n var key, listeners;\n\n if (!this._events)\n return this;\n\n // not listening for removeListener, no need to emit\n if (!this._events.removeListener) {\n if (arguments.length === 0)\n this._events = {};\n else if (this._events[type])\n delete this._events[type];\n return this;\n }\n\n // emit removeListener for all listeners on all events\n if (arguments.length === 0) {\n for (key in this._events) {\n if (key === 'removeListener') continue;\n this.removeAllListeners(key);\n }\n this.removeAllListeners('removeListener');\n this._events = {};\n return this;\n }\n\n listeners = this._events[type];\n\n if (isFunction(listeners)) {\n this.removeListener(type, listeners);\n } else {\n // LIFO order\n while (listeners.length)\n this.removeListener(type, listeners[listeners.length - 1]);\n }\n delete this._events[type];\n\n return this;\n};\n\nEventEmitter.prototype.listeners = function(type) {\n var ret;\n if (!this._events || !this._events[type])\n ret = [];\n else if (isFunction(this._events[type]))\n ret = [this._events[type]];\n else\n ret = this._events[type].slice();\n return ret;\n};\n\nEventEmitter.listenerCount = function(emitter, type) {\n var ret;\n if (!emitter._events || !emitter._events[type])\n ret = 0;\n else if (isFunction(emitter._events[type]))\n ret = 1;\n else\n ret = emitter._events[type].length;\n return ret;\n};\n\nfunction isFunction(arg) {\n return typeof arg === 'function';\n}\n\nfunction isNumber(arg) {\n return typeof arg === 'number';\n}\n\nfunction isObject(arg) {\n return typeof arg === 'object' && arg !== null;\n}\n\nfunction isUndefined(arg) {\n return arg === void 0;\n}\n","/* jshint node: true */\n'use strict';\n\nvar normalice = require('normalice');\n\n/**\n # freeice\n\n The `freeice` module is a simple way of getting random STUN or TURN server\n for your WebRTC application. The list of servers (just STUN at this stage)\n were sourced from this [gist](https://gist.github.com/zziuni/3741933).\n\n ## Example Use\n\n The following demonstrates how you can use `freeice` with\n [rtc-quickconnect](https://github.com/rtc-io/rtc-quickconnect):\n\n <<< examples/quickconnect.js\n\n As the `freeice` module generates ice servers in a list compliant with the\n WebRTC spec you will be able to use it with raw `RTCPeerConnection`\n constructors and other WebRTC libraries.\n\n ## Hey, don't use my STUN/TURN server!\n\n If for some reason your free STUN or TURN server ends up in the\n list of servers ([stun](https://github.com/DamonOehlman/freeice/blob/master/stun.json) or\n [turn](https://github.com/DamonOehlman/freeice/blob/master/turn.json))\n that is used in this module, you can feel\n free to open an issue on this repository and those servers will be removed\n within 24 hours (or sooner). This is the quickest and probably the most\n polite way to have something removed (and provides us some visibility\n if someone opens a pull request requesting that a server is added).\n\n ## Please add my server!\n\n If you have a server that you wish to add to the list, that's awesome! I'm\n sure I speak on behalf of a whole pile of WebRTC developers who say thanks.\n To get it into the list, feel free to either open a pull request or if you\n find that process a bit daunting then just create an issue requesting\n the addition of the server (make sure you provide all the details, and if\n you have a Terms of Service then including that in the PR/issue would be\n awesome).\n\n ## I know of a free server, can I add it?\n\n Sure, if you do your homework and make sure it is ok to use (I'm currently\n in the process of reviewing the terms of those STUN servers included from\n the original list). If it's ok to go, then please see the previous entry\n for how to add it.\n\n ## Current List of Servers\n\n * current as at the time of last `README.md` file generation\n\n ### STUN\n\n <<< stun.json\n\n ### TURN\n\n <<< turn.json\n\n**/\n\nvar freeice = module.exports = function(opts) {\n // if a list of servers has been provided, then use it instead of defaults\n var servers = {\n stun: (opts || {}).stun || require('./stun.json'),\n turn: (opts || {}).turn || require('./turn.json')\n };\n\n var stunCount = (opts || {}).stunCount || 2;\n var turnCount = (opts || {}).turnCount || 0;\n var selected;\n\n function getServers(type, count) {\n var out = [];\n var input = [].concat(servers[type]);\n var idx;\n\n while (input.length && out.length < count) {\n idx = (Math.random() * input.length) | 0;\n out = out.concat(input.splice(idx, 1));\n }\n\n return out.map(function(url) {\n //If it's a not a string, don't try to \"normalice\" it otherwise using type:url will screw it up\n if ((typeof url !== 'string') && (! (url instanceof String))) {\n return url;\n } else {\n return normalice(type + ':' + url);\n }\n });\n }\n\n // add stun servers\n selected = [].concat(getServers('stun', stunCount));\n\n if (turnCount) {\n selected = selected.concat(getServers('turn', turnCount));\n }\n\n return selected;\n};\n","if (typeof Object.create === 'function') {\n // implementation from standard node.js 'util' module\n module.exports = function inherits(ctor, superCtor) {\n ctor.super_ = superCtor\n ctor.prototype = Object.create(superCtor.prototype, {\n constructor: {\n value: ctor,\n enumerable: false,\n writable: true,\n configurable: true\n }\n });\n };\n} else {\n // old school shim for old browsers\n module.exports = function inherits(ctor, superCtor) {\n ctor.super_ = superCtor\n var TempCtor = function () {}\n TempCtor.prototype = superCtor.prototype\n ctor.prototype = new TempCtor()\n ctor.prototype.constructor = ctor\n }\n}\n","/*!\r\n * @name JavaScript/NodeJS Merge v1.2.0\r\n * @author yeikos\r\n * @repository https://github.com/yeikos/js.merge\r\n\r\n * Copyright 2014 yeikos - MIT license\r\n * https://raw.github.com/yeikos/js.merge/master/LICENSE\r\n */\r\n\r\n;(function(isNode) {\r\n\r\n\t/**\r\n\t * Merge one or more objects \r\n\t * @param bool? clone\r\n\t * @param mixed,... arguments\r\n\t * @return object\r\n\t */\r\n\r\n\tvar Public = function(clone) {\r\n\r\n\t\treturn merge(clone === true, false, arguments);\r\n\r\n\t}, publicName = 'merge';\r\n\r\n\t/**\r\n\t * Merge two or more objects recursively \r\n\t * @param bool? clone\r\n\t * @param mixed,... arguments\r\n\t * @return object\r\n\t */\r\n\r\n\tPublic.recursive = function(clone) {\r\n\r\n\t\treturn merge(clone === true, true, arguments);\r\n\r\n\t};\r\n\r\n\t/**\r\n\t * Clone the input removing any reference\r\n\t * @param mixed input\r\n\t * @return mixed\r\n\t */\r\n\r\n\tPublic.clone = function(input) {\r\n\r\n\t\tvar output = input,\r\n\t\t\ttype = typeOf(input),\r\n\t\t\tindex, size;\r\n\r\n\t\tif (type === 'array') {\r\n\r\n\t\t\toutput = [];\r\n\t\t\tsize = input.length;\r\n\r\n\t\t\tfor (index=0;index<size;++index)\r\n\r\n\t\t\t\toutput[index] = Public.clone(input[index]);\r\n\r\n\t\t} else if (type === 'object') {\r\n\r\n\t\t\toutput = {};\r\n\r\n\t\t\tfor (index in input)\r\n\r\n\t\t\t\toutput[index] = Public.clone(input[index]);\r\n\r\n\t\t}\r\n\r\n\t\treturn output;\r\n\r\n\t};\r\n\r\n\t/**\r\n\t * Merge two objects recursively\r\n\t * @param mixed input\r\n\t * @param mixed extend\r\n\t * @return mixed\r\n\t */\r\n\r\n\tfunction merge_recursive(base, extend) {\r\n\r\n\t\tif (typeOf(base) !== 'object')\r\n\r\n\t\t\treturn extend;\r\n\r\n\t\tfor (var key in extend) {\r\n\r\n\t\t\tif (typeOf(base[key]) === 'object' && typeOf(extend[key]) === 'object') {\r\n\r\n\t\t\t\tbase[key] = merge_recursive(base[key], extend[key]);\r\n\r\n\t\t\t} else {\r\n\r\n\t\t\t\tbase[key] = extend[key];\r\n\r\n\t\t\t}\r\n\r\n\t\t}\r\n\r\n\t\treturn base;\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Merge two or more objects\r\n\t * @param bool clone\r\n\t * @param bool recursive\r\n\t * @param array argv\r\n\t * @return object\r\n\t */\r\n\r\n\tfunction merge(clone, recursive, argv) {\r\n\r\n\t\tvar result = argv[0],\r\n\t\t\tsize = argv.length;\r\n\r\n\t\tif (clone || typeOf(result) !== 'object')\r\n\r\n\t\t\tresult = {};\r\n\r\n\t\tfor (var index=0;index<size;++index) {\r\n\r\n\t\t\tvar item = argv[index],\r\n\r\n\t\t\t\ttype = typeOf(item);\r\n\r\n\t\t\tif (type !== 'object') continue;\r\n\r\n\t\t\tfor (var key in item) {\r\n\r\n\t\t\t\tvar sitem = clone ? Public.clone(item[key]) : item[key];\r\n\r\n\t\t\t\tif (recursive) {\r\n\r\n\t\t\t\t\tresult[key] = merge_recursive(result[key], sitem);\r\n\r\n\t\t\t\t} else {\r\n\r\n\t\t\t\t\tresult[key] = sitem;\r\n\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\r\n\t\t}\r\n\r\n\t\treturn result;\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Get type of variable\r\n\t * @param mixed input\r\n\t * @return string\r\n\t *\r\n\t * @see http://jsperf.com/typeofvar\r\n\t */\r\n\r\n\tfunction typeOf(input) {\r\n\r\n\t\treturn ({}).toString.call(input).slice(8, -1).toLowerCase();\r\n\r\n\t}\r\n\r\n\tif (isNode) {\r\n\r\n\t\tmodule.exports = Public;\r\n\r\n\t} else {\r\n\r\n\t\twindow[publicName] = Public;\r\n\r\n\t}\r\n\r\n})(typeof module === 'object' && module && typeof module.exports === 'object' && module.exports);","// uuid.js\n//\n// Copyright (c) 2010-2012 Robert Kieffer\n// MIT License - http://opensource.org/licenses/mit-license.php\n\n// Unique ID creation requires a high quality random # generator. We feature\n// detect to determine the best RNG source, normalizing to a function that\n// returns 128-bits of randomness, since that's what's usually required\nvar _rng = require('./rng');\n\n// Maps for number <-> hex string conversion\nvar _byteToHex = [];\nvar _hexToByte = {};\nfor (var i = 0; i < 256; i++) {\n _byteToHex[i] = (i + 0x100).toString(16).substr(1);\n _hexToByte[_byteToHex[i]] = i;\n}\n\n// **`parse()` - Parse a UUID into it's component bytes**\nfunction parse(s, buf, offset) {\n var i = (buf && offset) || 0, ii = 0;\n\n buf = buf || [];\n s.toLowerCase().replace(/[0-9a-f]{2}/g, function(oct) {\n if (ii < 16) { // Don't overflow!\n buf[i + ii++] = _hexToByte[oct];\n }\n });\n\n // Zero out remaining bytes if string was short\n while (ii < 16) {\n buf[i + ii++] = 0;\n }\n\n return buf;\n}\n\n// **`unparse()` - Convert UUID byte array (ala parse()) into a string**\nfunction unparse(buf, offset) {\n var i = offset || 0, bth = _byteToHex;\n return bth[buf[i++]] + bth[buf[i++]] +\n bth[buf[i++]] + bth[buf[i++]] + '-' +\n bth[buf[i++]] + bth[buf[i++]] + '-' +\n bth[buf[i++]] + bth[buf[i++]] + '-' +\n bth[buf[i++]] + bth[buf[i++]] + '-' +\n bth[buf[i++]] + bth[buf[i++]] +\n bth[buf[i++]] + bth[buf[i++]] +\n bth[buf[i++]] + bth[buf[i++]];\n}\n\n// **`v1()` - Generate time-based UUID**\n//\n// Inspired by https://github.com/LiosK/UUID.js\n// and http://docs.python.org/library/uuid.html\n\n// random #'s we need to init node and clockseq\nvar _seedBytes = _rng();\n\n// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)\nvar _nodeId = [\n _seedBytes[0] | 0x01,\n _seedBytes[1], _seedBytes[2], _seedBytes[3], _seedBytes[4], _seedBytes[5]\n];\n\n// Per 4.2.2, randomize (14 bit) clockseq\nvar _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;\n\n// Previous uuid creation time\nvar _lastMSecs = 0, _lastNSecs = 0;\n\n// See https://github.com/broofa/node-uuid for API details\nfunction v1(options, buf, offset) {\n var i = buf && offset || 0;\n var b = buf || [];\n\n options = options || {};\n\n var clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq;\n\n // UUID timestamps are 100 nano-second units since the Gregorian epoch,\n // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so\n // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'\n // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.\n var msecs = options.msecs !== undefined ? options.msecs : new Date().getTime();\n\n // Per 4.2.1.2, use count of uuid's generated during the current clock\n // cycle to simulate higher resolution clock\n var nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1;\n\n // Time since last uuid creation (in msecs)\n var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs)/10000;\n\n // Per 4.2.1.2, Bump clockseq on clock regression\n if (dt < 0 && options.clockseq === undefined) {\n clockseq = clockseq + 1 & 0x3fff;\n }\n\n // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new\n // time interval\n if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {\n nsecs = 0;\n }\n\n // Per 4.2.1.2 Throw error if too many uuids are requested\n if (nsecs >= 10000) {\n throw new Error('uuid.v1(): Can\\'t create more than 10M uuids/sec');\n }\n\n _lastMSecs = msecs;\n _lastNSecs = nsecs;\n _clockseq = clockseq;\n\n // Per 4.1.4 - Convert from unix epoch to Gregorian epoch\n msecs += 12219292800000;\n\n // `time_low`\n var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;\n b[i++] = tl >>> 24 & 0xff;\n b[i++] = tl >>> 16 & 0xff;\n b[i++] = tl >>> 8 & 0xff;\n b[i++] = tl & 0xff;\n\n // `time_mid`\n var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;\n b[i++] = tmh >>> 8 & 0xff;\n b[i++] = tmh & 0xff;\n\n // `time_high_and_version`\n b[i++] = tmh >>> 24 & 0xf | 0x10; // include version\n b[i++] = tmh >>> 16 & 0xff;\n\n // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)\n b[i++] = clockseq >>> 8 | 0x80;\n\n // `clock_seq_low`\n b[i++] = clockseq & 0xff;\n\n // `node`\n var node = options.node || _nodeId;\n for (var n = 0; n < 6; n++) {\n b[i + n] = node[n];\n }\n\n return buf ? buf : unparse(b);\n}\n\n// **`v4()` - Generate random UUID**\n\n// See https://github.com/broofa/node-uuid for API details\nfunction v4(options, buf, offset) {\n // Deprecated - 'format' argument, as supported in v1.2\n var i = buf && offset || 0;\n\n if (typeof(options) == 'string') {\n buf = options == 'binary' ? new Array(16) : null;\n options = null;\n }\n options = options || {};\n\n var rnds = options.random || (options.rng || _rng)();\n\n // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`\n rnds[6] = (rnds[6] & 0x0f) | 0x40;\n rnds[8] = (rnds[8] & 0x3f) | 0x80;\n\n // Copy bytes to buffer, if provided\n if (buf) {\n for (var ii = 0; ii < 16; ii++) {\n buf[i + ii] = rnds[ii];\n }\n }\n\n return buf || unparse(rnds);\n}\n\n// Export public API\nvar uuid = v4;\nuuid.v1 = v1;\nuuid.v4 = v4;\nuuid.parse = parse;\nuuid.unparse = unparse;\n\nmodule.exports = uuid;\n","/**\n * UAParser.js v0.7.9\n * Lightweight JavaScript-based User-Agent string parser\n * https://github.com/faisalman/ua-parser-js\n *\n * Copyright © 2012-2015 Faisal Salman <fyzlman@gmail.com>\n * Dual licensed under GPLv2 & MIT\n */\n\n(function (window, undefined) {\n\n 'use strict';\n\n //////////////\n // Constants\n /////////////\n\n\n var LIBVERSION = '0.7.9',\n EMPTY = '',\n UNKNOWN = '?',\n FUNC_TYPE = 'function',\n UNDEF_TYPE = 'undefined',\n OBJ_TYPE = 'object',\n STR_TYPE = 'string',\n MAJOR = 'major', // deprecated\n MODEL = 'model',\n NAME = 'name',\n TYPE = 'type',\n VENDOR = 'vendor',\n VERSION = 'version',\n ARCHITECTURE= 'architecture',\n CONSOLE = 'console',\n MOBILE = 'mobile',\n TABLET = 'tablet',\n SMARTTV = 'smarttv',\n WEARABLE = 'wearable',\n EMBEDDED = 'embedded';\n\n\n ///////////\n // Helper\n //////////\n\n\n var util = {\n extend : function (regexes, extensions) {\n for (var i in extensions) {\n if (\"browser cpu device engine os\".indexOf(i) !== -1 && extensions[i].length % 2 === 0) {\n regexes[i] = extensions[i].concat(regexes[i]);\n }\n }\n return regexes;\n },\n has : function (str1, str2) {\n if (typeof str1 === \"string\") {\n return str2.toLowerCase().indexOf(str1.toLowerCase()) !== -1;\n } else {\n return false;\n }\n },\n lowerize : function (str) {\n return str.toLowerCase();\n },\n major : function (version) {\n return typeof(version) === STR_TYPE ? version.split(\".\")[0] : undefined;\n }\n };\n\n\n ///////////////\n // Map helper\n //////////////\n\n\n var mapper = {\n\n rgx : function () {\n\n var result, i = 0, j, k, p, q, matches, match, args = arguments;\n\n // loop through all regexes maps\n while (i < args.length && !matches) {\n\n var regex = args[i], // even sequence (0,2,4,..)\n props = args[i + 1]; // odd sequence (1,3,5,..)\n\n // construct object barebones\n if (typeof result === UNDEF_TYPE) {\n result = {};\n for (p in props) {\n q = props[p];\n if (typeof q === OBJ_TYPE) {\n result[q[0]] = undefined;\n } else {\n result[q] = undefined;\n }\n }\n }\n\n // try matching uastring with regexes\n j = k = 0;\n while (j < regex.length && !matches) {\n matches = regex[j++].exec(this.getUA());\n if (!!matches) {\n for (p = 0; p < props.length; p++) {\n match = matches[++k];\n q = props[p];\n // check if given property is actually array\n if (typeof q === OBJ_TYPE && q.length > 0) {\n if (q.length == 2) {\n if (typeof q[1] == FUNC_TYPE) {\n // assign modified match\n result[q[0]] = q[1].call(this, match);\n } else {\n // assign given value, ignore regex match\n result[q[0]] = q[1];\n }\n } else if (q.length == 3) {\n // check whether function or regex\n if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) {\n // call function (usually string mapper)\n result[q[0]] = match ? q[1].call(this, match, q[2]) : undefined;\n } else {\n // sanitize match using given regex\n result[q[0]] = match ? match.replace(q[1], q[2]) : undefined;\n }\n } else if (q.length == 4) {\n result[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined;\n }\n } else {\n result[q] = match ? match : undefined;\n }\n }\n }\n }\n i += 2;\n }\n return result;\n },\n\n str : function (str, map) {\n\n for (var i in map) {\n // check if array\n if (typeof map[i] === OBJ_TYPE && map[i].length > 0) {\n for (var j = 0; j < map[i].length; j++) {\n if (util.has(map[i][j], str)) {\n return (i === UNKNOWN) ? undefined : i;\n }\n }\n } else if (util.has(map[i], str)) {\n return (i === UNKNOWN) ? undefined : i;\n }\n }\n return str;\n }\n };\n\n\n ///////////////\n // String map\n //////////////\n\n\n var maps = {\n\n browser : {\n oldsafari : {\n version : {\n '1.0' : '/8',\n '1.2' : '/1',\n '1.3' : '/3',\n '2.0' : '/412',\n '2.0.2' : '/416',\n '2.0.3' : '/417',\n '2.0.4' : '/419',\n '?' : '/'\n }\n }\n },\n\n device : {\n amazon : {\n model : {\n 'Fire Phone' : ['SD', 'KF']\n }\n },\n sprint : {\n model : {\n 'Evo Shift 4G' : '7373KT'\n },\n vendor : {\n 'HTC' : 'APA',\n 'Sprint' : 'Sprint'\n }\n }\n },\n\n os : {\n windows : {\n version : {\n 'ME' : '4.90',\n 'NT 3.11' : 'NT3.51',\n 'NT 4.0' : 'NT4.0',\n '2000' : 'NT 5.0',\n 'XP' : ['NT 5.1', 'NT 5.2'],\n 'Vista' : 'NT 6.0',\n '7' : 'NT 6.1',\n '8' : 'NT 6.2',\n '8.1' : 'NT 6.3',\n '10' : ['NT 6.4', 'NT 10.0'],\n 'RT' : 'ARM'\n }\n }\n }\n };\n\n\n //////////////\n // Regex map\n /////////////\n\n\n var regexes = {\n\n browser : [[\n\n // Presto based\n /(opera\\smini)\\/([\\w\\.-]+)/i, // Opera Mini\n /(opera\\s[mobiletab]+).+version\\/([\\w\\.-]+)/i, // Opera Mobi/Tablet\n /(opera).+version\\/([\\w\\.]+)/i, // Opera > 9.80\n /(opera)[\\/\\s]+([\\w\\.]+)/i // Opera < 9.80\n\n ], [NAME, VERSION], [\n\n /\\s(opr)\\/([\\w\\.]+)/i // Opera Webkit\n ], [[NAME, 'Opera'], VERSION], [\n\n // Mixed\n /(kindle)\\/([\\w\\.]+)/i, // Kindle\n /(lunascape|maxthon|netfront|jasmine|blazer)[\\/\\s]?([\\w\\.]+)*/i,\n // Lunascape/Maxthon/Netfront/Jasmine/Blazer\n\n // Trident based\n /(avant\\s|iemobile|slim|baidu)(?:browser)?[\\/\\s]?([\\w\\.]*)/i,\n // Avant/IEMobile/SlimBrowser/Baidu\n /(?:ms|\\()(ie)\\s([\\w\\.]+)/i, // Internet Explorer\n\n // Webkit/KHTML based\n /(rekonq)\\/([\\w\\.]+)*/i, // Rekonq\n /(chromium|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium)\\/([\\w\\.-]+)/i\n // Chromium/Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium\n ], [NAME, VERSION], [\n\n /(trident).+rv[:\\s]([\\w\\.]+).+like\\sgecko/i // IE11\n ], [[NAME, 'IE'], VERSION], [\n\n /(edge)\\/((\\d+)?[\\w\\.]+)/i // Microsoft Edge\n ], [NAME, VERSION], [\n\n /(yabrowser)\\/([\\w\\.]+)/i // Yandex\n ], [[NAME, 'Yandex'], VERSION], [\n\n /(comodo_dragon)\\/([\\w\\.]+)/i // Comodo Dragon\n ], [[NAME, /_/g, ' '], VERSION], [\n\n /(chrome|omniweb|arora|[tizenoka]{5}\\s?browser)\\/v?([\\w\\.]+)/i,\n // Chrome/OmniWeb/Arora/Tizen/Nokia\n /(uc\\s?browser|qqbrowser)[\\/\\s]?([\\w\\.]+)/i\n // UCBrowser/QQBrowser\n ], [NAME, VERSION], [\n\n /(dolfin)\\/([\\w\\.]+)/i // Dolphin\n ], [[NAME, 'Dolphin'], VERSION], [\n\n /((?:android.+)crmo|crios)\\/([\\w\\.]+)/i // Chrome for Android/iOS\n ], [[NAME, 'Chrome'], VERSION], [\n\n /XiaoMi\\/MiuiBrowser\\/([\\w\\.]+)/i // MIUI Browser\n ], [VERSION, [NAME, 'MIUI Browser']], [\n\n /android.+version\\/([\\w\\.]+)\\s+(?:mobile\\s?safari|safari)/i // Android Browser\n ], [VERSION, [NAME, 'Android Browser']], [\n\n /FBAV\\/([\\w\\.]+);/i // Facebook App for iOS\n ], [VERSION, [NAME, 'Facebook']], [\n\n /version\\/([\\w\\.]+).+?mobile\\/\\w+\\s(safari)/i // Mobile Safari\n ], [VERSION, [NAME, 'Mobile Safari']], [\n\n /version\\/([\\w\\.]+).+?(mobile\\s?safari|safari)/i // Safari & Safari Mobile\n ], [VERSION, NAME], [\n\n /webkit.+?(mobile\\s?safari|safari)(\\/[\\w\\.]+)/i // Safari < 3.0\n ], [NAME, [VERSION, mapper.str, maps.browser.oldsafari.version]], [\n\n /(konqueror)\\/([\\w\\.]+)/i, // Konqueror\n /(webkit|khtml)\\/([\\w\\.]+)/i\n ], [NAME, VERSION], [\n\n // Gecko based\n /(navigator|netscape)\\/([\\w\\.-]+)/i // Netscape\n ], [[NAME, 'Netscape'], VERSION], [\n /fxios\\/([\\w\\.-]+)/i // Firefox for iOS\n ], [VERSION, [NAME, 'Firefox']], [\n /(swiftfox)/i, // Swiftfox\n /(icedragon|iceweasel|camino|chimera|fennec|maemo\\sbrowser|minimo|conkeror)[\\/\\s]?([\\w\\.\\+]+)/i,\n // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror\n /(firefox|seamonkey|k-meleon|icecat|iceape|firebird|phoenix)\\/([\\w\\.-]+)/i,\n // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix\n /(mozilla)\\/([\\w\\.]+).+rv\\:.+gecko\\/\\d+/i, // Mozilla\n\n // Other\n /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf)[\\/\\s]?([\\w\\.]+)/i,\n // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf\n /(links)\\s\\(([\\w\\.]+)/i, // Links\n /(gobrowser)\\/?([\\w\\.]+)*/i, // GoBrowser\n /(ice\\s?browser)\\/v?([\\w\\._]+)/i, // ICE Browser\n /(mosaic)[\\/\\s]([\\w\\.]+)/i // Mosaic\n ], [NAME, VERSION]\n\n /* /////////////////////\n // Media players BEGIN\n ////////////////////////\n\n , [\n\n /(apple(?:coremedia|))\\/((\\d+)[\\w\\._]+)/i, // Generic Apple CoreMedia\n /(coremedia) v((\\d+)[\\w\\._]+)/i\n ], [NAME, VERSION], [\n\n /(aqualung|lyssna|bsplayer)\\/((\\d+)?[\\w\\.-]+)/i // Aqualung/Lyssna/BSPlayer\n ], [NAME, VERSION], [\n\n /(ares|ossproxy)\\s((\\d+)[\\w\\.-]+)/i // Ares/OSSProxy\n ], [NAME, VERSION], [\n\n /(audacious|audimusicstream|amarok|bass|core|dalvik|gnomemplayer|music on console|nsplayer|psp-internetradioplayer|videos)\\/((\\d+)[\\w\\.-]+)/i,\n // Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/Dalvik/GnomeMplayer/MoC\n // NSPlayer/PSP-InternetRadioPlayer/Videos\n /(clementine|music player daemon)\\s((\\d+)[\\w\\.-]+)/i, // Clementine/MPD\n /(lg player|nexplayer)\\s((\\d+)[\\d\\.]+)/i,\n /player\\/(nexplayer|lg player)\\s((\\d+)[\\w\\.-]+)/i // NexPlayer/LG Player\n ], [NAME, VERSION], [\n /(nexplayer)\\s((\\d+)[\\w\\.-]+)/i // Nexplayer\n ], [NAME, VERSION], [\n\n /(flrp)\\/((\\d+)[\\w\\.-]+)/i // Flip Player\n ], [[NAME, 'Flip Player'], VERSION], [\n\n /(fstream|nativehost|queryseekspider|ia-archiver|facebookexternalhit)/i\n // FStream/NativeHost/QuerySeekSpider/IA Archiver/facebookexternalhit\n ], [NAME], [\n\n /(gstreamer) souphttpsrc (?:\\([^\\)]+\\)){0,1} libsoup\\/((\\d+)[\\w\\.-]+)/i\n // Gstreamer\n ], [NAME, VERSION], [\n\n /(htc streaming player)\\s[\\w_]+\\s\\/\\s((\\d+)[\\d\\.]+)/i, // HTC Streaming Player\n /(java|python-urllib|python-requests|wget|libcurl)\\/((\\d+)[\\w\\.-_]+)/i,\n // Java/urllib/requests/wget/cURL\n /(lavf)((\\d+)[\\d\\.]+)/i // Lavf (FFMPEG)\n ], [NAME, VERSION], [\n\n /(htc_one_s)\\/((\\d+)[\\d\\.]+)/i // HTC One S\n ], [[NAME, /_/g, ' '], VERSION], [\n\n /(mplayer)(?:\\s|\\/)(?:(?:sherpya-){0,1}svn)(?:-|\\s)(r\\d+(?:-\\d+[\\w\\.-]+){0,1})/i\n // MPlayer SVN\n ], [NAME, VERSION], [\n\n /(mplayer)(?:\\s|\\/|[unkow-]+)((\\d+)[\\w\\.-]+)/i // MPlayer\n ], [NAME, VERSION], [\n\n /(mplayer)/i, // MPlayer (no other info)\n /(yourmuze)/i, // YourMuze\n /(media player classic|nero showtime)/i // Media Player Classic/Nero ShowTime\n ], [NAME], [\n\n /(nero (?:home|scout))\\/((\\d+)[\\w\\.-]+)/i // Nero Home/Nero Scout\n ], [NAME, VERSION], [\n\n /(nokia\\d+)\\/((\\d+)[\\w\\.-]+)/i // Nokia\n ], [NAME, VERSION], [\n\n /\\s(songbird)\\/((\\d+)[\\w\\.-]+)/i // Songbird/Philips-Songbird\n ], [NAME, VERSION], [\n\n /(winamp)3 version ((\\d+)[\\w\\.-]+)/i, // Winamp\n /(winamp)\\s((\\d+)[\\w\\.-]+)/i,\n /(winamp)mpeg\\/((\\d+)[\\w\\.-]+)/i\n ], [NAME, VERSION], [\n\n /(ocms-bot|tapinradio|tunein radio|unknown|winamp|inlight radio)/i // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info)\n // inlight radio\n ], [NAME], [\n\n /(quicktime|rma|radioapp|radioclientapplication|soundtap|totem|stagefright|streamium)\\/((\\d+)[\\w\\.-]+)/i\n // QuickTime/RealMedia/RadioApp/RadioClientApplication/\n // SoundTap/Totem/Stagefright/Streamium\n ], [NAME, VERSION], [\n\n /(smp)((\\d+)[\\d\\.]+)/i // SMP\n ], [NAME, VERSION], [\n\n /(vlc) media player - version ((\\d+)[\\w\\.]+)/i, // VLC Videolan\n /(vlc)\\/((\\d+)[\\w\\.-]+)/i,\n /(xbmc|gvfs|xine|xmms|irapp)\\/((\\d+)[\\w\\.-]+)/i, // XBMC/gvfs/Xine/XMMS/irapp\n /(foobar2000)\\/((\\d+)[\\d\\.]+)/i, // Foobar2000\n /(itunes)\\/((\\d+)[\\d\\.]+)/i // iTunes\n ], [NAME, VERSION], [\n\n /(wmplayer)\\/((\\d+)[\\w\\.-]+)/i, // Windows Media Player\n /(windows-media-player)\\/((\\d+)[\\w\\.-]+)/i\n ], [[NAME, /-/g, ' '], VERSION], [\n\n /windows\\/((\\d+)[\\w\\.-]+) upnp\\/[\\d\\.]+ dlnadoc\\/[\\d\\.]+ (home media server)/i\n // Windows Media Server\n ], [VERSION, [NAME, 'Windows']], [\n\n /(com\\.riseupradioalarm)\\/((\\d+)[\\d\\.]*)/i // RiseUP Radio Alarm\n ], [NAME, VERSION], [\n\n /(rad.io)\\s((\\d+)[\\d\\.]+)/i, // Rad.io\n /(radio.(?:de|at|fr))\\s((\\d+)[\\d\\.]+)/i\n ], [[NAME, 'rad.io'], VERSION]\n\n //////////////////////\n // Media players END\n ////////////////////*/\n\n ],\n\n cpu : [[\n\n /(?:(amd|x(?:(?:86|64)[_-])?|wow|win)64)[;\\)]/i // AMD64\n ], [[ARCHITECTURE, 'amd64']], [\n\n /(ia32(?=;))/i // IA32 (quicktime)\n ], [[ARCHITECTURE, util.lowerize]], [\n\n /((?:i[346]|x)86)[;\\)]/i // IA32\n ], [[ARCHITECTURE, 'ia32']], [\n\n // PocketPC mistakenly identified as PowerPC\n /windows\\s(ce|mobile);\\sppc;/i\n ], [[ARCHITECTURE, 'arm']], [\n\n /((?:ppc|powerpc)(?:64)?)(?:\\smac|;|\\))/i // PowerPC\n ], [[ARCHITECTURE, /ower/, '', util.lowerize]], [\n\n /(sun4\\w)[;\\)]/i // SPARC\n ], [[ARCHITECTURE, 'sparc']], [\n\n /((?:avr32|ia64(?=;))|68k(?=\\))|arm(?:64|(?=v\\d+;))|(?=atmel\\s)avr|(?:irix|mips|sparc)(?:64)?(?=;)|pa-risc)/i\n // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC\n ], [[ARCHITECTURE, util.lowerize]]\n ],\n\n device : [[\n\n /\\((ipad|playbook);[\\w\\s\\);-]+(rim|apple)/i // iPad/PlayBook\n ], [MODEL, VENDOR, [TYPE, TABLET]], [\n\n /applecoremedia\\/[\\w\\.]+ \\((ipad)/ // iPad\n ], [MODEL, [VENDOR, 'Apple'], [TYPE, TABLET]], [\n\n /(apple\\s{0,1}tv)/i // Apple TV\n ], [[MODEL, 'Apple TV'], [VENDOR, 'Apple']], [\n\n /(archos)\\s(gamepad2?)/i, // Archos\n /(hp).+(touchpad)/i, // HP TouchPad\n /(kindle)\\/([\\w\\.]+)/i, // Kindle\n /\\s(nook)[\\w\\s]+build\\/(\\w+)/i, // Nook\n /(dell)\\s(strea[kpr\\s\\d]*[\\dko])/i // Dell Streak\n ], [VENDOR, MODEL, [TYPE, TABLET]], [\n\n /(kf[A-z]+)\\sbuild\\/[\\w\\.]+.*silk\\//i // Kindle Fire HD\n ], [MODEL, [VENDOR, 'Amazon'], [TYPE, TABLET]], [\n /(sd|kf)[0349hijorstuw]+\\sbuild\\/[\\w\\.]+.*silk\\//i // Fire Phone\n ], [[MODEL, mapper.str, maps.device.amazon.model], [VENDOR, 'Amazon'], [TYPE, MOBILE]], [\n\n /\\((ip[honed|\\s\\w*]+);.+(apple)/i // iPod/iPhone\n ], [MODEL, VENDOR, [TYPE, MOBILE]], [\n /\\((ip[honed|\\s\\w*]+);/i // iPod/iPhone\n ], [MODEL, [VENDOR, 'Apple'], [TYPE, MOBILE]], [\n\n /(blackberry)[\\s-]?(\\w+)/i, // BlackBerry\n /(blackberry|benq|palm(?=\\-)|sonyericsson|acer|asus|dell|huawei|meizu|motorola|polytron)[\\s_-]?([\\w-]+)*/i,\n // BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Huawei/Meizu/Motorola/Polytron\n /(hp)\\s([\\w\\s]+\\w)/i, // HP iPAQ\n /(asus)-?(\\w+)/i // Asus\n ], [VENDOR, MODEL, [TYPE, MOBILE]], [\n /\\(bb10;\\s(\\w+)/i // BlackBerry 10\n ], [MODEL, [VENDOR, 'BlackBerry'], [TYPE, MOBILE]], [\n // Asus Tablets\n /android.+(transfo[prime\\s]{4,10}\\s\\w+|eeepc|slider\\s\\w+|nexus 7)/i\n ], [MODEL, [VENDOR, 'Asus'], [TYPE, TABLET]], [\n\n /(sony)\\s(tablet\\s[ps])\\sbuild\\//i, // Sony\n /(sony)?(?:sgp.+)\\sbuild\\//i\n ], [[VENDOR, 'Sony'], [MODEL, 'Xperia Tablet'], [TYPE, TABLET]], [\n /(?:sony)?(?:(?:(?:c|d)\\d{4})|(?:so[-l].+))\\sbuild\\//i\n ], [[VENDOR, 'Sony'], [MODEL, 'Xperia Phone'], [TYPE, MOBILE]], [\n\n /\\s(ouya)\\s/i, // Ouya\n /(nintendo)\\s([wids3u]+)/i // Nintendo\n ], [VENDOR, MODEL, [TYPE, CONSOLE]], [\n\n /android.+;\\s(shield)\\sbuild/i // Nvidia\n ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, CONSOLE]], [\n\n /(playstation\\s[3portablevi]+)/i // Playstation\n ], [MODEL, [VENDOR, 'Sony'], [TYPE, CONSOLE]], [\n\n /(sprint\\s(\\w+))/i // Sprint Phones\n ], [[VENDOR, mapper.str, maps.device.sprint.vendor], [MODEL, mapper.str, maps.device.sprint.model], [TYPE, MOBILE]], [\n\n /(lenovo)\\s?(S(?:5000|6000)+(?:[-][\\w+]))/i // Lenovo tablets\n ], [VENDOR, MODEL, [TYPE, TABLET]], [\n\n /(htc)[;_\\s-]+([\\w\\s]+(?=\\))|\\w+)*/i, // HTC\n /(zte)-(\\w+)*/i, // ZTE\n /(alcatel|geeksphone|huawei|lenovo|nexian|panasonic|(?=;\\s)sony)[_\\s-]?([\\w-]+)*/i\n // Alcatel/GeeksPhone/Huawei/Lenovo/Nexian/Panasonic/Sony\n ], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [\n \n /(nexus\\s9)/i // HTC Nexus 9\n ], [MODEL, [VENDOR, 'HTC'], [TYPE, TABLET]], [\n\n /[\\s\\(;](xbox(?:\\sone)?)[\\s\\);]/i // Microsoft Xbox\n ], [MODEL, [VENDOR, 'Microsoft'], [TYPE, CONSOLE]], [\n /(kin\\.[onetw]{3})/i // Microsoft Kin\n ], [[MODEL, /\\./g, ' '], [VENDOR, 'Microsoft'], [TYPE, MOBILE]], [\n\n // Motorola\n /\\s(milestone|droid(?:[2-4x]|\\s(?:bionic|x2|pro|razr))?(:?\\s4g)?)[\\w\\s]+build\\//i,\n /mot[\\s-]?(\\w+)*/i,\n /(XT\\d{3,4}) build\\//i\n ], [MODEL, [VENDOR, 'Motorola'], [TYPE, MOBILE]], [\n /android.+\\s(mz60\\d|xoom[\\s2]{0,2})\\sbuild\\//i\n ], [MODEL, [VENDOR, 'Motorola'], [TYPE, TABLET]], [\n\n /android.+((sch-i[89]0\\d|shw-m380s|gt-p\\d{4}|gt-n8000|sgh-t8[56]9|nexus 10))/i,\n /((SM-T\\w+))/i\n ], [[VENDOR, 'Samsung'], MODEL, [TYPE, TABLET]], [ // Samsung\n /((s[cgp]h-\\w+|gt-\\w+|galaxy\\snexus|sm-n900))/i,\n /(sam[sung]*)[\\s-]*(\\w+-?[\\w-]*)*/i,\n /sec-((sgh\\w+))/i\n ], [[VENDOR, 'Samsung'], MODEL, [TYPE, MOBILE]], [\n /(samsung);smarttv/i\n ], [VENDOR, MODEL, [TYPE, SMARTTV]], [\n\n /\\(dtv[\\);].+(aquos)/i // Sharp\n ], [MODEL, [VENDOR, 'Sharp'], [TYPE, SMARTTV]], [\n /sie-(\\w+)*/i // Siemens\n ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [\n\n /(maemo|nokia).*(n900|lumia\\s\\d+)/i, // Nokia\n /(nokia)[\\s_-]?([\\w-]+)*/i\n ], [[VENDOR, 'Nokia'], MODEL, [TYPE, MOBILE]], [\n\n /android\\s3\\.[\\s\\w;-]{10}(a\\d{3})/i // Acer\n ], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [\n\n /android\\s3\\.[\\s\\w;-]{10}(lg?)-([06cv9]{3,4})/i // LG Tablet\n ], [[VENDOR, 'LG'], MODEL, [TYPE, TABLET]], [\n /(lg) netcast\\.tv/i // LG SmartTV\n ], [VENDOR, MODEL, [TYPE, SMARTTV]], [\n /(nexus\\s[45])/i, // LG\n /lg[e;\\s\\/-]+(\\w+)*/i\n ], [MODEL, [VENDOR, 'LG'], [TYPE, MOBILE]], [\n\n /android.+(ideatab[a-z0-9\\-\\s]+)/i // Lenovo\n ], [MODEL, [VENDOR, 'Lenovo'], [TYPE, TABLET]], [\n\n /linux;.+((jolla));/i // Jolla\n ], [VENDOR, MODEL, [TYPE, MOBILE]], [\n\n /((pebble))app\\/[\\d\\.]+\\s/i // Pebble\n ], [VENDOR, MODEL, [TYPE, WEARABLE]], [\n\n /android.+;\\s(glass)\\s\\d/i // Google Glass\n ], [MODEL, [VENDOR, 'Google'], [TYPE, WEARABLE]], [\n\n /android.+(\\w+)\\s+build\\/hm\\1/i, // Xiaomi Hongmi 'numeric' models\n /android.+(hm[\\s\\-_]*note?[\\s_]*(?:\\d\\w)?)\\s+build/i, // Xiaomi Hongmi\n /android.+(mi[\\s\\-_]*(?:one|one[\\s_]plus)?[\\s_]*(?:\\d\\w)?)\\s+build/i // Xiaomi Mi\n ], [[MODEL, /_/g, ' '], [VENDOR, 'Xiaomi'], [TYPE, MOBILE]], [\n\n /(mobile|tablet);.+rv\\:.+gecko\\//i // Unidentifiable\n ], [[TYPE, util.lowerize], VENDOR, MODEL]\n\n /*//////////////////////////\n // TODO: move to string map\n ////////////////////////////\n\n /(C6603)/i // Sony Xperia Z C6603\n ], [[MODEL, 'Xperia Z C6603'], [VENDOR, 'Sony'], [TYPE, MOBILE]], [\n /(C6903)/i // Sony Xperia Z 1\n ], [[MODEL, 'Xperia Z 1'], [VENDOR, 'Sony'], [TYPE, MOBILE]], [\n\n /(SM-G900[F|H])/i // Samsung Galaxy S5\n ], [[MODEL, 'Galaxy S5'], [VENDOR, 'Samsung'], [TYPE, MOBILE]], [\n /(SM-G7102)/i // Samsung Galaxy Grand 2\n ], [[MODEL, 'Galaxy Grand 2'], [VENDOR, 'Samsung'], [TYPE, MOBILE]], [\n /(SM-G530H)/i // Samsung Galaxy Grand Prime\n ], [[MODEL, 'Galaxy Grand Prime'], [VENDOR, 'Samsung'], [TYPE, MOBILE]], [\n /(SM-G313HZ)/i // Samsung Galaxy V\n ], [[MODEL, 'Galaxy V'], [VENDOR, 'Samsung'], [TYPE, MOBILE]], [\n /(SM-T805)/i // Samsung Galaxy Tab S 10.5\n ], [[MODEL, 'Galaxy Tab S 10.5'], [VENDOR, 'Samsung'], [TYPE, TABLET]], [\n /(SM-G800F)/i // Samsung Galaxy S5 Mini\n ], [[MODEL, 'Galaxy S5 Mini'], [VENDOR, 'Samsung'], [TYPE, MOBILE]], [\n /(SM-T311)/i // Samsung Galaxy Tab 3 8.0\n ], [[MODEL, 'Galaxy Tab 3 8.0'], [VENDOR, 'Samsung'], [TYPE, TABLET]], [\n\n /(R1001)/i // Oppo R1001\n ], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [\n /(X9006)/i // Oppo Find 7a\n ], [[MODEL, 'Find 7a'], [VENDOR, 'Oppo'], [TYPE, MOBILE]], [\n /(R2001)/i // Oppo YOYO R2001\n ], [[MODEL, 'Yoyo R2001'], [VENDOR, 'Oppo'], [TYPE, MOBILE]], [\n /(R815)/i // Oppo Clover R815\n ], [[MODEL, 'Clover R815'], [VENDOR, 'Oppo'], [TYPE, MOBILE]], [\n /(U707)/i // Oppo Find Way S\n ], [[MODEL, 'Find Way S'], [VENDOR, 'Oppo'], [TYPE, MOBILE]], [\n\n /(T3C)/i // Advan Vandroid T3C\n ], [MODEL, [VENDOR, 'Advan'], [TYPE, TABLET]], [\n /(ADVAN T1J\\+)/i // Advan Vandroid T1J+\n ], [[MODEL, 'Vandroid T1J+'], [VENDOR, 'Advan'], [TYPE, TABLET]], [\n /(ADVAN S4A)/i // Advan Vandroid S4A\n ], [[MODEL, 'Vandroid S4A'], [VENDOR, 'Advan'], [TYPE, MOBILE]], [\n\n /(V972M)/i // ZTE V972M\n ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [\n\n /(i-mobile)\\s(IQ\\s[\\d\\.]+)/i // i-mobile IQ\n ], [VENDOR, MODEL, [TYPE, MOBILE]], [\n /(IQ6.3)/i // i-mobile IQ IQ 6.3\n ], [[MODEL, 'IQ 6.3'], [VENDOR, 'i-mobile'], [TYPE, MOBILE]], [\n /(i-mobile)\\s(i-style\\s[\\d\\.]+)/i // i-mobile i-STYLE\n ], [VENDOR, MODEL, [TYPE, MOBILE]], [\n /(i-STYLE2.1)/i // i-mobile i-STYLE 2.1\n ], [[MODEL, 'i-STYLE 2.1'], [VENDOR, 'i-mobile'], [TYPE, MOBILE]], [\n \n /(mobiistar touch LAI 512)/i // mobiistar touch LAI 512\n ], [[MODEL, 'Touch LAI 512'], [VENDOR, 'mobiistar'], [TYPE, MOBILE]], [\n\n /////////////\n // END TODO\n ///////////*/\n\n ],\n\n engine : [[\n\n /windows.+\\sedge\\/([\\w\\.]+)/i // EdgeHTML\n ], [VERSION, [NAME, 'EdgeHTML']], [\n\n /(presto)\\/([\\w\\.]+)/i, // Presto\n /(webkit|trident|netfront|netsurf|amaya|lynx|w3m)\\/([\\w\\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m\n /(khtml|tasman|links)[\\/\\s]\\(?([\\w\\.]+)/i, // KHTML/Tasman/Links\n /(icab)[\\/\\s]([23]\\.[\\d\\.]+)/i // iCab\n ], [NAME, VERSION], [\n\n /rv\\:([\\w\\.]+).*(gecko)/i // Gecko\n ], [VERSION, NAME]\n ],\n\n os : [[\n\n // Windows based\n /microsoft\\s(windows)\\s(vista|xp)/i // Windows (iTunes)\n ], [NAME, VERSION], [\n /(windows)\\snt\\s6\\.2;\\s(arm)/i, // Windows RT\n /(windows\\sphone(?:\\sos)*|windows\\smobile|windows)[\\s\\/]?([ntce\\d\\.\\s]+\\w)/i\n ], [NAME, [VERSION, mapper.str, maps.os.windows.version]], [\n /(win(?=3|9|n)|win\\s9x\\s)([nt\\d\\.]+)/i\n ], [[NAME, 'Windows'], [VERSION, mapper.str, maps.os.windows.version]], [\n\n // Mobile/Embedded OS\n /\\((bb)(10);/i // BlackBerry 10\n ], [[NAME, 'BlackBerry'], VERSION], [\n /(blackberry)\\w*\\/?([\\w\\.]+)*/i, // Blackberry\n /(tizen)[\\/\\s]([\\w\\.]+)/i, // Tizen\n /(android|webos|palm\\sos|qnx|bada|rim\\stablet\\sos|meego|contiki)[\\/\\s-]?([\\w\\.]+)*/i,\n // Android/WebOS/Palm/QNX/Bada/RIM/MeeGo/Contiki\n /linux;.+(sailfish);/i // Sailfish OS\n ], [NAME, VERSION], [\n /(symbian\\s?os|symbos|s60(?=;))[\\/\\s-]?([\\w\\.]+)*/i // Symbian\n ], [[NAME, 'Symbian'], VERSION], [\n /\\((series40);/i // Series 40\n ], [NAME], [\n /mozilla.+\\(mobile;.+gecko.+firefox/i // Firefox OS\n ], [[NAME, 'Firefox OS'], VERSION], [\n\n // Console\n /(nintendo|playstation)\\s([wids3portablevu]+)/i, // Nintendo/Playstation\n\n // GNU/Linux based\n /(mint)[\\/\\s\\(]?(\\w+)*/i, // Mint\n /(mageia|vectorlinux)[;\\s]/i, // Mageia/VectorLinux\n /(joli|[kxln]?ubuntu|debian|[open]*suse|gentoo|arch|slackware|fedora|mandriva|centos|pclinuxos|redhat|zenwalk|linpus)[\\/\\s-]?([\\w\\.-]+)*/i,\n // Joli/Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware\n // Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus\n /(hurd|linux)\\s?([\\w\\.]+)*/i, // Hurd/Linux\n /(gnu)\\s?([\\w\\.]+)*/i // GNU\n ], [NAME, VERSION], [\n\n /(cros)\\s[\\w]+\\s([\\w\\.]+\\w)/i // Chromium OS\n ], [[NAME, 'Chromium OS'], VERSION],[\n\n // Solaris\n /(sunos)\\s?([\\w\\.]+\\d)*/i // Solaris\n ], [[NAME, 'Solaris'], VERSION], [\n\n // BSD based\n /\\s([frentopc-]{0,4}bsd|dragonfly)\\s?([\\w\\.]+)*/i // FreeBSD/NetBSD/OpenBSD/PC-BSD/DragonFly\n ], [NAME, VERSION],[\n\n /(ip[honead]+)(?:.*os\\s*([\\w]+)*\\slike\\smac|;\\sopera)/i // iOS\n ], [[NAME, 'iOS'], [VERSION, /_/g, '.']], [\n\n /(mac\\sos\\sx)\\s?([\\w\\s\\.]+\\w)*/i,\n /(macintosh|mac(?=_powerpc)\\s)/i // Mac OS\n ], [[NAME, 'Mac OS'], [VERSION, /_/g, '.']], [\n\n // Other\n /((?:open)?solaris)[\\/\\s-]?([\\w\\.]+)*/i, // Solaris\n /(haiku)\\s(\\w+)/i, // Haiku\n /(aix)\\s((\\d)(?=\\.|\\)|\\s)[\\w\\.]*)*/i, // AIX\n /(plan\\s9|minix|beos|os\\/2|amigaos|morphos|risc\\sos|openvms)/i,\n // Plan9/Minix/BeOS/OS2/AmigaOS/MorphOS/RISCOS/OpenVMS\n /(unix)\\s?([\\w\\.]+)*/i // UNIX\n ], [NAME, VERSION]\n ]\n };\n\n\n /////////////////\n // Constructor\n ////////////////\n\n\n var UAParser = function (uastring, extensions) {\n\n if (!(this instanceof UAParser)) {\n return new UAParser(uastring, extensions).getResult();\n }\n\n var ua = uastring || ((window && window.navigator && window.navigator.userAgent) ? window.navigator.userAgent : EMPTY);\n var rgxmap = extensions ? util.extend(regexes, extensions) : regexes;\n\n this.getBrowser = function () {\n var browser = mapper.rgx.apply(this, rgxmap.browser);\n browser.major = util.major(browser.version);\n return browser;\n };\n this.getCPU = function () {\n return mapper.rgx.apply(this, rgxmap.cpu);\n };\n this.getDevice = function () {\n return mapper.rgx.apply(this, rgxmap.device);\n };\n this.getEngine = function () {\n return mapper.rgx.apply(this, rgxmap.engine);\n };\n this.getOS = function () {\n return mapper.rgx.apply(this, rgxmap.os);\n };\n this.getResult = function() {\n return {\n ua : this.getUA(),\n browser : this.getBrowser(),\n engine : this.getEngine(),\n os : this.getOS(),\n device : this.getDevice(),\n cpu : this.getCPU()\n };\n };\n this.getUA = function () {\n return ua;\n };\n this.setUA = function (uastring) {\n ua = uastring;\n return this;\n };\n this.setUA(ua);\n return this;\n };\n\n UAParser.VERSION = LIBVERSION;\n UAParser.BROWSER = {\n NAME : NAME,\n MAJOR : MAJOR, // deprecated\n VERSION : VERSION\n };\n UAParser.CPU = {\n ARCHITECTURE : ARCHITECTURE\n };\n UAParser.DEVICE = {\n MODEL : MODEL,\n VENDOR : VENDOR,\n TYPE : TYPE,\n CONSOLE : CONSOLE,\n MOBILE : MOBILE,\n SMARTTV : SMARTTV,\n TABLET : TABLET,\n WEARABLE: WEARABLE,\n EMBEDDED: EMBEDDED\n };\n UAParser.ENGINE = {\n NAME : NAME,\n VERSION : VERSION\n };\n UAParser.OS = {\n NAME : NAME,\n VERSION : VERSION\n };\n\n\n ///////////\n // Export\n //////////\n\n\n // check js environment\n if (typeof(exports) !== UNDEF_TYPE) {\n // nodejs env\n if (typeof module !== UNDEF_TYPE && module.exports) {\n exports = module.exports = UAParser;\n }\n exports.UAParser = UAParser;\n } else {\n // requirejs env (optional)\n if (typeof(define) === FUNC_TYPE && define.amd) {\n define(function () {\n return UAParser;\n });\n } else {\n // browser env\n window.UAParser = UAParser;\n }\n }\n\n // jQuery/Zepto specific (optional)\n // Note: \n // In AMD env the global scope should be kept clean, but jQuery is an exception.\n // jQuery always exports to global scope, unless jQuery.noConflict(true) is used,\n // and we should catch that.\n var $ = window.jQuery || window.Zepto;\n if (typeof $ !== UNDEF_TYPE) {\n var parser = new UAParser();\n $.ua = parser.getResult();\n $.ua.get = function() {\n return parser.getUA();\n };\n $.ua.set = function (uastring) {\n parser.setUA(uastring);\n var result = parser.getResult();\n for (var prop in result) {\n $.ua[prop] = result[prop];\n }\n };\n }\n\n})(typeof window === 'object' ? window : this);\n","\nvar rng;\n\nif (global.crypto && crypto.getRandomValues) {\n // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto\n // Moderately fast, high quality\n var _rnds8 = new Uint8Array(16);\n rng = function whatwgRNG() {\n crypto.getRandomValues(_rnds8);\n return _rnds8;\n };\n}\n\nif (!rng) {\n // Math.random()-based (RNG)\n //\n // If all else fails, use Math.random(). It's fast, but is of unspecified\n // quality.\n var _rnds = new Array(16);\n rng = function() {\n for (var i = 0, r; i < 16; i++) {\n if ((i & 0x03) === 0) r = Math.random() * 0x100000000;\n _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;\n }\n\n return _rnds;\n };\n}\n\nmodule.exports = rng;\n\n","/**\n # normalice\n\n Normalize an ice server configuration object (or plain old string) into a format\n that is usable in all browsers supporting WebRTC. Primarily this module is designed\n to help with the transition of the `url` attribute of the configuration object to\n the `urls` attribute.\n\n ## Example Usage\n\n <<< examples/simple.js\n\n**/\n\nvar protocols = [\n 'stun:',\n 'turn:'\n];\n\nmodule.exports = function(input) {\n var url = (input || {}).url || input;\n var protocol;\n var parts;\n var output = {};\n\n // if we don't have a string url, then allow the input to passthrough\n if (typeof url != 'string' && (! (url instanceof String))) {\n return input;\n }\n\n // trim the url string, and convert to an array\n url = url.trim();\n\n // if the protocol is not known, then passthrough\n protocol = protocols[protocols.indexOf(url.slice(0, 5))];\n if (! protocol) {\n return input;\n }\n\n // now let's attack the remaining url parts\n url = url.slice(5);\n parts = url.split('@');\n\n output.username = input.username;\n output.credential = input.credential;\n // if we have an authentication part, then set the credentials\n if (parts.length > 1) {\n url = parts[1];\n parts = parts[0].split(':');\n\n // add the output credential and username\n output.username = parts[0];\n output.credential = (input || {}).credential || parts[1] || '';\n }\n\n output.url = protocol + url;\n output.urls = [ output.url ];\n\n return output;\n};\n"]}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
(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.kurentoUtils = 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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
2
|
+
function noop(e){e&&console.error(e)}function trackStop(e){e.stop&&e.stop()}function streamStop(e){e.getTracks().forEach(trackStop)}function bufferizeCandidates(e,n){var t=[];return e.addEventListener("signalingstatechange",function(){if("stable"===this.signalingState)for(;t.length;){var e=t.shift();this.addIceCandidate(e.candidate,e.callback,e.callback)}}),function(i,o){switch(o=o||n,e.signalingState){case"closed":o(new Error("PeerConnection object is closed"));break;case"stable":if(e.remoteDescription){e.addIceCandidate(i,o,o);break}default:t.push({candidate:i,callback:o})}}}function removeFIDFromOffer(e){var n=e.indexOf("a=ssrc-group:FID");return n>0?e.slice(0,n):e}function getSimulcastInfo(e){var n=e.getVideoTracks(),t=["a=x-google-flag:conference","a=ssrc-group:SIM 1 2 3","a=ssrc:1 cname:localVideo","a=ssrc:1 msid:"+e.id+" "+n[0].id,"a=ssrc:1 mslabel:"+e.id,"a=ssrc:1 label:"+n[0].id,"a=ssrc:2 cname:localVideo","a=ssrc:2 msid:"+e.id+" "+n[0].id,"a=ssrc:2 mslabel:"+e.id,"a=ssrc:2 label:"+n[0].id,"a=ssrc:3 cname:localVideo","a=ssrc:3 msid:"+e.id+" "+n[0].id,"a=ssrc:3 mslabel:"+e.id,"a=ssrc:3 label:"+n[0].id];return t.push(""),t.join("\n")}function WebRtcPeer(e,n,t){function i(){if(a){var e=p.getRemoteStreams()[0],n=e?URL.createObjectURL(e):"";a.pause(),a.src=n,a.load(),console.log("Remote URL:",n)}}function o(){C.emit("streamended",this)}function r(){"closed"===p.signalingState&&t('The peer connection object is in "closed" state. This is most likely due to an invocation of the dispose method before accepting in the dialogue'),d&&c&&C.showLocalVideo(),d&&(d.addEventListener("ended",o),p.addStream(d)),l&&(l.addEventListener("ended",o),p.addStream(l));var n=parser.getBrowser();"sendonly"!==e||"Chrome"!==n.name&&"Chromium"!==n.name||39!==n.major||(e="sendrecv"),t()}function s(e){void 0===e&&(e=MEDIA_CONSTRAINTS),getUserMedia(e,function(e){d=e,r()},t)}if(!(this instanceof WebRtcPeer))return new WebRtcPeer(e,n,t);WebRtcPeer.super_.call(this),n instanceof Function&&(t=n,n=void 0),n=n||{},t=(t||noop).bind(this);var c=n.localVideo,a=n.remoteVideo,d=n.videoStream,l=n.audioStream,u=n.mediaConstraints,f=n.connectionConstraints,p=n.peerConnection,v=n.sendSource||"webcam",h=uuid.v4(),g=recursive({iceServers:freeice()},n.configuration),b=n.onstreamended;b&&this.on("streamended",b);var m=n.onicecandidate;m&&this.on("icecandidate",m);var R=n.oncandidategatheringdone;R&&this.on("candidategatheringdone",R);var S=n.simulcast;p||(p=new RTCPeerConnection(g)),Object.defineProperties(this,{peerConnection:{get:function(){return p}},remoteVideo:{get:function(){return a}},localVideo:{get:function(){return c}},currentFrame:{get:function(){if(a){if(a.readyState<a.HAVE_CURRENT_DATA)throw new Error("No video stream data available");var e=document.createElement("canvas");return e.width=a.videoWidth,e.height=a.videoHeight,e.getContext("2d").drawImage(a,0,0),e}}}});var C=this,w=[],P=!1;p.addEventListener("icecandidate",function(e){var n=e.candidate;EventEmitter.listenerCount(C,"icecandidate")||EventEmitter.listenerCount(C,"candidategatheringdone")?n?(C.emit("icecandidate",n),P=!1):P||(C.emit("candidategatheringdone"),P=!0):P||(w.push(n),n||(P=!0))}),this.on("newListener",function(e,n){if("icecandidate"===e||"candidategatheringdone"===e)for(;w.length;){var t=w.shift();!t==("candidategatheringdone"===e)&&n(t)}});var y=bufferizeCandidates(p);this.addIceCandidate=function(e,n){var t=new RTCIceCandidate(e);console.log("ICE candidate received"),n=(n||noop).bind(this),y(t,n)},this.generateOffer=function(n){n=n.bind(this);var t=parser.getBrowser(),i=!0,o=!0;u&&(i="boolean"==typeof u.audio?u.audio:!0,o="boolean"==typeof u.video?u.video:!0);var r="Firefox"===t.name&&t.version>34?{offerToReceiveAudio:"sendonly"!==e&&i,offerToReceiveVideo:"sendonly"!==e&&o}:{mandatory:{OfferToReceiveAudio:"sendonly"!==e&&i,OfferToReceiveVideo:"sendonly"!==e&&o},optional:[{DtlsSrtpKeyAgreement:!0}]},s=recursive(r,f);console.log("constraints: "+JSON.stringify(s)),p.createOffer(function(e){console.log("Created SDP offer"),S&&("Chrome"===t.name||"Chromium"===t.name?(console.log("Adding multicast info"),e=new RTCSessionDescription({type:e.type,sdp:removeFIDFromOffer(e.sdp)+getSimulcastInfo(d)})):console.warn("Simulcast is only available in Chrome browser.")),p.setLocalDescription(e,function(){console.log("Local description set",e.sdp),n(null,e.sdp,C.processAnswer.bind(C))},n)},n,s)},this.getLocalSessionDescriptor=function(){return p.localDescription},this.getRemoteSessionDescriptor=function(){return p.remoteDescription},this.showLocalVideo=function(){c.src=URL.createObjectURL(d),c.muted=!0},this.processAnswer=function(e,n){n=(n||noop).bind(this);var t=new RTCSessionDescription({type:"answer",sdp:e});return console.log("SDP answer received, setting remote description"),"closed"===p.signalingState?n("PeerConnection is closed"):void p.setRemoteDescription(t,function(){i(),n()},n)},this.processOffer=function(e,n){n=n.bind(this);var t=new RTCSessionDescription({type:"offer",sdp:e});return console.log("SDP offer received, setting remote description"),"closed"===p.signalingState?n("PeerConnection is closed"):void p.setRemoteDescription(t,function(){i(),p.createAnswer(function(e){console.log("Created SDP answer"),p.setLocalDescription(e,function(){console.log("Local description set",e.sdp),n(null,e.sdp)},n)},n)},n)},"recvonly"===e||d||l?setTimeout(r,0):"webcam"===v?s(u):getScreenConstraints(v,function(e,n){return e?t(e):(constraints=[u],constraints.unshift(n),void s(recursive.apply(void 0,constraints)))},h),this.on("_dispose",function(){c&&(c.pause(),c.src="",c.load()),a&&(a.pause(),a.src="",a.load()),C.removeAllListeners(),void 0!==window.cancelChooseDesktopMedia&&window.cancelChooseDesktopMedia(h)})}function createEnableDescriptor(e){var n="get"+e+"Tracks";return{enumerable:!0,get:function(){if(this.peerConnection){var e=this.peerConnection.getLocalStreams();if(e.length){for(var t,i=0;t=e[i];i++)for(var o,r=t[n](),s=0;o=r[s];s++)if(!o.enabled)return!1;return!0}}},set:function(e){function t(n){n.enabled=e}this.peerConnection.getLocalStreams().forEach(function(e){e[n]().forEach(t)})}}}function WebRtcPeerRecvonly(e,n){return this instanceof WebRtcPeerRecvonly?void WebRtcPeerRecvonly.super_.call(this,"recvonly",e,n):new WebRtcPeerRecvonly(e,n)}function WebRtcPeerSendonly(e,n){return this instanceof WebRtcPeerSendonly?void WebRtcPeerSendonly.super_.call(this,"sendonly",e,n):new WebRtcPeerSendonly(e,n)}function WebRtcPeerSendrecv(e,n){return this instanceof WebRtcPeerSendrecv?void WebRtcPeerSendrecv.super_.call(this,"sendrecv",e,n):new WebRtcPeerSendrecv(e,n)}var freeice=require("freeice"),inherits=require("inherits"),UAParser=require("ua-parser-js"),uuid=require("uuid"),EventEmitter=require("events").EventEmitter,recursive=require("merge").recursive.bind(void 0,!0);try{require("kurento-browser-extensions")}catch(error){"undefined"==typeof getScreenConstraints&&(console.warn("screen sharing is not available"),getScreenConstraints=function(e,n){n(new Error("This library is not enabled for screen sharing"))})}var MEDIA_CONSTRAINTS={audio:!0,video:{width:640,framerate:15}},ua=window&&window.navigator?window.navigator.userAgent:"",parser=new UAParser(ua);inherits(WebRtcPeer,EventEmitter),Object.defineProperties(WebRtcPeer.prototype,{enabled:{enumerable:!0,get:function(){return this.audioEnabled&&this.videoEnabled},set:function(e){this.audioEnabled=this.videoEnabled=e}},audioEnabled:createEnableDescriptor("Audio"),videoEnabled:createEnableDescriptor("Video")}),WebRtcPeer.prototype.getLocalStream=function(e){return this.peerConnection?this.peerConnection.getLocalStreams()[e||0]:void 0},WebRtcPeer.prototype.getRemoteStream=function(e){return this.peerConnection?this.peerConnection.getRemoteStreams()[e||0]:void 0},WebRtcPeer.prototype.dispose=function(){console.log("Disposing WebRtcPeer");var e=this.peerConnection;try{if(e){if("closed"===e.signalingState)return;e.getLocalStreams().forEach(streamStop),e.close()}}catch(n){console.warn("Exception disposing webrtc peer "+n)}this.emit("_dispose")},inherits(WebRtcPeerRecvonly,WebRtcPeer),inherits(WebRtcPeerSendonly,WebRtcPeer),inherits(WebRtcPeerSendrecv,WebRtcPeer),exports.bufferizeCandidates=bufferizeCandidates,exports.WebRtcPeerRecvonly=WebRtcPeerRecvonly,exports.WebRtcPeerSendonly=WebRtcPeerSendonly,exports.WebRtcPeerSendrecv=WebRtcPeerSendrecv;
|
3
|
+
},{"events":4,"freeice":5,"inherits":9,"kurento-browser-extensions":10,"merge":11,"ua-parser-js":12,"uuid":14}],2:[function(require,module,exports){
|
4
|
+
window.addEventListener&&(module.exports=require("./index"));
|
5
|
+
},{"./index":3}],3:[function(require,module,exports){
|
6
|
+
var WebRtcPeer=require("./WebRtcPeer");exports.WebRtcPeer=WebRtcPeer;
|
7
|
+
},{"./WebRtcPeer":1}],4:[function(require,module,exports){
|
8
|
+
function EventEmitter(){this._events=this._events||{},this._maxListeners=this._maxListeners||void 0}function isFunction(e){return"function"==typeof e}function isNumber(e){return"number"==typeof e}function isObject(e){return"object"==typeof e&&null!==e}function isUndefined(e){return void 0===e}module.exports=EventEmitter,EventEmitter.EventEmitter=EventEmitter,EventEmitter.prototype._events=void 0,EventEmitter.prototype._maxListeners=void 0,EventEmitter.defaultMaxListeners=10,EventEmitter.prototype.setMaxListeners=function(e){if(!isNumber(e)||0>e||isNaN(e))throw TypeError("n must be a positive number");return this._maxListeners=e,this},EventEmitter.prototype.emit=function(e){var t,n,s,i,r,o;if(this._events||(this._events={}),"error"===e&&(!this._events.error||isObject(this._events.error)&&!this._events.error.length)){if(t=arguments[1],t instanceof Error)throw t;throw TypeError('Uncaught, unspecified "error" event.')}if(n=this._events[e],isUndefined(n))return!1;if(isFunction(n))switch(arguments.length){case 1:n.call(this);break;case 2:n.call(this,arguments[1]);break;case 3:n.call(this,arguments[1],arguments[2]);break;default:for(s=arguments.length,i=new Array(s-1),r=1;s>r;r++)i[r-1]=arguments[r];n.apply(this,i)}else if(isObject(n)){for(s=arguments.length,i=new Array(s-1),r=1;s>r;r++)i[r-1]=arguments[r];for(o=n.slice(),s=o.length,r=0;s>r;r++)o[r].apply(this,i)}return!0},EventEmitter.prototype.addListener=function(e,t){var n;if(!isFunction(t))throw TypeError("listener must be a function");if(this._events||(this._events={}),this._events.newListener&&this.emit("newListener",e,isFunction(t.listener)?t.listener:t),this._events[e]?isObject(this._events[e])?this._events[e].push(t):this._events[e]=[this._events[e],t]:this._events[e]=t,isObject(this._events[e])&&!this._events[e].warned){var n;n=isUndefined(this._maxListeners)?EventEmitter.defaultMaxListeners:this._maxListeners,n&&n>0&&this._events[e].length>n&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace())}return this},EventEmitter.prototype.on=EventEmitter.prototype.addListener,EventEmitter.prototype.once=function(e,t){function n(){this.removeListener(e,n),s||(s=!0,t.apply(this,arguments))}if(!isFunction(t))throw TypeError("listener must be a function");var s=!1;return n.listener=t,this.on(e,n),this},EventEmitter.prototype.removeListener=function(e,t){var n,s,i,r;if(!isFunction(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(n=this._events[e],i=n.length,s=-1,n===t||isFunction(n.listener)&&n.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(isObject(n)){for(r=i;r-- >0;)if(n[r]===t||n[r].listener&&n[r].listener===t){s=r;break}if(0>s)return this;1===n.length?(n.length=0,delete this._events[e]):n.splice(s,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},EventEmitter.prototype.removeAllListeners=function(e){var t,n;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(n=this._events[e],isFunction(n))this.removeListener(e,n);else for(;n.length;)this.removeListener(e,n[n.length-1]);return delete this._events[e],this},EventEmitter.prototype.listeners=function(e){var t;return t=this._events&&this._events[e]?isFunction(this._events[e])?[this._events[e]]:this._events[e].slice():[]},EventEmitter.listenerCount=function(e,t){var n;return n=e._events&&e._events[t]?isFunction(e._events[t])?1:e._events[t].length:0};
|
9
|
+
},{}],5:[function(require,module,exports){
|
10
|
+
"use strict";var normalice=require("normalice"),freeice=module.exports=function(n){function t(n,t){for(var r,u=[],o=[].concat(e[n]);o.length&&u.length<t;)r=Math.random()*o.length|0,u=u.concat(o.splice(r,1));return u.map(function(t){return"string"==typeof t||t instanceof String?normalice(n+":"+t):t})}var r,e={stun:(n||{}).stun||require("./stun.json"),turn:(n||{}).turn||require("./turn.json")},u=(n||{}).stunCount||2,o=(n||{}).turnCount||0;return r=[].concat(t("stun",u)),o&&(r=r.concat(t("turn",o))),r};
|
11
|
+
},{"./stun.json":7,"./turn.json":8,"normalice":6}],6:[function(require,module,exports){
|
12
|
+
var protocols=["stun:","turn:"];module.exports=function(e){var r,t,n=(e||{}).url||e,l={};return"string"==typeof n||n instanceof String?(n=n.trim(),(r=protocols[protocols.indexOf(n.slice(0,5))])?(n=n.slice(5),t=n.split("@"),l.username=e.username,l.credential=e.credential,t.length>1&&(n=t[1],t=t[0].split(":"),l.username=t[0],l.credential=(e||{}).credential||t[1]||""),l.url=r+n,l.urls=[l.url],l):e):e};
|
13
|
+
},{}],7:[function(require,module,exports){
|
14
|
+
module.exports=["stun.l.google.com:19302","stun1.l.google.com:19302","stun2.l.google.com:19302","stun3.l.google.com:19302","stun4.l.google.com:19302","stun.ekiga.net","stun.ideasip.com","stun.schlund.de","stun.stunprotocol.org:3478","stun.voiparound.com","stun.voipbuster.com","stun.voipstunt.com","stun.voxgratia.org","stun.services.mozilla.com"]
|
15
|
+
},{}],8:[function(require,module,exports){
|
16
|
+
module.exports=[]
|
17
|
+
},{}],9:[function(require,module,exports){
|
18
|
+
"function"==typeof Object.create?module.exports=function(t,e){t.super_=e,t.prototype=Object.create(e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}})}:module.exports=function(t,e){t.super_=e;var o=function(){};o.prototype=e.prototype,t.prototype=new o,t.prototype.constructor=t};
|
19
|
+
},{}],10:[function(require,module,exports){
|
20
|
+
|
21
|
+
},{}],11:[function(require,module,exports){
|
22
|
+
!function(e){function r(e,o){if("object"!==n(e))return o;for(var t in o)"object"===n(e[t])&&"object"===n(o[t])?e[t]=r(e[t],o[t]):e[t]=o[t];return e}function o(e,o,c){var u=c[0],f=c.length;(e||"object"!==n(u))&&(u={});for(var i=0;f>i;++i){var l=c[i],a=n(l);if("object"===a)for(var s in l){var v=e?t.clone(l[s]):l[s];o?u[s]=r(u[s],v):u[s]=v}}return u}function n(e){return{}.toString.call(e).slice(8,-1).toLowerCase()}var t=function(e){return o(e===!0,!1,arguments)},c="merge";t.recursive=function(e){return o(e===!0,!0,arguments)},t.clone=function(e){var r,o,c=e,u=n(e);if("array"===u)for(c=[],o=e.length,r=0;o>r;++r)c[r]=t.clone(e[r]);else if("object"===u){c={};for(r in e)c[r]=t.clone(e[r])}return c},e?module.exports=t:window[c]=t}("object"==typeof module&&module&&"object"==typeof module.exports&&module.exports);
|
23
|
+
},{}],12:[function(require,module,exports){
|
24
|
+
!function(i,e){"use strict";var s="0.7.9",o="",r="?",n="function",a="undefined",t="object",w="string",l="major",d="model",p="name",c="type",u="vendor",m="version",f="architecture",g="console",b="mobile",h="tablet",v="smarttv",x="wearable",k="embedded",y={extend:function(i,e){for(var s in e)-1!=="browser cpu device engine os".indexOf(s)&&e[s].length%2===0&&(i[s]=e[s].concat(i[s]));return i},has:function(i,e){return"string"==typeof i?-1!==e.toLowerCase().indexOf(i.toLowerCase()):!1},lowerize:function(i){return i.toLowerCase()},major:function(i){return typeof i===w?i.split(".")[0]:e}},A={rgx:function(){for(var i,s,o,r,w,l,d,p=0,c=arguments;p<c.length&&!l;){var u=c[p],m=c[p+1];if(typeof i===a){i={};for(r in m)w=m[r],typeof w===t?i[w[0]]=e:i[w]=e}for(s=o=0;s<u.length&&!l;)if(l=u[s++].exec(this.getUA()))for(r=0;r<m.length;r++)d=l[++o],w=m[r],typeof w===t&&w.length>0?2==w.length?typeof w[1]==n?i[w[0]]=w[1].call(this,d):i[w[0]]=w[1]:3==w.length?typeof w[1]!==n||w[1].exec&&w[1].test?i[w[0]]=d?d.replace(w[1],w[2]):e:i[w[0]]=d?w[1].call(this,d,w[2]):e:4==w.length&&(i[w[0]]=d?w[3].call(this,d.replace(w[1],w[2])):e):i[w]=d?d:e;p+=2}return i},str:function(i,s){for(var o in s)if(typeof s[o]===t&&s[o].length>0){for(var n=0;n<s[o].length;n++)if(y.has(s[o][n],i))return o===r?e:o}else if(y.has(s[o],i))return o===r?e:o;return i}},E={browser:{oldsafari:{version:{"1.0":"/8",1.2:"/1",1.3:"/3","2.0":"/412","2.0.2":"/416","2.0.3":"/417","2.0.4":"/419","?":"/"}}},device:{amazon:{model:{"Fire Phone":["SD","KF"]}},sprint:{model:{"Evo Shift 4G":"7373KT"},vendor:{HTC:"APA",Sprint:"Sprint"}}},os:{windows:{version:{ME:"4.90","NT 3.11":"NT3.51","NT 4.0":"NT4.0",2000:"NT 5.0",XP:["NT 5.1","NT 5.2"],Vista:"NT 6.0",7:"NT 6.1",8:"NT 6.2",8.1:"NT 6.3",10:["NT 6.4","NT 10.0"],RT:"ARM"}}}},S={browser:[[/(opera\smini)\/([\w\.-]+)/i,/(opera\s[mobiletab]+).+version\/([\w\.-]+)/i,/(opera).+version\/([\w\.]+)/i,/(opera)[\/\s]+([\w\.]+)/i],[p,m],[/\s(opr)\/([\w\.]+)/i],[[p,"Opera"],m],[/(kindle)\/([\w\.]+)/i,/(lunascape|maxthon|netfront|jasmine|blazer)[\/\s]?([\w\.]+)*/i,/(avant\s|iemobile|slim|baidu)(?:browser)?[\/\s]?([\w\.]*)/i,/(?:ms|\()(ie)\s([\w\.]+)/i,/(rekonq)\/([\w\.]+)*/i,/(chromium|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium)\/([\w\.-]+)/i],[p,m],[/(trident).+rv[:\s]([\w\.]+).+like\sgecko/i],[[p,"IE"],m],[/(edge)\/((\d+)?[\w\.]+)/i],[p,m],[/(yabrowser)\/([\w\.]+)/i],[[p,"Yandex"],m],[/(comodo_dragon)\/([\w\.]+)/i],[[p,/_/g," "],m],[/(chrome|omniweb|arora|[tizenoka]{5}\s?browser)\/v?([\w\.]+)/i,/(uc\s?browser|qqbrowser)[\/\s]?([\w\.]+)/i],[p,m],[/(dolfin)\/([\w\.]+)/i],[[p,"Dolphin"],m],[/((?:android.+)crmo|crios)\/([\w\.]+)/i],[[p,"Chrome"],m],[/XiaoMi\/MiuiBrowser\/([\w\.]+)/i],[m,[p,"MIUI Browser"]],[/android.+version\/([\w\.]+)\s+(?:mobile\s?safari|safari)/i],[m,[p,"Android Browser"]],[/FBAV\/([\w\.]+);/i],[m,[p,"Facebook"]],[/version\/([\w\.]+).+?mobile\/\w+\s(safari)/i],[m,[p,"Mobile Safari"]],[/version\/([\w\.]+).+?(mobile\s?safari|safari)/i],[m,p],[/webkit.+?(mobile\s?safari|safari)(\/[\w\.]+)/i],[p,[m,A.str,E.browser.oldsafari.version]],[/(konqueror)\/([\w\.]+)/i,/(webkit|khtml)\/([\w\.]+)/i],[p,m],[/(navigator|netscape)\/([\w\.-]+)/i],[[p,"Netscape"],m],[/fxios\/([\w\.-]+)/i],[m,[p,"Firefox"]],[/(swiftfox)/i,/(icedragon|iceweasel|camino|chimera|fennec|maemo\sbrowser|minimo|conkeror)[\/\s]?([\w\.\+]+)/i,/(firefox|seamonkey|k-meleon|icecat|iceape|firebird|phoenix)\/([\w\.-]+)/i,/(mozilla)\/([\w\.]+).+rv\:.+gecko\/\d+/i,/(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf)[\/\s]?([\w\.]+)/i,/(links)\s\(([\w\.]+)/i,/(gobrowser)\/?([\w\.]+)*/i,/(ice\s?browser)\/v?([\w\._]+)/i,/(mosaic)[\/\s]([\w\.]+)/i],[p,m]],cpu:[[/(?:(amd|x(?:(?:86|64)[_-])?|wow|win)64)[;\)]/i],[[f,"amd64"]],[/(ia32(?=;))/i],[[f,y.lowerize]],[/((?:i[346]|x)86)[;\)]/i],[[f,"ia32"]],[/windows\s(ce|mobile);\sppc;/i],[[f,"arm"]],[/((?:ppc|powerpc)(?:64)?)(?:\smac|;|\))/i],[[f,/ower/,"",y.lowerize]],[/(sun4\w)[;\)]/i],[[f,"sparc"]],[/((?:avr32|ia64(?=;))|68k(?=\))|arm(?:64|(?=v\d+;))|(?=atmel\s)avr|(?:irix|mips|sparc)(?:64)?(?=;)|pa-risc)/i],[[f,y.lowerize]]],device:[[/\((ipad|playbook);[\w\s\);-]+(rim|apple)/i],[d,u,[c,h]],[/applecoremedia\/[\w\.]+ \((ipad)/],[d,[u,"Apple"],[c,h]],[/(apple\s{0,1}tv)/i],[[d,"Apple TV"],[u,"Apple"]],[/(archos)\s(gamepad2?)/i,/(hp).+(touchpad)/i,/(kindle)\/([\w\.]+)/i,/\s(nook)[\w\s]+build\/(\w+)/i,/(dell)\s(strea[kpr\s\d]*[\dko])/i],[u,d,[c,h]],[/(kf[A-z]+)\sbuild\/[\w\.]+.*silk\//i],[d,[u,"Amazon"],[c,h]],[/(sd|kf)[0349hijorstuw]+\sbuild\/[\w\.]+.*silk\//i],[[d,A.str,E.device.amazon.model],[u,"Amazon"],[c,b]],[/\((ip[honed|\s\w*]+);.+(apple)/i],[d,u,[c,b]],[/\((ip[honed|\s\w*]+);/i],[d,[u,"Apple"],[c,b]],[/(blackberry)[\s-]?(\w+)/i,/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|huawei|meizu|motorola|polytron)[\s_-]?([\w-]+)*/i,/(hp)\s([\w\s]+\w)/i,/(asus)-?(\w+)/i],[u,d,[c,b]],[/\(bb10;\s(\w+)/i],[d,[u,"BlackBerry"],[c,b]],[/android.+(transfo[prime\s]{4,10}\s\w+|eeepc|slider\s\w+|nexus 7)/i],[d,[u,"Asus"],[c,h]],[/(sony)\s(tablet\s[ps])\sbuild\//i,/(sony)?(?:sgp.+)\sbuild\//i],[[u,"Sony"],[d,"Xperia Tablet"],[c,h]],[/(?:sony)?(?:(?:(?:c|d)\d{4})|(?:so[-l].+))\sbuild\//i],[[u,"Sony"],[d,"Xperia Phone"],[c,b]],[/\s(ouya)\s/i,/(nintendo)\s([wids3u]+)/i],[u,d,[c,g]],[/android.+;\s(shield)\sbuild/i],[d,[u,"Nvidia"],[c,g]],[/(playstation\s[3portablevi]+)/i],[d,[u,"Sony"],[c,g]],[/(sprint\s(\w+))/i],[[u,A.str,E.device.sprint.vendor],[d,A.str,E.device.sprint.model],[c,b]],[/(lenovo)\s?(S(?:5000|6000)+(?:[-][\w+]))/i],[u,d,[c,h]],[/(htc)[;_\s-]+([\w\s]+(?=\))|\w+)*/i,/(zte)-(\w+)*/i,/(alcatel|geeksphone|huawei|lenovo|nexian|panasonic|(?=;\s)sony)[_\s-]?([\w-]+)*/i],[u,[d,/_/g," "],[c,b]],[/(nexus\s9)/i],[d,[u,"HTC"],[c,h]],[/[\s\(;](xbox(?:\sone)?)[\s\);]/i],[d,[u,"Microsoft"],[c,g]],[/(kin\.[onetw]{3})/i],[[d,/\./g," "],[u,"Microsoft"],[c,b]],[/\s(milestone|droid(?:[2-4x]|\s(?:bionic|x2|pro|razr))?(:?\s4g)?)[\w\s]+build\//i,/mot[\s-]?(\w+)*/i,/(XT\d{3,4}) build\//i],[d,[u,"Motorola"],[c,b]],[/android.+\s(mz60\d|xoom[\s2]{0,2})\sbuild\//i],[d,[u,"Motorola"],[c,h]],[/android.+((sch-i[89]0\d|shw-m380s|gt-p\d{4}|gt-n8000|sgh-t8[56]9|nexus 10))/i,/((SM-T\w+))/i],[[u,"Samsung"],d,[c,h]],[/((s[cgp]h-\w+|gt-\w+|galaxy\snexus|sm-n900))/i,/(sam[sung]*)[\s-]*(\w+-?[\w-]*)*/i,/sec-((sgh\w+))/i],[[u,"Samsung"],d,[c,b]],[/(samsung);smarttv/i],[u,d,[c,v]],[/\(dtv[\);].+(aquos)/i],[d,[u,"Sharp"],[c,v]],[/sie-(\w+)*/i],[d,[u,"Siemens"],[c,b]],[/(maemo|nokia).*(n900|lumia\s\d+)/i,/(nokia)[\s_-]?([\w-]+)*/i],[[u,"Nokia"],d,[c,b]],[/android\s3\.[\s\w;-]{10}(a\d{3})/i],[d,[u,"Acer"],[c,h]],[/android\s3\.[\s\w;-]{10}(lg?)-([06cv9]{3,4})/i],[[u,"LG"],d,[c,h]],[/(lg) netcast\.tv/i],[u,d,[c,v]],[/(nexus\s[45])/i,/lg[e;\s\/-]+(\w+)*/i],[d,[u,"LG"],[c,b]],[/android.+(ideatab[a-z0-9\-\s]+)/i],[d,[u,"Lenovo"],[c,h]],[/linux;.+((jolla));/i],[u,d,[c,b]],[/((pebble))app\/[\d\.]+\s/i],[u,d,[c,x]],[/android.+;\s(glass)\s\d/i],[d,[u,"Google"],[c,x]],[/android.+(\w+)\s+build\/hm\1/i,/android.+(hm[\s\-_]*note?[\s_]*(?:\d\w)?)\s+build/i,/android.+(mi[\s\-_]*(?:one|one[\s_]plus)?[\s_]*(?:\d\w)?)\s+build/i],[[d,/_/g," "],[u,"Xiaomi"],[c,b]],[/(mobile|tablet);.+rv\:.+gecko\//i],[[c,y.lowerize],u,d]],engine:[[/windows.+\sedge\/([\w\.]+)/i],[m,[p,"EdgeHTML"]],[/(presto)\/([\w\.]+)/i,/(webkit|trident|netfront|netsurf|amaya|lynx|w3m)\/([\w\.]+)/i,/(khtml|tasman|links)[\/\s]\(?([\w\.]+)/i,/(icab)[\/\s]([23]\.[\d\.]+)/i],[p,m],[/rv\:([\w\.]+).*(gecko)/i],[m,p]],os:[[/microsoft\s(windows)\s(vista|xp)/i],[p,m],[/(windows)\snt\s6\.2;\s(arm)/i,/(windows\sphone(?:\sos)*|windows\smobile|windows)[\s\/]?([ntce\d\.\s]+\w)/i],[p,[m,A.str,E.os.windows.version]],[/(win(?=3|9|n)|win\s9x\s)([nt\d\.]+)/i],[[p,"Windows"],[m,A.str,E.os.windows.version]],[/\((bb)(10);/i],[[p,"BlackBerry"],m],[/(blackberry)\w*\/?([\w\.]+)*/i,/(tizen)[\/\s]([\w\.]+)/i,/(android|webos|palm\sos|qnx|bada|rim\stablet\sos|meego|contiki)[\/\s-]?([\w\.]+)*/i,/linux;.+(sailfish);/i],[p,m],[/(symbian\s?os|symbos|s60(?=;))[\/\s-]?([\w\.]+)*/i],[[p,"Symbian"],m],[/\((series40);/i],[p],[/mozilla.+\(mobile;.+gecko.+firefox/i],[[p,"Firefox OS"],m],[/(nintendo|playstation)\s([wids3portablevu]+)/i,/(mint)[\/\s\(]?(\w+)*/i,/(mageia|vectorlinux)[;\s]/i,/(joli|[kxln]?ubuntu|debian|[open]*suse|gentoo|arch|slackware|fedora|mandriva|centos|pclinuxos|redhat|zenwalk|linpus)[\/\s-]?([\w\.-]+)*/i,/(hurd|linux)\s?([\w\.]+)*/i,/(gnu)\s?([\w\.]+)*/i],[p,m],[/(cros)\s[\w]+\s([\w\.]+\w)/i],[[p,"Chromium OS"],m],[/(sunos)\s?([\w\.]+\d)*/i],[[p,"Solaris"],m],[/\s([frentopc-]{0,4}bsd|dragonfly)\s?([\w\.]+)*/i],[p,m],[/(ip[honead]+)(?:.*os\s*([\w]+)*\slike\smac|;\sopera)/i],[[p,"iOS"],[m,/_/g,"."]],[/(mac\sos\sx)\s?([\w\s\.]+\w)*/i,/(macintosh|mac(?=_powerpc)\s)/i],[[p,"Mac OS"],[m,/_/g,"."]],[/((?:open)?solaris)[\/\s-]?([\w\.]+)*/i,/(haiku)\s(\w+)/i,/(aix)\s((\d)(?=\.|\)|\s)[\w\.]*)*/i,/(plan\s9|minix|beos|os\/2|amigaos|morphos|risc\sos|openvms)/i,/(unix)\s?([\w\.]+)*/i],[p,m]]},T=function(e,s){if(!(this instanceof T))return new T(e,s).getResult();var r=e||(i&&i.navigator&&i.navigator.userAgent?i.navigator.userAgent:o),n=s?y.extend(S,s):S;return this.getBrowser=function(){var i=A.rgx.apply(this,n.browser);return i.major=y.major(i.version),i},this.getCPU=function(){return A.rgx.apply(this,n.cpu)},this.getDevice=function(){return A.rgx.apply(this,n.device)},this.getEngine=function(){return A.rgx.apply(this,n.engine)},this.getOS=function(){return A.rgx.apply(this,n.os)},this.getResult=function(){return{ua:this.getUA(),browser:this.getBrowser(),engine:this.getEngine(),os:this.getOS(),device:this.getDevice(),cpu:this.getCPU()}},this.getUA=function(){return r},this.setUA=function(i){return r=i,this},this.setUA(r),this};T.VERSION=s,T.BROWSER={NAME:p,MAJOR:l,VERSION:m},T.CPU={ARCHITECTURE:f},T.DEVICE={MODEL:d,VENDOR:u,TYPE:c,CONSOLE:g,MOBILE:b,SMARTTV:v,TABLET:h,WEARABLE:x,EMBEDDED:k},T.ENGINE={NAME:p,VERSION:m},T.OS={NAME:p,VERSION:m},typeof exports!==a?(typeof module!==a&&module.exports&&(exports=module.exports=T),exports.UAParser=T):typeof define===n&&define.amd?define(function(){return T}):i.UAParser=T;var N=i.jQuery||i.Zepto;if(typeof N!==a){var z=new T;N.ua=z.getResult(),N.ua.get=function(){return z.getUA()},N.ua.set=function(i){z.setUA(i);var e=z.getResult();for(var s in e)N.ua[s]=e[s]}}}("object"==typeof window?window:this);
|
25
|
+
},{}],13:[function(require,module,exports){
|
26
|
+
(function (global){
|
27
|
+
var rng;if(global.crypto&&crypto.getRandomValues){var _rnds8=new Uint8Array(16);rng=function(){return crypto.getRandomValues(_rnds8),_rnds8}}if(!rng){var _rnds=new Array(16);rng=function(){for(var r,n=0;16>n;n++)0===(3&n)&&(r=4294967296*Math.random()),_rnds[n]=r>>>((3&n)<<3)&255;return _rnds}}module.exports=rng;
|
28
|
+
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
29
|
+
|
30
|
+
},{}],14:[function(require,module,exports){
|
31
|
+
function parse(e,s,r){var t=s&&r||0,n=0;for(s=s||[],e.toLowerCase().replace(/[0-9a-f]{2}/g,function(e){16>n&&(s[t+n++]=_hexToByte[e])});16>n;)s[t+n++]=0;return s}function unparse(e,s){var r=s||0,t=_byteToHex;return t[e[r++]]+t[e[r++]]+t[e[r++]]+t[e[r++]]+"-"+t[e[r++]]+t[e[r++]]+"-"+t[e[r++]]+t[e[r++]]+"-"+t[e[r++]]+t[e[r++]]+"-"+t[e[r++]]+t[e[r++]]+t[e[r++]]+t[e[r++]]+t[e[r++]]+t[e[r++]]}function v1(e,s,r){var t=s&&r||0,n=s||[];e=e||{};var o=void 0!==e.clockseq?e.clockseq:_clockseq,a=void 0!==e.msecs?e.msecs:(new Date).getTime(),u=void 0!==e.nsecs?e.nsecs:_lastNSecs+1,c=a-_lastMSecs+(u-_lastNSecs)/1e4;if(0>c&&void 0===e.clockseq&&(o=o+1&16383),(0>c||a>_lastMSecs)&&void 0===e.nsecs&&(u=0),u>=1e4)throw new Error("uuid.v1(): Can't create more than 10M uuids/sec");_lastMSecs=a,_lastNSecs=u,_clockseq=o,a+=122192928e5;var i=(1e4*(268435455&a)+u)%4294967296;n[t++]=i>>>24&255,n[t++]=i>>>16&255,n[t++]=i>>>8&255,n[t++]=255&i;var _=a/4294967296*1e4&268435455;n[t++]=_>>>8&255,n[t++]=255&_,n[t++]=_>>>24&15|16,n[t++]=_>>>16&255,n[t++]=o>>>8|128,n[t++]=255&o;for(var d=e.node||_nodeId,v=0;6>v;v++)n[t+v]=d[v];return s?s:unparse(n)}function v4(e,s,r){var t=s&&r||0;"string"==typeof e&&(s="binary"==e?new Array(16):null,e=null),e=e||{};var n=e.random||(e.rng||_rng)();if(n[6]=15&n[6]|64,n[8]=63&n[8]|128,s)for(var o=0;16>o;o++)s[t+o]=n[o];return s||unparse(n)}for(var _rng=require("./rng"),_byteToHex=[],_hexToByte={},i=0;256>i;i++)_byteToHex[i]=(i+256).toString(16).substr(1),_hexToByte[_byteToHex[i]]=i;var _seedBytes=_rng(),_nodeId=[1|_seedBytes[0],_seedBytes[1],_seedBytes[2],_seedBytes[3],_seedBytes[4],_seedBytes[5]],_clockseq=16383&(_seedBytes[6]<<8|_seedBytes[7]),_lastMSecs=0,_lastNSecs=0,uuid=v4;uuid.v1=v1,uuid.v4=v4,uuid.parse=parse,uuid.unparse=unparse,module.exports=uuid;
|
32
|
+
},{"./rng":13}]},{},[2])(2)
|
33
|
+
});
|
34
|
+
|
35
|
+
|
36
|
+
//# sourceMappingURL=kurento-utils.map
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateVideoStreams < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :kurento_rails_video_streams do |t|
|
4
|
+
t.string :name
|
5
|
+
t.string :pipeline
|
6
|
+
t.string :file_url
|
7
|
+
t.string :sender_rtc
|
8
|
+
t.boolean :streaming
|
9
|
+
|
10
|
+
t.timestamps null: false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/src/events.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
WebsocketRails::EventMap.describe do
|
2
|
+
# You can use this file to map incoming events to controller actions.
|
3
|
+
# One event can be mapped to any number of controller actions. The
|
4
|
+
# actions will be executed in the order they were subscribed.
|
5
|
+
#
|
6
|
+
# Uncomment and edit the next line to handle the client connected event:
|
7
|
+
# subscribe :client_connected, :to => Controller, :with_method => :method_name
|
8
|
+
#
|
9
|
+
# Here is an example of mapping namespaced events:
|
10
|
+
# namespace :product do
|
11
|
+
# subscribe :new, :to => ProductController, :with_method => :new_product
|
12
|
+
# end
|
13
|
+
# The above will handle an event triggered on the client like `product.new`.
|
14
|
+
namespace :streams do
|
15
|
+
subscribe :active_streams, to: KurentoWebsocketsController, with_method: :active_streams
|
16
|
+
subscribe :recorded_streams, to: KurentoWebsocketsController, with_method: :recorded_streams
|
17
|
+
subscribe :broadcast, to: KurentoWebsocketsController, with_method: :broadcast
|
18
|
+
subscribe :stop_broadcasting, to: KurentoWebsocketsController, with_method: :stop_broadcasting
|
19
|
+
subscribe :view, to: KurentoWebsocketsController, with_method: :view
|
20
|
+
end
|
21
|
+
|
22
|
+
subscribe :client_disconnected, to: KurentoWebsocketsController, with_method: :user_disconnect
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,387 @@
|
|
1
|
+
# # Kurento Rails Javascript Client
|
2
|
+
#
|
3
|
+
# Notes:
|
4
|
+
# - Make sure that the url you pass to store video files in has sufficient space for the videos. Digital Ocean servers aren't going to cut it under heavy load.
|
5
|
+
# - All of the functions in the StreamingHelper class take a callback with the signature function(error, object). Error will be null if the function call was successful.
|
6
|
+
|
7
|
+
|
8
|
+
kurentoRailsHelpers = {}
|
9
|
+
|
10
|
+
kurentoRailsHelpers.getRailsUrlFromBrowser = () => "#{location.hostname}/websocket"
|
11
|
+
|
12
|
+
kurentoRailsHelpers.generateKurentoVideoUrl = (baseFileLocation, videoName) =>
|
13
|
+
date = new Date()
|
14
|
+
dateString = "#{date.getMonth() + 1}-#{date.getDate()}-#{date.getFullYear()}_#{date.getHours()}:#{date.getMinutes()}:#{date.getSeconds()}"
|
15
|
+
return "#{baseFileLocation}/#{encodeURIComponent(videoName.toLowerCase().replace(/\s/g, '-'))}-#{dateString}.webm"
|
16
|
+
|
17
|
+
kurentoRailsHelpers.onIceCandidate = (webRtcEndpoint, webRtcPeer, onError) =>
|
18
|
+
webRtcPeer.on 'icecandidate', (candidate) =>
|
19
|
+
candidate = kurentoClient.register.complexTypes.IceCandidate(candidate)
|
20
|
+
webRtcEndpoint.addIceCandidate candidate, onError
|
21
|
+
webRtcEndpoint.on 'OnIceCandidate', (event) =>
|
22
|
+
webRtcPeer.addIceCandidate event.candidate, onError
|
23
|
+
|
24
|
+
class StreamingHelper
|
25
|
+
|
26
|
+
# ## Constructor - give the urls for the rails server this page was served from, as well as the kurento server we will be streaming to and from
|
27
|
+
constructor: (@kurentoUrl, @railsUrl = kurentoRailsHelpers.getRailsUrlFromBrowser()) ->
|
28
|
+
@kurento = kurentoClient @kurentoUrl
|
29
|
+
@rails_websocket_dispatcher = new WebSocketRails @railsUrl
|
30
|
+
@broadcast = {}
|
31
|
+
@view = {}
|
32
|
+
|
33
|
+
### BROADCASTING FUNCTIONS AND MEMBERS ###
|
34
|
+
@broadcast.kurentoObjects = {}
|
35
|
+
@broadcast.kurentoObjects.pipeline = null
|
36
|
+
@broadcast.kurentoObjects.webRtcPeer = null
|
37
|
+
@broadcast.kurentoObjects.recorder = null
|
38
|
+
@broadcast.kurentoObjects.webRtcEndpoint = null
|
39
|
+
@broadcast.kurentoObjects.videoStreamId = null
|
40
|
+
@broadcast.kurentoObjects.currentCallback = null
|
41
|
+
|
42
|
+
onBroadcastingError = (error) =>
|
43
|
+
if error
|
44
|
+
console.log "Broadcasting error:", error
|
45
|
+
@broadcast.stopBroadcasting()
|
46
|
+
@broadcast.kurentoObjects.currentCallback(error) if @broadcast.kurentoObjects.currentCallback
|
47
|
+
|
48
|
+
onBroadcastingOffer = (error, offer) =>
|
49
|
+
return onBroadcastingError(error) if error
|
50
|
+
@kurento.create 'MediaPipeline', (error, pipeline) =>
|
51
|
+
return onBroadcastingError(error) if error or not @broadcast.kurentoObjects.webRtcPeer
|
52
|
+
@broadcast.kurentoObjects.pipeline = pipeline
|
53
|
+
file_url = kurentoRailsHelpers.generateKurentoVideoUrl(@broadcast.kurentoObjects.baseFileLocation, @broadcast.kurentoObjects.videoName)
|
54
|
+
mediaElements = [
|
55
|
+
{type: 'WebRtcEndpoint', params: {}}
|
56
|
+
{type: 'RecorderEndpoint', params: {uri: file_url}}
|
57
|
+
]
|
58
|
+
pipeline.create mediaElements, (error, elements) =>
|
59
|
+
return onBroadcastingError(error) if error or not @broadcast.kurentoObjects.pipeline
|
60
|
+
[@broadcast.kurentoObjects.webRtcEndpoint, @broadcast.kurentoObjects.recorder] = elements
|
61
|
+
kurentoRailsHelpers.onIceCandidate @broadcast.kurentoObjects.webRtcEndpoint, @broadcast.kurentoObjects.webRtcPeer, onBroadcastingError
|
62
|
+
@broadcast.kurentoObjects.webRtcEndpoint.processOffer offer, (error, answer) =>
|
63
|
+
return onBroadcastingError(error) if error or not @broadcast.kurentoObjects.pipeline
|
64
|
+
@broadcast.kurentoObjects.webRtcEndpoint.gatherCandidates onBroadcastingError
|
65
|
+
@broadcast.kurentoObjects.webRtcPeer.processAnswer answer, onBroadcastingError
|
66
|
+
@broadcast.kurentoObjects.webRtcEndpoint.connect @broadcast.kurentoObjects.recorder, (error) =>
|
67
|
+
return onBroadcastingError(error) if error or not @broadcast.kurentoObjects.pipeline
|
68
|
+
@broadcast.kurentoObjects.recorder.record (error) =>
|
69
|
+
return onBroadcastingError(error) if error or not @broadcast.kurentoObjects.pipeline
|
70
|
+
save_to_rails_success = (stream) =>
|
71
|
+
@broadcast.kurentoObjects.videoStreamId = stream.id
|
72
|
+
@broadcast.kurentoObjects.currentCallback(null, stream) if @broadcast.kurentoObjects.currentCallback
|
73
|
+
save_to_rails_failure = (stream) =>
|
74
|
+
@broadcast.kurentoObjects.currentCallback("Failed to save the data to rails", stream) if @broadcast.kurentoObjects.currentCallback
|
75
|
+
streamToBroadcast =
|
76
|
+
name: @broadcast.kurentoObjects.videoName
|
77
|
+
pipeline: @broadcast.kurentoObjects.pipeline.id
|
78
|
+
file_url: file_url
|
79
|
+
sender_rtc: @broadcast.kurentoObjects.webRtcEndpoint.id
|
80
|
+
streaming: true
|
81
|
+
@rails_websocket_dispatcher.trigger 'streams.broadcast', streamToBroadcast, save_to_rails_success, save_to_rails_failure
|
82
|
+
|
83
|
+
#### Broadcast (broadcast)
|
84
|
+
# This function can be used to send video to the kurento server. Users listening for new streams will also be notified of the new streams creation:
|
85
|
+
#
|
86
|
+
# Parameters:
|
87
|
+
# - videoName: string; The name of the video stream
|
88
|
+
# - videoElement: DOMElement; A DOM element representing an html 5 video tag
|
89
|
+
# - baseFileLocation: string; The file location on the kurento server (or an external file server) where files should be saved when streaming
|
90
|
+
# - callback: function(error, object); a function you wish to have called upon either successful completion of the streaming setup process, or on the occurence of any errors
|
91
|
+
#
|
92
|
+
# It should probably be noted that this function not only broadcasts, but also records the stream
|
93
|
+
@broadcast.broadcast = (videoName, videoElement, baseFileLocation = "file:///kurento", callback = null) =>
|
94
|
+
@broadcast.kurentoObjects.currentCallback = callback
|
95
|
+
@broadcast.kurentoObjects.baseFileLocation = baseFileLocation
|
96
|
+
@broadcast.kurentoObjects.videoName = videoName
|
97
|
+
return onBroadcastingError("You must pass in a valid video tag") if videoElement is null or $(videoElement).prop("tagName").toLowerCase().trim() is not "video"
|
98
|
+
videoElement = $(videoElement)[0] # a little fix to make this work if they used jquery to get the element
|
99
|
+
options = { localVideo: videoElement }
|
100
|
+
@broadcast.kurentoObjects.webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerSendonly options, (error) ->
|
101
|
+
return onBroadcastingError(error) if error
|
102
|
+
this.generateOffer(onBroadcastingOffer)
|
103
|
+
|
104
|
+
#### Stop Broadcasting (stopBroadcasting)
|
105
|
+
# This function can be used to stop transmitting video to the kurento server. Users listening for stream changes will be notified of its completion, as will users listening for changes on this stream.
|
106
|
+
#
|
107
|
+
# Parameters:
|
108
|
+
# - callback: function(error, object); a function you wish for us to call when we stop broadcasting successfully, or when an error occurs
|
109
|
+
@broadcast.stopBroadcasting = (callback = null) =>
|
110
|
+
successfully_notified_rails = (stream) =>
|
111
|
+
@broadcast.kurentoObjects.videoStreamId = null
|
112
|
+
callback() if callback
|
113
|
+
failed_to_notify_rails = (stream) =>
|
114
|
+
console.log "Unable to update video stream status in rails"
|
115
|
+
callback("Unable to update video stream status in rails") if callback
|
116
|
+
if @broadcast.kurentoObjects.recorder
|
117
|
+
@broadcast.kurentoObjects.recorder.stop()
|
118
|
+
@broadcast.kurentoObjects.recorder = null
|
119
|
+
if @broadcast.kurentoObjects.webRtcEndpoint
|
120
|
+
@broadcast.kurentoObjects.webRtcEndpoint.release()
|
121
|
+
@broadcast.kurentoObjects.webRtcEndpoint = null
|
122
|
+
if @broadcast.kurentoObjects.currentCallback
|
123
|
+
@broadcast.kurentoObjects.currentCallback = null
|
124
|
+
if @broadcast.kurentoObjects.webRtcPeer
|
125
|
+
@broadcast.kurentoObjects.webRtcPeer.dispose()
|
126
|
+
@broadcast.kurentoObjects.webRtcPeer = null
|
127
|
+
if @broadcast.kurentoObjects.pipeline
|
128
|
+
@broadcast.kurentoObjects.pipeline.release()
|
129
|
+
@broadcast.kurentoObjects.pipeline = null
|
130
|
+
if @broadcast.kurentoObjects.videoStreamId
|
131
|
+
@rails_websocket_dispatcher.trigger 'streams.stop_broadcasting', '', successfully_notified_rails, failed_to_notify_rails
|
132
|
+
else
|
133
|
+
callback() if callback
|
134
|
+
|
135
|
+
### END BROADCASTING FUNCTIONS AND MEMBERS ###
|
136
|
+
|
137
|
+
### VIEWING FUNCTIONS AND MEMBERS ###
|
138
|
+
@view.live = {}
|
139
|
+
@view.recorded = {}
|
140
|
+
@view.live.kurentoObjects = {}
|
141
|
+
@view.recorded.kurentoObjects = {}
|
142
|
+
|
143
|
+
#### Get Active Streams (view.live.activeStreams)
|
144
|
+
# This function gets an array of all of the currently active streams and passes them back in the callback you supply
|
145
|
+
# Parameters:
|
146
|
+
# - callback: function(error, streamObjectArray); this function will be called once either the streams are retrieved, or an error occurs
|
147
|
+
@view.live.activeStreams = (callback = null, searchParams = {}) =>
|
148
|
+
success = (streams) =>
|
149
|
+
callback(null, streams) if callback
|
150
|
+
error = (streams) =>
|
151
|
+
callback("There was an error getting the streams", streams) if callback
|
152
|
+
@rails_websocket_dispatcher.trigger 'streams.active_streams', searchParams, success, error
|
153
|
+
|
154
|
+
#### Subscribe to streams channel (view.live.subscribeToStreams)
|
155
|
+
# This function will subscribe you to the streams channel on the rails server. Every time a new stream becomes active, the new stream handler you pass in will be called. Every time a stream terminates, the stream deactivated handler you pass in will be called. Both handlers should take the form of function(streamObject)
|
156
|
+
@view.live.subscribeToStreams = (newStreamHandler, streamDeactivatedHandler) =>
|
157
|
+
@view.live.createChannel 'video_streams', {'new': newStreamHandler, 'remove': streamDeactivatedHandler}
|
158
|
+
|
159
|
+
#### Create custom channel (view.live.createChannel)
|
160
|
+
# This function will create a channel and subscribe you to it. The events parameter should be an object, where all of the keys are the names of the events (use quotes around these so that the keys work correctly with underscores and dashes and such), and the values are the handler function to be called when that event is triggered. Each handler should have have the appropriate number of arguments, for however you plan to trigger it.
|
161
|
+
@view.live.createChannel = (channelName, events) =>
|
162
|
+
channel = @rails_websocket_dispatcher.subscribe channelName
|
163
|
+
channel.bind eventName, eventHandler for own eventName, eventHandler of events
|
164
|
+
|
165
|
+
#### Trigger channel event (view.live.triggerChannelEvent)
|
166
|
+
# This function allows you to manually trigger a channel event from the client on any channel you would like. Any other clients who are subscribed to that event will receive the event and handle it however they've registered to do so.
|
167
|
+
@view.live.triggerChannelEvent = (channelName, eventName, objectToSend) =>
|
168
|
+
channel = @rails_websocket_dispatcher.subscribe channelName
|
169
|
+
channel.trigger eventName, objectToSend
|
170
|
+
|
171
|
+
#### Subscribe to the channel for a particular video stream (view.live.onEndOfStream)
|
172
|
+
# This function allows you to receive notifications when a video stream ends. When the stream ends, your endOfStreamHandler function (which should have the signature of function(streamObject)) will be called.
|
173
|
+
#
|
174
|
+
# Parameters:
|
175
|
+
# - stream: streamObject; This is the same thing that is returned by the active streams method. Alternatively, you can pass in a regular old javascript object, so long as it has an id property.
|
176
|
+
# - endOfStreamHandler: function(streamObject); This function will be called when the specified stream ends.
|
177
|
+
@view.live.onEndOfStream = (stream, endOfStreamHandler) =>
|
178
|
+
channel = @rails_websocket_dispatcher.subscribe "stream-#{stream.id}"
|
179
|
+
channel.bind 'end-of-stream', (updated_stream) =>
|
180
|
+
@view.live.stopViewing()
|
181
|
+
endOfStreamHandler(updated_stream)
|
182
|
+
|
183
|
+
onLiveViewError = (error) =>
|
184
|
+
if error
|
185
|
+
console.log "Error!", error
|
186
|
+
@view.live.stopViewing()
|
187
|
+
@view.live.kurentoObjects.callback(error) if @view.live.kurentoObjects.callback
|
188
|
+
|
189
|
+
onLiveViewOffer = (error, offer) =>
|
190
|
+
return onLiveViewError(error) if error
|
191
|
+
if @view.live.kurentoObjects.pipeline and @view.live.kurentoObjects.presenterWebRtcEndpoint
|
192
|
+
[pipeline, presenterEndpoint] = [@view.live.kurentoObjects.pipeline, @view.live.kurentoObjects.presenterWebRtcEndpoint]
|
193
|
+
pipeline.create 'WebRtcEndpoint', (error, endpoint) =>
|
194
|
+
return onLiveViewError(error) if error or not @view.live.kurentoObjects.pipeline
|
195
|
+
@view.live.kurentoObjects.viewerWebRtcEndpoint = endpoint
|
196
|
+
kurentoRailsHelpers.onIceCandidate endpoint, @view.live.kurentoObjects.webRtcPeer, =>
|
197
|
+
endpoint.on 'OnIceCandidate', (event) =>
|
198
|
+
candidate = kurentoClient.register.complexTypes.IceCandidate event.candidate
|
199
|
+
|
200
|
+
endpoint.processOffer offer, (error, answer) =>
|
201
|
+
return onLiveViewError(error) if error or not @view.live.kurentoObjects.pipeline
|
202
|
+
@view.live.kurentoObjects.webRtcPeer.processAnswer answer, (error) =>
|
203
|
+
return onLiveViewError(error) if error
|
204
|
+
endpoint.gatherCandidates (error) =>
|
205
|
+
return onLiveViewError(error) if error
|
206
|
+
@view.live.kurentoObjects.presenterWebRtcEndpoint.connect endpoint, (error) =>
|
207
|
+
return onLiveViewError(error) if error or not @view.live.kurentoObjects.pipeline
|
208
|
+
@view.live.kurentoObjects.callback(null, "Successfully started stream!") if @view.live.kurentoObjects.callback
|
209
|
+
|
210
|
+
#### Start Viewing (view.live.startViewing)
|
211
|
+
# This function accesses a live stream and starts it viewing. There is no error checking for if the stream is actually live, so make sure you know that it is before you start it viewing.
|
212
|
+
|
213
|
+
# Parameters:
|
214
|
+
# - stream: streamObject; This MUST contain at the very least the fields for sender_rtc and pipeline, though a full stream object is acceptable as well.
|
215
|
+
# - videoElement: DOM Element; This should be an html 5 video tag
|
216
|
+
# callback: function(error, object); The function you want to be called upon successful completion of the playback initiation process, or on error
|
217
|
+
@view.live.startViewing = (stream, videoElement, callback = null) =>
|
218
|
+
@view.live.kurentoObjects.callback = callback
|
219
|
+
return onLiveViewError("You must pass in a valid video tag") if videoElement is null or $(videoElement).prop("tagName").toLowerCase().trim() is not "video"
|
220
|
+
videoElement = $(videoElement)[0]
|
221
|
+
@kurento.getMediaobjectById stream.sender_rtc, (error, webRtcEndpoint) =>
|
222
|
+
return onLiveViewError(error) if error
|
223
|
+
@view.live.kurentoObjects.presenterWebRtcEndpoint = webRtcEndpoint
|
224
|
+
@kurento.getMediaobjectById stream.pipeline, (error, pipeline) =>
|
225
|
+
return onLiveViewError(error) if error or not @view.live.kurentoObjects.presenterWebRtcEndpoint
|
226
|
+
@view.live.kurentoObjects.pipeline = pipeline
|
227
|
+
options = {remoteVideo: videoElement}
|
228
|
+
@view.live.kurentoObjects.webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly options, (error) ->
|
229
|
+
return onLiveViewError(error) if error
|
230
|
+
this.generateOffer(onLiveViewOffer)
|
231
|
+
|
232
|
+
|
233
|
+
#### Stop viewing (view.live.stopViewing)
|
234
|
+
# This function stops whatever live stream is currently playing, then calls the callback when it has successfully stopped playback.
|
235
|
+
@view.live.stopViewing = (callback = null) =>
|
236
|
+
if @view.live.kurentoObjects.callback
|
237
|
+
@view.live.kurentoObjects.callback = null
|
238
|
+
if @view.live.kurentoObjects.viewerWebRtcEndpoint
|
239
|
+
@view.live.kurentoObjects.viewerWebRtcEndpoint.release()
|
240
|
+
@view.live.kurentoObjects.viewerWebRtcEndpoint = null
|
241
|
+
if @view.live.kurentoObjects.webRtcPeer
|
242
|
+
@view.live.kurentoObjects.webRtcPeer.dispose()
|
243
|
+
@view.live.kurentoObjects.webRtcPeer = null
|
244
|
+
@view.live.kurentoObjects.pipeline = null if @view.live.kurentoObjects.pipeline
|
245
|
+
@view.live.kurentoObjects.presenterWebRtcEndpoint = null if @view.live.kurentoObjects.presenterWebRtcEndpoint
|
246
|
+
callback() if callback
|
247
|
+
|
248
|
+
#### Get Available Recorded Streams (view.recorded.availableStreams)
|
249
|
+
# This function returns an array of all of the available stream objects. If you want to search for more specific streams, you can also do that by using the option search params object. The callback you provide will be called when either the streams are successfully retrieved, or an error occurs. Your callback should take the form of function(error, streamsArray)
|
250
|
+
@view.recorded.availableStreams = (callback = null, searchParams = {}) =>
|
251
|
+
success = (streams) =>
|
252
|
+
callback(null, streams) if callback
|
253
|
+
error = (streams) =>
|
254
|
+
callback("There was an error getting the streams", streams) if callback
|
255
|
+
@rails_websocket_dispatcher.trigger 'streams.recorded_streams', searchParams, success, error
|
256
|
+
|
257
|
+
onRecordedError = (error) =>
|
258
|
+
console.log "Error: ", error if error
|
259
|
+
@view.recorded.stopViewing()
|
260
|
+
@view.recorded.kurentoObjects.callback(error) if @view.recorded.kurentoObjects.callback
|
261
|
+
|
262
|
+
onRecordedOffer = (error, offer) =>
|
263
|
+
console.log "In the offer function"
|
264
|
+
onRecordedError(error) if error
|
265
|
+
@kurento.create 'MediaPipeline', (error, pipeline) =>
|
266
|
+
return onRecordedError(error) if error
|
267
|
+
@view.recorded.kurentoObjects.pipeline = pipeline
|
268
|
+
pipeline.create 'WebRtcEndpoint', (error, endpoint) =>
|
269
|
+
return onRecordedError(error) if error or not @view.recorded.kurentoObjects.pipeline
|
270
|
+
@view.recorded.kurentoObjects.webRtcEndpoint = endpoint
|
271
|
+
kurentoRailsHelpers.onIceCandidate endpoint, @view.recorded.kurentoObjects.webRtcPeer, =>
|
272
|
+
endpoint.processOffer offer, (error, answer) =>
|
273
|
+
return onRecordedError(error) if error or not @view.recorded.kurentoObjects.pipeline
|
274
|
+
endpoint.gatherCandidates =>
|
275
|
+
@view.recorded.kurentoObjects.webRtcPeer.processAnswer answer
|
276
|
+
|
277
|
+
options = {uri: @view.recorded.kurentoObjects.fileUrl}
|
278
|
+
pipeline.create 'PlayerEndpoint', options, (error, player) =>
|
279
|
+
return onRecordedError(error) if error or not @view.recorded.kurentoObjects.pipeline
|
280
|
+
player.on 'EndOfStream', (event) =>
|
281
|
+
channel = @rails_websocket_dispatcher.subscribe "recorded-stream-#{@view.recorded.kurentoObjects.streamId}"
|
282
|
+
channel.trigger 'end-of-stream', @view.recorded.kurentoObjects.streamId
|
283
|
+
@view.recorded.stopViewing()
|
284
|
+
player.connect endpoint, (error) =>
|
285
|
+
return onRecordedError(error) if error or not @view.recorded.kurentoObjects.pipeline
|
286
|
+
player.play (error) =>
|
287
|
+
return onRecordedError(error) if error or not @view.recorded.kurentoObjects.pipeline
|
288
|
+
@view.recorded.kurentoObjects.callback() if @view.recorded.kurentoObjects.callback
|
289
|
+
|
290
|
+
#### Start Viewng (view.recorded.startViewing)
|
291
|
+
# This function will allow you to start viewing a recorded video stream
|
292
|
+
#
|
293
|
+
# Parameters:
|
294
|
+
# - stream: streamObject; must contain at least the file url and the stream id.
|
295
|
+
# - videoElement: dom element; must be an html 5 video tag
|
296
|
+
# - callback: function(error, object)
|
297
|
+
@view.recorded.startViewing = (stream, videoElement, callback = null) =>
|
298
|
+
console.log "Stream", stream
|
299
|
+
@view.recorded.kurentoObjects.fileUrl = stream.file_url
|
300
|
+
@view.recorded.kurentoObjects.streamId = stream.id
|
301
|
+
@view.recorded.kurentoObjects.videoElement = videoElement
|
302
|
+
@view.recorded.kurentoObjects.callback = callback
|
303
|
+
return onRecordedError("You must pass in a valid video tag") if videoElement is null or $(videoElement).prop("tagName").toLowerCase().trim() is not "video"
|
304
|
+
videoElement = $(videoElement)[0]
|
305
|
+
options = {remoteVideo: videoElement}
|
306
|
+
@view.recorded.kurentoObjects.webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly options, (error) ->
|
307
|
+
return onRecordedError(error) if error
|
308
|
+
console.log "About to generate an offer."
|
309
|
+
this.generateOffer(onRecordedOffer)
|
310
|
+
|
311
|
+
#### Stop viewing (view.recorded.stopViewing)
|
312
|
+
# This function will stop playback of whatever video file is currently playing
|
313
|
+
# It is safe to call even if no video file is playing
|
314
|
+
@view.recorded.stopViewing = (callback = null) =>
|
315
|
+
@view.recorded.kurentoObjects.fileUrl = null if @view.recorded.kurentoObjects.fileUrl
|
316
|
+
@view.recorded.kurentoObjects.streamId = null if @view.recorded.kurentoObjects.streamId
|
317
|
+
@view.recorded.kurentoObjects.callback = null if @view.recorded.kurentoObjects.callback
|
318
|
+
@view.recorded.kurentoObjects.player = null if @view.recorded.kurentoObjects.player
|
319
|
+
if @view.recorded.kurentoObjects.videoElement
|
320
|
+
$(@view.recorded.kurentoObjects.videoElement).attr 'src', ''
|
321
|
+
@view.recorded.kurentoObjects.videoElement = null
|
322
|
+
if @view.recorded.kurentoObjects.webRtcEndpoint
|
323
|
+
@view.recorded.kurentoObjects.webRtcEndpoint.release()
|
324
|
+
@view.recorded.kurentoObjects.webRtcEndpoint = null
|
325
|
+
if @view.recorded.kurentoObjects.pipeline
|
326
|
+
@view.recorded.kurentoObjects.pipeline.release()
|
327
|
+
@view.recorded.kurentoObjects.pipeline = null
|
328
|
+
callback() if callback
|
329
|
+
|
330
|
+
#### On End Of Stream (view.recorded.onEndOfStream)
|
331
|
+
# This function can be used to register an end of stream handler function for a video. The stream parameter must contain at least an id, and the endOfStreamHandler should have the signature function(streamId)
|
332
|
+
@view.recorded.onEndOfStream = (stream, endOfStreamHandler) =>
|
333
|
+
channel = @rails_websocket_dispatcher.subscribe "recorded-stream-#{stream.id}"
|
334
|
+
channel.bind 'end-of-stream', (streamId) =>
|
335
|
+
endOfStreamHandler(streamId)
|
336
|
+
|
337
|
+
|
338
|
+
#### Start Viewing (view.startViewing)
|
339
|
+
# This is a helper function that makes no distinction about whether the stream you want to play is live or recorded. It will determine whether it is live for you, then call the appropriate handler function. However, an additional database call is made to determine if the stream is live, so in cases where you actually know whether the stream is live or not, you should call view.live.startViewing and view.recorded.startViewing directly. The video element should be an html5 video tag, which your video will be displayed in upon successfull completion of the viewing. As usual, your callback should take the form function(error, object)
|
340
|
+
@view.startViewing = (stream, videoElement, callback = null) =>
|
341
|
+
live = => @view.live.startViewing(stream, videoElement, callback)
|
342
|
+
recorded = => @view.recorded.startViewing(stream, videoElement, callback)
|
343
|
+
@rails_websocket_dispatcher.trigger 'streams.view', '', live, recorded
|
344
|
+
|
345
|
+
#### Stop Viewing (view.stopViewing)
|
346
|
+
# This is a helper function that makes no distinction about whether the currently playing stream is live or recorded (or even if you have one of each playing). It will stop the streams either way.
|
347
|
+
@view.stopViewing = (callback = null) =>
|
348
|
+
@view.live.stopViewing(callback)
|
349
|
+
@view.recorded.stopViewing(callback)
|
350
|
+
|
351
|
+
#### On End of Stream (view.onEndOfStream)
|
352
|
+
# This is a helper function that makes no distinction about whether a stream is live or recorded. It will determine whether the stream is live or recorded for you, then call the correct endOfStream handler registration function. Note that this function does make an extra database call, though, so if you already know whether the stream is live or not, it will be faster to use view.live.onEndOfStream and view.recorded.endOfStream
|
353
|
+
@view.onEndOfStream = (stream, endOfStreamHandler) =>
|
354
|
+
live = => @view.live.onEndOfStream(stream, endOfStreamHandler)
|
355
|
+
recorded = => @view.recorded.onEndOfStream(stream, endOfStreamHandler)
|
356
|
+
@rails_websocket_dispatcher.trigger 'streams.view', '', live, recorded
|
357
|
+
|
358
|
+
#### Currently Viewing
|
359
|
+
# This is just a simple boolean function to determine if something is currently playing
|
360
|
+
# It returns true if the user is currently viewing either a live stream or a recorded one.
|
361
|
+
# If the optional stream object is provided, the function will return true only if THAT specific stream is currently playing (it compares it based off of pipeline id and sender_rtc id, so make sure to pass that in).
|
362
|
+
@view.currentlyViewing = (stream = null) =>
|
363
|
+
if stream
|
364
|
+
if @view.live.kurentoObjects.pipeline and @view.live.kurentoObjects.presenterWebRtcEndpoint
|
365
|
+
return @view.live.kurentoObjects.pipeline.id is stream.pipeline and @view.live.kurentoObjects.presenterWebRtcEndpoint.id is stream.sender_rtc
|
366
|
+
else if @view.recorded.kurentoObjects.pipeline
|
367
|
+
return @view.recorded.kurentoObjects.pipeline.id is stream.pipeline
|
368
|
+
else
|
369
|
+
return false
|
370
|
+
return @view.live.kurentoObjects.pipeline or @view.recorded.kurentoObjects.pipeline
|
371
|
+
### END VIEWING FUNCTIONS AND MEMBERS ###
|
372
|
+
|
373
|
+
### AND FINALLY, A UNIVERSAL PAGE UNLOAD HANDLER ###
|
374
|
+
# I put this on a timeout of like 2 seconds so that if the user decides to register any page unload handlers, we won't be interfering with those, and they won't be overwriting our handlers
|
375
|
+
timeoutFunction = =>
|
376
|
+
window.addEventListener('unload', =>
|
377
|
+
@broadcast.stopBroadcasting()
|
378
|
+
@view.stopViewing()
|
379
|
+
@kurento.close() if @kurento
|
380
|
+
@rails_websocket_dispatcher.disconnect() if @rails_websocket_dispatcher
|
381
|
+
)
|
382
|
+
setTimeout timeoutFunction, 2000
|
383
|
+
return @
|
384
|
+
|
385
|
+
window.createStreamingHelper = (kurento_url, rails_url) =>
|
386
|
+
return new StreamingHelper(kurento_url, rails_url)
|
387
|
+
|