123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416 |
- (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(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
- var freeice = require('freeice');
- var inherits = require('inherits');
- var UAParser = require('ua-parser-js');
- var uuidv4 = require('uuid/v4');
- var hark = require('hark');
- var EventEmitter = require('events').EventEmitter;
- var recursive = require('merge').recursive.bind(undefined, true);
- var sdpTranslator = require('sdp-translator');
- var logger = typeof window === 'undefined' ? console : window.Logger || console;
- try {
- require('kurento-browser-extensions');
- } catch (error) {
- if (typeof getScreenConstraints === 'undefined') {
- logger.warn('screen sharing is not available');
- getScreenConstraints = function getScreenConstraints(sendSource, callback) {
- callback(new Error('This library is not enabled for screen sharing'));
- };
- }
- }
- var MEDIA_CONSTRAINTS = {
- audio: true,
- video: {
- width: 640,
- framerate: 15
- }
- };
- var ua = typeof window !== 'undefined' && window.navigator ? window.navigator.userAgent : '';
- var parser = new UAParser(ua);
- var browser = parser.getBrowser();
- function insertScriptSrcInHtmlDom(scriptSrc) {
- var script = document.createElement('script');
- script.src = scriptSrc;
- var ref = document.querySelector('script');
- ref.parentNode.insertBefore(script, ref);
- }
- function importScriptsDependsOnBrowser() {
- if (browser.name === 'IE') {
- insertScriptSrcInHtmlDom('https://cdn.temasys.io/adapterjs/0.15.x/adapter.debug.js');
- }
- }
- importScriptsDependsOnBrowser();
- var usePlanB = false;
- if (browser.name === 'Chrome' || browser.name === 'Chromium') {
- logger.debug(browser.name + ': using SDP PlanB');
- usePlanB = true;
- }
- function noop(error) {
- if (error)
- logger.error(error);
- }
- function trackStop(track) {
- track.stop && track.stop();
- }
- function streamStop(stream) {
- stream.getTracks().forEach(trackStop);
- }
- var dumpSDP = function (description) {
- if (typeof description === 'undefined' || description === null) {
- return '';
- }
- return 'type: ' + description.type + '\r\n' + description.sdp;
- };
- function bufferizeCandidates(pc, onerror) {
- var candidatesQueue = [];
- function setSignalingstatechangeAccordingWwebBrowser(functionToExecute, pc) {
- if (typeof AdapterJS !== 'undefined' && AdapterJS.webrtcDetectedBrowser === 'IE' && AdapterJS.webrtcDetectedVersion >= 9) {
- pc.onsignalingstatechange = functionToExecute;
- } else {
- pc.addEventListener('signalingstatechange', functionToExecute);
- }
- }
- var signalingstatechangeFunction = function () {
- if (pc.signalingState === 'stable') {
- while (candidatesQueue.length) {
- var entry = candidatesQueue.shift();
- pc.addIceCandidate(entry.candidate, entry.callback, entry.callback);
- }
- }
- };
- setSignalingstatechangeAccordingWwebBrowser(signalingstatechangeFunction, pc);
- return function (candidate, callback) {
- callback = callback || onerror;
- switch (pc.signalingState) {
- case 'closed':
- callback(new Error('PeerConnection object is closed'));
- break;
- case 'stable':
- if (pc.remoteDescription) {
- pc.addIceCandidate(candidate, callback, callback);
- break;
- }
- default:
- candidatesQueue.push({
- candidate: candidate,
- callback: callback
- });
- }
- };
- }
- function removeFIDFromOffer(sdp) {
- var n = sdp.indexOf('a=ssrc-group:FID');
- if (n > 0) {
- return sdp.slice(0, n);
- } else {
- return sdp;
- }
- }
- function getSimulcastInfo(videoStream) {
- var videoTracks = videoStream.getVideoTracks();
- if (!videoTracks.length) {
- logger.warn('No video tracks available in the video stream');
- return '';
- }
- var lines = [
- 'a=x-google-flag:conference',
- 'a=ssrc-group:SIM 1 2 3',
- 'a=ssrc:1 cname:localVideo',
- 'a=ssrc:1 msid:' + videoStream.id + ' ' + videoTracks[0].id,
- 'a=ssrc:1 mslabel:' + videoStream.id,
- 'a=ssrc:1 label:' + videoTracks[0].id,
- 'a=ssrc:2 cname:localVideo',
- 'a=ssrc:2 msid:' + videoStream.id + ' ' + videoTracks[0].id,
- 'a=ssrc:2 mslabel:' + videoStream.id,
- 'a=ssrc:2 label:' + videoTracks[0].id,
- 'a=ssrc:3 cname:localVideo',
- 'a=ssrc:3 msid:' + videoStream.id + ' ' + videoTracks[0].id,
- 'a=ssrc:3 mslabel:' + videoStream.id,
- 'a=ssrc:3 label:' + videoTracks[0].id
- ];
- lines.push('');
- return lines.join('\n');
- }
- function sleep(milliseconds) {
- var start = new Date().getTime();
- for (var i = 0; i < 10000000; i++) {
- if (new Date().getTime() - start > milliseconds) {
- break;
- }
- }
- }
- function setIceCandidateAccordingWebBrowser(functionToExecute, pc) {
- if (typeof AdapterJS !== 'undefined' && AdapterJS.webrtcDetectedBrowser === 'IE' && AdapterJS.webrtcDetectedVersion >= 9) {
- pc.onicecandidate = functionToExecute;
- } else {
- pc.addEventListener('icecandidate', functionToExecute);
- }
- }
- function WebRtcPeer(mode, options, callback) {
- if (!(this instanceof WebRtcPeer)) {
- return new WebRtcPeer(mode, options, callback);
- }
- WebRtcPeer.super_.call(this);
- if (options instanceof Function) {
- callback = options;
- options = undefined;
- }
- options = options || {};
- callback = (callback || noop).bind(this);
- var self = this;
- var localVideo = options.localVideo;
- var remoteVideo = options.remoteVideo;
- var videoStream = options.videoStream;
- var audioStream = options.audioStream;
- var mediaConstraints = options.mediaConstraints;
- var pc = options.peerConnection;
- var sendSource = options.sendSource || 'webcam';
- var dataChannelConfig = options.dataChannelConfig;
- var useDataChannels = options.dataChannels || false;
- var dataChannel;
- var guid = uuidv4();
- var configuration = recursive({ iceServers: freeice() }, options.configuration);
- var onicecandidate = options.onicecandidate;
- if (onicecandidate)
- this.on('icecandidate', onicecandidate);
- var oncandidategatheringdone = options.oncandidategatheringdone;
- if (oncandidategatheringdone) {
- this.on('candidategatheringdone', oncandidategatheringdone);
- }
- var simulcast = options.simulcast;
- var multistream = options.multistream;
- var interop = new sdpTranslator.Interop();
- var candidatesQueueOut = [];
- var candidategatheringdone = false;
- Object.defineProperties(this, {
- 'peerConnection': {
- get: function () {
- return pc;
- }
- },
- 'id': {
- value: options.id || guid,
- writable: false
- },
- 'remoteVideo': {
- get: function () {
- return remoteVideo;
- }
- },
- 'localVideo': {
- get: function () {
- return localVideo;
- }
- },
- 'dataChannel': {
- get: function () {
- return dataChannel;
- }
- },
- 'currentFrame': {
- get: function () {
- if (!remoteVideo)
- return;
- if (remoteVideo.readyState < remoteVideo.HAVE_CURRENT_DATA)
- throw new Error('No video stream data available');
- var canvas = document.createElement('canvas');
- canvas.width = remoteVideo.videoWidth;
- canvas.height = remoteVideo.videoHeight;
- canvas.getContext('2d').drawImage(remoteVideo, 0, 0);
- return canvas;
- }
- }
- });
- if (!pc) {
- pc = new RTCPeerConnection(configuration);
- if (useDataChannels && !dataChannel) {
- var dcId = 'WebRtcPeer-' + self.id;
- var dcOptions = undefined;
- if (dataChannelConfig) {
- dcId = dataChannelConfig.id || dcId;
- dcOptions = dataChannelConfig.options;
- }
- dataChannel = pc.createDataChannel(dcId, dcOptions);
- if (dataChannelConfig) {
- dataChannel.onopen = dataChannelConfig.onopen;
- dataChannel.onclose = dataChannelConfig.onclose;
- dataChannel.onmessage = dataChannelConfig.onmessage;
- dataChannel.onbufferedamountlow = dataChannelConfig.onbufferedamountlow;
- dataChannel.onerror = dataChannelConfig.onerror || noop;
- }
- }
- }
- if (!pc.getLocalStreams && pc.getSenders) {
- pc.getLocalStreams = function () {
- var stream = new MediaStream();
- pc.getSenders().forEach(function (sender) {
- stream.addTrack(sender.track);
- });
- return [stream];
- };
- }
- if (!pc.getRemoteStreams && pc.getReceivers) {
- pc.getRemoteStreams = function () {
- var stream = new MediaStream();
- pc.getReceivers().forEach(function (sender) {
- stream.addTrack(sender.track);
- });
- return [stream];
- };
- }
- var iceCandidateFunction = function (event) {
- var candidate = event.candidate;
- if (EventEmitter.listenerCount(self, 'icecandidate') || EventEmitter.listenerCount(self, 'candidategatheringdone')) {
- if (candidate) {
- var cand;
- if (multistream && usePlanB) {
- cand = interop.candidateToUnifiedPlan(candidate);
- } else {
- cand = candidate;
- }
- if (typeof AdapterJS === 'undefined') {
- self.emit('icecandidate', cand);
- }
- candidategatheringdone = false;
- } else if (!candidategatheringdone) {
- if (typeof AdapterJS !== 'undefined' && AdapterJS.webrtcDetectedBrowser === 'IE' && AdapterJS.webrtcDetectedVersion >= 9) {
- EventEmitter.prototype.emit('candidategatheringdone', cand);
- } else {
- self.emit('candidategatheringdone');
- }
- candidategatheringdone = true;
- }
- } else if (!candidategatheringdone) {
- candidatesQueueOut.push(candidate);
- if (!candidate)
- candidategatheringdone = true;
- }
- };
- setIceCandidateAccordingWebBrowser(iceCandidateFunction, pc);
- pc.onaddstream = options.onaddstream;
- pc.onnegotiationneeded = options.onnegotiationneeded;
- this.on('newListener', function (event, listener) {
- if (event === 'icecandidate' || event === 'candidategatheringdone') {
- while (candidatesQueueOut.length) {
- var candidate = candidatesQueueOut.shift();
- if (!candidate === (event === 'candidategatheringdone')) {
- listener(candidate);
- }
- }
- }
- });
- var addIceCandidate = bufferizeCandidates(pc);
- this.addIceCandidate = function (iceCandidate, callback) {
- var candidate;
- if (multistream && usePlanB) {
- candidate = interop.candidateToPlanB(iceCandidate);
- } else {
- candidate = new RTCIceCandidate(iceCandidate);
- }
- logger.debug('Remote ICE candidate received', iceCandidate);
- callback = (callback || noop).bind(this);
- addIceCandidate(candidate, callback);
- };
- this.generateOffer = function (callback) {
- callback = callback.bind(this);
- if (mode === 'recvonly') {
- var useAudio = mediaConstraints && typeof mediaConstraints.audio === 'boolean' ? mediaConstraints.audio : true;
- var useVideo = mediaConstraints && typeof mediaConstraints.video === 'boolean' ? mediaConstraints.video : true;
- if (useAudio) {
- pc.addTransceiver('audio', { direction: 'recvonly' });
- }
- if (useVideo) {
- pc.addTransceiver('video', { direction: 'recvonly' });
- }
- }
- if (typeof AdapterJS !== 'undefined' && AdapterJS.webrtcDetectedBrowser === 'IE' && AdapterJS.webrtcDetectedVersion >= 9) {
- var setLocalDescriptionOnSuccess = function () {
- sleep(1000);
- var localDescription = pc.localDescription;
- logger.debug('Local description set\n', localDescription.sdp);
- if (multistream && usePlanB) {
- localDescription = interop.toUnifiedPlan(localDescription);
- logger.debug('offer::origPlanB->UnifiedPlan', dumpSDP(localDescription));
- }
- callback(null, localDescription.sdp, self.processAnswer.bind(self));
- };
- var createOfferOnSuccess = function (offer) {
- logger.debug('Created SDP offer');
- logger.debug('Local description set\n', pc.localDescription);
- pc.setLocalDescription(offer, setLocalDescriptionOnSuccess, callback);
- };
- pc.createOffer(createOfferOnSuccess, callback);
- } else {
- pc.createOffer().then(function (offer) {
- logger.debug('Created SDP offer');
- offer = mangleSdpToAddSimulcast(offer);
- return pc.setLocalDescription(offer);
- }).then(function () {
- var localDescription = pc.localDescription;
- logger.debug('Local description set\n', localDescription.sdp);
- if (multistream && usePlanB) {
- localDescription = interop.toUnifiedPlan(localDescription);
- logger.debug('offer::origPlanB->UnifiedPlan', dumpSDP(localDescription));
- }
- callback(null, localDescription.sdp, self.processAnswer.bind(self));
- }).catch(callback);
- }
- };
- this.getLocalSessionDescriptor = function () {
- return pc.localDescription;
- };
- this.getRemoteSessionDescriptor = function () {
- return pc.remoteDescription;
- };
- function setRemoteVideo() {
- if (remoteVideo) {
- remoteVideo.pause();
- var stream = pc.getRemoteStreams()[0];
- remoteVideo.srcObject = stream;
- logger.debug('Remote stream:', stream);
- if (typeof AdapterJS !== 'undefined' && AdapterJS.webrtcDetectedBrowser === 'IE' && AdapterJS.webrtcDetectedVersion >= 9) {
- remoteVideo = attachMediaStream(remoteVideo, stream);
- } else {
- remoteVideo.load();
- }
- }
- }
- this.showLocalVideo = function () {
- localVideo.srcObject = videoStream;
- localVideo.muted = true;
- if (typeof AdapterJS !== 'undefined' && AdapterJS.webrtcDetectedBrowser === 'IE' && AdapterJS.webrtcDetectedVersion >= 9) {
- localVideo = attachMediaStream(localVideo, videoStream);
- }
- };
- this.send = function (data) {
- if (dataChannel && dataChannel.readyState === 'open') {
- dataChannel.send(data);
- } else {
- logger.warn('Trying to send data over a non-existing or closed data channel');
- }
- };
- this.processAnswer = function (sdpAnswer, callback) {
- callback = (callback || noop).bind(this);
- var answer = new RTCSessionDescription({
- type: 'answer',
- sdp: sdpAnswer
- });
- if (multistream && usePlanB) {
- var planBAnswer = interop.toPlanB(answer);
- logger.debug('asnwer::planB', dumpSDP(planBAnswer));
- answer = planBAnswer;
- }
- logger.debug('SDP answer received, setting remote description');
- if (pc.signalingState === 'closed') {
- return callback('PeerConnection is closed');
- }
- pc.setRemoteDescription(answer).then(function () {
- setRemoteVideo();
- callback();
- }, callback);
- };
- this.processOffer = function (sdpOffer, callback) {
- callback = callback.bind(this);
- var offer = new RTCSessionDescription({
- type: 'offer',
- sdp: sdpOffer
- });
- if (multistream && usePlanB) {
- var planBOffer = interop.toPlanB(offer);
- logger.debug('offer::planB', dumpSDP(planBOffer));
- offer = planBOffer;
- }
- logger.debug('SDP offer received, setting remote description');
- if (pc.signalingState === 'closed') {
- return callback('PeerConnection is closed');
- }
- pc.setRemoteDescription(offer).then(function () {
- return setRemoteVideo();
- }).then(function () {
- return pc.createAnswer();
- }).then(function (answer) {
- answer = mangleSdpToAddSimulcast(answer);
- logger.debug('Created SDP answer');
- return pc.setLocalDescription(answer);
- }).then(function () {
- var localDescription = pc.localDescription;
- if (multistream && usePlanB) {
- localDescription = interop.toUnifiedPlan(localDescription);
- logger.debug('answer::origPlanB->UnifiedPlan', dumpSDP(localDescription));
- }
- logger.debug('Local description set\n', localDescription.sdp);
- callback(null, localDescription.sdp);
- }).catch(callback);
- };
- function mangleSdpToAddSimulcast(answer) {
- if (simulcast) {
- if (browser.name === 'Chrome' || browser.name === 'Chromium') {
- logger.debug('Adding multicast info');
- answer = new RTCSessionDescription({
- 'type': answer.type,
- 'sdp': removeFIDFromOffer(answer.sdp) + getSimulcastInfo(videoStream)
- });
- } else {
- logger.warn('Simulcast is only available in Chrome browser.');
- }
- }
- return answer;
- }
- function start() {
- if (pc.signalingState === 'closed') {
- callback('The peer connection object is in "closed" state. This is most likely due to an invocation of the dispose method before accepting in the dialogue');
- }
- if (videoStream && localVideo) {
- self.showLocalVideo();
- }
- if (videoStream) {
- videoStream.getTracks().forEach(function (track) {
- pc.addTrack(track, videoStream);
- });
- }
- if (audioStream) {
- audioStream.getTracks().forEach(function (track) {
- pc.addTrack(track, audioStream);
- });
- }
- var browser = parser.getBrowser();
- if (mode === 'sendonly' && (browser.name === 'Chrome' || browser.name === 'Chromium') && browser.major === 39) {
- mode = 'sendrecv';
- }
- callback();
- }
- if (mode !== 'recvonly' && !videoStream && !audioStream) {
- function getMedia(constraints) {
- if (constraints === undefined) {
- constraints = MEDIA_CONSTRAINTS;
- }
- if (typeof AdapterJS !== 'undefined' && AdapterJS.webrtcDetectedBrowser === 'IE' && AdapterJS.webrtcDetectedVersion >= 9) {
- navigator.getUserMedia(constraints, function (stream) {
- videoStream = stream;
- start();
- }, callback);
- } else {
- navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
- videoStream = stream;
- start();
- }).catch(callback);
- }
- }
- if (sendSource === 'webcam') {
- getMedia(mediaConstraints);
- } else {
- getScreenConstraints(sendSource, function (error, constraints_) {
- if (error)
- return callback(error);
- constraints = [mediaConstraints];
- constraints.unshift(constraints_);
- getMedia(recursive.apply(undefined, constraints));
- }, guid);
- }
- } else {
- setTimeout(start, 0);
- }
- this.on('_dispose', function () {
- if (localVideo) {
- localVideo.pause();
- localVideo.srcObject = null;
- if (typeof AdapterJS === 'undefined') {
- localVideo.load();
- }
- localVideo.muted = false;
- }
- if (remoteVideo) {
- remoteVideo.pause();
- remoteVideo.srcObject = null;
- if (typeof AdapterJS === 'undefined') {
- remoteVideo.load();
- }
- }
- self.removeAllListeners();
- if (typeof window !== 'undefined' && window.cancelChooseDesktopMedia !== undefined) {
- window.cancelChooseDesktopMedia(guid);
- }
- });
- }
- inherits(WebRtcPeer, EventEmitter);
- function createEnableDescriptor(type) {
- var method = 'get' + type + 'Tracks';
- return {
- enumerable: true,
- get: function () {
- if (!this.peerConnection)
- return;
- var streams = this.peerConnection.getLocalStreams();
- if (!streams.length)
- return;
- for (var i = 0, stream; stream = streams[i]; i++) {
- var tracks = stream[method]();
- for (var j = 0, track; track = tracks[j]; j++)
- if (!track.enabled)
- return false;
- }
- return true;
- },
- set: function (value) {
- function trackSetEnable(track) {
- track.enabled = value;
- }
- this.peerConnection.getLocalStreams().forEach(function (stream) {
- stream[method]().forEach(trackSetEnable);
- });
- }
- };
- }
- Object.defineProperties(WebRtcPeer.prototype, {
- 'enabled': {
- enumerable: true,
- get: function () {
- return this.audioEnabled && this.videoEnabled;
- },
- set: function (value) {
- this.audioEnabled = this.videoEnabled = value;
- }
- },
- 'audioEnabled': createEnableDescriptor('Audio'),
- 'videoEnabled': createEnableDescriptor('Video')
- });
- WebRtcPeer.prototype.getLocalStream = function (index) {
- if (this.peerConnection) {
- return this.peerConnection.getLocalStreams()[index || 0];
- }
- };
- WebRtcPeer.prototype.getRemoteStream = function (index) {
- if (this.peerConnection) {
- return this.peerConnection.getRemoteStreams()[index || 0];
- }
- };
- WebRtcPeer.prototype.dispose = function () {
- logger.debug('Disposing WebRtcPeer');
- var pc = this.peerConnection;
- var dc = this.dataChannel;
- try {
- if (dc) {
- if (dc.readyState === 'closed')
- return;
- dc.close();
- }
- if (pc) {
- if (pc.signalingState === 'closed')
- return;
- pc.getLocalStreams().forEach(streamStop);
- pc.close();
- }
- } catch (err) {
- logger.warn('Exception disposing webrtc peer ' + err);
- }
- if (typeof AdapterJS === 'undefined') {
- this.emit('_dispose');
- }
- };
- function WebRtcPeerRecvonly(options, callback) {
- if (!(this instanceof WebRtcPeerRecvonly)) {
- return new WebRtcPeerRecvonly(options, callback);
- }
- WebRtcPeerRecvonly.super_.call(this, 'recvonly', options, callback);
- }
- inherits(WebRtcPeerRecvonly, WebRtcPeer);
- function WebRtcPeerSendonly(options, callback) {
- if (!(this instanceof WebRtcPeerSendonly)) {
- return new WebRtcPeerSendonly(options, callback);
- }
- WebRtcPeerSendonly.super_.call(this, 'sendonly', options, callback);
- }
- inherits(WebRtcPeerSendonly, WebRtcPeer);
- function WebRtcPeerSendrecv(options, callback) {
- if (!(this instanceof WebRtcPeerSendrecv)) {
- return new WebRtcPeerSendrecv(options, callback);
- }
- WebRtcPeerSendrecv.super_.call(this, 'sendrecv', options, callback);
- }
- inherits(WebRtcPeerSendrecv, WebRtcPeer);
- function harkUtils(stream, options) {
- return hark(stream, options);
- }
- exports.bufferizeCandidates = bufferizeCandidates;
- exports.WebRtcPeerRecvonly = WebRtcPeerRecvonly;
- exports.WebRtcPeerSendonly = WebRtcPeerSendonly;
- exports.WebRtcPeerSendrecv = WebRtcPeerSendrecv;
- exports.hark = harkUtils;
- },{"events":4,"freeice":5,"hark":8,"inherits":9,"kurento-browser-extensions":10,"merge":11,"sdp-translator":18,"ua-parser-js":21,"uuid/v4":24}],2:[function(require,module,exports){
- if (window.addEventListener)
- module.exports = require('./index');
- },{"./index":3}],3:[function(require,module,exports){
- var WebRtcPeer = require('./WebRtcPeer');
- exports.WebRtcPeer = WebRtcPeer;
- },{"./WebRtcPeer":1}],4:[function(require,module,exports){
- // Copyright Joyent, Inc. and other Node contributors.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a
- // copy of this software and associated documentation files (the
- // "Software"), to deal in the Software without restriction, including
- // without limitation the rights to use, copy, modify, merge, publish,
- // distribute, sublicense, and/or sell copies of the Software, and to permit
- // persons to whom the Software is furnished to do so, subject to the
- // following conditions:
- //
- // The above copyright notice and this permission notice shall be included
- // in all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
- // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
- // USE OR OTHER DEALINGS IN THE SOFTWARE.
- var objectCreate = Object.create || objectCreatePolyfill
- var objectKeys = Object.keys || objectKeysPolyfill
- var bind = Function.prototype.bind || functionBindPolyfill
- function EventEmitter() {
- if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) {
- this._events = objectCreate(null);
- this._eventsCount = 0;
- }
- this._maxListeners = this._maxListeners || undefined;
- }
- module.exports = EventEmitter;
- // Backwards-compat with node 0.10.x
- EventEmitter.EventEmitter = EventEmitter;
- EventEmitter.prototype._events = undefined;
- EventEmitter.prototype._maxListeners = undefined;
- // By default EventEmitters will print a warning if more than 10 listeners are
- // added to it. This is a useful default which helps finding memory leaks.
- var defaultMaxListeners = 10;
- var hasDefineProperty;
- try {
- var o = {};
- if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 });
- hasDefineProperty = o.x === 0;
- } catch (err) { hasDefineProperty = false }
- if (hasDefineProperty) {
- Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
- enumerable: true,
- get: function() {
- return defaultMaxListeners;
- },
- set: function(arg) {
- // check whether the input is a positive number (whose value is zero or
- // greater and not a NaN).
- if (typeof arg !== 'number' || arg < 0 || arg !== arg)
- throw new TypeError('"defaultMaxListeners" must be a positive number');
- defaultMaxListeners = arg;
- }
- });
- } else {
- EventEmitter.defaultMaxListeners = defaultMaxListeners;
- }
- // Obviously not all Emitters should be limited to 10. This function allows
- // that to be increased. Set to zero for unlimited.
- EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
- if (typeof n !== 'number' || n < 0 || isNaN(n))
- throw new TypeError('"n" argument must be a positive number');
- this._maxListeners = n;
- return this;
- };
- function $getMaxListeners(that) {
- if (that._maxListeners === undefined)
- return EventEmitter.defaultMaxListeners;
- return that._maxListeners;
- }
- EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
- return $getMaxListeners(this);
- };
- // These standalone emit* functions are used to optimize calling of event
- // handlers for fast cases because emit() itself often has a variable number of
- // arguments and can be deoptimized because of that. These functions always have
- // the same number of arguments and thus do not get deoptimized, so the code
- // inside them can execute faster.
- function emitNone(handler, isFn, self) {
- if (isFn)
- handler.call(self);
- else {
- var len = handler.length;
- var listeners = arrayClone(handler, len);
- for (var i = 0; i < len; ++i)
- listeners[i].call(self);
- }
- }
- function emitOne(handler, isFn, self, arg1) {
- if (isFn)
- handler.call(self, arg1);
- else {
- var len = handler.length;
- var listeners = arrayClone(handler, len);
- for (var i = 0; i < len; ++i)
- listeners[i].call(self, arg1);
- }
- }
- function emitTwo(handler, isFn, self, arg1, arg2) {
- if (isFn)
- handler.call(self, arg1, arg2);
- else {
- var len = handler.length;
- var listeners = arrayClone(handler, len);
- for (var i = 0; i < len; ++i)
- listeners[i].call(self, arg1, arg2);
- }
- }
- function emitThree(handler, isFn, self, arg1, arg2, arg3) {
- if (isFn)
- handler.call(self, arg1, arg2, arg3);
- else {
- var len = handler.length;
- var listeners = arrayClone(handler, len);
- for (var i = 0; i < len; ++i)
- listeners[i].call(self, arg1, arg2, arg3);
- }
- }
- function emitMany(handler, isFn, self, args) {
- if (isFn)
- handler.apply(self, args);
- else {
- var len = handler.length;
- var listeners = arrayClone(handler, len);
- for (var i = 0; i < len; ++i)
- listeners[i].apply(self, args);
- }
- }
- EventEmitter.prototype.emit = function emit(type) {
- var er, handler, len, args, i, events;
- var doError = (type === 'error');
- events = this._events;
- if (events)
- doError = (doError && events.error == null);
- else if (!doError)
- return false;
- // If there is no 'error' event listener then throw.
- if (doError) {
- if (arguments.length > 1)
- er = arguments[1];
- if (er instanceof Error) {
- throw er; // Unhandled 'error' event
- } else {
- // At least give some kind of context to the user
- var err = new Error('Unhandled "error" event. (' + er + ')');
- err.context = er;
- throw err;
- }
- return false;
- }
- handler = events[type];
- if (!handler)
- return false;
- var isFn = typeof handler === 'function';
- len = arguments.length;
- switch (len) {
- // fast cases
- case 1:
- emitNone(handler, isFn, this);
- break;
- case 2:
- emitOne(handler, isFn, this, arguments[1]);
- break;
- case 3:
- emitTwo(handler, isFn, this, arguments[1], arguments[2]);
- break;
- case 4:
- emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
- break;
- // slower
- default:
- args = new Array(len - 1);
- for (i = 1; i < len; i++)
- args[i - 1] = arguments[i];
- emitMany(handler, isFn, this, args);
- }
- return true;
- };
- function _addListener(target, type, listener, prepend) {
- var m;
- var events;
- var existing;
- if (typeof listener !== 'function')
- throw new TypeError('"listener" argument must be a function');
- events = target._events;
- if (!events) {
- events = target._events = objectCreate(null);
- target._eventsCount = 0;
- } else {
- // To avoid recursion in the case that type === "newListener"! Before
- // adding it to the listeners, first emit "newListener".
- if (events.newListener) {
- target.emit('newListener', type,
- listener.listener ? listener.listener : listener);
- // Re-assign `events` because a newListener handler could have caused the
- // this._events to be assigned to a new object
- events = target._events;
- }
- existing = events[type];
- }
- if (!existing) {
- // Optimize the case of one listener. Don't need the extra array object.
- existing = events[type] = listener;
- ++target._eventsCount;
- } else {
- if (typeof existing === 'function') {
- // Adding the second element, need to change to array.
- existing = events[type] =
- prepend ? [listener, existing] : [existing, listener];
- } else {
- // If we've already got an array, just append.
- if (prepend) {
- existing.unshift(listener);
- } else {
- existing.push(listener);
- }
- }
- // Check for listener leak
- if (!existing.warned) {
- m = $getMaxListeners(target);
- if (m && m > 0 && existing.length > m) {
- existing.warned = true;
- var w = new Error('Possible EventEmitter memory leak detected. ' +
- existing.length + ' "' + String(type) + '" listeners ' +
- 'added. Use emitter.setMaxListeners() to ' +
- 'increase limit.');
- w.name = 'MaxListenersExceededWarning';
- w.emitter = target;
- w.type = type;
- w.count = existing.length;
- if (typeof console === 'object' && console.warn) {
- console.warn('%s: %s', w.name, w.message);
- }
- }
- }
- }
- return target;
- }
- EventEmitter.prototype.addListener = function addListener(type, listener) {
- return _addListener(this, type, listener, false);
- };
- EventEmitter.prototype.on = EventEmitter.prototype.addListener;
- EventEmitter.prototype.prependListener =
- function prependListener(type, listener) {
- return _addListener(this, type, listener, true);
- };
- function onceWrapper() {
- if (!this.fired) {
- this.target.removeListener(this.type, this.wrapFn);
- this.fired = true;
- switch (arguments.length) {
- case 0:
- return this.listener.call(this.target);
- case 1:
- return this.listener.call(this.target, arguments[0]);
- case 2:
- return this.listener.call(this.target, arguments[0], arguments[1]);
- case 3:
- return this.listener.call(this.target, arguments[0], arguments[1],
- arguments[2]);
- default:
- var args = new Array(arguments.length);
- for (var i = 0; i < args.length; ++i)
- args[i] = arguments[i];
- this.listener.apply(this.target, args);
- }
- }
- }
- function _onceWrap(target, type, listener) {
- var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
- var wrapped = bind.call(onceWrapper, state);
- wrapped.listener = listener;
- state.wrapFn = wrapped;
- return wrapped;
- }
- EventEmitter.prototype.once = function once(type, listener) {
- if (typeof listener !== 'function')
- throw new TypeError('"listener" argument must be a function');
- this.on(type, _onceWrap(this, type, listener));
- return this;
- };
- EventEmitter.prototype.prependOnceListener =
- function prependOnceListener(type, listener) {
- if (typeof listener !== 'function')
- throw new TypeError('"listener" argument must be a function');
- this.prependListener(type, _onceWrap(this, type, listener));
- return this;
- };
- // Emits a 'removeListener' event if and only if the listener was removed.
- EventEmitter.prototype.removeListener =
- function removeListener(type, listener) {
- var list, events, position, i, originalListener;
- if (typeof listener !== 'function')
- throw new TypeError('"listener" argument must be a function');
- events = this._events;
- if (!events)
- return this;
- list = events[type];
- if (!list)
- return this;
- if (list === listener || list.listener === listener) {
- if (--this._eventsCount === 0)
- this._events = objectCreate(null);
- else {
- delete events[type];
- if (events.removeListener)
- this.emit('removeListener', type, list.listener || listener);
- }
- } else if (typeof list !== 'function') {
- position = -1;
- for (i = list.length - 1; i >= 0; i--) {
- if (list[i] === listener || list[i].listener === listener) {
- originalListener = list[i].listener;
- position = i;
- break;
- }
- }
- if (position < 0)
- return this;
- if (position === 0)
- list.shift();
- else
- spliceOne(list, position);
- if (list.length === 1)
- events[type] = list[0];
- if (events.removeListener)
- this.emit('removeListener', type, originalListener || listener);
- }
- return this;
- };
- EventEmitter.prototype.removeAllListeners =
- function removeAllListeners(type) {
- var listeners, events, i;
- events = this._events;
- if (!events)
- return this;
- // not listening for removeListener, no need to emit
- if (!events.removeListener) {
- if (arguments.length === 0) {
- this._events = objectCreate(null);
- this._eventsCount = 0;
- } else if (events[type]) {
- if (--this._eventsCount === 0)
- this._events = objectCreate(null);
- else
- delete events[type];
- }
- return this;
- }
- // emit removeListener for all listeners on all events
- if (arguments.length === 0) {
- var keys = objectKeys(events);
- var key;
- for (i = 0; i < keys.length; ++i) {
- key = keys[i];
- if (key === 'removeListener') continue;
- this.removeAllListeners(key);
- }
- this.removeAllListeners('removeListener');
- this._events = objectCreate(null);
- this._eventsCount = 0;
- return this;
- }
- listeners = events[type];
- if (typeof listeners === 'function') {
- this.removeListener(type, listeners);
- } else if (listeners) {
- // LIFO order
- for (i = listeners.length - 1; i >= 0; i--) {
- this.removeListener(type, listeners[i]);
- }
- }
- return this;
- };
- function _listeners(target, type, unwrap) {
- var events = target._events;
- if (!events)
- return [];
- var evlistener = events[type];
- if (!evlistener)
- return [];
- if (typeof evlistener === 'function')
- return unwrap ? [evlistener.listener || evlistener] : [evlistener];
- return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
- }
- EventEmitter.prototype.listeners = function listeners(type) {
- return _listeners(this, type, true);
- };
- EventEmitter.prototype.rawListeners = function rawListeners(type) {
- return _listeners(this, type, false);
- };
- EventEmitter.listenerCount = function(emitter, type) {
- if (typeof emitter.listenerCount === 'function') {
- return emitter.listenerCount(type);
- } else {
- return listenerCount.call(emitter, type);
- }
- };
- EventEmitter.prototype.listenerCount = listenerCount;
- function listenerCount(type) {
- var events = this._events;
- if (events) {
- var evlistener = events[type];
- if (typeof evlistener === 'function') {
- return 1;
- } else if (evlistener) {
- return evlistener.length;
- }
- }
- return 0;
- }
- EventEmitter.prototype.eventNames = function eventNames() {
- return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
- };
- // About 1.5x faster than the two-arg version of Array#splice().
- function spliceOne(list, index) {
- for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
- list[i] = list[k];
- list.pop();
- }
- function arrayClone(arr, n) {
- var copy = new Array(n);
- for (var i = 0; i < n; ++i)
- copy[i] = arr[i];
- return copy;
- }
- function unwrapListeners(arr) {
- var ret = new Array(arr.length);
- for (var i = 0; i < ret.length; ++i) {
- ret[i] = arr[i].listener || arr[i];
- }
- return ret;
- }
- function objectCreatePolyfill(proto) {
- var F = function() {};
- F.prototype = proto;
- return new F;
- }
- function objectKeysPolyfill(obj) {
- var keys = [];
- for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) {
- keys.push(k);
- }
- return k;
- }
- function functionBindPolyfill(context) {
- var fn = this;
- return function () {
- return fn.apply(context, arguments);
- };
- }
- },{}],5:[function(require,module,exports){
- /* jshint node: true */
- 'use strict';
- var normalice = require('normalice');
- /**
- # freeice
- The `freeice` module is a simple way of getting random STUN or TURN server
- for your WebRTC application. The list of servers (just STUN at this stage)
- were sourced from this [gist](https://gist.github.com/zziuni/3741933).
- ## Example Use
- The following demonstrates how you can use `freeice` with
- [rtc-quickconnect](https://github.com/rtc-io/rtc-quickconnect):
- <<< examples/quickconnect.js
- As the `freeice` module generates ice servers in a list compliant with the
- WebRTC spec you will be able to use it with raw `RTCPeerConnection`
- constructors and other WebRTC libraries.
- ## Hey, don't use my STUN/TURN server!
- If for some reason your free STUN or TURN server ends up in the
- list of servers ([stun](https://github.com/DamonOehlman/freeice/blob/master/stun.json) or
- [turn](https://github.com/DamonOehlman/freeice/blob/master/turn.json))
- that is used in this module, you can feel
- free to open an issue on this repository and those servers will be removed
- within 24 hours (or sooner). This is the quickest and probably the most
- polite way to have something removed (and provides us some visibility
- if someone opens a pull request requesting that a server is added).
- ## Please add my server!
- If you have a server that you wish to add to the list, that's awesome! I'm
- sure I speak on behalf of a whole pile of WebRTC developers who say thanks.
- To get it into the list, feel free to either open a pull request or if you
- find that process a bit daunting then just create an issue requesting
- the addition of the server (make sure you provide all the details, and if
- you have a Terms of Service then including that in the PR/issue would be
- awesome).
- ## I know of a free server, can I add it?
- Sure, if you do your homework and make sure it is ok to use (I'm currently
- in the process of reviewing the terms of those STUN servers included from
- the original list). If it's ok to go, then please see the previous entry
- for how to add it.
- ## Current List of Servers
- * current as at the time of last `README.md` file generation
- ### STUN
- <<< stun.json
- ### TURN
- <<< turn.json
- **/
- var freeice = function(opts) {
- // if a list of servers has been provided, then use it instead of defaults
- var servers = {
- stun: (opts || {}).stun || require('./stun.json'),
- turn: (opts || {}).turn || require('./turn.json')
- };
- var stunCount = (opts || {}).stunCount || 2;
- var turnCount = (opts || {}).turnCount || 0;
- var selected;
- function getServers(type, count) {
- var out = [];
- var input = [].concat(servers[type]);
- var idx;
- while (input.length && out.length < count) {
- idx = (Math.random() * input.length) | 0;
- out = out.concat(input.splice(idx, 1));
- }
- return out.map(function(url) {
- //If it's a not a string, don't try to "normalice" it otherwise using type:url will screw it up
- if ((typeof url !== 'string') && (! (url instanceof String))) {
- return url;
- } else {
- return normalice(type + ':' + url);
- }
- });
- }
- // add stun servers
- selected = [].concat(getServers('stun', stunCount));
- if (turnCount) {
- selected = selected.concat(getServers('turn', turnCount));
- }
- return selected;
- };
- module.exports = freeice;
- },{"./stun.json":6,"./turn.json":7,"normalice":12}],6:[function(require,module,exports){
- 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"
- ]
- },{}],7:[function(require,module,exports){
- module.exports=[]
- },{}],8:[function(require,module,exports){
- var WildEmitter = require('wildemitter');
- function getMaxVolume (analyser, fftBins) {
- var maxVolume = -Infinity;
- analyser.getFloatFrequencyData(fftBins);
- for(var i=4, ii=fftBins.length; i < ii; i++) {
- if (fftBins[i] > maxVolume && fftBins[i] < 0) {
- maxVolume = fftBins[i];
- }
- };
- return maxVolume;
- }
- var audioContextType;
- if (typeof window !== 'undefined') {
- audioContextType = window.AudioContext || window.webkitAudioContext;
- }
- // use a single audio context due to hardware limits
- var audioContext = null;
- module.exports = function(stream, options) {
- var harker = new WildEmitter();
- // make it not break in non-supported browsers
- if (!audioContextType) return harker;
- //Config
- var options = options || {},
- smoothing = (options.smoothing || 0.1),
- interval = (options.interval || 50),
- threshold = options.threshold,
- play = options.play,
- history = options.history || 10,
- running = true;
- // Ensure that just a single AudioContext is internally created
- audioContext = options.audioContext || audioContext || new audioContextType();
- var sourceNode, fftBins, analyser;
- analyser = audioContext.createAnalyser();
- analyser.fftSize = 512;
- analyser.smoothingTimeConstant = smoothing;
- fftBins = new Float32Array(analyser.frequencyBinCount);
- if (stream.jquery) stream = stream[0];
- if (stream instanceof HTMLAudioElement || stream instanceof HTMLVideoElement) {
- //Audio Tag
- sourceNode = audioContext.createMediaElementSource(stream);
- if (typeof play === 'undefined') play = true;
- threshold = threshold || -50;
- } else {
- //WebRTC Stream
- sourceNode = audioContext.createMediaStreamSource(stream);
- threshold = threshold || -50;
- }
- sourceNode.connect(analyser);
- if (play) analyser.connect(audioContext.destination);
- harker.speaking = false;
- harker.suspend = function() {
- return audioContext.suspend();
- }
- harker.resume = function() {
- return audioContext.resume();
- }
- Object.defineProperty(harker, 'state', { get: function() {
- return audioContext.state;
- }});
- audioContext.onstatechange = function() {
- harker.emit('state_change', audioContext.state);
- }
- harker.setThreshold = function(t) {
- threshold = t;
- };
- harker.setInterval = function(i) {
- interval = i;
- };
- harker.stop = function() {
- running = false;
- harker.emit('volume_change', -100, threshold);
- if (harker.speaking) {
- harker.speaking = false;
- harker.emit('stopped_speaking');
- }
- analyser.disconnect();
- sourceNode.disconnect();
- };
- harker.speakingHistory = [];
- for (var i = 0; i < history; i++) {
- harker.speakingHistory.push(0);
- }
- // Poll the analyser node to determine if speaking
- // and emit events if changed
- var looper = function() {
- setTimeout(function() {
- //check if stop has been called
- if(!running) {
- return;
- }
- var currentVolume = getMaxVolume(analyser, fftBins);
- harker.emit('volume_change', currentVolume, threshold);
- var history = 0;
- if (currentVolume > threshold && !harker.speaking) {
- // trigger quickly, short history
- for (var i = harker.speakingHistory.length - 3; i < harker.speakingHistory.length; i++) {
- history += harker.speakingHistory[i];
- }
- if (history >= 2) {
- harker.speaking = true;
- harker.emit('speaking');
- }
- } else if (currentVolume < threshold && harker.speaking) {
- for (var i = 0; i < harker.speakingHistory.length; i++) {
- history += harker.speakingHistory[i];
- }
- if (history == 0) {
- harker.speaking = false;
- harker.emit('stopped_speaking');
- }
- }
- harker.speakingHistory.shift();
- harker.speakingHistory.push(0 + (currentVolume > threshold));
- looper();
- }, interval);
- };
- looper();
- return harker;
- }
- },{"wildemitter":25}],9:[function(require,module,exports){
- if (typeof Object.create === 'function') {
- // implementation from standard node.js 'util' module
- module.exports = function inherits(ctor, superCtor) {
- if (superCtor) {
- ctor.super_ = superCtor
- ctor.prototype = Object.create(superCtor.prototype, {
- constructor: {
- value: ctor,
- enumerable: false,
- writable: true,
- configurable: true
- }
- })
- }
- };
- } else {
- // old school shim for old browsers
- module.exports = function inherits(ctor, superCtor) {
- if (superCtor) {
- ctor.super_ = superCtor
- var TempCtor = function () {}
- TempCtor.prototype = superCtor.prototype
- ctor.prototype = new TempCtor()
- ctor.prototype.constructor = ctor
- }
- }
- }
- },{}],10:[function(require,module,exports){
- // Does nothing at all.
- },{}],11:[function(require,module,exports){
- /*!
- * @name JavaScript/NodeJS Merge v1.2.1
- * @author yeikos
- * @repository https://github.com/yeikos/js.merge
- * Copyright 2014 yeikos - MIT license
- * https://raw.github.com/yeikos/js.merge/master/LICENSE
- */
- ;(function(isNode) {
- /**
- * Merge one or more objects
- * @param bool? clone
- * @param mixed,... arguments
- * @return object
- */
- var Public = function(clone) {
- return merge(clone === true, false, arguments);
- }, publicName = 'merge';
- /**
- * Merge two or more objects recursively
- * @param bool? clone
- * @param mixed,... arguments
- * @return object
- */
- Public.recursive = function(clone) {
- return merge(clone === true, true, arguments);
- };
- /**
- * Clone the input removing any reference
- * @param mixed input
- * @return mixed
- */
- Public.clone = function(input) {
- var output = input,
- type = typeOf(input),
- index, size;
- if (type === 'array') {
- output = [];
- size = input.length;
- for (index=0;index<size;++index)
- output[index] = Public.clone(input[index]);
- } else if (type === 'object') {
- output = {};
- for (index in input)
- output[index] = Public.clone(input[index]);
- }
- return output;
- };
- /**
- * Merge two objects recursively
- * @param mixed input
- * @param mixed extend
- * @return mixed
- */
- function merge_recursive(base, extend) {
- if (typeOf(base) !== 'object')
- return extend;
- for (var key in extend) {
- if (typeOf(base[key]) === 'object' && typeOf(extend[key]) === 'object') {
- base[key] = merge_recursive(base[key], extend[key]);
- } else {
- base[key] = extend[key];
- }
- }
- return base;
- }
- /**
- * Merge two or more objects
- * @param bool clone
- * @param bool recursive
- * @param array argv
- * @return object
- */
- function merge(clone, recursive, argv) {
- var result = argv[0],
- size = argv.length;
- if (clone || typeOf(result) !== 'object')
- result = {};
- for (var index=0;index<size;++index) {
- var item = argv[index],
- type = typeOf(item);
- if (type !== 'object') continue;
- for (var key in item) {
- if (key === '__proto__') continue;
- var sitem = clone ? Public.clone(item[key]) : item[key];
- if (recursive) {
- result[key] = merge_recursive(result[key], sitem);
- } else {
- result[key] = sitem;
- }
- }
- }
- return result;
- }
- /**
- * Get type of variable
- * @param mixed input
- * @return string
- *
- * @see http://jsperf.com/typeofvar
- */
- function typeOf(input) {
- return ({}).toString.call(input).slice(8, -1).toLowerCase();
- }
- if (isNode) {
- module.exports = Public;
- } else {
- window[publicName] = Public;
- }
- })(typeof module === 'object' && module && typeof module.exports === 'object' && module.exports);
- },{}],12:[function(require,module,exports){
- /**
- # normalice
- Normalize an ice server configuration object (or plain old string) into a format
- that is usable in all browsers supporting WebRTC. Primarily this module is designed
- to help with the transition of the `url` attribute of the configuration object to
- the `urls` attribute.
- ## Example Usage
- <<< examples/simple.js
- **/
- var protocols = [
- 'stun:',
- 'turn:'
- ];
- module.exports = function(input) {
- var url = (input || {}).url || input;
- var protocol;
- var parts;
- var output = {};
- // if we don't have a string url, then allow the input to passthrough
- if (typeof url != 'string' && (! (url instanceof String))) {
- return input;
- }
- // trim the url string, and convert to an array
- url = url.trim();
- // if the protocol is not known, then passthrough
- protocol = protocols[protocols.indexOf(url.slice(0, 5))];
- if (! protocol) {
- return input;
- }
- // now let's attack the remaining url parts
- url = url.slice(5);
- parts = url.split('@');
- output.username = input.username;
- output.credential = input.credential;
- // if we have an authentication part, then set the credentials
- if (parts.length > 1) {
- url = parts[1];
- parts = parts[0].split(':');
- // add the output credential and username
- output.username = parts[0];
- output.credential = (input || {}).credential || parts[1] || '';
- }
- output.url = protocol + url;
- output.urls = [ output.url ];
- return output;
- };
- },{}],13:[function(require,module,exports){
- var grammar = module.exports = {
- v: [{
- name: 'version',
- reg: /^(\d*)$/
- }],
- o: [{ //o=- 20518 0 IN IP4 203.0.113.1
- // NB: sessionId will be a String in most cases because it is huge
- name: 'origin',
- reg: /^(\S*) (\d*) (\d*) (\S*) IP(\d) (\S*)/,
- names: ['username', 'sessionId', 'sessionVersion', 'netType', 'ipVer', 'address'],
- format: "%s %s %d %s IP%d %s"
- }],
- // default parsing of these only (though some of these feel outdated)
- s: [{ name: 'name' }],
- i: [{ name: 'description' }],
- u: [{ name: 'uri' }],
- e: [{ name: 'email' }],
- p: [{ name: 'phone' }],
- z: [{ name: 'timezones' }], // TODO: this one can actually be parsed properly..
- r: [{ name: 'repeats' }], // TODO: this one can also be parsed properly
- //k: [{}], // outdated thing ignored
- t: [{ //t=0 0
- name: 'timing',
- reg: /^(\d*) (\d*)/,
- names: ['start', 'stop'],
- format: "%d %d"
- }],
- c: [{ //c=IN IP4 10.47.197.26
- name: 'connection',
- reg: /^IN IP(\d) (\S*)/,
- names: ['version', 'ip'],
- format: "IN IP%d %s"
- }],
- b: [{ //b=AS:4000
- push: 'bandwidth',
- reg: /^(TIAS|AS|CT|RR|RS):(\d*)/,
- names: ['type', 'limit'],
- format: "%s:%s"
- }],
- m: [{ //m=video 51744 RTP/AVP 126 97 98 34 31
- // NB: special - pushes to session
- // TODO: rtp/fmtp should be filtered by the payloads found here?
- reg: /^(\w*) (\d*) ([\w\/]*)(?: (.*))?/,
- names: ['type', 'port', 'protocol', 'payloads'],
- format: "%s %d %s %s"
- }],
- a: [
- { //a=rtpmap:110 opus/48000/2
- push: 'rtp',
- reg: /^rtpmap:(\d*) ([\w\-]*)(?:\s*\/(\d*)(?:\s*\/(\S*))?)?/,
- names: ['payload', 'codec', 'rate', 'encoding'],
- format: function (o) {
- return (o.encoding) ?
- "rtpmap:%d %s/%s/%s":
- o.rate ?
- "rtpmap:%d %s/%s":
- "rtpmap:%d %s";
- }
- },
- {
- //a=fmtp:108 profile-level-id=24;object=23;bitrate=64000
- //a=fmtp:111 minptime=10; useinbandfec=1
- push: 'fmtp',
- reg: /^fmtp:(\d*) ([\S| ]*)/,
- names: ['payload', 'config'],
- format: "fmtp:%d %s"
- },
- { //a=control:streamid=0
- name: 'control',
- reg: /^control:(.*)/,
- format: "control:%s"
- },
- { //a=rtcp:65179 IN IP4 193.84.77.194
- name: 'rtcp',
- reg: /^rtcp:(\d*)(?: (\S*) IP(\d) (\S*))?/,
- names: ['port', 'netType', 'ipVer', 'address'],
- format: function (o) {
- return (o.address != null) ?
- "rtcp:%d %s IP%d %s":
- "rtcp:%d";
- }
- },
- { //a=rtcp-fb:98 trr-int 100
- push: 'rtcpFbTrrInt',
- reg: /^rtcp-fb:(\*|\d*) trr-int (\d*)/,
- names: ['payload', 'value'],
- format: "rtcp-fb:%d trr-int %d"
- },
- { //a=rtcp-fb:98 nack rpsi
- push: 'rtcpFb',
- reg: /^rtcp-fb:(\*|\d*) ([\w-_]*)(?: ([\w-_]*))?/,
- names: ['payload', 'type', 'subtype'],
- format: function (o) {
- return (o.subtype != null) ?
- "rtcp-fb:%s %s %s":
- "rtcp-fb:%s %s";
- }
- },
- { //a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
- //a=extmap:1/recvonly URI-gps-string
- push: 'ext',
- reg: /^extmap:([\w_\/]*) (\S*)(?: (\S*))?/,
- names: ['value', 'uri', 'config'], // value may include "/direction" suffix
- format: function (o) {
- return (o.config != null) ?
- "extmap:%s %s %s":
- "extmap:%s %s";
- }
- },
- {
- //a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR|2^20|1:32
- push: 'crypto',
- reg: /^crypto:(\d*) ([\w_]*) (\S*)(?: (\S*))?/,
- names: ['id', 'suite', 'config', 'sessionConfig'],
- format: function (o) {
- return (o.sessionConfig != null) ?
- "crypto:%d %s %s %s":
- "crypto:%d %s %s";
- }
- },
- { //a=setup:actpass
- name: 'setup',
- reg: /^setup:(\w*)/,
- format: "setup:%s"
- },
- { //a=mid:1
- name: 'mid',
- reg: /^mid:([^\s]*)/,
- format: "mid:%s"
- },
- { //a=msid:0c8b064d-d807-43b4-b434-f92a889d8587 98178685-d409-46e0-8e16-7ef0db0db64a
- name: 'msid',
- reg: /^msid:(.*)/,
- format: "msid:%s"
- },
- { //a=ptime:20
- name: 'ptime',
- reg: /^ptime:(\d*)/,
- format: "ptime:%d"
- },
- { //a=maxptime:60
- name: 'maxptime',
- reg: /^maxptime:(\d*)/,
- format: "maxptime:%d"
- },
- { //a=sendrecv
- name: 'direction',
- reg: /^(sendrecv|recvonly|sendonly|inactive)/
- },
- { //a=ice-lite
- name: 'icelite',
- reg: /^(ice-lite)/
- },
- { //a=ice-ufrag:F7gI
- name: 'iceUfrag',
- reg: /^ice-ufrag:(\S*)/,
- format: "ice-ufrag:%s"
- },
- { //a=ice-pwd:x9cml/YzichV2+XlhiMu8g
- name: 'icePwd',
- reg: /^ice-pwd:(\S*)/,
- format: "ice-pwd:%s"
- },
- { //a=fingerprint:SHA-1 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33
- name: 'fingerprint',
- reg: /^fingerprint:(\S*) (\S*)/,
- names: ['type', 'hash'],
- format: "fingerprint:%s %s"
- },
- {
- //a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host
- //a=candidate:1162875081 1 udp 2113937151 192.168.34.75 60017 typ host generation 0
- //a=candidate:3289912957 2 udp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 generation 0
- //a=candidate:229815620 1 tcp 1518280447 192.168.150.19 60017 typ host tcptype active generation 0
- //a=candidate:3289912957 2 tcp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 tcptype passive generation 0
- push:'candidates',
- reg: /^candidate:(\S*) (\d*) (\S*) (\d*) (\S*) (\d*) typ (\S*)(?: raddr (\S*) rport (\d*))?(?: tcptype (\S*))?(?: generation (\d*))?/,
- names: ['foundation', 'component', 'transport', 'priority', 'ip', 'port', 'type', 'raddr', 'rport', 'tcptype', 'generation'],
- format: function (o) {
- var str = "candidate:%s %d %s %d %s %d typ %s";
- str += (o.raddr != null) ? " raddr %s rport %d" : "%v%v";
- // NB: candidate has three optional chunks, so %void middles one if it's missing
- str += (o.tcptype != null) ? " tcptype %s" : "%v";
- if (o.generation != null) {
- str += " generation %d";
- }
- return str;
- }
- },
- { //a=end-of-candidates (keep after the candidates line for readability)
- name: 'endOfCandidates',
- reg: /^(end-of-candidates)/
- },
- { //a=remote-candidates:1 203.0.113.1 54400 2 203.0.113.1 54401 ...
- name: 'remoteCandidates',
- reg: /^remote-candidates:(.*)/,
- format: "remote-candidates:%s"
- },
- { //a=ice-options:google-ice
- name: 'iceOptions',
- reg: /^ice-options:(\S*)/,
- format: "ice-options:%s"
- },
- { //a=ssrc:2566107569 cname:t9YU8M1UxTF8Y1A1
- push: "ssrcs",
- reg: /^ssrc:(\d*) ([\w_]*):(.*)/,
- names: ['id', 'attribute', 'value'],
- format: "ssrc:%d %s:%s"
- },
- { //a=ssrc-group:FEC 1 2
- push: "ssrcGroups",
- reg: /^ssrc-group:(\w*) (.*)/,
- names: ['semantics', 'ssrcs'],
- format: "ssrc-group:%s %s"
- },
- { //a=msid-semantic: WMS Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV
- name: "msidSemantic",
- reg: /^msid-semantic:\s?(\w*) (\S*)/,
- names: ['semantic', 'token'],
- format: "msid-semantic: %s %s" // space after ":" is not accidental
- },
- { //a=group:BUNDLE audio video
- push: 'groups',
- reg: /^group:(\w*) (.*)/,
- names: ['type', 'mids'],
- format: "group:%s %s"
- },
- { //a=rtcp-mux
- name: 'rtcpMux',
- reg: /^(rtcp-mux)/
- },
- { //a=rtcp-rsize
- name: 'rtcpRsize',
- reg: /^(rtcp-rsize)/
- },
- { // any a= that we don't understand is kepts verbatim on media.invalid
- push: 'invalid',
- names: ["value"]
- }
- ]
- };
- // set sensible defaults to avoid polluting the grammar with boring details
- Object.keys(grammar).forEach(function (key) {
- var objs = grammar[key];
- objs.forEach(function (obj) {
- if (!obj.reg) {
- obj.reg = /(.*)/;
- }
- if (!obj.format) {
- obj.format = "%s";
- }
- });
- });
- },{}],14:[function(require,module,exports){
- var parser = require('./parser');
- var writer = require('./writer');
- exports.write = writer;
- exports.parse = parser.parse;
- exports.parseFmtpConfig = parser.parseFmtpConfig;
- exports.parsePayloads = parser.parsePayloads;
- exports.parseRemoteCandidates = parser.parseRemoteCandidates;
- },{"./parser":15,"./writer":16}],15:[function(require,module,exports){
- var toIntIfInt = function (v) {
- return String(Number(v)) === v ? Number(v) : v;
- };
- var attachProperties = function (match, location, names, rawName) {
- if (rawName && !names) {
- location[rawName] = toIntIfInt(match[1]);
- }
- else {
- for (var i = 0; i < names.length; i += 1) {
- if (match[i+1] != null) {
- location[names[i]] = toIntIfInt(match[i+1]);
- }
- }
- }
- };
- var parseReg = function (obj, location, content) {
- var needsBlank = obj.name && obj.names;
- if (obj.push && !location[obj.push]) {
- location[obj.push] = [];
- }
- else if (needsBlank && !location[obj.name]) {
- location[obj.name] = {};
- }
- var keyLocation = obj.push ?
- {} : // blank object that will be pushed
- needsBlank ? location[obj.name] : location; // otherwise, named location or root
- attachProperties(content.match(obj.reg), keyLocation, obj.names, obj.name);
- if (obj.push) {
- location[obj.push].push(keyLocation);
- }
- };
- var grammar = require('./grammar');
- var validLine = RegExp.prototype.test.bind(/^([a-z])=(.*)/);
- exports.parse = function (sdp) {
- var session = {}
- , media = []
- , location = session; // points at where properties go under (one of the above)
- // parse lines we understand
- sdp.split(/(\r\n|\r|\n)/).filter(validLine).forEach(function (l) {
- var type = l[0];
- var content = l.slice(2);
- if (type === 'm') {
- media.push({rtp: [], fmtp: []});
- location = media[media.length-1]; // point at latest media line
- }
- for (var j = 0; j < (grammar[type] || []).length; j += 1) {
- var obj = grammar[type][j];
- if (obj.reg.test(content)) {
- return parseReg(obj, location, content);
- }
- }
- });
- session.media = media; // link it up
- return session;
- };
- var fmtpReducer = function (acc, expr) {
- var s = expr.split('=');
- if (s.length === 2) {
- acc[s[0]] = toIntIfInt(s[1]);
- }
- return acc;
- };
- exports.parseFmtpConfig = function (str) {
- return str.split(/\;\s?/).reduce(fmtpReducer, {});
- };
- exports.parsePayloads = function (str) {
- return str.split(' ').map(Number);
- };
- exports.parseRemoteCandidates = function (str) {
- var candidates = [];
- var parts = str.split(' ').map(toIntIfInt);
- for (var i = 0; i < parts.length; i += 3) {
- candidates.push({
- component: parts[i],
- ip: parts[i + 1],
- port: parts[i + 2]
- });
- }
- return candidates;
- };
- },{"./grammar":13}],16:[function(require,module,exports){
- var grammar = require('./grammar');
- // customized util.format - discards excess arguments and can void middle ones
- var formatRegExp = /%[sdv%]/g;
- var format = function (formatStr) {
- var i = 1;
- var args = arguments;
- var len = args.length;
- return formatStr.replace(formatRegExp, function (x) {
- if (i >= len) {
- return x; // missing argument
- }
- var arg = args[i];
- i += 1;
- switch (x) {
- case '%%':
- return '%';
- case '%s':
- return String(arg);
- case '%d':
- return Number(arg);
- case '%v':
- return '';
- }
- });
- // NB: we discard excess arguments - they are typically undefined from makeLine
- };
- var makeLine = function (type, obj, location) {
- var str = obj.format instanceof Function ?
- (obj.format(obj.push ? location : location[obj.name])) :
- obj.format;
- var args = [type + '=' + str];
- if (obj.names) {
- for (var i = 0; i < obj.names.length; i += 1) {
- var n = obj.names[i];
- if (obj.name) {
- args.push(location[obj.name][n]);
- }
- else { // for mLine and push attributes
- args.push(location[obj.names[i]]);
- }
- }
- }
- else {
- args.push(location[obj.name]);
- }
- return format.apply(null, args);
- };
- // RFC specified order
- // TODO: extend this with all the rest
- var defaultOuterOrder = [
- 'v', 'o', 's', 'i',
- 'u', 'e', 'p', 'c',
- 'b', 't', 'r', 'z', 'a'
- ];
- var defaultInnerOrder = ['i', 'c', 'b', 'a'];
- module.exports = function (session, opts) {
- opts = opts || {};
- // ensure certain properties exist
- if (session.version == null) {
- session.version = 0; // "v=0" must be there (only defined version atm)
- }
- if (session.name == null) {
- session.name = " "; // "s= " must be there if no meaningful name set
- }
- session.media.forEach(function (mLine) {
- if (mLine.payloads == null) {
- mLine.payloads = "";
- }
- });
- var outerOrder = opts.outerOrder || defaultOuterOrder;
- var innerOrder = opts.innerOrder || defaultInnerOrder;
- var sdp = [];
- // loop through outerOrder for matching properties on session
- outerOrder.forEach(function (type) {
- grammar[type].forEach(function (obj) {
- if (obj.name in session && session[obj.name] != null) {
- sdp.push(makeLine(type, obj, session));
- }
- else if (obj.push in session && session[obj.push] != null) {
- session[obj.push].forEach(function (el) {
- sdp.push(makeLine(type, obj, el));
- });
- }
- });
- });
- // then for each media line, follow the innerOrder
- session.media.forEach(function (mLine) {
- sdp.push(makeLine('m', grammar.m[0], mLine));
- innerOrder.forEach(function (type) {
- grammar[type].forEach(function (obj) {
- if (obj.name in mLine && mLine[obj.name] != null) {
- sdp.push(makeLine(type, obj, mLine));
- }
- else if (obj.push in mLine && mLine[obj.push] != null) {
- mLine[obj.push].forEach(function (el) {
- sdp.push(makeLine(type, obj, el));
- });
- }
- });
- });
- });
- return sdp.join('\r\n') + '\r\n';
- };
- },{"./grammar":13}],17:[function(require,module,exports){
- /* Copyright @ 2015 Atlassian Pty Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- module.exports = function arrayEquals(array) {
- // if the other array is a falsy value, return
- if (!array)
- return false;
- // compare lengths - can save a lot of time
- if (this.length != array.length)
- return false;
- for (var i = 0, l = this.length; i < l; i++) {
- // Check if we have nested arrays
- if (this[i] instanceof Array && array[i] instanceof Array) {
- // recurse into the nested arrays
- if (!arrayEquals.apply(this[i], [array[i]]))
- return false;
- } else if (this[i] != array[i]) {
- // Warning - two different object instances will never be equal:
- // {x:20} != {x:20}
- return false;
- }
- }
- return true;
- };
- },{}],18:[function(require,module,exports){
- /* Copyright @ 2015 Atlassian Pty Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- exports.Interop = require('./interop');
- },{"./interop":19}],19:[function(require,module,exports){
- /* Copyright @ 2015 Atlassian Pty Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /* global RTCSessionDescription */
- /* global RTCIceCandidate */
- /* jshint -W097 */
- "use strict";
- var transform = require('./transform');
- var arrayEquals = require('./array-equals');
- function Interop() {
- /**
- * This map holds the most recent Unified Plan offer/answer SDP that was
- * converted to Plan B, with the SDP type ('offer' or 'answer') as keys and
- * the SDP string as values.
- *
- * @type {{}}
- */
- this.cache = {
- mlB2UMap : {},
- mlU2BMap : {}
- };
- }
- module.exports = Interop;
- /**
- * Changes the candidate args to match with the related Unified Plan
- */
- Interop.prototype.candidateToUnifiedPlan = function(candidate) {
- var cand = new RTCIceCandidate(candidate);
- cand.sdpMLineIndex = this.cache.mlB2UMap[cand.sdpMLineIndex];
- /* TODO: change sdpMid to (audio|video)-SSRC */
- return cand;
- };
- /**
- * Changes the candidate args to match with the related Plan B
- */
- Interop.prototype.candidateToPlanB = function(candidate) {
- var cand = new RTCIceCandidate(candidate);
- if (cand.sdpMid.indexOf('audio') === 0) {
- cand.sdpMid = 'audio';
- } else if (cand.sdpMid.indexOf('video') === 0) {
- cand.sdpMid = 'video';
- } else {
- throw new Error('candidate with ' + cand.sdpMid + ' not allowed');
- }
- cand.sdpMLineIndex = this.cache.mlU2BMap[cand.sdpMLineIndex];
- return cand;
- };
- /**
- * Returns the index of the first m-line with the given media type and with a
- * direction which allows sending, in the last Unified Plan description with
- * type "answer" converted to Plan B. Returns {null} if there is no saved
- * answer, or if none of its m-lines with the given type allow sending.
- * @param type the media type ("audio" or "video").
- * @returns {*}
- */
- Interop.prototype.getFirstSendingIndexFromAnswer = function(type) {
- if (!this.cache.answer) {
- return null;
- }
- var session = transform.parse(this.cache.answer);
- if (session && session.media && Array.isArray(session.media)){
- for (var i = 0; i < session.media.length; i++) {
- if (session.media[i].type == type &&
- (!session.media[i].direction /* default to sendrecv */ ||
- session.media[i].direction === 'sendrecv' ||
- session.media[i].direction === 'sendonly')){
- return i;
- }
- }
- }
- return null;
- };
- /**
- * This method transforms a Unified Plan SDP to an equivalent Plan B SDP. A
- * PeerConnection wrapper transforms the SDP to Plan B before passing it to the
- * application.
- *
- * @param desc
- * @returns {*}
- */
- Interop.prototype.toPlanB = function(desc) {
- var self = this;
- //#region Preliminary input validation.
- if (typeof desc !== 'object' || desc === null ||
- typeof desc.sdp !== 'string') {
- console.warn('An empty description was passed as an argument.');
- return desc;
- }
- // Objectify the SDP for easier manipulation.
- var session = transform.parse(desc.sdp);
- // If the SDP contains no media, there's nothing to transform.
- if (typeof session.media === 'undefined' ||
- !Array.isArray(session.media) || session.media.length === 0) {
- console.warn('The description has no media.');
- return desc;
- }
- // Try some heuristics to "make sure" this is a Unified Plan SDP. Plan B
- // SDP has a video, an audio and a data "channel" at most.
- if (session.media.length <= 3 && session.media.every(function(m) {
- return ['video', 'audio', 'data'].indexOf(m.mid) !== -1;
- })) {
- console.warn('This description does not look like Unified Plan.');
- return desc;
- }
- //#endregion
- // HACK https://bugzilla.mozilla.org/show_bug.cgi?id=1113443
- var sdp = desc.sdp;
- var rewrite = false;
- for (var i = 0; i < session.media.length; i++) {
- var uLine = session.media[i];
- uLine.rtp.forEach(function(rtp) {
- if (rtp.codec === 'NULL')
- {
- rewrite = true;
- var offer = transform.parse(self.cache.offer);
- rtp.codec = offer.media[i].rtp[0].codec;
- }
- });
- }
- if (rewrite) {
- sdp = transform.write(session);
- }
- // Unified Plan SDP is our "precious". Cache it for later use in the Plan B
- // -> Unified Plan transformation.
- this.cache[desc.type] = sdp;
- //#region Convert from Unified Plan to Plan B.
- // We rebuild the session.media array.
- var media = session.media;
- session.media = [];
- // Associative array that maps channel types to channel objects for fast
- // access to channel objects by their type, e.g. type2bl['audio']->channel
- // obj.
- var type2bl = {};
- // Used to build the group:BUNDLE value after the channels construction
- // loop.
- var types = [];
- media.forEach(function(uLine) {
- // rtcp-mux is required in the Plan B SDP.
- if ((typeof uLine.rtcpMux !== 'string' ||
- uLine.rtcpMux !== 'rtcp-mux') &&
- uLine.direction !== 'inactive') {
- throw new Error('Cannot convert to Plan B because m-lines ' +
- 'without the rtcp-mux attribute were found.');
- }
- // If we don't have a channel for this uLine.type OR the selected is
- // inactive, then select this uLine as the channel basis.
- if (typeof type2bl[uLine.type] === 'undefined' ||
- type2bl[uLine.type].direction === 'inactive') {
- type2bl[uLine.type] = uLine;
- }
- if (uLine.protocol != type2bl[uLine.type].protocol) {
- throw new Error('Cannot convert to Plan B because m-lines ' +
- 'have different protocols and this library does not have ' +
- 'support for that');
- }
- if (uLine.payloads != type2bl[uLine.type].payloads) {
- throw new Error('Cannot convert to Plan B because m-lines ' +
- 'have different payloads and this library does not have ' +
- 'support for that');
- }
- });
- // Implode the Unified Plan m-lines/tracks into Plan B channels.
- media.forEach(function(uLine) {
- if (uLine.type === 'application') {
- session.media.push(uLine);
- types.push(uLine.mid);
- return;
- }
- // Add sources to the channel and handle a=msid.
- if (typeof uLine.sources === 'object') {
- Object.keys(uLine.sources).forEach(function(ssrc) {
- if (typeof type2bl[uLine.type].sources !== 'object')
- type2bl[uLine.type].sources = {};
- // Assign the sources to the channel.
- type2bl[uLine.type].sources[ssrc] =
- uLine.sources[ssrc];
- if (typeof uLine.msid !== 'undefined') {
- // In Plan B the msid is an SSRC attribute. Also, we don't
- // care about the obsolete label and mslabel attributes.
- //
- // Note that it is not guaranteed that the uLine will
- // have an msid. recvonly channels in particular don't have
- // one.
- type2bl[uLine.type].sources[ssrc].msid =
- uLine.msid;
- }
- // NOTE ssrcs in ssrc groups will share msids, as
- // draft-uberti-rtcweb-plan-00 mandates.
- });
- }
- // Add ssrc groups to the channel.
- if (typeof uLine.ssrcGroups !== 'undefined' &&
- Array.isArray(uLine.ssrcGroups)) {
- // Create the ssrcGroups array, if it's not defined.
- if (typeof type2bl[uLine.type].ssrcGroups === 'undefined' ||
- !Array.isArray(type2bl[uLine.type].ssrcGroups)) {
- type2bl[uLine.type].ssrcGroups = [];
- }
- type2bl[uLine.type].ssrcGroups =
- type2bl[uLine.type].ssrcGroups.concat(
- uLine.ssrcGroups);
- }
- if (type2bl[uLine.type] === uLine) {
- // Plan B mids are in ['audio', 'video', 'data']
- uLine.mid = uLine.type;
- // Plan B doesn't support/need the bundle-only attribute.
- delete uLine.bundleOnly;
- // In Plan B the msid is an SSRC attribute.
- delete uLine.msid;
- if (uLine.type == media[0].type) {
- types.unshift(uLine.type);
- // Add the channel to the new media array.
- session.media.unshift(uLine);
- } else {
- types.push(uLine.type);
- // Add the channel to the new media array.
- session.media.push(uLine);
- }
- }
- });
- if (typeof session.groups !== 'undefined') {
- // We regenerate the BUNDLE group with the new mids.
- session.groups.some(function(group) {
- if (group.type === 'BUNDLE') {
- group.mids = types.join(' ');
- return true;
- }
- });
- }
- // msid semantic
- session.msidSemantic = {
- semantic: 'WMS',
- token: '*'
- };
- var resStr = transform.write(session);
- return new RTCSessionDescription({
- type: desc.type,
- sdp: resStr
- });
- //#endregion
- };
- /* follow rules defined in RFC4145 */
- function addSetupAttr(uLine) {
- if (typeof uLine.setup === 'undefined') {
- return;
- }
- if (uLine.setup === "active") {
- uLine.setup = "passive";
- } else if (uLine.setup === "passive") {
- uLine.setup = "active";
- }
- }
- /**
- * This method transforms a Plan B SDP to an equivalent Unified Plan SDP. A
- * PeerConnection wrapper transforms the SDP to Unified Plan before passing it
- * to FF.
- *
- * @param desc
- * @returns {*}
- */
- Interop.prototype.toUnifiedPlan = function(desc) {
- var self = this;
- //#region Preliminary input validation.
- if (typeof desc !== 'object' || desc === null ||
- typeof desc.sdp !== 'string') {
- console.warn('An empty description was passed as an argument.');
- return desc;
- }
- var session = transform.parse(desc.sdp);
- // If the SDP contains no media, there's nothing to transform.
- if (typeof session.media === 'undefined' ||
- !Array.isArray(session.media) || session.media.length === 0) {
- console.warn('The description has no media.');
- return desc;
- }
- // Try some heuristics to "make sure" this is a Plan B SDP. Plan B SDP has
- // a video, an audio and a data "channel" at most.
- if (session.media.length > 3 || !session.media.every(function(m) {
- return ['video', 'audio', 'data'].indexOf(m.mid) !== -1;
- })) {
- console.warn('This description does not look like Plan B.');
- return desc;
- }
- // Make sure this Plan B SDP can be converted to a Unified Plan SDP.
- var mids = [];
- session.media.forEach(function(m) {
- mids.push(m.mid);
- });
- var hasBundle = false;
- if (typeof session.groups !== 'undefined' &&
- Array.isArray(session.groups)) {
- hasBundle = session.groups.every(function(g) {
- return g.type !== 'BUNDLE' ||
- arrayEquals.apply(g.mids.sort(), [mids.sort()]);
- });
- }
- if (!hasBundle) {
- var mustBeBundle = false;
- session.media.forEach(function(m) {
- if (m.direction !== 'inactive') {
- mustBeBundle = true;
- }
- });
- if (mustBeBundle) {
- throw new Error("Cannot convert to Unified Plan because m-lines that" +
- " are not bundled were found.");
- }
- }
- //#endregion
- //#region Convert from Plan B to Unified Plan.
- // Unfortunately, a Plan B offer/answer doesn't have enough information to
- // rebuild an equivalent Unified Plan offer/answer.
- //
- // For example, if this is a local answer (in Unified Plan style) that we
- // convert to Plan B prior to handing it over to the application (the
- // PeerConnection wrapper called us, for instance, after a successful
- // createAnswer), we want to remember the m-line at which we've seen the
- // (local) SSRC. That's because when the application wants to do call the
- // SLD method, forcing us to do the inverse transformation (from Plan B to
- // Unified Plan), we need to know to which m-line to assign the (local)
- // SSRC. We also need to know all the other m-lines that the original
- // answer had and include them in the transformed answer as well.
- //
- // Another example is if this is a remote offer that we convert to Plan B
- // prior to giving it to the application, we want to remember the mid at
- // which we've seen the (remote) SSRC.
- //
- // In the iteration that follows, we use the cached Unified Plan (if it
- // exists) to assign mids to ssrcs.
- var type;
- if (desc.type === 'answer') {
- type = 'offer';
- } else if (desc.type === 'offer') {
- type = 'answer';
- } else {
- throw new Error("Type '" + desc.type + "' not supported.");
- }
- var cached;
- if (typeof this.cache[type] !== 'undefined') {
- cached = transform.parse(this.cache[type]);
- }
- var recvonlySsrcs = {
- audio: {},
- video: {}
- };
- // A helper map that sends mids to m-line objects. We use it later to
- // rebuild the Unified Plan style session.media array.
- var mid2ul = {};
- var bIdx = 0;
- var uIdx = 0;
- var sources2ul = {};
- var candidates;
- var iceUfrag;
- var icePwd;
- var fingerprint;
- var payloads = {};
- var rtcpFb = {};
- var rtp = {};
- session.media.forEach(function(bLine) {
- if ((typeof bLine.rtcpMux !== 'string' ||
- bLine.rtcpMux !== 'rtcp-mux') &&
- bLine.direction !== 'inactive') {
- throw new Error("Cannot convert to Unified Plan because m-lines " +
- "without the rtcp-mux attribute were found.");
- }
- if (bLine.type === 'application') {
- mid2ul[bLine.mid] = bLine;
- return;
- }
- // With rtcp-mux and bundle all the channels should have the same ICE
- // stuff.
- var sources = bLine.sources;
- var ssrcGroups = bLine.ssrcGroups;
- var port = bLine.port;
- /* Chrome adds different candidates even using bundle, so we concat the candidates list */
- if (typeof bLine.candidates != 'undefined') {
- if (typeof candidates != 'undefined') {
- candidates = candidates.concat(bLine.candidates);
- } else {
- candidates = bLine.candidates;
- }
- }
- if ((typeof iceUfrag != 'undefined') && (typeof bLine.iceUfrag != 'undefined') && (iceUfrag != bLine.iceUfrag)) {
- throw new Error("Only BUNDLE supported, iceUfrag must be the same for all m-lines.\n" +
- "\tLast iceUfrag: " + iceUfrag + "\n" +
- "\tNew iceUfrag: " + bLine.iceUfrag
- );
- }
- if (typeof bLine.iceUfrag != 'undefined') {
- iceUfrag = bLine.iceUfrag;
- }
- if ((typeof icePwd != 'undefined') && (typeof bLine.icePwd != 'undefined') && (icePwd != bLine.icePwd)) {
- throw new Error("Only BUNDLE supported, icePwd must be the same for all m-lines.\n" +
- "\tLast icePwd: " + icePwd + "\n" +
- "\tNew icePwd: " + bLine.icePwd
- );
- }
- if (typeof bLine.icePwd != 'undefined') {
- icePwd = bLine.icePwd;
- }
- if ((typeof fingerprint != 'undefined') && (typeof bLine.fingerprint != 'undefined') &&
- (fingerprint.type != bLine.fingerprint.type || fingerprint.hash != bLine.fingerprint.hash)) {
- throw new Error("Only BUNDLE supported, fingerprint must be the same for all m-lines.\n" +
- "\tLast fingerprint: " + JSON.stringify(fingerprint) + "\n" +
- "\tNew fingerprint: " + JSON.stringify(bLine.fingerprint)
- );
- }
- if (typeof bLine.fingerprint != 'undefined') {
- fingerprint = bLine.fingerprint;
- }
- payloads[bLine.type] = bLine.payloads;
- rtcpFb[bLine.type] = bLine.rtcpFb;
- rtp[bLine.type] = bLine.rtp;
- // inverted ssrc group map
- var ssrc2group = {};
- if (typeof ssrcGroups !== 'undefined' && Array.isArray(ssrcGroups)) {
- ssrcGroups.forEach(function (ssrcGroup) {
- // XXX This might brake if an SSRC is in more than one group
- // for some reason.
- if (typeof ssrcGroup.ssrcs !== 'undefined' &&
- Array.isArray(ssrcGroup.ssrcs)) {
- ssrcGroup.ssrcs.forEach(function (ssrc) {
- if (typeof ssrc2group[ssrc] === 'undefined') {
- ssrc2group[ssrc] = [];
- }
- ssrc2group[ssrc].push(ssrcGroup);
- });
- }
- });
- }
- // ssrc to m-line index.
- var ssrc2ml = {};
- if (typeof sources === 'object') {
- // We'll use the "bLine" object as a prototype for each new "mLine"
- // that we create, but first we need to clean it up a bit.
- delete bLine.sources;
- delete bLine.ssrcGroups;
- delete bLine.candidates;
- delete bLine.iceUfrag;
- delete bLine.icePwd;
- delete bLine.fingerprint;
- delete bLine.port;
- delete bLine.mid;
- // Explode the Plan B channel sources with one m-line per source.
- Object.keys(sources).forEach(function(ssrc) {
- // The (unified) m-line for this SSRC. We either create it from
- // scratch or, if it's a grouped SSRC, we re-use a related
- // mline. In other words, if the source is grouped with another
- // source, put the two together in the same m-line.
- var uLine;
- // We assume here that we are the answerer in the O/A, so any
- // offers which we translate come from the remote side, while
- // answers are local. So the check below is to make that we
- // handle receive-only SSRCs in a special way only if they come
- // from the remote side.
- if (desc.type==='offer') {
- // We want to detect SSRCs which are used by a remote peer
- // in an m-line with direction=recvonly (i.e. they are
- // being used for RTCP only).
- // This information would have gotten lost if the remote
- // peer used Unified Plan and their local description was
- // translated to Plan B. So we use the lack of an MSID
- // attribute to deduce a "receive only" SSRC.
- if (!sources[ssrc].msid) {
- recvonlySsrcs[bLine.type][ssrc] = sources[ssrc];
- // Receive-only SSRCs must not create new m-lines. We
- // will assign them to an existing m-line later.
- return;
- }
- }
- if (typeof ssrc2group[ssrc] !== 'undefined' &&
- Array.isArray(ssrc2group[ssrc])) {
- ssrc2group[ssrc].some(function (ssrcGroup) {
- // ssrcGroup.ssrcs *is* an Array, no need to check
- // again here.
- return ssrcGroup.ssrcs.some(function (related) {
- if (typeof ssrc2ml[related] === 'object') {
- uLine = ssrc2ml[related];
- return true;
- }
- });
- });
- }
- if (typeof uLine === 'object') {
- // the m-line already exists. Just add the source.
- uLine.sources[ssrc] = sources[ssrc];
- delete sources[ssrc].msid;
- } else {
- // Use the "bLine" as a prototype for the "uLine".
- uLine = Object.create(bLine);
- ssrc2ml[ssrc] = uLine;
- if (typeof sources[ssrc].msid !== 'undefined') {
- // Assign the msid of the source to the m-line. Note
- // that it is not guaranteed that the source will have
- // msid. In particular "recvonly" sources don't have an
- // msid. Note that "recvonly" is a term only defined
- // for m-lines.
- uLine.msid = sources[ssrc].msid;
- delete sources[ssrc].msid;
- }
- // We assign one SSRC per media line.
- uLine.sources = {};
- uLine.sources[ssrc] = sources[ssrc];
- uLine.ssrcGroups = ssrc2group[ssrc];
- // Use the cached Unified Plan SDP (if it exists) to assign
- // SSRCs to mids.
- if (typeof cached !== 'undefined' &&
- typeof cached.media !== 'undefined' &&
- Array.isArray(cached.media)) {
- cached.media.forEach(function (m) {
- if (typeof m.sources === 'object') {
- Object.keys(m.sources).forEach(function (s) {
- if (s === ssrc) {
- uLine.mid = m.mid;
- }
- });
- }
- });
- }
- if (typeof uLine.mid === 'undefined') {
- // If this is an SSRC that we see for the first time
- // assign it a new mid. This is typically the case when
- // this method is called to transform a remote
- // description for the first time or when there is a
- // new SSRC in the remote description because a new
- // peer has joined the conference. Local SSRCs should
- // have already been added to the map in the toPlanB
- // method.
- //
- // Because FF generates answers in Unified Plan style,
- // we MUST already have a cached answer with all the
- // local SSRCs mapped to some m-line/mid.
- uLine.mid = [bLine.type, '-', ssrc].join('');
- }
- // Include the candidates in the 1st media line.
- uLine.candidates = candidates;
- uLine.iceUfrag = iceUfrag;
- uLine.icePwd = icePwd;
- uLine.fingerprint = fingerprint;
- uLine.port = port;
- mid2ul[uLine.mid] = uLine;
- sources2ul[uIdx] = uLine.sources;
- self.cache.mlU2BMap[uIdx] = bIdx;
- if (typeof self.cache.mlB2UMap[bIdx] === 'undefined') {
- self.cache.mlB2UMap[bIdx] = uIdx;
- }
- uIdx++;
- }
- });
- } else {
- var uLine = bLine;
- uLine.candidates = candidates;
- uLine.iceUfrag = iceUfrag;
- uLine.icePwd = icePwd;
- uLine.fingerprint = fingerprint;
- uLine.port = port;
- mid2ul[uLine.mid] = uLine;
- self.cache.mlU2BMap[uIdx] = bIdx;
- if (typeof self.cache.mlB2UMap[bIdx] === 'undefined') {
- self.cache.mlB2UMap[bIdx] = uIdx;
- }
- }
- bIdx++;
- });
- // Rebuild the media array in the right order and add the missing mLines
- // (missing from the Plan B SDP).
- session.media = [];
- mids = []; // reuse
- if (desc.type === 'answer') {
- // The media lines in the answer must match the media lines in the
- // offer. The order is important too. Here we assume that Firefox is
- // the answerer, so we merely have to use the reconstructed (unified)
- // answer to update the cached (unified) answer accordingly.
- //
- // In the general case, one would have to use the cached (unified)
- // offer to find the m-lines that are missing from the reconstructed
- // answer, potentially grabbing them from the cached (unified) answer.
- // One has to be careful with this approach because inactive m-lines do
- // not always have an mid, making it tricky (impossible?) to find where
- // exactly and which m-lines are missing from the reconstructed answer.
- for (var i = 0; i < cached.media.length; i++) {
- var uLine = cached.media[i];
- delete uLine.msid;
- delete uLine.sources;
- delete uLine.ssrcGroups;
- if (typeof sources2ul[i] === 'undefined') {
- if (!uLine.direction
- || uLine.direction === 'sendrecv')
- uLine.direction = 'recvonly';
- else if (uLine.direction === 'sendonly')
- uLine.direction = 'inactive';
- } else {
- if (!uLine.direction
- || uLine.direction === 'sendrecv')
- uLine.direction = 'sendrecv';
- else if (uLine.direction === 'recvonly')
- uLine.direction = 'sendonly';
- }
- uLine.sources = sources2ul[i];
- uLine.candidates = candidates;
- uLine.iceUfrag = iceUfrag;
- uLine.icePwd = icePwd;
- uLine.fingerprint = fingerprint;
- uLine.rtp = rtp[uLine.type];
- uLine.payloads = payloads[uLine.type];
- uLine.rtcpFb = rtcpFb[uLine.type];
- session.media.push(uLine);
- if (typeof uLine.mid === 'string') {
- // inactive lines don't/may not have an mid.
- mids.push(uLine.mid);
- }
- }
- } else {
- // SDP offer/answer (and the JSEP spec) forbids removing an m-section
- // under any circumstances. If we are no longer interested in sending a
- // track, we just remove the msid and ssrc attributes and set it to
- // either a=recvonly (as the reofferer, we must use recvonly if the
- // other side was previously sending on the m-section, but we can also
- // leave the possibility open if it wasn't previously in use), or
- // a=inactive.
- if (typeof cached !== 'undefined' &&
- typeof cached.media !== 'undefined' &&
- Array.isArray(cached.media)) {
- cached.media.forEach(function(uLine) {
- mids.push(uLine.mid);
- if (typeof mid2ul[uLine.mid] !== 'undefined') {
- session.media.push(mid2ul[uLine.mid]);
- } else {
- delete uLine.msid;
- delete uLine.sources;
- delete uLine.ssrcGroups;
- if (!uLine.direction
- || uLine.direction === 'sendrecv') {
- uLine.direction = 'sendonly';
- }
- if (!uLine.direction
- || uLine.direction === 'recvonly') {
- uLine.direction = 'inactive';
- }
- addSetupAttr (uLine);
- session.media.push(uLine);
- }
- });
- }
- // Add all the remaining (new) m-lines of the transformed SDP.
- Object.keys(mid2ul).forEach(function(mid) {
- if (mids.indexOf(mid) === -1) {
- mids.push(mid);
- if (mid2ul[mid].direction === 'recvonly') {
- // This is a remote recvonly channel. Add its SSRC to the
- // appropriate sendrecv or sendonly channel.
- // TODO(gp) what if we don't have sendrecv/sendonly
- // channel?
- var done = false;
- session.media.some(function (uLine) {
- if ((uLine.direction === 'sendrecv' ||
- uLine.direction === 'sendonly') &&
- uLine.type === mid2ul[mid].type) {
- // mid2ul[mid] shouldn't have any ssrc-groups
- Object.keys(mid2ul[mid].sources).forEach(
- function (ssrc) {
- uLine.sources[ssrc] =
- mid2ul[mid].sources[ssrc];
- });
- done = true;
- return true;
- }
- });
- if (!done) {
- session.media.push(mid2ul[mid]);
- }
- } else {
- session.media.push(mid2ul[mid]);
- }
- }
- });
- }
- // After we have constructed the Plan Unified m-lines we can figure out
- // where (in which m-line) to place the 'recvonly SSRCs'.
- // Note: we assume here that we are the answerer in the O/A, so any offers
- // which we translate come from the remote side, while answers are local
- // (and so our last local description is cached as an 'answer').
- ["audio", "video"].forEach(function (type) {
- if (!session || !session.media || !Array.isArray(session.media))
- return;
- var idx = null;
- if (Object.keys(recvonlySsrcs[type]).length > 0) {
- idx = self.getFirstSendingIndexFromAnswer(type);
- if (idx === null){
- // If this is the first offer we receive, we don't have a
- // cached answer. Assume that we will be sending media using
- // the first m-line for each media type.
- for (var i = 0; i < session.media.length; i++) {
- if (session.media[i].type === type) {
- idx = i;
- break;
- }
- }
- }
- }
- if (idx && session.media.length > idx) {
- var mLine = session.media[idx];
- Object.keys(recvonlySsrcs[type]).forEach(function(ssrc) {
- if (mLine.sources && mLine.sources[ssrc]) {
- console.warn("Replacing an existing SSRC.");
- }
- if (!mLine.sources) {
- mLine.sources = {};
- }
- mLine.sources[ssrc] = recvonlySsrcs[type][ssrc];
- });
- }
- });
- if (typeof session.groups !== 'undefined') {
- // We regenerate the BUNDLE group (since we regenerated the mids)
- session.groups.some(function(group) {
- if (group.type === 'BUNDLE') {
- group.mids = mids.join(' ');
- return true;
- }
- });
- }
- // msid semantic
- session.msidSemantic = {
- semantic: 'WMS',
- token: '*'
- };
- var resStr = transform.write(session);
- // Cache the transformed SDP (Unified Plan) for later re-use in this
- // function.
- this.cache[desc.type] = resStr;
- return new RTCSessionDescription({
- type: desc.type,
- sdp: resStr
- });
- //#endregion
- };
- },{"./array-equals":17,"./transform":20}],20:[function(require,module,exports){
- /* Copyright @ 2015 Atlassian Pty Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- var transform = require('sdp-transform');
- exports.write = function(session, opts) {
- if (typeof session !== 'undefined' &&
- typeof session.media !== 'undefined' &&
- Array.isArray(session.media)) {
- session.media.forEach(function (mLine) {
- // expand sources to ssrcs
- if (typeof mLine.sources !== 'undefined' &&
- Object.keys(mLine.sources).length !== 0) {
- mLine.ssrcs = [];
- Object.keys(mLine.sources).forEach(function (ssrc) {
- var source = mLine.sources[ssrc];
- Object.keys(source).forEach(function (attribute) {
- mLine.ssrcs.push({
- id: ssrc,
- attribute: attribute,
- value: source[attribute]
- });
- });
- });
- delete mLine.sources;
- }
- // join ssrcs in ssrc groups
- if (typeof mLine.ssrcGroups !== 'undefined' &&
- Array.isArray(mLine.ssrcGroups)) {
- mLine.ssrcGroups.forEach(function (ssrcGroup) {
- if (typeof ssrcGroup.ssrcs !== 'undefined' &&
- Array.isArray(ssrcGroup.ssrcs)) {
- ssrcGroup.ssrcs = ssrcGroup.ssrcs.join(' ');
- }
- });
- }
- });
- }
- // join group mids
- if (typeof session !== 'undefined' &&
- typeof session.groups !== 'undefined' && Array.isArray(session.groups)) {
- session.groups.forEach(function (g) {
- if (typeof g.mids !== 'undefined' && Array.isArray(g.mids)) {
- g.mids = g.mids.join(' ');
- }
- });
- }
- return transform.write(session, opts);
- };
- exports.parse = function(sdp) {
- var session = transform.parse(sdp);
- if (typeof session !== 'undefined' && typeof session.media !== 'undefined' &&
- Array.isArray(session.media)) {
- session.media.forEach(function (mLine) {
- // group sources attributes by ssrc
- if (typeof mLine.ssrcs !== 'undefined' && Array.isArray(mLine.ssrcs)) {
- mLine.sources = {};
- mLine.ssrcs.forEach(function (ssrc) {
- if (!mLine.sources[ssrc.id])
- mLine.sources[ssrc.id] = {};
- mLine.sources[ssrc.id][ssrc.attribute] = ssrc.value;
- });
- delete mLine.ssrcs;
- }
- // split ssrcs in ssrc groups
- if (typeof mLine.ssrcGroups !== 'undefined' &&
- Array.isArray(mLine.ssrcGroups)) {
- mLine.ssrcGroups.forEach(function (ssrcGroup) {
- if (typeof ssrcGroup.ssrcs === 'string') {
- ssrcGroup.ssrcs = ssrcGroup.ssrcs.split(' ');
- }
- });
- }
- });
- }
- // split group mids
- if (typeof session !== 'undefined' &&
- typeof session.groups !== 'undefined' && Array.isArray(session.groups)) {
- session.groups.forEach(function (g) {
- if (typeof g.mids === 'string') {
- g.mids = g.mids.split(' ');
- }
- });
- }
- return session;
- };
- },{"sdp-transform":14}],21:[function(require,module,exports){
- /*!
- * UAParser.js v0.7.21
- * Lightweight JavaScript-based User-Agent string parser
- * https://github.com/faisalman/ua-parser-js
- *
- * Copyright © 2012-2019 Faisal Salman <[email protected]>
- * Licensed under MIT License
- */
- (function (window, undefined) {
- 'use strict';
- //////////////
- // Constants
- /////////////
- var LIBVERSION = '0.7.21',
- EMPTY = '',
- UNKNOWN = '?',
- FUNC_TYPE = 'function',
- UNDEF_TYPE = 'undefined',
- OBJ_TYPE = 'object',
- STR_TYPE = 'string',
- MAJOR = 'major', // deprecated
- MODEL = 'model',
- NAME = 'name',
- TYPE = 'type',
- VENDOR = 'vendor',
- VERSION = 'version',
- ARCHITECTURE= 'architecture',
- CONSOLE = 'console',
- MOBILE = 'mobile',
- TABLET = 'tablet',
- SMARTTV = 'smarttv',
- WEARABLE = 'wearable',
- EMBEDDED = 'embedded';
- ///////////
- // Helper
- //////////
- var util = {
- extend : function (regexes, extensions) {
- var mergedRegexes = {};
- for (var i in regexes) {
- if (extensions[i] && extensions[i].length % 2 === 0) {
- mergedRegexes[i] = extensions[i].concat(regexes[i]);
- } else {
- mergedRegexes[i] = regexes[i];
- }
- }
- return mergedRegexes;
- },
- has : function (str1, str2) {
- if (typeof str1 === "string") {
- return str2.toLowerCase().indexOf(str1.toLowerCase()) !== -1;
- } else {
- return false;
- }
- },
- lowerize : function (str) {
- return str.toLowerCase();
- },
- major : function (version) {
- return typeof(version) === STR_TYPE ? version.replace(/[^\d\.]/g,'').split(".")[0] : undefined;
- },
- trim : function (str) {
- return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
- }
- };
- ///////////////
- // Map helper
- //////////////
- var mapper = {
- rgx : function (ua, arrays) {
- var i = 0, j, k, p, q, matches, match;
- // loop through all regexes maps
- while (i < arrays.length && !matches) {
- var regex = arrays[i], // even sequence (0,2,4,..)
- props = arrays[i + 1]; // odd sequence (1,3,5,..)
- j = k = 0;
- // try matching uastring with regexes
- while (j < regex.length && !matches) {
- matches = regex[j++].exec(ua);
- if (!!matches) {
- for (p = 0; p < props.length; p++) {
- match = matches[++k];
- q = props[p];
- // check if given property is actually array
- if (typeof q === OBJ_TYPE && q.length > 0) {
- if (q.length == 2) {
- if (typeof q[1] == FUNC_TYPE) {
- // assign modified match
- this[q[0]] = q[1].call(this, match);
- } else {
- // assign given value, ignore regex match
- this[q[0]] = q[1];
- }
- } else if (q.length == 3) {
- // check whether function or regex
- if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) {
- // call function (usually string mapper)
- this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined;
- } else {
- // sanitize match using given regex
- this[q[0]] = match ? match.replace(q[1], q[2]) : undefined;
- }
- } else if (q.length == 4) {
- this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined;
- }
- } else {
- this[q] = match ? match : undefined;
- }
- }
- }
- }
- i += 2;
- }
- },
- str : function (str, map) {
- for (var i in map) {
- // check if array
- if (typeof map[i] === OBJ_TYPE && map[i].length > 0) {
- for (var j = 0; j < map[i].length; j++) {
- if (util.has(map[i][j], str)) {
- return (i === UNKNOWN) ? undefined : i;
- }
- }
- } else if (util.has(map[i], str)) {
- return (i === UNKNOWN) ? undefined : i;
- }
- }
- return str;
- }
- };
- ///////////////
- // String map
- //////////////
- var maps = {
- 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'
- }
- }
- }
- };
- //////////////
- // Regex map
- /////////////
- var regexes = {
- browser : [[
- // Presto based
- /(opera\smini)\/([\w\.-]+)/i, // Opera Mini
- /(opera\s[mobiletab]+).+version\/([\w\.-]+)/i, // Opera Mobi/Tablet
- /(opera).+version\/([\w\.]+)/i, // Opera > 9.80
- /(opera)[\/\s]+([\w\.]+)/i // Opera < 9.80
- ], [NAME, VERSION], [
- /(opios)[\/\s]+([\w\.]+)/i // Opera mini on iphone >= 8.0
- ], [[NAME, 'Opera Mini'], VERSION], [
- /\s(opr)\/([\w\.]+)/i // Opera Webkit
- ], [[NAME, 'Opera'], VERSION], [
- // Mixed
- /(kindle)\/([\w\.]+)/i, // Kindle
- /(lunascape|maxthon|netfront|jasmine|blazer)[\/\s]?([\w\.]*)/i,
- // Lunascape/Maxthon/Netfront/Jasmine/Blazer
- // Trident based
- /(avant\s|iemobile|slim)(?:browser)?[\/\s]?([\w\.]*)/i,
- // Avant/IEMobile/SlimBrowser
- /(bidubrowser|baidubrowser)[\/\s]?([\w\.]+)/i, // Baidu Browser
- /(?:ms|\()(ie)\s([\w\.]+)/i, // Internet Explorer
- // Webkit/KHTML based
- /(rekonq)\/([\w\.]*)/i, // Rekonq
- /(chromium|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon)\/([\w\.-]+)/i
- // Chromium/Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon
- ], [NAME, VERSION], [
- /(konqueror)\/([\w\.]+)/i // Konqueror
- ], [[NAME, 'Konqueror'], VERSION], [
- /(trident).+rv[:\s]([\w\.]+).+like\sgecko/i // IE11
- ], [[NAME, 'IE'], VERSION], [
- /(edge|edgios|edga|edg)\/((\d+)?[\w\.]+)/i // Microsoft Edge
- ], [[NAME, 'Edge'], VERSION], [
- /(yabrowser)\/([\w\.]+)/i // Yandex
- ], [[NAME, 'Yandex'], VERSION], [
- /(Avast)\/([\w\.]+)/i // Avast Secure Browser
- ], [[NAME, 'Avast Secure Browser'], VERSION], [
- /(AVG)\/([\w\.]+)/i // AVG Secure Browser
- ], [[NAME, 'AVG Secure Browser'], VERSION], [
- /(puffin)\/([\w\.]+)/i // Puffin
- ], [[NAME, 'Puffin'], VERSION], [
- /(focus)\/([\w\.]+)/i // Firefox Focus
- ], [[NAME, 'Firefox Focus'], VERSION], [
- /(opt)\/([\w\.]+)/i // Opera Touch
- ], [[NAME, 'Opera Touch'], VERSION], [
- /((?:[\s\/])uc?\s?browser|(?:juc.+)ucweb)[\/\s]?([\w\.]+)/i // UCBrowser
- ], [[NAME, 'UCBrowser'], VERSION], [
- /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon
- ], [[NAME, /_/g, ' '], VERSION], [
- /(windowswechat qbcore)\/([\w\.]+)/i // WeChat Desktop for Windows Built-in Browser
- ], [[NAME, 'WeChat(Win) Desktop'], VERSION], [
- /(micromessenger)\/([\w\.]+)/i // WeChat
- ], [[NAME, 'WeChat'], VERSION], [
- /(brave)\/([\w\.]+)/i // Brave browser
- ], [[NAME, 'Brave'], VERSION], [
- /(qqbrowserlite)\/([\w\.]+)/i // QQBrowserLite
- ], [NAME, VERSION], [
- /(QQ)\/([\d\.]+)/i // QQ, aka ShouQ
- ], [NAME, VERSION], [
- /m?(qqbrowser)[\/\s]?([\w\.]+)/i // QQBrowser
- ], [NAME, VERSION], [
- /(baiduboxapp)[\/\s]?([\w\.]+)/i // Baidu App
- ], [NAME, VERSION], [
- /(2345Explorer)[\/\s]?([\w\.]+)/i // 2345 Browser
- ], [NAME, VERSION], [
- /(MetaSr)[\/\s]?([\w\.]+)/i // SouGouBrowser
- ], [NAME], [
- /(LBBROWSER)/i // LieBao Browser
- ], [NAME], [
- /xiaomi\/miuibrowser\/([\w\.]+)/i // MIUI Browser
- ], [VERSION, [NAME, 'MIUI Browser']], [
- /;fbav\/([\w\.]+);/i // Facebook App for iOS & Android
- ], [VERSION, [NAME, 'Facebook']], [
- /safari\s(line)\/([\w\.]+)/i, // Line App for iOS
- /android.+(line)\/([\w\.]+)\/iab/i // Line App for Android
- ], [NAME, VERSION], [
- /headlesschrome(?:\/([\w\.]+)|\s)/i // Chrome Headless
- ], [VERSION, [NAME, 'Chrome Headless']], [
- /\swv\).+(chrome)\/([\w\.]+)/i // Chrome WebView
- ], [[NAME, /(.+)/, '$1 WebView'], VERSION], [
- /((?:oculus|samsung)browser)\/([\w\.]+)/i
- ], [[NAME, /(.+(?:g|us))(.+)/, '$1 $2'], VERSION], [ // Oculus / Samsung Browser
- /android.+version\/([\w\.]+)\s+(?:mobile\s?safari|safari)*/i // Android Browser
- ], [VERSION, [NAME, 'Android Browser']], [
- /(sailfishbrowser)\/([\w\.]+)/i // Sailfish Browser
- ], [[NAME, 'Sailfish Browser'], VERSION], [
- /(chrome|omniweb|arora|[tizenoka]{5}\s?browser)\/v?([\w\.]+)/i
- // Chrome/OmniWeb/Arora/Tizen/Nokia
- ], [NAME, VERSION], [
- /(dolfin)\/([\w\.]+)/i // Dolphin
- ], [[NAME, 'Dolphin'], VERSION], [
- /(qihu|qhbrowser|qihoobrowser|360browser)/i // 360
- ], [[NAME, '360 Browser']], [
- /((?:android.+)crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS
- ], [[NAME, 'Chrome'], VERSION], [
- /(coast)\/([\w\.]+)/i // Opera Coast
- ], [[NAME, 'Opera Coast'], VERSION], [
- /fxios\/([\w\.-]+)/i // Firefox for iOS
- ], [VERSION, [NAME, 'Firefox']], [
- /version\/([\w\.]+).+?mobile\/\w+\s(safari)/i // Mobile Safari
- ], [VERSION, [NAME, 'Mobile Safari']], [
- /version\/([\w\.]+).+?(mobile\s?safari|safari)/i // Safari & Safari Mobile
- ], [VERSION, NAME], [
- /webkit.+?(gsa)\/([\w\.]+).+?(mobile\s?safari|safari)(\/[\w\.]+)/i // Google Search Appliance on iOS
- ], [[NAME, 'GSA'], VERSION], [
- /webkit.+?(mobile\s?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
- ], [NAME, [VERSION, mapper.str, maps.browser.oldsafari.version]], [
- /(webkit|khtml)\/([\w\.]+)/i
- ], [NAME, VERSION], [
- // Gecko based
- /(navigator|netscape)\/([\w\.-]+)/i // Netscape
- ], [[NAME, 'Netscape'], VERSION], [
- /(swiftfox)/i, // Swiftfox
- /(icedragon|iceweasel|camino|chimera|fennec|maemo\sbrowser|minimo|conkeror)[\/\s]?([\w\.\+]+)/i,
- // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror
- /(firefox|seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([\w\.-]+)$/i,
- // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
- /(mozilla)\/([\w\.]+).+rv\:.+gecko\/\d+/i, // Mozilla
- // Other
- /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir)[\/\s]?([\w\.]+)/i,
- // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir
- /(links)\s\(([\w\.]+)/i, // Links
- /(gobrowser)\/?([\w\.]*)/i, // GoBrowser
- /(ice\s?browser)\/v?([\w\._]+)/i, // ICE Browser
- /(mosaic)[\/\s]([\w\.]+)/i // Mosaic
- ], [NAME, VERSION]
- ],
- cpu : [[
- /(?:(amd|x(?:(?:86|64)[_-])?|wow|win)64)[;\)]/i // AMD64
- ], [[ARCHITECTURE, 'amd64']], [
- /(ia32(?=;))/i // IA32 (quicktime)
- ], [[ARCHITECTURE, util.lowerize]], [
- /((?:i[346]|x)86)[;\)]/i // IA32
- ], [[ARCHITECTURE, 'ia32']], [
- // PocketPC mistakenly identified as PowerPC
- /windows\s(ce|mobile);\sppc;/i
- ], [[ARCHITECTURE, 'arm']], [
- /((?:ppc|powerpc)(?:64)?)(?:\smac|;|\))/i // PowerPC
- ], [[ARCHITECTURE, /ower/, '', util.lowerize]], [
- /(sun4\w)[;\)]/i // SPARC
- ], [[ARCHITECTURE, 'sparc']], [
- /((?:avr32|ia64(?=;))|68k(?=\))|arm(?:64|(?=v\d+[;l]))|(?=atmel\s)avr|(?:irix|mips|sparc)(?:64)?(?=;)|pa-risc)/i
- // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC
- ], [[ARCHITECTURE, util.lowerize]]
- ],
- device : [[
- /\((ipad|playbook);[\w\s\),;-]+(rim|apple)/i // iPad/PlayBook
- ], [MODEL, VENDOR, [TYPE, TABLET]], [
- /applecoremedia\/[\w\.]+ \((ipad)/ // iPad
- ], [MODEL, [VENDOR, 'Apple'], [TYPE, TABLET]], [
- /(apple\s{0,1}tv)/i // Apple TV
- ], [[MODEL, 'Apple TV'], [VENDOR, 'Apple'], [TYPE, SMARTTV]], [
- /(archos)\s(gamepad2?)/i, // Archos
- /(hp).+(touchpad)/i, // HP TouchPad
- /(hp).+(tablet)/i, // HP Tablet
- /(kindle)\/([\w\.]+)/i, // Kindle
- /\s(nook)[\w\s]+build\/(\w+)/i, // Nook
- /(dell)\s(strea[kpr\s\d]*[\dko])/i // Dell Streak
- ], [VENDOR, MODEL, [TYPE, TABLET]], [
- /(kf[A-z]+)\sbuild\/.+silk\//i // Kindle Fire HD
- ], [MODEL, [VENDOR, 'Amazon'], [TYPE, TABLET]], [
- /(sd|kf)[0349hijorstuw]+\sbuild\/.+silk\//i // Fire Phone
- ], [[MODEL, mapper.str, maps.device.amazon.model], [VENDOR, 'Amazon'], [TYPE, MOBILE]], [
- /android.+aft([bms])\sbuild/i // Fire TV
- ], [MODEL, [VENDOR, 'Amazon'], [TYPE, SMARTTV]], [
- /\((ip[honed|\s\w*]+);.+(apple)/i // iPod/iPhone
- ], [MODEL, VENDOR, [TYPE, MOBILE]], [
- /\((ip[honed|\s\w*]+);/i // iPod/iPhone
- ], [MODEL, [VENDOR, 'Apple'], [TYPE, MOBILE]], [
- /(blackberry)[\s-]?(\w+)/i, // BlackBerry
- /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[\s_-]?([\w-]*)/i,
- // BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron
- /(hp)\s([\w\s]+\w)/i, // HP iPAQ
- /(asus)-?(\w+)/i // Asus
- ], [VENDOR, MODEL, [TYPE, MOBILE]], [
- /\(bb10;\s(\w+)/i // BlackBerry 10
- ], [MODEL, [VENDOR, 'BlackBerry'], [TYPE, MOBILE]], [
- // Asus Tablets
- /android.+(transfo[prime\s]{4,10}\s\w+|eeepc|slider\s\w+|nexus 7|padfone|p00c)/i
- ], [MODEL, [VENDOR, 'Asus'], [TYPE, TABLET]], [
- /(sony)\s(tablet\s[ps])\sbuild\//i, // Sony
- /(sony)?(?:sgp.+)\sbuild\//i
- ], [[VENDOR, 'Sony'], [MODEL, 'Xperia Tablet'], [TYPE, TABLET]], [
- /android.+\s([c-g]\d{4}|so[-l]\w+)(?=\sbuild\/|\).+chrome\/(?![1-6]{0,1}\d\.))/i
- ], [MODEL, [VENDOR, 'Sony'], [TYPE, MOBILE]], [
- /\s(ouya)\s/i, // Ouya
- /(nintendo)\s([wids3u]+)/i // Nintendo
- ], [VENDOR, MODEL, [TYPE, CONSOLE]], [
- /android.+;\s(shield)\sbuild/i // Nvidia
- ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, CONSOLE]], [
- /(playstation\s[34portablevi]+)/i // Playstation
- ], [MODEL, [VENDOR, 'Sony'], [TYPE, CONSOLE]], [
- /(sprint\s(\w+))/i // Sprint Phones
- ], [[VENDOR, mapper.str, maps.device.sprint.vendor], [MODEL, mapper.str, maps.device.sprint.model], [TYPE, MOBILE]], [
- /(htc)[;_\s-]+([\w\s]+(?=\)|\sbuild)|\w+)/i, // HTC
- /(zte)-(\w*)/i, // ZTE
- /(alcatel|geeksphone|nexian|panasonic|(?=;\s)sony)[_\s-]?([\w-]*)/i
- // Alcatel/GeeksPhone/Nexian/Panasonic/Sony
- ], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [
- /(nexus\s9)/i // HTC Nexus 9
- ], [MODEL, [VENDOR, 'HTC'], [TYPE, TABLET]], [
- /d\/huawei([\w\s-]+)[;\)]/i,
- /(nexus\s6p|vog-l29|ane-lx1|eml-l29)/i // Huawei
- ], [MODEL, [VENDOR, 'Huawei'], [TYPE, MOBILE]], [
- /android.+(bah2?-a?[lw]\d{2})/i // Huawei MediaPad
- ], [MODEL, [VENDOR, 'Huawei'], [TYPE, TABLET]], [
- /(microsoft);\s(lumia[\s\w]+)/i // Microsoft Lumia
- ], [VENDOR, MODEL, [TYPE, MOBILE]], [
- /[\s\(;](xbox(?:\sone)?)[\s\);]/i // Microsoft Xbox
- ], [MODEL, [VENDOR, 'Microsoft'], [TYPE, CONSOLE]], [
- /(kin\.[onetw]{3})/i // Microsoft Kin
- ], [[MODEL, /\./g, ' '], [VENDOR, 'Microsoft'], [TYPE, MOBILE]], [
- // Motorola
- /\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,
- /(nexus\s6)/i
- ], [MODEL, [VENDOR, 'Motorola'], [TYPE, MOBILE]], [
- /android.+\s(mz60\d|xoom[\s2]{0,2})\sbuild\//i
- ], [MODEL, [VENDOR, 'Motorola'], [TYPE, TABLET]], [
- /hbbtv\/\d+\.\d+\.\d+\s+\([\w\s]*;\s*(\w[^;]*);([^;]*)/i // HbbTV devices
- ], [[VENDOR, util.trim], [MODEL, util.trim], [TYPE, SMARTTV]], [
- /hbbtv.+maple;(\d+)/i
- ], [[MODEL, /^/, 'SmartTV'], [VENDOR, 'Samsung'], [TYPE, SMARTTV]], [
- /\(dtv[\);].+(aquos)/i // Sharp
- ], [MODEL, [VENDOR, 'Sharp'], [TYPE, SMARTTV]], [
- /android.+((sch-i[89]0\d|shw-m380s|gt-p\d{4}|gt-n\d+|sgh-t8[56]9|nexus 10))/i,
- /((SM-T\w+))/i
- ], [[VENDOR, 'Samsung'], MODEL, [TYPE, TABLET]], [ // Samsung
- /smart-tv.+(samsung)/i
- ], [VENDOR, [TYPE, SMARTTV], MODEL], [
- /((s[cgp]h-\w+|gt-\w+|galaxy\snexus|sm-\w[\w\d]+))/i,
- /(sam[sung]*)[\s-]*(\w+-?[\w-]*)/i,
- /sec-((sgh\w+))/i
- ], [[VENDOR, 'Samsung'], MODEL, [TYPE, MOBILE]], [
- /sie-(\w*)/i // Siemens
- ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [
- /(maemo|nokia).*(n900|lumia\s\d+)/i, // Nokia
- /(nokia)[\s_-]?([\w-]*)/i
- ], [[VENDOR, 'Nokia'], MODEL, [TYPE, MOBILE]], [
- /android[x\d\.\s;]+\s([ab][1-7]\-?[0178a]\d\d?)/i // Acer
- ], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [
- /android.+([vl]k\-?\d{3})\s+build/i // LG Tablet
- ], [MODEL, [VENDOR, 'LG'], [TYPE, TABLET]], [
- /android\s3\.[\s\w;-]{10}(lg?)-([06cv9]{3,4})/i // LG Tablet
- ], [[VENDOR, 'LG'], MODEL, [TYPE, TABLET]], [
- /(lg) netcast\.tv/i // LG SmartTV
- ], [VENDOR, MODEL, [TYPE, SMARTTV]], [
- /(nexus\s[45])/i, // LG
- /lg[e;\s\/-]+(\w*)/i,
- /android.+lg(\-?[\d\w]+)\s+build/i
- ], [MODEL, [VENDOR, 'LG'], [TYPE, MOBILE]], [
- /(lenovo)\s?(s(?:5000|6000)(?:[\w-]+)|tab(?:[\s\w]+))/i // Lenovo tablets
- ], [VENDOR, MODEL, [TYPE, TABLET]], [
- /android.+(ideatab[a-z0-9\-\s]+)/i // Lenovo
- ], [MODEL, [VENDOR, 'Lenovo'], [TYPE, TABLET]], [
- /(lenovo)[_\s-]?([\w-]+)/i
- ], [VENDOR, MODEL, [TYPE, MOBILE]], [
- /linux;.+((jolla));/i // Jolla
- ], [VENDOR, MODEL, [TYPE, MOBILE]], [
- /((pebble))app\/[\d\.]+\s/i // Pebble
- ], [VENDOR, MODEL, [TYPE, WEARABLE]], [
- /android.+;\s(oppo)\s?([\w\s]+)\sbuild/i // OPPO
- ], [VENDOR, MODEL, [TYPE, MOBILE]], [
- /crkey/i // Google Chromecast
- ], [[MODEL, 'Chromecast'], [VENDOR, 'Google'], [TYPE, SMARTTV]], [
- /android.+;\s(glass)\s\d/i // Google Glass
- ], [MODEL, [VENDOR, 'Google'], [TYPE, WEARABLE]], [
- /android.+;\s(pixel c)[\s)]/i // Google Pixel C
- ], [MODEL, [VENDOR, 'Google'], [TYPE, TABLET]], [
- /android.+;\s(pixel( [23])?( xl)?)[\s)]/i // Google Pixel
- ], [MODEL, [VENDOR, 'Google'], [TYPE, MOBILE]], [
- /android.+;\s(\w+)\s+build\/hm\1/i, // Xiaomi Hongmi 'numeric' models
- /android.+(hm[\s\-_]*note?[\s_]*(?:\d\w)?)\s+build/i, // Xiaomi Hongmi
- /android.+(mi[\s\-_]*(?:a\d|one|one[\s_]plus|note lte)?[\s_]*(?:\d?\w?)[\s_]*(?:plus)?)\s+build/i,
- // Xiaomi Mi
- /android.+(redmi[\s\-_]*(?:note)?(?:[\s_]*[\w\s]+))\s+build/i // Redmi Phones
- ], [[MODEL, /_/g, ' '], [VENDOR, 'Xiaomi'], [TYPE, MOBILE]], [
- /android.+(mi[\s\-_]*(?:pad)(?:[\s_]*[\w\s]+))\s+build/i // Mi Pad tablets
- ],[[MODEL, /_/g, ' '], [VENDOR, 'Xiaomi'], [TYPE, TABLET]], [
- /android.+;\s(m[1-5]\snote)\sbuild/i // Meizu
- ], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [
- /(mz)-([\w-]{2,})/i
- ], [[VENDOR, 'Meizu'], MODEL, [TYPE, MOBILE]], [
- /android.+a000(1)\s+build/i, // OnePlus
- /android.+oneplus\s(a\d{4})[\s)]/i
- ], [MODEL, [VENDOR, 'OnePlus'], [TYPE, MOBILE]], [
- /android.+[;\/]\s*(RCT[\d\w]+)\s+build/i // RCA Tablets
- ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [
- /android.+[;\/\s]+(Venue[\d\s]{2,7})\s+build/i // Dell Venue Tablets
- ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [
- /android.+[;\/]\s*(Q[T|M][\d\w]+)\s+build/i // Verizon Tablet
- ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [
- /android.+[;\/]\s+(Barnes[&\s]+Noble\s+|BN[RT])(V?.*)\s+build/i // Barnes & Noble Tablet
- ], [[VENDOR, 'Barnes & Noble'], MODEL, [TYPE, TABLET]], [
- /android.+[;\/]\s+(TM\d{3}.*\b)\s+build/i // Barnes & Noble Tablet
- ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [
- /android.+;\s(k88)\sbuild/i // ZTE K Series Tablet
- ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [
- /android.+[;\/]\s*(gen\d{3})\s+build.*49h/i // Swiss GEN Mobile
- ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [
- /android.+[;\/]\s*(zur\d{3})\s+build/i // Swiss ZUR Tablet
- ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [
- /android.+[;\/]\s*((Zeki)?TB.*\b)\s+build/i // Zeki Tablets
- ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [
- /(android).+[;\/]\s+([YR]\d{2})\s+build/i,
- /android.+[;\/]\s+(Dragon[\-\s]+Touch\s+|DT)(\w{5})\sbuild/i // Dragon Touch Tablet
- ], [[VENDOR, 'Dragon Touch'], MODEL, [TYPE, TABLET]], [
- /android.+[;\/]\s*(NS-?\w{0,9})\sbuild/i // Insignia Tablets
- ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [
- /android.+[;\/]\s*((NX|Next)-?\w{0,9})\s+build/i // NextBook Tablets
- ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [
- /android.+[;\/]\s*(Xtreme\_)?(V(1[045]|2[015]|30|40|60|7[05]|90))\s+build/i
- ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [ // Voice Xtreme Phones
- /android.+[;\/]\s*(LVTEL\-)?(V1[12])\s+build/i // LvTel Phones
- ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [
- /android.+;\s(PH-1)\s/i
- ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [ // Essential PH-1
- /android.+[;\/]\s*(V(100MD|700NA|7011|917G).*\b)\s+build/i // Envizen Tablets
- ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [
- /android.+[;\/]\s*(Le[\s\-]+Pan)[\s\-]+(\w{1,9})\s+build/i // Le Pan Tablets
- ], [VENDOR, MODEL, [TYPE, TABLET]], [
- /android.+[;\/]\s*(Trio[\s\-]*.*)\s+build/i // MachSpeed Tablets
- ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [
- /android.+[;\/]\s*(Trinity)[\-\s]*(T\d{3})\s+build/i // Trinity Tablets
- ], [VENDOR, MODEL, [TYPE, TABLET]], [
- /android.+[;\/]\s*TU_(1491)\s+build/i // Rotor Tablets
- ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]], [
- /android.+(KS(.+))\s+build/i // Amazon Kindle Tablets
- ], [MODEL, [VENDOR, 'Amazon'], [TYPE, TABLET]], [
- /android.+(Gigaset)[\s\-]+(Q\w{1,9})\s+build/i // Gigaset Tablets
- ], [VENDOR, MODEL, [TYPE, TABLET]], [
- /\s(tablet|tab)[;\/]/i, // Unidentifiable Tablet
- /\s(mobile)(?:[;\/]|\ssafari)/i // Unidentifiable Mobile
- ], [[TYPE, util.lowerize], VENDOR, MODEL], [
- /[\s\/\(](smart-?tv)[;\)]/i // SmartTV
- ], [[TYPE, SMARTTV]], [
- /(android[\w\.\s\-]{0,9});.+build/i // Generic Android Device
- ], [MODEL, [VENDOR, 'Generic']]
- ],
- engine : [[
- /windows.+\sedge\/([\w\.]+)/i // EdgeHTML
- ], [VERSION, [NAME, 'EdgeHTML']], [
- /webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i // Blink
- ], [VERSION, [NAME, 'Blink']], [
- /(presto)\/([\w\.]+)/i, // Presto
- /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i,
- // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna
- /(khtml|tasman|links)[\/\s]\(?([\w\.]+)/i, // KHTML/Tasman/Links
- /(icab)[\/\s]([23]\.[\d\.]+)/i // iCab
- ], [NAME, VERSION], [
- /rv\:([\w\.]{1,9}).+(gecko)/i // Gecko
- ], [VERSION, NAME]
- ],
- os : [[
- // Windows based
- /microsoft\s(windows)\s(vista|xp)/i // Windows (iTunes)
- ], [NAME, VERSION], [
- /(windows)\snt\s6\.2;\s(arm)/i, // Windows RT
- /(windows\sphone(?:\sos)*)[\s\/]?([\d\.\s\w]*)/i, // Windows Phone
- /(windows\smobile|windows)[\s\/]?([ntce\d\.\s]+\w)/i
- ], [NAME, [VERSION, mapper.str, maps.os.windows.version]], [
- /(win(?=3|9|n)|win\s9x\s)([nt\d\.]+)/i
- ], [[NAME, 'Windows'], [VERSION, mapper.str, maps.os.windows.version]], [
- // Mobile/Embedded OS
- /\((bb)(10);/i // BlackBerry 10
- ], [[NAME, 'BlackBerry'], VERSION], [
- /(blackberry)\w*\/?([\w\.]*)/i, // Blackberry
- /(tizen|kaios)[\/\s]([\w\.]+)/i, // Tizen/KaiOS
- /(android|webos|palm\sos|qnx|bada|rim\stablet\sos|meego|sailfish|contiki)[\/\s-]?([\w\.]*)/i
- // Android/WebOS/Palm/QNX/Bada/RIM/MeeGo/Contiki/Sailfish OS
- ], [NAME, VERSION], [
- /(symbian\s?os|symbos|s60(?=;))[\/\s-]?([\w\.]*)/i // Symbian
- ], [[NAME, 'Symbian'], VERSION], [
- /\((series40);/i // Series 40
- ], [NAME], [
- /mozilla.+\(mobile;.+gecko.+firefox/i // Firefox OS
- ], [[NAME, 'Firefox OS'], VERSION], [
- // Console
- /(nintendo|playstation)\s([wids34portablevu]+)/i, // Nintendo/Playstation
- // GNU/Linux based
- /(mint)[\/\s\(]?(\w*)/i, // Mint
- /(mageia|vectorlinux)[;\s]/i, // Mageia/VectorLinux
- /(joli|[kxln]?ubuntu|debian|suse|opensuse|gentoo|(?=\s)arch|slackware|fedora|mandriva|centos|pclinuxos|redhat|zenwalk|linpus)[\/\s-]?(?!chrom)([\w\.-]*)/i,
- // Joli/Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware
- // Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus
- /(hurd|linux)\s?([\w\.]*)/i, // Hurd/Linux
- /(gnu)\s?([\w\.]*)/i // GNU
- ], [NAME, VERSION], [
- /(cros)\s[\w]+\s([\w\.]+\w)/i // Chromium OS
- ], [[NAME, 'Chromium OS'], VERSION],[
- // Solaris
- /(sunos)\s?([\w\.\d]*)/i // Solaris
- ], [[NAME, 'Solaris'], VERSION], [
- // BSD based
- /\s([frentopc-]{0,4}bsd|dragonfly)\s?([\w\.]*)/i // FreeBSD/NetBSD/OpenBSD/PC-BSD/DragonFly
- ], [NAME, VERSION],[
- /(haiku)\s(\w+)/i // Haiku
- ], [NAME, VERSION],[
- /cfnetwork\/.+darwin/i,
- /ip[honead]{2,4}(?:.*os\s([\w]+)\slike\smac|;\sopera)/i // iOS
- ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
- /(mac\sos\sx)\s?([\w\s\.]*)/i,
- /(macintosh|mac(?=_powerpc)\s)/i // Mac OS
- ], [[NAME, 'Mac OS'], [VERSION, /_/g, '.']], [
- // Other
- /((?:open)?solaris)[\/\s-]?([\w\.]*)/i, // Solaris
- /(aix)\s((\d)(?=\.|\)|\s)[\w\.])*/i, // AIX
- /(plan\s9|minix|beos|os\/2|amigaos|morphos|risc\sos|openvms|fuchsia)/i,
- // Plan9/Minix/BeOS/OS2/AmigaOS/MorphOS/RISCOS/OpenVMS/Fuchsia
- /(unix)\s?([\w\.]*)/i // UNIX
- ], [NAME, VERSION]
- ]
- };
- /////////////////
- // Constructor
- ////////////////
- var UAParser = function (uastring, extensions) {
- if (typeof uastring === 'object') {
- extensions = uastring;
- uastring = undefined;
- }
- if (!(this instanceof UAParser)) {
- return new UAParser(uastring, extensions).getResult();
- }
- var ua = uastring || ((window && window.navigator && window.navigator.userAgent) ? window.navigator.userAgent : EMPTY);
- var rgxmap = extensions ? util.extend(regexes, extensions) : regexes;
- this.getBrowser = function () {
- var browser = { name: undefined, version: undefined };
- mapper.rgx.call(browser, ua, rgxmap.browser);
- browser.major = util.major(browser.version); // deprecated
- return browser;
- };
- this.getCPU = function () {
- var cpu = { architecture: undefined };
- mapper.rgx.call(cpu, ua, rgxmap.cpu);
- return cpu;
- };
- this.getDevice = function () {
- var device = { vendor: undefined, model: undefined, type: undefined };
- mapper.rgx.call(device, ua, rgxmap.device);
- return device;
- };
- this.getEngine = function () {
- var engine = { name: undefined, version: undefined };
- mapper.rgx.call(engine, ua, rgxmap.engine);
- return engine;
- };
- this.getOS = function () {
- var os = { name: undefined, version: undefined };
- mapper.rgx.call(os, ua, rgxmap.os);
- return 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 ua;
- };
- this.setUA = function (uastring) {
- ua = uastring;
- return this;
- };
- return this;
- };
- UAParser.VERSION = LIBVERSION;
- UAParser.BROWSER = {
- NAME : NAME,
- MAJOR : MAJOR, // deprecated
- VERSION : VERSION
- };
- UAParser.CPU = {
- ARCHITECTURE : ARCHITECTURE
- };
- UAParser.DEVICE = {
- MODEL : MODEL,
- VENDOR : VENDOR,
- TYPE : TYPE,
- CONSOLE : CONSOLE,
- MOBILE : MOBILE,
- SMARTTV : SMARTTV,
- TABLET : TABLET,
- WEARABLE: WEARABLE,
- EMBEDDED: EMBEDDED
- };
- UAParser.ENGINE = {
- NAME : NAME,
- VERSION : VERSION
- };
- UAParser.OS = {
- NAME : NAME,
- VERSION : VERSION
- };
- ///////////
- // Export
- //////////
- // check js environment
- if (typeof(exports) !== UNDEF_TYPE) {
- // nodejs env
- if (typeof module !== UNDEF_TYPE && module.exports) {
- exports = module.exports = UAParser;
- }
- exports.UAParser = UAParser;
- } else {
- // requirejs env (optional)
- if (typeof(define) === 'function' && define.amd) {
- define(function () {
- return UAParser;
- });
- } else if (window) {
- // browser env
- window.UAParser = UAParser;
- }
- }
- // jQuery/Zepto specific (optional)
- // Note:
- // In AMD env the global scope should be kept clean, but jQuery is an exception.
- // jQuery always exports to global scope, unless jQuery.noConflict(true) is used,
- // and we should catch that.
- var $ = window && (window.jQuery || window.Zepto);
- if ($ && !$.ua) {
- var parser = new UAParser();
- $.ua = parser.getResult();
- $.ua.get = function () {
- return parser.getUA();
- };
- $.ua.set = function (uastring) {
- parser.setUA(uastring);
- var result = parser.getResult();
- for (var prop in result) {
- $.ua[prop] = result[prop];
- }
- };
- }
- })(typeof window === 'object' ? window : this);
- },{}],22:[function(require,module,exports){
- /**
- * Convert array of 16 byte values to UUID string format of the form:
- * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
- */
- var byteToHex = [];
- for (var i = 0; i < 256; ++i) {
- byteToHex[i] = (i + 0x100).toString(16).substr(1);
- }
- function bytesToUuid(buf, offset) {
- var i = offset || 0;
- var bth = byteToHex;
- // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
- return ([
- bth[buf[i++]], bth[buf[i++]],
- bth[buf[i++]], bth[buf[i++]], '-',
- bth[buf[i++]], bth[buf[i++]], '-',
- bth[buf[i++]], bth[buf[i++]], '-',
- bth[buf[i++]], bth[buf[i++]], '-',
- bth[buf[i++]], bth[buf[i++]],
- bth[buf[i++]], bth[buf[i++]],
- bth[buf[i++]], bth[buf[i++]]
- ]).join('');
- }
- module.exports = bytesToUuid;
- },{}],23:[function(require,module,exports){
- // Unique ID creation requires a high quality random # generator. In the
- // browser this is a little complicated due to unknown quality of Math.random()
- // and inconsistent support for the `crypto` API. We do the best we can via
- // feature-detection
- // getRandomValues needs to be invoked in a context where "this" is a Crypto
- // implementation. Also, find the complete implementation of crypto on IE11.
- var getRandomValues = (typeof(crypto) != 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto)) ||
- (typeof(msCrypto) != 'undefined' && typeof window.msCrypto.getRandomValues == 'function' && msCrypto.getRandomValues.bind(msCrypto));
- if (getRandomValues) {
- // WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto
- var rnds8 = new Uint8Array(16); // eslint-disable-line no-undef
- module.exports = function whatwgRNG() {
- getRandomValues(rnds8);
- return rnds8;
- };
- } else {
- // Math.random()-based (RNG)
- //
- // If all else fails, use Math.random(). It's fast, but is of unspecified
- // quality.
- var rnds = new Array(16);
- module.exports = function mathRNG() {
- for (var i = 0, r; i < 16; i++) {
- if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
- rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
- }
- return rnds;
- };
- }
- },{}],24:[function(require,module,exports){
- var rng = require('./lib/rng');
- var bytesToUuid = require('./lib/bytesToUuid');
- function v4(options, buf, offset) {
- var i = buf && offset || 0;
- if (typeof(options) == 'string') {
- buf = options === 'binary' ? new Array(16) : null;
- options = null;
- }
- options = options || {};
- var rnds = options.random || (options.rng || rng)();
- // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
- rnds[6] = (rnds[6] & 0x0f) | 0x40;
- rnds[8] = (rnds[8] & 0x3f) | 0x80;
- // Copy bytes to buffer, if provided
- if (buf) {
- for (var ii = 0; ii < 16; ++ii) {
- buf[i + ii] = rnds[ii];
- }
- }
- return buf || bytesToUuid(rnds);
- }
- module.exports = v4;
- },{"./lib/bytesToUuid":22,"./lib/rng":23}],25:[function(require,module,exports){
- /*
- WildEmitter.js is a slim little event emitter by @henrikjoreteg largely based
- on @visionmedia's Emitter from UI Kit.
- Why? I wanted it standalone.
- I also wanted support for wildcard emitters like this:
- emitter.on('*', function (eventName, other, event, payloads) {
- });
- emitter.on('somenamespace*', function (eventName, payloads) {
- });
- Please note that callbacks triggered by wildcard registered events also get
- the event name as the first argument.
- */
- module.exports = WildEmitter;
- function WildEmitter() { }
- WildEmitter.mixin = function (constructor) {
- var prototype = constructor.prototype || constructor;
- prototype.isWildEmitter= true;
- // Listen on the given `event` with `fn`. Store a group name if present.
- prototype.on = function (event, groupName, fn) {
- this.callbacks = this.callbacks || {};
- var hasGroup = (arguments.length === 3),
- group = hasGroup ? arguments[1] : undefined,
- func = hasGroup ? arguments[2] : arguments[1];
- func._groupName = group;
- (this.callbacks[event] = this.callbacks[event] || []).push(func);
- return this;
- };
- // Adds an `event` listener that will be invoked a single
- // time then automatically removed.
- prototype.once = function (event, groupName, fn) {
- var self = this,
- hasGroup = (arguments.length === 3),
- group = hasGroup ? arguments[1] : undefined,
- func = hasGroup ? arguments[2] : arguments[1];
- function on() {
- self.off(event, on);
- func.apply(this, arguments);
- }
- this.on(event, group, on);
- return this;
- };
- // Unbinds an entire group
- prototype.releaseGroup = function (groupName) {
- this.callbacks = this.callbacks || {};
- var item, i, len, handlers;
- for (item in this.callbacks) {
- handlers = this.callbacks[item];
- for (i = 0, len = handlers.length; i < len; i++) {
- if (handlers[i]._groupName === groupName) {
- //console.log('removing');
- // remove it and shorten the array we're looping through
- handlers.splice(i, 1);
- i--;
- len--;
- }
- }
- }
- return this;
- };
- // Remove the given callback for `event` or all
- // registered callbacks.
- prototype.off = function (event, fn) {
- this.callbacks = this.callbacks || {};
- var callbacks = this.callbacks[event],
- i;
- if (!callbacks) return this;
- // remove all handlers
- if (arguments.length === 1) {
- delete this.callbacks[event];
- return this;
- }
- // remove specific handler
- i = callbacks.indexOf(fn);
- if (i !== -1) {
- callbacks.splice(i, 1);
- if (callbacks.length === 0) {
- delete this.callbacks[event];
- }
- }
- return this;
- };
- /// Emit `event` with the given args.
- // also calls any `*` handlers
- prototype.emit = function (event) {
- this.callbacks = this.callbacks || {};
- var args = [].slice.call(arguments, 1),
- callbacks = this.callbacks[event],
- specialCallbacks = this.getWildcardCallbacks(event),
- i,
- len,
- item,
- listeners;
- if (callbacks) {
- listeners = callbacks.slice();
- for (i = 0, len = listeners.length; i < len; ++i) {
- if (!listeners[i]) {
- break;
- }
- listeners[i].apply(this, args);
- }
- }
- if (specialCallbacks) {
- len = specialCallbacks.length;
- listeners = specialCallbacks.slice();
- for (i = 0, len = listeners.length; i < len; ++i) {
- if (!listeners[i]) {
- break;
- }
- listeners[i].apply(this, [event].concat(args));
- }
- }
- return this;
- };
- // Helper for for finding special wildcard event handlers that match the event
- prototype.getWildcardCallbacks = function (eventName) {
- this.callbacks = this.callbacks || {};
- var item,
- split,
- result = [];
- for (item in this.callbacks) {
- split = item.split('*');
- if (item === '*' || (split.length === 2 && eventName.slice(0, split[0].length) === split[0])) {
- result = result.concat(this.callbacks[item]);
- }
- }
- return result;
- };
- };
- WildEmitter.mixin(WildEmitter);
- },{}]},{},[2])(2)
- });
|