adapter-latest.js 163 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779
  1. (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.adapter = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  2. /*
  3. * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
  4. *
  5. * Use of this source code is governed by a BSD-style license
  6. * that can be found in the LICENSE file in the root of the source
  7. * tree.
  8. */
  9. /* eslint-env node */
  10. 'use strict';
  11. var SDPUtils = require('sdp');
  12. function writeMediaSection(transceiver, caps, type, stream, dtlsRole) {
  13. var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);
  14. // Map ICE parameters (ufrag, pwd) to SDP.
  15. sdp += SDPUtils.writeIceParameters(
  16. transceiver.iceGatherer.getLocalParameters());
  17. // Map DTLS parameters to SDP.
  18. sdp += SDPUtils.writeDtlsParameters(
  19. transceiver.dtlsTransport.getLocalParameters(),
  20. type === 'offer' ? 'actpass' : dtlsRole || 'active');
  21. sdp += 'a=mid:' + transceiver.mid + '\r\n';
  22. if (transceiver.rtpSender && transceiver.rtpReceiver) {
  23. sdp += 'a=sendrecv\r\n';
  24. } else if (transceiver.rtpSender) {
  25. sdp += 'a=sendonly\r\n';
  26. } else if (transceiver.rtpReceiver) {
  27. sdp += 'a=recvonly\r\n';
  28. } else {
  29. sdp += 'a=inactive\r\n';
  30. }
  31. if (transceiver.rtpSender) {
  32. // spec.
  33. var msid = 'msid:' + stream.id + ' ' +
  34. transceiver.rtpSender.track.id + '\r\n';
  35. sdp += 'a=' + msid;
  36. // for Chrome.
  37. sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
  38. ' ' + msid;
  39. if (transceiver.sendEncodingParameters[0].rtx) {
  40. sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
  41. ' ' + msid;
  42. sdp += 'a=ssrc-group:FID ' +
  43. transceiver.sendEncodingParameters[0].ssrc + ' ' +
  44. transceiver.sendEncodingParameters[0].rtx.ssrc +
  45. '\r\n';
  46. }
  47. }
  48. // FIXME: this should be written by writeRtpDescription.
  49. sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
  50. ' cname:' + SDPUtils.localCName + '\r\n';
  51. if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {
  52. sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
  53. ' cname:' + SDPUtils.localCName + '\r\n';
  54. }
  55. return sdp;
  56. }
  57. // Edge does not like
  58. // 1) stun: filtered after 14393 unless ?transport=udp is present
  59. // 2) turn: that does not have all of turn:host:port?transport=udp
  60. // 3) turn: with ipv6 addresses
  61. // 4) turn: occurring muliple times
  62. function filterIceServers(iceServers, edgeVersion) {
  63. var hasTurn = false;
  64. iceServers = JSON.parse(JSON.stringify(iceServers));
  65. return iceServers.filter(function(server) {
  66. if (server && (server.urls || server.url)) {
  67. var urls = server.urls || server.url;
  68. if (server.url && !server.urls) {
  69. console.warn('RTCIceServer.url is deprecated! Use urls instead.');
  70. }
  71. var isString = typeof urls === 'string';
  72. if (isString) {
  73. urls = [urls];
  74. }
  75. urls = urls.filter(function(url) {
  76. var validTurn = url.indexOf('turn:') === 0 &&
  77. url.indexOf('transport=udp') !== -1 &&
  78. url.indexOf('turn:[') === -1 &&
  79. !hasTurn;
  80. if (validTurn) {
  81. hasTurn = true;
  82. return true;
  83. }
  84. return url.indexOf('stun:') === 0 && edgeVersion >= 14393 &&
  85. url.indexOf('?transport=udp') === -1;
  86. });
  87. delete server.url;
  88. server.urls = isString ? urls[0] : urls;
  89. return !!urls.length;
  90. }
  91. });
  92. }
  93. // Determines the intersection of local and remote capabilities.
  94. function getCommonCapabilities(localCapabilities, remoteCapabilities) {
  95. var commonCapabilities = {
  96. codecs: [],
  97. headerExtensions: [],
  98. fecMechanisms: []
  99. };
  100. var findCodecByPayloadType = function(pt, codecs) {
  101. pt = parseInt(pt, 10);
  102. for (var i = 0; i < codecs.length; i++) {
  103. if (codecs[i].payloadType === pt ||
  104. codecs[i].preferredPayloadType === pt) {
  105. return codecs[i];
  106. }
  107. }
  108. };
  109. var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) {
  110. var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs);
  111. var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs);
  112. return lCodec && rCodec &&
  113. lCodec.name.toLowerCase() === rCodec.name.toLowerCase();
  114. };
  115. localCapabilities.codecs.forEach(function(lCodec) {
  116. for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
  117. var rCodec = remoteCapabilities.codecs[i];
  118. if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&
  119. lCodec.clockRate === rCodec.clockRate) {
  120. if (lCodec.name.toLowerCase() === 'rtx' &&
  121. lCodec.parameters && rCodec.parameters.apt) {
  122. // for RTX we need to find the local rtx that has a apt
  123. // which points to the same local codec as the remote one.
  124. if (!rtxCapabilityMatches(lCodec, rCodec,
  125. localCapabilities.codecs, remoteCapabilities.codecs)) {
  126. continue;
  127. }
  128. }
  129. rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy
  130. // number of channels is the highest common number of channels
  131. rCodec.numChannels = Math.min(lCodec.numChannels,
  132. rCodec.numChannels);
  133. // push rCodec so we reply with offerer payload type
  134. commonCapabilities.codecs.push(rCodec);
  135. // determine common feedback mechanisms
  136. rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) {
  137. for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {
  138. if (lCodec.rtcpFeedback[j].type === fb.type &&
  139. lCodec.rtcpFeedback[j].parameter === fb.parameter) {
  140. return true;
  141. }
  142. }
  143. return false;
  144. });
  145. // FIXME: also need to determine .parameters
  146. // see https://github.com/openpeer/ortc/issues/569
  147. break;
  148. }
  149. }
  150. });
  151. localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {
  152. for (var i = 0; i < remoteCapabilities.headerExtensions.length;
  153. i++) {
  154. var rHeaderExtension = remoteCapabilities.headerExtensions[i];
  155. if (lHeaderExtension.uri === rHeaderExtension.uri) {
  156. commonCapabilities.headerExtensions.push(rHeaderExtension);
  157. break;
  158. }
  159. }
  160. });
  161. // FIXME: fecMechanisms
  162. return commonCapabilities;
  163. }
  164. // is action=setLocalDescription with type allowed in signalingState
  165. function isActionAllowedInSignalingState(action, type, signalingState) {
  166. return {
  167. offer: {
  168. setLocalDescription: ['stable', 'have-local-offer'],
  169. setRemoteDescription: ['stable', 'have-remote-offer']
  170. },
  171. answer: {
  172. setLocalDescription: ['have-remote-offer', 'have-local-pranswer'],
  173. setRemoteDescription: ['have-local-offer', 'have-remote-pranswer']
  174. }
  175. }[type][action].indexOf(signalingState) !== -1;
  176. }
  177. function maybeAddCandidate(iceTransport, candidate) {
  178. // Edge's internal representation adds some fields therefore
  179. // not all fieldѕ are taken into account.
  180. var alreadyAdded = iceTransport.getRemoteCandidates()
  181. .find(function(remoteCandidate) {
  182. return candidate.foundation === remoteCandidate.foundation &&
  183. candidate.ip === remoteCandidate.ip &&
  184. candidate.port === remoteCandidate.port &&
  185. candidate.priority === remoteCandidate.priority &&
  186. candidate.protocol === remoteCandidate.protocol &&
  187. candidate.type === remoteCandidate.type;
  188. });
  189. if (!alreadyAdded) {
  190. iceTransport.addRemoteCandidate(candidate);
  191. }
  192. return !alreadyAdded;
  193. }
  194. // https://w3c.github.io/mediacapture-main/#mediastream
  195. // Helper function to add the track to the stream and
  196. // dispatch the event ourselves.
  197. function addTrackToStreamAndFireEvent(track, stream) {
  198. stream.addTrack(track);
  199. var e = new Event('addtrack'); // TODO: MediaStreamTrackEvent
  200. e.track = track;
  201. stream.dispatchEvent(e);
  202. }
  203. function removeTrackFromStreamAndFireEvent(track, stream) {
  204. stream.removeTrack(track);
  205. var e = new Event('removetrack'); // TODO: MediaStreamTrackEvent
  206. e.track = track;
  207. stream.dispatchEvent(e);
  208. }
  209. function fireAddTrack(pc, track, receiver, streams) {
  210. var trackEvent = new Event('track');
  211. trackEvent.track = track;
  212. trackEvent.receiver = receiver;
  213. trackEvent.transceiver = {receiver: receiver};
  214. trackEvent.streams = streams;
  215. window.setTimeout(function() {
  216. pc._dispatchEvent('track', trackEvent);
  217. });
  218. }
  219. function makeError(name, description) {
  220. var e = new Error(description);
  221. e.name = name;
  222. return e;
  223. }
  224. module.exports = function(window, edgeVersion) {
  225. var RTCPeerConnection = function(config) {
  226. var pc = this;
  227. var _eventTarget = document.createDocumentFragment();
  228. ['addEventListener', 'removeEventListener', 'dispatchEvent']
  229. .forEach(function(method) {
  230. pc[method] = _eventTarget[method].bind(_eventTarget);
  231. });
  232. this.canTrickleIceCandidates = null;
  233. this.needNegotiation = false;
  234. this.localStreams = [];
  235. this.remoteStreams = [];
  236. this.localDescription = null;
  237. this.remoteDescription = null;
  238. this.signalingState = 'stable';
  239. this.iceConnectionState = 'new';
  240. this.iceGatheringState = 'new';
  241. config = JSON.parse(JSON.stringify(config || {}));
  242. this.usingBundle = config.bundlePolicy === 'max-bundle';
  243. if (config.rtcpMuxPolicy === 'negotiate') {
  244. throw(makeError('NotSupportedError',
  245. 'rtcpMuxPolicy \'negotiate\' is not supported'));
  246. } else if (!config.rtcpMuxPolicy) {
  247. config.rtcpMuxPolicy = 'require';
  248. }
  249. switch (config.iceTransportPolicy) {
  250. case 'all':
  251. case 'relay':
  252. break;
  253. default:
  254. config.iceTransportPolicy = 'all';
  255. break;
  256. }
  257. switch (config.bundlePolicy) {
  258. case 'balanced':
  259. case 'max-compat':
  260. case 'max-bundle':
  261. break;
  262. default:
  263. config.bundlePolicy = 'balanced';
  264. break;
  265. }
  266. config.iceServers = filterIceServers(config.iceServers || [], edgeVersion);
  267. this._iceGatherers = [];
  268. if (config.iceCandidatePoolSize) {
  269. for (var i = config.iceCandidatePoolSize; i > 0; i--) {
  270. this._iceGatherers = new window.RTCIceGatherer({
  271. iceServers: config.iceServers,
  272. gatherPolicy: config.iceTransportPolicy
  273. });
  274. }
  275. } else {
  276. config.iceCandidatePoolSize = 0;
  277. }
  278. this._config = config;
  279. // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
  280. // everything that is needed to describe a SDP m-line.
  281. this.transceivers = [];
  282. this._sdpSessionId = SDPUtils.generateSessionId();
  283. this._sdpSessionVersion = 0;
  284. this._dtlsRole = undefined; // role for a=setup to use in answers.
  285. this._isClosed = false;
  286. };
  287. // set up event handlers on prototype
  288. RTCPeerConnection.prototype.onicecandidate = null;
  289. RTCPeerConnection.prototype.onaddstream = null;
  290. RTCPeerConnection.prototype.ontrack = null;
  291. RTCPeerConnection.prototype.onremovestream = null;
  292. RTCPeerConnection.prototype.onsignalingstatechange = null;
  293. RTCPeerConnection.prototype.oniceconnectionstatechange = null;
  294. RTCPeerConnection.prototype.onicegatheringstatechange = null;
  295. RTCPeerConnection.prototype.onnegotiationneeded = null;
  296. RTCPeerConnection.prototype.ondatachannel = null;
  297. RTCPeerConnection.prototype._dispatchEvent = function(name, event) {
  298. if (this._isClosed) {
  299. return;
  300. }
  301. this.dispatchEvent(event);
  302. if (typeof this['on' + name] === 'function') {
  303. this['on' + name](event);
  304. }
  305. };
  306. RTCPeerConnection.prototype._emitGatheringStateChange = function() {
  307. var event = new Event('icegatheringstatechange');
  308. this._dispatchEvent('icegatheringstatechange', event);
  309. };
  310. RTCPeerConnection.prototype.getConfiguration = function() {
  311. return this._config;
  312. };
  313. RTCPeerConnection.prototype.getLocalStreams = function() {
  314. return this.localStreams;
  315. };
  316. RTCPeerConnection.prototype.getRemoteStreams = function() {
  317. return this.remoteStreams;
  318. };
  319. // internal helper to create a transceiver object.
  320. // (whih is not yet the same as the WebRTC 1.0 transceiver)
  321. RTCPeerConnection.prototype._createTransceiver = function(kind) {
  322. var hasBundleTransport = this.transceivers.length > 0;
  323. var transceiver = {
  324. track: null,
  325. iceGatherer: null,
  326. iceTransport: null,
  327. dtlsTransport: null,
  328. localCapabilities: null,
  329. remoteCapabilities: null,
  330. rtpSender: null,
  331. rtpReceiver: null,
  332. kind: kind,
  333. mid: null,
  334. sendEncodingParameters: null,
  335. recvEncodingParameters: null,
  336. stream: null,
  337. associatedRemoteMediaStreams: [],
  338. wantReceive: true
  339. };
  340. if (this.usingBundle && hasBundleTransport) {
  341. transceiver.iceTransport = this.transceivers[0].iceTransport;
  342. transceiver.dtlsTransport = this.transceivers[0].dtlsTransport;
  343. } else {
  344. var transports = this._createIceAndDtlsTransports();
  345. transceiver.iceTransport = transports.iceTransport;
  346. transceiver.dtlsTransport = transports.dtlsTransport;
  347. }
  348. this.transceivers.push(transceiver);
  349. return transceiver;
  350. };
  351. RTCPeerConnection.prototype.addTrack = function(track, stream) {
  352. var alreadyExists = this.transceivers.find(function(s) {
  353. return s.track === track;
  354. });
  355. if (alreadyExists) {
  356. throw makeError('InvalidAccessError', 'Track already exists.');
  357. }
  358. if (this.signalingState === 'closed') {
  359. throw makeError('InvalidStateError',
  360. 'Attempted to call addTrack on a closed peerconnection.');
  361. }
  362. var transceiver;
  363. for (var i = 0; i < this.transceivers.length; i++) {
  364. if (!this.transceivers[i].track &&
  365. this.transceivers[i].kind === track.kind) {
  366. transceiver = this.transceivers[i];
  367. }
  368. }
  369. if (!transceiver) {
  370. transceiver = this._createTransceiver(track.kind);
  371. }
  372. this._maybeFireNegotiationNeeded();
  373. if (this.localStreams.indexOf(stream) === -1) {
  374. this.localStreams.push(stream);
  375. }
  376. transceiver.track = track;
  377. transceiver.stream = stream;
  378. transceiver.rtpSender = new window.RTCRtpSender(track,
  379. transceiver.dtlsTransport);
  380. return transceiver.rtpSender;
  381. };
  382. RTCPeerConnection.prototype.addStream = function(stream) {
  383. var pc = this;
  384. if (edgeVersion >= 15025) {
  385. stream.getTracks().forEach(function(track) {
  386. pc.addTrack(track, stream);
  387. });
  388. } else {
  389. // Clone is necessary for local demos mostly, attaching directly
  390. // to two different senders does not work (build 10547).
  391. // Fixed in 15025 (or earlier)
  392. var clonedStream = stream.clone();
  393. stream.getTracks().forEach(function(track, idx) {
  394. var clonedTrack = clonedStream.getTracks()[idx];
  395. track.addEventListener('enabled', function(event) {
  396. clonedTrack.enabled = event.enabled;
  397. });
  398. });
  399. clonedStream.getTracks().forEach(function(track) {
  400. pc.addTrack(track, clonedStream);
  401. });
  402. }
  403. };
  404. RTCPeerConnection.prototype.removeTrack = function(sender) {
  405. if (!(sender instanceof window.RTCRtpSender)) {
  406. throw new TypeError('Argument 1 of RTCPeerConnection.removeTrack ' +
  407. 'does not implement interface RTCRtpSender.');
  408. }
  409. var transceiver = this.transceivers.find(function(t) {
  410. return t.rtpSender === sender;
  411. });
  412. if (!transceiver) {
  413. throw makeError('InvalidAccessError',
  414. 'Sender was not created by this connection.');
  415. }
  416. var stream = transceiver.stream;
  417. transceiver.rtpSender.stop();
  418. transceiver.rtpSender = null;
  419. transceiver.track = null;
  420. transceiver.stream = null;
  421. // remove the stream from the set of local streams
  422. var localStreams = this.transceivers.map(function(t) {
  423. return t.stream;
  424. });
  425. if (localStreams.indexOf(stream) === -1 &&
  426. this.localStreams.indexOf(stream) > -1) {
  427. this.localStreams.splice(this.localStreams.indexOf(stream), 1);
  428. }
  429. this._maybeFireNegotiationNeeded();
  430. };
  431. RTCPeerConnection.prototype.removeStream = function(stream) {
  432. var pc = this;
  433. stream.getTracks().forEach(function(track) {
  434. var sender = pc.getSenders().find(function(s) {
  435. return s.track === track;
  436. });
  437. if (sender) {
  438. pc.removeTrack(sender);
  439. }
  440. });
  441. };
  442. RTCPeerConnection.prototype.getSenders = function() {
  443. return this.transceivers.filter(function(transceiver) {
  444. return !!transceiver.rtpSender;
  445. })
  446. .map(function(transceiver) {
  447. return transceiver.rtpSender;
  448. });
  449. };
  450. RTCPeerConnection.prototype.getReceivers = function() {
  451. return this.transceivers.filter(function(transceiver) {
  452. return !!transceiver.rtpReceiver;
  453. })
  454. .map(function(transceiver) {
  455. return transceiver.rtpReceiver;
  456. });
  457. };
  458. RTCPeerConnection.prototype._createIceGatherer = function(sdpMLineIndex,
  459. usingBundle) {
  460. var pc = this;
  461. if (usingBundle && sdpMLineIndex > 0) {
  462. return this.transceivers[0].iceGatherer;
  463. } else if (this._iceGatherers.length) {
  464. return this._iceGatherers.shift();
  465. }
  466. var iceGatherer = new window.RTCIceGatherer({
  467. iceServers: this._config.iceServers,
  468. gatherPolicy: this._config.iceTransportPolicy
  469. });
  470. Object.defineProperty(iceGatherer, 'state',
  471. {value: 'new', writable: true}
  472. );
  473. this.transceivers[sdpMLineIndex].candidates = [];
  474. this.transceivers[sdpMLineIndex].bufferCandidates = function(event) {
  475. var end = !event.candidate || Object.keys(event.candidate).length === 0;
  476. // polyfill since RTCIceGatherer.state is not implemented in
  477. // Edge 10547 yet.
  478. iceGatherer.state = end ? 'completed' : 'gathering';
  479. if (pc.transceivers[sdpMLineIndex].candidates !== null) {
  480. pc.transceivers[sdpMLineIndex].candidates.push(event.candidate);
  481. }
  482. };
  483. iceGatherer.addEventListener('localcandidate',
  484. this.transceivers[sdpMLineIndex].bufferCandidates);
  485. return iceGatherer;
  486. };
  487. // start gathering from an RTCIceGatherer.
  488. RTCPeerConnection.prototype._gather = function(mid, sdpMLineIndex) {
  489. var pc = this;
  490. var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;
  491. if (iceGatherer.onlocalcandidate) {
  492. return;
  493. }
  494. var candidates = this.transceivers[sdpMLineIndex].candidates;
  495. this.transceivers[sdpMLineIndex].candidates = null;
  496. iceGatherer.removeEventListener('localcandidate',
  497. this.transceivers[sdpMLineIndex].bufferCandidates);
  498. iceGatherer.onlocalcandidate = function(evt) {
  499. if (pc.usingBundle && sdpMLineIndex > 0) {
  500. // if we know that we use bundle we can drop candidates with
  501. // ѕdpMLineIndex > 0. If we don't do this then our state gets
  502. // confused since we dispose the extra ice gatherer.
  503. return;
  504. }
  505. var event = new Event('icecandidate');
  506. event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};
  507. var cand = evt.candidate;
  508. // Edge emits an empty object for RTCIceCandidateComplete‥
  509. var end = !cand || Object.keys(cand).length === 0;
  510. if (end) {
  511. // polyfill since RTCIceGatherer.state is not implemented in
  512. // Edge 10547 yet.
  513. if (iceGatherer.state === 'new' || iceGatherer.state === 'gathering') {
  514. iceGatherer.state = 'completed';
  515. }
  516. } else {
  517. if (iceGatherer.state === 'new') {
  518. iceGatherer.state = 'gathering';
  519. }
  520. // RTCIceCandidate doesn't have a component, needs to be added
  521. cand.component = 1;
  522. event.candidate.candidate = SDPUtils.writeCandidate(cand);
  523. }
  524. // update local description.
  525. var sections = SDPUtils.splitSections(pc.localDescription.sdp);
  526. if (!end) {
  527. sections[event.candidate.sdpMLineIndex + 1] +=
  528. 'a=' + event.candidate.candidate + '\r\n';
  529. } else {
  530. sections[event.candidate.sdpMLineIndex + 1] +=
  531. 'a=end-of-candidates\r\n';
  532. }
  533. pc.localDescription.sdp = sections.join('');
  534. var complete = pc.transceivers.every(function(transceiver) {
  535. return transceiver.iceGatherer &&
  536. transceiver.iceGatherer.state === 'completed';
  537. });
  538. if (pc.iceGatheringState !== 'gathering') {
  539. pc.iceGatheringState = 'gathering';
  540. pc._emitGatheringStateChange();
  541. }
  542. // Emit candidate. Also emit null candidate when all gatherers are
  543. // complete.
  544. if (!end) {
  545. pc._dispatchEvent('icecandidate', event);
  546. }
  547. if (complete) {
  548. pc._dispatchEvent('icecandidate', new Event('icecandidate'));
  549. pc.iceGatheringState = 'complete';
  550. pc._emitGatheringStateChange();
  551. }
  552. };
  553. // emit already gathered candidates.
  554. window.setTimeout(function() {
  555. candidates.forEach(function(candidate) {
  556. var e = new Event('RTCIceGatherEvent');
  557. e.candidate = candidate;
  558. iceGatherer.onlocalcandidate(e);
  559. });
  560. }, 0);
  561. };
  562. // Create ICE transport and DTLS transport.
  563. RTCPeerConnection.prototype._createIceAndDtlsTransports = function() {
  564. var pc = this;
  565. var iceTransport = new window.RTCIceTransport(null);
  566. iceTransport.onicestatechange = function() {
  567. pc._updateConnectionState();
  568. };
  569. var dtlsTransport = new window.RTCDtlsTransport(iceTransport);
  570. dtlsTransport.ondtlsstatechange = function() {
  571. pc._updateConnectionState();
  572. };
  573. dtlsTransport.onerror = function() {
  574. // onerror does not set state to failed by itpc.
  575. Object.defineProperty(dtlsTransport, 'state',
  576. {value: 'failed', writable: true});
  577. pc._updateConnectionState();
  578. };
  579. return {
  580. iceTransport: iceTransport,
  581. dtlsTransport: dtlsTransport
  582. };
  583. };
  584. // Destroy ICE gatherer, ICE transport and DTLS transport.
  585. // Without triggering the callbacks.
  586. RTCPeerConnection.prototype._disposeIceAndDtlsTransports = function(
  587. sdpMLineIndex) {
  588. var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;
  589. if (iceGatherer) {
  590. delete iceGatherer.onlocalcandidate;
  591. delete this.transceivers[sdpMLineIndex].iceGatherer;
  592. }
  593. var iceTransport = this.transceivers[sdpMLineIndex].iceTransport;
  594. if (iceTransport) {
  595. delete iceTransport.onicestatechange;
  596. delete this.transceivers[sdpMLineIndex].iceTransport;
  597. }
  598. var dtlsTransport = this.transceivers[sdpMLineIndex].dtlsTransport;
  599. if (dtlsTransport) {
  600. delete dtlsTransport.ondtlsstatechange;
  601. delete dtlsTransport.onerror;
  602. delete this.transceivers[sdpMLineIndex].dtlsTransport;
  603. }
  604. };
  605. // Start the RTP Sender and Receiver for a transceiver.
  606. RTCPeerConnection.prototype._transceive = function(transceiver,
  607. send, recv) {
  608. var params = getCommonCapabilities(transceiver.localCapabilities,
  609. transceiver.remoteCapabilities);
  610. if (send && transceiver.rtpSender) {
  611. params.encodings = transceiver.sendEncodingParameters;
  612. params.rtcp = {
  613. cname: SDPUtils.localCName,
  614. compound: transceiver.rtcpParameters.compound
  615. };
  616. if (transceiver.recvEncodingParameters.length) {
  617. params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;
  618. }
  619. transceiver.rtpSender.send(params);
  620. }
  621. if (recv && transceiver.rtpReceiver && params.codecs.length > 0) {
  622. // remove RTX field in Edge 14942
  623. if (transceiver.kind === 'video'
  624. && transceiver.recvEncodingParameters
  625. && edgeVersion < 15019) {
  626. transceiver.recvEncodingParameters.forEach(function(p) {
  627. delete p.rtx;
  628. });
  629. }
  630. if (transceiver.recvEncodingParameters.length) {
  631. params.encodings = transceiver.recvEncodingParameters;
  632. }
  633. params.rtcp = {
  634. compound: transceiver.rtcpParameters.compound
  635. };
  636. if (transceiver.rtcpParameters.cname) {
  637. params.rtcp.cname = transceiver.rtcpParameters.cname;
  638. }
  639. if (transceiver.sendEncodingParameters.length) {
  640. params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;
  641. }
  642. transceiver.rtpReceiver.receive(params);
  643. }
  644. };
  645. RTCPeerConnection.prototype.setLocalDescription = function(description) {
  646. var pc = this;
  647. if (!isActionAllowedInSignalingState('setLocalDescription',
  648. description.type, this.signalingState) || this._isClosed) {
  649. return Promise.reject(makeError('InvalidStateError',
  650. 'Can not set local ' + description.type +
  651. ' in state ' + pc.signalingState));
  652. }
  653. var sections;
  654. var sessionpart;
  655. if (description.type === 'offer') {
  656. // VERY limited support for SDP munging. Limited to:
  657. // * changing the order of codecs
  658. sections = SDPUtils.splitSections(description.sdp);
  659. sessionpart = sections.shift();
  660. sections.forEach(function(mediaSection, sdpMLineIndex) {
  661. var caps = SDPUtils.parseRtpParameters(mediaSection);
  662. pc.transceivers[sdpMLineIndex].localCapabilities = caps;
  663. });
  664. this.transceivers.forEach(function(transceiver, sdpMLineIndex) {
  665. pc._gather(transceiver.mid, sdpMLineIndex);
  666. });
  667. } else if (description.type === 'answer') {
  668. sections = SDPUtils.splitSections(pc.remoteDescription.sdp);
  669. sessionpart = sections.shift();
  670. var isIceLite = SDPUtils.matchPrefix(sessionpart,
  671. 'a=ice-lite').length > 0;
  672. sections.forEach(function(mediaSection, sdpMLineIndex) {
  673. var transceiver = pc.transceivers[sdpMLineIndex];
  674. var iceGatherer = transceiver.iceGatherer;
  675. var iceTransport = transceiver.iceTransport;
  676. var dtlsTransport = transceiver.dtlsTransport;
  677. var localCapabilities = transceiver.localCapabilities;
  678. var remoteCapabilities = transceiver.remoteCapabilities;
  679. // treat bundle-only as not-rejected.
  680. var rejected = SDPUtils.isRejected(mediaSection) &&
  681. SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0;
  682. if (!rejected && !transceiver.isDatachannel) {
  683. var remoteIceParameters = SDPUtils.getIceParameters(
  684. mediaSection, sessionpart);
  685. var remoteDtlsParameters = SDPUtils.getDtlsParameters(
  686. mediaSection, sessionpart);
  687. if (isIceLite) {
  688. remoteDtlsParameters.role = 'server';
  689. }
  690. if (!pc.usingBundle || sdpMLineIndex === 0) {
  691. pc._gather(transceiver.mid, sdpMLineIndex);
  692. if (iceTransport.state === 'new') {
  693. iceTransport.start(iceGatherer, remoteIceParameters,
  694. isIceLite ? 'controlling' : 'controlled');
  695. }
  696. if (dtlsTransport.state === 'new') {
  697. dtlsTransport.start(remoteDtlsParameters);
  698. }
  699. }
  700. // Calculate intersection of capabilities.
  701. var params = getCommonCapabilities(localCapabilities,
  702. remoteCapabilities);
  703. // Start the RTCRtpSender. The RTCRtpReceiver for this
  704. // transceiver has already been started in setRemoteDescription.
  705. pc._transceive(transceiver,
  706. params.codecs.length > 0,
  707. false);
  708. }
  709. });
  710. }
  711. this.localDescription = {
  712. type: description.type,
  713. sdp: description.sdp
  714. };
  715. switch (description.type) {
  716. case 'offer':
  717. this._updateSignalingState('have-local-offer');
  718. break;
  719. case 'answer':
  720. this._updateSignalingState('stable');
  721. break;
  722. default:
  723. throw new TypeError('unsupported type "' + description.type +
  724. '"');
  725. }
  726. return Promise.resolve();
  727. };
  728. RTCPeerConnection.prototype.setRemoteDescription = function(description) {
  729. var pc = this;
  730. if (!isActionAllowedInSignalingState('setRemoteDescription',
  731. description.type, this.signalingState) || this._isClosed) {
  732. return Promise.reject(makeError('InvalidStateError',
  733. 'Can not set remote ' + description.type +
  734. ' in state ' + pc.signalingState));
  735. }
  736. var streams = {};
  737. this.remoteStreams.forEach(function(stream) {
  738. streams[stream.id] = stream;
  739. });
  740. var receiverList = [];
  741. var sections = SDPUtils.splitSections(description.sdp);
  742. var sessionpart = sections.shift();
  743. var isIceLite = SDPUtils.matchPrefix(sessionpart,
  744. 'a=ice-lite').length > 0;
  745. var usingBundle = SDPUtils.matchPrefix(sessionpart,
  746. 'a=group:BUNDLE ').length > 0;
  747. this.usingBundle = usingBundle;
  748. var iceOptions = SDPUtils.matchPrefix(sessionpart,
  749. 'a=ice-options:')[0];
  750. if (iceOptions) {
  751. this.canTrickleIceCandidates = iceOptions.substr(14).split(' ')
  752. .indexOf('trickle') >= 0;
  753. } else {
  754. this.canTrickleIceCandidates = false;
  755. }
  756. sections.forEach(function(mediaSection, sdpMLineIndex) {
  757. var lines = SDPUtils.splitLines(mediaSection);
  758. var kind = SDPUtils.getKind(mediaSection);
  759. // treat bundle-only as not-rejected.
  760. var rejected = SDPUtils.isRejected(mediaSection) &&
  761. SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0;
  762. var protocol = lines[0].substr(2).split(' ')[2];
  763. var direction = SDPUtils.getDirection(mediaSection, sessionpart);
  764. var remoteMsid = SDPUtils.parseMsid(mediaSection);
  765. var mid = SDPUtils.getMid(mediaSection) || SDPUtils.generateIdentifier();
  766. // Reject datachannels which are not implemented yet.
  767. if (kind === 'application' && protocol === 'DTLS/SCTP') {
  768. pc.transceivers[sdpMLineIndex] = {
  769. mid: mid,
  770. isDatachannel: true
  771. };
  772. return;
  773. }
  774. var transceiver;
  775. var iceGatherer;
  776. var iceTransport;
  777. var dtlsTransport;
  778. var rtpReceiver;
  779. var sendEncodingParameters;
  780. var recvEncodingParameters;
  781. var localCapabilities;
  782. var track;
  783. // FIXME: ensure the mediaSection has rtcp-mux set.
  784. var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);
  785. var remoteIceParameters;
  786. var remoteDtlsParameters;
  787. if (!rejected) {
  788. remoteIceParameters = SDPUtils.getIceParameters(mediaSection,
  789. sessionpart);
  790. remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,
  791. sessionpart);
  792. remoteDtlsParameters.role = 'client';
  793. }
  794. recvEncodingParameters =
  795. SDPUtils.parseRtpEncodingParameters(mediaSection);
  796. var rtcpParameters = SDPUtils.parseRtcpParameters(mediaSection);
  797. var isComplete = SDPUtils.matchPrefix(mediaSection,
  798. 'a=end-of-candidates', sessionpart).length > 0;
  799. var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')
  800. .map(function(cand) {
  801. return SDPUtils.parseCandidate(cand);
  802. })
  803. .filter(function(cand) {
  804. return cand.component === 1;
  805. });
  806. // Check if we can use BUNDLE and dispose transports.
  807. if ((description.type === 'offer' || description.type === 'answer') &&
  808. !rejected && usingBundle && sdpMLineIndex > 0 &&
  809. pc.transceivers[sdpMLineIndex]) {
  810. pc._disposeIceAndDtlsTransports(sdpMLineIndex);
  811. pc.transceivers[sdpMLineIndex].iceGatherer =
  812. pc.transceivers[0].iceGatherer;
  813. pc.transceivers[sdpMLineIndex].iceTransport =
  814. pc.transceivers[0].iceTransport;
  815. pc.transceivers[sdpMLineIndex].dtlsTransport =
  816. pc.transceivers[0].dtlsTransport;
  817. if (pc.transceivers[sdpMLineIndex].rtpSender) {
  818. pc.transceivers[sdpMLineIndex].rtpSender.setTransport(
  819. pc.transceivers[0].dtlsTransport);
  820. }
  821. if (pc.transceivers[sdpMLineIndex].rtpReceiver) {
  822. pc.transceivers[sdpMLineIndex].rtpReceiver.setTransport(
  823. pc.transceivers[0].dtlsTransport);
  824. }
  825. }
  826. if (description.type === 'offer' && !rejected) {
  827. transceiver = pc.transceivers[sdpMLineIndex] ||
  828. pc._createTransceiver(kind);
  829. transceiver.mid = mid;
  830. if (!transceiver.iceGatherer) {
  831. transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex,
  832. usingBundle);
  833. }
  834. if (cands.length && transceiver.iceTransport.state === 'new') {
  835. if (isComplete && (!usingBundle || sdpMLineIndex === 0)) {
  836. transceiver.iceTransport.setRemoteCandidates(cands);
  837. } else {
  838. cands.forEach(function(candidate) {
  839. maybeAddCandidate(transceiver.iceTransport, candidate);
  840. });
  841. }
  842. }
  843. localCapabilities = window.RTCRtpReceiver.getCapabilities(kind);
  844. // filter RTX until additional stuff needed for RTX is implemented
  845. // in adapter.js
  846. if (edgeVersion < 15019) {
  847. localCapabilities.codecs = localCapabilities.codecs.filter(
  848. function(codec) {
  849. return codec.name !== 'rtx';
  850. });
  851. }
  852. sendEncodingParameters = transceiver.sendEncodingParameters || [{
  853. ssrc: (2 * sdpMLineIndex + 2) * 1001
  854. }];
  855. // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams
  856. var isNewTrack = false;
  857. if (direction === 'sendrecv' || direction === 'sendonly') {
  858. isNewTrack = !transceiver.rtpReceiver;
  859. rtpReceiver = transceiver.rtpReceiver ||
  860. new window.RTCRtpReceiver(transceiver.dtlsTransport, kind);
  861. if (isNewTrack) {
  862. var stream;
  863. track = rtpReceiver.track;
  864. // FIXME: does not work with Plan B.
  865. if (remoteMsid && remoteMsid.stream === '-') {
  866. // no-op. a stream id of '-' means: no associated stream.
  867. } else if (remoteMsid) {
  868. if (!streams[remoteMsid.stream]) {
  869. streams[remoteMsid.stream] = new window.MediaStream();
  870. Object.defineProperty(streams[remoteMsid.stream], 'id', {
  871. get: function() {
  872. return remoteMsid.stream;
  873. }
  874. });
  875. }
  876. Object.defineProperty(track, 'id', {
  877. get: function() {
  878. return remoteMsid.track;
  879. }
  880. });
  881. stream = streams[remoteMsid.stream];
  882. } else {
  883. if (!streams.default) {
  884. streams.default = new window.MediaStream();
  885. }
  886. stream = streams.default;
  887. }
  888. if (stream) {
  889. addTrackToStreamAndFireEvent(track, stream);
  890. transceiver.associatedRemoteMediaStreams.push(stream);
  891. }
  892. receiverList.push([track, rtpReceiver, stream]);
  893. }
  894. } else if (transceiver.rtpReceiver && transceiver.rtpReceiver.track) {
  895. transceiver.associatedRemoteMediaStreams.forEach(function(s) {
  896. var nativeTrack = s.getTracks().find(function(t) {
  897. return t.id === transceiver.rtpReceiver.track.id;
  898. });
  899. if (nativeTrack) {
  900. removeTrackFromStreamAndFireEvent(nativeTrack, s);
  901. }
  902. });
  903. transceiver.associatedRemoteMediaStreams = [];
  904. }
  905. transceiver.localCapabilities = localCapabilities;
  906. transceiver.remoteCapabilities = remoteCapabilities;
  907. transceiver.rtpReceiver = rtpReceiver;
  908. transceiver.rtcpParameters = rtcpParameters;
  909. transceiver.sendEncodingParameters = sendEncodingParameters;
  910. transceiver.recvEncodingParameters = recvEncodingParameters;
  911. // Start the RTCRtpReceiver now. The RTPSender is started in
  912. // setLocalDescription.
  913. pc._transceive(pc.transceivers[sdpMLineIndex],
  914. false,
  915. isNewTrack);
  916. } else if (description.type === 'answer' && !rejected) {
  917. transceiver = pc.transceivers[sdpMLineIndex];
  918. iceGatherer = transceiver.iceGatherer;
  919. iceTransport = transceiver.iceTransport;
  920. dtlsTransport = transceiver.dtlsTransport;
  921. rtpReceiver = transceiver.rtpReceiver;
  922. sendEncodingParameters = transceiver.sendEncodingParameters;
  923. localCapabilities = transceiver.localCapabilities;
  924. pc.transceivers[sdpMLineIndex].recvEncodingParameters =
  925. recvEncodingParameters;
  926. pc.transceivers[sdpMLineIndex].remoteCapabilities =
  927. remoteCapabilities;
  928. pc.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters;
  929. if (cands.length && iceTransport.state === 'new') {
  930. if ((isIceLite || isComplete) &&
  931. (!usingBundle || sdpMLineIndex === 0)) {
  932. iceTransport.setRemoteCandidates(cands);
  933. } else {
  934. cands.forEach(function(candidate) {
  935. maybeAddCandidate(transceiver.iceTransport, candidate);
  936. });
  937. }
  938. }
  939. if (!usingBundle || sdpMLineIndex === 0) {
  940. if (iceTransport.state === 'new') {
  941. iceTransport.start(iceGatherer, remoteIceParameters,
  942. 'controlling');
  943. }
  944. if (dtlsTransport.state === 'new') {
  945. dtlsTransport.start(remoteDtlsParameters);
  946. }
  947. }
  948. pc._transceive(transceiver,
  949. direction === 'sendrecv' || direction === 'recvonly',
  950. direction === 'sendrecv' || direction === 'sendonly');
  951. // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams
  952. if (rtpReceiver &&
  953. (direction === 'sendrecv' || direction === 'sendonly')) {
  954. track = rtpReceiver.track;
  955. if (remoteMsid) {
  956. if (!streams[remoteMsid.stream]) {
  957. streams[remoteMsid.stream] = new window.MediaStream();
  958. }
  959. addTrackToStreamAndFireEvent(track, streams[remoteMsid.stream]);
  960. receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]);
  961. } else {
  962. if (!streams.default) {
  963. streams.default = new window.MediaStream();
  964. }
  965. addTrackToStreamAndFireEvent(track, streams.default);
  966. receiverList.push([track, rtpReceiver, streams.default]);
  967. }
  968. } else {
  969. // FIXME: actually the receiver should be created later.
  970. delete transceiver.rtpReceiver;
  971. }
  972. }
  973. });
  974. if (this._dtlsRole === undefined) {
  975. this._dtlsRole = description.type === 'offer' ? 'active' : 'passive';
  976. }
  977. this.remoteDescription = {
  978. type: description.type,
  979. sdp: description.sdp
  980. };
  981. switch (description.type) {
  982. case 'offer':
  983. this._updateSignalingState('have-remote-offer');
  984. break;
  985. case 'answer':
  986. this._updateSignalingState('stable');
  987. break;
  988. default:
  989. throw new TypeError('unsupported type "' + description.type +
  990. '"');
  991. }
  992. Object.keys(streams).forEach(function(sid) {
  993. var stream = streams[sid];
  994. if (stream.getTracks().length) {
  995. if (pc.remoteStreams.indexOf(stream) === -1) {
  996. pc.remoteStreams.push(stream);
  997. var event = new Event('addstream');
  998. event.stream = stream;
  999. window.setTimeout(function() {
  1000. pc._dispatchEvent('addstream', event);
  1001. });
  1002. }
  1003. receiverList.forEach(function(item) {
  1004. var track = item[0];
  1005. var receiver = item[1];
  1006. if (stream.id !== item[2].id) {
  1007. return;
  1008. }
  1009. fireAddTrack(pc, track, receiver, [stream]);
  1010. });
  1011. }
  1012. });
  1013. receiverList.forEach(function(item) {
  1014. if (item[2]) {
  1015. return;
  1016. }
  1017. fireAddTrack(pc, item[0], item[1], []);
  1018. });
  1019. // check whether addIceCandidate({}) was called within four seconds after
  1020. // setRemoteDescription.
  1021. window.setTimeout(function() {
  1022. if (!(pc && pc.transceivers)) {
  1023. return;
  1024. }
  1025. pc.transceivers.forEach(function(transceiver) {
  1026. if (transceiver.iceTransport &&
  1027. transceiver.iceTransport.state === 'new' &&
  1028. transceiver.iceTransport.getRemoteCandidates().length > 0) {
  1029. console.warn('Timeout for addRemoteCandidate. Consider sending ' +
  1030. 'an end-of-candidates notification');
  1031. transceiver.iceTransport.addRemoteCandidate({});
  1032. }
  1033. });
  1034. }, 4000);
  1035. return Promise.resolve();
  1036. };
  1037. RTCPeerConnection.prototype.close = function() {
  1038. this.transceivers.forEach(function(transceiver) {
  1039. /* not yet
  1040. if (transceiver.iceGatherer) {
  1041. transceiver.iceGatherer.close();
  1042. }
  1043. */
  1044. if (transceiver.iceTransport) {
  1045. transceiver.iceTransport.stop();
  1046. }
  1047. if (transceiver.dtlsTransport) {
  1048. transceiver.dtlsTransport.stop();
  1049. }
  1050. if (transceiver.rtpSender) {
  1051. transceiver.rtpSender.stop();
  1052. }
  1053. if (transceiver.rtpReceiver) {
  1054. transceiver.rtpReceiver.stop();
  1055. }
  1056. });
  1057. // FIXME: clean up tracks, local streams, remote streams, etc
  1058. this._isClosed = true;
  1059. this._updateSignalingState('closed');
  1060. };
  1061. // Update the signaling state.
  1062. RTCPeerConnection.prototype._updateSignalingState = function(newState) {
  1063. this.signalingState = newState;
  1064. var event = new Event('signalingstatechange');
  1065. this._dispatchEvent('signalingstatechange', event);
  1066. };
  1067. // Determine whether to fire the negotiationneeded event.
  1068. RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() {
  1069. var pc = this;
  1070. if (this.signalingState !== 'stable' || this.needNegotiation === true) {
  1071. return;
  1072. }
  1073. this.needNegotiation = true;
  1074. window.setTimeout(function() {
  1075. if (pc.needNegotiation === false) {
  1076. return;
  1077. }
  1078. pc.needNegotiation = false;
  1079. var event = new Event('negotiationneeded');
  1080. pc._dispatchEvent('negotiationneeded', event);
  1081. }, 0);
  1082. };
  1083. // Update the connection state.
  1084. RTCPeerConnection.prototype._updateConnectionState = function() {
  1085. var newState;
  1086. var states = {
  1087. 'new': 0,
  1088. closed: 0,
  1089. connecting: 0,
  1090. checking: 0,
  1091. connected: 0,
  1092. completed: 0,
  1093. disconnected: 0,
  1094. failed: 0
  1095. };
  1096. this.transceivers.forEach(function(transceiver) {
  1097. states[transceiver.iceTransport.state]++;
  1098. states[transceiver.dtlsTransport.state]++;
  1099. });
  1100. // ICETransport.completed and connected are the same for this purpose.
  1101. states.connected += states.completed;
  1102. newState = 'new';
  1103. if (states.failed > 0) {
  1104. newState = 'failed';
  1105. } else if (states.connecting > 0 || states.checking > 0) {
  1106. newState = 'connecting';
  1107. } else if (states.disconnected > 0) {
  1108. newState = 'disconnected';
  1109. } else if (states.new > 0) {
  1110. newState = 'new';
  1111. } else if (states.connected > 0 || states.completed > 0) {
  1112. newState = 'connected';
  1113. }
  1114. if (newState !== this.iceConnectionState) {
  1115. this.iceConnectionState = newState;
  1116. var event = new Event('iceconnectionstatechange');
  1117. this._dispatchEvent('iceconnectionstatechange', event);
  1118. }
  1119. };
  1120. RTCPeerConnection.prototype.createOffer = function() {
  1121. var pc = this;
  1122. if (this._isClosed) {
  1123. return Promise.reject(makeError('InvalidStateError',
  1124. 'Can not call createOffer after close'));
  1125. }
  1126. var numAudioTracks = this.transceivers.filter(function(t) {
  1127. return t.kind === 'audio';
  1128. }).length;
  1129. var numVideoTracks = this.transceivers.filter(function(t) {
  1130. return t.kind === 'video';
  1131. }).length;
  1132. // Determine number of audio and video tracks we need to send/recv.
  1133. var offerOptions = arguments[0];
  1134. if (offerOptions) {
  1135. // Reject Chrome legacy constraints.
  1136. if (offerOptions.mandatory || offerOptions.optional) {
  1137. throw new TypeError(
  1138. 'Legacy mandatory/optional constraints not supported.');
  1139. }
  1140. if (offerOptions.offerToReceiveAudio !== undefined) {
  1141. if (offerOptions.offerToReceiveAudio === true) {
  1142. numAudioTracks = 1;
  1143. } else if (offerOptions.offerToReceiveAudio === false) {
  1144. numAudioTracks = 0;
  1145. } else {
  1146. numAudioTracks = offerOptions.offerToReceiveAudio;
  1147. }
  1148. }
  1149. if (offerOptions.offerToReceiveVideo !== undefined) {
  1150. if (offerOptions.offerToReceiveVideo === true) {
  1151. numVideoTracks = 1;
  1152. } else if (offerOptions.offerToReceiveVideo === false) {
  1153. numVideoTracks = 0;
  1154. } else {
  1155. numVideoTracks = offerOptions.offerToReceiveVideo;
  1156. }
  1157. }
  1158. }
  1159. this.transceivers.forEach(function(transceiver) {
  1160. if (transceiver.kind === 'audio') {
  1161. numAudioTracks--;
  1162. if (numAudioTracks < 0) {
  1163. transceiver.wantReceive = false;
  1164. }
  1165. } else if (transceiver.kind === 'video') {
  1166. numVideoTracks--;
  1167. if (numVideoTracks < 0) {
  1168. transceiver.wantReceive = false;
  1169. }
  1170. }
  1171. });
  1172. // Create M-lines for recvonly streams.
  1173. while (numAudioTracks > 0 || numVideoTracks > 0) {
  1174. if (numAudioTracks > 0) {
  1175. this._createTransceiver('audio');
  1176. numAudioTracks--;
  1177. }
  1178. if (numVideoTracks > 0) {
  1179. this._createTransceiver('video');
  1180. numVideoTracks--;
  1181. }
  1182. }
  1183. var sdp = SDPUtils.writeSessionBoilerplate(this._sdpSessionId,
  1184. this._sdpSessionVersion++);
  1185. this.transceivers.forEach(function(transceiver, sdpMLineIndex) {
  1186. // For each track, create an ice gatherer, ice transport,
  1187. // dtls transport, potentially rtpsender and rtpreceiver.
  1188. var track = transceiver.track;
  1189. var kind = transceiver.kind;
  1190. var mid = SDPUtils.generateIdentifier();
  1191. transceiver.mid = mid;
  1192. if (!transceiver.iceGatherer) {
  1193. transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex,
  1194. pc.usingBundle);
  1195. }
  1196. var localCapabilities = window.RTCRtpSender.getCapabilities(kind);
  1197. // filter RTX until additional stuff needed for RTX is implemented
  1198. // in adapter.js
  1199. if (edgeVersion < 15019) {
  1200. localCapabilities.codecs = localCapabilities.codecs.filter(
  1201. function(codec) {
  1202. return codec.name !== 'rtx';
  1203. });
  1204. }
  1205. localCapabilities.codecs.forEach(function(codec) {
  1206. // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552
  1207. // by adding level-asymmetry-allowed=1
  1208. if (codec.name === 'H264' &&
  1209. codec.parameters['level-asymmetry-allowed'] === undefined) {
  1210. codec.parameters['level-asymmetry-allowed'] = '1';
  1211. }
  1212. });
  1213. // generate an ssrc now, to be used later in rtpSender.send
  1214. var sendEncodingParameters = transceiver.sendEncodingParameters || [{
  1215. ssrc: (2 * sdpMLineIndex + 1) * 1001
  1216. }];
  1217. if (track) {
  1218. // add RTX
  1219. if (edgeVersion >= 15019 && kind === 'video' &&
  1220. !sendEncodingParameters[0].rtx) {
  1221. sendEncodingParameters[0].rtx = {
  1222. ssrc: sendEncodingParameters[0].ssrc + 1
  1223. };
  1224. }
  1225. }
  1226. if (transceiver.wantReceive) {
  1227. transceiver.rtpReceiver = new window.RTCRtpReceiver(
  1228. transceiver.dtlsTransport, kind);
  1229. }
  1230. transceiver.localCapabilities = localCapabilities;
  1231. transceiver.sendEncodingParameters = sendEncodingParameters;
  1232. });
  1233. // always offer BUNDLE and dispose on return if not supported.
  1234. if (this._config.bundlePolicy !== 'max-compat') {
  1235. sdp += 'a=group:BUNDLE ' + this.transceivers.map(function(t) {
  1236. return t.mid;
  1237. }).join(' ') + '\r\n';
  1238. }
  1239. sdp += 'a=ice-options:trickle\r\n';
  1240. this.transceivers.forEach(function(transceiver, sdpMLineIndex) {
  1241. sdp += writeMediaSection(transceiver, transceiver.localCapabilities,
  1242. 'offer', transceiver.stream, pc._dtlsRole);
  1243. sdp += 'a=rtcp-rsize\r\n';
  1244. if (transceiver.iceGatherer && pc.iceGatheringState !== 'new' &&
  1245. (sdpMLineIndex === 0 || !pc.usingBundle)) {
  1246. transceiver.iceGatherer.getLocalCandidates().forEach(function(cand) {
  1247. cand.component = 1;
  1248. sdp += 'a=' + SDPUtils.writeCandidate(cand) + '\r\n';
  1249. });
  1250. if (transceiver.iceGatherer.state === 'completed') {
  1251. sdp += 'a=end-of-candidates\r\n';
  1252. }
  1253. }
  1254. });
  1255. var desc = new window.RTCSessionDescription({
  1256. type: 'offer',
  1257. sdp: sdp
  1258. });
  1259. return Promise.resolve(desc);
  1260. };
  1261. RTCPeerConnection.prototype.createAnswer = function() {
  1262. var pc = this;
  1263. if (this._isClosed) {
  1264. return Promise.reject(makeError('InvalidStateError',
  1265. 'Can not call createAnswer after close'));
  1266. }
  1267. var sdp = SDPUtils.writeSessionBoilerplate(this._sdpSessionId,
  1268. this._sdpSessionVersion++);
  1269. if (this.usingBundle) {
  1270. sdp += 'a=group:BUNDLE ' + this.transceivers.map(function(t) {
  1271. return t.mid;
  1272. }).join(' ') + '\r\n';
  1273. }
  1274. var mediaSectionsInOffer = SDPUtils.splitSections(
  1275. this.remoteDescription.sdp).length - 1;
  1276. this.transceivers.forEach(function(transceiver, sdpMLineIndex) {
  1277. if (sdpMLineIndex + 1 > mediaSectionsInOffer) {
  1278. return;
  1279. }
  1280. if (transceiver.isDatachannel) {
  1281. sdp += 'm=application 0 DTLS/SCTP 5000\r\n' +
  1282. 'c=IN IP4 0.0.0.0\r\n' +
  1283. 'a=mid:' + transceiver.mid + '\r\n';
  1284. return;
  1285. }
  1286. // FIXME: look at direction.
  1287. if (transceiver.stream) {
  1288. var localTrack;
  1289. if (transceiver.kind === 'audio') {
  1290. localTrack = transceiver.stream.getAudioTracks()[0];
  1291. } else if (transceiver.kind === 'video') {
  1292. localTrack = transceiver.stream.getVideoTracks()[0];
  1293. }
  1294. if (localTrack) {
  1295. // add RTX
  1296. if (edgeVersion >= 15019 && transceiver.kind === 'video' &&
  1297. !transceiver.sendEncodingParameters[0].rtx) {
  1298. transceiver.sendEncodingParameters[0].rtx = {
  1299. ssrc: transceiver.sendEncodingParameters[0].ssrc + 1
  1300. };
  1301. }
  1302. }
  1303. }
  1304. // Calculate intersection of capabilities.
  1305. var commonCapabilities = getCommonCapabilities(
  1306. transceiver.localCapabilities,
  1307. transceiver.remoteCapabilities);
  1308. var hasRtx = commonCapabilities.codecs.filter(function(c) {
  1309. return c.name.toLowerCase() === 'rtx';
  1310. }).length;
  1311. if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {
  1312. delete transceiver.sendEncodingParameters[0].rtx;
  1313. }
  1314. sdp += writeMediaSection(transceiver, commonCapabilities,
  1315. 'answer', transceiver.stream, pc._dtlsRole);
  1316. if (transceiver.rtcpParameters &&
  1317. transceiver.rtcpParameters.reducedSize) {
  1318. sdp += 'a=rtcp-rsize\r\n';
  1319. }
  1320. });
  1321. var desc = new window.RTCSessionDescription({
  1322. type: 'answer',
  1323. sdp: sdp
  1324. });
  1325. return Promise.resolve(desc);
  1326. };
  1327. RTCPeerConnection.prototype.addIceCandidate = function(candidate) {
  1328. var sections;
  1329. if (!candidate || candidate.candidate === '') {
  1330. for (var j = 0; j < this.transceivers.length; j++) {
  1331. if (this.transceivers[j].isDatachannel) {
  1332. continue;
  1333. }
  1334. this.transceivers[j].iceTransport.addRemoteCandidate({});
  1335. sections = SDPUtils.splitSections(this.remoteDescription.sdp);
  1336. sections[j + 1] += 'a=end-of-candidates\r\n';
  1337. this.remoteDescription.sdp = sections.join('');
  1338. if (this.usingBundle) {
  1339. break;
  1340. }
  1341. }
  1342. } else if (!(candidate.sdpMLineIndex !== undefined || candidate.sdpMid)) {
  1343. throw new TypeError('sdpMLineIndex or sdpMid required');
  1344. } else if (!this.remoteDescription) {
  1345. return Promise.reject(makeError('InvalidStateError',
  1346. 'Can not add ICE candidate without a remote description'));
  1347. } else {
  1348. var sdpMLineIndex = candidate.sdpMLineIndex;
  1349. if (candidate.sdpMid) {
  1350. for (var i = 0; i < this.transceivers.length; i++) {
  1351. if (this.transceivers[i].mid === candidate.sdpMid) {
  1352. sdpMLineIndex = i;
  1353. break;
  1354. }
  1355. }
  1356. }
  1357. var transceiver = this.transceivers[sdpMLineIndex];
  1358. if (transceiver) {
  1359. if (transceiver.isDatachannel) {
  1360. return Promise.resolve();
  1361. }
  1362. var cand = Object.keys(candidate.candidate).length > 0 ?
  1363. SDPUtils.parseCandidate(candidate.candidate) : {};
  1364. // Ignore Chrome's invalid candidates since Edge does not like them.
  1365. if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {
  1366. return Promise.resolve();
  1367. }
  1368. // Ignore RTCP candidates, we assume RTCP-MUX.
  1369. if (cand.component && cand.component !== 1) {
  1370. return Promise.resolve();
  1371. }
  1372. // when using bundle, avoid adding candidates to the wrong
  1373. // ice transport. And avoid adding candidates added in the SDP.
  1374. if (sdpMLineIndex === 0 || (sdpMLineIndex > 0 &&
  1375. transceiver.iceTransport !== this.transceivers[0].iceTransport)) {
  1376. if (!maybeAddCandidate(transceiver.iceTransport, cand)) {
  1377. return Promise.reject(makeError('OperationError',
  1378. 'Can not add ICE candidate'));
  1379. }
  1380. }
  1381. // update the remoteDescription.
  1382. var candidateString = candidate.candidate.trim();
  1383. if (candidateString.indexOf('a=') === 0) {
  1384. candidateString = candidateString.substr(2);
  1385. }
  1386. sections = SDPUtils.splitSections(this.remoteDescription.sdp);
  1387. sections[sdpMLineIndex + 1] += 'a=' +
  1388. (cand.type ? candidateString : 'end-of-candidates')
  1389. + '\r\n';
  1390. this.remoteDescription.sdp = sections.join('');
  1391. } else {
  1392. return Promise.reject(makeError('OperationError',
  1393. 'Can not add ICE candidate'));
  1394. }
  1395. }
  1396. return Promise.resolve();
  1397. };
  1398. RTCPeerConnection.prototype.getStats = function() {
  1399. var promises = [];
  1400. this.transceivers.forEach(function(transceiver) {
  1401. ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',
  1402. 'dtlsTransport'].forEach(function(method) {
  1403. if (transceiver[method]) {
  1404. promises.push(transceiver[method].getStats());
  1405. }
  1406. });
  1407. });
  1408. var fixStatsType = function(stat) {
  1409. return {
  1410. inboundrtp: 'inbound-rtp',
  1411. outboundrtp: 'outbound-rtp',
  1412. candidatepair: 'candidate-pair',
  1413. localcandidate: 'local-candidate',
  1414. remotecandidate: 'remote-candidate'
  1415. }[stat.type] || stat.type;
  1416. };
  1417. return new Promise(function(resolve) {
  1418. // shim getStats with maplike support
  1419. var results = new Map();
  1420. Promise.all(promises).then(function(res) {
  1421. res.forEach(function(result) {
  1422. Object.keys(result).forEach(function(id) {
  1423. result[id].type = fixStatsType(result[id]);
  1424. results.set(id, result[id]);
  1425. });
  1426. });
  1427. resolve(results);
  1428. });
  1429. });
  1430. };
  1431. // legacy callback shims. Should be moved to adapter.js some days.
  1432. var methods = ['createOffer', 'createAnswer'];
  1433. methods.forEach(function(method) {
  1434. var nativeMethod = RTCPeerConnection.prototype[method];
  1435. RTCPeerConnection.prototype[method] = function() {
  1436. var args = arguments;
  1437. if (typeof args[0] === 'function' ||
  1438. typeof args[1] === 'function') { // legacy
  1439. return nativeMethod.apply(this, [arguments[2]])
  1440. .then(function(description) {
  1441. if (typeof args[0] === 'function') {
  1442. args[0].apply(null, [description]);
  1443. }
  1444. }, function(error) {
  1445. if (typeof args[1] === 'function') {
  1446. args[1].apply(null, [error]);
  1447. }
  1448. });
  1449. }
  1450. return nativeMethod.apply(this, arguments);
  1451. };
  1452. });
  1453. methods = ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'];
  1454. methods.forEach(function(method) {
  1455. var nativeMethod = RTCPeerConnection.prototype[method];
  1456. RTCPeerConnection.prototype[method] = function() {
  1457. var args = arguments;
  1458. if (typeof args[1] === 'function' ||
  1459. typeof args[2] === 'function') { // legacy
  1460. return nativeMethod.apply(this, arguments)
  1461. .then(function() {
  1462. if (typeof args[1] === 'function') {
  1463. args[1].apply(null);
  1464. }
  1465. }, function(error) {
  1466. if (typeof args[2] === 'function') {
  1467. args[2].apply(null, [error]);
  1468. }
  1469. });
  1470. }
  1471. return nativeMethod.apply(this, arguments);
  1472. };
  1473. });
  1474. // getStats is special. It doesn't have a spec legacy method yet we support
  1475. // getStats(something, cb) without error callbacks.
  1476. ['getStats'].forEach(function(method) {
  1477. var nativeMethod = RTCPeerConnection.prototype[method];
  1478. RTCPeerConnection.prototype[method] = function() {
  1479. var args = arguments;
  1480. if (typeof args[1] === 'function') {
  1481. return nativeMethod.apply(this, arguments)
  1482. .then(function() {
  1483. if (typeof args[1] === 'function') {
  1484. args[1].apply(null);
  1485. }
  1486. });
  1487. }
  1488. return nativeMethod.apply(this, arguments);
  1489. };
  1490. });
  1491. return RTCPeerConnection;
  1492. };
  1493. },{"sdp":2}],2:[function(require,module,exports){
  1494. /* eslint-env node */
  1495. 'use strict';
  1496. // SDP helpers.
  1497. var SDPUtils = {};
  1498. // Generate an alphanumeric identifier for cname or mids.
  1499. // TODO: use UUIDs instead? https://gist.github.com/jed/982883
  1500. SDPUtils.generateIdentifier = function() {
  1501. return Math.random().toString(36).substr(2, 10);
  1502. };
  1503. // The RTCP CNAME used by all peerconnections from the same JS.
  1504. SDPUtils.localCName = SDPUtils.generateIdentifier();
  1505. // Splits SDP into lines, dealing with both CRLF and LF.
  1506. SDPUtils.splitLines = function(blob) {
  1507. return blob.trim().split('\n').map(function(line) {
  1508. return line.trim();
  1509. });
  1510. };
  1511. // Splits SDP into sessionpart and mediasections. Ensures CRLF.
  1512. SDPUtils.splitSections = function(blob) {
  1513. var parts = blob.split('\nm=');
  1514. return parts.map(function(part, index) {
  1515. return (index > 0 ? 'm=' + part : part).trim() + '\r\n';
  1516. });
  1517. };
  1518. // Returns lines that start with a certain prefix.
  1519. SDPUtils.matchPrefix = function(blob, prefix) {
  1520. return SDPUtils.splitLines(blob).filter(function(line) {
  1521. return line.indexOf(prefix) === 0;
  1522. });
  1523. };
  1524. // Parses an ICE candidate line. Sample input:
  1525. // candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8
  1526. // rport 55996"
  1527. SDPUtils.parseCandidate = function(line) {
  1528. var parts;
  1529. // Parse both variants.
  1530. if (line.indexOf('a=candidate:') === 0) {
  1531. parts = line.substring(12).split(' ');
  1532. } else {
  1533. parts = line.substring(10).split(' ');
  1534. }
  1535. var candidate = {
  1536. foundation: parts[0],
  1537. component: parseInt(parts[1], 10),
  1538. protocol: parts[2].toLowerCase(),
  1539. priority: parseInt(parts[3], 10),
  1540. ip: parts[4],
  1541. port: parseInt(parts[5], 10),
  1542. // skip parts[6] == 'typ'
  1543. type: parts[7]
  1544. };
  1545. for (var i = 8; i < parts.length; i += 2) {
  1546. switch (parts[i]) {
  1547. case 'raddr':
  1548. candidate.relatedAddress = parts[i + 1];
  1549. break;
  1550. case 'rport':
  1551. candidate.relatedPort = parseInt(parts[i + 1], 10);
  1552. break;
  1553. case 'tcptype':
  1554. candidate.tcpType = parts[i + 1];
  1555. break;
  1556. case 'ufrag':
  1557. candidate.ufrag = parts[i + 1]; // for backward compability.
  1558. candidate.usernameFragment = parts[i + 1];
  1559. break;
  1560. default: // extension handling, in particular ufrag
  1561. candidate[parts[i]] = parts[i + 1];
  1562. break;
  1563. }
  1564. }
  1565. return candidate;
  1566. };
  1567. // Translates a candidate object into SDP candidate attribute.
  1568. SDPUtils.writeCandidate = function(candidate) {
  1569. var sdp = [];
  1570. sdp.push(candidate.foundation);
  1571. sdp.push(candidate.component);
  1572. sdp.push(candidate.protocol.toUpperCase());
  1573. sdp.push(candidate.priority);
  1574. sdp.push(candidate.ip);
  1575. sdp.push(candidate.port);
  1576. var type = candidate.type;
  1577. sdp.push('typ');
  1578. sdp.push(type);
  1579. if (type !== 'host' && candidate.relatedAddress &&
  1580. candidate.relatedPort) {
  1581. sdp.push('raddr');
  1582. sdp.push(candidate.relatedAddress); // was: relAddr
  1583. sdp.push('rport');
  1584. sdp.push(candidate.relatedPort); // was: relPort
  1585. }
  1586. if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
  1587. sdp.push('tcptype');
  1588. sdp.push(candidate.tcpType);
  1589. }
  1590. if (candidate.ufrag) {
  1591. sdp.push('ufrag');
  1592. sdp.push(candidate.ufrag);
  1593. }
  1594. return 'candidate:' + sdp.join(' ');
  1595. };
  1596. // Parses an ice-options line, returns an array of option tags.
  1597. // a=ice-options:foo bar
  1598. SDPUtils.parseIceOptions = function(line) {
  1599. return line.substr(14).split(' ');
  1600. }
  1601. // Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
  1602. // a=rtpmap:111 opus/48000/2
  1603. SDPUtils.parseRtpMap = function(line) {
  1604. var parts = line.substr(9).split(' ');
  1605. var parsed = {
  1606. payloadType: parseInt(parts.shift(), 10) // was: id
  1607. };
  1608. parts = parts[0].split('/');
  1609. parsed.name = parts[0];
  1610. parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
  1611. // was: channels
  1612. parsed.numChannels = parts.length === 3 ? parseInt(parts[2], 10) : 1;
  1613. return parsed;
  1614. };
  1615. // Generate an a=rtpmap line from RTCRtpCodecCapability or
  1616. // RTCRtpCodecParameters.
  1617. SDPUtils.writeRtpMap = function(codec) {
  1618. var pt = codec.payloadType;
  1619. if (codec.preferredPayloadType !== undefined) {
  1620. pt = codec.preferredPayloadType;
  1621. }
  1622. return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +
  1623. (codec.numChannels !== 1 ? '/' + codec.numChannels : '') + '\r\n';
  1624. };
  1625. // Parses an a=extmap line (headerextension from RFC 5285). Sample input:
  1626. // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
  1627. // a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset
  1628. SDPUtils.parseExtmap = function(line) {
  1629. var parts = line.substr(9).split(' ');
  1630. return {
  1631. id: parseInt(parts[0], 10),
  1632. direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',
  1633. uri: parts[1]
  1634. };
  1635. };
  1636. // Generates a=extmap line from RTCRtpHeaderExtensionParameters or
  1637. // RTCRtpHeaderExtension.
  1638. SDPUtils.writeExtmap = function(headerExtension) {
  1639. return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +
  1640. (headerExtension.direction && headerExtension.direction !== 'sendrecv'
  1641. ? '/' + headerExtension.direction
  1642. : '') +
  1643. ' ' + headerExtension.uri + '\r\n';
  1644. };
  1645. // Parses an ftmp line, returns dictionary. Sample input:
  1646. // a=fmtp:96 vbr=on;cng=on
  1647. // Also deals with vbr=on; cng=on
  1648. SDPUtils.parseFmtp = function(line) {
  1649. var parsed = {};
  1650. var kv;
  1651. var parts = line.substr(line.indexOf(' ') + 1).split(';');
  1652. for (var j = 0; j < parts.length; j++) {
  1653. kv = parts[j].trim().split('=');
  1654. parsed[kv[0].trim()] = kv[1];
  1655. }
  1656. return parsed;
  1657. };
  1658. // Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
  1659. SDPUtils.writeFmtp = function(codec) {
  1660. var line = '';
  1661. var pt = codec.payloadType;
  1662. if (codec.preferredPayloadType !== undefined) {
  1663. pt = codec.preferredPayloadType;
  1664. }
  1665. if (codec.parameters && Object.keys(codec.parameters).length) {
  1666. var params = [];
  1667. Object.keys(codec.parameters).forEach(function(param) {
  1668. params.push(param + '=' + codec.parameters[param]);
  1669. });
  1670. line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
  1671. }
  1672. return line;
  1673. };
  1674. // Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
  1675. // a=rtcp-fb:98 nack rpsi
  1676. SDPUtils.parseRtcpFb = function(line) {
  1677. var parts = line.substr(line.indexOf(' ') + 1).split(' ');
  1678. return {
  1679. type: parts.shift(),
  1680. parameter: parts.join(' ')
  1681. };
  1682. };
  1683. // Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
  1684. SDPUtils.writeRtcpFb = function(codec) {
  1685. var lines = '';
  1686. var pt = codec.payloadType;
  1687. if (codec.preferredPayloadType !== undefined) {
  1688. pt = codec.preferredPayloadType;
  1689. }
  1690. if (codec.rtcpFeedback && codec.rtcpFeedback.length) {
  1691. // FIXME: special handling for trr-int?
  1692. codec.rtcpFeedback.forEach(function(fb) {
  1693. lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +
  1694. (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +
  1695. '\r\n';
  1696. });
  1697. }
  1698. return lines;
  1699. };
  1700. // Parses an RFC 5576 ssrc media attribute. Sample input:
  1701. // a=ssrc:3735928559 cname:something
  1702. SDPUtils.parseSsrcMedia = function(line) {
  1703. var sp = line.indexOf(' ');
  1704. var parts = {
  1705. ssrc: parseInt(line.substr(7, sp - 7), 10)
  1706. };
  1707. var colon = line.indexOf(':', sp);
  1708. if (colon > -1) {
  1709. parts.attribute = line.substr(sp + 1, colon - sp - 1);
  1710. parts.value = line.substr(colon + 1);
  1711. } else {
  1712. parts.attribute = line.substr(sp + 1);
  1713. }
  1714. return parts;
  1715. };
  1716. // Extracts the MID (RFC 5888) from a media section.
  1717. // returns the MID or undefined if no mid line was found.
  1718. SDPUtils.getMid = function(mediaSection) {
  1719. var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];
  1720. if (mid) {
  1721. return mid.substr(6);
  1722. }
  1723. }
  1724. SDPUtils.parseFingerprint = function(line) {
  1725. var parts = line.substr(14).split(' ');
  1726. return {
  1727. algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.
  1728. value: parts[1]
  1729. };
  1730. };
  1731. // Extracts DTLS parameters from SDP media section or sessionpart.
  1732. // FIXME: for consistency with other functions this should only
  1733. // get the fingerprint line as input. See also getIceParameters.
  1734. SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {
  1735. var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,
  1736. 'a=fingerprint:');
  1737. // Note: a=setup line is ignored since we use the 'auto' role.
  1738. // Note2: 'algorithm' is not case sensitive except in Edge.
  1739. return {
  1740. role: 'auto',
  1741. fingerprints: lines.map(SDPUtils.parseFingerprint)
  1742. };
  1743. };
  1744. // Serializes DTLS parameters to SDP.
  1745. SDPUtils.writeDtlsParameters = function(params, setupType) {
  1746. var sdp = 'a=setup:' + setupType + '\r\n';
  1747. params.fingerprints.forEach(function(fp) {
  1748. sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
  1749. });
  1750. return sdp;
  1751. };
  1752. // Parses ICE information from SDP media section or sessionpart.
  1753. // FIXME: for consistency with other functions this should only
  1754. // get the ice-ufrag and ice-pwd lines as input.
  1755. SDPUtils.getIceParameters = function(mediaSection, sessionpart) {
  1756. var lines = SDPUtils.splitLines(mediaSection);
  1757. // Search in session part, too.
  1758. lines = lines.concat(SDPUtils.splitLines(sessionpart));
  1759. var iceParameters = {
  1760. usernameFragment: lines.filter(function(line) {
  1761. return line.indexOf('a=ice-ufrag:') === 0;
  1762. })[0].substr(12),
  1763. password: lines.filter(function(line) {
  1764. return line.indexOf('a=ice-pwd:') === 0;
  1765. })[0].substr(10)
  1766. };
  1767. return iceParameters;
  1768. };
  1769. // Serializes ICE parameters to SDP.
  1770. SDPUtils.writeIceParameters = function(params) {
  1771. return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' +
  1772. 'a=ice-pwd:' + params.password + '\r\n';
  1773. };
  1774. // Parses the SDP media section and returns RTCRtpParameters.
  1775. SDPUtils.parseRtpParameters = function(mediaSection) {
  1776. var description = {
  1777. codecs: [],
  1778. headerExtensions: [],
  1779. fecMechanisms: [],
  1780. rtcp: []
  1781. };
  1782. var lines = SDPUtils.splitLines(mediaSection);
  1783. var mline = lines[0].split(' ');
  1784. for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]
  1785. var pt = mline[i];
  1786. var rtpmapline = SDPUtils.matchPrefix(
  1787. mediaSection, 'a=rtpmap:' + pt + ' ')[0];
  1788. if (rtpmapline) {
  1789. var codec = SDPUtils.parseRtpMap(rtpmapline);
  1790. var fmtps = SDPUtils.matchPrefix(
  1791. mediaSection, 'a=fmtp:' + pt + ' ');
  1792. // Only the first a=fmtp:<pt> is considered.
  1793. codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
  1794. codec.rtcpFeedback = SDPUtils.matchPrefix(
  1795. mediaSection, 'a=rtcp-fb:' + pt + ' ')
  1796. .map(SDPUtils.parseRtcpFb);
  1797. description.codecs.push(codec);
  1798. // parse FEC mechanisms from rtpmap lines.
  1799. switch (codec.name.toUpperCase()) {
  1800. case 'RED':
  1801. case 'ULPFEC':
  1802. description.fecMechanisms.push(codec.name.toUpperCase());
  1803. break;
  1804. default: // only RED and ULPFEC are recognized as FEC mechanisms.
  1805. break;
  1806. }
  1807. }
  1808. }
  1809. SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) {
  1810. description.headerExtensions.push(SDPUtils.parseExtmap(line));
  1811. });
  1812. // FIXME: parse rtcp.
  1813. return description;
  1814. };
  1815. // Generates parts of the SDP media section describing the capabilities /
  1816. // parameters.
  1817. SDPUtils.writeRtpDescription = function(kind, caps) {
  1818. var sdp = '';
  1819. // Build the mline.
  1820. sdp += 'm=' + kind + ' ';
  1821. sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
  1822. sdp += ' UDP/TLS/RTP/SAVPF ';
  1823. sdp += caps.codecs.map(function(codec) {
  1824. if (codec.preferredPayloadType !== undefined) {
  1825. return codec.preferredPayloadType;
  1826. }
  1827. return codec.payloadType;
  1828. }).join(' ') + '\r\n';
  1829. sdp += 'c=IN IP4 0.0.0.0\r\n';
  1830. sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';
  1831. // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
  1832. caps.codecs.forEach(function(codec) {
  1833. sdp += SDPUtils.writeRtpMap(codec);
  1834. sdp += SDPUtils.writeFmtp(codec);
  1835. sdp += SDPUtils.writeRtcpFb(codec);
  1836. });
  1837. var maxptime = 0;
  1838. caps.codecs.forEach(function(codec) {
  1839. if (codec.maxptime > maxptime) {
  1840. maxptime = codec.maxptime;
  1841. }
  1842. });
  1843. if (maxptime > 0) {
  1844. sdp += 'a=maxptime:' + maxptime + '\r\n';
  1845. }
  1846. sdp += 'a=rtcp-mux\r\n';
  1847. caps.headerExtensions.forEach(function(extension) {
  1848. sdp += SDPUtils.writeExtmap(extension);
  1849. });
  1850. // FIXME: write fecMechanisms.
  1851. return sdp;
  1852. };
  1853. // Parses the SDP media section and returns an array of
  1854. // RTCRtpEncodingParameters.
  1855. SDPUtils.parseRtpEncodingParameters = function(mediaSection) {
  1856. var encodingParameters = [];
  1857. var description = SDPUtils.parseRtpParameters(mediaSection);
  1858. var hasRed = description.fecMechanisms.indexOf('RED') !== -1;
  1859. var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;
  1860. // filter a=ssrc:... cname:, ignore PlanB-msid
  1861. var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
  1862. .map(function(line) {
  1863. return SDPUtils.parseSsrcMedia(line);
  1864. })
  1865. .filter(function(parts) {
  1866. return parts.attribute === 'cname';
  1867. });
  1868. var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;
  1869. var secondarySsrc;
  1870. var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')
  1871. .map(function(line) {
  1872. var parts = line.split(' ');
  1873. parts.shift();
  1874. return parts.map(function(part) {
  1875. return parseInt(part, 10);
  1876. });
  1877. });
  1878. if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {
  1879. secondarySsrc = flows[0][1];
  1880. }
  1881. description.codecs.forEach(function(codec) {
  1882. if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {
  1883. var encParam = {
  1884. ssrc: primarySsrc,
  1885. codecPayloadType: parseInt(codec.parameters.apt, 10),
  1886. rtx: {
  1887. ssrc: secondarySsrc
  1888. }
  1889. };
  1890. encodingParameters.push(encParam);
  1891. if (hasRed) {
  1892. encParam = JSON.parse(JSON.stringify(encParam));
  1893. encParam.fec = {
  1894. ssrc: secondarySsrc,
  1895. mechanism: hasUlpfec ? 'red+ulpfec' : 'red'
  1896. };
  1897. encodingParameters.push(encParam);
  1898. }
  1899. }
  1900. });
  1901. if (encodingParameters.length === 0 && primarySsrc) {
  1902. encodingParameters.push({
  1903. ssrc: primarySsrc
  1904. });
  1905. }
  1906. // we support both b=AS and b=TIAS but interpret AS as TIAS.
  1907. var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');
  1908. if (bandwidth.length) {
  1909. if (bandwidth[0].indexOf('b=TIAS:') === 0) {
  1910. bandwidth = parseInt(bandwidth[0].substr(7), 10);
  1911. } else if (bandwidth[0].indexOf('b=AS:') === 0) {
  1912. // use formula from JSEP to convert b=AS to TIAS value.
  1913. bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95
  1914. - (50 * 40 * 8);
  1915. } else {
  1916. bandwidth = undefined;
  1917. }
  1918. encodingParameters.forEach(function(params) {
  1919. params.maxBitrate = bandwidth;
  1920. });
  1921. }
  1922. return encodingParameters;
  1923. };
  1924. // parses http://draft.ortc.org/#rtcrtcpparameters*
  1925. SDPUtils.parseRtcpParameters = function(mediaSection) {
  1926. var rtcpParameters = {};
  1927. var cname;
  1928. // Gets the first SSRC. Note that with RTX there might be multiple
  1929. // SSRCs.
  1930. var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
  1931. .map(function(line) {
  1932. return SDPUtils.parseSsrcMedia(line);
  1933. })
  1934. .filter(function(obj) {
  1935. return obj.attribute === 'cname';
  1936. })[0];
  1937. if (remoteSsrc) {
  1938. rtcpParameters.cname = remoteSsrc.value;
  1939. rtcpParameters.ssrc = remoteSsrc.ssrc;
  1940. }
  1941. // Edge uses the compound attribute instead of reducedSize
  1942. // compound is !reducedSize
  1943. var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');
  1944. rtcpParameters.reducedSize = rsize.length > 0;
  1945. rtcpParameters.compound = rsize.length === 0;
  1946. // parses the rtcp-mux attrіbute.
  1947. // Note that Edge does not support unmuxed RTCP.
  1948. var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');
  1949. rtcpParameters.mux = mux.length > 0;
  1950. return rtcpParameters;
  1951. };
  1952. // parses either a=msid: or a=ssrc:... msid lines and returns
  1953. // the id of the MediaStream and MediaStreamTrack.
  1954. SDPUtils.parseMsid = function(mediaSection) {
  1955. var parts;
  1956. var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');
  1957. if (spec.length === 1) {
  1958. parts = spec[0].substr(7).split(' ');
  1959. return {stream: parts[0], track: parts[1]};
  1960. }
  1961. var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
  1962. .map(function(line) {
  1963. return SDPUtils.parseSsrcMedia(line);
  1964. })
  1965. .filter(function(parts) {
  1966. return parts.attribute === 'msid';
  1967. });
  1968. if (planB.length > 0) {
  1969. parts = planB[0].value.split(' ');
  1970. return {stream: parts[0], track: parts[1]};
  1971. }
  1972. };
  1973. // Generate a session ID for SDP.
  1974. // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1
  1975. // recommends using a cryptographically random +ve 64-bit value
  1976. // but right now this should be acceptable and within the right range
  1977. SDPUtils.generateSessionId = function() {
  1978. return Math.random().toString().substr(2, 21);
  1979. };
  1980. // Write boilder plate for start of SDP
  1981. // sessId argument is optional - if not supplied it will
  1982. // be generated randomly
  1983. // sessVersion is optional and defaults to 2
  1984. SDPUtils.writeSessionBoilerplate = function(sessId, sessVer) {
  1985. var sessionId;
  1986. var version = sessVer !== undefined ? sessVer : 2;
  1987. if (sessId) {
  1988. sessionId = sessId;
  1989. } else {
  1990. sessionId = SDPUtils.generateSessionId();
  1991. }
  1992. // FIXME: sess-id should be an NTP timestamp.
  1993. return 'v=0\r\n' +
  1994. 'o=thisisadapterortc ' + sessionId + ' ' + version + ' IN IP4 127.0.0.1\r\n' +
  1995. 's=-\r\n' +
  1996. 't=0 0\r\n';
  1997. };
  1998. SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {
  1999. var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);
  2000. // Map ICE parameters (ufrag, pwd) to SDP.
  2001. sdp += SDPUtils.writeIceParameters(
  2002. transceiver.iceGatherer.getLocalParameters());
  2003. // Map DTLS parameters to SDP.
  2004. sdp += SDPUtils.writeDtlsParameters(
  2005. transceiver.dtlsTransport.getLocalParameters(),
  2006. type === 'offer' ? 'actpass' : 'active');
  2007. sdp += 'a=mid:' + transceiver.mid + '\r\n';
  2008. if (transceiver.direction) {
  2009. sdp += 'a=' + transceiver.direction + '\r\n';
  2010. } else if (transceiver.rtpSender && transceiver.rtpReceiver) {
  2011. sdp += 'a=sendrecv\r\n';
  2012. } else if (transceiver.rtpSender) {
  2013. sdp += 'a=sendonly\r\n';
  2014. } else if (transceiver.rtpReceiver) {
  2015. sdp += 'a=recvonly\r\n';
  2016. } else {
  2017. sdp += 'a=inactive\r\n';
  2018. }
  2019. if (transceiver.rtpSender) {
  2020. // spec.
  2021. var msid = 'msid:' + stream.id + ' ' +
  2022. transceiver.rtpSender.track.id + '\r\n';
  2023. sdp += 'a=' + msid;
  2024. // for Chrome.
  2025. sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
  2026. ' ' + msid;
  2027. if (transceiver.sendEncodingParameters[0].rtx) {
  2028. sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
  2029. ' ' + msid;
  2030. sdp += 'a=ssrc-group:FID ' +
  2031. transceiver.sendEncodingParameters[0].ssrc + ' ' +
  2032. transceiver.sendEncodingParameters[0].rtx.ssrc +
  2033. '\r\n';
  2034. }
  2035. }
  2036. // FIXME: this should be written by writeRtpDescription.
  2037. sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
  2038. ' cname:' + SDPUtils.localCName + '\r\n';
  2039. if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {
  2040. sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
  2041. ' cname:' + SDPUtils.localCName + '\r\n';
  2042. }
  2043. return sdp;
  2044. };
  2045. // Gets the direction from the mediaSection or the sessionpart.
  2046. SDPUtils.getDirection = function(mediaSection, sessionpart) {
  2047. // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
  2048. var lines = SDPUtils.splitLines(mediaSection);
  2049. for (var i = 0; i < lines.length; i++) {
  2050. switch (lines[i]) {
  2051. case 'a=sendrecv':
  2052. case 'a=sendonly':
  2053. case 'a=recvonly':
  2054. case 'a=inactive':
  2055. return lines[i].substr(2);
  2056. default:
  2057. // FIXME: What should happen here?
  2058. }
  2059. }
  2060. if (sessionpart) {
  2061. return SDPUtils.getDirection(sessionpart);
  2062. }
  2063. return 'sendrecv';
  2064. };
  2065. SDPUtils.getKind = function(mediaSection) {
  2066. var lines = SDPUtils.splitLines(mediaSection);
  2067. var mline = lines[0].split(' ');
  2068. return mline[0].substr(2);
  2069. };
  2070. SDPUtils.isRejected = function(mediaSection) {
  2071. return mediaSection.split(' ', 2)[1] === '0';
  2072. };
  2073. SDPUtils.parseMLine = function(mediaSection) {
  2074. var lines = SDPUtils.splitLines(mediaSection);
  2075. var mline = lines[0].split(' ');
  2076. return {
  2077. kind: mline[0].substr(2),
  2078. port: parseInt(mline[1], 10),
  2079. protocol: mline[2],
  2080. fmt: mline.slice(3).join(' ')
  2081. };
  2082. };
  2083. // Expose public methods.
  2084. if (typeof module === 'object') {
  2085. module.exports = SDPUtils;
  2086. }
  2087. },{}],3:[function(require,module,exports){
  2088. (function (global){
  2089. /*
  2090. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  2091. *
  2092. * Use of this source code is governed by a BSD-style license
  2093. * that can be found in the LICENSE file in the root of the source
  2094. * tree.
  2095. */
  2096. /* eslint-env node */
  2097. 'use strict';
  2098. var adapterFactory = require('./adapter_factory.js');
  2099. module.exports = adapterFactory({window: global.window});
  2100. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  2101. },{"./adapter_factory.js":4}],4:[function(require,module,exports){
  2102. /*
  2103. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  2104. *
  2105. * Use of this source code is governed by a BSD-style license
  2106. * that can be found in the LICENSE file in the root of the source
  2107. * tree.
  2108. */
  2109. /* eslint-env node */
  2110. 'use strict';
  2111. var utils = require('./utils');
  2112. // Shimming starts here.
  2113. module.exports = function(dependencies, opts) {
  2114. var window = dependencies && dependencies.window;
  2115. var options = {
  2116. shimChrome: true,
  2117. shimFirefox: true,
  2118. shimEdge: true,
  2119. shimSafari: true,
  2120. };
  2121. for (var key in opts) {
  2122. if (hasOwnProperty.call(opts, key)) {
  2123. options[key] = opts[key];
  2124. }
  2125. }
  2126. // Utils.
  2127. var logging = utils.log;
  2128. var browserDetails = utils.detectBrowser(window);
  2129. // Uncomment the line below if you want logging to occur, including logging
  2130. // for the switch statement below. Can also be turned on in the browser via
  2131. // adapter.disableLog(false), but then logging from the switch statement below
  2132. // will not appear.
  2133. // require('./utils').disableLog(false);
  2134. // Browser shims.
  2135. var chromeShim = require('./chrome/chrome_shim') || null;
  2136. var edgeShim = require('./edge/edge_shim') || null;
  2137. var firefoxShim = require('./firefox/firefox_shim') || null;
  2138. var safariShim = require('./safari/safari_shim') || null;
  2139. var commonShim = require('./common_shim') || null;
  2140. // Export to the adapter global object visible in the browser.
  2141. var adapter = {
  2142. browserDetails: browserDetails,
  2143. commonShim: commonShim,
  2144. extractVersion: utils.extractVersion,
  2145. disableLog: utils.disableLog,
  2146. disableWarnings: utils.disableWarnings
  2147. };
  2148. // Shim browser if found.
  2149. switch (browserDetails.browser) {
  2150. case 'chrome':
  2151. if (!chromeShim || !chromeShim.shimPeerConnection ||
  2152. !options.shimChrome) {
  2153. logging('Chrome shim is not included in this adapter release.');
  2154. return adapter;
  2155. }
  2156. logging('adapter.js shimming chrome.');
  2157. // Export to the adapter global object visible in the browser.
  2158. adapter.browserShim = chromeShim;
  2159. commonShim.shimCreateObjectURL(window);
  2160. chromeShim.shimGetUserMedia(window);
  2161. chromeShim.shimMediaStream(window);
  2162. chromeShim.shimSourceObject(window);
  2163. chromeShim.shimPeerConnection(window);
  2164. chromeShim.shimOnTrack(window);
  2165. chromeShim.shimAddTrackRemoveTrack(window);
  2166. chromeShim.shimGetSendersWithDtmf(window);
  2167. commonShim.shimRTCIceCandidate(window);
  2168. commonShim.shimMaxMessageSize(window);
  2169. commonShim.shimSendThrowTypeError(window);
  2170. break;
  2171. case 'firefox':
  2172. if (!firefoxShim || !firefoxShim.shimPeerConnection ||
  2173. !options.shimFirefox) {
  2174. logging('Firefox shim is not included in this adapter release.');
  2175. return adapter;
  2176. }
  2177. logging('adapter.js shimming firefox.');
  2178. // Export to the adapter global object visible in the browser.
  2179. adapter.browserShim = firefoxShim;
  2180. commonShim.shimCreateObjectURL(window);
  2181. firefoxShim.shimGetUserMedia(window);
  2182. firefoxShim.shimSourceObject(window);
  2183. firefoxShim.shimPeerConnection(window);
  2184. firefoxShim.shimOnTrack(window);
  2185. firefoxShim.shimRemoveStream(window);
  2186. commonShim.shimRTCIceCandidate(window);
  2187. commonShim.shimMaxMessageSize(window);
  2188. commonShim.shimSendThrowTypeError(window);
  2189. break;
  2190. case 'edge':
  2191. if (!edgeShim || !edgeShim.shimPeerConnection || !options.shimEdge) {
  2192. logging('MS edge shim is not included in this adapter release.');
  2193. return adapter;
  2194. }
  2195. logging('adapter.js shimming edge.');
  2196. // Export to the adapter global object visible in the browser.
  2197. adapter.browserShim = edgeShim;
  2198. commonShim.shimCreateObjectURL(window);
  2199. edgeShim.shimGetUserMedia(window);
  2200. edgeShim.shimPeerConnection(window);
  2201. edgeShim.shimReplaceTrack(window);
  2202. // the edge shim implements the full RTCIceCandidate object.
  2203. commonShim.shimMaxMessageSize(window);
  2204. commonShim.shimSendThrowTypeError(window);
  2205. break;
  2206. case 'safari':
  2207. if (!safariShim || !options.shimSafari) {
  2208. logging('Safari shim is not included in this adapter release.');
  2209. return adapter;
  2210. }
  2211. logging('adapter.js shimming safari.');
  2212. // Export to the adapter global object visible in the browser.
  2213. adapter.browserShim = safariShim;
  2214. commonShim.shimCreateObjectURL(window);
  2215. safariShim.shimRTCIceServerUrls(window);
  2216. safariShim.shimCallbacksAPI(window);
  2217. safariShim.shimLocalStreamsAPI(window);
  2218. safariShim.shimRemoteStreamsAPI(window);
  2219. safariShim.shimTrackEventTransceiver(window);
  2220. safariShim.shimGetUserMedia(window);
  2221. safariShim.shimCreateOfferLegacy(window);
  2222. commonShim.shimRTCIceCandidate(window);
  2223. commonShim.shimMaxMessageSize(window);
  2224. commonShim.shimSendThrowTypeError(window);
  2225. break;
  2226. default:
  2227. logging('Unsupported browser!');
  2228. break;
  2229. }
  2230. return adapter;
  2231. };
  2232. },{"./chrome/chrome_shim":5,"./common_shim":7,"./edge/edge_shim":8,"./firefox/firefox_shim":10,"./safari/safari_shim":12,"./utils":13}],5:[function(require,module,exports){
  2233. /*
  2234. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  2235. *
  2236. * Use of this source code is governed by a BSD-style license
  2237. * that can be found in the LICENSE file in the root of the source
  2238. * tree.
  2239. */
  2240. /* eslint-env node */
  2241. 'use strict';
  2242. var utils = require('../utils.js');
  2243. var logging = utils.log;
  2244. module.exports = {
  2245. shimGetUserMedia: require('./getusermedia'),
  2246. shimMediaStream: function(window) {
  2247. window.MediaStream = window.MediaStream || window.webkitMediaStream;
  2248. },
  2249. shimOnTrack: function(window) {
  2250. if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in
  2251. window.RTCPeerConnection.prototype)) {
  2252. Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
  2253. get: function() {
  2254. return this._ontrack;
  2255. },
  2256. set: function(f) {
  2257. if (this._ontrack) {
  2258. this.removeEventListener('track', this._ontrack);
  2259. }
  2260. this.addEventListener('track', this._ontrack = f);
  2261. }
  2262. });
  2263. var origSetRemoteDescription =
  2264. window.RTCPeerConnection.prototype.setRemoteDescription;
  2265. window.RTCPeerConnection.prototype.setRemoteDescription = function() {
  2266. var pc = this;
  2267. if (!pc._ontrackpoly) {
  2268. pc._ontrackpoly = function(e) {
  2269. // onaddstream does not fire when a track is added to an existing
  2270. // stream. But stream.onaddtrack is implemented so we use that.
  2271. e.stream.addEventListener('addtrack', function(te) {
  2272. var receiver;
  2273. if (window.RTCPeerConnection.prototype.getReceivers) {
  2274. receiver = pc.getReceivers().find(function(r) {
  2275. return r.track && r.track.id === te.track.id;
  2276. });
  2277. } else {
  2278. receiver = {track: te.track};
  2279. }
  2280. var event = new Event('track');
  2281. event.track = te.track;
  2282. event.receiver = receiver;
  2283. event.transceiver = {receiver: receiver};
  2284. event.streams = [e.stream];
  2285. pc.dispatchEvent(event);
  2286. });
  2287. e.stream.getTracks().forEach(function(track) {
  2288. var receiver;
  2289. if (window.RTCPeerConnection.prototype.getReceivers) {
  2290. receiver = pc.getReceivers().find(function(r) {
  2291. return r.track && r.track.id === track.id;
  2292. });
  2293. } else {
  2294. receiver = {track: track};
  2295. }
  2296. var event = new Event('track');
  2297. event.track = track;
  2298. event.receiver = receiver;
  2299. event.transceiver = {receiver: receiver};
  2300. event.streams = [e.stream];
  2301. pc.dispatchEvent(event);
  2302. });
  2303. };
  2304. pc.addEventListener('addstream', pc._ontrackpoly);
  2305. }
  2306. return origSetRemoteDescription.apply(pc, arguments);
  2307. };
  2308. } else if (!('RTCRtpTransceiver' in window)) {
  2309. utils.wrapPeerConnectionEvent(window, 'track', function(e) {
  2310. if (!e.transceiver) {
  2311. e.transceiver = {receiver: e.receiver};
  2312. }
  2313. return e;
  2314. });
  2315. }
  2316. },
  2317. shimGetSendersWithDtmf: function(window) {
  2318. // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.
  2319. if (typeof window === 'object' && window.RTCPeerConnection &&
  2320. !('getSenders' in window.RTCPeerConnection.prototype) &&
  2321. 'createDTMFSender' in window.RTCPeerConnection.prototype) {
  2322. var shimSenderWithDtmf = function(pc, track) {
  2323. return {
  2324. track: track,
  2325. get dtmf() {
  2326. if (this._dtmf === undefined) {
  2327. if (track.kind === 'audio') {
  2328. this._dtmf = pc.createDTMFSender(track);
  2329. } else {
  2330. this._dtmf = null;
  2331. }
  2332. }
  2333. return this._dtmf;
  2334. },
  2335. _pc: pc
  2336. };
  2337. };
  2338. // augment addTrack when getSenders is not available.
  2339. if (!window.RTCPeerConnection.prototype.getSenders) {
  2340. window.RTCPeerConnection.prototype.getSenders = function() {
  2341. this._senders = this._senders || [];
  2342. return this._senders.slice(); // return a copy of the internal state.
  2343. };
  2344. var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
  2345. window.RTCPeerConnection.prototype.addTrack = function(track, stream) {
  2346. var pc = this;
  2347. var sender = origAddTrack.apply(pc, arguments);
  2348. if (!sender) {
  2349. sender = shimSenderWithDtmf(pc, track);
  2350. pc._senders.push(sender);
  2351. }
  2352. return sender;
  2353. };
  2354. var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
  2355. window.RTCPeerConnection.prototype.removeTrack = function(sender) {
  2356. var pc = this;
  2357. origRemoveTrack.apply(pc, arguments);
  2358. var idx = pc._senders.indexOf(sender);
  2359. if (idx !== -1) {
  2360. pc._senders.splice(idx, 1);
  2361. }
  2362. };
  2363. }
  2364. var origAddStream = window.RTCPeerConnection.prototype.addStream;
  2365. window.RTCPeerConnection.prototype.addStream = function(stream) {
  2366. var pc = this;
  2367. pc._senders = pc._senders || [];
  2368. origAddStream.apply(pc, [stream]);
  2369. stream.getTracks().forEach(function(track) {
  2370. pc._senders.push(shimSenderWithDtmf(pc, track));
  2371. });
  2372. };
  2373. var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
  2374. window.RTCPeerConnection.prototype.removeStream = function(stream) {
  2375. var pc = this;
  2376. pc._senders = pc._senders || [];
  2377. origRemoveStream.apply(pc, [stream]);
  2378. stream.getTracks().forEach(function(track) {
  2379. var sender = pc._senders.find(function(s) {
  2380. return s.track === track;
  2381. });
  2382. if (sender) {
  2383. pc._senders.splice(pc._senders.indexOf(sender), 1); // remove sender
  2384. }
  2385. });
  2386. };
  2387. } else if (typeof window === 'object' && window.RTCPeerConnection &&
  2388. 'getSenders' in window.RTCPeerConnection.prototype &&
  2389. 'createDTMFSender' in window.RTCPeerConnection.prototype &&
  2390. window.RTCRtpSender &&
  2391. !('dtmf' in window.RTCRtpSender.prototype)) {
  2392. var origGetSenders = window.RTCPeerConnection.prototype.getSenders;
  2393. window.RTCPeerConnection.prototype.getSenders = function() {
  2394. var pc = this;
  2395. var senders = origGetSenders.apply(pc, []);
  2396. senders.forEach(function(sender) {
  2397. sender._pc = pc;
  2398. });
  2399. return senders;
  2400. };
  2401. Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
  2402. get: function() {
  2403. if (this._dtmf === undefined) {
  2404. if (this.track.kind === 'audio') {
  2405. this._dtmf = this._pc.createDTMFSender(this.track);
  2406. } else {
  2407. this._dtmf = null;
  2408. }
  2409. }
  2410. return this._dtmf;
  2411. }
  2412. });
  2413. }
  2414. },
  2415. shimSourceObject: function(window) {
  2416. var URL = window && window.URL;
  2417. if (typeof window === 'object') {
  2418. if (window.HTMLMediaElement &&
  2419. !('srcObject' in window.HTMLMediaElement.prototype)) {
  2420. // Shim the srcObject property, once, when HTMLMediaElement is found.
  2421. Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {
  2422. get: function() {
  2423. return this._srcObject;
  2424. },
  2425. set: function(stream) {
  2426. var self = this;
  2427. // Use _srcObject as a private property for this shim
  2428. this._srcObject = stream;
  2429. if (this.src) {
  2430. URL.revokeObjectURL(this.src);
  2431. }
  2432. if (!stream) {
  2433. this.src = '';
  2434. return undefined;
  2435. }
  2436. this.src = URL.createObjectURL(stream);
  2437. // We need to recreate the blob url when a track is added or
  2438. // removed. Doing it manually since we want to avoid a recursion.
  2439. stream.addEventListener('addtrack', function() {
  2440. if (self.src) {
  2441. URL.revokeObjectURL(self.src);
  2442. }
  2443. self.src = URL.createObjectURL(stream);
  2444. });
  2445. stream.addEventListener('removetrack', function() {
  2446. if (self.src) {
  2447. URL.revokeObjectURL(self.src);
  2448. }
  2449. self.src = URL.createObjectURL(stream);
  2450. });
  2451. }
  2452. });
  2453. }
  2454. }
  2455. },
  2456. shimAddTrackRemoveTrackWithNative: function(window) {
  2457. // shim addTrack/removeTrack with native variants in order to make
  2458. // the interactions with legacy getLocalStreams behave as in other browsers.
  2459. // Keeps a mapping stream.id => [stream, rtpsenders...]
  2460. window.RTCPeerConnection.prototype.getLocalStreams = function() {
  2461. var pc = this;
  2462. this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  2463. return Object.keys(this._shimmedLocalStreams).map(function(streamId) {
  2464. return pc._shimmedLocalStreams[streamId][0];
  2465. });
  2466. };
  2467. var origAddTrack = window.RTCPeerConnection.prototype.addTrack;
  2468. window.RTCPeerConnection.prototype.addTrack = function(track, stream) {
  2469. if (!stream) {
  2470. return origAddTrack.apply(this, arguments);
  2471. }
  2472. this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  2473. var sender = origAddTrack.apply(this, arguments);
  2474. if (!this._shimmedLocalStreams[stream.id]) {
  2475. this._shimmedLocalStreams[stream.id] = [stream, sender];
  2476. } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) {
  2477. this._shimmedLocalStreams[stream.id].push(sender);
  2478. }
  2479. return sender;
  2480. };
  2481. var origAddStream = window.RTCPeerConnection.prototype.addStream;
  2482. window.RTCPeerConnection.prototype.addStream = function(stream) {
  2483. var pc = this;
  2484. this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  2485. stream.getTracks().forEach(function(track) {
  2486. var alreadyExists = pc.getSenders().find(function(s) {
  2487. return s.track === track;
  2488. });
  2489. if (alreadyExists) {
  2490. throw new DOMException('Track already exists.',
  2491. 'InvalidAccessError');
  2492. }
  2493. });
  2494. var existingSenders = pc.getSenders();
  2495. origAddStream.apply(this, arguments);
  2496. var newSenders = pc.getSenders().filter(function(newSender) {
  2497. return existingSenders.indexOf(newSender) === -1;
  2498. });
  2499. this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders);
  2500. };
  2501. var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
  2502. window.RTCPeerConnection.prototype.removeStream = function(stream) {
  2503. this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  2504. delete this._shimmedLocalStreams[stream.id];
  2505. return origRemoveStream.apply(this, arguments);
  2506. };
  2507. var origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
  2508. window.RTCPeerConnection.prototype.removeTrack = function(sender) {
  2509. var pc = this;
  2510. this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  2511. if (sender) {
  2512. Object.keys(this._shimmedLocalStreams).forEach(function(streamId) {
  2513. var idx = pc._shimmedLocalStreams[streamId].indexOf(sender);
  2514. if (idx !== -1) {
  2515. pc._shimmedLocalStreams[streamId].splice(idx, 1);
  2516. }
  2517. if (pc._shimmedLocalStreams[streamId].length === 1) {
  2518. delete pc._shimmedLocalStreams[streamId];
  2519. }
  2520. });
  2521. }
  2522. return origRemoveTrack.apply(this, arguments);
  2523. };
  2524. },
  2525. shimAddTrackRemoveTrack: function(window) {
  2526. var browserDetails = utils.detectBrowser(window);
  2527. // shim addTrack and removeTrack.
  2528. if (window.RTCPeerConnection.prototype.addTrack &&
  2529. browserDetails.version >= 65) {
  2530. return this.shimAddTrackRemoveTrackWithNative(window);
  2531. }
  2532. // also shim pc.getLocalStreams when addTrack is shimmed
  2533. // to return the original streams.
  2534. var origGetLocalStreams = window.RTCPeerConnection.prototype
  2535. .getLocalStreams;
  2536. window.RTCPeerConnection.prototype.getLocalStreams = function() {
  2537. var pc = this;
  2538. var nativeStreams = origGetLocalStreams.apply(this);
  2539. pc._reverseStreams = pc._reverseStreams || {};
  2540. return nativeStreams.map(function(stream) {
  2541. return pc._reverseStreams[stream.id];
  2542. });
  2543. };
  2544. var origAddStream = window.RTCPeerConnection.prototype.addStream;
  2545. window.RTCPeerConnection.prototype.addStream = function(stream) {
  2546. var pc = this;
  2547. pc._streams = pc._streams || {};
  2548. pc._reverseStreams = pc._reverseStreams || {};
  2549. stream.getTracks().forEach(function(track) {
  2550. var alreadyExists = pc.getSenders().find(function(s) {
  2551. return s.track === track;
  2552. });
  2553. if (alreadyExists) {
  2554. throw new DOMException('Track already exists.',
  2555. 'InvalidAccessError');
  2556. }
  2557. });
  2558. // Add identity mapping for consistency with addTrack.
  2559. // Unless this is being used with a stream from addTrack.
  2560. if (!pc._reverseStreams[stream.id]) {
  2561. var newStream = new window.MediaStream(stream.getTracks());
  2562. pc._streams[stream.id] = newStream;
  2563. pc._reverseStreams[newStream.id] = stream;
  2564. stream = newStream;
  2565. }
  2566. origAddStream.apply(pc, [stream]);
  2567. };
  2568. var origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
  2569. window.RTCPeerConnection.prototype.removeStream = function(stream) {
  2570. var pc = this;
  2571. pc._streams = pc._streams || {};
  2572. pc._reverseStreams = pc._reverseStreams || {};
  2573. origRemoveStream.apply(pc, [(pc._streams[stream.id] || stream)]);
  2574. delete pc._reverseStreams[(pc._streams[stream.id] ?
  2575. pc._streams[stream.id].id : stream.id)];
  2576. delete pc._streams[stream.id];
  2577. };
  2578. window.RTCPeerConnection.prototype.addTrack = function(track, stream) {
  2579. var pc = this;
  2580. if (pc.signalingState === 'closed') {
  2581. throw new DOMException(
  2582. 'The RTCPeerConnection\'s signalingState is \'closed\'.',
  2583. 'InvalidStateError');
  2584. }
  2585. var streams = [].slice.call(arguments, 1);
  2586. if (streams.length !== 1 ||
  2587. !streams[0].getTracks().find(function(t) {
  2588. return t === track;
  2589. })) {
  2590. // this is not fully correct but all we can manage without
  2591. // [[associated MediaStreams]] internal slot.
  2592. throw new DOMException(
  2593. 'The adapter.js addTrack polyfill only supports a single ' +
  2594. ' stream which is associated with the specified track.',
  2595. 'NotSupportedError');
  2596. }
  2597. var alreadyExists = pc.getSenders().find(function(s) {
  2598. return s.track === track;
  2599. });
  2600. if (alreadyExists) {
  2601. throw new DOMException('Track already exists.',
  2602. 'InvalidAccessError');
  2603. }
  2604. pc._streams = pc._streams || {};
  2605. pc._reverseStreams = pc._reverseStreams || {};
  2606. var oldStream = pc._streams[stream.id];
  2607. if (oldStream) {
  2608. // this is using odd Chrome behaviour, use with caution:
  2609. // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815
  2610. // Note: we rely on the high-level addTrack/dtmf shim to
  2611. // create the sender with a dtmf sender.
  2612. oldStream.addTrack(track);
  2613. // Trigger ONN async.
  2614. Promise.resolve().then(function() {
  2615. pc.dispatchEvent(new Event('negotiationneeded'));
  2616. });
  2617. } else {
  2618. var newStream = new window.MediaStream([track]);
  2619. pc._streams[stream.id] = newStream;
  2620. pc._reverseStreams[newStream.id] = stream;
  2621. pc.addStream(newStream);
  2622. }
  2623. return pc.getSenders().find(function(s) {
  2624. return s.track === track;
  2625. });
  2626. };
  2627. // replace the internal stream id with the external one and
  2628. // vice versa.
  2629. function replaceInternalStreamId(pc, description) {
  2630. var sdp = description.sdp;
  2631. Object.keys(pc._reverseStreams || []).forEach(function(internalId) {
  2632. var externalStream = pc._reverseStreams[internalId];
  2633. var internalStream = pc._streams[externalStream.id];
  2634. sdp = sdp.replace(new RegExp(internalStream.id, 'g'),
  2635. externalStream.id);
  2636. });
  2637. return new RTCSessionDescription({
  2638. type: description.type,
  2639. sdp: sdp
  2640. });
  2641. }
  2642. function replaceExternalStreamId(pc, description) {
  2643. var sdp = description.sdp;
  2644. Object.keys(pc._reverseStreams || []).forEach(function(internalId) {
  2645. var externalStream = pc._reverseStreams[internalId];
  2646. var internalStream = pc._streams[externalStream.id];
  2647. sdp = sdp.replace(new RegExp(externalStream.id, 'g'),
  2648. internalStream.id);
  2649. });
  2650. return new RTCSessionDescription({
  2651. type: description.type,
  2652. sdp: sdp
  2653. });
  2654. }
  2655. ['createOffer', 'createAnswer'].forEach(function(method) {
  2656. var nativeMethod = window.RTCPeerConnection.prototype[method];
  2657. window.RTCPeerConnection.prototype[method] = function() {
  2658. var pc = this;
  2659. var args = arguments;
  2660. var isLegacyCall = arguments.length &&
  2661. typeof arguments[0] === 'function';
  2662. if (isLegacyCall) {
  2663. return nativeMethod.apply(pc, [
  2664. function(description) {
  2665. var desc = replaceInternalStreamId(pc, description);
  2666. args[0].apply(null, [desc]);
  2667. },
  2668. function(err) {
  2669. if (args[1]) {
  2670. args[1].apply(null, err);
  2671. }
  2672. }, arguments[2]
  2673. ]);
  2674. }
  2675. return nativeMethod.apply(pc, arguments)
  2676. .then(function(description) {
  2677. return replaceInternalStreamId(pc, description);
  2678. });
  2679. };
  2680. });
  2681. var origSetLocalDescription =
  2682. window.RTCPeerConnection.prototype.setLocalDescription;
  2683. window.RTCPeerConnection.prototype.setLocalDescription = function() {
  2684. var pc = this;
  2685. if (!arguments.length || !arguments[0].type) {
  2686. return origSetLocalDescription.apply(pc, arguments);
  2687. }
  2688. arguments[0] = replaceExternalStreamId(pc, arguments[0]);
  2689. return origSetLocalDescription.apply(pc, arguments);
  2690. };
  2691. // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier
  2692. var origLocalDescription = Object.getOwnPropertyDescriptor(
  2693. window.RTCPeerConnection.prototype, 'localDescription');
  2694. Object.defineProperty(window.RTCPeerConnection.prototype,
  2695. 'localDescription', {
  2696. get: function() {
  2697. var pc = this;
  2698. var description = origLocalDescription.get.apply(this);
  2699. if (description.type === '') {
  2700. return description;
  2701. }
  2702. return replaceInternalStreamId(pc, description);
  2703. }
  2704. });
  2705. window.RTCPeerConnection.prototype.removeTrack = function(sender) {
  2706. var pc = this;
  2707. if (pc.signalingState === 'closed') {
  2708. throw new DOMException(
  2709. 'The RTCPeerConnection\'s signalingState is \'closed\'.',
  2710. 'InvalidStateError');
  2711. }
  2712. // We can not yet check for sender instanceof RTCRtpSender
  2713. // since we shim RTPSender. So we check if sender._pc is set.
  2714. if (!sender._pc) {
  2715. throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' +
  2716. 'does not implement interface RTCRtpSender.', 'TypeError');
  2717. }
  2718. var isLocal = sender._pc === pc;
  2719. if (!isLocal) {
  2720. throw new DOMException('Sender was not created by this connection.',
  2721. 'InvalidAccessError');
  2722. }
  2723. // Search for the native stream the senders track belongs to.
  2724. pc._streams = pc._streams || {};
  2725. var stream;
  2726. Object.keys(pc._streams).forEach(function(streamid) {
  2727. var hasTrack = pc._streams[streamid].getTracks().find(function(track) {
  2728. return sender.track === track;
  2729. });
  2730. if (hasTrack) {
  2731. stream = pc._streams[streamid];
  2732. }
  2733. });
  2734. if (stream) {
  2735. if (stream.getTracks().length === 1) {
  2736. // if this is the last track of the stream, remove the stream. This
  2737. // takes care of any shimmed _senders.
  2738. pc.removeStream(pc._reverseStreams[stream.id]);
  2739. } else {
  2740. // relying on the same odd chrome behaviour as above.
  2741. stream.removeTrack(sender.track);
  2742. }
  2743. pc.dispatchEvent(new Event('negotiationneeded'));
  2744. }
  2745. };
  2746. },
  2747. shimPeerConnection: function(window) {
  2748. var browserDetails = utils.detectBrowser(window);
  2749. // The RTCPeerConnection object.
  2750. if (!window.RTCPeerConnection) {
  2751. window.RTCPeerConnection = function(pcConfig, pcConstraints) {
  2752. // Translate iceTransportPolicy to iceTransports,
  2753. // see https://code.google.com/p/webrtc/issues/detail?id=4869
  2754. // this was fixed in M56 along with unprefixing RTCPeerConnection.
  2755. logging('PeerConnection');
  2756. if (pcConfig && pcConfig.iceTransportPolicy) {
  2757. pcConfig.iceTransports = pcConfig.iceTransportPolicy;
  2758. }
  2759. return new window.webkitRTCPeerConnection(pcConfig, pcConstraints);
  2760. };
  2761. window.RTCPeerConnection.prototype =
  2762. window.webkitRTCPeerConnection.prototype;
  2763. // wrap static methods. Currently just generateCertificate.
  2764. if (window.webkitRTCPeerConnection.generateCertificate) {
  2765. Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
  2766. get: function() {
  2767. return window.webkitRTCPeerConnection.generateCertificate;
  2768. }
  2769. });
  2770. }
  2771. } else {
  2772. // migrate from non-spec RTCIceServer.url to RTCIceServer.urls
  2773. var OrigPeerConnection = window.RTCPeerConnection;
  2774. window.RTCPeerConnection = function(pcConfig, pcConstraints) {
  2775. if (pcConfig && pcConfig.iceServers) {
  2776. var newIceServers = [];
  2777. for (var i = 0; i < pcConfig.iceServers.length; i++) {
  2778. var server = pcConfig.iceServers[i];
  2779. if (!server.hasOwnProperty('urls') &&
  2780. server.hasOwnProperty('url')) {
  2781. utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');
  2782. server = JSON.parse(JSON.stringify(server));
  2783. server.urls = server.url;
  2784. newIceServers.push(server);
  2785. } else {
  2786. newIceServers.push(pcConfig.iceServers[i]);
  2787. }
  2788. }
  2789. pcConfig.iceServers = newIceServers;
  2790. }
  2791. return new OrigPeerConnection(pcConfig, pcConstraints);
  2792. };
  2793. window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
  2794. // wrap static methods. Currently just generateCertificate.
  2795. Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
  2796. get: function() {
  2797. return OrigPeerConnection.generateCertificate;
  2798. }
  2799. });
  2800. }
  2801. var origGetStats = window.RTCPeerConnection.prototype.getStats;
  2802. window.RTCPeerConnection.prototype.getStats = function(selector,
  2803. successCallback, errorCallback) {
  2804. var pc = this;
  2805. var args = arguments;
  2806. // If selector is a function then we are in the old style stats so just
  2807. // pass back the original getStats format to avoid breaking old users.
  2808. if (arguments.length > 0 && typeof selector === 'function') {
  2809. return origGetStats.apply(this, arguments);
  2810. }
  2811. // When spec-style getStats is supported, return those when called with
  2812. // either no arguments or the selector argument is null.
  2813. if (origGetStats.length === 0 && (arguments.length === 0 ||
  2814. typeof arguments[0] !== 'function')) {
  2815. return origGetStats.apply(this, []);
  2816. }
  2817. var fixChromeStats_ = function(response) {
  2818. var standardReport = {};
  2819. var reports = response.result();
  2820. reports.forEach(function(report) {
  2821. var standardStats = {
  2822. id: report.id,
  2823. timestamp: report.timestamp,
  2824. type: {
  2825. localcandidate: 'local-candidate',
  2826. remotecandidate: 'remote-candidate'
  2827. }[report.type] || report.type
  2828. };
  2829. report.names().forEach(function(name) {
  2830. standardStats[name] = report.stat(name);
  2831. });
  2832. standardReport[standardStats.id] = standardStats;
  2833. });
  2834. return standardReport;
  2835. };
  2836. // shim getStats with maplike support
  2837. var makeMapStats = function(stats) {
  2838. return new Map(Object.keys(stats).map(function(key) {
  2839. return [key, stats[key]];
  2840. }));
  2841. };
  2842. if (arguments.length >= 2) {
  2843. var successCallbackWrapper_ = function(response) {
  2844. args[1](makeMapStats(fixChromeStats_(response)));
  2845. };
  2846. return origGetStats.apply(this, [successCallbackWrapper_,
  2847. arguments[0]]);
  2848. }
  2849. // promise-support
  2850. return new Promise(function(resolve, reject) {
  2851. origGetStats.apply(pc, [
  2852. function(response) {
  2853. resolve(makeMapStats(fixChromeStats_(response)));
  2854. }, reject]);
  2855. }).then(successCallback, errorCallback);
  2856. };
  2857. // add promise support -- natively available in Chrome 51
  2858. if (browserDetails.version < 51) {
  2859. ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
  2860. .forEach(function(method) {
  2861. var nativeMethod = window.RTCPeerConnection.prototype[method];
  2862. window.RTCPeerConnection.prototype[method] = function() {
  2863. var args = arguments;
  2864. var pc = this;
  2865. var promise = new Promise(function(resolve, reject) {
  2866. nativeMethod.apply(pc, [args[0], resolve, reject]);
  2867. });
  2868. if (args.length < 2) {
  2869. return promise;
  2870. }
  2871. return promise.then(function() {
  2872. args[1].apply(null, []);
  2873. },
  2874. function(err) {
  2875. if (args.length >= 3) {
  2876. args[2].apply(null, [err]);
  2877. }
  2878. });
  2879. };
  2880. });
  2881. }
  2882. // promise support for createOffer and createAnswer. Available (without
  2883. // bugs) since M52: crbug/619289
  2884. if (browserDetails.version < 52) {
  2885. ['createOffer', 'createAnswer'].forEach(function(method) {
  2886. var nativeMethod = window.RTCPeerConnection.prototype[method];
  2887. window.RTCPeerConnection.prototype[method] = function() {
  2888. var pc = this;
  2889. if (arguments.length < 1 || (arguments.length === 1 &&
  2890. typeof arguments[0] === 'object')) {
  2891. var opts = arguments.length === 1 ? arguments[0] : undefined;
  2892. return new Promise(function(resolve, reject) {
  2893. nativeMethod.apply(pc, [resolve, reject, opts]);
  2894. });
  2895. }
  2896. return nativeMethod.apply(this, arguments);
  2897. };
  2898. });
  2899. }
  2900. // shim implicit creation of RTCSessionDescription/RTCIceCandidate
  2901. ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
  2902. .forEach(function(method) {
  2903. var nativeMethod = window.RTCPeerConnection.prototype[method];
  2904. window.RTCPeerConnection.prototype[method] = function() {
  2905. arguments[0] = new ((method === 'addIceCandidate') ?
  2906. window.RTCIceCandidate :
  2907. window.RTCSessionDescription)(arguments[0]);
  2908. return nativeMethod.apply(this, arguments);
  2909. };
  2910. });
  2911. // support for addIceCandidate(null or undefined)
  2912. var nativeAddIceCandidate =
  2913. window.RTCPeerConnection.prototype.addIceCandidate;
  2914. window.RTCPeerConnection.prototype.addIceCandidate = function() {
  2915. if (!arguments[0]) {
  2916. if (arguments[1]) {
  2917. arguments[1].apply(null);
  2918. }
  2919. return Promise.resolve();
  2920. }
  2921. return nativeAddIceCandidate.apply(this, arguments);
  2922. };
  2923. }
  2924. };
  2925. },{"../utils.js":13,"./getusermedia":6}],6:[function(require,module,exports){
  2926. /*
  2927. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  2928. *
  2929. * Use of this source code is governed by a BSD-style license
  2930. * that can be found in the LICENSE file in the root of the source
  2931. * tree.
  2932. */
  2933. /* eslint-env node */
  2934. 'use strict';
  2935. var utils = require('../utils.js');
  2936. var logging = utils.log;
  2937. // Expose public methods.
  2938. module.exports = function(window) {
  2939. var browserDetails = utils.detectBrowser(window);
  2940. var navigator = window && window.navigator;
  2941. var constraintsToChrome_ = function(c) {
  2942. if (typeof c !== 'object' || c.mandatory || c.optional) {
  2943. return c;
  2944. }
  2945. var cc = {};
  2946. Object.keys(c).forEach(function(key) {
  2947. if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
  2948. return;
  2949. }
  2950. var r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};
  2951. if (r.exact !== undefined && typeof r.exact === 'number') {
  2952. r.min = r.max = r.exact;
  2953. }
  2954. var oldname_ = function(prefix, name) {
  2955. if (prefix) {
  2956. return prefix + name.charAt(0).toUpperCase() + name.slice(1);
  2957. }
  2958. return (name === 'deviceId') ? 'sourceId' : name;
  2959. };
  2960. if (r.ideal !== undefined) {
  2961. cc.optional = cc.optional || [];
  2962. var oc = {};
  2963. if (typeof r.ideal === 'number') {
  2964. oc[oldname_('min', key)] = r.ideal;
  2965. cc.optional.push(oc);
  2966. oc = {};
  2967. oc[oldname_('max', key)] = r.ideal;
  2968. cc.optional.push(oc);
  2969. } else {
  2970. oc[oldname_('', key)] = r.ideal;
  2971. cc.optional.push(oc);
  2972. }
  2973. }
  2974. if (r.exact !== undefined && typeof r.exact !== 'number') {
  2975. cc.mandatory = cc.mandatory || {};
  2976. cc.mandatory[oldname_('', key)] = r.exact;
  2977. } else {
  2978. ['min', 'max'].forEach(function(mix) {
  2979. if (r[mix] !== undefined) {
  2980. cc.mandatory = cc.mandatory || {};
  2981. cc.mandatory[oldname_(mix, key)] = r[mix];
  2982. }
  2983. });
  2984. }
  2985. });
  2986. if (c.advanced) {
  2987. cc.optional = (cc.optional || []).concat(c.advanced);
  2988. }
  2989. return cc;
  2990. };
  2991. var shimConstraints_ = function(constraints, func) {
  2992. if (browserDetails.version >= 61) {
  2993. return func(constraints);
  2994. }
  2995. constraints = JSON.parse(JSON.stringify(constraints));
  2996. if (constraints && typeof constraints.audio === 'object') {
  2997. var remap = function(obj, a, b) {
  2998. if (a in obj && !(b in obj)) {
  2999. obj[b] = obj[a];
  3000. delete obj[a];
  3001. }
  3002. };
  3003. constraints = JSON.parse(JSON.stringify(constraints));
  3004. remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');
  3005. remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');
  3006. constraints.audio = constraintsToChrome_(constraints.audio);
  3007. }
  3008. if (constraints && typeof constraints.video === 'object') {
  3009. // Shim facingMode for mobile & surface pro.
  3010. var face = constraints.video.facingMode;
  3011. face = face && ((typeof face === 'object') ? face : {ideal: face});
  3012. var getSupportedFacingModeLies = browserDetails.version < 66;
  3013. if ((face && (face.exact === 'user' || face.exact === 'environment' ||
  3014. face.ideal === 'user' || face.ideal === 'environment')) &&
  3015. !(navigator.mediaDevices.getSupportedConstraints &&
  3016. navigator.mediaDevices.getSupportedConstraints().facingMode &&
  3017. !getSupportedFacingModeLies)) {
  3018. delete constraints.video.facingMode;
  3019. var matches;
  3020. if (face.exact === 'environment' || face.ideal === 'environment') {
  3021. matches = ['back', 'rear'];
  3022. } else if (face.exact === 'user' || face.ideal === 'user') {
  3023. matches = ['front'];
  3024. }
  3025. if (matches) {
  3026. // Look for matches in label, or use last cam for back (typical).
  3027. return navigator.mediaDevices.enumerateDevices()
  3028. .then(function(devices) {
  3029. devices = devices.filter(function(d) {
  3030. return d.kind === 'videoinput';
  3031. });
  3032. var dev = devices.find(function(d) {
  3033. return matches.some(function(match) {
  3034. return d.label.toLowerCase().indexOf(match) !== -1;
  3035. });
  3036. });
  3037. if (!dev && devices.length && matches.indexOf('back') !== -1) {
  3038. dev = devices[devices.length - 1]; // more likely the back cam
  3039. }
  3040. if (dev) {
  3041. constraints.video.deviceId = face.exact ? {exact: dev.deviceId} :
  3042. {ideal: dev.deviceId};
  3043. }
  3044. constraints.video = constraintsToChrome_(constraints.video);
  3045. logging('chrome: ' + JSON.stringify(constraints));
  3046. return func(constraints);
  3047. });
  3048. }
  3049. }
  3050. constraints.video = constraintsToChrome_(constraints.video);
  3051. }
  3052. logging('chrome: ' + JSON.stringify(constraints));
  3053. return func(constraints);
  3054. };
  3055. var shimError_ = function(e) {
  3056. return {
  3057. name: {
  3058. PermissionDeniedError: 'NotAllowedError',
  3059. InvalidStateError: 'NotReadableError',
  3060. DevicesNotFoundError: 'NotFoundError',
  3061. ConstraintNotSatisfiedError: 'OverconstrainedError',
  3062. TrackStartError: 'NotReadableError',
  3063. MediaDeviceFailedDueToShutdown: 'NotReadableError',
  3064. MediaDeviceKillSwitchOn: 'NotReadableError'
  3065. }[e.name] || e.name,
  3066. message: e.message,
  3067. constraint: e.constraintName,
  3068. toString: function() {
  3069. return this.name + (this.message && ': ') + this.message;
  3070. }
  3071. };
  3072. };
  3073. var getUserMedia_ = function(constraints, onSuccess, onError) {
  3074. shimConstraints_(constraints, function(c) {
  3075. navigator.webkitGetUserMedia(c, onSuccess, function(e) {
  3076. if (onError) {
  3077. onError(shimError_(e));
  3078. }
  3079. });
  3080. });
  3081. };
  3082. navigator.getUserMedia = getUserMedia_;
  3083. // Returns the result of getUserMedia as a Promise.
  3084. var getUserMediaPromise_ = function(constraints) {
  3085. return new Promise(function(resolve, reject) {
  3086. navigator.getUserMedia(constraints, resolve, reject);
  3087. });
  3088. };
  3089. if (!navigator.mediaDevices) {
  3090. navigator.mediaDevices = {
  3091. getUserMedia: getUserMediaPromise_,
  3092. enumerateDevices: function() {
  3093. return new Promise(function(resolve) {
  3094. var kinds = {audio: 'audioinput', video: 'videoinput'};
  3095. return window.MediaStreamTrack.getSources(function(devices) {
  3096. resolve(devices.map(function(device) {
  3097. return {label: device.label,
  3098. kind: kinds[device.kind],
  3099. deviceId: device.id,
  3100. groupId: ''};
  3101. }));
  3102. });
  3103. });
  3104. },
  3105. getSupportedConstraints: function() {
  3106. return {
  3107. deviceId: true, echoCancellation: true, facingMode: true,
  3108. frameRate: true, height: true, width: true
  3109. };
  3110. }
  3111. };
  3112. }
  3113. // A shim for getUserMedia method on the mediaDevices object.
  3114. // TODO(KaptenJansson) remove once implemented in Chrome stable.
  3115. if (!navigator.mediaDevices.getUserMedia) {
  3116. navigator.mediaDevices.getUserMedia = function(constraints) {
  3117. return getUserMediaPromise_(constraints);
  3118. };
  3119. } else {
  3120. // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
  3121. // function which returns a Promise, it does not accept spec-style
  3122. // constraints.
  3123. var origGetUserMedia = navigator.mediaDevices.getUserMedia.
  3124. bind(navigator.mediaDevices);
  3125. navigator.mediaDevices.getUserMedia = function(cs) {
  3126. return shimConstraints_(cs, function(c) {
  3127. return origGetUserMedia(c).then(function(stream) {
  3128. if (c.audio && !stream.getAudioTracks().length ||
  3129. c.video && !stream.getVideoTracks().length) {
  3130. stream.getTracks().forEach(function(track) {
  3131. track.stop();
  3132. });
  3133. throw new DOMException('', 'NotFoundError');
  3134. }
  3135. return stream;
  3136. }, function(e) {
  3137. return Promise.reject(shimError_(e));
  3138. });
  3139. });
  3140. };
  3141. }
  3142. // Dummy devicechange event methods.
  3143. // TODO(KaptenJansson) remove once implemented in Chrome stable.
  3144. if (typeof navigator.mediaDevices.addEventListener === 'undefined') {
  3145. navigator.mediaDevices.addEventListener = function() {
  3146. logging('Dummy mediaDevices.addEventListener called.');
  3147. };
  3148. }
  3149. if (typeof navigator.mediaDevices.removeEventListener === 'undefined') {
  3150. navigator.mediaDevices.removeEventListener = function() {
  3151. logging('Dummy mediaDevices.removeEventListener called.');
  3152. };
  3153. }
  3154. };
  3155. },{"../utils.js":13}],7:[function(require,module,exports){
  3156. /*
  3157. * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
  3158. *
  3159. * Use of this source code is governed by a BSD-style license
  3160. * that can be found in the LICENSE file in the root of the source
  3161. * tree.
  3162. */
  3163. /* eslint-env node */
  3164. 'use strict';
  3165. var SDPUtils = require('sdp');
  3166. var utils = require('./utils');
  3167. module.exports = {
  3168. shimRTCIceCandidate: function(window) {
  3169. // foundation is arbitrarily chosen as an indicator for full support for
  3170. // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface
  3171. if (window.RTCIceCandidate && 'foundation' in
  3172. window.RTCIceCandidate.prototype) {
  3173. return;
  3174. }
  3175. var NativeRTCIceCandidate = window.RTCIceCandidate;
  3176. window.RTCIceCandidate = function(args) {
  3177. // Remove the a= which shouldn't be part of the candidate string.
  3178. if (typeof args === 'object' && args.candidate &&
  3179. args.candidate.indexOf('a=') === 0) {
  3180. args = JSON.parse(JSON.stringify(args));
  3181. args.candidate = args.candidate.substr(2);
  3182. }
  3183. // Augment the native candidate with the parsed fields.
  3184. var nativeCandidate = new NativeRTCIceCandidate(args);
  3185. var parsedCandidate = SDPUtils.parseCandidate(args.candidate);
  3186. var augmentedCandidate = Object.assign(nativeCandidate,
  3187. parsedCandidate);
  3188. // Add a serializer that does not serialize the extra attributes.
  3189. augmentedCandidate.toJSON = function() {
  3190. return {
  3191. candidate: augmentedCandidate.candidate,
  3192. sdpMid: augmentedCandidate.sdpMid,
  3193. sdpMLineIndex: augmentedCandidate.sdpMLineIndex,
  3194. usernameFragment: augmentedCandidate.usernameFragment,
  3195. };
  3196. };
  3197. return augmentedCandidate;
  3198. };
  3199. // Hook up the augmented candidate in onicecandidate and
  3200. // addEventListener('icecandidate', ...)
  3201. utils.wrapPeerConnectionEvent(window, 'icecandidate', function(e) {
  3202. if (e.candidate) {
  3203. Object.defineProperty(e, 'candidate', {
  3204. value: new window.RTCIceCandidate(e.candidate),
  3205. writable: 'false'
  3206. });
  3207. }
  3208. return e;
  3209. });
  3210. },
  3211. // shimCreateObjectURL must be called before shimSourceObject to avoid loop.
  3212. shimCreateObjectURL: function(window) {
  3213. var URL = window && window.URL;
  3214. if (!(typeof window === 'object' && window.HTMLMediaElement &&
  3215. 'srcObject' in window.HTMLMediaElement.prototype &&
  3216. URL.createObjectURL && URL.revokeObjectURL)) {
  3217. // Only shim CreateObjectURL using srcObject if srcObject exists.
  3218. return undefined;
  3219. }
  3220. var nativeCreateObjectURL = URL.createObjectURL.bind(URL);
  3221. var nativeRevokeObjectURL = URL.revokeObjectURL.bind(URL);
  3222. var streams = new Map(), newId = 0;
  3223. URL.createObjectURL = function(stream) {
  3224. if ('getTracks' in stream) {
  3225. var url = 'polyblob:' + (++newId);
  3226. streams.set(url, stream);
  3227. utils.deprecated('URL.createObjectURL(stream)',
  3228. 'elem.srcObject = stream');
  3229. return url;
  3230. }
  3231. return nativeCreateObjectURL(stream);
  3232. };
  3233. URL.revokeObjectURL = function(url) {
  3234. nativeRevokeObjectURL(url);
  3235. streams.delete(url);
  3236. };
  3237. var dsc = Object.getOwnPropertyDescriptor(window.HTMLMediaElement.prototype,
  3238. 'src');
  3239. Object.defineProperty(window.HTMLMediaElement.prototype, 'src', {
  3240. get: function() {
  3241. return dsc.get.apply(this);
  3242. },
  3243. set: function(url) {
  3244. this.srcObject = streams.get(url) || null;
  3245. return dsc.set.apply(this, [url]);
  3246. }
  3247. });
  3248. var nativeSetAttribute = window.HTMLMediaElement.prototype.setAttribute;
  3249. window.HTMLMediaElement.prototype.setAttribute = function() {
  3250. if (arguments.length === 2 &&
  3251. ('' + arguments[0]).toLowerCase() === 'src') {
  3252. this.srcObject = streams.get(arguments[1]) || null;
  3253. }
  3254. return nativeSetAttribute.apply(this, arguments);
  3255. };
  3256. },
  3257. shimMaxMessageSize: function(window) {
  3258. if (window.RTCSctpTransport) {
  3259. return;
  3260. }
  3261. var browserDetails = utils.detectBrowser(window);
  3262. if (!('sctp' in window.RTCPeerConnection.prototype)) {
  3263. Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', {
  3264. get: function() {
  3265. return typeof this._sctp === 'undefined' ? null : this._sctp;
  3266. }
  3267. });
  3268. }
  3269. var sctpInDescription = function(description) {
  3270. var sections = SDPUtils.splitSections(description.sdp);
  3271. sections.shift();
  3272. return sections.some(function(mediaSection) {
  3273. var mLine = SDPUtils.parseMLine(mediaSection);
  3274. return mLine && mLine.kind === 'application'
  3275. && mLine.protocol.indexOf('SCTP') !== -1;
  3276. });
  3277. };
  3278. var getRemoteFirefoxVersion = function(description) {
  3279. // TODO: Is there a better solution for detecting Firefox?
  3280. var match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/);
  3281. if (match === null || match.length < 2) {
  3282. return -1;
  3283. }
  3284. var version = parseInt(match[1], 10);
  3285. // Test for NaN (yes, this is ugly)
  3286. return version !== version ? -1 : version;
  3287. };
  3288. var getCanSendMaxMessageSize = function(remoteIsFirefox) {
  3289. // Every implementation we know can send at least 64 KiB.
  3290. // Note: Although Chrome is technically able to send up to 256 KiB, the
  3291. // data does not reach the other peer reliably.
  3292. // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419
  3293. var canSendMaxMessageSize = 65536;
  3294. if (browserDetails.browser === 'firefox') {
  3295. if (browserDetails.version < 57) {
  3296. if (remoteIsFirefox === -1) {
  3297. // FF < 57 will send in 16 KiB chunks using the deprecated PPID
  3298. // fragmentation.
  3299. canSendMaxMessageSize = 16384;
  3300. } else {
  3301. // However, other FF (and RAWRTC) can reassemble PPID-fragmented
  3302. // messages. Thus, supporting ~2 GiB when sending.
  3303. canSendMaxMessageSize = 2147483637;
  3304. }
  3305. } else {
  3306. // Currently, all FF >= 57 will reset the remote maximum message size
  3307. // to the default value when a data channel is created at a later
  3308. // stage. :(
  3309. // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
  3310. canSendMaxMessageSize =
  3311. browserDetails.version === 57 ? 65535 : 65536;
  3312. }
  3313. }
  3314. return canSendMaxMessageSize;
  3315. };
  3316. var getMaxMessageSize = function(description, remoteIsFirefox) {
  3317. // Note: 65536 bytes is the default value from the SDP spec. Also,
  3318. // every implementation we know supports receiving 65536 bytes.
  3319. var maxMessageSize = 65536;
  3320. // FF 57 has a slightly incorrect default remote max message size, so
  3321. // we need to adjust it here to avoid a failure when sending.
  3322. // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697
  3323. if (browserDetails.browser === 'firefox'
  3324. && browserDetails.version === 57) {
  3325. maxMessageSize = 65535;
  3326. }
  3327. var match = SDPUtils.matchPrefix(description.sdp, 'a=max-message-size:');
  3328. if (match.length > 0) {
  3329. maxMessageSize = parseInt(match[0].substr(19), 10);
  3330. } else if (browserDetails.browser === 'firefox' &&
  3331. remoteIsFirefox !== -1) {
  3332. // If the maximum message size is not present in the remote SDP and
  3333. // both local and remote are Firefox, the remote peer can receive
  3334. // ~2 GiB.
  3335. maxMessageSize = 2147483637;
  3336. }
  3337. return maxMessageSize;
  3338. };
  3339. var origSetRemoteDescription =
  3340. window.RTCPeerConnection.prototype.setRemoteDescription;
  3341. window.RTCPeerConnection.prototype.setRemoteDescription = function() {
  3342. var pc = this;
  3343. pc._sctp = null;
  3344. if (sctpInDescription(arguments[0])) {
  3345. // Check if the remote is FF.
  3346. var isFirefox = getRemoteFirefoxVersion(arguments[0]);
  3347. // Get the maximum message size the local peer is capable of sending
  3348. var canSendMMS = getCanSendMaxMessageSize(isFirefox);
  3349. // Get the maximum message size of the remote peer.
  3350. var remoteMMS = getMaxMessageSize(arguments[0], isFirefox);
  3351. // Determine final maximum message size
  3352. var maxMessageSize;
  3353. if (canSendMMS === 0 && remoteMMS === 0) {
  3354. maxMessageSize = Number.POSITIVE_INFINITY;
  3355. } else if (canSendMMS === 0 || remoteMMS === 0) {
  3356. maxMessageSize = Math.max(canSendMMS, remoteMMS);
  3357. } else {
  3358. maxMessageSize = Math.min(canSendMMS, remoteMMS);
  3359. }
  3360. // Create a dummy RTCSctpTransport object and the 'maxMessageSize'
  3361. // attribute.
  3362. var sctp = {};
  3363. Object.defineProperty(sctp, 'maxMessageSize', {
  3364. get: function() {
  3365. return maxMessageSize;
  3366. }
  3367. });
  3368. pc._sctp = sctp;
  3369. }
  3370. return origSetRemoteDescription.apply(pc, arguments);
  3371. };
  3372. },
  3373. shimSendThrowTypeError: function(window) {
  3374. // Note: Although Firefox >= 57 has a native implementation, the maximum
  3375. // message size can be reset for all data channels at a later stage.
  3376. // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
  3377. var origCreateDataChannel =
  3378. window.RTCPeerConnection.prototype.createDataChannel;
  3379. window.RTCPeerConnection.prototype.createDataChannel = function() {
  3380. var pc = this;
  3381. var dataChannel = origCreateDataChannel.apply(pc, arguments);
  3382. var origDataChannelSend = dataChannel.send;
  3383. // Patch 'send' method
  3384. dataChannel.send = function() {
  3385. var dc = this;
  3386. var data = arguments[0];
  3387. var length = data.length || data.size || data.byteLength;
  3388. if (length > pc.sctp.maxMessageSize) {
  3389. throw new DOMException('Message too large (can send a maximum of ' +
  3390. pc.sctp.maxMessageSize + ' bytes)', 'TypeError');
  3391. }
  3392. return origDataChannelSend.apply(dc, arguments);
  3393. };
  3394. return dataChannel;
  3395. };
  3396. }
  3397. };
  3398. },{"./utils":13,"sdp":2}],8:[function(require,module,exports){
  3399. /*
  3400. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  3401. *
  3402. * Use of this source code is governed by a BSD-style license
  3403. * that can be found in the LICENSE file in the root of the source
  3404. * tree.
  3405. */
  3406. /* eslint-env node */
  3407. 'use strict';
  3408. var utils = require('../utils');
  3409. var shimRTCPeerConnection = require('rtcpeerconnection-shim');
  3410. module.exports = {
  3411. shimGetUserMedia: require('./getusermedia'),
  3412. shimPeerConnection: function(window) {
  3413. var browserDetails = utils.detectBrowser(window);
  3414. if (window.RTCIceGatherer) {
  3415. // ORTC defines an RTCIceCandidate object but no constructor.
  3416. // Not implemented in Edge.
  3417. if (!window.RTCIceCandidate) {
  3418. window.RTCIceCandidate = function(args) {
  3419. return args;
  3420. };
  3421. }
  3422. // ORTC does not have a session description object but
  3423. // other browsers (i.e. Chrome) that will support both PC and ORTC
  3424. // in the future might have this defined already.
  3425. if (!window.RTCSessionDescription) {
  3426. window.RTCSessionDescription = function(args) {
  3427. return args;
  3428. };
  3429. }
  3430. // this adds an additional event listener to MediaStrackTrack that signals
  3431. // when a tracks enabled property was changed. Workaround for a bug in
  3432. // addStream, see below. No longer required in 15025+
  3433. if (browserDetails.version < 15025) {
  3434. var origMSTEnabled = Object.getOwnPropertyDescriptor(
  3435. window.MediaStreamTrack.prototype, 'enabled');
  3436. Object.defineProperty(window.MediaStreamTrack.prototype, 'enabled', {
  3437. set: function(value) {
  3438. origMSTEnabled.set.call(this, value);
  3439. var ev = new Event('enabled');
  3440. ev.enabled = value;
  3441. this.dispatchEvent(ev);
  3442. }
  3443. });
  3444. }
  3445. }
  3446. // ORTC defines the DTMF sender a bit different.
  3447. // https://github.com/w3c/ortc/issues/714
  3448. if (window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) {
  3449. Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
  3450. get: function() {
  3451. if (this._dtmf === undefined) {
  3452. if (this.track.kind === 'audio') {
  3453. this._dtmf = new window.RTCDtmfSender(this);
  3454. } else if (this.track.kind === 'video') {
  3455. this._dtmf = null;
  3456. }
  3457. }
  3458. return this._dtmf;
  3459. }
  3460. });
  3461. }
  3462. window.RTCPeerConnection =
  3463. shimRTCPeerConnection(window, browserDetails.version);
  3464. },
  3465. shimReplaceTrack: function(window) {
  3466. // ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614
  3467. if (window.RTCRtpSender &&
  3468. !('replaceTrack' in window.RTCRtpSender.prototype)) {
  3469. window.RTCRtpSender.prototype.replaceTrack =
  3470. window.RTCRtpSender.prototype.setTrack;
  3471. }
  3472. }
  3473. };
  3474. },{"../utils":13,"./getusermedia":9,"rtcpeerconnection-shim":1}],9:[function(require,module,exports){
  3475. /*
  3476. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  3477. *
  3478. * Use of this source code is governed by a BSD-style license
  3479. * that can be found in the LICENSE file in the root of the source
  3480. * tree.
  3481. */
  3482. /* eslint-env node */
  3483. 'use strict';
  3484. // Expose public methods.
  3485. module.exports = function(window) {
  3486. var navigator = window && window.navigator;
  3487. var shimError_ = function(e) {
  3488. return {
  3489. name: {PermissionDeniedError: 'NotAllowedError'}[e.name] || e.name,
  3490. message: e.message,
  3491. constraint: e.constraint,
  3492. toString: function() {
  3493. return this.name;
  3494. }
  3495. };
  3496. };
  3497. // getUserMedia error shim.
  3498. var origGetUserMedia = navigator.mediaDevices.getUserMedia.
  3499. bind(navigator.mediaDevices);
  3500. navigator.mediaDevices.getUserMedia = function(c) {
  3501. return origGetUserMedia(c).catch(function(e) {
  3502. return Promise.reject(shimError_(e));
  3503. });
  3504. };
  3505. };
  3506. },{}],10:[function(require,module,exports){
  3507. /*
  3508. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  3509. *
  3510. * Use of this source code is governed by a BSD-style license
  3511. * that can be found in the LICENSE file in the root of the source
  3512. * tree.
  3513. */
  3514. /* eslint-env node */
  3515. 'use strict';
  3516. var utils = require('../utils');
  3517. module.exports = {
  3518. shimGetUserMedia: require('./getusermedia'),
  3519. shimOnTrack: function(window) {
  3520. if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in
  3521. window.RTCPeerConnection.prototype)) {
  3522. Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
  3523. get: function() {
  3524. return this._ontrack;
  3525. },
  3526. set: function(f) {
  3527. if (this._ontrack) {
  3528. this.removeEventListener('track', this._ontrack);
  3529. this.removeEventListener('addstream', this._ontrackpoly);
  3530. }
  3531. this.addEventListener('track', this._ontrack = f);
  3532. this.addEventListener('addstream', this._ontrackpoly = function(e) {
  3533. e.stream.getTracks().forEach(function(track) {
  3534. var event = new Event('track');
  3535. event.track = track;
  3536. event.receiver = {track: track};
  3537. event.transceiver = {receiver: event.receiver};
  3538. event.streams = [e.stream];
  3539. this.dispatchEvent(event);
  3540. }.bind(this));
  3541. }.bind(this));
  3542. }
  3543. });
  3544. }
  3545. if (typeof window === 'object' && window.RTCTrackEvent &&
  3546. ('receiver' in window.RTCTrackEvent.prototype) &&
  3547. !('transceiver' in window.RTCTrackEvent.prototype)) {
  3548. Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
  3549. get: function() {
  3550. return {receiver: this.receiver};
  3551. }
  3552. });
  3553. }
  3554. },
  3555. shimSourceObject: function(window) {
  3556. // Firefox has supported mozSrcObject since FF22, unprefixed in 42.
  3557. if (typeof window === 'object') {
  3558. if (window.HTMLMediaElement &&
  3559. !('srcObject' in window.HTMLMediaElement.prototype)) {
  3560. // Shim the srcObject property, once, when HTMLMediaElement is found.
  3561. Object.defineProperty(window.HTMLMediaElement.prototype, 'srcObject', {
  3562. get: function() {
  3563. return this.mozSrcObject;
  3564. },
  3565. set: function(stream) {
  3566. this.mozSrcObject = stream;
  3567. }
  3568. });
  3569. }
  3570. }
  3571. },
  3572. shimPeerConnection: function(window) {
  3573. var browserDetails = utils.detectBrowser(window);
  3574. if (typeof window !== 'object' || !(window.RTCPeerConnection ||
  3575. window.mozRTCPeerConnection)) {
  3576. return; // probably media.peerconnection.enabled=false in about:config
  3577. }
  3578. // The RTCPeerConnection object.
  3579. if (!window.RTCPeerConnection) {
  3580. window.RTCPeerConnection = function(pcConfig, pcConstraints) {
  3581. if (browserDetails.version < 38) {
  3582. // .urls is not supported in FF < 38.
  3583. // create RTCIceServers with a single url.
  3584. if (pcConfig && pcConfig.iceServers) {
  3585. var newIceServers = [];
  3586. for (var i = 0; i < pcConfig.iceServers.length; i++) {
  3587. var server = pcConfig.iceServers[i];
  3588. if (server.hasOwnProperty('urls')) {
  3589. for (var j = 0; j < server.urls.length; j++) {
  3590. var newServer = {
  3591. url: server.urls[j]
  3592. };
  3593. if (server.urls[j].indexOf('turn') === 0) {
  3594. newServer.username = server.username;
  3595. newServer.credential = server.credential;
  3596. }
  3597. newIceServers.push(newServer);
  3598. }
  3599. } else {
  3600. newIceServers.push(pcConfig.iceServers[i]);
  3601. }
  3602. }
  3603. pcConfig.iceServers = newIceServers;
  3604. }
  3605. }
  3606. return new window.mozRTCPeerConnection(pcConfig, pcConstraints);
  3607. };
  3608. window.RTCPeerConnection.prototype =
  3609. window.mozRTCPeerConnection.prototype;
  3610. // wrap static methods. Currently just generateCertificate.
  3611. if (window.mozRTCPeerConnection.generateCertificate) {
  3612. Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
  3613. get: function() {
  3614. return window.mozRTCPeerConnection.generateCertificate;
  3615. }
  3616. });
  3617. }
  3618. window.RTCSessionDescription = window.mozRTCSessionDescription;
  3619. window.RTCIceCandidate = window.mozRTCIceCandidate;
  3620. }
  3621. // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
  3622. ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
  3623. .forEach(function(method) {
  3624. var nativeMethod = window.RTCPeerConnection.prototype[method];
  3625. window.RTCPeerConnection.prototype[method] = function() {
  3626. arguments[0] = new ((method === 'addIceCandidate') ?
  3627. window.RTCIceCandidate :
  3628. window.RTCSessionDescription)(arguments[0]);
  3629. return nativeMethod.apply(this, arguments);
  3630. };
  3631. });
  3632. // support for addIceCandidate(null or undefined)
  3633. var nativeAddIceCandidate =
  3634. window.RTCPeerConnection.prototype.addIceCandidate;
  3635. window.RTCPeerConnection.prototype.addIceCandidate = function() {
  3636. if (!arguments[0]) {
  3637. if (arguments[1]) {
  3638. arguments[1].apply(null);
  3639. }
  3640. return Promise.resolve();
  3641. }
  3642. return nativeAddIceCandidate.apply(this, arguments);
  3643. };
  3644. // shim getStats with maplike support
  3645. var makeMapStats = function(stats) {
  3646. var map = new Map();
  3647. Object.keys(stats).forEach(function(key) {
  3648. map.set(key, stats[key]);
  3649. map[key] = stats[key];
  3650. });
  3651. return map;
  3652. };
  3653. var modernStatsTypes = {
  3654. inboundrtp: 'inbound-rtp',
  3655. outboundrtp: 'outbound-rtp',
  3656. candidatepair: 'candidate-pair',
  3657. localcandidate: 'local-candidate',
  3658. remotecandidate: 'remote-candidate'
  3659. };
  3660. var nativeGetStats = window.RTCPeerConnection.prototype.getStats;
  3661. window.RTCPeerConnection.prototype.getStats = function(
  3662. selector,
  3663. onSucc,
  3664. onErr
  3665. ) {
  3666. return nativeGetStats.apply(this, [selector || null])
  3667. .then(function(stats) {
  3668. if (browserDetails.version < 48) {
  3669. stats = makeMapStats(stats);
  3670. }
  3671. if (browserDetails.version < 53 && !onSucc) {
  3672. // Shim only promise getStats with spec-hyphens in type names
  3673. // Leave callback version alone; misc old uses of forEach before Map
  3674. try {
  3675. stats.forEach(function(stat) {
  3676. stat.type = modernStatsTypes[stat.type] || stat.type;
  3677. });
  3678. } catch (e) {
  3679. if (e.name !== 'TypeError') {
  3680. throw e;
  3681. }
  3682. // Avoid TypeError: "type" is read-only, in old versions. 34-43ish
  3683. stats.forEach(function(stat, i) {
  3684. stats.set(i, Object.assign({}, stat, {
  3685. type: modernStatsTypes[stat.type] || stat.type
  3686. }));
  3687. });
  3688. }
  3689. }
  3690. return stats;
  3691. })
  3692. .then(onSucc, onErr);
  3693. };
  3694. },
  3695. shimRemoveStream: function(window) {
  3696. if (!window.RTCPeerConnection ||
  3697. 'removeStream' in window.RTCPeerConnection.prototype) {
  3698. return;
  3699. }
  3700. window.RTCPeerConnection.prototype.removeStream = function(stream) {
  3701. var pc = this;
  3702. utils.deprecated('removeStream', 'removeTrack');
  3703. this.getSenders().forEach(function(sender) {
  3704. if (sender.track && stream.getTracks().indexOf(sender.track) !== -1) {
  3705. pc.removeTrack(sender);
  3706. }
  3707. });
  3708. };
  3709. }
  3710. };
  3711. },{"../utils":13,"./getusermedia":11}],11:[function(require,module,exports){
  3712. /*
  3713. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  3714. *
  3715. * Use of this source code is governed by a BSD-style license
  3716. * that can be found in the LICENSE file in the root of the source
  3717. * tree.
  3718. */
  3719. /* eslint-env node */
  3720. 'use strict';
  3721. var utils = require('../utils');
  3722. var logging = utils.log;
  3723. // Expose public methods.
  3724. module.exports = function(window) {
  3725. var browserDetails = utils.detectBrowser(window);
  3726. var navigator = window && window.navigator;
  3727. var MediaStreamTrack = window && window.MediaStreamTrack;
  3728. var shimError_ = function(e) {
  3729. return {
  3730. name: {
  3731. InternalError: 'NotReadableError',
  3732. NotSupportedError: 'TypeError',
  3733. PermissionDeniedError: 'NotAllowedError',
  3734. SecurityError: 'NotAllowedError'
  3735. }[e.name] || e.name,
  3736. message: {
  3737. 'The operation is insecure.': 'The request is not allowed by the ' +
  3738. 'user agent or the platform in the current context.'
  3739. }[e.message] || e.message,
  3740. constraint: e.constraint,
  3741. toString: function() {
  3742. return this.name + (this.message && ': ') + this.message;
  3743. }
  3744. };
  3745. };
  3746. // getUserMedia constraints shim.
  3747. var getUserMedia_ = function(constraints, onSuccess, onError) {
  3748. var constraintsToFF37_ = function(c) {
  3749. if (typeof c !== 'object' || c.require) {
  3750. return c;
  3751. }
  3752. var require = [];
  3753. Object.keys(c).forEach(function(key) {
  3754. if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
  3755. return;
  3756. }
  3757. var r = c[key] = (typeof c[key] === 'object') ?
  3758. c[key] : {ideal: c[key]};
  3759. if (r.min !== undefined ||
  3760. r.max !== undefined || r.exact !== undefined) {
  3761. require.push(key);
  3762. }
  3763. if (r.exact !== undefined) {
  3764. if (typeof r.exact === 'number') {
  3765. r. min = r.max = r.exact;
  3766. } else {
  3767. c[key] = r.exact;
  3768. }
  3769. delete r.exact;
  3770. }
  3771. if (r.ideal !== undefined) {
  3772. c.advanced = c.advanced || [];
  3773. var oc = {};
  3774. if (typeof r.ideal === 'number') {
  3775. oc[key] = {min: r.ideal, max: r.ideal};
  3776. } else {
  3777. oc[key] = r.ideal;
  3778. }
  3779. c.advanced.push(oc);
  3780. delete r.ideal;
  3781. if (!Object.keys(r).length) {
  3782. delete c[key];
  3783. }
  3784. }
  3785. });
  3786. if (require.length) {
  3787. c.require = require;
  3788. }
  3789. return c;
  3790. };
  3791. constraints = JSON.parse(JSON.stringify(constraints));
  3792. if (browserDetails.version < 38) {
  3793. logging('spec: ' + JSON.stringify(constraints));
  3794. if (constraints.audio) {
  3795. constraints.audio = constraintsToFF37_(constraints.audio);
  3796. }
  3797. if (constraints.video) {
  3798. constraints.video = constraintsToFF37_(constraints.video);
  3799. }
  3800. logging('ff37: ' + JSON.stringify(constraints));
  3801. }
  3802. return navigator.mozGetUserMedia(constraints, onSuccess, function(e) {
  3803. onError(shimError_(e));
  3804. });
  3805. };
  3806. // Returns the result of getUserMedia as a Promise.
  3807. var getUserMediaPromise_ = function(constraints) {
  3808. return new Promise(function(resolve, reject) {
  3809. getUserMedia_(constraints, resolve, reject);
  3810. });
  3811. };
  3812. // Shim for mediaDevices on older versions.
  3813. if (!navigator.mediaDevices) {
  3814. navigator.mediaDevices = {getUserMedia: getUserMediaPromise_,
  3815. addEventListener: function() { },
  3816. removeEventListener: function() { }
  3817. };
  3818. }
  3819. navigator.mediaDevices.enumerateDevices =
  3820. navigator.mediaDevices.enumerateDevices || function() {
  3821. return new Promise(function(resolve) {
  3822. var infos = [
  3823. {kind: 'audioinput', deviceId: 'default', label: '', groupId: ''},
  3824. {kind: 'videoinput', deviceId: 'default', label: '', groupId: ''}
  3825. ];
  3826. resolve(infos);
  3827. });
  3828. };
  3829. if (browserDetails.version < 41) {
  3830. // Work around http://bugzil.la/1169665
  3831. var orgEnumerateDevices =
  3832. navigator.mediaDevices.enumerateDevices.bind(navigator.mediaDevices);
  3833. navigator.mediaDevices.enumerateDevices = function() {
  3834. return orgEnumerateDevices().then(undefined, function(e) {
  3835. if (e.name === 'NotFoundError') {
  3836. return [];
  3837. }
  3838. throw e;
  3839. });
  3840. };
  3841. }
  3842. if (browserDetails.version < 49) {
  3843. var origGetUserMedia = navigator.mediaDevices.getUserMedia.
  3844. bind(navigator.mediaDevices);
  3845. navigator.mediaDevices.getUserMedia = function(c) {
  3846. return origGetUserMedia(c).then(function(stream) {
  3847. // Work around https://bugzil.la/802326
  3848. if (c.audio && !stream.getAudioTracks().length ||
  3849. c.video && !stream.getVideoTracks().length) {
  3850. stream.getTracks().forEach(function(track) {
  3851. track.stop();
  3852. });
  3853. throw new DOMException('The object can not be found here.',
  3854. 'NotFoundError');
  3855. }
  3856. return stream;
  3857. }, function(e) {
  3858. return Promise.reject(shimError_(e));
  3859. });
  3860. };
  3861. }
  3862. if (!(browserDetails.version > 55 &&
  3863. 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {
  3864. var remap = function(obj, a, b) {
  3865. if (a in obj && !(b in obj)) {
  3866. obj[b] = obj[a];
  3867. delete obj[a];
  3868. }
  3869. };
  3870. var nativeGetUserMedia = navigator.mediaDevices.getUserMedia.
  3871. bind(navigator.mediaDevices);
  3872. navigator.mediaDevices.getUserMedia = function(c) {
  3873. if (typeof c === 'object' && typeof c.audio === 'object') {
  3874. c = JSON.parse(JSON.stringify(c));
  3875. remap(c.audio, 'autoGainControl', 'mozAutoGainControl');
  3876. remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');
  3877. }
  3878. return nativeGetUserMedia(c);
  3879. };
  3880. if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {
  3881. var nativeGetSettings = MediaStreamTrack.prototype.getSettings;
  3882. MediaStreamTrack.prototype.getSettings = function() {
  3883. var obj = nativeGetSettings.apply(this, arguments);
  3884. remap(obj, 'mozAutoGainControl', 'autoGainControl');
  3885. remap(obj, 'mozNoiseSuppression', 'noiseSuppression');
  3886. return obj;
  3887. };
  3888. }
  3889. if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {
  3890. var nativeApplyConstraints = MediaStreamTrack.prototype.applyConstraints;
  3891. MediaStreamTrack.prototype.applyConstraints = function(c) {
  3892. if (this.kind === 'audio' && typeof c === 'object') {
  3893. c = JSON.parse(JSON.stringify(c));
  3894. remap(c, 'autoGainControl', 'mozAutoGainControl');
  3895. remap(c, 'noiseSuppression', 'mozNoiseSuppression');
  3896. }
  3897. return nativeApplyConstraints.apply(this, [c]);
  3898. };
  3899. }
  3900. }
  3901. navigator.getUserMedia = function(constraints, onSuccess, onError) {
  3902. if (browserDetails.version < 44) {
  3903. return getUserMedia_(constraints, onSuccess, onError);
  3904. }
  3905. // Replace Firefox 44+'s deprecation warning with unprefixed version.
  3906. utils.deprecated('navigator.getUserMedia',
  3907. 'navigator.mediaDevices.getUserMedia');
  3908. navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
  3909. };
  3910. };
  3911. },{"../utils":13}],12:[function(require,module,exports){
  3912. /*
  3913. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  3914. *
  3915. * Use of this source code is governed by a BSD-style license
  3916. * that can be found in the LICENSE file in the root of the source
  3917. * tree.
  3918. */
  3919. 'use strict';
  3920. var utils = require('../utils');
  3921. module.exports = {
  3922. shimLocalStreamsAPI: function(window) {
  3923. if (typeof window !== 'object' || !window.RTCPeerConnection) {
  3924. return;
  3925. }
  3926. if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {
  3927. window.RTCPeerConnection.prototype.getLocalStreams = function() {
  3928. if (!this._localStreams) {
  3929. this._localStreams = [];
  3930. }
  3931. return this._localStreams;
  3932. };
  3933. }
  3934. if (!('getStreamById' in window.RTCPeerConnection.prototype)) {
  3935. window.RTCPeerConnection.prototype.getStreamById = function(id) {
  3936. var result = null;
  3937. if (this._localStreams) {
  3938. this._localStreams.forEach(function(stream) {
  3939. if (stream.id === id) {
  3940. result = stream;
  3941. }
  3942. });
  3943. }
  3944. if (this._remoteStreams) {
  3945. this._remoteStreams.forEach(function(stream) {
  3946. if (stream.id === id) {
  3947. result = stream;
  3948. }
  3949. });
  3950. }
  3951. return result;
  3952. };
  3953. }
  3954. if (!('addStream' in window.RTCPeerConnection.prototype)) {
  3955. var _addTrack = window.RTCPeerConnection.prototype.addTrack;
  3956. window.RTCPeerConnection.prototype.addStream = function(stream) {
  3957. if (!this._localStreams) {
  3958. this._localStreams = [];
  3959. }
  3960. if (this._localStreams.indexOf(stream) === -1) {
  3961. this._localStreams.push(stream);
  3962. }
  3963. var pc = this;
  3964. stream.getTracks().forEach(function(track) {
  3965. _addTrack.call(pc, track, stream);
  3966. });
  3967. };
  3968. window.RTCPeerConnection.prototype.addTrack = function(track, stream) {
  3969. if (stream) {
  3970. if (!this._localStreams) {
  3971. this._localStreams = [stream];
  3972. } else if (this._localStreams.indexOf(stream) === -1) {
  3973. this._localStreams.push(stream);
  3974. }
  3975. }
  3976. return _addTrack.call(this, track, stream);
  3977. };
  3978. }
  3979. if (!('removeStream' in window.RTCPeerConnection.prototype)) {
  3980. window.RTCPeerConnection.prototype.removeStream = function(stream) {
  3981. if (!this._localStreams) {
  3982. this._localStreams = [];
  3983. }
  3984. var index = this._localStreams.indexOf(stream);
  3985. if (index === -1) {
  3986. return;
  3987. }
  3988. this._localStreams.splice(index, 1);
  3989. var pc = this;
  3990. var tracks = stream.getTracks();
  3991. this.getSenders().forEach(function(sender) {
  3992. if (tracks.indexOf(sender.track) !== -1) {
  3993. pc.removeTrack(sender);
  3994. }
  3995. });
  3996. };
  3997. }
  3998. },
  3999. shimRemoteStreamsAPI: function(window) {
  4000. if (typeof window !== 'object' || !window.RTCPeerConnection) {
  4001. return;
  4002. }
  4003. if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {
  4004. window.RTCPeerConnection.prototype.getRemoteStreams = function() {
  4005. return this._remoteStreams ? this._remoteStreams : [];
  4006. };
  4007. }
  4008. if (!('onaddstream' in window.RTCPeerConnection.prototype)) {
  4009. Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {
  4010. get: function() {
  4011. return this._onaddstream;
  4012. },
  4013. set: function(f) {
  4014. var pc = this;
  4015. if (this._onaddstream) {
  4016. this.removeEventListener('addstream', this._onaddstream);
  4017. this.removeEventListener('track', this._onaddstreampoly);
  4018. }
  4019. this.addEventListener('addstream', this._onaddstream = f);
  4020. this.addEventListener('track', this._onaddstreampoly = function(e) {
  4021. e.streams.forEach(function(stream) {
  4022. if (!pc._remoteStreams) {
  4023. pc._remoteStreams = [];
  4024. }
  4025. if (pc._remoteStreams.indexOf(stream) >= 0) {
  4026. return;
  4027. }
  4028. pc._remoteStreams.push(stream);
  4029. var event = new Event('addstream');
  4030. event.stream = stream;
  4031. pc.dispatchEvent(event);
  4032. });
  4033. });
  4034. }
  4035. });
  4036. }
  4037. },
  4038. shimCallbacksAPI: function(window) {
  4039. if (typeof window !== 'object' || !window.RTCPeerConnection) {
  4040. return;
  4041. }
  4042. var prototype = window.RTCPeerConnection.prototype;
  4043. var createOffer = prototype.createOffer;
  4044. var createAnswer = prototype.createAnswer;
  4045. var setLocalDescription = prototype.setLocalDescription;
  4046. var setRemoteDescription = prototype.setRemoteDescription;
  4047. var addIceCandidate = prototype.addIceCandidate;
  4048. prototype.createOffer = function(successCallback, failureCallback) {
  4049. var options = (arguments.length >= 2) ? arguments[2] : arguments[0];
  4050. var promise = createOffer.apply(this, [options]);
  4051. if (!failureCallback) {
  4052. return promise;
  4053. }
  4054. promise.then(successCallback, failureCallback);
  4055. return Promise.resolve();
  4056. };
  4057. prototype.createAnswer = function(successCallback, failureCallback) {
  4058. var options = (arguments.length >= 2) ? arguments[2] : arguments[0];
  4059. var promise = createAnswer.apply(this, [options]);
  4060. if (!failureCallback) {
  4061. return promise;
  4062. }
  4063. promise.then(successCallback, failureCallback);
  4064. return Promise.resolve();
  4065. };
  4066. var withCallback = function(description, successCallback, failureCallback) {
  4067. var promise = setLocalDescription.apply(this, [description]);
  4068. if (!failureCallback) {
  4069. return promise;
  4070. }
  4071. promise.then(successCallback, failureCallback);
  4072. return Promise.resolve();
  4073. };
  4074. prototype.setLocalDescription = withCallback;
  4075. withCallback = function(description, successCallback, failureCallback) {
  4076. var promise = setRemoteDescription.apply(this, [description]);
  4077. if (!failureCallback) {
  4078. return promise;
  4079. }
  4080. promise.then(successCallback, failureCallback);
  4081. return Promise.resolve();
  4082. };
  4083. prototype.setRemoteDescription = withCallback;
  4084. withCallback = function(candidate, successCallback, failureCallback) {
  4085. var promise = addIceCandidate.apply(this, [candidate]);
  4086. if (!failureCallback) {
  4087. return promise;
  4088. }
  4089. promise.then(successCallback, failureCallback);
  4090. return Promise.resolve();
  4091. };
  4092. prototype.addIceCandidate = withCallback;
  4093. },
  4094. shimGetUserMedia: function(window) {
  4095. var navigator = window && window.navigator;
  4096. if (!navigator.getUserMedia) {
  4097. if (navigator.webkitGetUserMedia) {
  4098. navigator.getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
  4099. } else if (navigator.mediaDevices &&
  4100. navigator.mediaDevices.getUserMedia) {
  4101. navigator.getUserMedia = function(constraints, cb, errcb) {
  4102. navigator.mediaDevices.getUserMedia(constraints)
  4103. .then(cb, errcb);
  4104. }.bind(navigator);
  4105. }
  4106. }
  4107. },
  4108. shimRTCIceServerUrls: function(window) {
  4109. // migrate from non-spec RTCIceServer.url to RTCIceServer.urls
  4110. var OrigPeerConnection = window.RTCPeerConnection;
  4111. window.RTCPeerConnection = function(pcConfig, pcConstraints) {
  4112. if (pcConfig && pcConfig.iceServers) {
  4113. var newIceServers = [];
  4114. for (var i = 0; i < pcConfig.iceServers.length; i++) {
  4115. var server = pcConfig.iceServers[i];
  4116. if (!server.hasOwnProperty('urls') &&
  4117. server.hasOwnProperty('url')) {
  4118. utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');
  4119. server = JSON.parse(JSON.stringify(server));
  4120. server.urls = server.url;
  4121. delete server.url;
  4122. newIceServers.push(server);
  4123. } else {
  4124. newIceServers.push(pcConfig.iceServers[i]);
  4125. }
  4126. }
  4127. pcConfig.iceServers = newIceServers;
  4128. }
  4129. return new OrigPeerConnection(pcConfig, pcConstraints);
  4130. };
  4131. window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
  4132. // wrap static methods. Currently just generateCertificate.
  4133. if ('generateCertificate' in window.RTCPeerConnection) {
  4134. Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
  4135. get: function() {
  4136. return OrigPeerConnection.generateCertificate;
  4137. }
  4138. });
  4139. }
  4140. },
  4141. shimTrackEventTransceiver: function(window) {
  4142. // Add event.transceiver member over deprecated event.receiver
  4143. if (typeof window === 'object' && window.RTCPeerConnection &&
  4144. ('receiver' in window.RTCTrackEvent.prototype) &&
  4145. // can't check 'transceiver' in window.RTCTrackEvent.prototype, as it is
  4146. // defined for some reason even when window.RTCTransceiver is not.
  4147. !window.RTCTransceiver) {
  4148. Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
  4149. get: function() {
  4150. return {receiver: this.receiver};
  4151. }
  4152. });
  4153. }
  4154. },
  4155. shimCreateOfferLegacy: function(window) {
  4156. var origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
  4157. window.RTCPeerConnection.prototype.createOffer = function(offerOptions) {
  4158. var pc = this;
  4159. if (offerOptions) {
  4160. var audioTransceiver = pc.getTransceivers().find(function(transceiver) {
  4161. return transceiver.sender.track &&
  4162. transceiver.sender.track.kind === 'audio';
  4163. });
  4164. if (offerOptions.offerToReceiveAudio === false && audioTransceiver) {
  4165. if (audioTransceiver.direction === 'sendrecv') {
  4166. if (audioTransceiver.setDirection) {
  4167. audioTransceiver.setDirection('sendonly');
  4168. } else {
  4169. audioTransceiver.direction = 'sendonly';
  4170. }
  4171. } else if (audioTransceiver.direction === 'recvonly') {
  4172. if (audioTransceiver.setDirection) {
  4173. audioTransceiver.setDirection('inactive');
  4174. } else {
  4175. audioTransceiver.direction = 'inactive';
  4176. }
  4177. }
  4178. } else if (offerOptions.offerToReceiveAudio === true &&
  4179. !audioTransceiver) {
  4180. pc.addTransceiver('audio');
  4181. }
  4182. var videoTransceiver = pc.getTransceivers().find(function(transceiver) {
  4183. return transceiver.sender.track &&
  4184. transceiver.sender.track.kind === 'video';
  4185. });
  4186. if (offerOptions.offerToReceiveVideo === false && videoTransceiver) {
  4187. if (videoTransceiver.direction === 'sendrecv') {
  4188. videoTransceiver.setDirection('sendonly');
  4189. } else if (videoTransceiver.direction === 'recvonly') {
  4190. videoTransceiver.setDirection('inactive');
  4191. }
  4192. } else if (offerOptions.offerToReceiveVideo === true &&
  4193. !videoTransceiver) {
  4194. pc.addTransceiver('video');
  4195. }
  4196. }
  4197. return origCreateOffer.apply(pc, arguments);
  4198. };
  4199. }
  4200. };
  4201. },{"../utils":13}],13:[function(require,module,exports){
  4202. /*
  4203. * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  4204. *
  4205. * Use of this source code is governed by a BSD-style license
  4206. * that can be found in the LICENSE file in the root of the source
  4207. * tree.
  4208. */
  4209. /* eslint-env node */
  4210. 'use strict';
  4211. var logDisabled_ = true;
  4212. var deprecationWarnings_ = true;
  4213. /**
  4214. * Extract browser version out of the provided user agent string.
  4215. *
  4216. * @param {!string} uastring userAgent string.
  4217. * @param {!string} expr Regular expression used as match criteria.
  4218. * @param {!number} pos position in the version string to be returned.
  4219. * @return {!number} browser version.
  4220. */
  4221. function extractVersion(uastring, expr, pos) {
  4222. var match = uastring.match(expr);
  4223. return match && match.length >= pos && parseInt(match[pos], 10);
  4224. }
  4225. // Wraps the peerconnection event eventNameToWrap in a function
  4226. // which returns the modified event object.
  4227. function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {
  4228. if (!window.RTCPeerConnection) {
  4229. return;
  4230. }
  4231. var proto = window.RTCPeerConnection.prototype;
  4232. var nativeAddEventListener = proto.addEventListener;
  4233. proto.addEventListener = function(nativeEventName, cb) {
  4234. if (nativeEventName !== eventNameToWrap) {
  4235. return nativeAddEventListener.apply(this, arguments);
  4236. }
  4237. var wrappedCallback = function(e) {
  4238. cb(wrapper(e));
  4239. };
  4240. this._eventMap = this._eventMap || {};
  4241. this._eventMap[cb] = wrappedCallback;
  4242. return nativeAddEventListener.apply(this, [nativeEventName,
  4243. wrappedCallback]);
  4244. };
  4245. var nativeRemoveEventListener = proto.removeEventListener;
  4246. proto.removeEventListener = function(nativeEventName, cb) {
  4247. if (nativeEventName !== eventNameToWrap || !this._eventMap
  4248. || !this._eventMap[cb]) {
  4249. return nativeRemoveEventListener.apply(this, arguments);
  4250. }
  4251. var unwrappedCb = this._eventMap[cb];
  4252. delete this._eventMap[cb];
  4253. return nativeRemoveEventListener.apply(this, [nativeEventName,
  4254. unwrappedCb]);
  4255. };
  4256. Object.defineProperty(proto, 'on' + eventNameToWrap, {
  4257. get: function() {
  4258. return this['_on' + eventNameToWrap];
  4259. },
  4260. set: function(cb) {
  4261. if (this['_on' + eventNameToWrap]) {
  4262. this.removeEventListener(eventNameToWrap,
  4263. this['_on' + eventNameToWrap]);
  4264. delete this['_on' + eventNameToWrap];
  4265. }
  4266. if (cb) {
  4267. this.addEventListener(eventNameToWrap,
  4268. this['_on' + eventNameToWrap] = cb);
  4269. }
  4270. }
  4271. });
  4272. }
  4273. // Utility methods.
  4274. module.exports = {
  4275. extractVersion: extractVersion,
  4276. wrapPeerConnectionEvent: wrapPeerConnectionEvent,
  4277. disableLog: function(bool) {
  4278. if (typeof bool !== 'boolean') {
  4279. return new Error('Argument type: ' + typeof bool +
  4280. '. Please use a boolean.');
  4281. }
  4282. logDisabled_ = bool;
  4283. return (bool) ? 'adapter.js logging disabled' :
  4284. 'adapter.js logging enabled';
  4285. },
  4286. /**
  4287. * Disable or enable deprecation warnings
  4288. * @param {!boolean} bool set to true to disable warnings.
  4289. */
  4290. disableWarnings: function(bool) {
  4291. if (typeof bool !== 'boolean') {
  4292. return new Error('Argument type: ' + typeof bool +
  4293. '. Please use a boolean.');
  4294. }
  4295. deprecationWarnings_ = !bool;
  4296. return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');
  4297. },
  4298. log: function() {
  4299. if (typeof window === 'object') {
  4300. if (logDisabled_) {
  4301. return;
  4302. }
  4303. if (typeof console !== 'undefined' && typeof console.log === 'function') {
  4304. console.log.apply(console, arguments);
  4305. }
  4306. }
  4307. },
  4308. /**
  4309. * Shows a deprecation warning suggesting the modern and spec-compatible API.
  4310. */
  4311. deprecated: function(oldMethod, newMethod) {
  4312. if (!deprecationWarnings_) {
  4313. return;
  4314. }
  4315. console.warn(oldMethod + ' is deprecated, please use ' + newMethod +
  4316. ' instead.');
  4317. },
  4318. /**
  4319. * Browser detector.
  4320. *
  4321. * @return {object} result containing browser and version
  4322. * properties.
  4323. */
  4324. detectBrowser: function(window) {
  4325. var navigator = window && window.navigator;
  4326. // Returned result object.
  4327. var result = {};
  4328. result.browser = null;
  4329. result.version = null;
  4330. // Fail early if it's not a browser
  4331. if (typeof window === 'undefined' || !window.navigator) {
  4332. result.browser = 'Not a browser.';
  4333. return result;
  4334. }
  4335. // Firefox.
  4336. if (navigator.mozGetUserMedia) {
  4337. result.browser = 'firefox';
  4338. result.version = extractVersion(navigator.userAgent,
  4339. /Firefox\/(\d+)\./, 1);
  4340. } else if (navigator.webkitGetUserMedia) {
  4341. // Chrome, Chromium, Webview, Opera, all use the chrome shim for now
  4342. if (window.webkitRTCPeerConnection) {
  4343. result.browser = 'chrome';
  4344. result.version = extractVersion(navigator.userAgent,
  4345. /Chrom(e|ium)\/(\d+)\./, 2);
  4346. } else { // Safari (in an unpublished version) or unknown webkit-based.
  4347. if (navigator.userAgent.match(/Version\/(\d+).(\d+)/)) {
  4348. result.browser = 'safari';
  4349. result.version = extractVersion(navigator.userAgent,
  4350. /AppleWebKit\/(\d+)\./, 1);
  4351. } else { // unknown webkit-based browser.
  4352. result.browser = 'Unsupported webkit-based browser ' +
  4353. 'with GUM support but no WebRTC support.';
  4354. return result;
  4355. }
  4356. }
  4357. } else if (navigator.mediaDevices &&
  4358. navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { // Edge.
  4359. result.browser = 'edge';
  4360. result.version = extractVersion(navigator.userAgent,
  4361. /Edge\/(\d+).(\d+)$/, 2);
  4362. } else if (navigator.mediaDevices &&
  4363. navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) {
  4364. // Safari, with webkitGetUserMedia removed.
  4365. result.browser = 'safari';
  4366. result.version = extractVersion(navigator.userAgent,
  4367. /AppleWebKit\/(\d+)\./, 1);
  4368. } else { // Default fallthrough: not supported.
  4369. result.browser = 'Not a supported browser.';
  4370. return result;
  4371. }
  4372. return result;
  4373. }
  4374. };
  4375. },{}]},{},[3])(3)
  4376. });