From 20aa4c3fe11a379a640c13af608157a3241d80ca Mon Sep 17 00:00:00 2001 From: twilio-vblocks-ci Date: Fri, 5 Jan 2024 17:28:02 +0000 Subject: [PATCH] 2.10.0 --- dist/twilio.js | 14777 ++++++++++++++++ dist/twilio.min.js | 1 + docs/assets/css/main.css | 852 + docs/assets/css/main.css.map | 7 + docs/assets/images/icons.png | Bin 0 -> 9615 bytes docs/assets/images/icons@2x.png | Bin 0 -> 28144 bytes docs/assets/images/widgets.png | Bin 0 -> 480 bytes docs/assets/images/widgets@2x.png | Bin 0 -> 855 bytes docs/assets/js/lib/backbone-1.1.2.min.js | 2 + docs/assets/js/lib/jquery-2.1.1.min.js | 4 + docs/assets/js/lib/lunr.min.js | 7 + docs/assets/js/lib/underscore-1.6.0.min.js | 6 + docs/assets/js/main.js | 5 + docs/assets/js/search.json | 1 + docs/classes/tools.dialtoneplayer.html | 355 + docs/classes/tools.wstransport.html | 1125 ++ docs/classes/twilio.backoff.html | 914 + .../twilio_errors.acquisitionfailederror.html | 445 + .../twilio_errors.clientlocaldescfailed.html | 445 + .../twilio_errors.clientremotedescfailed.html | 445 + .../twilio_errors.connectiondisconnected.html | 445 + .../twilio_errors.connectionerror-1.html | 445 + .../twilio_errors.connectionerror.html | 445 + .../twilio_errors.invalidargumenterror.html | 309 + .../twilio_errors.invalidstateerror.html | 309 + .../twilio_errors.notsupportederror.html | 309 + .../twilio_errors.permissiondeniederror.html | 445 + docs/classes/voice.asyncqueue.html | 260 + docs/classes/voice.audiohelper.html | 1450 ++ docs/classes/voice.call.html | 2033 +++ docs/classes/voice.deferred-1.html | 333 + docs/classes/voice.deferred.html | 330 + docs/classes/voice.device.html | 1741 ++ docs/classes/voice.eventpublisher.html | 880 + docs/classes/voice.eventtarget.html | 286 + docs/classes/voice.icecandidate.html | 275 + docs/classes/voice.mediadeviceinfoshim.html | 223 + .../classes/voice.outputdevicecollection.html | 337 + docs/classes/voice.preflighttest.html | 1359 ++ docs/classes/voice.pstream.html | 897 + docs/classes/voice.statsmonitor.html | 1063 ++ docs/classes/voice.twilioerror.html | 533 + docs/enums/tools.wstransportstate.html | 294 + docs/enums/voice.call.calldirection.html | 231 + docs/enums/voice.call.codec.html | 231 + docs/enums/voice.call.feedbackissue.html | 288 + docs/enums/voice.call.feedbackscore.html | 274 + .../voice.call.icegatheringfailurereason.html | 231 + docs/enums/voice.call.mediafailure.html | 259 + docs/enums/voice.call.messagetype.html | 225 + docs/enums/voice.call.state.html | 287 + docs/enums/voice.device.eventname.html | 301 + docs/enums/voice.device.soundname.html | 413 + docs/enums/voice.device.state.html | 259 + .../voice.preflighttest.callquality.html | 299 + docs/enums/voice.preflighttest.events.html | 298 + docs/enums/voice.preflighttest.status.html | 279 + docs/globals.html | 181 + docs/index.html | 284 + docs/interfaces/tools.imessageevent.html | 280 + .../tools.iwstransportconstructoroptions.html | 359 + .../voice.asyncqueue.operation.html | 267 + docs/interfaces/voice.audiooutput.html | 223 + docs/interfaces/voice.audioprocessor.html | 342 + docs/interfaces/voice.call.acceptoptions.html | 254 + docs/interfaces/voice.call.callerinfo.html | 233 + docs/interfaces/voice.call.message.html | 291 + docs/interfaces/voice.chromeaudiocontext.html | 1244 ++ .../voice.chromehtmlaudioelement.html | 6633 +++++++ .../voice.device.connectoptions.html | 275 + docs/interfaces/voice.device.options.html | 762 + .../interfaces/voice.iaudioplayeroptions.html | 201 + ...voice.mediastreamaudiodestinationnode.html | 643 + docs/interfaces/voice.networktiming.html | 288 + .../voice.preflighttest.extendedoptions.html | 497 + .../voice.preflighttest.options.html | 396 + .../voice.preflighttest.report.html | 461 + ...flighttest.rtcicecandidatestatsreport.html | 249 + ...test.rtcselectedicecandidatepairstats.html | 251 + .../voice.preflighttest.rtcstats.html | 268 + .../interfaces/voice.preflighttest.stats.html | 268 + .../voice.preflighttest.warning.html | 268 + docs/interfaces/voice.rtcsample.html | 478 + docs/interfaces/voice.rtcsampletotals.html | 317 + docs/interfaces/voice.rtcwarning.html | 298 + .../voice.thresholdwarningdata.html | 241 + docs/interfaces/voice.timemeasurement.html | 260 + docs/modules/root.html | 144 + docs/modules/tools.html | 233 + docs/modules/twilio.html | 181 + docs/modules/twilio_errors.html | 201 + docs/modules/voice.html | 3706 ++++ es5/twilio.d.ts | 11 + es5/twilio.js | 14 + es5/twilio/asyncQueue.d.ts | 41 + es5/twilio/asyncQueue.js | 115 + es5/twilio/audiohelper.d.ts | 382 + es5/twilio/audiohelper.js | 740 + es5/twilio/audioplayer/audioplayer.d.ts | 150 + es5/twilio/audioplayer/audioplayer.js | 393 + .../audioplayer/chromeaudiocontext.d.ts | 15 + es5/twilio/audioplayer/chromeaudiocontext.js | 3 + es5/twilio/audioplayer/deferred.d.ts | 13 + es5/twilio/audioplayer/deferred.js | 30 + es5/twilio/audioplayer/eventtarget.d.ts | 13 + es5/twilio/audioplayer/eventtarget.js | 38 + es5/twilio/audioprocessor.d.ts | 97 + es5/twilio/audioprocessor.js | 7 + es5/twilio/audioprocessoreventobserver.d.ts | 18 + es5/twilio/audioprocessoreventobserver.js | 51 + es5/twilio/backoff.d.ts | 20 + es5/twilio/backoff.js | 89 + es5/twilio/call.d.ts | 678 + es5/twilio/call.js | 1355 ++ es5/twilio/constants.d.ts | 14 + es5/twilio/constants.js | 22 + es5/twilio/deferred.d.ts | 38 + es5/twilio/deferred.js | 47 + es5/twilio/device.d.ts | 778 + es5/twilio/device.js | 1477 ++ es5/twilio/dialtonePlayer.d.ts | 19 + es5/twilio/dialtonePlayer.js | 75 + es5/twilio/errors/generated.d.ts | 478 + es5/twilio/errors/generated.js | 1050 ++ es5/twilio/errors/index.d.ts | 27 + es5/twilio/errors/index.js | 142 + es5/twilio/errors/twilioError.d.ts | 41 + es5/twilio/errors/twilioError.js | 40 + es5/twilio/eventpublisher.d.ts | 35 + es5/twilio/eventpublisher.js | 294 + es5/twilio/log.d.ts | 77 + es5/twilio/log.js | 119 + es5/twilio/outputdevicecollection.d.ts | 48 + es5/twilio/outputdevicecollection.js | 121 + es5/twilio/preflight/preflight.d.ts | 556 + es5/twilio/preflight/preflight.js | 628 + es5/twilio/preflight/timing.d.ts | 52 + es5/twilio/preflight/timing.js | 8 + es5/twilio/pstream.d.ts | 26 + es5/twilio/pstream.js | 285 + es5/twilio/regions.d.ts | 109 + es5/twilio/regions.js | 194 + es5/twilio/request.d.ts | 17 + es5/twilio/request.js | 48 + es5/twilio/rtc/getusermedia.d.ts | 2 + es5/twilio/rtc/getusermedia.js | 41 + es5/twilio/rtc/icecandidate.d.ts | 97 + es5/twilio/rtc/icecandidate.js | 63 + es5/twilio/rtc/index.d.ts | 9 + es5/twilio/rtc/index.js | 21 + es5/twilio/rtc/mockrtcstatsreport.d.ts | 20 + es5/twilio/rtc/mockrtcstatsreport.js | 410 + es5/twilio/rtc/mos.d.ts | 27 + es5/twilio/rtc/mos.js | 72 + es5/twilio/rtc/peerconnection.d.ts | 14 + es5/twilio/rtc/peerconnection.js | 1033 ++ es5/twilio/rtc/rtcpc.d.ts | 10 + es5/twilio/rtc/rtcpc.js | 203 + es5/twilio/rtc/sample.d.ts | 96 + es5/twilio/rtc/sample.js | 8 + es5/twilio/rtc/sdp.d.ts | 15 + es5/twilio/rtc/sdp.js | 170 + es5/twilio/rtc/stats.d.ts | 19 + es5/twilio/rtc/stats.js | 217 + es5/twilio/rtc/warning.d.ts | 44 + es5/twilio/rtc/warning.js | 3 + es5/twilio/shims/eventtarget.d.ts | 7 + es5/twilio/shims/eventtarget.js | 50 + es5/twilio/shims/mediadeviceinfo.d.ts | 9 + es5/twilio/shims/mediadeviceinfo.js | 21 + es5/twilio/sound.d.ts | 18 + es5/twilio/sound.js | 211 + es5/twilio/statsMonitor.d.ts | 289 + es5/twilio/statsMonitor.js | 485 + es5/twilio/util.d.ts | 32 + es5/twilio/util.js | 144 + es5/twilio/uuid.d.ts | 6 + es5/twilio/uuid.js | 40 + es5/twilio/wstransport.d.ts | 237 + es5/twilio/wstransport.js | 487 + esm/index.d.ts | 11 + esm/index.js | 7 + esm/twilio/asyncQueue.d.ts | 41 + esm/twilio/asyncQueue.js | 73 + esm/twilio/audiohelper.d.ts | 382 + esm/twilio/audiohelper.js | 703 + esm/twilio/audioplayer/audioplayer.d.ts | 150 + esm/twilio/audioplayer/audioplayer.js | 273 + .../audioplayer/chromeaudiocontext.d.ts | 15 + esm/twilio/audioplayer/chromeaudiocontext.js | 2 + esm/twilio/audioplayer/deferred.d.ts | 13 + esm/twilio/audioplayer/deferred.js | 17 + esm/twilio/audioplayer/eventtarget.d.ts | 13 + esm/twilio/audioplayer/eventtarget.js | 22 + esm/twilio/audioprocessor.d.ts | 97 + esm/twilio/audioprocessor.js | 6 + esm/twilio/audioprocessoreventobserver.d.ts | 18 + esm/twilio/audioprocessoreventobserver.js | 31 + esm/twilio/backoff.d.ts | 20 + esm/twilio/backoff.js | 70 + esm/twilio/call.d.ts | 678 + esm/twilio/call.js | 1308 ++ esm/twilio/constants.d.ts | 14 + esm/twilio/constants.js | 15 + esm/twilio/deferred.d.ts | 38 + esm/twilio/deferred.js | 38 + esm/twilio/device.d.ts | 778 + esm/twilio/device.js | 1295 ++ esm/twilio/dialtonePlayer.d.ts | 19 + esm/twilio/dialtonePlayer.js | 69 + esm/twilio/errors/generated.d.ts | 478 + esm/twilio/errors/generated.js | 923 + esm/twilio/errors/index.d.ts | 27 + esm/twilio/errors/index.js | 111 + esm/twilio/errors/twilioError.d.ts | 41 + esm/twilio/errors/twilioError.js | 21 + esm/twilio/eventpublisher.d.ts | 35 + esm/twilio/eventpublisher.js | 274 + esm/twilio/log.d.ts | 77 + esm/twilio/log.js | 88 + esm/twilio/outputdevicecollection.d.ts | 48 + esm/twilio/outputdevicecollection.js | 115 + esm/twilio/preflight/preflight.d.ts | 556 + esm/twilio/preflight/preflight.js | 518 + esm/twilio/preflight/timing.d.ts | 52 + esm/twilio/preflight/timing.js | 7 + esm/twilio/pstream.d.ts | 26 + esm/twilio/pstream.js | 266 + esm/twilio/regions.d.ts | 109 + esm/twilio/regions.js | 186 + esm/twilio/request.d.ts | 17 + esm/twilio/request.js | 43 + esm/twilio/rtc/getusermedia.d.ts | 2 + esm/twilio/rtc/getusermedia.js | 39 + esm/twilio/rtc/icecandidate.d.ts | 97 + esm/twilio/rtc/icecandidate.js | 57 + esm/twilio/rtc/index.d.ts | 9 + esm/twilio/rtc/index.js | 16 + esm/twilio/rtc/mockrtcstatsreport.d.ts | 20 + esm/twilio/rtc/mockrtcstatsreport.js | 403 + esm/twilio/rtc/mos.d.ts | 27 + esm/twilio/rtc/mos.js | 67 + esm/twilio/rtc/peerconnection.d.ts | 14 + esm/twilio/rtc/peerconnection.js | 1020 ++ esm/twilio/rtc/rtcpc.d.ts | 10 + esm/twilio/rtc/rtcpc.js | 198 + esm/twilio/rtc/sample.d.ts | 96 + esm/twilio/rtc/sample.js | 7 + esm/twilio/rtc/sdp.d.ts | 15 + esm/twilio/rtc/sdp.js | 164 + esm/twilio/rtc/stats.d.ts | 19 + esm/twilio/rtc/stats.js | 205 + esm/twilio/rtc/warning.d.ts | 44 + esm/twilio/rtc/warning.js | 2 + esm/twilio/shims/eventtarget.d.ts | 7 + esm/twilio/shims/eventtarget.js | 46 + esm/twilio/shims/mediadeviceinfo.d.ts | 9 + esm/twilio/shims/mediadeviceinfo.js | 18 + esm/twilio/sound.d.ts | 18 + esm/twilio/sound.js | 205 + esm/twilio/statsMonitor.d.ts | 289 + esm/twilio/statsMonitor.js | 444 + esm/twilio/util.d.ts | 32 + esm/twilio/util.js | 131 + esm/twilio/uuid.d.ts | 6 + esm/twilio/uuid.js | 36 + esm/twilio/wstransport.d.ts | 237 + esm/twilio/wstransport.js | 448 + lib/twilio/constants.ts | 22 + package.json | 2 +- 270 files changed, 90226 insertions(+), 1 deletion(-) create mode 100644 dist/twilio.js create mode 100644 dist/twilio.min.js create mode 100644 docs/assets/css/main.css create mode 100644 docs/assets/css/main.css.map create mode 100644 docs/assets/images/icons.png create mode 100644 docs/assets/images/icons@2x.png create mode 100644 docs/assets/images/widgets.png create mode 100644 docs/assets/images/widgets@2x.png create mode 100644 docs/assets/js/lib/backbone-1.1.2.min.js create mode 100644 docs/assets/js/lib/jquery-2.1.1.min.js create mode 100644 docs/assets/js/lib/lunr.min.js create mode 100644 docs/assets/js/lib/underscore-1.6.0.min.js create mode 100644 docs/assets/js/main.js create mode 100644 docs/assets/js/search.json create mode 100644 docs/classes/tools.dialtoneplayer.html create mode 100644 docs/classes/tools.wstransport.html create mode 100644 docs/classes/twilio.backoff.html create mode 100644 docs/classes/twilio_errors.acquisitionfailederror.html create mode 100644 docs/classes/twilio_errors.clientlocaldescfailed.html create mode 100644 docs/classes/twilio_errors.clientremotedescfailed.html create mode 100644 docs/classes/twilio_errors.connectiondisconnected.html create mode 100644 docs/classes/twilio_errors.connectionerror-1.html create mode 100644 docs/classes/twilio_errors.connectionerror.html create mode 100644 docs/classes/twilio_errors.invalidargumenterror.html create mode 100644 docs/classes/twilio_errors.invalidstateerror.html create mode 100644 docs/classes/twilio_errors.notsupportederror.html create mode 100644 docs/classes/twilio_errors.permissiondeniederror.html create mode 100644 docs/classes/voice.asyncqueue.html create mode 100644 docs/classes/voice.audiohelper.html create mode 100644 docs/classes/voice.call.html create mode 100644 docs/classes/voice.deferred-1.html create mode 100644 docs/classes/voice.deferred.html create mode 100644 docs/classes/voice.device.html create mode 100644 docs/classes/voice.eventpublisher.html create mode 100644 docs/classes/voice.eventtarget.html create mode 100644 docs/classes/voice.icecandidate.html create mode 100644 docs/classes/voice.mediadeviceinfoshim.html create mode 100644 docs/classes/voice.outputdevicecollection.html create mode 100644 docs/classes/voice.preflighttest.html create mode 100644 docs/classes/voice.pstream.html create mode 100644 docs/classes/voice.statsmonitor.html create mode 100644 docs/classes/voice.twilioerror.html create mode 100644 docs/enums/tools.wstransportstate.html create mode 100644 docs/enums/voice.call.calldirection.html create mode 100644 docs/enums/voice.call.codec.html create mode 100644 docs/enums/voice.call.feedbackissue.html create mode 100644 docs/enums/voice.call.feedbackscore.html create mode 100644 docs/enums/voice.call.icegatheringfailurereason.html create mode 100644 docs/enums/voice.call.mediafailure.html create mode 100644 docs/enums/voice.call.messagetype.html create mode 100644 docs/enums/voice.call.state.html create mode 100644 docs/enums/voice.device.eventname.html create mode 100644 docs/enums/voice.device.soundname.html create mode 100644 docs/enums/voice.device.state.html create mode 100644 docs/enums/voice.preflighttest.callquality.html create mode 100644 docs/enums/voice.preflighttest.events.html create mode 100644 docs/enums/voice.preflighttest.status.html create mode 100644 docs/globals.html create mode 100644 docs/index.html create mode 100644 docs/interfaces/tools.imessageevent.html create mode 100644 docs/interfaces/tools.iwstransportconstructoroptions.html create mode 100644 docs/interfaces/voice.asyncqueue.operation.html create mode 100644 docs/interfaces/voice.audiooutput.html create mode 100644 docs/interfaces/voice.audioprocessor.html create mode 100644 docs/interfaces/voice.call.acceptoptions.html create mode 100644 docs/interfaces/voice.call.callerinfo.html create mode 100644 docs/interfaces/voice.call.message.html create mode 100644 docs/interfaces/voice.chromeaudiocontext.html create mode 100644 docs/interfaces/voice.chromehtmlaudioelement.html create mode 100644 docs/interfaces/voice.device.connectoptions.html create mode 100644 docs/interfaces/voice.device.options.html create mode 100644 docs/interfaces/voice.iaudioplayeroptions.html create mode 100644 docs/interfaces/voice.mediastreamaudiodestinationnode.html create mode 100644 docs/interfaces/voice.networktiming.html create mode 100644 docs/interfaces/voice.preflighttest.extendedoptions.html create mode 100644 docs/interfaces/voice.preflighttest.options.html create mode 100644 docs/interfaces/voice.preflighttest.report.html create mode 100644 docs/interfaces/voice.preflighttest.rtcicecandidatestatsreport.html create mode 100644 docs/interfaces/voice.preflighttest.rtcselectedicecandidatepairstats.html create mode 100644 docs/interfaces/voice.preflighttest.rtcstats.html create mode 100644 docs/interfaces/voice.preflighttest.stats.html create mode 100644 docs/interfaces/voice.preflighttest.warning.html create mode 100644 docs/interfaces/voice.rtcsample.html create mode 100644 docs/interfaces/voice.rtcsampletotals.html create mode 100644 docs/interfaces/voice.rtcwarning.html create mode 100644 docs/interfaces/voice.thresholdwarningdata.html create mode 100644 docs/interfaces/voice.timemeasurement.html create mode 100644 docs/modules/root.html create mode 100644 docs/modules/tools.html create mode 100644 docs/modules/twilio.html create mode 100644 docs/modules/twilio_errors.html create mode 100644 docs/modules/voice.html create mode 100644 es5/twilio.d.ts create mode 100644 es5/twilio.js create mode 100644 es5/twilio/asyncQueue.d.ts create mode 100644 es5/twilio/asyncQueue.js create mode 100644 es5/twilio/audiohelper.d.ts create mode 100644 es5/twilio/audiohelper.js create mode 100644 es5/twilio/audioplayer/audioplayer.d.ts create mode 100644 es5/twilio/audioplayer/audioplayer.js create mode 100644 es5/twilio/audioplayer/chromeaudiocontext.d.ts create mode 100644 es5/twilio/audioplayer/chromeaudiocontext.js create mode 100644 es5/twilio/audioplayer/deferred.d.ts create mode 100644 es5/twilio/audioplayer/deferred.js create mode 100644 es5/twilio/audioplayer/eventtarget.d.ts create mode 100644 es5/twilio/audioplayer/eventtarget.js create mode 100644 es5/twilio/audioprocessor.d.ts create mode 100644 es5/twilio/audioprocessor.js create mode 100644 es5/twilio/audioprocessoreventobserver.d.ts create mode 100644 es5/twilio/audioprocessoreventobserver.js create mode 100644 es5/twilio/backoff.d.ts create mode 100644 es5/twilio/backoff.js create mode 100644 es5/twilio/call.d.ts create mode 100644 es5/twilio/call.js create mode 100644 es5/twilio/constants.d.ts create mode 100644 es5/twilio/constants.js create mode 100644 es5/twilio/deferred.d.ts create mode 100644 es5/twilio/deferred.js create mode 100644 es5/twilio/device.d.ts create mode 100644 es5/twilio/device.js create mode 100644 es5/twilio/dialtonePlayer.d.ts create mode 100644 es5/twilio/dialtonePlayer.js create mode 100644 es5/twilio/errors/generated.d.ts create mode 100644 es5/twilio/errors/generated.js create mode 100644 es5/twilio/errors/index.d.ts create mode 100644 es5/twilio/errors/index.js create mode 100644 es5/twilio/errors/twilioError.d.ts create mode 100644 es5/twilio/errors/twilioError.js create mode 100644 es5/twilio/eventpublisher.d.ts create mode 100644 es5/twilio/eventpublisher.js create mode 100644 es5/twilio/log.d.ts create mode 100644 es5/twilio/log.js create mode 100644 es5/twilio/outputdevicecollection.d.ts create mode 100644 es5/twilio/outputdevicecollection.js create mode 100644 es5/twilio/preflight/preflight.d.ts create mode 100644 es5/twilio/preflight/preflight.js create mode 100644 es5/twilio/preflight/timing.d.ts create mode 100644 es5/twilio/preflight/timing.js create mode 100644 es5/twilio/pstream.d.ts create mode 100644 es5/twilio/pstream.js create mode 100644 es5/twilio/regions.d.ts create mode 100644 es5/twilio/regions.js create mode 100644 es5/twilio/request.d.ts create mode 100644 es5/twilio/request.js create mode 100644 es5/twilio/rtc/getusermedia.d.ts create mode 100644 es5/twilio/rtc/getusermedia.js create mode 100644 es5/twilio/rtc/icecandidate.d.ts create mode 100644 es5/twilio/rtc/icecandidate.js create mode 100644 es5/twilio/rtc/index.d.ts create mode 100644 es5/twilio/rtc/index.js create mode 100644 es5/twilio/rtc/mockrtcstatsreport.d.ts create mode 100644 es5/twilio/rtc/mockrtcstatsreport.js create mode 100644 es5/twilio/rtc/mos.d.ts create mode 100644 es5/twilio/rtc/mos.js create mode 100644 es5/twilio/rtc/peerconnection.d.ts create mode 100644 es5/twilio/rtc/peerconnection.js create mode 100644 es5/twilio/rtc/rtcpc.d.ts create mode 100644 es5/twilio/rtc/rtcpc.js create mode 100644 es5/twilio/rtc/sample.d.ts create mode 100644 es5/twilio/rtc/sample.js create mode 100644 es5/twilio/rtc/sdp.d.ts create mode 100644 es5/twilio/rtc/sdp.js create mode 100644 es5/twilio/rtc/stats.d.ts create mode 100644 es5/twilio/rtc/stats.js create mode 100644 es5/twilio/rtc/warning.d.ts create mode 100644 es5/twilio/rtc/warning.js create mode 100644 es5/twilio/shims/eventtarget.d.ts create mode 100644 es5/twilio/shims/eventtarget.js create mode 100644 es5/twilio/shims/mediadeviceinfo.d.ts create mode 100644 es5/twilio/shims/mediadeviceinfo.js create mode 100644 es5/twilio/sound.d.ts create mode 100644 es5/twilio/sound.js create mode 100644 es5/twilio/statsMonitor.d.ts create mode 100644 es5/twilio/statsMonitor.js create mode 100644 es5/twilio/util.d.ts create mode 100644 es5/twilio/util.js create mode 100644 es5/twilio/uuid.d.ts create mode 100644 es5/twilio/uuid.js create mode 100644 es5/twilio/wstransport.d.ts create mode 100644 es5/twilio/wstransport.js create mode 100644 esm/index.d.ts create mode 100644 esm/index.js create mode 100644 esm/twilio/asyncQueue.d.ts create mode 100644 esm/twilio/asyncQueue.js create mode 100644 esm/twilio/audiohelper.d.ts create mode 100644 esm/twilio/audiohelper.js create mode 100644 esm/twilio/audioplayer/audioplayer.d.ts create mode 100644 esm/twilio/audioplayer/audioplayer.js create mode 100644 esm/twilio/audioplayer/chromeaudiocontext.d.ts create mode 100644 esm/twilio/audioplayer/chromeaudiocontext.js create mode 100644 esm/twilio/audioplayer/deferred.d.ts create mode 100644 esm/twilio/audioplayer/deferred.js create mode 100644 esm/twilio/audioplayer/eventtarget.d.ts create mode 100644 esm/twilio/audioplayer/eventtarget.js create mode 100644 esm/twilio/audioprocessor.d.ts create mode 100644 esm/twilio/audioprocessor.js create mode 100644 esm/twilio/audioprocessoreventobserver.d.ts create mode 100644 esm/twilio/audioprocessoreventobserver.js create mode 100644 esm/twilio/backoff.d.ts create mode 100644 esm/twilio/backoff.js create mode 100644 esm/twilio/call.d.ts create mode 100644 esm/twilio/call.js create mode 100644 esm/twilio/constants.d.ts create mode 100644 esm/twilio/constants.js create mode 100644 esm/twilio/deferred.d.ts create mode 100644 esm/twilio/deferred.js create mode 100644 esm/twilio/device.d.ts create mode 100644 esm/twilio/device.js create mode 100644 esm/twilio/dialtonePlayer.d.ts create mode 100644 esm/twilio/dialtonePlayer.js create mode 100644 esm/twilio/errors/generated.d.ts create mode 100644 esm/twilio/errors/generated.js create mode 100644 esm/twilio/errors/index.d.ts create mode 100644 esm/twilio/errors/index.js create mode 100644 esm/twilio/errors/twilioError.d.ts create mode 100644 esm/twilio/errors/twilioError.js create mode 100644 esm/twilio/eventpublisher.d.ts create mode 100644 esm/twilio/eventpublisher.js create mode 100644 esm/twilio/log.d.ts create mode 100644 esm/twilio/log.js create mode 100644 esm/twilio/outputdevicecollection.d.ts create mode 100644 esm/twilio/outputdevicecollection.js create mode 100644 esm/twilio/preflight/preflight.d.ts create mode 100644 esm/twilio/preflight/preflight.js create mode 100644 esm/twilio/preflight/timing.d.ts create mode 100644 esm/twilio/preflight/timing.js create mode 100644 esm/twilio/pstream.d.ts create mode 100644 esm/twilio/pstream.js create mode 100644 esm/twilio/regions.d.ts create mode 100644 esm/twilio/regions.js create mode 100644 esm/twilio/request.d.ts create mode 100644 esm/twilio/request.js create mode 100644 esm/twilio/rtc/getusermedia.d.ts create mode 100644 esm/twilio/rtc/getusermedia.js create mode 100644 esm/twilio/rtc/icecandidate.d.ts create mode 100644 esm/twilio/rtc/icecandidate.js create mode 100644 esm/twilio/rtc/index.d.ts create mode 100644 esm/twilio/rtc/index.js create mode 100644 esm/twilio/rtc/mockrtcstatsreport.d.ts create mode 100644 esm/twilio/rtc/mockrtcstatsreport.js create mode 100644 esm/twilio/rtc/mos.d.ts create mode 100644 esm/twilio/rtc/mos.js create mode 100644 esm/twilio/rtc/peerconnection.d.ts create mode 100644 esm/twilio/rtc/peerconnection.js create mode 100644 esm/twilio/rtc/rtcpc.d.ts create mode 100644 esm/twilio/rtc/rtcpc.js create mode 100644 esm/twilio/rtc/sample.d.ts create mode 100644 esm/twilio/rtc/sample.js create mode 100644 esm/twilio/rtc/sdp.d.ts create mode 100644 esm/twilio/rtc/sdp.js create mode 100644 esm/twilio/rtc/stats.d.ts create mode 100644 esm/twilio/rtc/stats.js create mode 100644 esm/twilio/rtc/warning.d.ts create mode 100644 esm/twilio/rtc/warning.js create mode 100644 esm/twilio/shims/eventtarget.d.ts create mode 100644 esm/twilio/shims/eventtarget.js create mode 100644 esm/twilio/shims/mediadeviceinfo.d.ts create mode 100644 esm/twilio/shims/mediadeviceinfo.js create mode 100644 esm/twilio/sound.d.ts create mode 100644 esm/twilio/sound.js create mode 100644 esm/twilio/statsMonitor.d.ts create mode 100644 esm/twilio/statsMonitor.js create mode 100644 esm/twilio/util.d.ts create mode 100644 esm/twilio/util.js create mode 100644 esm/twilio/uuid.d.ts create mode 100644 esm/twilio/uuid.js create mode 100644 esm/twilio/wstransport.d.ts create mode 100644 esm/twilio/wstransport.js create mode 100644 lib/twilio/constants.ts diff --git a/dist/twilio.js b/dist/twilio.js new file mode 100644 index 00000000..459632c0 --- /dev/null +++ b/dist/twilio.js @@ -0,0 +1,14777 @@ +/*! @twilio/voice-sdk.js 2.10.0 + +The following license applies to all parts of this software except as +documented below. + + Copyright (C) 2015-2024 Twilio, inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +This software includes rtcpeerconnection-shim under the following (BSD 3-Clause) license. + + Copyright (c) 2017 Philipp Hancke. All rights reserved. + + Copyright (c) 2014, The WebRTC project authors. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Philipp Hancke nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This software includes backoff under the following (MIT) license. + + Copyright (C) 2012 Mathieu Turcotte + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +This software includes loglevel under the following (MIT) license. + + Copyright (c) 2013 Tim Perry + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + */ +(function(root) { + var bundle = (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AsyncQueue = void 0; +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +var deferred_1 = require("./deferred"); +/** + * Queue async operations and executes them synchronously. + */ +var AsyncQueue = /** @class */ (function () { + function AsyncQueue() { + /** + * The list of async operations in this queue + */ + this._operations = []; + } + /** + * Adds the async operation to the queue + * @param callback An async callback that returns a promise + * @returns A promise that will get resolved or rejected after executing the callback + */ + AsyncQueue.prototype.enqueue = function (callback) { + var hasPending = !!this._operations.length; + var deferred = new deferred_1.default(); + this._operations.push({ deferred: deferred, callback: callback }); + if (!hasPending) { + this._processQueue(); + } + return deferred.promise; + }; + /** + * Start processing the queue. This executes the first item and removes it after. + * Then do the same for next items until the queue is emptied. + */ + AsyncQueue.prototype._processQueue = function () { + return __awaiter(this, void 0, void 0, function () { + var _a, deferred, callback, result, error, hasResolved, e_1; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!this._operations.length) return [3 /*break*/, 5]; + _a = this._operations[0], deferred = _a.deferred, callback = _a.callback; + result = void 0; + error = void 0; + hasResolved = void 0; + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + return [4 /*yield*/, callback()]; + case 2: + result = _b.sent(); + hasResolved = true; + return [3 /*break*/, 4]; + case 3: + e_1 = _b.sent(); + error = e_1; + return [3 /*break*/, 4]; + case 4: + // Remove the item + this._operations.shift(); + if (hasResolved) { + deferred.resolve(result); + } + else { + deferred.reject(error); + } + return [3 /*break*/, 0]; + case 5: return [2 /*return*/]; + } + }); + }); + }; + return AsyncQueue; +}()); +exports.AsyncQueue = AsyncQueue; + +},{"./deferred":11}],3:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @packageDocumentation + * @module Voice + */ +var events_1 = require("events"); +var device_1 = require("./device"); +var errors_1 = require("./errors"); +var log_1 = require("./log"); +var outputdevicecollection_1 = require("./outputdevicecollection"); +var mediadeviceinfo_1 = require("./shims/mediadeviceinfo"); +var util_1 = require("./util"); +/** + * Aliases for audio kinds, used for labelling. + * @private + */ +var kindAliases = { + audioinput: 'Audio Input', + audiooutput: 'Audio Output', +}; +/** + * Provides input and output audio-based functionality in one convenient class. + * @publicapi + */ +var AudioHelper = /** @class */ (function (_super) { + __extends(AudioHelper, _super); + /** + * @constructor + * @private + * @param onActiveOutputsChanged - A callback to be called when the user changes the active output devices. + * @param onActiveInputChanged - A callback to be called when the user changes the active input device. + * @param [options] + */ + function AudioHelper(onActiveOutputsChanged, onActiveInputChanged, options) { + var _a; + var _this = _super.call(this) || this; + /** + * A Map of all audio input devices currently available to the browser by their device ID. + */ + _this.availableInputDevices = new Map(); + /** + * A Map of all audio output devices currently available to the browser by their device ID. + */ + _this.availableOutputDevices = new Map(); + /** + * The currently set audio constraints set by setAudioConstraints(). + */ + _this._audioConstraints = null; + /** + * The audio stream of the default device. + * This is populated when _openDefaultDeviceWithConstraints is called, + * See _selectedInputDeviceStream for differences. + * TODO: Combine these two workflows (3.x?) + */ + _this._defaultInputDeviceStream = null; + /** + * Whether each sound is enabled. + */ + _this._enabledSounds = (_a = {}, + _a[device_1.default.SoundName.Disconnect] = true, + _a[device_1.default.SoundName.Incoming] = true, + _a[device_1.default.SoundName.Outgoing] = true, + _a); + /** + * The current input device. + */ + _this._inputDevice = null; + /** + * Whether the {@link AudioHelper} is currently polling the input stream's volume. + */ + _this._isPollingInputVolume = false; + /** + * An instance of Logger to use. + */ + _this._log = new log_1.default('AudioHelper'); + /** + * Internal reference to the processed stream + */ + _this._processedStream = null; + /** + * The selected input stream coming from the microphone device. + * This is populated when the setInputDevice is called, meaning, + * the end user manually selected it, which is different than + * the defaultInputDeviceStream. + * TODO: Combine these two workflows (3.x?) + */ + _this._selectedInputDeviceStream = null; + /** + * A record of unknown devices (Devices without labels) + */ + _this._unknownDeviceIndexes = { + audioinput: {}, + audiooutput: {}, + }; + /** + * Update the available input and output devices + * @private + */ + _this._updateAvailableDevices = function () { + if (!_this._mediaDevices || !_this._enumerateDevices) { + return Promise.reject('Enumeration not supported'); + } + return _this._enumerateDevices().then(function (devices) { + _this._updateDevices(devices.filter(function (d) { return d.kind === 'audiooutput'; }), _this.availableOutputDevices, _this._removeLostOutput); + _this._updateDevices(devices.filter(function (d) { return d.kind === 'audioinput'; }), _this.availableInputDevices, _this._removeLostInput); + var defaultDevice = _this.availableOutputDevices.get('default') + || Array.from(_this.availableOutputDevices.values())[0]; + [_this.speakerDevices, _this.ringtoneDevices].forEach(function (outputDevices) { + if (!outputDevices.get().size && _this.availableOutputDevices.size && _this.isOutputSelectionSupported) { + outputDevices.set(defaultDevice.deviceId) + .catch(function (reason) { + _this._log.warn("Unable to set audio output devices. " + reason); + }); + } + }); + }); + }; + /** + * Remove an input device from inputs + * @param lostDevice + * @returns Whether the device was active + */ + _this._removeLostInput = function (lostDevice) { + if (!_this.inputDevice || _this.inputDevice.deviceId !== lostDevice.deviceId) { + return false; + } + _this._destroyProcessedStream(); + _this._replaceStream(null); + _this._inputDevice = null; + _this._maybeStopPollingVolume(); + var defaultDevice = _this.availableInputDevices.get('default') + || Array.from(_this.availableInputDevices.values())[0]; + if (defaultDevice) { + _this.setInputDevice(defaultDevice.deviceId); + } + return true; + }; + /** + * Remove an input device from outputs + * @param lostDevice + * @returns Whether the device was active + */ + _this._removeLostOutput = function (lostDevice) { + var wasSpeakerLost = _this.speakerDevices.delete(lostDevice); + var wasRingtoneLost = _this.ringtoneDevices.delete(lostDevice); + return wasSpeakerLost || wasRingtoneLost; + }; + options = Object.assign({ + AudioContext: typeof AudioContext !== 'undefined' && AudioContext, + setSinkId: typeof HTMLAudioElement !== 'undefined' && HTMLAudioElement.prototype.setSinkId, + }, options); + _this._updateUserOptions(options); + _this._audioProcessorEventObserver = options.audioProcessorEventObserver; + _this._mediaDevices = options.mediaDevices || navigator.mediaDevices; + _this._onActiveInputChanged = onActiveInputChanged; + _this._enumerateDevices = typeof options.enumerateDevices === 'function' + ? options.enumerateDevices + : _this._mediaDevices && _this._mediaDevices.enumerateDevices.bind(_this._mediaDevices); + var isAudioContextSupported = !!(options.AudioContext || options.audioContext); + var isEnumerationSupported = !!_this._enumerateDevices; + if (options.enabledSounds) { + _this._enabledSounds = options.enabledSounds; + } + var isSetSinkSupported = typeof options.setSinkId === 'function'; + _this.isOutputSelectionSupported = isEnumerationSupported && isSetSinkSupported; + _this.isVolumeSupported = isAudioContextSupported; + if (_this.isVolumeSupported) { + _this._audioContext = options.audioContext || options.AudioContext && new options.AudioContext(); + if (_this._audioContext) { + _this._inputVolumeAnalyser = _this._audioContext.createAnalyser(); + _this._inputVolumeAnalyser.fftSize = 32; + _this._inputVolumeAnalyser.smoothingTimeConstant = 0.3; + } + } + _this.ringtoneDevices = new outputdevicecollection_1.default('ringtone', _this.availableOutputDevices, onActiveOutputsChanged, _this.isOutputSelectionSupported); + _this.speakerDevices = new outputdevicecollection_1.default('speaker', _this.availableOutputDevices, onActiveOutputsChanged, _this.isOutputSelectionSupported); + _this.addListener('newListener', function (eventName) { + if (eventName === 'inputVolume') { + _this._maybeStartPollingVolume(); + } + }); + _this.addListener('removeListener', function (eventName) { + if (eventName === 'inputVolume') { + _this._maybeStopPollingVolume(); + } + }); + _this.once('newListener', function () { + // NOTE (rrowland): Ideally we would only check isEnumerationSupported here, but + // in at least one browser version (Tested in FF48) enumerateDevices actually + // returns bad data for the listed devices. Instead, we check for + // isOutputSelectionSupported to avoid these quirks that may negatively affect customers. + if (!_this.isOutputSelectionSupported) { + _this._log.warn('Warning: This browser does not support audio output selection.'); + } + if (!_this.isVolumeSupported) { + _this._log.warn("Warning: This browser does not support Twilio's volume indicator feature."); + } + }); + if (isEnumerationSupported) { + _this._initializeEnumeration(); + } + return _this; + } + Object.defineProperty(AudioHelper.prototype, "audioConstraints", { + /** + * The currently set audio constraints set by setAudioConstraints(). Starts as null. + */ + get: function () { return this._audioConstraints; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(AudioHelper.prototype, "inputDevice", { + /** + * The active input device. Having no inputDevice specified by `setInputDevice()` + * will disable input selection related functionality. + */ + get: function () { return this._inputDevice; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(AudioHelper.prototype, "inputStream", { + /** + * The current input stream coming from the microphone device or + * the processed audio stream if there is an {@link AudioProcessor}. + */ + get: function () { return this._processedStream || this._selectedInputDeviceStream; }, + enumerable: false, + configurable: true + }); + /** + * Destroy this AudioHelper instance + * @private + */ + AudioHelper.prototype._destroy = function () { + this._stopDefaultInputDeviceStream(); + this._stopSelectedInputDeviceStream(); + this._destroyProcessedStream(); + this._maybeStopPollingVolume(); + this.removeAllListeners(); + this._unbind(); + }; + /** + * Start polling volume if it's supported and there's an input stream to poll. + * @private + */ + AudioHelper.prototype._maybeStartPollingVolume = function () { + var _this = this; + if (!this.isVolumeSupported || !this.inputStream) { + return; + } + this._updateVolumeSource(); + if (this._isPollingInputVolume || !this._inputVolumeAnalyser) { + return; + } + var bufferLength = this._inputVolumeAnalyser.frequencyBinCount; + var buffer = new Uint8Array(bufferLength); + this._isPollingInputVolume = true; + var emitVolume = function () { + if (!_this._isPollingInputVolume) { + return; + } + if (_this._inputVolumeAnalyser) { + _this._inputVolumeAnalyser.getByteFrequencyData(buffer); + var inputVolume = util_1.average(buffer); + _this.emit('inputVolume', inputVolume / 255); + } + requestAnimationFrame(emitVolume); + }; + requestAnimationFrame(emitVolume); + }; + /** + * Stop polling volume if it's currently polling and there are no listeners. + * @private + */ + AudioHelper.prototype._maybeStopPollingVolume = function () { + if (!this.isVolumeSupported) { + return; + } + if (!this._isPollingInputVolume || (this.inputStream && this.listenerCount('inputVolume'))) { + return; + } + if (this._inputVolumeSource) { + this._inputVolumeSource.disconnect(); + delete this._inputVolumeSource; + } + this._isPollingInputVolume = false; + }; + /** + * Call getUserMedia with specified constraints + * @private + */ + AudioHelper.prototype._openDefaultDeviceWithConstraints = function (constraints) { + var _this = this; + this._log.info('Opening default device with constraints', constraints); + return this._getUserMedia(constraints).then(function (stream) { + _this._log.info('Opened default device. Updating available devices.'); + // Ensures deviceId's and labels are populated after the gUM call + // by calling enumerateDevices + _this._updateAvailableDevices().catch(function (error) { + // Ignore error, we don't want to break the call flow + _this._log.warn('Unable to updateAvailableDevices after gUM call', error); + }); + _this._defaultInputDeviceStream = stream; + return _this._maybeCreateProcessedStream(stream); + }); + }; + /** + * Stop the default audio stream + * @private + */ + AudioHelper.prototype._stopDefaultInputDeviceStream = function () { + if (this._defaultInputDeviceStream) { + this._log.info('stopping default device stream'); + this._defaultInputDeviceStream.getTracks().forEach(function (track) { return track.stop(); }); + this._defaultInputDeviceStream = null; + this._destroyProcessedStream(); + } + }; + /** + * Unbind the listeners from mediaDevices. + * @private + */ + AudioHelper.prototype._unbind = function () { + if (!this._mediaDevices || !this._enumerateDevices) { + throw new errors_1.NotSupportedError('Enumeration is not supported'); + } + if (this._mediaDevices.removeEventListener) { + this._mediaDevices.removeEventListener('devicechange', this._updateAvailableDevices); + } + }; + /** + * Update AudioHelper options that can be changed by the user + * @private + */ + AudioHelper.prototype._updateUserOptions = function (options) { + if (typeof options.enumerateDevices === 'function') { + this._enumerateDevices = options.enumerateDevices; + } + if (typeof options.getUserMedia === 'function') { + this._getUserMedia = options.getUserMedia; + } + }; + /** + * Adds an {@link AudioProcessor} object. Once added, the AudioHelper will route + * the input audio stream through the processor before sending the audio + * stream to Twilio. Only one AudioProcessor can be added at this time. + * + * See the {@link AudioProcessor} interface for an example. + * + * @param processor The AudioProcessor to add. + * @returns + */ + AudioHelper.prototype.addProcessor = function (processor) { + this._log.debug('.addProcessor'); + if (this._processor) { + throw new errors_1.NotSupportedError('Adding multiple AudioProcessors is not supported at this time.'); + } + if (typeof processor !== 'object' || processor === null) { + throw new errors_1.InvalidArgumentError('Missing AudioProcessor argument.'); + } + if (typeof processor.createProcessedStream !== 'function') { + throw new errors_1.InvalidArgumentError('Missing createProcessedStream() method.'); + } + if (typeof processor.destroyProcessedStream !== 'function') { + throw new errors_1.InvalidArgumentError('Missing destroyProcessedStream() method.'); + } + this._processor = processor; + this._audioProcessorEventObserver.emit('add'); + return this._restartStreams(); + }; + /** + * Enable or disable the disconnect sound. + * @param doEnable Passing `true` will enable the sound and `false` will disable the sound. + * Not passing this parameter will not alter the enable-status of the sound. + * @returns The enable-status of the sound. + */ + AudioHelper.prototype.disconnect = function (doEnable) { + this._log.debug('.disconnect', doEnable); + return this._maybeEnableSound(device_1.default.SoundName.Disconnect, doEnable); + }; + /** + * Enable or disable the incoming sound. + * @param doEnable Passing `true` will enable the sound and `false` will disable the sound. + * Not passing this parameter will not alter the enable-status of the sound. + * @returns The enable-status of the sound. + */ + AudioHelper.prototype.incoming = function (doEnable) { + this._log.debug('.incoming', doEnable); + return this._maybeEnableSound(device_1.default.SoundName.Incoming, doEnable); + }; + /** + * Enable or disable the outgoing sound. + * @param doEnable Passing `true` will enable the sound and `false` will disable the sound. + * Not passing this parameter will not alter the enable-status of the sound. + * @returns The enable-status of the sound. + */ + AudioHelper.prototype.outgoing = function (doEnable) { + this._log.debug('.outgoing', doEnable); + return this._maybeEnableSound(device_1.default.SoundName.Outgoing, doEnable); + }; + /** + * Removes an {@link AudioProcessor}. Once removed, the AudioHelper will start using + * the audio stream from the selected input device for existing or future calls. + * + * @param processor The AudioProcessor to remove. + * @returns + */ + AudioHelper.prototype.removeProcessor = function (processor) { + this._log.debug('.removeProcessor'); + if (typeof processor !== 'object' || processor === null) { + throw new errors_1.InvalidArgumentError('Missing AudioProcessor argument.'); + } + if (this._processor !== processor) { + throw new errors_1.InvalidArgumentError('Cannot remove an AudioProcessor that has not been previously added.'); + } + this._destroyProcessedStream(); + this._processor = null; + this._audioProcessorEventObserver.emit('remove'); + return this._restartStreams(); + }; + /** + * Set the MediaTrackConstraints to be applied on every getUserMedia call for new input + * device audio. Any deviceId specified here will be ignored. Instead, device IDs should + * be specified using {@link AudioHelper#setInputDevice}. The returned Promise resolves + * when the media is successfully reacquired, or immediately if no input device is set. + * @param audioConstraints - The MediaTrackConstraints to apply. + */ + AudioHelper.prototype.setAudioConstraints = function (audioConstraints) { + this._log.debug('.setAudioConstraints', audioConstraints); + this._audioConstraints = Object.assign({}, audioConstraints); + delete this._audioConstraints.deviceId; + return this.inputDevice + ? this._setInputDevice(this.inputDevice.deviceId, true) + : Promise.resolve(); + }; + /** + * Replace the current input device with a new device by ID. + * @param deviceId - An ID of a device to replace the existing + * input device with. + */ + AudioHelper.prototype.setInputDevice = function (deviceId) { + this._log.debug('.setInputDevice', deviceId); + return this._setInputDevice(deviceId, false); + }; + /** + * Unset the MediaTrackConstraints to be applied on every getUserMedia call for new input + * device audio. The returned Promise resolves when the media is successfully reacquired, + * or immediately if no input device is set. + */ + AudioHelper.prototype.unsetAudioConstraints = function () { + this._log.debug('.unsetAudioConstraints'); + this._audioConstraints = null; + return this.inputDevice + ? this._setInputDevice(this.inputDevice.deviceId, true) + : Promise.resolve(); + }; + /** + * Unset the input device, stopping the tracks. This should only be called when not in a connection, and + * will not allow removal of the input device during a live call. + */ + AudioHelper.prototype.unsetInputDevice = function () { + var _this = this; + this._log.debug('.unsetInputDevice', this.inputDevice); + if (!this.inputDevice) { + return Promise.resolve(); + } + this._destroyProcessedStream(); + return this._onActiveInputChanged(null).then(function () { + _this._replaceStream(null); + _this._inputDevice = null; + _this._maybeStopPollingVolume(); + }); + }; + /** + * Destroys processed stream and update references + */ + AudioHelper.prototype._destroyProcessedStream = function () { + if (this._processor && this._processedStream) { + this._log.info('destroying processed stream'); + var processedStream = this._processedStream; + this._processedStream.getTracks().forEach(function (track) { return track.stop(); }); + this._processedStream = null; + this._processor.destroyProcessedStream(processedStream); + this._audioProcessorEventObserver.emit('destroy'); + } + }; + /** + * Get the index of an un-labeled Device. + * @param mediaDeviceInfo + * @returns The index of the passed MediaDeviceInfo + */ + AudioHelper.prototype._getUnknownDeviceIndex = function (mediaDeviceInfo) { + var id = mediaDeviceInfo.deviceId; + var kind = mediaDeviceInfo.kind; + var index = this._unknownDeviceIndexes[kind][id]; + if (!index) { + index = Object.keys(this._unknownDeviceIndexes[kind]).length + 1; + this._unknownDeviceIndexes[kind][id] = index; + } + return index; + }; + /** + * Initialize output device enumeration. + */ + AudioHelper.prototype._initializeEnumeration = function () { + var _this = this; + if (!this._mediaDevices || !this._enumerateDevices) { + throw new errors_1.NotSupportedError('Enumeration is not supported'); + } + if (this._mediaDevices.addEventListener) { + this._mediaDevices.addEventListener('devicechange', this._updateAvailableDevices); + } + this._updateAvailableDevices().then(function () { + if (!_this.isOutputSelectionSupported) { + return; + } + Promise.all([ + _this.speakerDevices.set('default'), + _this.ringtoneDevices.set('default'), + ]).catch(function (reason) { + _this._log.warn("Warning: Unable to set audio output devices. " + reason); + }); + }); + }; + /** + * Route input stream to the processor if it exists + */ + AudioHelper.prototype._maybeCreateProcessedStream = function (stream) { + var _this = this; + if (this._processor) { + this._log.info('Creating processed stream'); + return this._processor.createProcessedStream(stream).then(function (processedStream) { + _this._processedStream = processedStream; + _this._audioProcessorEventObserver.emit('create'); + return _this._processedStream; + }); + } + return Promise.resolve(stream); + }; + /** + * Set whether the sound is enabled or not + * @param soundName + * @param doEnable + * @returns Whether the sound is enabled or not + */ + AudioHelper.prototype._maybeEnableSound = function (soundName, doEnable) { + if (typeof doEnable !== 'undefined') { + this._enabledSounds[soundName] = doEnable; + } + return this._enabledSounds[soundName]; + }; + /** + * Stop the tracks on the current input stream before replacing it with the passed stream. + * @param stream - The new stream + */ + AudioHelper.prototype._replaceStream = function (stream) { + this._log.info('Replacing with new stream.'); + if (this._selectedInputDeviceStream) { + this._log.info('Old stream detected. Stopping tracks.'); + this._stopSelectedInputDeviceStream(); + } + this._selectedInputDeviceStream = stream; + }; + /** + * Restart the active streams + */ + AudioHelper.prototype._restartStreams = function () { + if (this.inputDevice && this._selectedInputDeviceStream) { + this._log.info('Restarting selected input device'); + return this._setInputDevice(this.inputDevice.deviceId, true); + } + if (this._defaultInputDeviceStream) { + var defaultDevice = this.availableInputDevices.get('default') + || Array.from(this.availableInputDevices.values())[0]; + this._log.info('Restarting default input device, now becoming selected.'); + return this._setInputDevice(defaultDevice.deviceId, true); + } + return Promise.resolve(); + }; + /** + * Replace the current input device with a new device by ID. + * @param deviceId - An ID of a device to replace the existing + * input device with. + * @param forceGetUserMedia - If true, getUserMedia will be called even if + * the specified device is already active. + */ + AudioHelper.prototype._setInputDevice = function (deviceId, forceGetUserMedia) { + var _this = this; + if (typeof deviceId !== 'string') { + return Promise.reject(new errors_1.InvalidArgumentError('Must specify the device to set')); + } + var device = this.availableInputDevices.get(deviceId); + if (!device) { + return Promise.reject(new errors_1.InvalidArgumentError("Device not found: " + deviceId)); + } + this._log.info('Setting input device. ID: ' + deviceId); + if (this._inputDevice && this._inputDevice.deviceId === deviceId && this._selectedInputDeviceStream) { + if (!forceGetUserMedia) { + return Promise.resolve(); + } + // If the currently active track is still in readyState `live`, gUM may return the same track + // rather than returning a fresh track. + this._log.info('Same track detected on setInputDevice, stopping old tracks.'); + this._stopSelectedInputDeviceStream(); + } + // Release the default device in case it was created previously + this._stopDefaultInputDeviceStream(); + var constraints = { audio: Object.assign({ deviceId: { exact: deviceId } }, this.audioConstraints) }; + this._log.info('setInputDevice: getting new tracks.'); + return this._getUserMedia(constraints).then(function (originalStream) { + _this._destroyProcessedStream(); + return _this._maybeCreateProcessedStream(originalStream).then(function (newStream) { + _this._log.info('setInputDevice: invoking _onActiveInputChanged.'); + return _this._onActiveInputChanged(newStream).then(function () { + _this._replaceStream(originalStream); + _this._inputDevice = device; + _this._maybeStartPollingVolume(); + }); + }); + }); + }; + /** + * Stop the selected audio stream + */ + AudioHelper.prototype._stopSelectedInputDeviceStream = function () { + if (this._selectedInputDeviceStream) { + this._log.info('Stopping selected device stream'); + this._selectedInputDeviceStream.getTracks().forEach(function (track) { return track.stop(); }); + } + }; + /** + * Update a set of devices. + * @param updatedDevices - An updated list of available Devices + * @param availableDevices - The previous list of available Devices + * @param removeLostDevice - The method to call if a previously available Device is + * no longer available. + */ + AudioHelper.prototype._updateDevices = function (updatedDevices, availableDevices, removeLostDevice) { + var _this = this; + var updatedDeviceIds = updatedDevices.map(function (d) { return d.deviceId; }); + var knownDeviceIds = Array.from(availableDevices.values()).map(function (d) { return d.deviceId; }); + var lostActiveDevices = []; + // Remove lost devices + var lostDeviceIds = util_1.difference(knownDeviceIds, updatedDeviceIds); + lostDeviceIds.forEach(function (lostDeviceId) { + var lostDevice = availableDevices.get(lostDeviceId); + if (lostDevice) { + availableDevices.delete(lostDeviceId); + if (removeLostDevice(lostDevice)) { + lostActiveDevices.push(lostDevice); + } + } + }); + // Add any new devices, or devices with updated labels + var deviceChanged = false; + updatedDevices.forEach(function (newDevice) { + var existingDevice = availableDevices.get(newDevice.deviceId); + var newMediaDeviceInfo = _this._wrapMediaDeviceInfo(newDevice); + if (!existingDevice || existingDevice.label !== newMediaDeviceInfo.label) { + availableDevices.set(newDevice.deviceId, newMediaDeviceInfo); + deviceChanged = true; + } + }); + if (deviceChanged || lostDeviceIds.length) { + // Force a new gUM in case the underlying tracks of the active stream have changed. One + // reason this might happen is when `default` is selected and set to a USB device, + // then that device is unplugged or plugged back in. We can't check for the 'ended' + // event or readyState because it is asynchronous and may take upwards of 5 seconds, + // in my testing. (rrowland) + if (this.inputDevice !== null && this.inputDevice.deviceId === 'default') { + this._log.warn("Calling getUserMedia after device change to ensure that the tracks of the active device (default) have not gone stale."); + this._setInputDevice(this.inputDevice.deviceId, true); + } + this._log.debug('#deviceChange', lostActiveDevices); + this.emit('deviceChange', lostActiveDevices); + } + }; + /** + * Disconnect the old input volume source, and create and connect a new one with the current + * input stream. + */ + AudioHelper.prototype._updateVolumeSource = function () { + if (!this.inputStream || !this._audioContext || !this._inputVolumeAnalyser) { + return; + } + if (this._inputVolumeSource) { + this._inputVolumeSource.disconnect(); + } + try { + this._inputVolumeSource = this._audioContext.createMediaStreamSource(this.inputStream); + this._inputVolumeSource.connect(this._inputVolumeAnalyser); + } + catch (ex) { + this._log.warn('Unable to update volume source', ex); + delete this._inputVolumeSource; + } + }; + /** + * Convert a MediaDeviceInfo to a IMediaDeviceInfoShim. + * @param mediaDeviceInfo - The info to convert + * @returns The converted shim + */ + AudioHelper.prototype._wrapMediaDeviceInfo = function (mediaDeviceInfo) { + var options = { + deviceId: mediaDeviceInfo.deviceId, + groupId: mediaDeviceInfo.groupId, + kind: mediaDeviceInfo.kind, + label: mediaDeviceInfo.label, + }; + if (!options.label) { + if (options.deviceId === 'default') { + options.label = 'Default'; + } + else { + var index = this._getUnknownDeviceIndex(mediaDeviceInfo); + options.label = "Unknown " + kindAliases[options.kind] + " Device " + index; + } + } + return new mediadeviceinfo_1.default(options); + }; + return AudioHelper; +}(events_1.EventEmitter)); +(function (AudioHelper) { +})(AudioHelper || (AudioHelper = {})); +exports.default = AudioHelper; + +},{"./device":12,"./errors":15,"./log":18,"./outputdevicecollection":19,"./shims/mediadeviceinfo":33,"./util":36,"events":39}],4:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +var deferred_1 = require("./deferred"); +var eventtarget_1 = require("./eventtarget"); +/** + * An {@link AudioPlayer} is an HTMLAudioElement-like object that uses AudioContext + * to circumvent browser limitations. + * @private + */ +var AudioPlayer = /** @class */ (function (_super) { + __extends(AudioPlayer, _super); + /** + * @private + */ + function AudioPlayer(audioContext, srcOrOptions, options) { + if (srcOrOptions === void 0) { srcOrOptions = {}; } + if (options === void 0) { options = {}; } + var _this = _super.call(this) || this; + /** + * The AudioBufferSourceNode of the actively loaded sound. Null if a sound + * has not been loaded yet. This is re-used for each time the sound is + * played. + */ + _this._audioNode = null; + /** + * Whether or not the audio element should loop. If disabled during playback, + * playing continues until the sound ends and then stops looping. + */ + _this._loop = false; + /** + * An Array of deferred-like objects for each pending `play` Promise. When + * .pause() is called or .src is set, all pending play Promises are + * immediately rejected. + */ + _this._pendingPlayDeferreds = []; + /** + * The current sinkId of the device audio is being played through. + */ + _this._sinkId = 'default'; + /** + * The source URL of the sound to play. When set, the currently playing sound will stop. + */ + _this._src = ''; + if (typeof srcOrOptions !== 'string') { + options = srcOrOptions; + } + _this._audioContext = audioContext; + _this._audioElement = new (options.AudioFactory || Audio)(); + _this._bufferPromise = _this._createPlayDeferred().promise; + _this._destination = _this._audioContext.destination; + _this._gainNode = _this._audioContext.createGain(); + _this._gainNode.connect(_this._destination); + _this._XMLHttpRequest = options.XMLHttpRequestFactory || XMLHttpRequest; + _this.addEventListener('canplaythrough', function () { + _this._resolvePlayDeferreds(); + }); + if (typeof srcOrOptions === 'string') { + _this.src = srcOrOptions; + } + return _this; + } + Object.defineProperty(AudioPlayer.prototype, "destination", { + get: function () { return this._destination; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(AudioPlayer.prototype, "loop", { + get: function () { return this._loop; }, + set: function (shouldLoop) { + var self = this; + function pauseAfterPlaythrough() { + self._audioNode.removeEventListener('ended', pauseAfterPlaythrough); + self.pause(); + } + // If a sound is already looping, it should continue playing + // the current playthrough and then stop. + if (!shouldLoop && this.loop && !this.paused) { + this._audioNode.addEventListener('ended', pauseAfterPlaythrough); + } + this._loop = shouldLoop; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(AudioPlayer.prototype, "muted", { + /** + * Whether the audio element is muted. + */ + get: function () { return this._gainNode.gain.value === 0; }, + set: function (shouldBeMuted) { + this._gainNode.gain.value = shouldBeMuted ? 0 : 1; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(AudioPlayer.prototype, "paused", { + /** + * Whether the sound is paused. this._audioNode only exists when sound is playing; + * otherwise AudioPlayer is considered paused. + */ + get: function () { return this._audioNode === null; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(AudioPlayer.prototype, "src", { + get: function () { return this._src; }, + set: function (src) { + this._load(src); + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(AudioPlayer.prototype, "srcObject", { + /** + * The srcObject of the HTMLMediaElement + */ + get: function () { + return this._audioElement.srcObject; + }, + set: function (srcObject) { + this._audioElement.srcObject = srcObject; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(AudioPlayer.prototype, "sinkId", { + get: function () { return this._sinkId; }, + enumerable: false, + configurable: true + }); + /** + * Stop any ongoing playback and reload the source file. + */ + AudioPlayer.prototype.load = function () { + this._load(this._src); + }; + /** + * Pause the audio coming from this AudioPlayer. This will reject any pending + * play Promises. + */ + AudioPlayer.prototype.pause = function () { + if (this.paused) { + return; + } + this._audioElement.pause(); + this._audioNode.stop(); + this._audioNode.disconnect(this._gainNode); + this._audioNode = null; + this._rejectPlayDeferreds(new Error('The play() request was interrupted by a call to pause().')); + }; + /** + * Play the sound. If the buffer hasn't loaded yet, wait for the buffer to load. If + * the source URL is not set yet, this Promise will remain pending until a source + * URL is set. + */ + AudioPlayer.prototype.play = function () { + return __awaiter(this, void 0, void 0, function () { + var buffer; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!!this.paused) return [3 /*break*/, 2]; + return [4 /*yield*/, this._bufferPromise]; + case 1: + _a.sent(); + if (!this.paused) { + return [2 /*return*/]; + } + throw new Error('The play() request was interrupted by a call to pause().'); + case 2: + this._audioNode = this._audioContext.createBufferSource(); + this._audioNode.loop = this.loop; + this._audioNode.addEventListener('ended', function () { + if (_this._audioNode && _this._audioNode.loop) { + return; + } + _this.dispatchEvent('ended'); + }); + return [4 /*yield*/, this._bufferPromise]; + case 3: + buffer = _a.sent(); + if (this.paused) { + throw new Error('The play() request was interrupted by a call to pause().'); + } + this._audioNode.buffer = buffer; + this._audioNode.connect(this._gainNode); + this._audioNode.start(); + if (this._audioElement.srcObject) { + return [2 /*return*/, this._audioElement.play()]; + } + return [2 /*return*/]; + } + }); + }); + }; + /** + * Change which device the sound should play through. + * @param sinkId - The sink of the device to play sound through. + */ + AudioPlayer.prototype.setSinkId = function (sinkId) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (typeof this._audioElement.setSinkId !== 'function') { + throw new Error('This browser does not support setSinkId.'); + } + if (sinkId === this.sinkId) { + return [2 /*return*/]; + } + if (sinkId === 'default') { + if (!this.paused) { + this._gainNode.disconnect(this._destination); + } + this._audioElement.srcObject = null; + this._destination = this._audioContext.destination; + this._gainNode.connect(this._destination); + this._sinkId = sinkId; + return [2 /*return*/]; + } + return [4 /*yield*/, this._audioElement.setSinkId(sinkId)]; + case 1: + _a.sent(); + if (this._audioElement.srcObject) { + return [2 /*return*/]; + } + this._gainNode.disconnect(this._audioContext.destination); + this._destination = this._audioContext.createMediaStreamDestination(); + this._audioElement.srcObject = this._destination.stream; + this._sinkId = sinkId; + this._gainNode.connect(this._destination); + return [2 /*return*/]; + } + }); + }); + }; + /** + * Create a Deferred for a Promise that will be resolved when .src is set or rejected + * when .pause is called. + */ + AudioPlayer.prototype._createPlayDeferred = function () { + var deferred = new deferred_1.default(); + this._pendingPlayDeferreds.push(deferred); + return deferred; + }; + /** + * Stop current playback and load a sound file. + * @param src - The source URL of the file to load + */ + AudioPlayer.prototype._load = function (src) { + var _this = this; + if (this._src && this._src !== src) { + this.pause(); + } + this._src = src; + this._bufferPromise = new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () { + var buffer; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (!src) { + return [2 /*return*/, this._createPlayDeferred().promise]; + } + return [4 /*yield*/, bufferSound(this._audioContext, this._XMLHttpRequest, src)]; + case 1: + buffer = _a.sent(); + this.dispatchEvent('canplaythrough'); + resolve(buffer); + return [2 /*return*/]; + } + }); + }); }); + }; + /** + * Reject all deferreds for the Play promise. + * @param reason + */ + AudioPlayer.prototype._rejectPlayDeferreds = function (reason) { + var deferreds = this._pendingPlayDeferreds; + deferreds.splice(0, deferreds.length).forEach(function (_a) { + var reject = _a.reject; + return reject(reason); + }); + }; + /** + * Resolve all deferreds for the Play promise. + * @param result + */ + AudioPlayer.prototype._resolvePlayDeferreds = function (result) { + var deferreds = this._pendingPlayDeferreds; + deferreds.splice(0, deferreds.length).forEach(function (_a) { + var resolve = _a.resolve; + return resolve(result); + }); + }; + return AudioPlayer; +}(eventtarget_1.default)); +/** + * Use XMLHttpRequest to load the AudioBuffer of a remote audio asset. + * @private + * @param context - The AudioContext to use to decode the audio data + * @param RequestFactory - The XMLHttpRequest factory to build + * @param src - The URL of the audio asset to load. + * @returns A Promise containing the decoded AudioBuffer. + */ +// tslint:disable-next-line:variable-name +function bufferSound(context, RequestFactory, src) { + return __awaiter(this, void 0, void 0, function () { + var request, event; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + request = new RequestFactory(); + request.open('GET', src, true); + request.responseType = 'arraybuffer'; + return [4 /*yield*/, new Promise(function (resolve) { + request.addEventListener('load', resolve); + request.send(); + })]; + case 1: + event = _a.sent(); + // Safari uses a callback here instead of a Promise. + try { + return [2 /*return*/, context.decodeAudioData(event.target.response)]; + } + catch (e) { + return [2 /*return*/, new Promise(function (resolve) { + context.decodeAudioData(event.target.response, resolve); + })]; + } + return [2 /*return*/]; + } + }); + }); +} +exports.default = AudioPlayer; + +},{"./deferred":5,"./eventtarget":6}],5:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +var Deferred = /** @class */ (function () { + function Deferred() { + var _this = this; + this.promise = new Promise(function (resolve, reject) { + _this._resolve = resolve; + _this._reject = reject; + }); + } + Object.defineProperty(Deferred.prototype, "reject", { + get: function () { return this._reject; }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Deferred.prototype, "resolve", { + get: function () { return this._resolve; }, + enumerable: false, + configurable: true + }); + return Deferred; +}()); +exports.default = Deferred; + +},{}],6:[function(require,module,exports){ +"use strict"; +var __spreadArrays = (this && this.__spreadArrays) || function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +var events_1 = require("events"); +var EventTarget = /** @class */ (function () { + function EventTarget() { + this._eventEmitter = new events_1.EventEmitter(); + } + EventTarget.prototype.addEventListener = function (name, handler) { + return this._eventEmitter.addListener(name, handler); + }; + EventTarget.prototype.dispatchEvent = function (name) { + var _a; + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + return (_a = this._eventEmitter).emit.apply(_a, __spreadArrays([name], args)); + }; + EventTarget.prototype.removeEventListener = function (name, handler) { + return this._eventEmitter.removeListener(name, handler); + }; + return EventTarget; +}()); +exports.default = EventTarget; + +},{"events":39}],7:[function(require,module,exports){ +"use strict"; +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AudioProcessorEventObserver = void 0; +var events_1 = require("events"); +var log_1 = require("./log"); +/** + * AudioProcessorEventObserver observes {@link AudioProcessor} + * related operations and re-emits them as generic events. + * @private + */ +var AudioProcessorEventObserver = /** @class */ (function (_super) { + __extends(AudioProcessorEventObserver, _super); + function AudioProcessorEventObserver() { + var _this = _super.call(this) || this; + _this._log = new log_1.default('AudioProcessorEventObserver'); + _this._log.info('Creating AudioProcessorEventObserver instance'); + _this.on('add', function () { return _this._reEmitEvent('add'); }); + _this.on('remove', function () { return _this._reEmitEvent('remove'); }); + _this.on('create', function () { return _this._reEmitEvent('create-processed-stream'); }); + _this.on('destroy', function () { return _this._reEmitEvent('destroy-processed-stream'); }); + return _this; + } + AudioProcessorEventObserver.prototype.destroy = function () { + this.removeAllListeners(); + }; + AudioProcessorEventObserver.prototype._reEmitEvent = function (name) { + this._log.info("AudioProcessor:" + name); + this.emit('event', { name: name, group: 'audio-processor' }); + }; + return AudioProcessorEventObserver; +}(events_1.EventEmitter)); +exports.AudioProcessorEventObserver = AudioProcessorEventObserver; + +},{"./log":18,"events":39}],8:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @packageDocumentation + * @internalapi + */ +// @ts-nocheck +// NOTE (csantos): This file was taken directly from twilio-video and has been renamed from JS to TS only. +// It needs to be re-written as part of the overall updating of the files to TS. +var events_1 = require("events"); +var Backoff = /** @class */ (function (_super) { + __extends(Backoff, _super); + /** + * Construct a {@link Backoff}. + * @param {object} options + * @property {number} min - Initial timeout in milliseconds [100] + * @property {number} max - Max timeout [10000] + * @property {boolean} jitter - Apply jitter [0] + * @property {number} factor - Multiplication factor for Backoff operation [2] + */ + function Backoff(options) { + var _this = _super.call(this) || this; + Object.defineProperties(_this, { + _attempts: { + value: 0, + writable: true, + }, + _duration: { + enumerable: false, + get: function () { + var ms = this._min * Math.pow(this._factor, this._attempts); + if (this._jitter) { + var rand = Math.random(); + var deviation = Math.floor(rand * this._jitter * ms); + // tslint:disable-next-line + ms = (Math.floor(rand * 10) & 1) === 0 ? ms - deviation : ms + deviation; + } + // tslint:disable-next-line + return Math.min(ms, this._max) | 0; + }, + }, + _factor: { value: options.factor || 2 }, + _jitter: { value: options.jitter > 0 && options.jitter <= 1 ? options.jitter : 0 }, + _max: { value: options.max || 10000 }, + _min: { value: options.min || 100 }, + _timeoutID: { + value: null, + writable: true, + }, + }); + return _this; + } + Backoff.prototype.backoff = function () { + var _this = this; + var duration = this._duration; + if (this._timeoutID) { + clearTimeout(this._timeoutID); + this._timeoutID = null; + } + this.emit('backoff', this._attempts, duration); + this._timeoutID = setTimeout(function () { + _this.emit('ready', _this._attempts, duration); + _this._attempts++; + }, duration); + }; + Backoff.prototype.reset = function () { + this._attempts = 0; + if (this._timeoutID) { + clearTimeout(this._timeoutID); + this._timeoutID = null; + } + }; + return Backoff; +}(events_1.EventEmitter)); +exports.default = Backoff; + +},{"events":39}],9:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @packageDocumentation + * @module Voice + * @publicapi + * @internal + */ +var events_1 = require("events"); +var backoff_1 = require("./backoff"); +var device_1 = require("./device"); +var errors_1 = require("./errors"); +var log_1 = require("./log"); +var rtc_1 = require("./rtc"); +var icecandidate_1 = require("./rtc/icecandidate"); +var sdp_1 = require("./rtc/sdp"); +var statsMonitor_1 = require("./statsMonitor"); +var util_1 = require("./util"); +var uuid_1 = require("./uuid"); +var constants_1 = require("./constants"); +var BACKOFF_CONFIG = { + factor: 1.1, + jitter: 0.5, + max: 30000, + min: 1, +}; +var DTMF_INTER_TONE_GAP = 70; +var DTMF_PAUSE_DURATION = 500; +var DTMF_TONE_DURATION = 160; +var METRICS_BATCH_SIZE = 10; +var METRICS_DELAY = 5000; +var MEDIA_DISCONNECT_ERROR = { + disconnect: true, + info: { + code: 31003, + message: 'Connection with Twilio was interrupted.', + twilioError: new errors_1.MediaErrors.ConnectionError(), + }, +}; +var MULTIPLE_THRESHOLD_WARNING_NAMES = { + // The stat `packetsLostFraction` is monitored by two separate thresholds, + // `maxAverage` and `max`. Each threshold emits a different warning name. + packetsLostFraction: { + max: 'packet-loss', + maxAverage: 'packets-lost-fraction', + }, +}; +var WARNING_NAMES = { + audioInputLevel: 'audio-input-level', + audioOutputLevel: 'audio-output-level', + bytesReceived: 'bytes-received', + bytesSent: 'bytes-sent', + jitter: 'jitter', + mos: 'mos', + rtt: 'rtt', +}; +var WARNING_PREFIXES = { + max: 'high-', + maxAverage: 'high-', + maxDuration: 'constant-', + min: 'low-', + minStandardDeviation: 'constant-', +}; +/** + * A {@link Call} represents a media and signaling connection to a TwiML application. + * @publicapi + */ +var Call = /** @class */ (function (_super) { + __extends(Call, _super); + /** + * @constructor + * @private + * @param config - Mandatory configuration options + * @param [options] - Optional settings + */ + function Call(config, options) { + var _this = _super.call(this) || this; + /** + * Call parameters received from Twilio for an incoming call. + */ + _this.parameters = {}; + /** + * The number of times input volume has been the same consecutively. + */ + _this._inputVolumeStreak = 0; + /** + * Whether the call has been answered. + */ + _this._isAnswered = false; + /** + * Whether the call has been cancelled. + */ + _this._isCancelled = false; + /** + * Whether the call has been rejected + */ + _this._isRejected = false; + /** + * The most recent public input volume value. 0 -> 1 representing -100 to -30 dB. + */ + _this._latestInputVolume = 0; + /** + * The most recent public output volume value. 0 -> 1 representing -100 to -30 dB. + */ + _this._latestOutputVolume = 0; + /** + * An instance of Logger to use. + */ + _this._log = new log_1.default('Call'); + /** + * State of the {@link Call}'s media. + */ + _this._mediaStatus = Call.State.Pending; + /** + * A map of messages sent via sendMessage API using voiceEventSid as the key. + * The message will be deleted once an 'ack' or an error is received from the server. + */ + _this._messages = new Map(); + /** + * A batch of metrics samples to send to Insights. Gets cleared after + * each send and appended to on each new sample. + */ + _this._metricsSamples = []; + /** + * Options passed to this {@link Call}. + */ + _this._options = { + MediaHandler: rtc_1.PeerConnection, + enableImprovedSignalingErrorPrecision: false, + offerSdp: null, + shouldPlayDisconnect: function () { return true; }, + voiceEventSidGenerator: uuid_1.generateVoiceEventSid, + }; + /** + * The number of times output volume has been the same consecutively. + */ + _this._outputVolumeStreak = 0; + /** + * Whether the {@link Call} should send a hangup on disconnect. + */ + _this._shouldSendHangup = true; + /** + * State of the {@link Call}'s signaling. + */ + _this._signalingStatus = Call.State.Pending; + /** + * A Map of Sounds to play. + */ + _this._soundcache = new Map(); + /** + * State of the {@link Call}. + */ + _this._status = Call.State.Pending; + /** + * Whether the {@link Call} has been connected. Used to determine if we are reconnected. + */ + _this._wasConnected = false; + /** + * String representation of {@link Call} instance. + * @private + */ + _this.toString = function () { return '[Twilio.Call instance]'; }; + _this._emitWarning = function (groupPrefix, warningName, threshold, value, wasCleared, warningData) { + var groupSuffix = wasCleared ? '-cleared' : '-raised'; + var groupName = groupPrefix + "warning" + groupSuffix; + // Ignore constant input if the Call is muted (Expected) + if (warningName === 'constant-audio-input-level' && _this.isMuted()) { + return; + } + var level = wasCleared ? 'info' : 'warning'; + // Avoid throwing false positives as warnings until we refactor volume metrics + if (warningName === 'constant-audio-output-level') { + level = 'info'; + } + var payloadData = { threshold: threshold }; + if (value) { + if (value instanceof Array) { + payloadData.values = value.map(function (val) { + if (typeof val === 'number') { + return Math.round(val * 100) / 100; + } + return value; + }); + } + else { + payloadData.value = value; + } + } + _this._publisher.post(level, groupName, warningName, { data: payloadData }, _this); + if (warningName !== 'constant-audio-output-level') { + var emitName = wasCleared ? 'warning-cleared' : 'warning'; + _this._log.debug("#" + emitName, warningName); + _this.emit(emitName, warningName, warningData && !wasCleared ? warningData : null); + } + }; + /** + * Called when the {@link Call} receives an ack from signaling + * @param payload + */ + _this._onAck = function (payload) { + var acktype = payload.acktype, callsid = payload.callsid, voiceeventsid = payload.voiceeventsid; + if (_this.parameters.CallSid !== callsid) { + _this._log.warn("Received ack from a different callsid: " + callsid); + return; + } + if (acktype === 'message') { + _this._onMessageSent(voiceeventsid); + } + }; + /** + * Called when the {@link Call} is answered. + * @param payload + */ + _this._onAnswer = function (payload) { + if (typeof payload.reconnect === 'string') { + _this._signalingReconnectToken = payload.reconnect; + } + // answerOnBridge=false will send a 183 which we need to catch in _onRinging when + // the enableRingingState flag is disabled. In that case, we will receive a 200 after + // the callee accepts the call firing a second `accept` event if we don't + // short circuit here. + if (_this._isAnswered && _this._status !== Call.State.Reconnecting) { + return; + } + _this._setCallSid(payload); + _this._isAnswered = true; + _this._maybeTransitionToOpen(); + }; + /** + * Called when the {@link Call} is cancelled. + * @param payload + */ + _this._onCancel = function (payload) { + // (rrowland) Is this check necessary? Verify, and if so move to pstream / VSP module. + var callsid = payload.callsid; + if (_this.parameters.CallSid === callsid) { + _this._isCancelled = true; + _this._publisher.info('connection', 'cancel', null, _this); + _this._cleanupEventListeners(); + _this._mediaHandler.close(); + _this._status = Call.State.Closed; + _this._log.debug('#cancel'); + _this.emit('cancel'); + _this._pstream.removeListener('cancel', _this._onCancel); + } + }; + /** + * Called when we receive a connected event from pstream. + * Re-emits the event. + */ + _this._onConnected = function () { + _this._log.info('Received connected from pstream'); + if (_this._signalingReconnectToken && _this._mediaHandler.version) { + _this._pstream.reconnect(_this._mediaHandler.version.getSDP(), _this.parameters.CallSid, _this._signalingReconnectToken); + } + }; + /** + * Called when the {@link Call} is hung up. + * @param payload + */ + _this._onHangup = function (payload) { + if (_this.status() === Call.State.Closed) { + return; + } + /** + * see if callsid passed in message matches either callsid or outbound id + * call should always have either callsid or outbound id + * if no callsid passed hangup anyways + */ + if (payload.callsid && (_this.parameters.CallSid || _this.outboundConnectionId)) { + if (payload.callsid !== _this.parameters.CallSid + && payload.callsid !== _this.outboundConnectionId) { + return; + } + } + else if (payload.callsid) { + // hangup is for another call + return; + } + _this._log.info('Received HANGUP from gateway'); + if (payload.error) { + var code = payload.error.code; + var errorConstructor = errors_1.getPreciseSignalingErrorByCode(_this._options.enableImprovedSignalingErrorPrecision, code); + var error = typeof errorConstructor !== 'undefined' + ? new errorConstructor(payload.error.message) + : new errors_1.GeneralErrors.ConnectionError('Error sent from gateway in HANGUP'); + _this._log.error('Received an error from the gateway:', error); + _this._log.debug('#error', error); + _this.emit('error', error); + } + _this._shouldSendHangup = false; + _this._publisher.info('connection', 'disconnected-by-remote', null, _this); + _this._disconnect(null, true); + _this._cleanupEventListeners(); + }; + /** + * Called when there is a media failure. + * Manages all media-related states and takes action base on the states + * @param type - Type of media failure + */ + _this._onMediaFailure = function (type) { + var _a = Call.MediaFailure, ConnectionDisconnected = _a.ConnectionDisconnected, ConnectionFailed = _a.ConnectionFailed, IceGatheringFailed = _a.IceGatheringFailed, LowBytes = _a.LowBytes; + // These types signifies the end of a single ICE cycle + var isEndOfIceCycle = type === ConnectionFailed || type === IceGatheringFailed; + // All browsers except chrome doesn't update pc.iceConnectionState and pc.connectionState + // after issuing an ICE Restart, which we use to determine if ICE Restart is complete. + // Since we cannot detect if ICE Restart is complete, we will not retry. + if (!util_1.isChrome(window, window.navigator) && type === ConnectionFailed) { + return _this._mediaHandler.onerror(MEDIA_DISCONNECT_ERROR); + } + // Ignore subsequent requests if ice restart is in progress + if (_this._mediaStatus === Call.State.Reconnecting) { + // This is a retry. Previous ICE Restart failed + if (isEndOfIceCycle) { + // We already exceeded max retry time. + if (Date.now() - _this._mediaReconnectStartTime > BACKOFF_CONFIG.max) { + _this._log.warn('Exceeded max ICE retries'); + return _this._mediaHandler.onerror(MEDIA_DISCONNECT_ERROR); + } + // Issue ICE restart with backoff + try { + _this._mediaReconnectBackoff.backoff(); + } + catch (error) { + // Catch and ignore 'Backoff in progress.' errors. If a backoff is + // ongoing and we try to start another one, there shouldn't be a + // problem. + if (!(error.message && error.message === 'Backoff in progress.')) { + throw error; + } + } + } + return; + } + var pc = _this._mediaHandler.version.pc; + var isIceDisconnected = pc && pc.iceConnectionState === 'disconnected'; + var hasLowBytesWarning = _this._monitor.hasActiveWarning('bytesSent', 'min') + || _this._monitor.hasActiveWarning('bytesReceived', 'min'); + // Only certain conditions can trigger media reconnection + if ((type === LowBytes && isIceDisconnected) + || (type === ConnectionDisconnected && hasLowBytesWarning) + || isEndOfIceCycle) { + var mediaReconnectionError = new errors_1.MediaErrors.ConnectionError('Media connection failed.'); + _this._log.warn('ICE Connection disconnected.'); + _this._publisher.warn('connection', 'error', mediaReconnectionError, _this); + _this._publisher.info('connection', 'reconnecting', null, _this); + _this._mediaReconnectStartTime = Date.now(); + _this._status = Call.State.Reconnecting; + _this._mediaStatus = Call.State.Reconnecting; + _this._mediaReconnectBackoff.reset(); + _this._mediaReconnectBackoff.backoff(); + _this._log.debug('#reconnecting'); + _this.emit('reconnecting', mediaReconnectionError); + } + }; + /** + * Called when media call is restored + */ + _this._onMediaReconnected = function () { + // Only trigger once. + // This can trigger on pc.onIceConnectionChange and pc.onConnectionChange. + if (_this._mediaStatus !== Call.State.Reconnecting) { + return; + } + _this._log.info('ICE Connection reestablished.'); + _this._mediaStatus = Call.State.Open; + if (_this._signalingStatus === Call.State.Open) { + _this._publisher.info('connection', 'reconnected', null, _this); + _this._log.debug('#reconnected'); + _this.emit('reconnected'); + _this._status = Call.State.Open; + } + }; + /** + * Raised when a Call receives a message from the backend. + * @param payload - A record representing the payload of the message from the + * Twilio backend. + */ + _this._onMessageReceived = function (payload) { + var callsid = payload.callsid, content = payload.content, contenttype = payload.contenttype, messagetype = payload.messagetype, voiceeventsid = payload.voiceeventsid; + if (_this.parameters.CallSid !== callsid) { + _this._log.warn("Received a message from a different callsid: " + callsid); + return; + } + var data = { + content: content, + contentType: contenttype, + messageType: messagetype, + voiceEventSid: voiceeventsid, + }; + _this._log.debug('#messageReceived', JSON.stringify(data)); + _this.emit('messageReceived', data); + }; + /** + * Raised when a Call receives an 'ack' with an 'acktype' of 'message. + * This means that the message sent via sendMessage API has been received by the signaling server. + * @param voiceEventSid + */ + _this._onMessageSent = function (voiceEventSid) { + if (!_this._messages.has(voiceEventSid)) { + _this._log.warn("Received a messageSent with a voiceEventSid that doesn't exists: " + voiceEventSid); + return; + } + var message = _this._messages.get(voiceEventSid); + _this._messages.delete(voiceEventSid); + _this._log.debug('#messageSent', JSON.stringify(message)); + _this.emit('messageSent', message); + }; + /** + * When we get a RINGING signal from PStream, update the {@link Call} status. + * @param payload + */ + _this._onRinging = function (payload) { + _this._setCallSid(payload); + // If we're not in 'connecting' or 'ringing' state, this event was received out of order. + if (_this._status !== Call.State.Connecting && _this._status !== Call.State.Ringing) { + return; + } + var hasEarlyMedia = !!payload.sdp; + _this._status = Call.State.Ringing; + _this._publisher.info('connection', 'outgoing-ringing', { hasEarlyMedia: hasEarlyMedia }, _this); + _this._log.debug('#ringing'); + _this.emit('ringing', hasEarlyMedia); + }; + /** + * Called each time StatsMonitor emits a sample. + * Emits stats event and batches the call stats metrics and sends them to Insights. + * @param sample + */ + _this._onRTCSample = function (sample) { + var callMetrics = __assign(__assign({}, sample), { inputVolume: _this._latestInputVolume, outputVolume: _this._latestOutputVolume }); + _this._codec = callMetrics.codecName; + _this._metricsSamples.push(callMetrics); + if (_this._metricsSamples.length >= METRICS_BATCH_SIZE) { + _this._publishMetrics(); + } + _this.emit('sample', sample); + }; + /** + * Called when an 'error' event is received from the signaling stream. + */ + _this._onSignalingError = function (payload) { + var callsid = payload.callsid, voiceeventsid = payload.voiceeventsid; + if (_this.parameters.CallSid !== callsid) { + _this._log.warn("Received an error from a different callsid: " + callsid); + return; + } + if (voiceeventsid && _this._messages.has(voiceeventsid)) { + // Do not emit an error here. Device is handling all signaling related errors. + _this._messages.delete(voiceeventsid); + _this._log.warn("Received an error while sending a message.", payload); + } + }; + /** + * Called when signaling is restored + */ + _this._onSignalingReconnected = function () { + if (_this._signalingStatus !== Call.State.Reconnecting) { + return; + } + _this._log.info('Signaling Connection reestablished.'); + _this._signalingStatus = Call.State.Open; + if (_this._mediaStatus === Call.State.Open) { + _this._publisher.info('connection', 'reconnected', null, _this); + _this._log.debug('#reconnected'); + _this.emit('reconnected'); + _this._status = Call.State.Open; + } + }; + /** + * Called when we receive a transportClose event from pstream. + * Re-emits the event. + */ + _this._onTransportClose = function () { + _this._log.error('Received transportClose from pstream'); + _this._log.debug('#transportClose'); + _this.emit('transportClose'); + if (_this._signalingReconnectToken) { + _this._status = Call.State.Reconnecting; + _this._signalingStatus = Call.State.Reconnecting; + _this._log.debug('#reconnecting'); + _this.emit('reconnecting', new errors_1.SignalingErrors.ConnectionDisconnected()); + } + else { + _this._status = Call.State.Closed; + _this._signalingStatus = Call.State.Closed; + } + }; + /** + * Re-emit an StatsMonitor warning as a {@link Call}.warning or .warning-cleared event. + * @param warningData + * @param wasCleared - Whether this is a -cleared or -raised event. + */ + _this._reemitWarning = function (warningData, wasCleared) { + var groupPrefix = /^audio/.test(warningData.name) ? + 'audio-level-' : 'network-quality-'; + var warningPrefix = WARNING_PREFIXES[warningData.threshold.name]; + /** + * NOTE: There are two "packet-loss" warnings: `high-packet-loss` and + * `high-packets-lost-fraction`, so in this case we need to use a different + * `WARNING_NAME` mapping. + */ + var warningName; + if (warningData.name in MULTIPLE_THRESHOLD_WARNING_NAMES) { + warningName = MULTIPLE_THRESHOLD_WARNING_NAMES[warningData.name][warningData.threshold.name]; + } + else if (warningData.name in WARNING_NAMES) { + warningName = WARNING_NAMES[warningData.name]; + } + var warning = warningPrefix + warningName; + _this._emitWarning(groupPrefix, warning, warningData.threshold.value, warningData.values || warningData.value, wasCleared, warningData); + }; + /** + * Re-emit an StatsMonitor warning-cleared as a .warning-cleared event. + * @param warningData + */ + _this._reemitWarningCleared = function (warningData) { + _this._reemitWarning(warningData, true); + }; + _this._isUnifiedPlanDefault = config.isUnifiedPlanDefault; + _this._soundcache = config.soundcache; + if (typeof config.onIgnore === 'function') { + _this._onIgnore = config.onIgnore; + } + var message = options && options.twimlParams || {}; + _this.customParameters = new Map(Object.entries(message).map(function (_a) { + var key = _a[0], val = _a[1]; + return [key, String(val)]; + })); + Object.assign(_this._options, options); + if (_this._options.callParameters) { + _this.parameters = _this._options.callParameters; + } + if (_this._options.reconnectToken) { + _this._signalingReconnectToken = _this._options.reconnectToken; + } + _this._voiceEventSidGenerator = + _this._options.voiceEventSidGenerator || uuid_1.generateVoiceEventSid; + _this._direction = _this.parameters.CallSid ? Call.CallDirection.Incoming : Call.CallDirection.Outgoing; + if (_this._direction === Call.CallDirection.Incoming && _this.parameters) { + _this.callerInfo = _this.parameters.StirStatus + ? { isVerified: _this.parameters.StirStatus === 'TN-Validation-Passed-A' } + : null; + } + else { + _this.callerInfo = null; + } + _this._mediaReconnectBackoff = new backoff_1.default(BACKOFF_CONFIG); + _this._mediaReconnectBackoff.on('ready', function () { return _this._mediaHandler.iceRestart(); }); + // temporary call sid to be used for outgoing calls + _this.outboundConnectionId = generateTempCallSid(); + var publisher = _this._publisher = config.publisher; + if (_this._direction === Call.CallDirection.Incoming) { + publisher.info('connection', 'incoming', null, _this); + } + else { + publisher.info('connection', 'outgoing', { preflight: _this._options.preflight }, _this); + } + var monitor = _this._monitor = new (_this._options.StatsMonitor || statsMonitor_1.default)(); + monitor.on('sample', _this._onRTCSample); + // First 20 seconds or so are choppy, so let's not bother with these warnings. + monitor.disableWarnings(); + setTimeout(function () { return monitor.enableWarnings(); }, METRICS_DELAY); + monitor.on('warning', function (data, wasCleared) { + if (data.name === 'bytesSent' || data.name === 'bytesReceived') { + _this._onMediaFailure(Call.MediaFailure.LowBytes); + } + _this._reemitWarning(data, wasCleared); + }); + monitor.on('warning-cleared', function (data) { + _this._reemitWarningCleared(data); + }); + _this._mediaHandler = new (_this._options.MediaHandler)(config.audioHelper, config.pstream, { + RTCPeerConnection: _this._options.RTCPeerConnection, + codecPreferences: _this._options.codecPreferences, + dscp: _this._options.dscp, + forceAggressiveIceNomination: _this._options.forceAggressiveIceNomination, + isUnifiedPlan: _this._isUnifiedPlanDefault, + maxAverageBitrate: _this._options.maxAverageBitrate, + preflight: _this._options.preflight, + }); + _this.on('volume', function (inputVolume, outputVolume) { + _this._inputVolumeStreak = _this._checkVolume(inputVolume, _this._inputVolumeStreak, _this._latestInputVolume, 'input'); + _this._outputVolumeStreak = _this._checkVolume(outputVolume, _this._outputVolumeStreak, _this._latestOutputVolume, 'output'); + _this._latestInputVolume = inputVolume; + _this._latestOutputVolume = outputVolume; + }); + _this._mediaHandler.onaudio = function (remoteAudio) { + _this._log.debug('#audio'); + _this.emit('audio', remoteAudio); + }; + _this._mediaHandler.onvolume = function (inputVolume, outputVolume, internalInputVolume, internalOutputVolume) { + // (rrowland) These values mock the 0 -> 32767 format used by legacy getStats. We should look into + // migrating to a newer standard, either 0.0 -> linear or -127 to 0 in dB, matching the range + // chosen below. + monitor.addVolumes((internalInputVolume / 255) * 32767, (internalOutputVolume / 255) * 32767); + // (rrowland) 0.0 -> 1.0 linear + _this.emit('volume', inputVolume, outputVolume); + }; + _this._mediaHandler.ondtlstransportstatechange = function (state) { + var level = state === 'failed' ? 'error' : 'debug'; + _this._publisher.post(level, 'dtls-transport-state', state, null, _this); + }; + _this._mediaHandler.onpcconnectionstatechange = function (state) { + var level = 'debug'; + var dtlsTransport = _this._mediaHandler.getRTCDtlsTransport(); + if (state === 'failed') { + level = dtlsTransport && dtlsTransport.state === 'failed' ? 'error' : 'warning'; + } + _this._publisher.post(level, 'pc-connection-state', state, null, _this); + }; + _this._mediaHandler.onicecandidate = function (candidate) { + var payload = new icecandidate_1.IceCandidate(candidate).toPayload(); + _this._publisher.debug('ice-candidate', 'ice-candidate', payload, _this); + }; + _this._mediaHandler.onselectedcandidatepairchange = function (pair) { + var localCandidatePayload = new icecandidate_1.IceCandidate(pair.local).toPayload(); + var remoteCandidatePayload = new icecandidate_1.IceCandidate(pair.remote, true).toPayload(); + _this._publisher.debug('ice-candidate', 'selected-ice-candidate-pair', { + local_candidate: localCandidatePayload, + remote_candidate: remoteCandidatePayload, + }, _this); + }; + _this._mediaHandler.oniceconnectionstatechange = function (state) { + var level = state === 'failed' ? 'error' : 'debug'; + _this._publisher.post(level, 'ice-connection-state', state, null, _this); + }; + _this._mediaHandler.onicegatheringfailure = function (type) { + _this._publisher.warn('ice-gathering-state', type, null, _this); + _this._onMediaFailure(Call.MediaFailure.IceGatheringFailed); + }; + _this._mediaHandler.onicegatheringstatechange = function (state) { + _this._publisher.debug('ice-gathering-state', state, null, _this); + }; + _this._mediaHandler.onsignalingstatechange = function (state) { + _this._publisher.debug('signaling-state', state, null, _this); + }; + _this._mediaHandler.ondisconnected = function (msg) { + _this._log.warn(msg); + _this._publisher.warn('network-quality-warning-raised', 'ice-connectivity-lost', { + message: msg, + }, _this); + _this._log.debug('#warning', 'ice-connectivity-lost'); + _this.emit('warning', 'ice-connectivity-lost'); + _this._onMediaFailure(Call.MediaFailure.ConnectionDisconnected); + }; + _this._mediaHandler.onfailed = function (msg) { + _this._onMediaFailure(Call.MediaFailure.ConnectionFailed); + }; + _this._mediaHandler.onconnected = function () { + // First time _mediaHandler is connected, but ICE Gathering issued an ICE restart and succeeded. + if (_this._status === Call.State.Reconnecting) { + _this._onMediaReconnected(); + } + }; + _this._mediaHandler.onreconnected = function (msg) { + _this._log.info(msg); + _this._publisher.info('network-quality-warning-cleared', 'ice-connectivity-lost', { + message: msg, + }, _this); + _this._log.debug('#warning-cleared', 'ice-connectivity-lost'); + _this.emit('warning-cleared', 'ice-connectivity-lost'); + _this._onMediaReconnected(); + }; + _this._mediaHandler.onerror = function (e) { + if (e.disconnect === true) { + _this._disconnect(e.info && e.info.message); + } + var error = e.info.twilioError || new errors_1.GeneralErrors.UnknownError(e.info.message); + _this._log.error('Received an error from MediaStream:', e); + _this._log.debug('#error', error); + _this.emit('error', error); + }; + _this._mediaHandler.onopen = function () { + // NOTE(mroberts): While this may have been happening in previous + // versions of Chrome, since Chrome 45 we have seen the + // PeerConnection's onsignalingstatechange handler invoked multiple + // times in the same signalingState 'stable'. When this happens, we + // invoke this onopen function. If we invoke it twice without checking + // for _status 'open', we'd accidentally close the PeerConnection. + // + // See . + if (_this._status === Call.State.Open || _this._status === Call.State.Reconnecting) { + return; + } + else if (_this._status === Call.State.Ringing || _this._status === Call.State.Connecting) { + _this.mute(_this._mediaHandler.isMuted); + _this._mediaStatus = Call.State.Open; + _this._maybeTransitionToOpen(); + } + else { + // call was probably canceled sometime before this + _this._mediaHandler.close(); + } + }; + _this._mediaHandler.onclose = function () { + _this._status = Call.State.Closed; + if (_this._options.shouldPlayDisconnect && _this._options.shouldPlayDisconnect() + // Don't play disconnect sound if this was from a cancel event. i.e. the call + // was ignored or hung up even before it was answered. + // Similarly, don't play disconnect sound if the call was rejected. + && !_this._isCancelled && !_this._isRejected) { + _this._soundcache.get(device_1.default.SoundName.Disconnect).play(); + } + monitor.disable(); + _this._publishMetrics(); + if (!_this._isCancelled && !_this._isRejected) { + // tslint:disable no-console + _this._log.debug('#disconnect'); + _this.emit('disconnect', _this); + } + }; + _this._pstream = config.pstream; + _this._pstream.on('ack', _this._onAck); + _this._pstream.on('cancel', _this._onCancel); + _this._pstream.on('error', _this._onSignalingError); + _this._pstream.on('ringing', _this._onRinging); + _this._pstream.on('transportClose', _this._onTransportClose); + _this._pstream.on('connected', _this._onConnected); + _this._pstream.on('message', _this._onMessageReceived); + _this.on('error', function (error) { + _this._publisher.error('connection', 'error', { + code: error.code, message: error.message, + }, _this); + if (_this._pstream && _this._pstream.status === 'disconnected') { + _this._cleanupEventListeners(); + } + }); + _this.on('disconnect', function () { + _this._cleanupEventListeners(); + }); + return _this; + } + Object.defineProperty(Call.prototype, "direction", { + /** + * Whether this {@link Call} is incoming or outgoing. + */ + get: function () { + return this._direction; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Call.prototype, "codec", { + /** + * Audio codec used for this {@link Call}. Expecting {@link Call.Codec} but + * will copy whatever we get from RTC stats. + */ + get: function () { + return this._codec; + }, + enumerable: false, + configurable: true + }); + /** + * Set the audio input tracks from a given stream. + * @param stream + * @private + */ + Call.prototype._setInputTracksFromStream = function (stream) { + return this._mediaHandler.setInputTracksFromStream(stream); + }; + /** + * Set the audio output sink IDs. + * @param sinkIds + * @private + */ + Call.prototype._setSinkIds = function (sinkIds) { + return this._mediaHandler._setSinkIds(sinkIds); + }; + /** + * Accept the incoming {@link Call}. + * @param [options] + */ + Call.prototype.accept = function (options) { + var _this = this; + if (this._status !== Call.State.Pending) { + return; + } + this._log.debug('.accept', options); + options = options || {}; + var rtcConfiguration = options.rtcConfiguration || this._options.rtcConfiguration; + var rtcConstraints = options.rtcConstraints || this._options.rtcConstraints || {}; + var audioConstraints = rtcConstraints.audio || { audio: true }; + this._status = Call.State.Connecting; + var connect = function () { + if (_this._status !== Call.State.Connecting) { + // call must have been canceled + _this._cleanupEventListeners(); + _this._mediaHandler.close(); + return; + } + var onAnswer = function (pc, reconnectToken) { + // Report that the call was answered, and directionality + var eventName = _this._direction === Call.CallDirection.Incoming + ? 'accepted-by-local' + : 'accepted-by-remote'; + _this._publisher.info('connection', eventName, null, _this); + if (typeof reconnectToken === 'string') { + _this._signalingReconnectToken = reconnectToken; + } + // Report the preferred codec and params as they appear in the SDP + var _a = sdp_1.getPreferredCodecInfo(_this._mediaHandler.version.getSDP()), codecName = _a.codecName, codecParams = _a.codecParams; + _this._publisher.info('settings', 'codec', { + codec_params: codecParams, + selected_codec: codecName, + }, _this); + // Enable RTC monitoring + _this._monitor.enable(pc); + }; + var sinkIds = typeof _this._options.getSinkIds === 'function' && _this._options.getSinkIds(); + if (Array.isArray(sinkIds)) { + _this._mediaHandler._setSinkIds(sinkIds).catch(function () { + // (rrowland) We don't want this to throw to console since the customer + // can't control this. This will most commonly be rejected on browsers + // that don't support setting sink IDs. + }); + } + _this._pstream.addListener('hangup', _this._onHangup); + if (_this._direction === Call.CallDirection.Incoming) { + _this._isAnswered = true; + _this._pstream.on('answer', _this._onAnswer); + _this._mediaHandler.answerIncomingCall(_this.parameters.CallSid, _this._options.offerSdp, rtcConstraints, rtcConfiguration, onAnswer); + } + else { + var params = Array.from(_this.customParameters.entries()).map(function (pair) { + return encodeURIComponent(pair[0]) + "=" + encodeURIComponent(pair[1]); + }).join('&'); + _this._pstream.on('answer', _this._onAnswer); + _this._mediaHandler.makeOutgoingCall(_this._pstream.token, params, _this.outboundConnectionId, rtcConstraints, rtcConfiguration, onAnswer); + } + }; + if (this._options.beforeAccept) { + this._options.beforeAccept(this); + } + var inputStream = typeof this._options.getInputStream === 'function' && this._options.getInputStream(); + var promise = inputStream + ? this._mediaHandler.setInputTracksFromStream(inputStream) + : this._mediaHandler.openDefaultDeviceWithConstraints(audioConstraints); + promise.then(function () { + _this._publisher.info('get-user-media', 'succeeded', { + data: { audioConstraints: audioConstraints }, + }, _this); + connect(); + }, function (error) { + var twilioError; + if (error.code === 31208 + || ['PermissionDeniedError', 'NotAllowedError'].indexOf(error.name) !== -1) { + twilioError = new errors_1.UserMediaErrors.PermissionDeniedError(); + _this._publisher.error('get-user-media', 'denied', { + data: { + audioConstraints: audioConstraints, + error: error, + }, + }, _this); + } + else { + twilioError = new errors_1.UserMediaErrors.AcquisitionFailedError(); + _this._publisher.error('get-user-media', 'failed', { + data: { + audioConstraints: audioConstraints, + error: error, + }, + }, _this); + } + _this._disconnect(); + _this._log.debug('#error', error); + _this.emit('error', twilioError); + }); + }; + /** + * Disconnect from the {@link Call}. + */ + Call.prototype.disconnect = function () { + this._log.debug('.disconnect'); + this._disconnect(); + }; + /** + * Get the local MediaStream, if set. + */ + Call.prototype.getLocalStream = function () { + return this._mediaHandler && this._mediaHandler.stream; + }; + /** + * Get the remote MediaStream, if set. + */ + Call.prototype.getRemoteStream = function () { + return this._mediaHandler && this._mediaHandler._remoteStream; + }; + /** + * Ignore the incoming {@link Call}. + */ + Call.prototype.ignore = function () { + if (this._status !== Call.State.Pending) { + return; + } + this._log.debug('.ignore'); + this._status = Call.State.Closed; + this._mediaHandler.ignore(this.parameters.CallSid); + this._publisher.info('connection', 'ignored-by-local', null, this); + if (this._onIgnore) { + this._onIgnore(); + } + }; + /** + * Check whether call is muted + */ + Call.prototype.isMuted = function () { + return this._mediaHandler.isMuted; + }; + /** + * Mute incoming audio. + * @param shouldMute - Whether the incoming audio should be muted. Defaults to true. + */ + Call.prototype.mute = function (shouldMute) { + if (shouldMute === void 0) { shouldMute = true; } + this._log.debug('.mute', shouldMute); + var wasMuted = this._mediaHandler.isMuted; + this._mediaHandler.mute(shouldMute); + var isMuted = this._mediaHandler.isMuted; + if (wasMuted !== isMuted) { + this._publisher.info('connection', isMuted ? 'muted' : 'unmuted', null, this); + this._log.debug('#mute', isMuted); + this.emit('mute', isMuted, this); + } + }; + /** + * Post an event to Endpoint Analytics indicating that the end user + * has given call quality feedback. Called without a score, this + * will report that the customer declined to give feedback. + * @param score - The end-user's rating of the call; an + * integer 1 through 5. Or undefined if the user declined to give + * feedback. + * @param issue - The primary issue the end user + * experienced on the call. Can be: ['one-way-audio', 'choppy-audio', + * 'dropped-call', 'audio-latency', 'noisy-call', 'echo'] + */ + Call.prototype.postFeedback = function (score, issue) { + if (typeof score === 'undefined' || score === null) { + return this._postFeedbackDeclined(); + } + if (!Object.values(Call.FeedbackScore).includes(score)) { + throw new errors_1.InvalidArgumentError("Feedback score must be one of: " + Object.values(Call.FeedbackScore)); + } + if (typeof issue !== 'undefined' && issue !== null && !Object.values(Call.FeedbackIssue).includes(issue)) { + throw new errors_1.InvalidArgumentError("Feedback issue must be one of: " + Object.values(Call.FeedbackIssue)); + } + return this._publisher.info('feedback', 'received', { + issue_name: issue, + quality_score: score, + }, this, true); + }; + /** + * Reject the incoming {@link Call}. + */ + Call.prototype.reject = function () { + if (this._status !== Call.State.Pending) { + return; + } + this._log.debug('.reject'); + this._isRejected = true; + this._pstream.reject(this.parameters.CallSid); + this._mediaHandler.reject(this.parameters.CallSid); + this._publisher.info('connection', 'rejected-by-local', null, this); + this._cleanupEventListeners(); + this._mediaHandler.close(); + this._status = Call.State.Closed; + this._log.debug('#reject'); + this.emit('reject'); + }; + /** + * Send a string of digits. + * @param digits + */ + Call.prototype.sendDigits = function (digits) { + var _this = this; + this._log.debug('.sendDigits', digits); + if (digits.match(/[^0-9*#w]/)) { + throw new errors_1.InvalidArgumentError('Illegal character passed into sendDigits'); + } + var customSounds = this._options.customSounds || {}; + var sequence = []; + digits.split('').forEach(function (digit) { + var dtmf = (digit !== 'w') ? "dtmf" + digit : ''; + if (dtmf === 'dtmf*') { + dtmf = 'dtmfs'; + } + if (dtmf === 'dtmf#') { + dtmf = 'dtmfh'; + } + sequence.push(dtmf); + }); + var playNextDigit = function () { + var digit = sequence.shift(); + if (digit) { + if (_this._options.dialtonePlayer && !customSounds[digit]) { + _this._options.dialtonePlayer.play(digit); + } + else { + _this._soundcache.get(digit).play(); + } + } + if (sequence.length) { + setTimeout(function () { return playNextDigit(); }, 200); + } + }; + playNextDigit(); + var dtmfSender = this._mediaHandler.getOrCreateDTMFSender(); + function insertDTMF(dtmfs) { + if (!dtmfs.length) { + return; + } + var dtmf = dtmfs.shift(); + if (dtmf && dtmf.length) { + dtmfSender.insertDTMF(dtmf, DTMF_TONE_DURATION, DTMF_INTER_TONE_GAP); + } + setTimeout(insertDTMF.bind(null, dtmfs), DTMF_PAUSE_DURATION); + } + if (dtmfSender) { + if (!('canInsertDTMF' in dtmfSender) || dtmfSender.canInsertDTMF) { + this._log.info('Sending digits using RTCDTMFSender'); + // NOTE(mroberts): We can't just map 'w' to ',' since + // RTCDTMFSender's pause duration is 2 s and Twilio's is more + // like 500 ms. Instead, we will fudge it with setTimeout. + insertDTMF(digits.split('w')); + return; + } + this._log.info('RTCDTMFSender cannot insert DTMF'); + } + // send pstream message to send DTMF + this._log.info('Sending digits over PStream'); + if (this._pstream !== null && this._pstream.status !== 'disconnected') { + this._pstream.dtmf(this.parameters.CallSid, digits); + } + else { + var error = new errors_1.GeneralErrors.ConnectionError('Could not send DTMF: Signaling channel is disconnected'); + this._log.debug('#error', error); + this.emit('error', error); + } + }; + /** + * Send a message to Twilio. Your backend application can listen for these + * messages to allow communication between your frontend and backend applications. + *

This feature is currently in Beta. + * @param message - The message object to send. + * @returns A voice event sid that uniquely identifies the message that was sent. + */ + Call.prototype.sendMessage = function (message) { + this._log.debug('.sendMessage', JSON.stringify(message)); + var content = message.content, contentType = message.contentType, messageType = message.messageType; + if (typeof content === 'undefined' || content === null) { + throw new errors_1.InvalidArgumentError('`content` is empty'); + } + if (typeof messageType !== 'string') { + throw new errors_1.InvalidArgumentError('`messageType` must be an enumeration value of `Call.MessageType` or ' + + 'a string.'); + } + if (messageType.length === 0) { + throw new errors_1.InvalidArgumentError('`messageType` must be a non-empty string.'); + } + if (this._pstream === null) { + throw new errors_1.InvalidStateError('Could not send CallMessage; Signaling channel is disconnected'); + } + var callSid = this.parameters.CallSid; + if (typeof this.parameters.CallSid === 'undefined') { + throw new errors_1.InvalidStateError('Could not send CallMessage; Call has no CallSid'); + } + var voiceEventSid = this._voiceEventSidGenerator(); + this._messages.set(voiceEventSid, { content: content, contentType: contentType, messageType: messageType, voiceEventSid: voiceEventSid }); + this._pstream.sendMessage(callSid, content, contentType, messageType, voiceEventSid); + return voiceEventSid; + }; + /** + * Get the current {@link Call} status. + */ + Call.prototype.status = function () { + return this._status; + }; + /** + * Check the volume passed, emitting a warning if one way audio is detected or cleared. + * @param currentVolume - The current volume for this direction + * @param streakFieldName - The name of the field on the {@link Call} object that tracks how many times the + * current value has been repeated consecutively. + * @param lastValueFieldName - The name of the field on the {@link Call} object that tracks the most recent + * volume for this direction + * @param direction - The directionality of this audio track, either 'input' or 'output' + * @returns The current streak; how many times in a row the same value has been polled. + */ + Call.prototype._checkVolume = function (currentVolume, currentStreak, lastValue, direction) { + var wasWarningRaised = currentStreak >= 10; + var newStreak = 0; + if (lastValue === currentVolume) { + newStreak = currentStreak; + } + if (newStreak >= 10) { + this._emitWarning('audio-level-', "constant-audio-" + direction + "-level", 10, newStreak, false); + } + else if (wasWarningRaised) { + this._emitWarning('audio-level-', "constant-audio-" + direction + "-level", 10, newStreak, true); + } + return newStreak; + }; + /** + * Clean up event listeners. + */ + Call.prototype._cleanupEventListeners = function () { + var _this = this; + var cleanup = function () { + if (!_this._pstream) { + return; + } + _this._pstream.removeListener('ack', _this._onAck); + _this._pstream.removeListener('answer', _this._onAnswer); + _this._pstream.removeListener('cancel', _this._onCancel); + _this._pstream.removeListener('error', _this._onSignalingError); + _this._pstream.removeListener('hangup', _this._onHangup); + _this._pstream.removeListener('ringing', _this._onRinging); + _this._pstream.removeListener('transportClose', _this._onTransportClose); + _this._pstream.removeListener('connected', _this._onConnected); + _this._pstream.removeListener('message', _this._onMessageReceived); + }; + // This is kind of a hack, but it lets us avoid rewriting more code. + // Basically, there's a sequencing problem with the way PeerConnection raises + // the + // + // Cannot establish call. SDK is disconnected + // + // error in Call#accept. It calls PeerConnection#onerror, which emits + // the error event on Call. An error handler on Call then calls + // cleanupEventListeners, but then control returns to Call#accept. It's + // at this point that we add a listener for the answer event that never gets + // removed. setTimeout will allow us to rerun cleanup again, _after_ + // Call#accept returns. + cleanup(); + setTimeout(cleanup, 0); + }; + /** + * Create the payload wrapper for a batch of metrics to be sent to Insights. + */ + Call.prototype._createMetricPayload = function () { + var payload = { + call_sid: this.parameters.CallSid, + dscp: !!this._options.dscp, + sdk_version: constants_1.RELEASE_VERSION, + }; + if (this._options.gateway) { + payload.gateway = this._options.gateway; + } + payload.direction = this._direction; + return payload; + }; + /** + * Disconnect the {@link Call}. + * @param message - A message explaining why the {@link Call} is being disconnected. + * @param wasRemote - Whether the disconnect was triggered locally or remotely. + */ + Call.prototype._disconnect = function (message, wasRemote) { + message = typeof message === 'string' ? message : null; + if (this._status !== Call.State.Open + && this._status !== Call.State.Connecting + && this._status !== Call.State.Reconnecting + && this._status !== Call.State.Ringing) { + return; + } + this._log.info('Disconnecting...'); + // send pstream hangup message + if (this._pstream !== null && this._pstream.status !== 'disconnected' && this._shouldSendHangup) { + var callsid = this.parameters.CallSid || this.outboundConnectionId; + if (callsid) { + this._pstream.hangup(callsid, message); + } + } + this._cleanupEventListeners(); + this._mediaHandler.close(); + if (!wasRemote) { + this._publisher.info('connection', 'disconnected-by-local', null, this); + } + }; + /** + * Transition to {@link CallStatus.Open} if criteria is met. + */ + Call.prototype._maybeTransitionToOpen = function () { + var wasConnected = this._wasConnected; + if (this._isAnswered) { + this._onSignalingReconnected(); + this._signalingStatus = Call.State.Open; + if (this._mediaHandler && this._mediaHandler.status === 'open') { + this._status = Call.State.Open; + if (!this._wasConnected) { + this._wasConnected = true; + this._log.debug('#accept'); + this.emit('accept', this); + } + } + } + }; + /** + * Post an event to Endpoint Analytics indicating that the end user + * has ignored a request for feedback. + */ + Call.prototype._postFeedbackDeclined = function () { + return this._publisher.info('feedback', 'received-none', null, this, true); + }; + /** + * Publish the current set of queued metrics samples to Insights. + */ + Call.prototype._publishMetrics = function () { + var _this = this; + if (this._metricsSamples.length === 0) { + return; + } + this._publisher.postMetrics('quality-metrics-samples', 'metrics-sample', this._metricsSamples.splice(0), this._createMetricPayload(), this).catch(function (e) { + _this._log.warn('Unable to post metrics to Insights. Received error:', e); + }); + }; + /** + * Set the CallSid + * @param payload + */ + Call.prototype._setCallSid = function (payload) { + var callSid = payload.callsid; + if (!callSid) { + return; + } + this.parameters.CallSid = callSid; + this._mediaHandler.callSid = callSid; + }; + /** + * String representation of the {@link Call} class. + * @private + */ + Call.toString = function () { return '[Twilio.Call class]'; }; + return Call; +}(events_1.EventEmitter)); +(function (Call) { + /** + * Possible states of the {@link Call}. + */ + var State; + (function (State) { + State["Closed"] = "closed"; + State["Connecting"] = "connecting"; + State["Open"] = "open"; + State["Pending"] = "pending"; + State["Reconnecting"] = "reconnecting"; + State["Ringing"] = "ringing"; + })(State = Call.State || (Call.State = {})); + /** + * Different issues that may have been experienced during a call, that can be + * reported to Twilio Insights via {@link Call}.postFeedback(). + */ + var FeedbackIssue; + (function (FeedbackIssue) { + FeedbackIssue["AudioLatency"] = "audio-latency"; + FeedbackIssue["ChoppyAudio"] = "choppy-audio"; + FeedbackIssue["DroppedCall"] = "dropped-call"; + FeedbackIssue["Echo"] = "echo"; + FeedbackIssue["NoisyCall"] = "noisy-call"; + FeedbackIssue["OneWayAudio"] = "one-way-audio"; + })(FeedbackIssue = Call.FeedbackIssue || (Call.FeedbackIssue = {})); + /** + * A rating of call quality experienced during a call, to be reported to Twilio Insights + * via {@link Call}.postFeedback(). + */ + var FeedbackScore; + (function (FeedbackScore) { + FeedbackScore[FeedbackScore["One"] = 1] = "One"; + FeedbackScore[FeedbackScore["Two"] = 2] = "Two"; + FeedbackScore[FeedbackScore["Three"] = 3] = "Three"; + FeedbackScore[FeedbackScore["Four"] = 4] = "Four"; + FeedbackScore[FeedbackScore["Five"] = 5] = "Five"; + })(FeedbackScore = Call.FeedbackScore || (Call.FeedbackScore = {})); + /** + * The directionality of the {@link Call}, whether incoming or outgoing. + */ + var CallDirection; + (function (CallDirection) { + CallDirection["Incoming"] = "INCOMING"; + CallDirection["Outgoing"] = "OUTGOING"; + })(CallDirection = Call.CallDirection || (Call.CallDirection = {})); + /** + * Valid audio codecs to use for the media connection. + */ + var Codec; + (function (Codec) { + Codec["Opus"] = "opus"; + Codec["PCMU"] = "pcmu"; + })(Codec = Call.Codec || (Call.Codec = {})); + /** + * Possible ICE Gathering failures + */ + var IceGatheringFailureReason; + (function (IceGatheringFailureReason) { + IceGatheringFailureReason["None"] = "none"; + IceGatheringFailureReason["Timeout"] = "timeout"; + })(IceGatheringFailureReason = Call.IceGatheringFailureReason || (Call.IceGatheringFailureReason = {})); + /** + * Possible media failures + */ + var MediaFailure; + (function (MediaFailure) { + MediaFailure["ConnectionDisconnected"] = "ConnectionDisconnected"; + MediaFailure["ConnectionFailed"] = "ConnectionFailed"; + MediaFailure["IceGatheringFailed"] = "IceGatheringFailed"; + MediaFailure["LowBytes"] = "LowBytes"; + })(MediaFailure = Call.MediaFailure || (Call.MediaFailure = {})); + /** + * Known call message types. + */ + var MessageType; + (function (MessageType) { + /** + * Allows for any object types to be defined by the user. + * When this value is used in the {@link Call.Message} object, + * The {@link Call.Message.content} can be of any type as long as + * it matches the MIME type defined in {@link Call.Message.contentType}. + */ + MessageType["UserDefinedMessage"] = "user-defined-message"; + })(MessageType = Call.MessageType || (Call.MessageType = {})); +})(Call || (Call = {})); +function generateTempCallSid() { + return 'TJSxxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + /* tslint:disable:no-bitwise */ + var r = Math.random() * 16 | 0; + var v = c === 'x' ? r : (r & 0x3 | 0x8); + /* tslint:enable:no-bitwise */ + return v.toString(16); + }); +} +exports.default = Call; + +},{"./backoff":8,"./constants":10,"./device":12,"./errors":15,"./log":18,"./rtc":26,"./rtc/icecandidate":25,"./rtc/sdp":31,"./statsMonitor":35,"./util":36,"./uuid":37,"events":39}],10:[function(require,module,exports){ +"use strict"; +/** + * This file is generated on build. To make changes, see /templates/constants.ts + */ +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SOUNDS_BASE_URL = exports.RELEASE_VERSION = exports.PACKAGE_NAME = exports.ECHO_TEST_DURATION = exports.COWBELL_AUDIO_URL = void 0; +var PACKAGE_NAME = '@twilio/voice-sdk'; +exports.PACKAGE_NAME = PACKAGE_NAME; +var RELEASE_VERSION = '2.10.0'; +exports.RELEASE_VERSION = RELEASE_VERSION; +var SOUNDS_BASE_URL = 'https://sdk.twilio.com/js/client/sounds/releases/1.0.0'; +exports.SOUNDS_BASE_URL = SOUNDS_BASE_URL; +var COWBELL_AUDIO_URL = SOUNDS_BASE_URL + "/cowbell.mp3?cache=" + RELEASE_VERSION; +exports.COWBELL_AUDIO_URL = COWBELL_AUDIO_URL; +var ECHO_TEST_DURATION = 20000; +exports.ECHO_TEST_DURATION = ECHO_TEST_DURATION; + +},{}],11:[function(require,module,exports){ +"use strict"; +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Deferred Promise + */ +var Deferred = /** @class */ (function () { + /** + * @constructor + */ + function Deferred() { + var _this = this; + this._promise = new Promise(function (resolve, reject) { + _this._resolve = resolve; + _this._reject = reject; + }); + } + Object.defineProperty(Deferred.prototype, "promise", { + /** + * @returns The {@link Deferred} Promise + */ + get: function () { + return this._promise; + }, + enumerable: false, + configurable: true + }); + /** + * Rejects this promise + */ + Deferred.prototype.reject = function (reason) { + this._reject(reason); + }; + /** + * Resolves this promise + */ + Deferred.prototype.resolve = function (value) { + this._resolve(value); + }; + return Deferred; +}()); +exports.default = Deferred; + +},{}],12:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @packageDocumentation + * @module Voice + * @preferred + * @publicapi + */ +var events_1 = require("events"); +var loglevel_1 = require("loglevel"); +var audiohelper_1 = require("./audiohelper"); +var audioprocessoreventobserver_1 = require("./audioprocessoreventobserver"); +var call_1 = require("./call"); +var C = require("./constants"); +var dialtonePlayer_1 = require("./dialtonePlayer"); +var errors_1 = require("./errors"); +var eventpublisher_1 = require("./eventpublisher"); +var log_1 = require("./log"); +var preflight_1 = require("./preflight/preflight"); +var pstream_1 = require("./pstream"); +var regions_1 = require("./regions"); +var rtc = require("./rtc"); +var getusermedia_1 = require("./rtc/getusermedia"); +var sound_1 = require("./sound"); +var util_1 = require("./util"); +var uuid_1 = require("./uuid"); +var REGISTRATION_INTERVAL = 30000; +var RINGTONE_PLAY_TIMEOUT = 2000; +var PUBLISHER_PRODUCT_NAME = 'twilio-js-sdk'; +var INVALID_TOKEN_MESSAGE = 'Parameter "token" must be of type "string".'; +/** + * Twilio Device. Allows registration for incoming calls, and placing outgoing calls. + * @publicapi + */ +var Device = /** @class */ (function (_super) { + __extends(Device, _super); + /** + * Construct a {@link Device} instance. The {@link Device} can be registered + * to make and listen for calls using {@link Device.register}. + * @constructor + * @param options + */ + function Device(token, options) { + var _a; + if (options === void 0) { options = {}; } + var _this = _super.call(this) || this; + /** + * The currently active {@link Call}, if there is one. + */ + _this._activeCall = null; + /** + * The AudioHelper instance associated with this {@link Device}. + */ + _this._audio = null; + /** + * The AudioProcessorEventObserver instance to use + */ + _this._audioProcessorEventObserver = null; + /** + * An audio input MediaStream to pass to new {@link Call} instances. + */ + _this._callInputStream = null; + /** + * An array of {@link Call}s. Though only one can be active, multiple may exist when there + * are multiple incoming, unanswered {@link Call}s. + */ + _this._calls = []; + /** + * An array of {@link Device} IDs to be used to play sounds through, to be passed to + * new {@link Call} instances. + */ + _this._callSinkIds = ['default']; + /** + * The list of chunder URIs that will be passed to PStream + */ + _this._chunderURIs = []; + /** + * Default options used by {@link Device}. + */ + _this._defaultOptions = { + allowIncomingWhileBusy: false, + closeProtection: false, + codecPreferences: [call_1.default.Codec.PCMU, call_1.default.Codec.Opus], + dscp: true, + enableImprovedSignalingErrorPrecision: false, + forceAggressiveIceNomination: false, + logLevel: loglevel_1.levels.ERROR, + maxCallSignalingTimeoutMs: 0, + preflight: false, + sounds: {}, + tokenRefreshMs: 10000, + voiceEventSidGenerator: uuid_1.generateVoiceEventSid, + }; + /** + * The name of the edge the {@link Device} is connected to. + */ + _this._edge = null; + /** + * The name of the home region the {@link Device} is connected to. + */ + _this._home = null; + /** + * The identity associated with this Device. + */ + _this._identity = null; + /** + * An instance of Logger to use. + */ + _this._log = new log_1.default('Device'); + /** + * The options passed to {@link Device} constructor or {@link Device.updateOptions}. + */ + _this._options = {}; + /** + * The preferred URI to (re)-connect signaling to. + */ + _this._preferredURI = null; + /** + * An Insights Event Publisher. + */ + _this._publisher = null; + /** + * The region the {@link Device} is connected to. + */ + _this._region = null; + /** + * A timeout ID for a setTimeout schedule to re-register the {@link Device}. + */ + _this._regTimer = null; + /** + * Boolean representing whether or not the {@link Device} was registered when + * receiving a signaling `offline`. Determines if the {@link Device} attempts + * a `re-register` once signaling is re-established when receiving a + * `connected` event from the stream. + */ + _this._shouldReRegister = false; + /** + * A Map of Sounds to play. + */ + _this._soundcache = new Map(); + /** + * The current status of the {@link Device}. + */ + _this._state = Device.State.Unregistered; + /** + * A map from {@link Device.State} to {@link Device.EventName}. + */ + _this._stateEventMapping = (_a = {}, + _a[Device.State.Destroyed] = Device.EventName.Destroyed, + _a[Device.State.Unregistered] = Device.EventName.Unregistered, + _a[Device.State.Registering] = Device.EventName.Registering, + _a[Device.State.Registered] = Device.EventName.Registered, + _a); + /** + * The Signaling stream. + */ + _this._stream = null; + /** + * A promise that will resolve when the Signaling stream is ready. + */ + _this._streamConnectedPromise = null; + /** + * A timeout to track when the current AccessToken will expire. + */ + _this._tokenWillExpireTimeout = null; + /** + * Create the default Insights payload + * @param call + */ + _this._createDefaultPayload = function (call) { + var payload = { + aggressive_nomination: _this._options.forceAggressiveIceNomination, + browser_extension: _this._isBrowserExtension, + dscp: !!_this._options.dscp, + ice_restart_enabled: true, + platform: rtc.getMediaEngine(), + sdk_version: C.RELEASE_VERSION, + }; + function setIfDefined(propertyName, value) { + if (value) { + payload[propertyName] = value; + } + } + if (call) { + var callSid = call.parameters.CallSid; + setIfDefined('call_sid', /^TJ/.test(callSid) ? undefined : callSid); + setIfDefined('temp_call_sid', call.outboundConnectionId); + setIfDefined('audio_codec', call.codec); + payload.direction = call.direction; + } + setIfDefined('gateway', _this._stream && _this._stream.gateway); + setIfDefined('region', _this._stream && _this._stream.region); + return payload; + }; + /** + * Called when a 'close' event is received from the signaling stream. + */ + _this._onSignalingClose = function () { + _this._stream = null; + _this._streamConnectedPromise = null; + }; + /** + * Called when a 'connected' event is received from the signaling stream. + */ + _this._onSignalingConnected = function (payload) { + var _a; + var region = regions_1.getRegionShortcode(payload.region); + _this._edge = payload.edge || regions_1.regionToEdge[region] || payload.region; + _this._region = region || payload.region; + _this._home = payload.home; + (_a = _this._publisher) === null || _a === void 0 ? void 0 : _a.setHost(regions_1.createEventGatewayURI(payload.home)); + if (payload.token) { + _this._identity = payload.token.identity; + if (typeof payload.token.ttl === 'number' && + typeof _this._options.tokenRefreshMs === 'number') { + var ttlMs = payload.token.ttl * 1000; + var timeoutMs = Math.max(0, ttlMs - _this._options.tokenRefreshMs); + _this._tokenWillExpireTimeout = setTimeout(function () { + _this._log.debug('#tokenWillExpire'); + _this.emit('tokenWillExpire', _this); + if (_this._tokenWillExpireTimeout) { + clearTimeout(_this._tokenWillExpireTimeout); + _this._tokenWillExpireTimeout = null; + } + }, timeoutMs); + } + } + var preferredURIs = regions_1.getChunderURIs(_this._edge); + if (preferredURIs.length > 0) { + var preferredURI = preferredURIs[0]; + _this._preferredURI = regions_1.createSignalingEndpointURL(preferredURI); + } + else { + _this._log.warn('Could not parse a preferred URI from the stream#connected event.'); + } + // The signaling stream emits a `connected` event after reconnection, if the + // device was registered before this, then register again. + if (_this._shouldReRegister) { + _this.register(); + } + }; + /** + * Called when an 'error' event is received from the signaling stream. + */ + _this._onSignalingError = function (payload) { + if (typeof payload !== 'object') { + return; + } + var originalError = payload.error, callsid = payload.callsid; + if (typeof originalError !== 'object') { + return; + } + var call = (typeof callsid === 'string' && _this._findCall(callsid)) || undefined; + var code = originalError.code, customMessage = originalError.message; + var twilioError = originalError.twilioError; + if (typeof code === 'number') { + if (code === 31201) { + twilioError = new errors_1.AuthorizationErrors.AuthenticationFailed(originalError); + } + else if (code === 31204) { + twilioError = new errors_1.AuthorizationErrors.AccessTokenInvalid(originalError); + } + else if (code === 31205) { + // Stop trying to register presence after token expires + _this._stopRegistrationTimer(); + twilioError = new errors_1.AuthorizationErrors.AccessTokenExpired(originalError); + } + else { + var errorConstructor = errors_1.getPreciseSignalingErrorByCode(!!_this._options.enableImprovedSignalingErrorPrecision, code); + if (typeof errorConstructor !== 'undefined') { + twilioError = new errorConstructor(originalError); + } + } + } + if (!twilioError) { + _this._log.error('Unknown signaling error: ', originalError); + twilioError = new errors_1.GeneralErrors.UnknownError(customMessage, originalError); + } + _this._log.error('Received error: ', twilioError); + _this._log.debug('#error', originalError); + _this.emit(Device.EventName.Error, twilioError, call); + }; + /** + * Called when an 'invite' event is received from the signaling stream. + */ + _this._onSignalingInvite = function (payload) { return __awaiter(_this, void 0, void 0, function () { + var wasBusy, callParameters, customParameters, call, play; + var _this = this; + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + wasBusy = !!this._activeCall; + if (wasBusy && !this._options.allowIncomingWhileBusy) { + this._log.info('Device busy; ignoring incoming invite'); + return [2 /*return*/]; + } + if (!payload.callsid || !payload.sdp) { + this._log.debug('#error', payload); + this.emit(Device.EventName.Error, new errors_1.ClientErrors.BadRequest('Malformed invite from gateway')); + return [2 /*return*/]; + } + callParameters = payload.parameters || {}; + callParameters.CallSid = callParameters.CallSid || payload.callsid; + customParameters = Object.assign({}, util_1.queryToJson(callParameters.Params)); + return [4 /*yield*/, this._makeCall(customParameters, { + callParameters: callParameters, + enableImprovedSignalingErrorPrecision: !!this._options.enableImprovedSignalingErrorPrecision, + offerSdp: payload.sdp, + reconnectToken: payload.reconnect, + voiceEventSidGenerator: this._options.voiceEventSidGenerator, + })]; + case 1: + call = _b.sent(); + this._calls.push(call); + call.once('accept', function () { + _this._soundcache.get(Device.SoundName.Incoming).stop(); + _this._publishNetworkChange(); + }); + play = (((_a = this._audio) === null || _a === void 0 ? void 0 : _a.incoming()) && !wasBusy) + ? function () { return _this._soundcache.get(Device.SoundName.Incoming).play(); } + : function () { return Promise.resolve(); }; + this._showIncomingCall(call, play); + return [2 /*return*/]; + } + }); + }); }; + /** + * Called when an 'offline' event is received from the signaling stream. + */ + _this._onSignalingOffline = function () { + _this._log.info('Stream is offline'); + _this._edge = null; + _this._region = null; + _this._shouldReRegister = _this.state !== Device.State.Unregistered; + _this._setState(Device.State.Unregistered); + }; + /** + * Called when a 'ready' event is received from the signaling stream. + */ + _this._onSignalingReady = function () { + _this._log.info('Stream is ready'); + _this._setState(Device.State.Registered); + }; + /** + * Publish a NetworkInformation#change event to Insights if there's an active {@link Call}. + */ + _this._publishNetworkChange = function () { + if (!_this._activeCall) { + return; + } + if (_this._networkInformation) { + _this._publisher.info('network-information', 'network-change', { + connection_type: _this._networkInformation.type, + downlink: _this._networkInformation.downlink, + downlinkMax: _this._networkInformation.downlinkMax, + effective_type: _this._networkInformation.effectiveType, + rtt: _this._networkInformation.rtt, + }, _this._activeCall); + } + }; + /** + * Update the input stream being used for calls so that any current call and all future calls + * will use the new input stream. + * @param inputStream + */ + _this._updateInputStream = function (inputStream) { + var call = _this._activeCall; + if (call && !inputStream) { + return Promise.reject(new errors_1.InvalidStateError('Cannot unset input device while a call is in progress.')); + } + _this._callInputStream = inputStream; + return call + ? call._setInputTracksFromStream(inputStream) + : Promise.resolve(); + }; + /** + * Update the device IDs of output devices being used to play sounds through. + * @param type - Whether to update ringtone or speaker sounds + * @param sinkIds - An array of device IDs + */ + _this._updateSinkIds = function (type, sinkIds) { + var promise = type === 'ringtone' + ? _this._updateRingtoneSinkIds(sinkIds) + : _this._updateSpeakerSinkIds(sinkIds); + return promise.then(function () { + _this._publisher.info('audio', type + "-devices-set", { + audio_device_ids: sinkIds, + }, _this._activeCall); + }, function (error) { + _this._publisher.error('audio', type + "-devices-set-failed", { + audio_device_ids: sinkIds, + message: error.message, + }, _this._activeCall); + throw error; + }); + }; + // Setup loglevel asap to avoid missed logs + _this._setupLoglevel(options.logLevel); + _this._logOptions('constructor', options); + _this.updateToken(token); + if (util_1.isLegacyEdge()) { + throw new errors_1.NotSupportedError('Microsoft Edge Legacy (https://support.microsoft.com/en-us/help/4533505/what-is-microsoft-edge-legacy) ' + + 'is deprecated and will not be able to connect to Twilio to make or receive calls after September 1st, 2020. ' + + 'Please see this documentation for a list of supported browsers ' + + 'https://www.twilio.com/docs/voice/client/javascript#supported-browsers'); + } + if (!Device.isSupported && options.ignoreBrowserSupport) { + if (window && window.location && window.location.protocol === 'http:') { + throw new errors_1.NotSupportedError("twilio.js wasn't able to find WebRTC browser support. This is most likely because this page is served over http rather than https, which does not support WebRTC in many browsers. Please load this page over https and try again."); + } + throw new errors_1.NotSupportedError("twilio.js 1.3+ SDKs require WebRTC browser support. For more information, see . If you have any questions about this announcement, please contact Twilio Support at ."); + } + if (window) { + var root = window; + var browser = root.msBrowser || root.browser || root.chrome; + _this._isBrowserExtension = (!!browser && !!browser.runtime && !!browser.runtime.id) + || (!!root.safari && !!root.safari.extension); + } + if (_this._isBrowserExtension) { + _this._log.info('Running as browser extension.'); + } + if (navigator) { + var n = navigator; + _this._networkInformation = n.connection + || n.mozConnection + || n.webkitConnection; + } + if (_this._networkInformation && typeof _this._networkInformation.addEventListener === 'function') { + _this._networkInformation.addEventListener('change', _this._publishNetworkChange); + } + Device._getOrCreateAudioContext(); + if (Device._audioContext) { + if (!Device._dialtonePlayer) { + Device._dialtonePlayer = new dialtonePlayer_1.default(Device._audioContext); + } + } + if (typeof Device._isUnifiedPlanDefault === 'undefined') { + Device._isUnifiedPlanDefault = typeof window !== 'undefined' + && typeof RTCPeerConnection !== 'undefined' + && typeof RTCRtpTransceiver !== 'undefined' + ? util_1.isUnifiedPlanDefault(window, window.navigator, RTCPeerConnection, RTCRtpTransceiver) + : false; + } + _this._boundDestroy = _this.destroy.bind(_this); + _this._boundConfirmClose = _this._confirmClose.bind(_this); + if (typeof window !== 'undefined' && window.addEventListener) { + window.addEventListener('unload', _this._boundDestroy); + window.addEventListener('pagehide', _this._boundDestroy); + } + _this.updateOptions(options); + return _this; + } + Object.defineProperty(Device, "audioContext", { + /** + * The AudioContext to be used by {@link Device} instances. + * @private + */ + get: function () { + return Device._audioContext; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Device, "extension", { + /** + * Which sound file extension is supported. + * @private + */ + get: function () { + // NOTE(mroberts): Node workaround. + var a = typeof document !== 'undefined' + ? document.createElement('audio') : { canPlayType: false }; + var canPlayMp3; + try { + canPlayMp3 = a.canPlayType && !!a.canPlayType('audio/mpeg').replace(/no/, ''); + } + catch (e) { + canPlayMp3 = false; + } + var canPlayVorbis; + try { + canPlayVorbis = a.canPlayType && !!a.canPlayType('audio/ogg;codecs=\'vorbis\'').replace(/no/, ''); + } + catch (e) { + canPlayVorbis = false; + } + return (canPlayVorbis && !canPlayMp3) ? 'ogg' : 'mp3'; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Device, "isSupported", { + /** + * Whether or not this SDK is supported by the current browser. + */ + get: function () { return rtc.enabled(); }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Device, "packageName", { + /** + * Package name of the SDK. + */ + get: function () { return C.PACKAGE_NAME; }, + enumerable: false, + configurable: true + }); + /** + * Run some tests to identify issues, if any, prohibiting successful calling. + * @param token - A Twilio JWT token string + * @param options + */ + Device.runPreflight = function (token, options) { + return new preflight_1.PreflightTest(token, __assign({ audioContext: Device._getOrCreateAudioContext() }, options)); + }; + /** + * String representation of {@link Device} class. + * @private + */ + Device.toString = function () { + return '[Twilio.Device class]'; + }; + Object.defineProperty(Device, "version", { + /** + * Current SDK version. + */ + get: function () { return C.RELEASE_VERSION; }, + enumerable: false, + configurable: true + }); + /** + * Initializes the AudioContext instance shared across the Voice SDK, + * or returns the existing instance if one has already been initialized. + */ + Device._getOrCreateAudioContext = function () { + if (!Device._audioContext) { + if (typeof AudioContext !== 'undefined') { + Device._audioContext = new AudioContext(); + } + else if (typeof webkitAudioContext !== 'undefined') { + Device._audioContext = new webkitAudioContext(); + } + } + return Device._audioContext; + }; + Object.defineProperty(Device.prototype, "audio", { + /** + * Return the {@link AudioHelper} used by this {@link Device}. + */ + get: function () { + return this._audio; + }, + enumerable: false, + configurable: true + }); + /** + * Make an outgoing Call. + * @param options + */ + Device.prototype.connect = function (options) { + if (options === void 0) { options = {}; } + return __awaiter(this, void 0, void 0, function () { + var activeCall, _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + this._log.debug('.connect', JSON.stringify(options && options.params || {}), options); + this._throwIfDestroyed(); + if (this._activeCall) { + throw new errors_1.InvalidStateError('A Call is already active'); + } + _a = this; + return [4 /*yield*/, this._makeCall(options.params || {}, { + enableImprovedSignalingErrorPrecision: !!this._options.enableImprovedSignalingErrorPrecision, + rtcConfiguration: options.rtcConfiguration, + voiceEventSidGenerator: this._options.voiceEventSidGenerator, + })]; + case 1: + activeCall = _a._activeCall = _b.sent(); + // Make sure any incoming calls are ignored + this._calls.splice(0).forEach(function (call) { return call.ignore(); }); + // Stop the incoming sound if it's playing + this._soundcache.get(Device.SoundName.Incoming).stop(); + activeCall.accept({ rtcConstraints: options.rtcConstraints }); + this._publishNetworkChange(); + return [2 /*return*/, activeCall]; + } + }); + }); + }; + Object.defineProperty(Device.prototype, "calls", { + /** + * Return the calls that this {@link Device} is maintaining. + */ + get: function () { + return this._calls; + }, + enumerable: false, + configurable: true + }); + /** + * Destroy the {@link Device}, freeing references to be garbage collected. + */ + Device.prototype.destroy = function () { + var _a; + this._log.debug('.destroy'); + this.disconnectAll(); + this._stopRegistrationTimer(); + this._destroyStream(); + this._destroyPublisher(); + this._destroyAudioHelper(); + (_a = this._audioProcessorEventObserver) === null || _a === void 0 ? void 0 : _a.destroy(); + if (this._networkInformation && typeof this._networkInformation.removeEventListener === 'function') { + this._networkInformation.removeEventListener('change', this._publishNetworkChange); + } + if (typeof window !== 'undefined' && window.removeEventListener) { + window.removeEventListener('beforeunload', this._boundConfirmClose); + window.removeEventListener('unload', this._boundDestroy); + window.removeEventListener('pagehide', this._boundDestroy); + } + this._setState(Device.State.Destroyed); + events_1.EventEmitter.prototype.removeAllListeners.call(this); + }; + /** + * Disconnect all {@link Call}s. + */ + Device.prototype.disconnectAll = function () { + this._log.debug('.disconnectAll'); + var calls = this._calls.splice(0); + calls.forEach(function (call) { return call.disconnect(); }); + if (this._activeCall) { + this._activeCall.disconnect(); + } + }; + Object.defineProperty(Device.prototype, "edge", { + /** + * Returns the {@link Edge} value the {@link Device} is currently connected + * to. The value will be `null` when the {@link Device} is offline. + */ + get: function () { + return this._edge; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Device.prototype, "home", { + /** + * Returns the home value the {@link Device} is currently connected + * to. The value will be `null` when the {@link Device} is offline. + */ + get: function () { + return this._home; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Device.prototype, "identity", { + /** + * Returns the identity associated with the {@link Device} for incoming calls. Only + * populated when registered. + */ + get: function () { + return this._identity; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Device.prototype, "isBusy", { + /** + * Whether the Device is currently on an active Call. + */ + get: function () { + return !!this._activeCall; + }, + enumerable: false, + configurable: true + }); + /** + * Register the `Device` to the Twilio backend, allowing it to receive calls. + */ + Device.prototype.register = function () { + return __awaiter(this, void 0, void 0, function () { + var stream, streamReadyPromise; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + this._log.debug('.register'); + if (this.state !== Device.State.Unregistered) { + throw new errors_1.InvalidStateError("Attempt to register when device is in state \"" + this.state + "\". " + + ("Must be \"" + Device.State.Unregistered + "\".")); + } + this._shouldReRegister = false; + this._setState(Device.State.Registering); + return [4 /*yield*/, (this._streamConnectedPromise || this._setupStream())]; + case 1: + stream = _a.sent(); + streamReadyPromise = new Promise(function (resolve) { + _this.once(Device.State.Registered, resolve); + }); + return [4 /*yield*/, this._sendPresence(true)]; + case 2: + _a.sent(); + return [4 /*yield*/, streamReadyPromise]; + case 3: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + Object.defineProperty(Device.prototype, "state", { + /** + * Get the state of this {@link Device} instance + */ + get: function () { + return this._state; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Device.prototype, "token", { + /** + * Get the token used by this {@link Device}. + */ + get: function () { + return this._token; + }, + enumerable: false, + configurable: true + }); + /** + * String representation of {@link Device} instance. + * @private + */ + Device.prototype.toString = function () { + return '[Twilio.Device instance]'; + }; + /** + * Unregister the `Device` to the Twilio backend, disallowing it to receive + * calls. + */ + Device.prototype.unregister = function () { + return __awaiter(this, void 0, void 0, function () { + var stream, streamOfflinePromise; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + this._log.debug('.unregister'); + if (this.state !== Device.State.Registered) { + throw new errors_1.InvalidStateError("Attempt to unregister when device is in state \"" + this.state + "\". " + + ("Must be \"" + Device.State.Registered + "\".")); + } + this._shouldReRegister = false; + return [4 /*yield*/, this._streamConnectedPromise]; + case 1: + stream = _a.sent(); + streamOfflinePromise = new Promise(function (resolve) { + stream.on('offline', resolve); + }); + return [4 /*yield*/, this._sendPresence(false)]; + case 2: + _a.sent(); + return [4 /*yield*/, streamOfflinePromise]; + case 3: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + /** + * Set the options used within the {@link Device}. + * @param options + */ + Device.prototype.updateOptions = function (options) { + if (options === void 0) { options = {}; } + this._logOptions('updateOptions', options); + if (this.state === Device.State.Destroyed) { + throw new errors_1.InvalidStateError("Attempt to \"updateOptions\" when device is in state \"" + this.state + "\"."); + } + this._options = __assign(__assign(__assign({}, this._defaultOptions), this._options), options); + var originalChunderURIs = new Set(this._chunderURIs); + var chunderw = typeof this._options.chunderw === 'string' + ? [this._options.chunderw] + : Array.isArray(this._options.chunderw) && this._options.chunderw; + var newChunderURIs = this._chunderURIs = (chunderw || regions_1.getChunderURIs(this._options.edge)).map(regions_1.createSignalingEndpointURL); + var hasChunderURIsChanged = originalChunderURIs.size !== newChunderURIs.length; + if (!hasChunderURIsChanged) { + for (var _i = 0, newChunderURIs_1 = newChunderURIs; _i < newChunderURIs_1.length; _i++) { + var uri = newChunderURIs_1[_i]; + if (!originalChunderURIs.has(uri)) { + hasChunderURIsChanged = true; + break; + } + } + } + if (this.isBusy && hasChunderURIsChanged) { + throw new errors_1.InvalidStateError('Cannot change Edge while on an active Call'); + } + this._setupLoglevel(this._options.logLevel); + if (this._options.dscp) { + if (!this._options.rtcConstraints) { + this._options.rtcConstraints = {}; + } + this._options.rtcConstraints.optional = [{ googDscp: true }]; + } + for (var _a = 0, _b = Object.keys(Device._defaultSounds); _a < _b.length; _a++) { + var name_1 = _b[_a]; + var soundDef = Device._defaultSounds[name_1]; + var defaultUrl = C.SOUNDS_BASE_URL + "/" + soundDef.filename + "." + Device.extension + + ("?cache=" + C.RELEASE_VERSION); + var soundUrl = this._options.sounds && this._options.sounds[name_1] || defaultUrl; + var sound = new (this._options.Sound || sound_1.default)(name_1, soundUrl, { + audioContext: this._options.disableAudioContextSounds ? null : Device.audioContext, + maxDuration: soundDef.maxDuration, + shouldLoop: soundDef.shouldLoop, + }); + this._soundcache.set(name_1, sound); + } + this._setupAudioHelper(); + this._setupPublisher(); + if (hasChunderURIsChanged && this._streamConnectedPromise) { + this._setupStream(); + } + // Setup close protection and make sure we clean up ongoing calls on unload. + if (typeof window !== 'undefined' && + typeof window.addEventListener === 'function' && + this._options.closeProtection) { + window.removeEventListener('beforeunload', this._boundConfirmClose); + window.addEventListener('beforeunload', this._boundConfirmClose); + } + }; + /** + * Update the token used by this {@link Device} to connect to Twilio. + * It is recommended to call this API after [[Device.tokenWillExpireEvent]] is emitted, + * and before or after a call to prevent a potential ~1s audio loss during the update process. + * @param token + */ + Device.prototype.updateToken = function (token) { + this._log.debug('.updateToken'); + if (this.state === Device.State.Destroyed) { + throw new errors_1.InvalidStateError("Attempt to \"updateToken\" when device is in state \"" + this.state + "\"."); + } + if (typeof token !== 'string') { + throw new errors_1.InvalidArgumentError(INVALID_TOKEN_MESSAGE); + } + this._token = token; + if (this._stream) { + this._stream.setToken(this._token); + } + if (this._publisher) { + this._publisher.setToken(this._token); + } + }; + /** + * Called on window's beforeunload event if closeProtection is enabled, + * preventing users from accidentally navigating away from an active call. + * @param event + */ + Device.prototype._confirmClose = function (event) { + if (!this._activeCall) { + return ''; + } + var closeProtection = this._options.closeProtection || false; + var confirmationMsg = typeof closeProtection !== 'string' + ? 'A call is currently in-progress. Leaving or reloading this page will end the call.' + : closeProtection; + (event || window.event).returnValue = confirmationMsg; + return confirmationMsg; + }; + /** + * Destroy the AudioHelper. + */ + Device.prototype._destroyAudioHelper = function () { + if (!this._audio) { + return; + } + this._audio._destroy(); + this._audio = null; + }; + /** + * Destroy the publisher. + */ + Device.prototype._destroyPublisher = function () { + // Attempt to destroy non-existent publisher. + if (!this._publisher) { + return; + } + this._publisher = null; + }; + /** + * Destroy the connection to the signaling server. + */ + Device.prototype._destroyStream = function () { + if (this._stream) { + this._stream.removeListener('close', this._onSignalingClose); + this._stream.removeListener('connected', this._onSignalingConnected); + this._stream.removeListener('error', this._onSignalingError); + this._stream.removeListener('invite', this._onSignalingInvite); + this._stream.removeListener('offline', this._onSignalingOffline); + this._stream.removeListener('ready', this._onSignalingReady); + this._stream.destroy(); + this._stream = null; + } + this._onSignalingOffline(); + this._streamConnectedPromise = null; + }; + /** + * Find a {@link Call} by its CallSid. + * @param callSid + */ + Device.prototype._findCall = function (callSid) { + return this._calls.find(function (call) { return call.parameters.CallSid === callSid + || call.outboundConnectionId === callSid; }) || null; + }; + /** + * Utility function to log device options + */ + Device.prototype._logOptions = function (caller, options) { + if (options === void 0) { options = {}; } + // Selectively log options that users can modify. + // Also, convert user overrides. + // This prevents potential app crash when calling JSON.stringify + // and when sending log strings remotely + var userOptions = [ + 'allowIncomingWhileBusy', + 'appName', + 'appVersion', + 'closeProtection', + 'codecPreferences', + 'disableAudioContextSounds', + 'dscp', + 'edge', + 'enableImprovedSignalingErrorPrecision', + 'forceAggressiveIceNomination', + 'logLevel', + 'maxAverageBitrate', + 'maxCallSignalingTimeoutMs', + 'sounds', + 'tokenRefreshMs', + ]; + var userOptionOverrides = [ + 'RTCPeerConnection', + 'enumerateDevices', + 'getUserMedia', + ]; + if (typeof options === 'object') { + var toLog_1 = __assign({}, options); + Object.keys(toLog_1).forEach(function (key) { + if (!userOptions.includes(key) && !userOptionOverrides.includes(key)) { + delete toLog_1[key]; + } + if (userOptionOverrides.includes(key)) { + toLog_1[key] = true; + } + }); + this._log.debug("." + caller, JSON.stringify(toLog_1)); + } + }; + /** + * Create a new {@link Call}. + * @param twimlParams - A flat object containing key:value pairs to be sent to the TwiML app. + * @param options - Options to be used to instantiate the {@link Call}. + */ + Device.prototype._makeCall = function (twimlParams, options) { + return __awaiter(this, void 0, void 0, function () { + var config, maybeUnsetPreferredUri, call; + var _a; + var _this = this; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (typeof Device._isUnifiedPlanDefault === 'undefined') { + throw new errors_1.InvalidStateError('Device has not been initialized.'); + } + _a = { + audioHelper: this._audio, + isUnifiedPlanDefault: Device._isUnifiedPlanDefault, + onIgnore: function () { + _this._soundcache.get(Device.SoundName.Incoming).stop(); + } + }; + return [4 /*yield*/, (this._streamConnectedPromise || this._setupStream())]; + case 1: + config = (_a.pstream = _b.sent(), + _a.publisher = this._publisher, + _a.soundcache = this._soundcache, + _a); + options = Object.assign({ + MediaStream: this._options.MediaStream || rtc.PeerConnection, + RTCPeerConnection: this._options.RTCPeerConnection, + beforeAccept: function (currentCall) { + if (!_this._activeCall || _this._activeCall === currentCall) { + return; + } + _this._activeCall.disconnect(); + _this._removeCall(_this._activeCall); + }, + codecPreferences: this._options.codecPreferences, + customSounds: this._options.sounds, + dialtonePlayer: Device._dialtonePlayer, + dscp: this._options.dscp, + // TODO(csantos): Remove forceAggressiveIceNomination option in 3.x + forceAggressiveIceNomination: this._options.forceAggressiveIceNomination, + getInputStream: function () { return _this._options.fileInputStream || _this._callInputStream; }, + getSinkIds: function () { return _this._callSinkIds; }, + maxAverageBitrate: this._options.maxAverageBitrate, + preflight: this._options.preflight, + rtcConstraints: this._options.rtcConstraints, + shouldPlayDisconnect: function () { var _a; return (_a = _this._audio) === null || _a === void 0 ? void 0 : _a.disconnect(); }, + twimlParams: twimlParams, + voiceEventSidGenerator: this._options.voiceEventSidGenerator, + }, options); + maybeUnsetPreferredUri = function () { + if (!_this._stream) { + _this._log.warn('UnsetPreferredUri called without a stream'); + return; + } + if (_this._activeCall === null && _this._calls.length === 0) { + _this._stream.updatePreferredURI(null); + } + }; + call = new (this._options.Call || call_1.default)(config, options); + this._publisher.info('settings', 'init', { + RTCPeerConnection: !!this._options.RTCPeerConnection, + enumerateDevices: !!this._options.enumerateDevices, + getUserMedia: !!this._options.getUserMedia, + }, call); + call.once('accept', function () { + var _a; + _this._stream.updatePreferredURI(_this._preferredURI); + _this._removeCall(call); + _this._activeCall = call; + if (_this._audio) { + _this._audio._maybeStartPollingVolume(); + } + if (call.direction === call_1.default.CallDirection.Outgoing && ((_a = _this._audio) === null || _a === void 0 ? void 0 : _a.outgoing())) { + _this._soundcache.get(Device.SoundName.Outgoing).play(); + } + var data = { edge: _this._edge || _this._region }; + if (_this._options.edge) { + data['selected_edge'] = Array.isArray(_this._options.edge) + ? _this._options.edge + : [_this._options.edge]; + } + _this._publisher.info('settings', 'edge', data, call); + }); + call.addListener('error', function (error) { + if (call.status() === 'closed') { + _this._removeCall(call); + maybeUnsetPreferredUri(); + } + if (_this._audio) { + _this._audio._maybeStopPollingVolume(); + } + _this._maybeStopIncomingSound(); + }); + call.once('cancel', function () { + _this._log.info("Canceled: " + call.parameters.CallSid); + _this._removeCall(call); + maybeUnsetPreferredUri(); + if (_this._audio) { + _this._audio._maybeStopPollingVolume(); + } + _this._maybeStopIncomingSound(); + }); + call.once('disconnect', function () { + if (_this._audio) { + _this._audio._maybeStopPollingVolume(); + } + _this._removeCall(call); + maybeUnsetPreferredUri(); + /** + * NOTE(kamalbennani): We need to stop the incoming sound when the call is + * disconnected right after the user has accepted the call (activeCall.accept()), and before + * the call has been fully connected (i.e. before the `pstream.answer` event) + */ + _this._maybeStopIncomingSound(); + }); + call.once('reject', function () { + _this._log.info("Rejected: " + call.parameters.CallSid); + if (_this._audio) { + _this._audio._maybeStopPollingVolume(); + } + _this._removeCall(call); + maybeUnsetPreferredUri(); + _this._maybeStopIncomingSound(); + }); + call.on('transportClose', function () { + if (call.status() !== call_1.default.State.Pending) { + return; + } + if (_this._audio) { + _this._audio._maybeStopPollingVolume(); + } + _this._removeCall(call); + /** + * NOTE(mhuynh): We don't want to call `maybeUnsetPreferredUri` because + * a `transportClose` will happen during signaling reconnection. + */ + _this._maybeStopIncomingSound(); + }); + return [2 /*return*/, call]; + } + }); + }); + }; + /** + * Stop the incoming sound if no {@link Call}s remain. + */ + Device.prototype._maybeStopIncomingSound = function () { + if (!this._calls.length) { + this._soundcache.get(Device.SoundName.Incoming).stop(); + } + }; + /** + * Remove a {@link Call} from device.calls by reference + * @param call + */ + Device.prototype._removeCall = function (call) { + if (this._activeCall === call) { + this._activeCall = null; + } + for (var i = this._calls.length - 1; i >= 0; i--) { + if (call === this._calls[i]) { + this._calls.splice(i, 1); + } + } + }; + /** + * Register with the signaling server. + */ + Device.prototype._sendPresence = function (presence) { + return __awaiter(this, void 0, void 0, function () { + var stream; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this._streamConnectedPromise]; + case 1: + stream = _a.sent(); + if (!stream) { + return [2 /*return*/]; + } + stream.register({ audio: presence }); + if (presence) { + this._startRegistrationTimer(); + } + else { + this._stopRegistrationTimer(); + } + return [2 /*return*/]; + } + }); + }); + }; + /** + * Helper function that sets and emits the state of the device. + * @param state The new state of the device. + */ + Device.prototype._setState = function (state) { + if (state === this.state) { + return; + } + this._state = state; + var name = this._stateEventMapping[state]; + this._log.debug("#" + name); + this.emit(name); + }; + /** + * Set up an audio helper for usage by this {@link Device}. + */ + Device.prototype._setupAudioHelper = function () { + var _this = this; + if (!this._audioProcessorEventObserver) { + this._audioProcessorEventObserver = new audioprocessoreventobserver_1.AudioProcessorEventObserver(); + this._audioProcessorEventObserver.on('event', function (_a) { + var name = _a.name, group = _a.group; + _this._publisher.info(group, name, {}, _this._activeCall); + }); + } + var audioOptions = { + audioContext: Device.audioContext, + audioProcessorEventObserver: this._audioProcessorEventObserver, + enumerateDevices: this._options.enumerateDevices, + getUserMedia: this._options.getUserMedia || getusermedia_1.default, + }; + if (this._audio) { + this._log.info('Found existing audio helper; updating options...'); + this._audio._updateUserOptions(audioOptions); + return; + } + this._audio = new (this._options.AudioHelper || audiohelper_1.default)(this._updateSinkIds, this._updateInputStream, audioOptions); + this._audio.on('deviceChange', function (lostActiveDevices) { + var activeCall = _this._activeCall; + var deviceIds = lostActiveDevices.map(function (device) { return device.deviceId; }); + _this._publisher.info('audio', 'device-change', { + lost_active_device_ids: deviceIds, + }, activeCall); + if (activeCall) { + activeCall['_mediaHandler']._onInputDevicesChanged(); + } + }); + }; + /** + * Setup logger's loglevel + */ + Device.prototype._setupLoglevel = function (logLevel) { + var level = typeof logLevel === 'number' || + typeof logLevel === 'string' ? + logLevel : loglevel_1.levels.ERROR; + this._log.setDefaultLevel(level); + this._log.info('Set logger default level to', level); + }; + /** + * Create and set a publisher for the {@link Device} to use. + */ + Device.prototype._setupPublisher = function () { + var _this = this; + if (this._publisher) { + this._log.info('Found existing publisher; destroying...'); + this._destroyPublisher(); + } + var publisherOptions = { + defaultPayload: this._createDefaultPayload, + metadata: { + app_name: this._options.appName, + app_version: this._options.appVersion, + }, + }; + if (this._options.eventgw) { + publisherOptions.host = this._options.eventgw; + } + if (this._home) { + publisherOptions.host = regions_1.createEventGatewayURI(this._home); + } + this._publisher = new (this._options.Publisher || eventpublisher_1.default)(PUBLISHER_PRODUCT_NAME, this.token, publisherOptions); + if (this._options.publishEvents === false) { + this._publisher.disable(); + } + else { + this._publisher.on('error', function (error) { + _this._log.warn('Cannot connect to insights.', error); + }); + } + return this._publisher; + }; + /** + * Set up the connection to the signaling server. Tears down an existing + * stream if called while a stream exists. + */ + Device.prototype._setupStream = function () { + var _this = this; + if (this._stream) { + this._log.info('Found existing stream; destroying...'); + this._destroyStream(); + } + this._log.info('Setting up VSP'); + this._stream = new (this._options.PStream || pstream_1.default)(this.token, this._chunderURIs, { + backoffMaxMs: this._options.backoffMaxMs, + maxPreferredDurationMs: this._options.maxCallSignalingTimeoutMs, + }); + this._stream.addListener('close', this._onSignalingClose); + this._stream.addListener('connected', this._onSignalingConnected); + this._stream.addListener('error', this._onSignalingError); + this._stream.addListener('invite', this._onSignalingInvite); + this._stream.addListener('offline', this._onSignalingOffline); + this._stream.addListener('ready', this._onSignalingReady); + return this._streamConnectedPromise = new Promise(function (resolve) { + return _this._stream.once('connected', function () { + resolve(_this._stream); + }); + }); + }; + /** + * Start playing the incoming ringtone, and subsequently emit the incoming event. + * @param call + * @param play - The function to be used to play the sound. Must return a Promise. + */ + Device.prototype._showIncomingCall = function (call, play) { + var _this = this; + var timeout; + return Promise.race([ + play(), + new Promise(function (resolve, reject) { + timeout = setTimeout(function () { + var msg = 'Playing incoming ringtone took too long; it might not play. Continuing execution...'; + reject(new Error(msg)); + }, RINGTONE_PLAY_TIMEOUT); + }), + ]).catch(function (reason) { + _this._log.warn(reason.message); + }).then(function () { + clearTimeout(timeout); + _this._log.debug('#incoming', JSON.stringify({ + customParameters: call.customParameters, + parameters: call.parameters, + })); + _this.emit(Device.EventName.Incoming, call); + }); + }; + /** + * Set a timeout to send another register message to the signaling server. + */ + Device.prototype._startRegistrationTimer = function () { + var _this = this; + this._stopRegistrationTimer(); + this._regTimer = setTimeout(function () { + _this._sendPresence(true); + }, REGISTRATION_INTERVAL); + }; + /** + * Stop sending registration messages to the signaling server. + */ + Device.prototype._stopRegistrationTimer = function () { + if (this._regTimer) { + clearTimeout(this._regTimer); + } + }; + /** + * Throw an error if the {@link Device} is destroyed. + */ + Device.prototype._throwIfDestroyed = function () { + if (this.state === Device.State.Destroyed) { + throw new errors_1.InvalidStateError('Device has been destroyed.'); + } + }; + /** + * Update the device IDs of output devices being used to play the incoming ringtone through. + * @param sinkIds - An array of device IDs + */ + Device.prototype._updateRingtoneSinkIds = function (sinkIds) { + return Promise.resolve(this._soundcache.get(Device.SoundName.Incoming).setSinkIds(sinkIds)); + }; + /** + * Update the device IDs of output devices being used to play the non-ringtone sounds + * and Call audio through. + * @param sinkIds - An array of device IDs + */ + Device.prototype._updateSpeakerSinkIds = function (sinkIds) { + Array.from(this._soundcache.entries()) + .filter(function (entry) { return entry[0] !== Device.SoundName.Incoming; }) + .forEach(function (entry) { return entry[1].setSinkIds(sinkIds); }); + this._callSinkIds = sinkIds; + var call = this._activeCall; + return call + ? call._setSinkIds(sinkIds) + : Promise.resolve(); + }; + Device._defaultSounds = { + disconnect: { filename: 'disconnect', maxDuration: 3000 }, + dtmf0: { filename: 'dtmf-0', maxDuration: 1000 }, + dtmf1: { filename: 'dtmf-1', maxDuration: 1000 }, + dtmf2: { filename: 'dtmf-2', maxDuration: 1000 }, + dtmf3: { filename: 'dtmf-3', maxDuration: 1000 }, + dtmf4: { filename: 'dtmf-4', maxDuration: 1000 }, + dtmf5: { filename: 'dtmf-5', maxDuration: 1000 }, + dtmf6: { filename: 'dtmf-6', maxDuration: 1000 }, + dtmf7: { filename: 'dtmf-7', maxDuration: 1000 }, + dtmf8: { filename: 'dtmf-8', maxDuration: 1000 }, + dtmf9: { filename: 'dtmf-9', maxDuration: 1000 }, + dtmfh: { filename: 'dtmf-hash', maxDuration: 1000 }, + dtmfs: { filename: 'dtmf-star', maxDuration: 1000 }, + incoming: { filename: 'incoming', shouldLoop: true }, + outgoing: { filename: 'outgoing', maxDuration: 3000 }, + }; + return Device; +}(events_1.EventEmitter)); +(function (Device) { + /** + * All valid {@link Device} event names. + */ + var EventName; + (function (EventName) { + EventName["Error"] = "error"; + EventName["Incoming"] = "incoming"; + EventName["Destroyed"] = "destroyed"; + EventName["Unregistered"] = "unregistered"; + EventName["Registering"] = "registering"; + EventName["Registered"] = "registered"; + EventName["TokenWillExpire"] = "tokenWillExpire"; + })(EventName = Device.EventName || (Device.EventName = {})); + /** + * All possible {@link Device} states. + */ + var State; + (function (State) { + State["Destroyed"] = "destroyed"; + State["Unregistered"] = "unregistered"; + State["Registering"] = "registering"; + State["Registered"] = "registered"; + })(State = Device.State || (Device.State = {})); + /** + * Names of all sounds handled by the {@link Device}. + */ + var SoundName; + (function (SoundName) { + SoundName["Incoming"] = "incoming"; + SoundName["Outgoing"] = "outgoing"; + SoundName["Disconnect"] = "disconnect"; + SoundName["Dtmf0"] = "dtmf0"; + SoundName["Dtmf1"] = "dtmf1"; + SoundName["Dtmf2"] = "dtmf2"; + SoundName["Dtmf3"] = "dtmf3"; + SoundName["Dtmf4"] = "dtmf4"; + SoundName["Dtmf5"] = "dtmf5"; + SoundName["Dtmf6"] = "dtmf6"; + SoundName["Dtmf7"] = "dtmf7"; + SoundName["Dtmf8"] = "dtmf8"; + SoundName["Dtmf9"] = "dtmf9"; + SoundName["DtmfS"] = "dtmfs"; + SoundName["DtmfH"] = "dtmfh"; + })(SoundName = Device.SoundName || (Device.SoundName = {})); +})(Device || (Device = {})); +exports.default = Device; + +},{"./audiohelper":3,"./audioprocessoreventobserver":7,"./call":9,"./constants":10,"./dialtonePlayer":13,"./errors":15,"./eventpublisher":17,"./log":18,"./preflight/preflight":20,"./pstream":21,"./regions":22,"./rtc":26,"./rtc/getusermedia":24,"./sound":34,"./util":36,"./uuid":37,"events":39,"loglevel":43}],13:[function(require,module,exports){ +"use strict"; +/** + * @packageDocumentation + * @module Tools + * @internalapi + */ +Object.defineProperty(exports, "__esModule", { value: true }); +var errors_1 = require("./errors"); +/** + * A Map of DTMF Sound Names to their mock frequency pairs. + */ +var bandFrequencies = { + dtmf0: [1360, 960], + dtmf1: [1230, 720], + dtmf2: [1360, 720], + dtmf3: [1480, 720], + dtmf4: [1230, 790], + dtmf5: [1360, 790], + dtmf6: [1480, 790], + dtmf7: [1230, 870], + dtmf8: [1360, 870], + dtmf9: [1480, 870], + dtmfh: [1480, 960], + dtmfs: [1230, 960], +}; +var DialtonePlayer = /** @class */ (function () { + function DialtonePlayer(_context) { + var _this = this; + this._context = _context; + /** + * Gain nodes, reducing the frequency. + */ + this._gainNodes = []; + this._gainNodes = [ + this._context.createGain(), + this._context.createGain(), + ]; + this._gainNodes.forEach(function (gainNode) { + gainNode.connect(_this._context.destination); + gainNode.gain.value = 0.1; + _this._gainNodes.push(gainNode); + }); + } + DialtonePlayer.prototype.cleanup = function () { + this._gainNodes.forEach(function (gainNode) { + gainNode.disconnect(); + }); + }; + /** + * Play the dual frequency tone for the passed DTMF name. + * @param sound + */ + DialtonePlayer.prototype.play = function (sound) { + var _this = this; + var frequencies = bandFrequencies[sound]; + if (!frequencies) { + throw new errors_1.InvalidArgumentError('Invalid DTMF sound name'); + } + var oscillators = [ + this._context.createOscillator(), + this._context.createOscillator(), + ]; + oscillators.forEach(function (oscillator, i) { + oscillator.type = 'sine'; + oscillator.frequency.value = frequencies[i]; + oscillator.connect(_this._gainNodes[i]); + oscillator.start(); + oscillator.stop(_this._context.currentTime + 0.1); + oscillator.addEventListener('ended', function () { return oscillator.disconnect(); }); + }); + }; + return DialtonePlayer; +}()); +exports.default = DialtonePlayer; + +},{"./errors":15}],14:[function(require,module,exports){ +"use strict"; +/* tslint:disable max-classes-per-file max-line-length */ +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.errorsByCode = exports.MediaErrors = exports.SignalingErrors = exports.UserMediaErrors = exports.MalformedRequestErrors = exports.GeneralErrors = exports.SIPServerErrors = exports.ClientErrors = exports.SignatureValidationErrors = exports.AuthorizationErrors = exports.TwilioError = void 0; +/** + * This is a generated file. Any modifications here will be overwritten. See scripts/errors.js. + */ +var twilioError_1 = require("./twilioError"); +exports.TwilioError = twilioError_1.default; +var AuthorizationErrors; +(function (AuthorizationErrors) { + var AccessTokenInvalid = /** @class */ (function (_super) { + __extends(AccessTokenInvalid, _super); + function AccessTokenInvalid(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 20101; + _this.description = 'Invalid access token'; + _this.explanation = 'Twilio was unable to validate your Access Token'; + _this.name = 'AccessTokenInvalid'; + _this.solutions = []; + Object.setPrototypeOf(_this, AuthorizationErrors.AccessTokenInvalid.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return AccessTokenInvalid; + }(twilioError_1.default)); + AuthorizationErrors.AccessTokenInvalid = AccessTokenInvalid; + var AccessTokenExpired = /** @class */ (function (_super) { + __extends(AccessTokenExpired, _super); + function AccessTokenExpired(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 20104; + _this.description = 'Access token expired or expiration date invalid'; + _this.explanation = 'The Access Token provided to the Twilio API has expired, the expiration time specified in the token was invalid, or the expiration time specified was too far in the future'; + _this.name = 'AccessTokenExpired'; + _this.solutions = []; + Object.setPrototypeOf(_this, AuthorizationErrors.AccessTokenExpired.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return AccessTokenExpired; + }(twilioError_1.default)); + AuthorizationErrors.AccessTokenExpired = AccessTokenExpired; + var AuthenticationFailed = /** @class */ (function (_super) { + __extends(AuthenticationFailed, _super); + function AuthenticationFailed(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 20151; + _this.description = 'Authentication Failed'; + _this.explanation = 'The Authentication with the provided JWT failed'; + _this.name = 'AuthenticationFailed'; + _this.solutions = []; + Object.setPrototypeOf(_this, AuthorizationErrors.AuthenticationFailed.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return AuthenticationFailed; + }(twilioError_1.default)); + AuthorizationErrors.AuthenticationFailed = AuthenticationFailed; +})(AuthorizationErrors = exports.AuthorizationErrors || (exports.AuthorizationErrors = {})); +var SignatureValidationErrors; +(function (SignatureValidationErrors) { + var AccessTokenSignatureValidationFailed = /** @class */ (function (_super) { + __extends(AccessTokenSignatureValidationFailed, _super); + function AccessTokenSignatureValidationFailed(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = [ + 'The access token has an invalid Account SID, API Key, or API Key Secret.', + ]; + _this.code = 31202; + _this.description = 'Signature validation failed.'; + _this.explanation = 'The provided access token failed signature validation.'; + _this.name = 'AccessTokenSignatureValidationFailed'; + _this.solutions = [ + 'Ensure the Account SID, API Key, and API Key Secret are valid when generating your access token.', + ]; + Object.setPrototypeOf(_this, SignatureValidationErrors.AccessTokenSignatureValidationFailed.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return AccessTokenSignatureValidationFailed; + }(twilioError_1.default)); + SignatureValidationErrors.AccessTokenSignatureValidationFailed = AccessTokenSignatureValidationFailed; +})(SignatureValidationErrors = exports.SignatureValidationErrors || (exports.SignatureValidationErrors = {})); +var ClientErrors; +(function (ClientErrors) { + var BadRequest = /** @class */ (function (_super) { + __extends(BadRequest, _super); + function BadRequest(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31400; + _this.description = 'Bad Request (HTTP/SIP)'; + _this.explanation = 'The request could not be understood due to malformed syntax.'; + _this.name = 'BadRequest'; + _this.solutions = []; + Object.setPrototypeOf(_this, ClientErrors.BadRequest.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return BadRequest; + }(twilioError_1.default)); + ClientErrors.BadRequest = BadRequest; + var NotFound = /** @class */ (function (_super) { + __extends(NotFound, _super); + function NotFound(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = [ + 'The outbound call was made to an invalid phone number.', + 'The TwiML application sid is missing a Voice URL.', + ]; + _this.code = 31404; + _this.description = 'Not Found (HTTP/SIP)'; + _this.explanation = 'The server has not found anything matching the request.'; + _this.name = 'NotFound'; + _this.solutions = [ + 'Ensure the phone number dialed is valid.', + 'Ensure the TwiML application is configured correctly with a Voice URL link.', + ]; + Object.setPrototypeOf(_this, ClientErrors.NotFound.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return NotFound; + }(twilioError_1.default)); + ClientErrors.NotFound = NotFound; + var TemporarilyUnavailable = /** @class */ (function (_super) { + __extends(TemporarilyUnavailable, _super); + function TemporarilyUnavailable(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31480; + _this.description = 'Temporarily Unavailable (SIP)'; + _this.explanation = 'The callee is currently unavailable.'; + _this.name = 'TemporarilyUnavailable'; + _this.solutions = []; + Object.setPrototypeOf(_this, ClientErrors.TemporarilyUnavailable.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return TemporarilyUnavailable; + }(twilioError_1.default)); + ClientErrors.TemporarilyUnavailable = TemporarilyUnavailable; + var BusyHere = /** @class */ (function (_super) { + __extends(BusyHere, _super); + function BusyHere(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31486; + _this.description = 'Busy Here (SIP)'; + _this.explanation = 'The callee is busy.'; + _this.name = 'BusyHere'; + _this.solutions = []; + Object.setPrototypeOf(_this, ClientErrors.BusyHere.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return BusyHere; + }(twilioError_1.default)); + ClientErrors.BusyHere = BusyHere; +})(ClientErrors = exports.ClientErrors || (exports.ClientErrors = {})); +var SIPServerErrors; +(function (SIPServerErrors) { + var Decline = /** @class */ (function (_super) { + __extends(Decline, _super); + function Decline(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31603; + _this.description = 'Decline (SIP)'; + _this.explanation = 'The callee does not wish to participate in the call.'; + _this.name = 'Decline'; + _this.solutions = []; + Object.setPrototypeOf(_this, SIPServerErrors.Decline.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return Decline; + }(twilioError_1.default)); + SIPServerErrors.Decline = Decline; +})(SIPServerErrors = exports.SIPServerErrors || (exports.SIPServerErrors = {})); +var GeneralErrors; +(function (GeneralErrors) { + var UnknownError = /** @class */ (function (_super) { + __extends(UnknownError, _super); + function UnknownError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31000; + _this.description = 'Unknown Error'; + _this.explanation = 'An unknown error has occurred. See error details for more information.'; + _this.name = 'UnknownError'; + _this.solutions = []; + Object.setPrototypeOf(_this, GeneralErrors.UnknownError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return UnknownError; + }(twilioError_1.default)); + GeneralErrors.UnknownError = UnknownError; + var ApplicationNotFoundError = /** @class */ (function (_super) { + __extends(ApplicationNotFoundError, _super); + function ApplicationNotFoundError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31001; + _this.description = 'Application Not Found'; + _this.explanation = ''; + _this.name = 'ApplicationNotFoundError'; + _this.solutions = []; + Object.setPrototypeOf(_this, GeneralErrors.ApplicationNotFoundError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return ApplicationNotFoundError; + }(twilioError_1.default)); + GeneralErrors.ApplicationNotFoundError = ApplicationNotFoundError; + var ConnectionDeclinedError = /** @class */ (function (_super) { + __extends(ConnectionDeclinedError, _super); + function ConnectionDeclinedError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31002; + _this.description = 'Connection Declined'; + _this.explanation = ''; + _this.name = 'ConnectionDeclinedError'; + _this.solutions = []; + Object.setPrototypeOf(_this, GeneralErrors.ConnectionDeclinedError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return ConnectionDeclinedError; + }(twilioError_1.default)); + GeneralErrors.ConnectionDeclinedError = ConnectionDeclinedError; + var ConnectionTimeoutError = /** @class */ (function (_super) { + __extends(ConnectionTimeoutError, _super); + function ConnectionTimeoutError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31003; + _this.description = 'Connection Timeout'; + _this.explanation = 'The server could not produce a response within a suitable amount of time.'; + _this.name = 'ConnectionTimeoutError'; + _this.solutions = []; + Object.setPrototypeOf(_this, GeneralErrors.ConnectionTimeoutError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return ConnectionTimeoutError; + }(twilioError_1.default)); + GeneralErrors.ConnectionTimeoutError = ConnectionTimeoutError; + var ConnectionError = /** @class */ (function (_super) { + __extends(ConnectionError, _super); + function ConnectionError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31005; + _this.description = 'Connection error'; + _this.explanation = 'A connection error occurred during the call'; + _this.name = 'ConnectionError'; + _this.solutions = []; + Object.setPrototypeOf(_this, GeneralErrors.ConnectionError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return ConnectionError; + }(twilioError_1.default)); + GeneralErrors.ConnectionError = ConnectionError; + var CallCancelledError = /** @class */ (function (_super) { + __extends(CallCancelledError, _super); + function CallCancelledError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = [ + 'The incoming call was cancelled because it was not answered in time or it was accepted/rejected by another application instance registered with the same identity.', + ]; + _this.code = 31008; + _this.description = 'Call cancelled'; + _this.explanation = 'Unable to answer because the call has ended'; + _this.name = 'CallCancelledError'; + _this.solutions = []; + Object.setPrototypeOf(_this, GeneralErrors.CallCancelledError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return CallCancelledError; + }(twilioError_1.default)); + GeneralErrors.CallCancelledError = CallCancelledError; + var TransportError = /** @class */ (function (_super) { + __extends(TransportError, _super); + function TransportError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31009; + _this.description = 'Transport error'; + _this.explanation = 'No transport available to send or receive messages'; + _this.name = 'TransportError'; + _this.solutions = []; + Object.setPrototypeOf(_this, GeneralErrors.TransportError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return TransportError; + }(twilioError_1.default)); + GeneralErrors.TransportError = TransportError; +})(GeneralErrors = exports.GeneralErrors || (exports.GeneralErrors = {})); +var MalformedRequestErrors; +(function (MalformedRequestErrors) { + var MalformedRequestError = /** @class */ (function (_super) { + __extends(MalformedRequestError, _super); + function MalformedRequestError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = [ + 'Invalid content or MessageType passed to sendMessage method.', + ]; + _this.code = 31100; + _this.description = 'The request had malformed syntax.'; + _this.explanation = 'The request could not be understood due to malformed syntax.'; + _this.name = 'MalformedRequestError'; + _this.solutions = [ + 'Ensure content and MessageType passed to sendMessage method are valid.', + ]; + Object.setPrototypeOf(_this, MalformedRequestErrors.MalformedRequestError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return MalformedRequestError; + }(twilioError_1.default)); + MalformedRequestErrors.MalformedRequestError = MalformedRequestError; + var MissingParameterArrayError = /** @class */ (function (_super) { + __extends(MissingParameterArrayError, _super); + function MissingParameterArrayError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31101; + _this.description = 'Missing parameter array in request'; + _this.explanation = ''; + _this.name = 'MissingParameterArrayError'; + _this.solutions = []; + Object.setPrototypeOf(_this, MalformedRequestErrors.MissingParameterArrayError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return MissingParameterArrayError; + }(twilioError_1.default)); + MalformedRequestErrors.MissingParameterArrayError = MissingParameterArrayError; + var AuthorizationTokenMissingError = /** @class */ (function (_super) { + __extends(AuthorizationTokenMissingError, _super); + function AuthorizationTokenMissingError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31102; + _this.description = 'Authorization token missing in request.'; + _this.explanation = ''; + _this.name = 'AuthorizationTokenMissingError'; + _this.solutions = []; + Object.setPrototypeOf(_this, MalformedRequestErrors.AuthorizationTokenMissingError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return AuthorizationTokenMissingError; + }(twilioError_1.default)); + MalformedRequestErrors.AuthorizationTokenMissingError = AuthorizationTokenMissingError; + var MaxParameterLengthExceededError = /** @class */ (function (_super) { + __extends(MaxParameterLengthExceededError, _super); + function MaxParameterLengthExceededError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31103; + _this.description = 'Maximum parameter length has been exceeded.'; + _this.explanation = 'Length of parameters cannot exceed MAX_PARAM_LENGTH.'; + _this.name = 'MaxParameterLengthExceededError'; + _this.solutions = []; + Object.setPrototypeOf(_this, MalformedRequestErrors.MaxParameterLengthExceededError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return MaxParameterLengthExceededError; + }(twilioError_1.default)); + MalformedRequestErrors.MaxParameterLengthExceededError = MaxParameterLengthExceededError; + var InvalidBridgeTokenError = /** @class */ (function (_super) { + __extends(InvalidBridgeTokenError, _super); + function InvalidBridgeTokenError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31104; + _this.description = 'Invalid bridge token'; + _this.explanation = ''; + _this.name = 'InvalidBridgeTokenError'; + _this.solutions = []; + Object.setPrototypeOf(_this, MalformedRequestErrors.InvalidBridgeTokenError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return InvalidBridgeTokenError; + }(twilioError_1.default)); + MalformedRequestErrors.InvalidBridgeTokenError = InvalidBridgeTokenError; + var InvalidClientNameError = /** @class */ (function (_super) { + __extends(InvalidClientNameError, _super); + function InvalidClientNameError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = [ + 'Client name contains invalid characters.', + ]; + _this.code = 31105; + _this.description = 'Invalid client name'; + _this.explanation = 'Client name should not contain control, space, delims, or unwise characters.'; + _this.name = 'InvalidClientNameError'; + _this.solutions = [ + 'Make sure that client name does not contain any of the invalid characters.', + ]; + Object.setPrototypeOf(_this, MalformedRequestErrors.InvalidClientNameError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return InvalidClientNameError; + }(twilioError_1.default)); + MalformedRequestErrors.InvalidClientNameError = InvalidClientNameError; + var ReconnectParameterInvalidError = /** @class */ (function (_super) { + __extends(ReconnectParameterInvalidError, _super); + function ReconnectParameterInvalidError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31107; + _this.description = 'The reconnect parameter is invalid'; + _this.explanation = ''; + _this.name = 'ReconnectParameterInvalidError'; + _this.solutions = []; + Object.setPrototypeOf(_this, MalformedRequestErrors.ReconnectParameterInvalidError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return ReconnectParameterInvalidError; + }(twilioError_1.default)); + MalformedRequestErrors.ReconnectParameterInvalidError = ReconnectParameterInvalidError; +})(MalformedRequestErrors = exports.MalformedRequestErrors || (exports.MalformedRequestErrors = {})); +(function (AuthorizationErrors) { + var AuthorizationError = /** @class */ (function (_super) { + __extends(AuthorizationError, _super); + function AuthorizationError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31201; + _this.description = 'Authorization error'; + _this.explanation = 'The request requires user authentication. The server understood the request, but is refusing to fulfill it.'; + _this.name = 'AuthorizationError'; + _this.solutions = []; + Object.setPrototypeOf(_this, AuthorizationErrors.AuthorizationError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return AuthorizationError; + }(twilioError_1.default)); + AuthorizationErrors.AuthorizationError = AuthorizationError; + var NoValidAccountError = /** @class */ (function (_super) { + __extends(NoValidAccountError, _super); + function NoValidAccountError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31203; + _this.description = 'No valid account'; + _this.explanation = ''; + _this.name = 'NoValidAccountError'; + _this.solutions = []; + Object.setPrototypeOf(_this, AuthorizationErrors.NoValidAccountError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return NoValidAccountError; + }(twilioError_1.default)); + AuthorizationErrors.NoValidAccountError = NoValidAccountError; + var InvalidJWTTokenError = /** @class */ (function (_super) { + __extends(InvalidJWTTokenError, _super); + function InvalidJWTTokenError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31204; + _this.description = 'Invalid JWT token'; + _this.explanation = ''; + _this.name = 'InvalidJWTTokenError'; + _this.solutions = []; + Object.setPrototypeOf(_this, AuthorizationErrors.InvalidJWTTokenError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return InvalidJWTTokenError; + }(twilioError_1.default)); + AuthorizationErrors.InvalidJWTTokenError = InvalidJWTTokenError; + var JWTTokenExpiredError = /** @class */ (function (_super) { + __extends(JWTTokenExpiredError, _super); + function JWTTokenExpiredError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31205; + _this.description = 'JWT token expired'; + _this.explanation = ''; + _this.name = 'JWTTokenExpiredError'; + _this.solutions = []; + Object.setPrototypeOf(_this, AuthorizationErrors.JWTTokenExpiredError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return JWTTokenExpiredError; + }(twilioError_1.default)); + AuthorizationErrors.JWTTokenExpiredError = JWTTokenExpiredError; + var RateExceededError = /** @class */ (function (_super) { + __extends(RateExceededError, _super); + function RateExceededError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = [ + 'Rate limit exceeded.', + ]; + _this.code = 31206; + _this.description = 'Rate exceeded authorized limit.'; + _this.explanation = 'The request performed exceeds the authorized limit.'; + _this.name = 'RateExceededError'; + _this.solutions = [ + 'Ensure message send rate does not exceed authorized limits.', + ]; + Object.setPrototypeOf(_this, AuthorizationErrors.RateExceededError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return RateExceededError; + }(twilioError_1.default)); + AuthorizationErrors.RateExceededError = RateExceededError; + var JWTTokenExpirationTooLongError = /** @class */ (function (_super) { + __extends(JWTTokenExpirationTooLongError, _super); + function JWTTokenExpirationTooLongError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 31207; + _this.description = 'JWT token expiration too long'; + _this.explanation = ''; + _this.name = 'JWTTokenExpirationTooLongError'; + _this.solutions = []; + Object.setPrototypeOf(_this, AuthorizationErrors.JWTTokenExpirationTooLongError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return JWTTokenExpirationTooLongError; + }(twilioError_1.default)); + AuthorizationErrors.JWTTokenExpirationTooLongError = JWTTokenExpirationTooLongError; + var PayloadSizeExceededError = /** @class */ (function (_super) { + __extends(PayloadSizeExceededError, _super); + function PayloadSizeExceededError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = [ + 'The payload size of Call Message Event exceeds the authorized limit.', + ]; + _this.code = 31209; + _this.description = 'Call Message Event Payload size exceeded authorized limit.'; + _this.explanation = 'The request performed to send a Call Message Event exceeds the payload size authorized limit'; + _this.name = 'PayloadSizeExceededError'; + _this.solutions = [ + 'Reduce payload size of Call Message Event to be within the authorized limit and try again.', + ]; + Object.setPrototypeOf(_this, AuthorizationErrors.PayloadSizeExceededError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return PayloadSizeExceededError; + }(twilioError_1.default)); + AuthorizationErrors.PayloadSizeExceededError = PayloadSizeExceededError; +})(AuthorizationErrors = exports.AuthorizationErrors || (exports.AuthorizationErrors = {})); +var UserMediaErrors; +(function (UserMediaErrors) { + var PermissionDeniedError = /** @class */ (function (_super) { + __extends(PermissionDeniedError, _super); + function PermissionDeniedError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = [ + 'The user denied the getUserMedia request.', + 'The browser denied the getUserMedia request.', + ]; + _this.code = 31401; + _this.description = 'UserMedia Permission Denied Error'; + _this.explanation = 'The browser or end-user denied permissions to user media. Therefore we were unable to acquire input audio.'; + _this.name = 'PermissionDeniedError'; + _this.solutions = [ + 'The user should accept the request next time prompted. If the browser saved the deny, the user should change that permission in their browser.', + 'The user should to verify that the browser has permission to access the microphone at this address.', + ]; + Object.setPrototypeOf(_this, UserMediaErrors.PermissionDeniedError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return PermissionDeniedError; + }(twilioError_1.default)); + UserMediaErrors.PermissionDeniedError = PermissionDeniedError; + var AcquisitionFailedError = /** @class */ (function (_super) { + __extends(AcquisitionFailedError, _super); + function AcquisitionFailedError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = [ + 'NotFoundError - The deviceID specified was not found.', + 'The getUserMedia constraints were overconstrained and no devices matched.', + ]; + _this.code = 31402; + _this.description = 'UserMedia Acquisition Failed Error'; + _this.explanation = 'The browser and end-user allowed permissions, however getting the media failed. Usually this is due to bad constraints, but can sometimes fail due to browser, OS or hardware issues.'; + _this.name = 'AcquisitionFailedError'; + _this.solutions = [ + 'Ensure the deviceID being specified exists.', + 'Try acquiring media with fewer constraints.', + ]; + Object.setPrototypeOf(_this, UserMediaErrors.AcquisitionFailedError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return AcquisitionFailedError; + }(twilioError_1.default)); + UserMediaErrors.AcquisitionFailedError = AcquisitionFailedError; +})(UserMediaErrors = exports.UserMediaErrors || (exports.UserMediaErrors = {})); +var SignalingErrors; +(function (SignalingErrors) { + var ConnectionError = /** @class */ (function (_super) { + __extends(ConnectionError, _super); + function ConnectionError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = []; + _this.code = 53000; + _this.description = 'Signaling connection error'; + _this.explanation = 'Raised whenever a signaling connection error occurs that is not covered by a more specific error code.'; + _this.name = 'ConnectionError'; + _this.solutions = []; + Object.setPrototypeOf(_this, SignalingErrors.ConnectionError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return ConnectionError; + }(twilioError_1.default)); + SignalingErrors.ConnectionError = ConnectionError; + var ConnectionDisconnected = /** @class */ (function (_super) { + __extends(ConnectionDisconnected, _super); + function ConnectionDisconnected(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = [ + 'The device running your application lost its Internet connection.', + ]; + _this.code = 53001; + _this.description = 'Signaling connection disconnected'; + _this.explanation = 'Raised whenever the signaling connection is unexpectedly disconnected.'; + _this.name = 'ConnectionDisconnected'; + _this.solutions = [ + 'Ensure the device running your application has access to a stable Internet connection.', + ]; + Object.setPrototypeOf(_this, SignalingErrors.ConnectionDisconnected.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return ConnectionDisconnected; + }(twilioError_1.default)); + SignalingErrors.ConnectionDisconnected = ConnectionDisconnected; +})(SignalingErrors = exports.SignalingErrors || (exports.SignalingErrors = {})); +var MediaErrors; +(function (MediaErrors) { + var ClientLocalDescFailed = /** @class */ (function (_super) { + __extends(ClientLocalDescFailed, _super); + function ClientLocalDescFailed(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = [ + 'The Client may not be using a supported WebRTC implementation.', + 'The Client may not have the necessary resources to create or apply a new media description.', + ]; + _this.code = 53400; + _this.description = 'Client is unable to create or apply a local media description'; + _this.explanation = 'Raised whenever a Client is unable to create or apply a local media description.'; + _this.name = 'ClientLocalDescFailed'; + _this.solutions = [ + 'If you are experiencing this error using the JavaScript SDK, ensure you are running it with a supported WebRTC implementation.', + ]; + Object.setPrototypeOf(_this, MediaErrors.ClientLocalDescFailed.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return ClientLocalDescFailed; + }(twilioError_1.default)); + MediaErrors.ClientLocalDescFailed = ClientLocalDescFailed; + var ClientRemoteDescFailed = /** @class */ (function (_super) { + __extends(ClientRemoteDescFailed, _super); + function ClientRemoteDescFailed(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = [ + 'The Client may not be using a supported WebRTC implementation.', + 'The Client may be connecting peer-to-peer with another Participant that is not using a supported WebRTC implementation.', + 'The Client may not have the necessary resources to apply a new media description.', + ]; + _this.code = 53402; + _this.description = 'Client is unable to apply a remote media description'; + _this.explanation = 'Raised whenever the Client receives a remote media description but is unable to apply it.'; + _this.name = 'ClientRemoteDescFailed'; + _this.solutions = [ + 'If you are experiencing this error using the JavaScript SDK, ensure you are running it with a supported WebRTC implementation.', + ]; + Object.setPrototypeOf(_this, MediaErrors.ClientRemoteDescFailed.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return ClientRemoteDescFailed; + }(twilioError_1.default)); + MediaErrors.ClientRemoteDescFailed = ClientRemoteDescFailed; + var ConnectionError = /** @class */ (function (_super) { + __extends(ConnectionError, _super); + function ConnectionError(messageOrError, error) { + var _this = _super.call(this, messageOrError, error) || this; + _this.causes = [ + 'The Client was unable to establish a media connection.', + 'A media connection which was active failed liveliness checks.', + ]; + _this.code = 53405; + _this.description = 'Media connection failed'; + _this.explanation = 'Raised by the Client or Server whenever a media connection fails.'; + _this.name = 'ConnectionError'; + _this.solutions = [ + 'If the problem persists, try connecting to another region.', + 'Check your Client\'s network connectivity.', + 'If you\'ve provided custom ICE Servers then ensure that the URLs and credentials are valid.', + ]; + Object.setPrototypeOf(_this, MediaErrors.ConnectionError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return ConnectionError; + }(twilioError_1.default)); + MediaErrors.ConnectionError = ConnectionError; +})(MediaErrors = exports.MediaErrors || (exports.MediaErrors = {})); +/** + * @private + */ +exports.errorsByCode = new Map([ + [20101, AuthorizationErrors.AccessTokenInvalid], + [20104, AuthorizationErrors.AccessTokenExpired], + [20151, AuthorizationErrors.AuthenticationFailed], + [31202, SignatureValidationErrors.AccessTokenSignatureValidationFailed], + [31400, ClientErrors.BadRequest], + [31404, ClientErrors.NotFound], + [31480, ClientErrors.TemporarilyUnavailable], + [31486, ClientErrors.BusyHere], + [31603, SIPServerErrors.Decline], + [31000, GeneralErrors.UnknownError], + [31001, GeneralErrors.ApplicationNotFoundError], + [31002, GeneralErrors.ConnectionDeclinedError], + [31003, GeneralErrors.ConnectionTimeoutError], + [31005, GeneralErrors.ConnectionError], + [31008, GeneralErrors.CallCancelledError], + [31009, GeneralErrors.TransportError], + [31100, MalformedRequestErrors.MalformedRequestError], + [31101, MalformedRequestErrors.MissingParameterArrayError], + [31102, MalformedRequestErrors.AuthorizationTokenMissingError], + [31103, MalformedRequestErrors.MaxParameterLengthExceededError], + [31104, MalformedRequestErrors.InvalidBridgeTokenError], + [31105, MalformedRequestErrors.InvalidClientNameError], + [31107, MalformedRequestErrors.ReconnectParameterInvalidError], + [31201, AuthorizationErrors.AuthorizationError], + [31203, AuthorizationErrors.NoValidAccountError], + [31204, AuthorizationErrors.InvalidJWTTokenError], + [31205, AuthorizationErrors.JWTTokenExpiredError], + [31206, AuthorizationErrors.RateExceededError], + [31207, AuthorizationErrors.JWTTokenExpirationTooLongError], + [31209, AuthorizationErrors.PayloadSizeExceededError], + [31401, UserMediaErrors.PermissionDeniedError], + [31402, UserMediaErrors.AcquisitionFailedError], + [53000, SignalingErrors.ConnectionError], + [53001, SignalingErrors.ConnectionDisconnected], + [53400, MediaErrors.ClientLocalDescFailed], + [53402, MediaErrors.ClientRemoteDescFailed], + [53405, MediaErrors.ConnectionError], +]); +Object.freeze(exports.errorsByCode); + +},{"./twilioError":16}],15:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UserMediaErrors = exports.TwilioError = exports.SIPServerErrors = exports.SignatureValidationErrors = exports.SignalingErrors = exports.MediaErrors = exports.MalformedRequestErrors = exports.GeneralErrors = exports.ClientErrors = exports.AuthorizationErrors = exports.hasErrorByCode = exports.getErrorByCode = exports.NotSupportedError = exports.InvalidStateError = exports.InvalidArgumentError = exports.getPreciseSignalingErrorByCode = void 0; +/** + * @packageDocumentation + * @internalapi + */ +/* tslint:disable max-classes-per-file */ +var generated_1 = require("./generated"); +Object.defineProperty(exports, "AuthorizationErrors", { enumerable: true, get: function () { return generated_1.AuthorizationErrors; } }); +Object.defineProperty(exports, "ClientErrors", { enumerable: true, get: function () { return generated_1.ClientErrors; } }); +Object.defineProperty(exports, "GeneralErrors", { enumerable: true, get: function () { return generated_1.GeneralErrors; } }); +Object.defineProperty(exports, "MalformedRequestErrors", { enumerable: true, get: function () { return generated_1.MalformedRequestErrors; } }); +Object.defineProperty(exports, "MediaErrors", { enumerable: true, get: function () { return generated_1.MediaErrors; } }); +Object.defineProperty(exports, "SignalingErrors", { enumerable: true, get: function () { return generated_1.SignalingErrors; } }); +Object.defineProperty(exports, "SignatureValidationErrors", { enumerable: true, get: function () { return generated_1.SignatureValidationErrors; } }); +Object.defineProperty(exports, "SIPServerErrors", { enumerable: true, get: function () { return generated_1.SIPServerErrors; } }); +Object.defineProperty(exports, "TwilioError", { enumerable: true, get: function () { return generated_1.TwilioError; } }); +Object.defineProperty(exports, "UserMediaErrors", { enumerable: true, get: function () { return generated_1.UserMediaErrors; } }); +/** + * NOTE(mhuynh): Replacing generic error codes with new (more specific) codes, + * is a breaking change. If an error code is found in this set, we only perform + * the transformation if the feature flag is enabled. + * + * With every major version bump, such that we are allowed to introduce breaking + * changes as per semver specification, this array should be cleared. + * + * TODO: [VBLOCKS-2295] Remove this in 3.x + */ +var PRECISE_SIGNALING_ERROR_CODES = new Set([ + /** + * 310XX Errors + */ + 31001, + 31002, + 31003, + /** + * 311XX Errors + */ + 31101, + 31102, + 31103, + 31104, + 31105, + 31107, + /** + * 312XX Errors + */ + 31201, + 31202, + 31203, + 31204, + 31205, + 31207, + /** + * 314XX Errors + */ + 31404, + 31480, + 31486, + /** + * 316XX Errors + */ + 31603, +]); +function getPreciseSignalingErrorByCode(enableImprovedSignalingErrorPrecision, errorCode) { + if (typeof errorCode !== 'number') { + return; + } + if (!hasErrorByCode(errorCode)) { + return; + } + var shouldTransform = enableImprovedSignalingErrorPrecision + ? true + : !PRECISE_SIGNALING_ERROR_CODES.has(errorCode); + if (!shouldTransform) { + return; + } + return getErrorByCode(errorCode); +} +exports.getPreciseSignalingErrorByCode = getPreciseSignalingErrorByCode; +// Application errors that can be avoided by good app logic +var InvalidArgumentError = /** @class */ (function (_super) { + __extends(InvalidArgumentError, _super); + function InvalidArgumentError(message) { + var _this = _super.call(this, message) || this; + _this.name = 'InvalidArgumentError'; + return _this; + } + return InvalidArgumentError; +}(Error)); +exports.InvalidArgumentError = InvalidArgumentError; +var InvalidStateError = /** @class */ (function (_super) { + __extends(InvalidStateError, _super); + function InvalidStateError(message) { + var _this = _super.call(this, message) || this; + _this.name = 'InvalidStateError'; + return _this; + } + return InvalidStateError; +}(Error)); +exports.InvalidStateError = InvalidStateError; +var NotSupportedError = /** @class */ (function (_super) { + __extends(NotSupportedError, _super); + function NotSupportedError(message) { + var _this = _super.call(this, message) || this; + _this.name = 'NotSupportedError'; + return _this; + } + return NotSupportedError; +}(Error)); +exports.NotSupportedError = NotSupportedError; +// This should only be used to look up error codes returned by a server +// using the same repo of error codes. +function getErrorByCode(code) { + var error = generated_1.errorsByCode.get(code); + if (!error) { + throw new InvalidArgumentError("Error code " + code + " not found"); + } + return error; +} +exports.getErrorByCode = getErrorByCode; +// This should only be used to look up error codes returned by a server +// using the same repo of error codes. +function hasErrorByCode(code) { + return generated_1.errorsByCode.has(code); +} +exports.hasErrorByCode = hasErrorByCode; + +},{"./generated":14}],16:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @packageDocumentation + * @module Voice + * @publicapi + * @internal + */ +var TwilioError = /** @class */ (function (_super) { + __extends(TwilioError, _super); + function TwilioError(messageOrError, error) { + var _this = _super.call(this) || this; + Object.setPrototypeOf(_this, TwilioError.prototype); + var message = typeof messageOrError === 'string' + ? messageOrError + : _this.explanation; + var originalError = typeof messageOrError === 'object' + ? messageOrError + : error; + _this.message = _this.name + " (" + _this.code + "): " + message; + _this.originalError = originalError; + return _this; + } + return TwilioError; +}(Error)); +exports.default = TwilioError; + +},{}],17:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +var events_1 = require("events"); +var log_1 = require("./log"); +var request_1 = require("./request"); +/** + * Builds Endpoint Analytics (EA) event payloads and sends them to + * the EA server. + * @constructor + * @param {String} productName - Name of the product publishing events. + * @param {String} token - The JWT token to use to authenticate with + * the EA server. + * @param {EventPublisher.Options} options + * @property {Boolean} isEnabled - Whether or not this publisher is publishing + * to the server. Currently ignores the request altogether, in the future this + * may store them in case publishing is re-enabled later. Defaults to true. + */ +/** + * @typedef {Object} EventPublisher.Options + * @property {Object} [metadata=undefined] - A publisher_metadata object to send + * with each payload. + * @property {String} [host='eventgw.twilio.com'] - The host address of the EA + * server to publish to. + * @property {Object|Function} [defaultPayload] - A default payload to extend + * when creating and sending event payloads. Also takes a function that + * should return an object representing the default payload. This is + * useful for fields that should always be present when they are + * available, but are not always available. + */ +var EventPublisher = /** @class */ (function (_super) { + __extends(EventPublisher, _super); + function EventPublisher(productName, token, options) { + var _this = _super.call(this) || this; + if (!(_this instanceof EventPublisher)) { + return new EventPublisher(productName, token, options); + } + // Apply default options + options = Object.assign({ defaultPayload: function () { return {}; } }, options); + var defaultPayload = options.defaultPayload; + if (typeof defaultPayload !== 'function') { + defaultPayload = function () { return Object.assign({}, options.defaultPayload); }; + } + var isEnabled = true; + var metadata = Object.assign({ app_name: undefined, app_version: undefined }, options.metadata); + Object.defineProperties(_this, { + _defaultPayload: { value: defaultPayload }, + _host: { value: options.host, writable: true }, + _isEnabled: { + get: function () { return isEnabled; }, + set: function (_isEnabled) { isEnabled = _isEnabled; }, + }, + _log: { value: new log_1.default('EventPublisher') }, + _request: { value: options.request || request_1.default, writable: true }, + _token: { value: token, writable: true }, + isEnabled: { + enumerable: true, + get: function () { return isEnabled; }, + }, + metadata: { + enumerable: true, + get: function () { return metadata; }, + }, + productName: { enumerable: true, value: productName }, + token: { + enumerable: true, + get: function () { return this._token; }, + }, + }); + return _this; + } + return EventPublisher; +}(events_1.EventEmitter)); +/** + * Post to an EA server. + * @private + * @param {String} endpointName - Endpoint to post the event to + * @param {String} level - ['debug', 'info', 'warning', 'error'] + * @param {String} group - The name of the group the event belongs to. + * @param {String} name - The designated event name. + * @param {?Object} [payload=null] - The payload to pass. This will be extended + * onto the default payload object, if one exists. + * @param {?Connection} [connection=null] - The {@link Connection} which is posting this payload. + * @param {?Boolean} [force=false] - Whether or not to send this even if + * publishing is disabled. + * @returns {Promise} Fulfilled if the HTTP response is 20x. + */ +EventPublisher.prototype._post = function _post(endpointName, level, group, name, payload, connection, force) { + var _this = this; + if ((!this.isEnabled && !force) || !this._host) { + this._log.debug('Publishing cancelled', JSON.stringify({ isEnabled: this.isEnabled, force: force, host: this._host })); + return Promise.resolve(); + } + if (!connection || ((!connection.parameters || !connection.parameters.CallSid) && !connection.outboundConnectionId)) { + if (!connection) { + this._log.debug('Publishing cancelled. Missing connection object'); + } + else { + this._log.debug('Publishing cancelled. Missing connection info', JSON.stringify({ + outboundConnectionId: connection.outboundConnectionId, parameters: connection.parameters, + })); + } + return Promise.resolve(); + } + var event = { + group: group, + level: level.toUpperCase(), + name: name, + payload: (payload && payload.forEach) ? + payload.slice(0) : Object.assign(this._defaultPayload(connection), payload), + payload_type: 'application/json', + private: false, + publisher: this.productName, + timestamp: (new Date()).toISOString(), + }; + if (this.metadata) { + event.publisher_metadata = this.metadata; + } + if (endpointName === 'EndpointEvents') { + this._log.debug('Publishing insights', JSON.stringify({ endpointName: endpointName, event: event, force: force, host: this._host })); + } + var requestParams = { + body: event, + headers: { + 'Content-Type': 'application/json', + 'X-Twilio-Token': this.token, + }, + url: "https://" + this._host + "/v4/" + endpointName, + }; + return new Promise(function (resolve, reject) { + _this._request.post(requestParams, function (err) { + if (err) { + _this.emit('error', err); + reject(err); + } + else { + resolve(); + } + }); + }).catch(function (e) { + _this._log.error("Unable to post " + group + " " + name + " event to Insights. Received error: " + e); + }); +}; +/** + * Post an event to the EA server. Use this method when the level + * is dynamic. Otherwise, it's better practice to use the sugar + * methods named for the specific level. + * @param {String} level - ['debug', 'info', 'warning', 'error'] + * @param {String} group - The name of the group the event belongs to. + * @param {String} name - The designated event name. + * @param {?Object} [payload=null] - The payload to pass. This will be extended + * onto the default payload object, if one exists. + * @param {?Connection} [connection=null] - The {@link Connection} which is posting this payload. + * @returns {Promise} Fulfilled if the HTTP response is 20x. + */ +EventPublisher.prototype.post = function post(level, group, name, payload, connection, force) { + return this._post('EndpointEvents', level, group, name, payload, connection, force); +}; +/** + * Post a debug-level event to the EA server. + * @param {String} group - The name of the group the event belongs to. + * @param {String} name - The designated event name. + * @param {?Object} [payload=null] - The payload to pass. This will be extended + * onto the default payload object, if one exists. + * @param {?Connection} [connection=null] - The {@link Connection} which is posting this payload. + * @returns {Promise} Fulfilled if the HTTP response is 20x. + */ +EventPublisher.prototype.debug = function debug(group, name, payload, connection) { + return this.post('debug', group, name, payload, connection); +}; +/** + * Post an info-level event to the EA server. + * @param {String} group - The name of the group the event belongs to. + * @param {String} name - The designated event name. + * @param {?Object} [payload=null] - The payload to pass. This will be extended + * onto the default payload object, if one exists. + * @param {?Connection} [connection=null] - The {@link Connection} which is posting this payload. + * @returns {Promise} Fulfilled if the HTTP response is 20x. + */ +EventPublisher.prototype.info = function info(group, name, payload, connection) { + return this.post('info', group, name, payload, connection); +}; +/** + * Post a warning-level event to the EA server. + * @param {String} group - The name of the group the event belongs to. + * @param {String} name - The designated event name. + * @param {?Object} [payload=null] - The payload to pass. This will be extended + * onto the default payload object, if one exists. + * @param {?Connection} [connection=null] - The {@link Connection} which is posting this payload. + * @returns {Promise} Fulfilled if the HTTP response is 20x. + */ +EventPublisher.prototype.warn = function warn(group, name, payload, connection) { + return this.post('warning', group, name, payload, connection); +}; +/** + * Post an error-level event to the EA server. + * @param {String} group - The name of the group the event belongs to. + * @param {String} name - The designated event name. + * @param {?Object} [payload=null] - The payload to pass. This will be extended + * onto the default payload object, if one exists. + * @param {?Connection} [connection=null] - The {@link Connection} which is posting this payload. + * @returns {Promise} Fulfilled if the HTTP response is 20x. + */ +EventPublisher.prototype.error = function error(group, name, payload, connection) { + return this.post('error', group, name, payload, connection); +}; +/** + * Post a metrics event to the EA server. + * @param {String} group - The name of the group the event belongs to. + * @param {String} name - The designated event name. + * @param {Array} metrics - The metrics to post. + * @param {?Object} [customFields] - Custom fields to append to each payload. + * @returns {Promise} Fulfilled if the HTTP response is 20x. + */ +EventPublisher.prototype.postMetrics = function postMetrics(group, name, metrics, customFields, connection) { + var _this = this; + return new Promise(function (resolve) { + var samples = metrics + .map(formatMetric) + .map(function (sample) { return Object.assign(sample, customFields); }); + resolve(_this._post('EndpointMetrics', 'info', group, name, samples, connection)); + }); +}; +/** + * Update the host address of the insights server to publish to. + * @param {String} host - The new host address of the insights server. + */ +EventPublisher.prototype.setHost = function setHost(host) { + this._host = host; +}; +/** + * Update the token to use to authenticate requests. + * @param {string} token + * @returns {void} + */ +EventPublisher.prototype.setToken = function setToken(token) { + this._token = token; +}; +/** + * Enable the publishing of events. + */ +EventPublisher.prototype.enable = function enable() { + this._isEnabled = true; +}; +/** + * Disable the publishing of events. + */ +EventPublisher.prototype.disable = function disable() { + this._isEnabled = false; +}; +function formatMetric(sample) { + return { + audio_codec: sample.codecName, + audio_level_in: sample.audioInputLevel, + audio_level_out: sample.audioOutputLevel, + bytes_received: sample.bytesReceived, + bytes_sent: sample.bytesSent, + call_volume_input: sample.inputVolume, + call_volume_output: sample.outputVolume, + jitter: sample.jitter, + mos: sample.mos && (Math.round(sample.mos * 100) / 100), + packets_lost: sample.packetsLost, + packets_lost_fraction: sample.packetsLostFraction && + (Math.round(sample.packetsLostFraction * 100) / 100), + packets_received: sample.packetsReceived, + rtt: sample.rtt, + timestamp: (new Date(sample.timestamp)).toISOString(), + total_bytes_received: sample.totals.bytesReceived, + total_bytes_sent: sample.totals.bytesSent, + total_packets_lost: sample.totals.packetsLost, + total_packets_received: sample.totals.packetsReceived, + total_packets_sent: sample.totals.packetsSent, + }; +} +exports.default = EventPublisher; + +},{"./log":18,"./request":23,"events":39}],18:[function(require,module,exports){ +"use strict"; +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +var __spreadArrays = (this && this.__spreadArrays) || function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Logger = void 0; +var LogLevelModule = require("loglevel"); +var constants_1 = require("./constants"); +/** + * {@link Log} provides logging features throughout the sdk using loglevel module + * See https://github.com/pimterry/loglevel for documentation + * @private + */ +var Log = /** @class */ (function () { + /** + * @constructor + * @param [tag] - tag name for the logs + * @param [options] - Optional settings + */ + function Log(tag, options) { + this._log = Log.getLogLevelInstance(options); + this._prefix = "[TwilioVoice][" + tag + "]"; + } + /** + * Return the `loglevel` instance maintained internally. + * @param [options] - Optional settings + * @returns The `loglevel` instance. + */ + Log.getLogLevelInstance = function (options) { + if (!Log.loglevelInstance) { + try { + Log.loglevelInstance = (options && options.LogLevelModule ? options.LogLevelModule : LogLevelModule).getLogger(constants_1.PACKAGE_NAME); + } + catch (_a) { + // tslint:disable-next-line + console.warn('Cannot create custom logger'); + Log.loglevelInstance = console; + } + } + return Log.loglevelInstance; + }; + /** + * Log a debug message + * @param args - Any number of arguments to be passed to loglevel.debug + */ + Log.prototype.debug = function () { + var _a; + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + (_a = this._log).debug.apply(_a, __spreadArrays([this._prefix], args)); + }; + /** + * Log an error message + * @param args - Any number of arguments to be passed to loglevel.error + */ + Log.prototype.error = function () { + var _a; + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + (_a = this._log).error.apply(_a, __spreadArrays([this._prefix], args)); + }; + /** + * Log an info message + * @param args - Any number of arguments to be passed to loglevel.info + */ + Log.prototype.info = function () { + var _a; + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + (_a = this._log).info.apply(_a, __spreadArrays([this._prefix], args)); + }; + /** + * Set a default log level to disable all logging below the given level + */ + Log.prototype.setDefaultLevel = function (level) { + if (this._log.setDefaultLevel) { + this._log.setDefaultLevel(level); + } + else { + // tslint:disable-next-line + console.warn('Logger cannot setDefaultLevel'); + } + }; + /** + * Log a warning message + * @param args - Any number of arguments to be passed to loglevel.warn + */ + Log.prototype.warn = function () { + var _a; + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + (_a = this._log).warn.apply(_a, __spreadArrays([this._prefix], args)); + }; + /** + * Log levels + */ + Log.levels = LogLevelModule.levels; + return Log; +}()); +exports.Logger = Log.getLogLevelInstance(); +exports.default = Log; + +},{"./constants":10,"loglevel":43}],19:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @packageDocumentation + * @module Voice + */ +var constants_1 = require("./constants"); +var errors_1 = require("./errors"); +var log_1 = require("./log"); +var DEFAULT_TEST_SOUND_URL = constants_1.SOUNDS_BASE_URL + "/outgoing.mp3"; +/** + * A smart collection containing a Set of active output devices. + * @publicapi + */ +var OutputDeviceCollection = /** @class */ (function () { + /** + * @private + */ + function OutputDeviceCollection(_name, _availableDevices, _beforeChange, _isSupported) { + this._name = _name; + this._availableDevices = _availableDevices; + this._beforeChange = _beforeChange; + this._isSupported = _isSupported; + /** + * The currently active output devices. + */ + this._activeDevices = new Set(); + /** + * An instance of Logger to use. + */ + this._log = new log_1.default('OutputDeviceCollection'); + } + /** + * Delete a device from the collection. If no devices remain, the 'default' + * device will be added as the sole device. If no `default` device exists, + * the first available device will be used. + * @param device - The device to delete from the collection + * @returns whether the device was present before it was deleted + */ + OutputDeviceCollection.prototype.delete = function (device) { + this._log.debug('.delete', device); + var wasDeleted = !!(this._activeDevices.delete(device)); + var defaultDevice = this._availableDevices.get('default') + || Array.from(this._availableDevices.values())[0]; + if (!this._activeDevices.size && defaultDevice) { + this._activeDevices.add(defaultDevice); + } + // Call _beforeChange so that the implementation can react when a device is + // removed or lost. + var deviceIds = Array.from(this._activeDevices.values()).map(function (deviceInfo) { return deviceInfo.deviceId; }); + this._beforeChange(this._name, deviceIds); + return !!wasDeleted; + }; + /** + * Get the current set of devices. + */ + OutputDeviceCollection.prototype.get = function () { + return this._activeDevices; + }; + /** + * Replace the current set of devices with a new set of devices. + * @param deviceIdOrIds - An ID or array of IDs of devices to replace the existing devices with. + * @returns Rejects if this feature is not supported, any of the supplied IDs are not found, + * or no IDs are passed. + */ + OutputDeviceCollection.prototype.set = function (deviceIdOrIds) { + var _this = this; + this._log.debug('.set', deviceIdOrIds); + if (!this._isSupported) { + return Promise.reject(new errors_1.NotSupportedError('This browser does not support audio output selection')); + } + var deviceIds = Array.isArray(deviceIdOrIds) ? deviceIdOrIds : [deviceIdOrIds]; + if (!deviceIds.length) { + return Promise.reject(new errors_1.InvalidArgumentError('Must specify at least one device to set')); + } + var missingIds = []; + var devices = deviceIds.map(function (id) { + var device = _this._availableDevices.get(id); + if (!device) { + missingIds.push(id); + } + return device; + }); + if (missingIds.length) { + return Promise.reject(new errors_1.InvalidArgumentError("Devices not found: " + missingIds.join(', '))); + } + return new Promise(function (resolve) { + resolve(_this._beforeChange(_this._name, deviceIds)); + }).then(function () { + _this._activeDevices.clear(); + devices.forEach(_this._activeDevices.add, _this._activeDevices); + }); + }; + /** + * Test the devices by playing audio through them. + * @param [soundUrl] - An optional URL. If none is specified, we will + * play a default test tone. + * @returns Resolves with the result of the underlying HTMLAudioElements' play() calls. + */ + OutputDeviceCollection.prototype.test = function (soundUrl) { + if (soundUrl === void 0) { soundUrl = DEFAULT_TEST_SOUND_URL; } + if (!this._isSupported) { + return Promise.reject(new errors_1.NotSupportedError('This browser does not support audio output selection')); + } + if (!this._activeDevices.size) { + return Promise.reject(new errors_1.InvalidStateError('No active output devices to test')); + } + return Promise.all(Array.from(this._activeDevices).map(function (device) { + var el; + // (rrowland) We need to wait for the oncanplay event because of a regression introduced + // in Chrome M72: https://bugs.chromium.org/p/chromium/issues/detail?id=930876 + return new Promise(function (resolve) { + el = new Audio(soundUrl); + el.oncanplay = resolve; + }).then(function () { return el.setSinkId(device.deviceId).then(function () { return el.play(); }); }); + })); + }; + return OutputDeviceCollection; +}()); +exports.default = OutputDeviceCollection; + +},{"./constants":10,"./errors":15,"./log":18}],20:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PreflightTest = void 0; +/** + * @packageDocumentation + * @module Voice + * @preferred + * @publicapi + */ +var events_1 = require("events"); +var call_1 = require("../call"); +var device_1 = require("../device"); +var errors_1 = require("../errors"); +var log_1 = require("../log"); +var stats_1 = require("../rtc/stats"); +var constants_1 = require("../constants"); +/** + * Runs some tests to identify issues, if any, prohibiting successful calling. + */ +var PreflightTest = /** @class */ (function (_super) { + __extends(PreflightTest, _super); + /** + * Construct a {@link PreflightTest} instance. + * @constructor + * @param token - A Twilio JWT token string. + * @param options + */ + function PreflightTest(token, options) { + var _this = _super.call(this) || this; + /** + * Whether this test has already logged an insights-connection-warning. + */ + _this._hasInsightsErrored = false; + /** + * An instance of Logger to use. + */ + _this._log = new log_1.default('PreflightTest'); + /** + * Network related timing measurements for this test + */ + _this._networkTiming = {}; + /** + * The options passed to {@link PreflightTest} constructor + */ + _this._options = { + codecPreferences: [call_1.default.Codec.PCMU, call_1.default.Codec.Opus], + edge: 'roaming', + fakeMicInput: false, + logLevel: 'error', + signalingTimeoutMs: 10000, + }; + /** + * Current status of this test + */ + _this._status = PreflightTest.Status.Connecting; + Object.assign(_this._options, options); + _this._samples = []; + _this._warnings = []; + _this._startTime = Date.now(); + _this._initDevice(token, __assign(__assign({}, _this._options), { fileInputStream: _this._options.fakeMicInput ? + _this._getStreamFromFile() : undefined })); + // Device sets the loglevel so start logging after initializing the device. + // Then selectively log options that users can modify. + var userOptions = [ + 'codecPreferences', + 'edge', + 'fakeMicInput', + 'logLevel', + 'signalingTimeoutMs', + ]; + var userOptionOverrides = [ + 'audioContext', + 'deviceFactory', + 'fileInputStream', + 'getRTCIceCandidateStatsReport', + 'iceServers', + 'rtcConfiguration', + ]; + if (typeof options === 'object') { + var toLog_1 = __assign({}, options); + Object.keys(toLog_1).forEach(function (key) { + if (!userOptions.includes(key) && !userOptionOverrides.includes(key)) { + delete toLog_1[key]; + } + if (userOptionOverrides.includes(key)) { + toLog_1[key] = true; + } + }); + _this._log.debug('.constructor', JSON.stringify(toLog_1)); + } + return _this; + } + /** + * Stops the current test and raises a failed event. + */ + PreflightTest.prototype.stop = function () { + var _this = this; + this._log.debug('.stop'); + var error = new errors_1.GeneralErrors.CallCancelledError(); + if (this._device) { + this._device.once(device_1.default.EventName.Unregistered, function () { return _this._onFailed(error); }); + this._device.destroy(); + } + else { + this._onFailed(error); + } + }; + /** + * Emit a {PreflightTest.Warning} + */ + PreflightTest.prototype._emitWarning = function (name, description, rtcWarning) { + var warning = { name: name, description: description }; + if (rtcWarning) { + warning.rtcWarning = rtcWarning; + } + this._warnings.push(warning); + this._log.debug("#" + PreflightTest.Events.Warning, JSON.stringify(warning)); + this.emit(PreflightTest.Events.Warning, warning); + }; + /** + * Returns call quality base on the RTC Stats + */ + PreflightTest.prototype._getCallQuality = function (mos) { + if (mos > 4.2) { + return PreflightTest.CallQuality.Excellent; + } + else if (mos >= 4.1 && mos <= 4.2) { + return PreflightTest.CallQuality.Great; + } + else if (mos >= 3.7 && mos <= 4) { + return PreflightTest.CallQuality.Good; + } + else if (mos >= 3.1 && mos <= 3.6) { + return PreflightTest.CallQuality.Fair; + } + else { + return PreflightTest.CallQuality.Degraded; + } + }; + /** + * Returns the report for this test. + */ + PreflightTest.prototype._getReport = function () { + var stats = this._getRTCStats(); + var testTiming = { start: this._startTime }; + if (this._endTime) { + testTiming.end = this._endTime; + testTiming.duration = this._endTime - this._startTime; + } + var report = { + callSid: this._callSid, + edge: this._edge, + iceCandidateStats: this._rtcIceCandidateStatsReport.iceCandidateStats, + networkTiming: this._networkTiming, + samples: this._samples, + selectedEdge: this._options.edge, + stats: stats, + testTiming: testTiming, + totals: this._getRTCSampleTotals(), + warnings: this._warnings, + }; + var selectedIceCandidatePairStats = this._rtcIceCandidateStatsReport.selectedIceCandidatePairStats; + if (selectedIceCandidatePairStats) { + report.selectedIceCandidatePairStats = selectedIceCandidatePairStats; + report.isTurnRequired = selectedIceCandidatePairStats.localCandidate.candidateType === 'relay' + || selectedIceCandidatePairStats.remoteCandidate.candidateType === 'relay'; + } + if (stats) { + report.callQuality = this._getCallQuality(stats.mos.average); + } + return report; + }; + /** + * Returns RTC stats totals for this test + */ + PreflightTest.prototype._getRTCSampleTotals = function () { + if (!this._latestSample) { + return; + } + return __assign({}, this._latestSample.totals); + }; + /** + * Returns RTC related stats captured during the test call + */ + PreflightTest.prototype._getRTCStats = function () { + var firstMosSampleIdx = this._samples.findIndex(function (sample) { return typeof sample.mos === 'number' && sample.mos > 0; }); + var samples = firstMosSampleIdx >= 0 + ? this._samples.slice(firstMosSampleIdx) + : []; + if (!samples || !samples.length) { + return; + } + return ['jitter', 'mos', 'rtt'].reduce(function (statObj, stat) { + var _a; + var values = samples.map(function (s) { return s[stat]; }); + return __assign(__assign({}, statObj), (_a = {}, _a[stat] = { + average: Number((values.reduce(function (total, value) { return total + value; }) / values.length).toPrecision(5)), + max: Math.max.apply(Math, values), + min: Math.min.apply(Math, values), + }, _a)); + }, {}); + }; + /** + * Returns a MediaStream from a media file + */ + PreflightTest.prototype._getStreamFromFile = function () { + var audioContext = this._options.audioContext; + if (!audioContext) { + throw new errors_1.NotSupportedError('Cannot fake input audio stream: AudioContext is not supported by this browser.'); + } + var audioEl = new Audio(constants_1.COWBELL_AUDIO_URL); + audioEl.addEventListener('canplaythrough', function () { return audioEl.play(); }); + if (typeof audioEl.setAttribute === 'function') { + audioEl.setAttribute('crossorigin', 'anonymous'); + } + var src = audioContext.createMediaElementSource(audioEl); + var dest = audioContext.createMediaStreamDestination(); + src.connect(dest); + return dest.stream; + }; + /** + * Initialize the device + */ + PreflightTest.prototype._initDevice = function (token, options) { + var _this = this; + try { + this._device = new (options.deviceFactory || device_1.default)(token, { + codecPreferences: options.codecPreferences, + edge: options.edge, + fileInputStream: options.fileInputStream, + logLevel: options.logLevel, + preflight: true, + }); + this._device.once(device_1.default.EventName.Registered, function () { + _this._onDeviceRegistered(); + }); + this._device.once(device_1.default.EventName.Error, function (error) { + _this._onDeviceError(error); + }); + this._device.register(); + } + catch (error) { + // We want to return before failing so the consumer can capture the event + setTimeout(function () { + _this._onFailed(error); + }); + return; + } + this._signalingTimeoutTimer = setTimeout(function () { + _this._onDeviceError(new errors_1.SignalingErrors.ConnectionError('WebSocket Connection Timeout')); + }, options.signalingTimeoutMs); + }; + /** + * Called on {@link Device} error event + * @param error + */ + PreflightTest.prototype._onDeviceError = function (error) { + this._device.destroy(); + this._onFailed(error); + }; + /** + * Called on {@link Device} ready event + */ + PreflightTest.prototype._onDeviceRegistered = function () { + return __awaiter(this, void 0, void 0, function () { + var _a, audio, publisher; + var _this = this; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + clearTimeout(this._echoTimer); + clearTimeout(this._signalingTimeoutTimer); + _a = this; + return [4 /*yield*/, this._device.connect({ + rtcConfiguration: this._options.rtcConfiguration, + })]; + case 1: + _a._call = _b.sent(); + this._networkTiming.signaling = { start: Date.now() }; + this._setupCallHandlers(this._call); + this._edge = this._device.edge || undefined; + if (this._options.fakeMicInput) { + this._echoTimer = setTimeout(function () { return _this._device.disconnectAll(); }, constants_1.ECHO_TEST_DURATION); + audio = this._device.audio; + if (audio) { + audio.disconnect(false); + audio.outgoing(false); + } + } + this._call.once('disconnect', function () { + _this._device.once(device_1.default.EventName.Unregistered, function () { return _this._onUnregistered(); }); + _this._device.destroy(); + }); + publisher = this._call['_publisher']; + publisher.on('error', function () { + if (!_this._hasInsightsErrored) { + _this._emitWarning('insights-connection-error', 'Received an error when attempting to connect to Insights gateway'); + } + _this._hasInsightsErrored = true; + }); + return [2 /*return*/]; + } + }); + }); + }; + /** + * Called when there is a fatal error + * @param error + */ + PreflightTest.prototype._onFailed = function (error) { + clearTimeout(this._echoTimer); + clearTimeout(this._signalingTimeoutTimer); + this._releaseHandlers(); + this._endTime = Date.now(); + this._status = PreflightTest.Status.Failed; + this._log.debug("#" + PreflightTest.Events.Failed, error); + this.emit(PreflightTest.Events.Failed, error); + }; + /** + * Called when the device goes offline. + * This indicates that the test has been completed, but we won't know if it failed or not. + * The onError event will be the indicator whether the test failed. + */ + PreflightTest.prototype._onUnregistered = function () { + var _this = this; + // We need to make sure we always execute preflight.on('completed') last + // as client SDK sometimes emits 'offline' event before emitting fatal errors. + setTimeout(function () { + if (_this._status === PreflightTest.Status.Failed) { + return; + } + clearTimeout(_this._echoTimer); + clearTimeout(_this._signalingTimeoutTimer); + _this._releaseHandlers(); + _this._endTime = Date.now(); + _this._status = PreflightTest.Status.Completed; + _this._report = _this._getReport(); + _this._log.debug("#" + PreflightTest.Events.Completed, JSON.stringify(_this._report)); + _this.emit(PreflightTest.Events.Completed, _this._report); + }, 10); + }; + /** + * Clean up all handlers for device and call + */ + PreflightTest.prototype._releaseHandlers = function () { + [this._device, this._call].forEach(function (emitter) { + if (emitter) { + emitter.eventNames().forEach(function (name) { return emitter.removeAllListeners(name); }); + } + }); + }; + /** + * Setup the event handlers for the {@link Call} of the test call + * @param call + */ + PreflightTest.prototype._setupCallHandlers = function (call) { + var _this = this; + if (this._options.fakeMicInput) { + // When volume events start emitting, it means all audio outputs have been created. + // Let's mute them if we're using fake mic input. + call.once('volume', function () { + call['_mediaHandler'].outputs + .forEach(function (output) { return output.audio.muted = true; }); + }); + } + call.on('warning', function (name, data) { + _this._emitWarning(name, 'Received an RTCWarning. See .rtcWarning for the RTCWarning', data); + }); + call.once('accept', function () { + _this._callSid = call['_mediaHandler'].callSid; + _this._status = PreflightTest.Status.Connected; + _this._log.debug("#" + PreflightTest.Events.Connected); + _this.emit(PreflightTest.Events.Connected); + }); + call.on('sample', function (sample) { return __awaiter(_this, void 0, void 0, function () { + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!!this._latestSample) return [3 /*break*/, 2]; + _a = this; + return [4 /*yield*/, (this._options.getRTCIceCandidateStatsReport || stats_1.getRTCIceCandidateStatsReport)(call['_mediaHandler'].version.pc)]; + case 1: + _a._rtcIceCandidateStatsReport = _b.sent(); + _b.label = 2; + case 2: + this._latestSample = sample; + this._samples.push(sample); + this._log.debug("#" + PreflightTest.Events.Sample, JSON.stringify(sample)); + this.emit(PreflightTest.Events.Sample, sample); + return [2 /*return*/]; + } + }); + }); }); + // TODO: Update the following once the SDK supports emitting these events + // Let's shim for now + [{ + reportLabel: 'peerConnection', + type: 'pcconnection', + }, { + reportLabel: 'ice', + type: 'iceconnection', + }, { + reportLabel: 'dtls', + type: 'dtlstransport', + }, { + reportLabel: 'signaling', + type: 'signaling', + }].forEach(function (_a) { + var type = _a.type, reportLabel = _a.reportLabel; + var handlerName = "on" + type + "statechange"; + var originalHandler = call['_mediaHandler'][handlerName]; + call['_mediaHandler'][handlerName] = function (state) { + var timing = _this._networkTiming[reportLabel] + = _this._networkTiming[reportLabel] || { start: 0 }; + if (state === 'connecting' || state === 'checking') { + timing.start = Date.now(); + } + else if ((state === 'connected' || state === 'stable') && !timing.duration) { + timing.end = Date.now(); + timing.duration = timing.end - timing.start; + } + originalHandler(state); + }; + }); + }; + Object.defineProperty(PreflightTest.prototype, "callSid", { + /** + * The callsid generated for the test call. + */ + get: function () { + return this._callSid; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(PreflightTest.prototype, "endTime", { + /** + * A timestamp in milliseconds of when the test ended. + */ + get: function () { + return this._endTime; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(PreflightTest.prototype, "latestSample", { + /** + * The latest WebRTC sample collected. + */ + get: function () { + return this._latestSample; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(PreflightTest.prototype, "report", { + /** + * The report for this test. + */ + get: function () { + return this._report; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(PreflightTest.prototype, "startTime", { + /** + * A timestamp in milliseconds of when the test started. + */ + get: function () { + return this._startTime; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(PreflightTest.prototype, "status", { + /** + * The status of the test. + */ + get: function () { + return this._status; + }, + enumerable: false, + configurable: true + }); + return PreflightTest; +}(events_1.EventEmitter)); +exports.PreflightTest = PreflightTest; +(function (PreflightTest) { + /** + * The quality of the call determined by different mos ranges. + * Mos is calculated base on the WebRTC stats - rtt, jitter, and packet lost. + */ + var CallQuality; + (function (CallQuality) { + /** + * If the average mos is over 4.2. + */ + CallQuality["Excellent"] = "excellent"; + /** + * If the average mos is between 4.1 and 4.2 both inclusive. + */ + CallQuality["Great"] = "great"; + /** + * If the average mos is between 3.7 and 4.0 both inclusive. + */ + CallQuality["Good"] = "good"; + /** + * If the average mos is between 3.1 and 3.6 both inclusive. + */ + CallQuality["Fair"] = "fair"; + /** + * If the average mos is 3.0 or below. + */ + CallQuality["Degraded"] = "degraded"; + })(CallQuality = PreflightTest.CallQuality || (PreflightTest.CallQuality = {})); + /** + * Possible events that a [[PreflightTest]] might emit. + */ + var Events; + (function (Events) { + /** + * See [[PreflightTest.completedEvent]] + */ + Events["Completed"] = "completed"; + /** + * See [[PreflightTest.connectedEvent]] + */ + Events["Connected"] = "connected"; + /** + * See [[PreflightTest.failedEvent]] + */ + Events["Failed"] = "failed"; + /** + * See [[PreflightTest.sampleEvent]] + */ + Events["Sample"] = "sample"; + /** + * See [[PreflightTest.warningEvent]] + */ + Events["Warning"] = "warning"; + })(Events = PreflightTest.Events || (PreflightTest.Events = {})); + /** + * Possible status of the test. + */ + var Status; + (function (Status) { + /** + * Call to Twilio has initiated. + */ + Status["Connecting"] = "connecting"; + /** + * Call to Twilio has been established. + */ + Status["Connected"] = "connected"; + /** + * The connection to Twilio has been disconnected and the test call has completed. + */ + Status["Completed"] = "completed"; + /** + * The test has stopped and failed. + */ + Status["Failed"] = "failed"; + })(Status = PreflightTest.Status || (PreflightTest.Status = {})); +})(PreflightTest = exports.PreflightTest || (exports.PreflightTest = {})); +exports.PreflightTest = PreflightTest; + +},{"../call":9,"../constants":10,"../device":12,"../errors":15,"../log":18,"../rtc/stats":32,"events":39}],21:[function(require,module,exports){ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +var events_1 = require("events"); +var C = require("./constants"); +var errors_1 = require("./errors"); +var log_1 = require("./log"); +var wstransport_1 = require("./wstransport"); +var PSTREAM_VERSION = '1.6'; +/** + * Constructor for PStream objects. + * + * @exports PStream as Twilio.PStream + * @memberOf Twilio + * @borrows EventEmitter#addListener as #addListener + * @borrows EventEmitter#removeListener as #removeListener + * @borrows EventEmitter#emit as #emit + * @borrows EventEmitter#hasListener as #hasListener + * @constructor + * @param {string} token The Twilio capabilities JWT + * @param {string[]} uris An array of PStream endpoint URIs + * @param {object} [options] + * @config {boolean} [options.backoffMaxMs=20000] Enable debugging + */ +var PStream = /** @class */ (function (_super) { + __extends(PStream, _super); + function PStream(token, uris, options) { + var _this = _super.call(this) || this; + if (!(_this instanceof PStream)) { + return new PStream(token, uris, options); + } + var defaults = { + TransportFactory: wstransport_1.default, + }; + options = options || {}; + for (var prop in defaults) { + if (prop in options) { + continue; + } + options[prop] = defaults[prop]; + } + _this.options = options; + _this.token = token || ''; + _this.status = 'disconnected'; + _this.gateway = null; + _this.region = null; + _this._messageQueue = []; + _this._preferredUri = null; + _this._uris = uris; + _this._handleTransportClose = _this._handleTransportClose.bind(_this); + _this._handleTransportError = _this._handleTransportError.bind(_this); + _this._handleTransportMessage = _this._handleTransportMessage.bind(_this); + _this._handleTransportOpen = _this._handleTransportOpen.bind(_this); + _this._log = new log_1.default('PStream'); + // NOTE(mroberts): EventEmitter requires that we catch all errors. + _this.on('error', function () { + _this._log.warn('Unexpected error handled in pstream'); + }); + /* + *events used by device + *'invite', + *'ready', + *'error', + *'offline', + * + *'cancel', + *'presence', + *'roster', + *'answer', + *'candidate', + *'hangup' + */ + var self = _this; + _this.addListener('ready', function () { + self.status = 'ready'; + }); + _this.addListener('offline', function () { + self.status = 'offline'; + }); + _this.addListener('close', function () { + self._log.info('Received "close" from server. Destroying PStream...'); + self._destroy(); + }); + _this.transport = new _this.options.TransportFactory(_this._uris, { + backoffMaxMs: _this.options.backoffMaxMs, + maxPreferredDurationMs: _this.options.maxPreferredDurationMs, + }); + Object.defineProperties(_this, { + uri: { + enumerable: true, + get: function () { + return this.transport.uri; + }, + }, + }); + _this.transport.on('close', _this._handleTransportClose); + _this.transport.on('error', _this._handleTransportError); + _this.transport.on('message', _this._handleTransportMessage); + _this.transport.on('open', _this._handleTransportOpen); + _this.transport.open(); + return _this; + } + return PStream; +}(events_1.EventEmitter)); +PStream.prototype._handleTransportClose = function () { + this.emit('transportClose'); + if (this.status !== 'disconnected') { + if (this.status !== 'offline') { + this.emit('offline', this); + } + this.status = 'disconnected'; + } +}; +PStream.prototype._handleTransportError = function (error) { + if (!error) { + this.emit('error', { error: { + code: 31000, + message: 'Websocket closed without a provided reason', + twilioError: new errors_1.SignalingErrors.ConnectionDisconnected(), + } }); + return; + } + // We receive some errors without call metadata (just the error). We need to convert these + // to be contained within the 'error' field so that these errors match the expected format. + this.emit('error', typeof error.code !== 'undefined' ? { error: error } : error); +}; +PStream.prototype._handleTransportMessage = function (msg) { + if (!msg || !msg.data || typeof msg.data !== 'string') { + return; + } + var _a = JSON.parse(msg.data), type = _a.type, _b = _a.payload, payload = _b === void 0 ? {} : _b; + this.gateway = payload.gateway || this.gateway; + this.region = payload.region || this.region; + if (type === 'error' && payload.error) { + payload.error.twilioError = new errors_1.SignalingErrors.ConnectionError(); + } + this.emit(type, payload); +}; +PStream.prototype._handleTransportOpen = function () { + var _this = this; + this.status = 'connected'; + this.setToken(this.token); + this.emit('transportOpen'); + var messages = this._messageQueue.splice(0, this._messageQueue.length); + messages.forEach(function (message) { return _this._publish.apply(_this, message); }); +}; +/** + * @return {string} + */ +PStream.toString = function () { return '[Twilio.PStream class]'; }; +PStream.prototype.toString = function () { return '[Twilio.PStream instance]'; }; +PStream.prototype.setToken = function (token) { + this._log.info('Setting token and publishing listen'); + this.token = token; + var payload = { + browserinfo: getBrowserInfo(), + token: token, + }; + this._publish('listen', payload); +}; +PStream.prototype.sendMessage = function (callsid, content, contenttype, messagetype, voiceeventsid) { + if (contenttype === void 0) { contenttype = 'application/json'; } + var payload = { + callsid: callsid, + content: content, + contenttype: contenttype, + messagetype: messagetype, + voiceeventsid: voiceeventsid, + }; + this._publish('message', payload, true); +}; +PStream.prototype.register = function (mediaCapabilities) { + var regPayload = { media: mediaCapabilities }; + this._publish('register', regPayload, true); +}; +PStream.prototype.invite = function (sdp, callsid, preflight, params) { + var payload = { + callsid: callsid, + preflight: !!preflight, + sdp: sdp, + twilio: params ? { params: params } : {}, + }; + this._publish('invite', payload, true); +}; +PStream.prototype.reconnect = function (sdp, callsid, reconnect, params) { + var payload = { + callsid: callsid, + preflight: false, + reconnect: reconnect, + sdp: sdp, + twilio: params ? { params: params } : {}, + }; + this._publish('invite', payload, true); +}; +PStream.prototype.answer = function (sdp, callsid) { + this._publish('answer', { sdp: sdp, callsid: callsid }, true); +}; +PStream.prototype.dtmf = function (callsid, digits) { + this._publish('dtmf', { callsid: callsid, dtmf: digits }, true); +}; +PStream.prototype.hangup = function (callsid, message) { + var payload = message ? { callsid: callsid, message: message } : { callsid: callsid }; + this._publish('hangup', payload, true); +}; +PStream.prototype.reject = function (callsid) { + this._publish('reject', { callsid: callsid }, true); +}; +PStream.prototype.reinvite = function (sdp, callsid) { + this._publish('reinvite', { sdp: sdp, callsid: callsid }, false); +}; +PStream.prototype._destroy = function () { + this.transport.removeListener('close', this._handleTransportClose); + this.transport.removeListener('error', this._handleTransportError); + this.transport.removeListener('message', this._handleTransportMessage); + this.transport.removeListener('open', this._handleTransportOpen); + this.transport.close(); + this.emit('offline', this); +}; +PStream.prototype.destroy = function () { + this._log.info('PStream.destroy() called...'); + this._destroy(); + return this; +}; +PStream.prototype.updatePreferredURI = function (uri) { + this._preferredUri = uri; + this.transport.updatePreferredURI(uri); +}; +PStream.prototype.updateURIs = function (uris) { + this._uris = uris; + this.transport.updateURIs(this._uris); +}; +PStream.prototype.publish = function (type, payload) { + return this._publish(type, payload, true); +}; +PStream.prototype._publish = function (type, payload, shouldRetry) { + var msg = JSON.stringify({ + payload: payload, + type: type, + version: PSTREAM_VERSION, + }); + var isSent = !!this.transport.send(msg); + if (!isSent) { + this.emit('error', { error: { + code: 31009, + message: 'No transport available to send or receive messages', + twilioError: new errors_1.GeneralErrors.TransportError(), + } }); + if (shouldRetry) { + this._messageQueue.push([type, payload, true]); + } + } +}; +function getBrowserInfo() { + var nav = typeof navigator !== 'undefined' ? navigator : {}; + var info = { + browser: { + platform: nav.platform || 'unknown', + userAgent: nav.userAgent || 'unknown', + }, + p: 'browser', + plugin: 'rtc', + v: C.RELEASE_VERSION, + }; + return info; +} +exports.default = PStream; + +},{"./constants":10,"./errors":15,"./log":18,"./wstransport":38,"events":39}],22:[function(require,module,exports){ +"use strict"; +var _a; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getRegionShortcode = exports.getChunderURIs = exports.createSignalingEndpointURL = exports.createEventGatewayURI = exports.defaultEdge = exports.regionToEdge = exports.regionShortcodes = exports.Region = exports.Edge = void 0; +/** + * @packageDocumentation + * @module Voice + * This module describes valid and deprecated regions. + */ +var errors_1 = require("./errors"); +/** + * Valid edges. + * @private + */ +var Edge; +(function (Edge) { + /** + * Public edges + */ + Edge["Sydney"] = "sydney"; + Edge["SaoPaulo"] = "sao-paulo"; + Edge["Dublin"] = "dublin"; + Edge["Frankfurt"] = "frankfurt"; + Edge["Tokyo"] = "tokyo"; + Edge["Singapore"] = "singapore"; + Edge["Ashburn"] = "ashburn"; + Edge["Umatilla"] = "umatilla"; + Edge["Roaming"] = "roaming"; + /** + * Interconnect edges + */ + Edge["AshburnIx"] = "ashburn-ix"; + Edge["SanJoseIx"] = "san-jose-ix"; + Edge["LondonIx"] = "london-ix"; + Edge["FrankfurtIx"] = "frankfurt-ix"; + Edge["SingaporeIx"] = "singapore-ix"; + Edge["SydneyIx"] = "sydney-ix"; + Edge["TokyoIx"] = "tokyo-ix"; +})(Edge = exports.Edge || (exports.Edge = {})); +/** + * Valid current regions. + * + * @deprecated + * + * CLIENT-6831 + * This is no longer used or updated for checking validity of regions in the + * SDK. We now allow any string to be passed for region. Invalid regions won't + * be able to connect, and won't throw an exception. + * + * CLIENT-7519 + * This is used again to temporarily convert edge values to regions as part of + * Phase 1 Regional. This is still considered deprecated. + * + * @private + */ +var Region; +(function (Region) { + Region["Au1"] = "au1"; + Region["Au1Ix"] = "au1-ix"; + Region["Br1"] = "br1"; + Region["De1"] = "de1"; + Region["De1Ix"] = "de1-ix"; + Region["Gll"] = "gll"; + Region["Ie1"] = "ie1"; + Region["Ie1Ix"] = "ie1-ix"; + Region["Ie1Tnx"] = "ie1-tnx"; + Region["Jp1"] = "jp1"; + Region["Jp1Ix"] = "jp1-ix"; + Region["Sg1"] = "sg1"; + Region["Sg1Ix"] = "sg1-ix"; + Region["Sg1Tnx"] = "sg1-tnx"; + Region["Us1"] = "us1"; + Region["Us1Ix"] = "us1-ix"; + Region["Us1Tnx"] = "us1-tnx"; + Region["Us2"] = "us2"; + Region["Us2Ix"] = "us2-ix"; + Region["Us2Tnx"] = "us2-tnx"; +})(Region = exports.Region || (exports.Region = {})); +/** + * Region shortcodes. Maps the full region name from AWS to the Twilio shortcode. + * @private + */ +exports.regionShortcodes = { + ASIAPAC_SINGAPORE: Region.Sg1, + ASIAPAC_SYDNEY: Region.Au1, + ASIAPAC_TOKYO: Region.Jp1, + EU_FRANKFURT: Region.De1, + EU_IRELAND: Region.Ie1, + SOUTH_AMERICA_SAO_PAULO: Region.Br1, + US_EAST_VIRGINIA: Region.Us1, + US_WEST_OREGON: Region.Us2, +}; +/** + * Region to edge mapping, as part of Phase 1 Regional (CLIENT-7519). + * Temporary. + * @private + */ +exports.regionToEdge = (_a = {}, + _a[Region.Au1] = Edge.Sydney, + _a[Region.Br1] = Edge.SaoPaulo, + _a[Region.Ie1] = Edge.Dublin, + _a[Region.De1] = Edge.Frankfurt, + _a[Region.Jp1] = Edge.Tokyo, + _a[Region.Sg1] = Edge.Singapore, + _a[Region.Us1] = Edge.Ashburn, + _a[Region.Us2] = Edge.Umatilla, + _a[Region.Gll] = Edge.Roaming, + /** + * Interconnect edges + */ + _a[Region.Us1Ix] = Edge.AshburnIx, + _a[Region.Us2Ix] = Edge.SanJoseIx, + _a[Region.Ie1Ix] = Edge.LondonIx, + _a[Region.De1Ix] = Edge.FrankfurtIx, + _a[Region.Sg1Ix] = Edge.SingaporeIx, + _a[Region.Au1Ix] = Edge.SydneyIx, + _a[Region.Jp1Ix] = Edge.TokyoIx, + /** + * Tnx regions + */ + _a[Region.Us1Tnx] = Edge.AshburnIx, + _a[Region.Us2Tnx] = Edge.AshburnIx, + _a[Region.Ie1Tnx] = Edge.LondonIx, + _a[Region.Sg1Tnx] = Edge.SingaporeIx, + _a); +/** + * The default edge to connect to and create a chunder uri from, if the edge + * parameter is not specified during setup in `Device`. + * @constant + */ +exports.defaultEdge = Edge.Roaming; +/** + * The default event gateway URI to publish to. + * @constant + * @private + */ +var defaultEventGatewayURI = 'eventgw.twilio.com'; +/** + * String template for an edge chunder URI + * @param edge - The edge. + */ +function createChunderEdgeURI(edge) { + return "voice-js." + edge + ".twilio.com"; +} +/** + * String template for a region insights URI + * @param region - The region. + */ +function createEventGatewayURI(region) { + return region + ? "eventgw." + region + ".twilio.com" + : defaultEventGatewayURI; +} +exports.createEventGatewayURI = createEventGatewayURI; +/** + * Create a signaling endpoint URL to connect a websocket to from a chunder URI. + * @param uri the chunder URI to create a signaling endpoint URL for + */ +function createSignalingEndpointURL(uri) { + return "wss://" + uri + "/signal"; +} +exports.createSignalingEndpointURL = createSignalingEndpointURL; +/** + * Get the URI associated with the passed edge. + * @private + * @param edge - A string or an array of edge values + * @returns An array of chunder URIs + */ +function getChunderURIs(edge) { + if (!!edge && typeof edge !== 'string' && !Array.isArray(edge)) { + throw new errors_1.InvalidArgumentError('If `edge` is provided, it must be of type `string` or an array of strings.'); + } + var uris; + if (edge) { + var edgeParams = Array.isArray(edge) ? edge : [edge]; + uris = edgeParams.map(function (param) { return createChunderEdgeURI(param); }); + } + else { + uris = [createChunderEdgeURI(exports.defaultEdge)]; + } + return uris; +} +exports.getChunderURIs = getChunderURIs; +/** + * Get the region shortcode by its full AWS region string. + * + * @private + * @param region - The region's full AWS string. + */ +function getRegionShortcode(region) { + return exports.regionShortcodes[region] || null; +} +exports.getRegionShortcode = getRegionShortcode; + +},{"./errors":15}],23:[function(require,module,exports){ +"use strict"; +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +Object.defineProperty(exports, "__esModule", { value: true }); +function request(method, params, callback) { + var body = JSON.stringify(params.body || {}); + var headers = new Headers(); + params.headers = params.headers || []; + Object.entries(params.headers).forEach(function (_a) { + var headerName = _a[0], headerBody = _a[1]; + return headers.append(headerName, headerBody); + }); + fetch(params.url, { body: body, headers: headers, method: method }) + .then(function (response) { return response.text(); }, callback) + .then(function (responseText) { return callback(null, responseText); }, callback); +} +/** + * Use XMLHttpRequest to get a network resource. + * @param {String} method - HTTP Method + * @param {Object} params - Request parameters + * @param {String} params.url - URL of the resource + * @param {Array} params.headers - An array of headers to pass [{ headerName : headerBody }] + * @param {Object} params.body - A JSON body to send to the resource + * @returns {response} + */ +var Request = request; +/** + * Sugar function for request('GET', params, callback); + * @param {Object} params - Request parameters + * @param {Request~get} callback - The callback that handles the response. + */ +Request.get = function get(params, callback) { + return new this('GET', params, callback); +}; +/** + * Sugar function for request('POST', params, callback); + * @param {Object} params - Request parameters + * @param {Request~post} callback - The callback that handles the response. + */ +Request.post = function post(params, callback) { + return new this('POST', params, callback); +}; +exports.default = Request; + +},{}],24:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +var errors_1 = require("../errors"); +var util = require("../util"); +function getUserMedia(constraints, options) { + options = options || {}; + options.util = options.util || util; + options.navigator = options.navigator + || (typeof navigator !== 'undefined' ? navigator : null); + return new Promise(function (resolve, reject) { + if (!options.navigator) { + throw new errors_1.NotSupportedError('getUserMedia is not supported'); + } + switch ('function') { + case typeof (options.navigator.mediaDevices && options.navigator.mediaDevices.getUserMedia): + return resolve(options.navigator.mediaDevices.getUserMedia(constraints)); + case typeof options.navigator.webkitGetUserMedia: + return options.navigator.webkitGetUserMedia(constraints, resolve, reject); + case typeof options.navigator.mozGetUserMedia: + return options.navigator.mozGetUserMedia(constraints, resolve, reject); + case typeof options.navigator.getUserMedia: + return options.navigator.getUserMedia(constraints, resolve, reject); + default: + throw new errors_1.NotSupportedError('getUserMedia is not supported'); + } + }).catch(function (e) { + throw (options.util.isFirefox() && e.name === 'NotReadableError') + ? new errors_1.NotSupportedError('Firefox does not currently support opening multiple audio input tracks' + + 'simultaneously, even across different tabs.\n' + + 'Related Bugzilla thread: https://bugzilla.mozilla.org/show_bug.cgi?id=1299324') + : e; + }); +} +exports.default = getUserMedia; + +},{"../errors":15,"../util":36}],25:[function(require,module,exports){ +"use strict"; +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.IceCandidate = void 0; +/** + * {@link RTCIceCandidate} parses an ICE candidate gathered by the browser + * and returns a IceCandidate object + */ +var IceCandidate = /** @class */ (function () { + /** + * @constructor + * @param iceCandidate RTCIceCandidate coming from the browser + */ + function IceCandidate(iceCandidate, isRemote) { + if (isRemote === void 0) { isRemote = false; } + /** + * Whether this is deleted from the list of candidate gathered + */ + this.deleted = false; + var cost; + var parts = iceCandidate.candidate.split('network-cost '); + if (parts[1]) { + cost = parseInt(parts[1], 10); + } + this.candidateType = iceCandidate.type; + this.ip = iceCandidate.ip || iceCandidate.address; + this.isRemote = isRemote; + this.networkCost = cost; + this.port = iceCandidate.port; + this.priority = iceCandidate.priority; + this.protocol = iceCandidate.protocol; + this.relatedAddress = iceCandidate.relatedAddress; + this.relatedPort = iceCandidate.relatedPort; + this.tcpType = iceCandidate.tcpType; + this.transportId = iceCandidate.sdpMid; + } + /** + * Get the payload object for insights + */ + IceCandidate.prototype.toPayload = function () { + return { + 'candidate_type': this.candidateType, + 'deleted': this.deleted, + 'ip': this.ip, + 'is_remote': this.isRemote, + 'network-cost': this.networkCost, + 'port': this.port, + 'priority': this.priority, + 'protocol': this.protocol, + 'related_address': this.relatedAddress, + 'related_port': this.relatedPort, + 'tcp_type': this.tcpType, + 'transport_id': this.transportId, + }; + }; + return IceCandidate; +}()); +exports.IceCandidate = IceCandidate; + +},{}],26:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.PeerConnection = exports.getMediaEngine = exports.enabled = void 0; +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +var peerconnection_1 = require("./peerconnection"); +exports.PeerConnection = peerconnection_1.default; +var rtcpc_1 = require("./rtcpc"); +function enabled() { + return rtcpc_1.default.test(); +} +exports.enabled = enabled; +function getMediaEngine() { + return typeof RTCIceGatherer !== 'undefined' ? 'ORTC' : 'WebRTC'; +} +exports.getMediaEngine = getMediaEngine; + +},{"./peerconnection":29,"./rtcpc":30}],27:[function(require,module,exports){ +"use strict"; +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * This file was imported from another project. If making changes to this file, please don't + * make them here. Make them on the linked repo below, then copy back: + * https://code.hq.twilio.com/client/MockRTCStatsReport + */ +// The legacy max volume, which is the positive half of a signed short integer. +var OLD_MAX_VOLUME = 32767; +var NativeRTCStatsReport = typeof window !== 'undefined' + ? window.RTCStatsReport : undefined; +/** + * Create a MockRTCStatsReport wrapper around a Map of RTCStats objects. If RTCStatsReport is available + * natively, it will be inherited so that instanceof checks pass. + * @constructor + * @extends RTCStatsReport + * @param {Map} statsMap - A Map of RTCStats objects to wrap + * with a MockRTCStatsReport object. + */ +function MockRTCStatsReport(statsMap) { + if (!(this instanceof MockRTCStatsReport)) { + return new MockRTCStatsReport(statsMap); + } + var self = this; + Object.defineProperties(this, { + _map: { value: statsMap }, + size: { + enumerable: true, + get: function () { + return self._map.size; + }, + }, + }); + this[Symbol.iterator] = statsMap[Symbol.iterator]; +} +// If RTCStatsReport is available natively, inherit it. Keep our constructor. +if (NativeRTCStatsReport) { + MockRTCStatsReport.prototype = Object.create(NativeRTCStatsReport.prototype); + MockRTCStatsReport.prototype.constructor = MockRTCStatsReport; +} +// Map the Map-like read methods to the underlying Map +['entries', 'forEach', 'get', 'has', 'keys', 'values'].forEach(function (key) { + MockRTCStatsReport.prototype[key] = function () { + var _a; + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return (_a = this._map)[key].apply(_a, args); + }; +}); +/** + * Convert an array of RTCStats objects into a mock RTCStatsReport object. + * @param {Array} + * @return {MockRTCStatsReport} + */ +MockRTCStatsReport.fromArray = function fromArray(array) { + return new MockRTCStatsReport(array.reduce(function (map, rtcStats) { + map.set(rtcStats.id, rtcStats); + return map; + }, new Map())); +}; +/** + * Convert a legacy RTCStatsResponse object into a mock RTCStatsReport object. + * @param {RTCStatsResponse} statsResponse - An RTCStatsResponse object returned by the + * legacy getStats(callback) method in Chrome. + * @return {MockRTCStatsReport} A mock RTCStatsReport object. + */ +MockRTCStatsReport.fromRTCStatsResponse = function fromRTCStatsResponse(statsResponse) { + var activeCandidatePairId; + var transportIds = new Map(); + var statsMap = statsResponse.result().reduce(function (map, report) { + var id = report.id; + switch (report.type) { + case 'googCertificate': + map.set(id, createRTCCertificateStats(report)); + break; + case 'datachannel': + map.set(id, createRTCDataChannelStats(report)); + break; + case 'googCandidatePair': + if (getBoolean(report, 'googActiveConnection')) { + activeCandidatePairId = id; + } + map.set(id, createRTCIceCandidatePairStats(report)); + break; + case 'localcandidate': + map.set(id, createRTCIceCandidateStats(report, false)); + break; + case 'remotecandidate': + map.set(id, createRTCIceCandidateStats(report, true)); + break; + case 'ssrc': + if (isPresent(report, 'packetsReceived')) { + map.set("rtp-" + id, createRTCInboundRTPStreamStats(report)); + } + else { + map.set("rtp-" + id, createRTCOutboundRTPStreamStats(report)); + } + map.set("track-" + id, createRTCMediaStreamTrackStats(report)); + map.set("codec-" + id, createRTCCodecStats(report)); + break; + case 'googComponent': + var transportReport = createRTCTransportStats(report); + transportIds.set(transportReport.selectedCandidatePairId, id); + map.set(id, createRTCTransportStats(report)); + break; + } + return map; + }, new Map()); + if (activeCandidatePairId) { + var activeTransportId = transportIds.get(activeCandidatePairId); + if (activeTransportId) { + statsMap.get(activeTransportId).dtlsState = 'connected'; + } + } + return new MockRTCStatsReport(statsMap); +}; +/** + * @param {RTCLegacyStatsReport} report + * @returns {RTCTransportStats} + */ +function createRTCTransportStats(report) { + return { + bytesReceived: undefined, + bytesSent: undefined, + dtlsState: undefined, + id: report.id, + localCertificateId: report.stat('localCertificateId'), + remoteCertificateId: report.stat('remoteCertificateId'), + rtcpTransportStatsId: undefined, + selectedCandidatePairId: report.stat('selectedCandidatePairId'), + timestamp: Date.parse(report.timestamp), + type: 'transport', + }; +} +/** + * @param {RTCLegacyStatsReport} report + * @returns {RTCCodecStats} + */ +function createRTCCodecStats(report) { + return { + channels: undefined, + clockRate: undefined, + id: report.id, + implementation: undefined, + mimeType: report.stat('mediaType') + "/" + report.stat('googCodecName'), + payloadType: undefined, + sdpFmtpLine: undefined, + timestamp: Date.parse(report.timestamp), + type: 'codec', + }; +} +/** + * @param {RTCLegacyStatsReport} report + * @returns {RTCMediaStreamTrackStats} + */ +function createRTCMediaStreamTrackStats(report) { + return { + audioLevel: isPresent(report, 'audioOutputLevel') + ? getInt(report, 'audioOutputLevel') / OLD_MAX_VOLUME + : (getInt(report, 'audioInputLevel') || 0) / OLD_MAX_VOLUME, + detached: undefined, + echoReturnLoss: getFloat(report, 'googEchoCancellationReturnLoss'), + echoReturnLossEnhancement: getFloat(report, 'googEchoCancellationReturnLossEnhancement'), + ended: undefined, + frameHeight: isPresent(report, 'googFrameHeightReceived') + ? getInt(report, 'googFrameHeightReceived') + : getInt(report, 'googFrameHeightSent'), + frameWidth: isPresent(report, 'googFrameWidthReceived') + ? getInt(report, 'googFrameWidthReceived') + : getInt(report, 'googFrameWidthSent'), + framesCorrupted: undefined, + framesDecoded: getInt(report, 'framesDecoded'), + framesDropped: undefined, + framesPerSecond: undefined, + framesReceived: undefined, + framesSent: getInt(report, 'framesEncoded'), + fullFramesLost: undefined, + id: report.id, + kind: report.stat('mediaType'), + partialFramesLost: undefined, + remoteSource: undefined, + ssrcIds: undefined, + timestamp: Date.parse(report.timestamp), + trackIdentifier: report.stat('googTrackId'), + type: 'track', + }; +} +/** + * @param {RTCLegacyStatsReport} report + * @param {boolean} isInbound - Whether to create an inbound stats object, or outbound. + * @returns {RTCRTPStreamStats} + */ +function createRTCRTPStreamStats(report, isInbound) { + return { + associateStatsId: undefined, + codecId: "codec-" + report.id, + firCount: isInbound + ? getInt(report, 'googFirsSent') + : undefined, + id: report.id, + isRemote: undefined, + mediaType: report.stat('mediaType'), + nackCount: isInbound + ? getInt(report, 'googNacksSent') + : getInt(report, 'googNacksReceived'), + pliCount: isInbound + ? getInt(report, 'googPlisSent') + : getInt(report, 'googPlisReceived'), + qpSum: getInt(report, 'qpSum'), + sliCount: undefined, + ssrc: report.stat('ssrc'), + timestamp: Date.parse(report.timestamp), + trackId: "track-" + report.id, + transportId: report.stat('transportId'), + }; +} +/** + * @param {RTCLegacyStatsReport} report + * @returns {RTCInboundRTPStreamStats} + */ +function createRTCInboundRTPStreamStats(report) { + var rtp = createRTCRTPStreamStats(report, true); + Object.assign(rtp, { + burstDiscardCount: undefined, + burstDiscardRate: undefined, + burstLossCount: undefined, + burstLossRate: undefined, + burstPacketsDiscarded: undefined, + burstPacketsLost: undefined, + bytesReceived: getInt(report, 'bytesReceived'), + fractionLost: undefined, + framesDecoded: getInt(report, 'framesDecoded'), + gapDiscardRate: undefined, + gapLossRate: undefined, + jitter: convertMsToSeconds(report.stat('googJitterReceived')), + packetsDiscarded: undefined, + packetsLost: getInt(report, 'packetsLost'), + packetsReceived: getInt(report, 'packetsReceived'), + packetsRepaired: undefined, + roundTripTime: convertMsToSeconds(report.stat('googRtt')), + type: 'inbound-rtp', + }); + return rtp; +} +/** + * @param {RTCLegacyStatsReport} report + * @returns {RTCOutboundRTPStreamStats} + */ +function createRTCOutboundRTPStreamStats(report) { + var rtp = createRTCRTPStreamStats(report, false); + Object.assign(rtp, { + bytesSent: getInt(report, 'bytesSent'), + framesEncoded: getInt(report, 'framesEncoded'), + packetsSent: getInt(report, 'packetsSent'), + remoteTimestamp: undefined, + targetBitrate: undefined, + type: 'outbound-rtp', + }); + return rtp; +} +/** + * @param {RTCLegacyStatsReport} report + * @param {boolean} isRemote - Whether to create for a remote candidate, or local candidate. + * @returns {RTCIceCandidateStats} + */ +function createRTCIceCandidateStats(report, isRemote) { + return { + candidateType: translateCandidateType(report.stat('candidateType')), + deleted: undefined, + id: report.id, + ip: report.stat('ipAddress'), + isRemote: isRemote, + port: getInt(report, 'portNumber'), + priority: getFloat(report, 'priority'), + protocol: report.stat('transport'), + relayProtocol: undefined, + timestamp: Date.parse(report.timestamp), + transportId: undefined, + type: isRemote + ? 'remote-candidate' + : 'local-candidate', + url: undefined, + }; +} +/** + * @param {RTCLegacyStatsReport} report + * @returns {RTCIceCandidatePairStats} + */ +function createRTCIceCandidatePairStats(report) { + return { + availableIncomingBitrate: undefined, + availableOutgoingBitrate: undefined, + bytesReceived: getInt(report, 'bytesReceived'), + bytesSent: getInt(report, 'bytesSent'), + consentRequestsSent: getInt(report, 'consentRequestsSent'), + currentRoundTripTime: convertMsToSeconds(report.stat('googRtt')), + id: report.id, + lastPacketReceivedTimestamp: undefined, + lastPacketSentTimestamp: undefined, + localCandidateId: report.stat('localCandidateId'), + nominated: undefined, + priority: undefined, + readable: undefined, + remoteCandidateId: report.stat('remoteCandidateId'), + requestsReceived: getInt(report, 'requestsReceived'), + requestsSent: getInt(report, 'requestsSent'), + responsesReceived: getInt(report, 'responsesReceived'), + responsesSent: getInt(report, 'responsesSent'), + retransmissionsReceived: undefined, + retransmissionsSent: undefined, + state: undefined, + timestamp: Date.parse(report.timestamp), + totalRoundTripTime: undefined, + transportId: report.stat('googChannelId'), + type: 'candidate-pair', + writable: getBoolean(report, 'googWritable'), + }; +} +/** + * @param {RTCLegacyStatsReport} report + * @returns {RTCIceCertificateStats} + */ +function createRTCCertificateStats(report) { + return { + base64Certificate: report.stat('googDerBase64'), + fingerprint: report.stat('googFingerprint'), + fingerprintAlgorithm: report.stat('googFingerprintAlgorithm'), + id: report.id, + issuerCertificateId: report.stat('googIssuerId'), + timestamp: Date.parse(report.timestamp), + type: 'certificate', + }; +} +/** + * @param {RTCLegacyStatsReport} report + * @returns {RTCDataChannelStats} + */ +function createRTCDataChannelStats(report) { + return { + bytesReceived: undefined, + bytesSent: undefined, + datachannelid: report.stat('datachannelid'), + id: report.id, + label: report.stat('label'), + messagesReceived: undefined, + messagesSent: undefined, + protocol: report.stat('protocol'), + state: report.stat('state'), + timestamp: Date.parse(report.timestamp), + transportId: report.stat('transportId'), + type: 'data-channel', + }; +} +/** + * @param {number} inMs - A time in milliseconds + * @returns {number} The time in seconds + */ +function convertMsToSeconds(inMs) { + return isNaN(inMs) || inMs === '' + ? undefined + : parseInt(inMs, 10) / 1000; +} +/** + * @param {string} type - A type in the legacy format + * @returns {string} The type adjusted to new standards for known naming changes + */ +function translateCandidateType(type) { + switch (type) { + case 'peerreflexive': + return 'prflx'; + case 'serverreflexive': + return 'srflx'; + case 'host': + case 'relay': + default: + return type; + } +} +function getInt(report, statName) { + var stat = report.stat(statName); + return isPresent(report, statName) + ? parseInt(stat, 10) + : undefined; +} +function getFloat(report, statName) { + var stat = report.stat(statName); + return isPresent(report, statName) + ? parseFloat(stat) + : undefined; +} +function getBoolean(report, statName) { + var stat = report.stat(statName); + return isPresent(report, statName) + ? (stat === 'true' || stat === true) + : undefined; +} +function isPresent(report, statName) { + var stat = report.stat(statName); + return typeof stat !== 'undefined' && stat !== ''; +} +exports.default = MockRTCStatsReport; + +},{}],28:[function(require,module,exports){ +"use strict"; +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isNonNegativeNumber = exports.calculate = void 0; +var r0 = 94.768; // Constant used in computing "rFactor". +/** + * Calculate the mos score of a stats object + * @param {number} rtt + * @param {number} jitter + * @param {number} fractionLost - The fraction of packets that have been lost. + * Calculated by packetsLost / totalPackets + * @return {number | null} mos - Calculated MOS, `1.0` through roughly `4.5`. + * Returns `null` when any of the input parameters are not a `non-negative` + * number. + */ +function calculate(rtt, jitter, fractionLost) { + if (typeof rtt !== 'number' || + typeof jitter !== 'number' || + typeof fractionLost !== 'number' || + !isNonNegativeNumber(rtt) || + !isNonNegativeNumber(jitter) || + !isNonNegativeNumber(fractionLost)) { + return null; + } + // Compute the effective latency. + var effectiveLatency = rtt + (jitter * 2) + 10; + // Compute the initial "rFactor" from effective latency. + var rFactor = 0; + switch (true) { + case effectiveLatency < 160: + rFactor = r0 - (effectiveLatency / 40); + break; + case effectiveLatency < 1000: + rFactor = r0 - ((effectiveLatency - 120) / 10); + break; + } + // Adjust "rFactor" with the fraction of packets lost. + switch (true) { + case fractionLost <= (rFactor / 2.5): + rFactor = Math.max(rFactor - fractionLost * 2.5, 6.52); + break; + default: + rFactor = 0; + break; + } + // Compute MOS from "rFactor". + var mos = 1 + + (0.035 * rFactor) + + (0.000007 * rFactor) * + (rFactor - 60) * + (100 - rFactor); + return mos; +} +exports.calculate = calculate; +/** + * Returns true if and only if the parameter passed is a number, is not `NaN`, + * is finite, and is greater than or equal to `0`. + * @param n + */ +function isNonNegativeNumber(n) { + return typeof n === 'number' && !isNaN(n) && isFinite(n) && n >= 0; +} +exports.isNonNegativeNumber = isNonNegativeNumber; +exports.default = { + calculate: calculate, + isNonNegativeNumber: isNonNegativeNumber, +}; + +},{}],29:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +var errors_1 = require("../errors"); +var log_1 = require("../log"); +var util = require("../util"); +var rtcpc_1 = require("./rtcpc"); +var sdp_1 = require("./sdp"); +var ICE_GATHERING_TIMEOUT = 15000; +var ICE_GATHERING_FAIL_NONE = 'none'; +var ICE_GATHERING_FAIL_TIMEOUT = 'timeout'; +var INITIAL_ICE_CONNECTION_STATE = 'new'; +var VOLUME_INTERVAL_MS = 50; +/** + * @typedef {Object} PeerConnection + * @param audioHelper + * @param pstream + * @param options + * @return {PeerConnection} + * @constructor + */ +function PeerConnection(audioHelper, pstream, options) { + if (!audioHelper || !pstream) { + throw new errors_1.InvalidArgumentError('Audiohelper, and pstream are required arguments'); + } + if (!(this instanceof PeerConnection)) { + return new PeerConnection(audioHelper, pstream, options); + } + this._log = new log_1.default('PeerConnection'); + function noop() { + this._log.warn('Unexpected noop call in peerconnection'); + } + this.onaudio = noop; + this.onopen = noop; + this.onerror = noop; + this.onclose = noop; + this.ondisconnected = noop; + this.onfailed = noop; + this.onconnected = noop; + this.onreconnected = noop; + this.onsignalingstatechange = noop; + this.ondtlstransportstatechange = noop; + this.onicegatheringfailure = noop; + this.onicegatheringstatechange = noop; + this.oniceconnectionstatechange = noop; + this.onpcconnectionstatechange = noop; + this.onicecandidate = noop; + this.onselectedcandidatepairchange = noop; + this.onvolume = noop; + this.version = null; + this.pstream = pstream; + this.stream = null; + this.sinkIds = new Set(['default']); + this.outputs = new Map(); + this.status = 'connecting'; + this.callSid = null; + this.isMuted = false; + var AudioContext = typeof window !== 'undefined' + && (window.AudioContext || window.webkitAudioContext); + this._isSinkSupported = !!AudioContext && + typeof HTMLAudioElement !== 'undefined' && HTMLAudioElement.prototype.setSinkId; + // NOTE(mmalavalli): Since each Connection creates its own AudioContext, + // after 6 instances an exception is thrown. Refer https://www.w3.org/2011/audio/track/issues/3. + // In order to get around it, we are re-using the Device's AudioContext. + this._audioContext = AudioContext && audioHelper._audioContext; + this._audioHelper = audioHelper; + this._hasIceCandidates = false; + this._hasIceGatheringFailures = false; + this._iceGatheringTimeoutId = null; + this._masterAudio = null; + this._masterAudioDeviceId = null; + this._mediaStreamSource = null; + this._dtmfSender = null; + this._dtmfSenderUnsupported = false; + this._callEvents = []; + this._nextTimeToPublish = Date.now(); + this._onAnswerOrRinging = noop; + this._onHangup = noop; + this._remoteStream = null; + this._shouldManageStream = true; + this._iceState = INITIAL_ICE_CONNECTION_STATE; + this._isUnifiedPlan = options.isUnifiedPlan; + this.options = options = options || {}; + this.navigator = options.navigator + || (typeof navigator !== 'undefined' ? navigator : null); + this.util = options.util || util; + this.codecPreferences = options.codecPreferences; + return this; +} +PeerConnection.prototype.uri = function () { + return this._uri; +}; +/** + * Open the underlying RTCPeerConnection with a MediaStream obtained by + * passed constraints. The resulting MediaStream is created internally + * and will therefore be managed and destroyed internally. + * @param {MediaStreamConstraints} constraints + */ +PeerConnection.prototype.openDefaultDeviceWithConstraints = function (constraints) { + return this._audioHelper._openDefaultDeviceWithConstraints(constraints) + .then(this._setInputTracksFromStream.bind(this, false)); +}; +/** + * Replace the existing input audio tracks with the audio tracks from the + * passed input audio stream. We re-use the existing stream because + * the AnalyzerNode is bound to the stream. + * @param {MediaStream} stream + */ +PeerConnection.prototype.setInputTracksFromStream = function (stream) { + var self = this; + return this._setInputTracksFromStream(true, stream).then(function () { + self._shouldManageStream = false; + }); +}; +PeerConnection.prototype._createAnalyser = function (audioContext, options) { + options = Object.assign({ + fftSize: 32, + smoothingTimeConstant: 0.3, + }, options); + var analyser = audioContext.createAnalyser(); + // tslint:disable-next-line + for (var field in options) { + analyser[field] = options[field]; + } + return analyser; +}; +PeerConnection.prototype._setVolumeHandler = function (handler) { + this.onvolume = handler; +}; +PeerConnection.prototype._startPollingVolume = function () { + if (!this._audioContext || !this.stream || !this._remoteStream) { + return; + } + var audioContext = this._audioContext; + var inputAnalyser = this._inputAnalyser = this._createAnalyser(audioContext); + var inputBufferLength = inputAnalyser.frequencyBinCount; + var inputDataArray = new Uint8Array(inputBufferLength); + this._inputAnalyser2 = this._createAnalyser(audioContext, { + maxDecibels: 0, + minDecibels: -127, + smoothingTimeConstant: 0, + }); + var outputAnalyser = this._outputAnalyser = this._createAnalyser(audioContext); + var outputBufferLength = outputAnalyser.frequencyBinCount; + var outputDataArray = new Uint8Array(outputBufferLength); + this._outputAnalyser2 = this._createAnalyser(audioContext, { + maxDecibels: 0, + minDecibels: -127, + smoothingTimeConstant: 0, + }); + this._updateInputStreamSource(this.stream); + this._updateOutputStreamSource(this._remoteStream); + var self = this; + setTimeout(function emitVolume() { + if (!self._audioContext) { + return; + } + else if (self.status === 'closed') { + self._inputAnalyser.disconnect(); + self._outputAnalyser.disconnect(); + self._inputAnalyser2.disconnect(); + self._outputAnalyser2.disconnect(); + return; + } + self._inputAnalyser.getByteFrequencyData(inputDataArray); + var inputVolume = self.util.average(inputDataArray); + self._inputAnalyser2.getByteFrequencyData(inputDataArray); + var inputVolume2 = self.util.average(inputDataArray); + self._outputAnalyser.getByteFrequencyData(outputDataArray); + var outputVolume = self.util.average(outputDataArray); + self._outputAnalyser2.getByteFrequencyData(outputDataArray); + var outputVolume2 = self.util.average(outputDataArray); + self.onvolume(inputVolume / 255, outputVolume / 255, inputVolume2, outputVolume2); + setTimeout(emitVolume, VOLUME_INTERVAL_MS); + }, VOLUME_INTERVAL_MS); +}; +PeerConnection.prototype._stopStream = function _stopStream() { + // We shouldn't stop the tracks if they were not created inside + // this PeerConnection. + if (!this._shouldManageStream) { + return; + } + this._audioHelper._stopDefaultInputDeviceStream(); +}; +/** + * Update the stream source with the new input audio stream. + * @param {MediaStream} stream + * @private + */ +PeerConnection.prototype._updateInputStreamSource = function (stream) { + if (this._inputStreamSource) { + this._inputStreamSource.disconnect(); + } + try { + this._inputStreamSource = this._audioContext.createMediaStreamSource(stream); + this._inputStreamSource.connect(this._inputAnalyser); + this._inputStreamSource.connect(this._inputAnalyser2); + } + catch (ex) { + this._log.warn('Unable to update input MediaStreamSource', ex); + this._inputStreamSource = null; + } +}; +/** + * Update the stream source with the new ouput audio stream. + * @param {MediaStream} stream + * @private + */ +PeerConnection.prototype._updateOutputStreamSource = function (stream) { + if (this._outputStreamSource) { + this._outputStreamSource.disconnect(); + } + try { + this._outputStreamSource = this._audioContext.createMediaStreamSource(stream); + this._outputStreamSource.connect(this._outputAnalyser); + this._outputStreamSource.connect(this._outputAnalyser2); + } + catch (ex) { + this._log.warn('Unable to update output MediaStreamSource', ex); + this._outputStreamSource = null; + } +}; +/** + * Replace the tracks of the current stream with new tracks. We do this rather than replacing the + * whole stream because AnalyzerNodes are bound to a stream. + * @param {Boolean} shouldClone - Whether the stream should be cloned if it is the first + * stream, or set directly. As a rule of thumb, streams that are passed in externally may have + * their lifecycle managed externally, and should be cloned so that we do not tear it or its tracks + * down when the call ends. Streams that we create internally (inside PeerConnection) should be set + * directly so that when the call ends it is disposed of. + * @param {MediaStream} newStream - The new stream to copy the tracks over from. + * @private + */ +PeerConnection.prototype._setInputTracksFromStream = function (shouldClone, newStream) { + return this._isUnifiedPlan + ? this._setInputTracksForUnifiedPlan(shouldClone, newStream) + : this._setInputTracksForPlanB(shouldClone, newStream); +}; +/** + * Replace the tracks of the current stream with new tracks using the 'plan-b' method. + * @param {Boolean} shouldClone - Whether the stream should be cloned if it is the first + * stream, or set directly. As a rule of thumb, streams that are passed in externally may have + * their lifecycle managed externally, and should be cloned so that we do not tear it or its tracks + * down when the call ends. Streams that we create internally (inside PeerConnection) should be set + * directly so that when the call ends it is disposed of. + * @param {MediaStream} newStream - The new stream to copy the tracks over from. + * @private + */ +PeerConnection.prototype._setInputTracksForPlanB = function (shouldClone, newStream) { + var _this = this; + if (!newStream) { + return Promise.reject(new errors_1.InvalidArgumentError('Can not set input stream to null while in a call')); + } + if (!newStream.getAudioTracks().length) { + return Promise.reject(new errors_1.InvalidArgumentError('Supplied input stream has no audio tracks')); + } + var localStream = this.stream; + if (!localStream) { + // We can't use MediaStream.clone() here because it stopped copying over tracks + // as of Chrome 61. https://bugs.chromium.org/p/chromium/issues/detail?id=770908 + this.stream = shouldClone ? cloneStream(newStream) : newStream; + } + else { + this._stopStream(); + removeStream(this.version.pc, localStream); + localStream.getAudioTracks().forEach(localStream.removeTrack, localStream); + newStream.getAudioTracks().forEach(localStream.addTrack, localStream); + addStream(this.version.pc, newStream); + this._updateInputStreamSource(this.stream); + } + // Apply mute settings to new input track + this.mute(this.isMuted); + if (!this.version) { + return Promise.resolve(this.stream); + } + return new Promise(function (resolve, reject) { + _this.version.createOffer(_this.options.maxAverageBitrate, _this.codecPreferences, { audio: true }, function () { + _this.version.processAnswer(_this.codecPreferences, _this._answerSdp, function () { + resolve(_this.stream); + }, reject); + }, reject); + }); +}; +/** + * Replace the tracks of the current stream with new tracks using the 'unified-plan' method. + * @param {Boolean} shouldClone - Whether the stream should be cloned if it is the first + * stream, or set directly. As a rule of thumb, streams that are passed in externally may have + * their lifecycle managed externally, and should be cloned so that we do not tear it or its tracks + * down when the call ends. Streams that we create internally (inside PeerConnection) should be set + * directly so that when the call ends it is disposed of. + * @param {MediaStream} newStream - The new stream to copy the tracks over from. + * @private + */ +PeerConnection.prototype._setInputTracksForUnifiedPlan = function (shouldClone, newStream) { + var _this = this; + if (!newStream) { + return Promise.reject(new errors_1.InvalidArgumentError('Can not set input stream to null while in a call')); + } + if (!newStream.getAudioTracks().length) { + return Promise.reject(new errors_1.InvalidArgumentError('Supplied input stream has no audio tracks')); + } + var localStream = this.stream; + var getStreamPromise = function () { + // Apply mute settings to new input track + _this.mute(_this.isMuted); + return Promise.resolve(_this.stream); + }; + if (!localStream) { + // We can't use MediaStream.clone() here because it stopped copying over tracks + // as of Chrome 61. https://bugs.chromium.org/p/chromium/issues/detail?id=770908 + this.stream = shouldClone ? cloneStream(newStream) : newStream; + } + else { + // If the call was started with gUM, and we are now replacing that track with an + // external stream's tracks, we should stop the old managed track. + if (this._shouldManageStream) { + this._stopStream(); + } + if (!this._sender) { + this._sender = this.version.pc.getSenders()[0]; + } + return this._sender.replaceTrack(newStream.getAudioTracks()[0]).then(function () { + _this._updateInputStreamSource(newStream); + _this.stream = shouldClone ? cloneStream(newStream) : newStream; + return getStreamPromise(); + }); + } + return getStreamPromise(); +}; +PeerConnection.prototype._onInputDevicesChanged = function () { + if (!this.stream) { + return; + } + // If all of our active tracks are ended, then our active input was lost + var activeInputWasLost = this.stream.getAudioTracks().every(function (track) { return track.readyState === 'ended'; }); + // We only want to act if we manage the stream in PeerConnection (It was created + // here, rather than passed in.) + if (activeInputWasLost && this._shouldManageStream) { + this.openDefaultDeviceWithConstraints({ audio: true }); + } +}; +PeerConnection.prototype._onIceGatheringFailure = function (type) { + this._hasIceGatheringFailures = true; + this.onicegatheringfailure(type); +}; +PeerConnection.prototype._onMediaConnectionStateChange = function (newState) { + var previousState = this._iceState; + if (previousState === newState + || (newState !== 'connected' + && newState !== 'disconnected' + && newState !== 'failed')) { + return; + } + this._iceState = newState; + var message; + switch (newState) { + case 'connected': + if (previousState === 'disconnected' || previousState === 'failed') { + message = 'ICE liveliness check succeeded. Connection with Twilio restored'; + this._log.info(message); + this.onreconnected(message); + } + else { + message = 'Media connection established.'; + this._log.info(message); + this.onconnected(message); + } + this._stopIceGatheringTimeout(); + this._hasIceGatheringFailures = false; + break; + case 'disconnected': + message = 'ICE liveliness check failed. May be having trouble connecting to Twilio'; + this._log.warn(message); + this.ondisconnected(message); + break; + case 'failed': + message = 'Connection with Twilio was interrupted.'; + this._log.warn(message); + this.onfailed(message); + break; + } +}; +PeerConnection.prototype._setSinkIds = function (sinkIds) { + if (!this._isSinkSupported) { + return Promise.reject(new errors_1.NotSupportedError('Audio output selection is not supported by this browser')); + } + this.sinkIds = new Set(sinkIds.forEach ? sinkIds : [sinkIds]); + return this.version + ? this._updateAudioOutputs() + : Promise.resolve(); +}; +/** + * Start timeout for ICE Gathering + */ +PeerConnection.prototype._startIceGatheringTimeout = function startIceGatheringTimeout() { + var _this = this; + this._stopIceGatheringTimeout(); + this._iceGatheringTimeoutId = setTimeout(function () { + _this._onIceGatheringFailure(ICE_GATHERING_FAIL_TIMEOUT); + }, ICE_GATHERING_TIMEOUT); +}; +/** + * Stop timeout for ICE Gathering + */ +PeerConnection.prototype._stopIceGatheringTimeout = function stopIceGatheringTimeout() { + clearInterval(this._iceGatheringTimeoutId); +}; +PeerConnection.prototype._updateAudioOutputs = function updateAudioOutputs() { + var addedOutputIds = Array.from(this.sinkIds).filter(function (id) { + return !this.outputs.has(id); + }, this); + var removedOutputIds = Array.from(this.outputs.keys()).filter(function (id) { + return !this.sinkIds.has(id); + }, this); + var self = this; + var createOutputPromises = addedOutputIds.map(this._createAudioOutput, this); + return Promise.all(createOutputPromises).then(function () { return Promise.all(removedOutputIds.map(self._removeAudioOutput, self)); }); +}; +PeerConnection.prototype._createAudio = function createAudio(arr) { + var audio = new Audio(arr); + this.onaudio(audio); + return audio; +}; +PeerConnection.prototype._createAudioOutput = function createAudioOutput(id) { + var dest = null; + if (this._mediaStreamSource) { + dest = this._audioContext.createMediaStreamDestination(); + this._mediaStreamSource.connect(dest); + } + var audio = this._createAudio(); + setAudioSource(audio, dest && dest.stream ? dest.stream : this.pcStream); + var self = this; + return audio.setSinkId(id).then(function () { return audio.play(); }).then(function () { + self.outputs.set(id, { + audio: audio, + dest: dest, + }); + }); +}; +PeerConnection.prototype._removeAudioOutputs = function removeAudioOutputs() { + if (this._masterAudio && typeof this._masterAudioDeviceId !== 'undefined') { + this._disableOutput(this, this._masterAudioDeviceId); + this.outputs.delete(this._masterAudioDeviceId); + this._masterAudioDeviceId = null; + // Release the audio resources before deleting the audio + if (!this._masterAudio.paused) { + this._masterAudio.pause(); + } + if (typeof this._masterAudio.srcObject !== 'undefined') { + this._masterAudio.srcObject = null; + } + else { + this._masterAudio.src = ''; + } + this._masterAudio = null; + } + return Array.from(this.outputs.keys()).map(this._removeAudioOutput, this); +}; +PeerConnection.prototype._disableOutput = function disableOutput(pc, id) { + var output = pc.outputs.get(id); + if (!output) { + return; + } + if (output.audio) { + output.audio.pause(); + output.audio.src = ''; + } + if (output.dest) { + output.dest.disconnect(); + } +}; +/** + * Disable a non-master output, and update the master output to assume its state. This + * is called when the device ID assigned to the master output has been removed from + * active devices. We can not simply remove the master audio output, so we must + * instead reassign it. + * @private + * @param {PeerConnection} pc + * @param {string} masterId - The current device ID assigned to the master audio element. + */ +PeerConnection.prototype._reassignMasterOutput = function reassignMasterOutput(pc, masterId) { + var masterOutput = pc.outputs.get(masterId); + pc.outputs.delete(masterId); + var self = this; + var idToReplace = Array.from(pc.outputs.keys())[0] || 'default'; + return masterOutput.audio.setSinkId(idToReplace).then(function () { + self._disableOutput(pc, idToReplace); + pc.outputs.set(idToReplace, masterOutput); + pc._masterAudioDeviceId = idToReplace; + }).catch(function rollback() { + pc.outputs.set(masterId, masterOutput); + self._log.info('Could not reassign master output. Attempted to roll back.'); + }); +}; +PeerConnection.prototype._removeAudioOutput = function removeAudioOutput(id) { + if (this._masterAudioDeviceId === id) { + return this._reassignMasterOutput(this, id); + } + this._disableOutput(this, id); + this.outputs.delete(id); + return Promise.resolve(); +}; +/** + * Use an AudioContext to potentially split our audio output stream to multiple + * audio devices. This is only available to browsers with AudioContext and + * HTMLAudioElement.setSinkId() available. We save the source stream in + * _masterAudio, and use it for one of the active audio devices. We keep + * track of its ID because we must replace it if we lose its initial device. + */ +PeerConnection.prototype._onAddTrack = function onAddTrack(pc, stream) { + var audio = pc._masterAudio = this._createAudio(); + setAudioSource(audio, stream); + audio.play(); + // Assign the initial master audio element to a random active output device + var deviceId = Array.from(pc.outputs.keys())[0] || 'default'; + pc._masterAudioDeviceId = deviceId; + pc.outputs.set(deviceId, { audio: audio }); + try { + pc._mediaStreamSource = pc._audioContext.createMediaStreamSource(stream); + } + catch (ex) { + this._log.warn('Unable to create a MediaStreamSource from onAddTrack', ex); + this._mediaStreamSource = null; + } + pc.pcStream = stream; + pc._updateAudioOutputs(); +}; +/** + * Use a single audio element to play the audio output stream. This does not + * support multiple output devices, and is a fallback for when AudioContext + * and/or HTMLAudioElement.setSinkId() is not available to the client. + */ +PeerConnection.prototype._fallbackOnAddTrack = function fallbackOnAddTrack(pc, stream) { + var audio = document && document.createElement('audio'); + audio.autoplay = true; + if (!setAudioSource(audio, stream)) { + pc._log.info('Error attaching stream to element.'); + } + pc.outputs.set('default', { audio: audio }); +}; +PeerConnection.prototype._setEncodingParameters = function (enableDscp) { + if (!enableDscp + || !this._sender + || typeof this._sender.getParameters !== 'function' + || typeof this._sender.setParameters !== 'function') { + return; + } + var params = this._sender.getParameters(); + if (!params.priority && !(params.encodings && params.encodings.length)) { + return; + } + // This is how MDN's RTPSenderParameters defines priority + params.priority = 'high'; + // And this is how it's currently implemented in Chrome M72+ + if (params.encodings && params.encodings.length) { + params.encodings.forEach(function (encoding) { + encoding.priority = 'high'; + encoding.networkPriority = 'high'; + }); + } + this._sender.setParameters(params); +}; +PeerConnection.prototype._setupPeerConnection = function (rtcConstraints, rtcConfiguration) { + var _this = this; + var self = this; + var version = new (this.options.rtcpcFactory || rtcpc_1.default)({ RTCPeerConnection: this.options.RTCPeerConnection }); + version.create(rtcConstraints, rtcConfiguration); + addStream(version.pc, this.stream); + var eventName = 'ontrack' in version.pc + ? 'ontrack' : 'onaddstream'; + version.pc[eventName] = function (event) { + var stream = self._remoteStream = event.stream || event.streams[0]; + if (typeof version.pc.getSenders === 'function') { + _this._sender = version.pc.getSenders()[0]; + } + if (self._isSinkSupported) { + self._onAddTrack(self, stream); + } + else { + self._fallbackOnAddTrack(self, stream); + } + self._startPollingVolume(); + }; + return version; +}; +PeerConnection.prototype._maybeSetIceAggressiveNomination = function (sdp) { + return this.options.forceAggressiveIceNomination ? sdp_1.setIceAggressiveNomination(sdp) : sdp; +}; +PeerConnection.prototype._setupChannel = function () { + var _this = this; + var pc = this.version.pc; + // Chrome 25 supports onopen + this.version.pc.onopen = function () { + _this.status = 'open'; + _this.onopen(); + }; + // Chrome 26 doesn't support onopen so must detect state change + this.version.pc.onstatechange = function () { + if (_this.version.pc && _this.version.pc.readyState === 'stable') { + _this.status = 'open'; + _this.onopen(); + } + }; + // Chrome 27 changed onstatechange to onsignalingstatechange + this.version.pc.onsignalingstatechange = function () { + var state = pc.signalingState; + _this._log.info("signalingState is \"" + state + "\""); + if (_this.version.pc && _this.version.pc.signalingState === 'stable') { + _this.status = 'open'; + _this.onopen(); + } + _this.onsignalingstatechange(pc.signalingState); + }; + // Chrome 72+ + pc.onconnectionstatechange = function (event) { + var state = pc.connectionState; + if (!state && event && event.target) { + // VDI environment + var targetPc = event.target; + state = targetPc.connectionState || targetPc.connectionState_; + _this._log.info("pc.connectionState not detected. Using target PC. State=" + state); + } + if (!state) { + _this._log.warn("onconnectionstatechange detected but state is \"" + state + "\""); + } + else { + _this._log.info("pc.connectionState is \"" + state + "\""); + } + _this.onpcconnectionstatechange(state); + _this._onMediaConnectionStateChange(state); + }; + pc.onicecandidate = function (event) { + var candidate = event.candidate; + if (candidate) { + _this._hasIceCandidates = true; + _this.onicecandidate(candidate); + _this._setupRTCIceTransportListener(); + } + _this._log.info("ICE Candidate: " + JSON.stringify(candidate)); + }; + pc.onicegatheringstatechange = function () { + var state = pc.iceGatheringState; + if (state === 'gathering') { + _this._startIceGatheringTimeout(); + } + else if (state === 'complete') { + _this._stopIceGatheringTimeout(); + // Fail if no candidates found + if (!_this._hasIceCandidates) { + _this._onIceGatheringFailure(ICE_GATHERING_FAIL_NONE); + } + // There was a failure mid-gathering phase. We want to start our timer and issue + // an ice restart if we don't get connected after our timeout + if (_this._hasIceCandidates && _this._hasIceGatheringFailures) { + _this._startIceGatheringTimeout(); + } + } + _this._log.info("pc.iceGatheringState is \"" + pc.iceGatheringState + "\""); + _this.onicegatheringstatechange(state); + }; + pc.oniceconnectionstatechange = function () { + _this._log.info("pc.iceConnectionState is \"" + pc.iceConnectionState + "\""); + _this.oniceconnectionstatechange(pc.iceConnectionState); + _this._onMediaConnectionStateChange(pc.iceConnectionState); + }; +}; +PeerConnection.prototype._initializeMediaStream = function (rtcConstraints, rtcConfiguration) { + // if mediastream already open then do nothing + if (this.status === 'open') { + return false; + } + if (this.pstream.status === 'disconnected') { + this.onerror({ info: { + code: 31000, + message: 'Cannot establish connection. Client is disconnected', + twilioError: new errors_1.SignalingErrors.ConnectionDisconnected(), + } }); + this.close(); + return false; + } + this.version = this._setupPeerConnection(rtcConstraints, rtcConfiguration); + this._setupChannel(); + return true; +}; +/** + * Remove reconnection-related listeners + * @private + */ +PeerConnection.prototype._removeReconnectionListeners = function () { + if (this.pstream) { + this.pstream.removeListener('answer', this._onAnswerOrRinging); + this.pstream.removeListener('hangup', this._onHangup); + } +}; +/** + * Setup a listener for RTCDtlsTransport to capture state changes events + * @private + */ +PeerConnection.prototype._setupRTCDtlsTransportListener = function () { + var _this = this; + var dtlsTransport = this.getRTCDtlsTransport(); + if (!dtlsTransport || dtlsTransport.onstatechange) { + return; + } + var handler = function () { + _this._log.info("dtlsTransportState is \"" + dtlsTransport.state + "\""); + _this.ondtlstransportstatechange(dtlsTransport.state); + }; + // Publish initial state + handler(); + dtlsTransport.onstatechange = handler; +}; +/** + * Setup a listener for RTCIceTransport to capture selected candidate pair changes + * @private + */ +PeerConnection.prototype._setupRTCIceTransportListener = function () { + var _this = this; + var iceTransport = this._getRTCIceTransport(); + if (!iceTransport || iceTransport.onselectedcandidatepairchange) { + return; + } + iceTransport.onselectedcandidatepairchange = function () { + return _this.onselectedcandidatepairchange(iceTransport.getSelectedCandidatePair()); + }; +}; +/** + * Restarts ICE for the current connection + * ICE Restart failures are ignored. Retries are managed in Connection + * @private + */ +PeerConnection.prototype.iceRestart = function () { + var _this = this; + this._log.info('Attempting to restart ICE...'); + this._hasIceCandidates = false; + this.version.createOffer(this.options.maxAverageBitrate, this.codecPreferences, { iceRestart: true }).then(function () { + _this._removeReconnectionListeners(); + _this._onAnswerOrRinging = function (payload) { + _this._removeReconnectionListeners(); + if (!payload.sdp || _this.version.pc.signalingState !== 'have-local-offer') { + var message = 'Invalid state or param during ICE Restart:' + + ("hasSdp:" + !!payload.sdp + ", signalingState:" + _this.version.pc.signalingState); + _this._log.warn(message); + return; + } + var sdp = _this._maybeSetIceAggressiveNomination(payload.sdp); + _this._answerSdp = sdp; + if (_this.status !== 'closed') { + _this.version.processAnswer(_this.codecPreferences, sdp, null, function (err) { + var message = err && err.message ? err.message : err; + _this._log.error("Failed to process answer during ICE Restart. Error: " + message); + }); + } + }; + _this._onHangup = function () { + _this._log.info('Received hangup during ICE Restart'); + _this._removeReconnectionListeners(); + }; + _this.pstream.on('answer', _this._onAnswerOrRinging); + _this.pstream.on('hangup', _this._onHangup); + _this.pstream.reinvite(_this.version.getSDP(), _this.callSid); + }).catch(function (err) { + var message = err && err.message ? err.message : err; + _this._log.error("Failed to createOffer during ICE Restart. Error: " + message); + // CreateOffer failures doesn't transition ice state to failed + // We need trigger it so it can be picked up by retries + _this.onfailed(message); + }); +}; +PeerConnection.prototype.makeOutgoingCall = function (token, params, callsid, rtcConstraints, rtcConfiguration, onMediaStarted) { + var _this = this; + if (!this._initializeMediaStream(rtcConstraints, rtcConfiguration)) { + return; + } + var self = this; + this.callSid = callsid; + function onAnswerSuccess() { + if (self.options) { + self._setEncodingParameters(self.options.dscp); + } + onMediaStarted(self.version.pc); + } + function onAnswerError(err) { + var errMsg = err.message || err; + self.onerror({ info: { + code: 31000, + message: "Error processing answer: " + errMsg, + twilioError: new errors_1.MediaErrors.ClientRemoteDescFailed(), + } }); + } + this._onAnswerOrRinging = function (payload) { + if (!payload.sdp) { + return; + } + var sdp = _this._maybeSetIceAggressiveNomination(payload.sdp); + self._answerSdp = sdp; + if (self.status !== 'closed') { + self.version.processAnswer(_this.codecPreferences, sdp, onAnswerSuccess, onAnswerError); + } + self.pstream.removeListener('answer', self._onAnswerOrRinging); + self.pstream.removeListener('ringing', self._onAnswerOrRinging); + }; + this.pstream.on('answer', this._onAnswerOrRinging); + this.pstream.on('ringing', this._onAnswerOrRinging); + function onOfferSuccess() { + if (self.status !== 'closed') { + self.pstream.invite(self.version.getSDP(), self.callSid, self.options.preflight, params); + self._setupRTCDtlsTransportListener(); + } + } + function onOfferError(err) { + var errMsg = err.message || err; + self.onerror({ info: { + code: 31000, + message: "Error creating the offer: " + errMsg, + twilioError: new errors_1.MediaErrors.ClientLocalDescFailed(), + } }); + } + this.version.createOffer(this.options.maxAverageBitrate, this.codecPreferences, { audio: true }, onOfferSuccess, onOfferError); +}; +PeerConnection.prototype.answerIncomingCall = function (callSid, sdp, rtcConstraints, rtcConfiguration, onMediaStarted) { + if (!this._initializeMediaStream(rtcConstraints, rtcConfiguration)) { + return; + } + sdp = this._maybeSetIceAggressiveNomination(sdp); + this._answerSdp = sdp.replace(/^a=setup:actpass$/gm, 'a=setup:passive'); + this.callSid = callSid; + var self = this; + function onAnswerSuccess() { + if (self.status !== 'closed') { + self.pstream.answer(self.version.getSDP(), callSid); + if (self.options) { + self._setEncodingParameters(self.options.dscp); + } + onMediaStarted(self.version.pc); + self._setupRTCDtlsTransportListener(); + } + } + function onAnswerError(err) { + var errMsg = err.message || err; + self.onerror({ info: { + code: 31000, + message: "Error creating the answer: " + errMsg, + twilioError: new errors_1.MediaErrors.ClientRemoteDescFailed(), + } }); + } + this.version.processSDP(this.options.maxAverageBitrate, this.codecPreferences, sdp, { audio: true }, onAnswerSuccess, onAnswerError); +}; +PeerConnection.prototype.close = function () { + if (this.version && this.version.pc) { + if (this.version.pc.signalingState !== 'closed') { + this.version.pc.close(); + } + this.version.pc = null; + } + if (this.stream) { + this.mute(false); + this._stopStream(); + } + this.stream = null; + this._removeReconnectionListeners(); + this._stopIceGatheringTimeout(); + Promise.all(this._removeAudioOutputs()).catch(function () { + // We don't need to alert about failures here. + }); + if (this._mediaStreamSource) { + this._mediaStreamSource.disconnect(); + } + if (this._inputAnalyser) { + this._inputAnalyser.disconnect(); + } + if (this._outputAnalyser) { + this._outputAnalyser.disconnect(); + } + if (this._inputAnalyser2) { + this._inputAnalyser2.disconnect(); + } + if (this._outputAnalyser2) { + this._outputAnalyser2.disconnect(); + } + this.status = 'closed'; + this.onclose(); +}; +PeerConnection.prototype.reject = function (callSid) { + this.callSid = callSid; +}; +PeerConnection.prototype.ignore = function (callSid) { + this.callSid = callSid; +}; +/** + * Mute or unmute input audio. If the stream is not yet present, the setting + * is saved and applied to future streams/tracks. + * @params {boolean} shouldMute - Whether the input audio should + * be muted or unmuted. + */ +PeerConnection.prototype.mute = function (shouldMute) { + this.isMuted = shouldMute; + if (!this.stream) { + return; + } + if (this._sender && this._sender.track) { + this._sender.track.enabled = !shouldMute; + } + else { + var audioTracks = typeof this.stream.getAudioTracks === 'function' + ? this.stream.getAudioTracks() + : this.stream.audioTracks; + audioTracks.forEach(function (track) { + track.enabled = !shouldMute; + }); + } +}; +/** + * Get or create an RTCDTMFSender for the first local audio MediaStreamTrack + * we can get from the RTCPeerConnection. Return null if unsupported. + * @instance + * @returns ?RTCDTMFSender + */ +PeerConnection.prototype.getOrCreateDTMFSender = function getOrCreateDTMFSender() { + if (this._dtmfSender || this._dtmfSenderUnsupported) { + return this._dtmfSender || null; + } + var self = this; + var pc = this.version.pc; + if (!pc) { + this._log.warn('No RTCPeerConnection available to call createDTMFSender on'); + return null; + } + if (typeof pc.getSenders === 'function' && (typeof RTCDTMFSender === 'function' || typeof RTCDtmfSender === 'function')) { + var chosenSender = pc.getSenders().find(function (sender) { return sender.dtmf; }); + if (chosenSender) { + this._log.info('Using RTCRtpSender#dtmf'); + this._dtmfSender = chosenSender.dtmf; + return this._dtmfSender; + } + } + if (typeof pc.createDTMFSender === 'function' && typeof pc.getLocalStreams === 'function') { + var track = pc.getLocalStreams().map(function (stream) { + var tracks = self._getAudioTracks(stream); + return tracks && tracks[0]; + })[0]; + if (!track) { + this._log.warn('No local audio MediaStreamTrack available on the RTCPeerConnection to pass to createDTMFSender'); + return null; + } + this._log.info('Creating RTCDTMFSender'); + this._dtmfSender = pc.createDTMFSender(track); + return this._dtmfSender; + } + this._log.info('RTCPeerConnection does not support RTCDTMFSender'); + this._dtmfSenderUnsupported = true; + return null; +}; +/** + * Get the RTCDtlTransport object from the PeerConnection + * @returns RTCDtlTransport + */ +PeerConnection.prototype.getRTCDtlsTransport = function getRTCDtlsTransport() { + var sender = this.version && this.version.pc + && typeof this.version.pc.getSenders === 'function' + && this.version.pc.getSenders()[0]; + return sender && sender.transport || null; +}; +PeerConnection.prototype._canStopMediaStreamTrack = function () { return typeof MediaStreamTrack.prototype.stop === 'function'; }; +PeerConnection.prototype._getAudioTracks = function (stream) { return typeof stream.getAudioTracks === 'function' ? + stream.getAudioTracks() : stream.audioTracks; }; +/** + * Get the RTCIceTransport object from the PeerConnection + * @returns RTCIceTransport + */ +PeerConnection.prototype._getRTCIceTransport = function _getRTCIceTransport() { + var dtlsTransport = this.getRTCDtlsTransport(); + return dtlsTransport && dtlsTransport.iceTransport || null; +}; +// Is PeerConnection.protocol used outside of our SDK? We should remove this if not. +PeerConnection.protocol = ((function () { return rtcpc_1.default.test() ? new rtcpc_1.default() : null; }))(); +function addStream(pc, stream) { + if (typeof pc.addTrack === 'function') { + stream.getAudioTracks().forEach(function (track) { + // The second parameters, stream, should not be necessary per the latest editor's + // draft, but FF requires it. https://bugzilla.mozilla.org/show_bug.cgi?id=1231414 + pc.addTrack(track, stream); + }); + } + else { + pc.addStream(stream); + } +} +function cloneStream(oldStream) { + var newStream = typeof MediaStream !== 'undefined' + ? new MediaStream() + : new webkitMediaStream(); + oldStream.getAudioTracks().forEach(newStream.addTrack, newStream); + return newStream; +} +function removeStream(pc, stream) { + if (typeof pc.removeTrack === 'function') { + pc.getSenders().forEach(function (sender) { pc.removeTrack(sender); }); + } + else { + pc.removeStream(stream); + } +} +/** + * Set the source of an HTMLAudioElement to the specified MediaStream + * @param {HTMLAudioElement} audio + * @param {MediaStream} stream + * @returns {boolean} Whether the audio source was set successfully + */ +function setAudioSource(audio, stream) { + if (typeof audio.srcObject !== 'undefined') { + audio.srcObject = stream; + } + else if (typeof audio.mozSrcObject !== 'undefined') { + audio.mozSrcObject = stream; + } + else if (typeof audio.src !== 'undefined') { + var _window = audio.options.window || window; + audio.src = (_window.URL || _window.webkitURL).createObjectURL(stream); + } + else { + return false; + } + return true; +} +PeerConnection.enabled = rtcpc_1.default.test(); +exports.default = PeerConnection; + +},{"../errors":15,"../log":18,"../util":36,"./rtcpc":30,"./sdp":31}],30:[function(require,module,exports){ +(function (global){(function (){ +"use strict"; +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +// tslint:disable only-arrow-functions +/* global webkitRTCPeerConnection, mozRTCPeerConnection, mozRTCSessionDescription, mozRTCIceCandidate */ +Object.defineProperty(exports, "__esModule", { value: true }); +var log_1 = require("../log"); +var util = require("../util"); +var sdp_1 = require("./sdp"); +var RTCPeerConnectionShim = require('rtcpeerconnection-shim'); +function RTCPC(options) { + if (typeof window === 'undefined') { + this.log.info('No RTCPeerConnection implementation available. The window object was not found.'); + return; + } + if (options && options.RTCPeerConnection) { + this.RTCPeerConnection = options.RTCPeerConnection; + } + else if (util.isLegacyEdge()) { + this.RTCPeerConnection = new RTCPeerConnectionShim(typeof window !== 'undefined' ? window : global); + } + else if (typeof window.RTCPeerConnection === 'function') { + this.RTCPeerConnection = window.RTCPeerConnection; + } + else if (typeof window.webkitRTCPeerConnection === 'function') { + this.RTCPeerConnection = webkitRTCPeerConnection; + } + else if (typeof window.mozRTCPeerConnection === 'function') { + this.RTCPeerConnection = mozRTCPeerConnection; + window.RTCSessionDescription = mozRTCSessionDescription; + window.RTCIceCandidate = mozRTCIceCandidate; + } + else { + this.log.info('No RTCPeerConnection implementation available'); + } +} +RTCPC.prototype.create = function (rtcConstraints, rtcConfiguration) { + this.log = new log_1.default('RTCPC'); + this.pc = new this.RTCPeerConnection(rtcConfiguration, rtcConstraints); +}; +RTCPC.prototype.createModernConstraints = function (c) { + // createOffer differs between Chrome 23 and Chrome 24+. + // See https://groups.google.com/forum/?fromgroups=#!topic/discuss-webrtc/JBDZtrMumyU + // Unfortunately I haven't figured out a way to detect which format + // is required ahead of time, so we'll first try the old way, and + // if we get an exception, then we'll try the new way. + if (typeof c === 'undefined') { + return null; + } + // NOTE(mroberts): As of Chrome 38, Chrome still appears to expect + // constraints under the 'mandatory' key, and with the first letter of each + // constraint capitalized. Firefox, on the other hand, has deprecated the + // 'mandatory' key and does not expect the first letter of each constraint + // capitalized. + var nc = Object.assign({}, c); + if (typeof webkitRTCPeerConnection !== 'undefined' && !util.isLegacyEdge()) { + nc.mandatory = {}; + if (typeof c.audio !== 'undefined') { + nc.mandatory.OfferToReceiveAudio = c.audio; + } + if (typeof c.video !== 'undefined') { + nc.mandatory.OfferToReceiveVideo = c.video; + } + } + else { + if (typeof c.audio !== 'undefined') { + nc.offerToReceiveAudio = c.audio; + } + if (typeof c.video !== 'undefined') { + nc.offerToReceiveVideo = c.video; + } + } + delete nc.audio; + delete nc.video; + return nc; +}; +RTCPC.prototype.createOffer = function (maxAverageBitrate, codecPreferences, constraints, onSuccess, onError) { + var _this = this; + constraints = this.createModernConstraints(constraints); + return promisifyCreate(this.pc.createOffer, this.pc)(constraints).then(function (offer) { + if (!_this.pc) { + return Promise.resolve(); + } + var sdp = sdp_1.setMaxAverageBitrate(offer.sdp, maxAverageBitrate); + return promisifySet(_this.pc.setLocalDescription, _this.pc)(new RTCSessionDescription({ + sdp: sdp_1.setCodecPreferences(sdp, codecPreferences), + type: 'offer', + })); + }).then(onSuccess, onError); +}; +RTCPC.prototype.createAnswer = function (maxAverageBitrate, codecPreferences, constraints, onSuccess, onError) { + var _this = this; + constraints = this.createModernConstraints(constraints); + return promisifyCreate(this.pc.createAnswer, this.pc)(constraints).then(function (answer) { + if (!_this.pc) { + return Promise.resolve(); + } + var sdp = sdp_1.setMaxAverageBitrate(answer.sdp, maxAverageBitrate); + return promisifySet(_this.pc.setLocalDescription, _this.pc)(new RTCSessionDescription({ + sdp: sdp_1.setCodecPreferences(sdp, codecPreferences), + type: 'answer', + })); + }).then(onSuccess, onError); +}; +RTCPC.prototype.processSDP = function (maxAverageBitrate, codecPreferences, sdp, constraints, onSuccess, onError) { + var _this = this; + sdp = sdp_1.setCodecPreferences(sdp, codecPreferences); + var desc = new RTCSessionDescription({ sdp: sdp, type: 'offer' }); + return promisifySet(this.pc.setRemoteDescription, this.pc)(desc).then(function () { + _this.createAnswer(maxAverageBitrate, codecPreferences, constraints, onSuccess, onError); + }); +}; +RTCPC.prototype.getSDP = function () { + return this.pc.localDescription.sdp; +}; +RTCPC.prototype.processAnswer = function (codecPreferences, sdp, onSuccess, onError) { + if (!this.pc) { + return Promise.resolve(); + } + sdp = sdp_1.setCodecPreferences(sdp, codecPreferences); + return promisifySet(this.pc.setRemoteDescription, this.pc)(new RTCSessionDescription({ sdp: sdp, type: 'answer' })).then(onSuccess, onError); +}; +/* NOTE(mroberts): Firefox 18 through 21 include a `mozRTCPeerConnection` + object, but attempting to instantiate it will throw the error + + Error: PeerConnection not enabled (did you set the pref?) + + unless the `media.peerconnection.enabled` pref is enabled. So we need to test + if we can actually instantiate `mozRTCPeerConnection`; however, if the user + *has* enabled `media.peerconnection.enabled`, we need to perform the same + test that we use to detect Firefox 24 and above, namely: + + typeof (new mozRTCPeerConnection()).getLocalStreams === 'function' + + NOTE(rrowland): We no longer support Legacy Edge as of Sep 1, 2020. +*/ +RTCPC.test = function () { + if (typeof navigator === 'object') { + var getUserMedia = (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) + || navigator.webkitGetUserMedia + || navigator.mozGetUserMedia + || navigator.getUserMedia; + if (util.isLegacyEdge(navigator)) { + return false; + } + if (getUserMedia && typeof window.RTCPeerConnection === 'function') { + return true; + } + else if (getUserMedia && typeof window.webkitRTCPeerConnection === 'function') { + return true; + } + else if (getUserMedia && typeof window.mozRTCPeerConnection === 'function') { + try { + var test_1 = new window.mozRTCPeerConnection(); + if (typeof test_1.getLocalStreams !== 'function') { + return false; + } + } + catch (e) { + return false; + } + return true; + } + else if (typeof RTCIceGatherer !== 'undefined') { + return true; + } + } + return false; +}; +function promisify(fn, ctx, areCallbacksFirst, checkRval) { + return function () { + var args = Array.prototype.slice.call(arguments); + return new Promise(function (resolve) { + var returnValue = fn.apply(ctx, args); + if (!checkRval) { + resolve(returnValue); + return; + } + if (typeof returnValue === 'object' && typeof returnValue.then === 'function') { + resolve(returnValue); + } + else { + throw new Error(); + } + }).catch(function () { return new Promise(function (resolve, reject) { + fn.apply(ctx, areCallbacksFirst + ? [resolve, reject].concat(args) + : args.concat([resolve, reject])); + }); }); + }; +} +function promisifyCreate(fn, ctx) { + return promisify(fn, ctx, true, true); +} +function promisifySet(fn, ctx) { + return promisify(fn, ctx, false, false); +} +exports.default = RTCPC; + +}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"../log":18,"../util":36,"./sdp":31,"rtcpeerconnection-shim":45}],31:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.setMaxAverageBitrate = exports.setIceAggressiveNomination = exports.setCodecPreferences = exports.getPreferredCodecInfo = void 0; +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +var util = require("../util"); +var ptToFixedBitrateAudioCodecName = { + 0: 'PCMU', + 8: 'PCMA', +}; +var defaultOpusId = 111; +var BITRATE_MAX = 510000; +var BITRATE_MIN = 6000; +function getPreferredCodecInfo(sdp) { + var _a = /a=rtpmap:(\d+) (\S+)/m.exec(sdp) || [null, '', ''], codecId = _a[1], codecName = _a[2]; + var regex = new RegExp("a=fmtp:" + codecId + " (\\S+)", 'm'); + var _b = regex.exec(sdp) || [null, ''], codecParams = _b[1]; + return { codecName: codecName, codecParams: codecParams }; +} +exports.getPreferredCodecInfo = getPreferredCodecInfo; +function setIceAggressiveNomination(sdp) { + // This only works on Chrome. We don't want any side effects on other browsers + // https://bugs.chromium.org/p/chromium/issues/detail?id=1024096 + // https://issues.corp.twilio.com/browse/CLIENT-6911 + if (!util.isChrome(window, window.navigator)) { + return sdp; + } + return sdp.split('\n') + .filter(function (line) { return line.indexOf('a=ice-lite') === -1; }) + .join('\n'); +} +exports.setIceAggressiveNomination = setIceAggressiveNomination; +function setMaxAverageBitrate(sdp, maxAverageBitrate) { + if (typeof maxAverageBitrate !== 'number' + || maxAverageBitrate < BITRATE_MIN + || maxAverageBitrate > BITRATE_MAX) { + return sdp; + } + var matches = /a=rtpmap:(\d+) opus/m.exec(sdp); + var opusId = matches && matches.length ? matches[1] : defaultOpusId; + var regex = new RegExp("a=fmtp:" + opusId); + var lines = sdp.split('\n').map(function (line) { return regex.test(line) + ? line + (";maxaveragebitrate=" + maxAverageBitrate) + : line; }); + return lines.join('\n'); +} +exports.setMaxAverageBitrate = setMaxAverageBitrate; +/** + * Return a new SDP string with the re-ordered codec preferences. + * @param {string} sdp + * @param {Array} preferredCodecs - If empty, the existing order + * of audio codecs is preserved + * @returns {string} Updated SDP string + */ +function setCodecPreferences(sdp, preferredCodecs) { + var mediaSections = getMediaSections(sdp); + var session = sdp.split('\r\nm=')[0]; + return [session].concat(mediaSections.map(function (section) { + // Codec preferences should not be applied to m=application sections. + if (!/^m=(audio|video)/.test(section)) { + return section; + } + var kind = section.match(/^m=(audio|video)/)[1]; + var codecMap = createCodecMapForMediaSection(section); + var payloadTypes = getReorderedPayloadTypes(codecMap, preferredCodecs); + var newSection = setPayloadTypesInMediaSection(payloadTypes, section); + var pcmaPayloadTypes = codecMap.get('pcma') || []; + var pcmuPayloadTypes = codecMap.get('pcmu') || []; + var fixedBitratePayloadTypes = kind === 'audio' + ? new Set(pcmaPayloadTypes.concat(pcmuPayloadTypes)) + : new Set(); + return fixedBitratePayloadTypes.has(payloadTypes[0]) + ? newSection.replace(/\r\nb=(AS|TIAS):([0-9]+)/g, '') + : newSection; + })).join('\r\n'); +} +exports.setCodecPreferences = setCodecPreferences; +/** + * Get the m= sections of a particular kind and direction from an sdp. + * @param {string} sdp - SDP string + * @param {string} [kind] - Pattern for matching kind + * @param {string} [direction] - Pattern for matching direction + * @returns {Array} mediaSections + */ +function getMediaSections(sdp, kind, direction) { + return sdp.replace(/\r\n\r\n$/, '\r\n').split('\r\nm=').slice(1).map(function (mediaSection) { return "m=" + mediaSection; }).filter(function (mediaSection) { + var kindPattern = new RegExp("m=" + (kind || '.*'), 'gm'); + var directionPattern = new RegExp("a=" + (direction || '.*'), 'gm'); + return kindPattern.test(mediaSection) && directionPattern.test(mediaSection); + }); +} +/** + * Create a Codec Map for the given m= section. + * @param {string} section - The given m= section + * @returns {Map>} + */ +function createCodecMapForMediaSection(section) { + return Array.from(createPtToCodecName(section)).reduce(function (codecMap, pair) { + var pt = pair[0]; + var codecName = pair[1]; + var pts = codecMap.get(codecName) || []; + return codecMap.set(codecName, pts.concat(pt)); + }, new Map()); +} +/** + * Create the reordered Codec Payload Types based on the preferred Codec Names. + * @param {Map>} codecMap - Codec Map + * @param {Array} preferredCodecs - Preferred Codec Names + * @returns {Array} Reordered Payload Types + */ +function getReorderedPayloadTypes(codecMap, preferredCodecs) { + preferredCodecs = preferredCodecs.map(function (codecName) { return codecName.toLowerCase(); }); + var preferredPayloadTypes = util.flatMap(preferredCodecs, function (codecName) { return codecMap.get(codecName) || []; }); + var remainingCodecs = util.difference(Array.from(codecMap.keys()), preferredCodecs); + var remainingPayloadTypes = util.flatMap(remainingCodecs, function (codecName) { return codecMap.get(codecName); }); + return preferredPayloadTypes.concat(remainingPayloadTypes); +} +/** + * Set the given Codec Payload Types in the first line of the given m= section. + * @param {Array} payloadTypes - Payload Types + * @param {string} section - Given m= section + * @returns {string} - Updated m= section + */ +function setPayloadTypesInMediaSection(payloadTypes, section) { + var lines = section.split('\r\n'); + var mLine = lines[0]; + var otherLines = lines.slice(1); + mLine = mLine.replace(/([0-9]+\s?)+$/, payloadTypes.join(' ')); + return [mLine].concat(otherLines).join('\r\n'); +} +/** + * Create a Map from PTs to codec names for the given m= section. + * @param {string} mediaSection - The given m= section. + * @returns {Map} ptToCodecName + */ +function createPtToCodecName(mediaSection) { + return getPayloadTypesInMediaSection(mediaSection).reduce(function (ptToCodecName, pt) { + var rtpmapPattern = new RegExp("a=rtpmap:" + pt + " ([^/]+)"); + var matches = mediaSection.match(rtpmapPattern); + var codecName = matches + ? matches[1].toLowerCase() + : ptToFixedBitrateAudioCodecName[pt] + ? ptToFixedBitrateAudioCodecName[pt].toLowerCase() + : ''; + return ptToCodecName.set(pt, codecName); + }, new Map()); +} +/** + * Get the Codec Payload Types present in the first line of the given m= section + * @param {string} section - The m= section + * @returns {Array} Payload Types + */ +function getPayloadTypesInMediaSection(section) { + var mLine = section.split('\r\n')[0]; + // In "m= ... ", + // the regex matches and the PayloadTypes. + var matches = mLine.match(/([0-9]+)/g); + // This should not happen, but in case there are no PayloadTypes in + // the m= line, return an empty array. + if (!matches) { + return []; + } + // Since only the PayloadTypes are needed, we discard the . + return matches.slice(1).map(function (match) { return parseInt(match, 10); }); +} + +},{"../util":36}],32:[function(require,module,exports){ +"use strict"; +var __spreadArrays = (this && this.__spreadArrays) || function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getRTCIceCandidateStatsReport = exports.getRTCStats = void 0; +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +// tslint:disable no-empty +var errors_1 = require("../errors"); +var mockrtcstatsreport_1 = require("./mockrtcstatsreport"); +var ERROR_PEER_CONNECTION_NULL = 'PeerConnection is null'; +var ERROR_WEB_RTC_UNSUPPORTED = 'WebRTC statistics are unsupported'; +/** + * Helper function to find a specific stat from a report. + * Some environment provide the stats report as a map (regular browsers) + * but some provide stats report as an array (citrix vdi) + * @private + */ +function findStatById(report, id) { + if (typeof report.get === 'function') { + return report.get(id); + } + return report.find(function (s) { return s.id === id; }); +} +/** + * Generate WebRTC statistics report for the given {@link PeerConnection} + * @param {PeerConnection} peerConnection - Target connection. + * @return {Promise} WebRTC RTCStatsReport object + */ +function getRTCStatsReport(peerConnection) { + if (!peerConnection) { + return Promise.reject(new errors_1.InvalidArgumentError(ERROR_PEER_CONNECTION_NULL)); + } + if (typeof peerConnection.getStats !== 'function') { + return Promise.reject(new errors_1.NotSupportedError(ERROR_WEB_RTC_UNSUPPORTED)); + } + var promise; + try { + promise = peerConnection.getStats(); + } + catch (e) { + promise = new Promise(function (resolve) { return peerConnection.getStats(resolve); }).then(mockrtcstatsreport_1.default.fromRTCStatsResponse); + } + return promise; +} +/** + * @typedef {Object} StatsOptions + * Used for testing to inject and extract methods. + * @property {function} [createRTCSample] - Method for parsing an RTCStatsReport + */ +/** + * Collects any WebRTC statistics for the given {@link PeerConnection} + * @param {PeerConnection} peerConnection - Target connection. + * @param {StatsOptions} options - List of custom options. + * @return {Promise} Universally-formatted version of RTC stats. + */ +function getRTCStats(peerConnection, options) { + options = Object.assign({ createRTCSample: createRTCSample }, options); + return getRTCStatsReport(peerConnection).then(options.createRTCSample); +} +exports.getRTCStats = getRTCStats; +/** + * Generate WebRTC stats report containing relevant information about ICE candidates for the given {@link PeerConnection} + * @param {PeerConnection} peerConnection - Target connection. + * @return {Promise} RTCIceCandidateStatsReport object + */ +function getRTCIceCandidateStatsReport(peerConnection) { + return getRTCStatsReport(peerConnection).then(function (report) { + // Find the relevant information needed to determine selected candidates later + var _a = Array.from(report.values()).reduce(function (rval, stat) { + ['candidatePairs', 'localCandidates', 'remoteCandidates'].forEach(function (prop) { + if (!rval[prop]) { + rval[prop] = []; + } + }); + switch (stat.type) { + case 'candidate-pair': + rval.candidatePairs.push(stat); + break; + case 'local-candidate': + rval.localCandidates.push(stat); + break; + case 'remote-candidate': + rval.remoteCandidates.push(stat); + break; + case 'transport': + // This transport is the one being used if selectedCandidatePairId is populated + if (stat.selectedCandidatePairId) { + rval.transport = stat; + } + break; + } + return rval; + }, {}), candidatePairs = _a.candidatePairs, localCandidates = _a.localCandidates, remoteCandidates = _a.remoteCandidates, transport = _a.transport; + // This is a report containing information about the selected candidates, such as IDs + // This is coming from WebRTC stats directly and doesn't contain the actual ICE Candidates info + var selectedCandidatePairReport = candidatePairs.find(function (pair) { + // Firefox + return pair.selected || + // Spec-compliant way + (transport && pair.id === transport.selectedCandidatePairId); + }); + var selectedIceCandidatePairStats; + if (selectedCandidatePairReport) { + selectedIceCandidatePairStats = { + localCandidate: localCandidates.find(function (candidate) { return candidate.id === selectedCandidatePairReport.localCandidateId; }), + remoteCandidate: remoteCandidates.find(function (candidate) { return candidate.id === selectedCandidatePairReport.remoteCandidateId; }), + }; + } + // Build the return object + return { + iceCandidateStats: __spreadArrays(localCandidates, remoteCandidates), + selectedIceCandidatePairStats: selectedIceCandidatePairStats, + }; + }); +} +exports.getRTCIceCandidateStatsReport = getRTCIceCandidateStatsReport; +/** + * @typedef {Object} RTCSample - A sample containing relevant WebRTC stats information. + * @property {Number} [timestamp] + * @property {String} [codecName] - MimeType name of the codec being used by the outbound audio stream + * @property {Number} [rtt] - Round trip time + * @property {Number} [jitter] + * @property {Number} [packetsSent] + * @property {Number} [packetsLost] + * @property {Number} [packetsReceived] + * @property {Number} [bytesReceived] + * @property {Number} [bytesSent] + * @property {Number} [localAddress] + * @property {Number} [remoteAddress] + */ +function RTCSample() { } +/** + * Create an RTCSample object from an RTCStatsReport + * @private + * @param {RTCStatsReport} statsReport + * @returns {RTCSample} + */ +function createRTCSample(statsReport) { + var activeTransportId = null; + var sample = new RTCSample(); + var fallbackTimestamp; + Array.from(statsReport.values()).forEach(function (stats) { + // Skip isRemote tracks which will be phased out completely and break in FF66. + if (stats.isRemote) { + return; + } + // Firefox hack -- Older firefox doesn't have dashes in type names + var type = stats.type.replace('-', ''); + fallbackTimestamp = fallbackTimestamp || stats.timestamp; + // (rrowland) As I understand it, this is supposed to come in on remote-inbound-rtp but it's + // currently coming in on remote-outbound-rtp, so I'm leaving this outside the switch until + // the appropriate place to look is cleared up. + if (stats.remoteId) { + var remote = findStatById(statsReport, stats.remoteId); + if (remote && remote.roundTripTime) { + sample.rtt = remote.roundTripTime * 1000; + } + } + switch (type) { + case 'inboundrtp': + sample.timestamp = sample.timestamp || stats.timestamp; + sample.jitter = stats.jitter * 1000; + sample.packetsLost = stats.packetsLost; + sample.packetsReceived = stats.packetsReceived; + sample.bytesReceived = stats.bytesReceived; + break; + case 'outboundrtp': + sample.timestamp = stats.timestamp; + sample.packetsSent = stats.packetsSent; + sample.bytesSent = stats.bytesSent; + if (stats.codecId) { + var codec = findStatById(statsReport, stats.codecId); + sample.codecName = codec + ? codec.mimeType && codec.mimeType.match(/(.*\/)?(.*)/)[2] + : stats.codecId; + } + break; + case 'transport': + activeTransportId = stats.id; + break; + } + }); + if (!sample.timestamp) { + sample.timestamp = fallbackTimestamp; + } + var activeTransport = findStatById(statsReport, activeTransportId); + if (!activeTransport) { + return sample; + } + var selectedCandidatePair = findStatById(statsReport, activeTransport.selectedCandidatePairId); + if (!selectedCandidatePair) { + return sample; + } + var localCandidate = findStatById(statsReport, selectedCandidatePair.localCandidateId); + var remoteCandidate = findStatById(statsReport, selectedCandidatePair.remoteCandidateId); + if (!sample.rtt) { + sample.rtt = selectedCandidatePair && + (selectedCandidatePair.currentRoundTripTime * 1000); + } + Object.assign(sample, { + // ip is deprecated. use address first then ip if on older versions of browser + localAddress: localCandidate && (localCandidate.address || localCandidate.ip), + remoteAddress: remoteCandidate && (remoteCandidate.address || remoteCandidate.ip), + }); + return sample; +} + +},{"../errors":15,"./mockrtcstatsreport":27}],33:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +var MediaDeviceInfoShim = /** @class */ (function () { + function MediaDeviceInfoShim(options) { + Object.defineProperties(this, { + deviceId: { get: function () { return options.deviceId; } }, + groupId: { get: function () { return options.groupId; } }, + kind: { get: function () { return options.kind; } }, + label: { get: function () { return options.label; } }, + }); + } + return MediaDeviceInfoShim; +}()); +exports.default = MediaDeviceInfoShim; + +},{}],34:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +var asyncQueue_1 = require("./asyncQueue"); +var audioplayer_1 = require("./audioplayer/audioplayer"); +var errors_1 = require("./errors"); +/** + * @class + * @param {string} name - Name of the sound + * @param {string} url - URL of the sound + * @param {Sound#ConstructorOptions} options + * @property {boolean} isPlaying - Whether the Sound is currently playing audio. + * @property {string} name - Name of the sound + * @property {string} url - URL of the sound + * @property {AudioContext} audioContext - The AudioContext to use if available for AudioPlayer. + */ +/** + * @typedef {Object} Sound#ConstructorOptions + * @property {number} [maxDuration=0] - The maximum length of time to play the sound + * before stopping it. + * @property {Boolean} [shouldLoop=false] - Whether the sound should be looped. + */ +function Sound(name, url, options) { + if (!(this instanceof Sound)) { + return new Sound(name, url, options); + } + if (!name || !url) { + throw new errors_1.InvalidArgumentError('name and url are required arguments'); + } + options = Object.assign({ + AudioFactory: typeof Audio !== 'undefined' ? Audio : null, + maxDuration: 0, + shouldLoop: false, + }, options); + options.AudioPlayer = options.audioContext + ? audioplayer_1.default.bind(audioplayer_1.default, options.audioContext) + : options.AudioFactory; + Object.defineProperties(this, { + _Audio: { value: options.AudioPlayer }, + _activeEls: { value: new Map() }, + _isSinkSupported: { + value: options.AudioFactory !== null + && typeof options.AudioFactory.prototype.setSinkId === 'function', + }, + _maxDuration: { value: options.maxDuration }, + _maxDurationTimeout: { + value: null, + writable: true, + }, + _operations: { value: new asyncQueue_1.AsyncQueue() }, + _playPromise: { + value: null, + writable: true, + }, + _shouldLoop: { value: options.shouldLoop }, + _sinkIds: { value: ['default'] }, + isPlaying: { + enumerable: true, + get: function () { + return !!this._playPromise; + }, + }, + name: { + enumerable: true, + value: name, + }, + url: { + enumerable: true, + value: url, + }, + }); + if (this._Audio) { + // Play it (muted and should not loop) as soon as possible so that it does not get incorrectly caught by Chrome's + // "gesture requirement for media playback" feature. + // https://plus.google.com/+FrancoisBeaufort/posts/6PiJQqJzGqX + this._play(true, false); + } +} +function destroyAudioElement(audioElement) { + if (audioElement) { + audioElement.pause(); + audioElement.src = ''; + audioElement.srcObject = null; + audioElement.load(); + } +} +/** + * Plays the audio element that was initialized using the speficied sinkId + */ +Sound.prototype._playAudioElement = function _playAudioElement(sinkId, isMuted, shouldLoop) { + var _this = this; + var audioElement = this._activeEls.get(sinkId); + if (!audioElement) { + throw new errors_1.InvalidArgumentError("sinkId: \"" + sinkId + "\" doesn't have an audio element"); + } + audioElement.muted = !!isMuted; + audioElement.loop = !!shouldLoop; + return audioElement.play() + .then(function () { return audioElement; }) + .catch(function (reason) { + destroyAudioElement(audioElement); + _this._activeEls.delete(sinkId); + throw reason; + }); +}; +/** + * Start playing the sound. Will stop the currently playing sound first. + * If it exists, the audio element that was initialized for the sinkId will be used + */ +Sound.prototype._play = function _play(forceIsMuted, forceShouldLoop) { + if (this.isPlaying) { + this._stop(); + } + if (this._maxDuration > 0) { + this._maxDurationTimeout = setTimeout(this._stop.bind(this), this._maxDuration); + } + forceShouldLoop = typeof forceShouldLoop === 'boolean' ? forceShouldLoop : this._shouldLoop; + var self = this; + var playPromise = this._playPromise = Promise.all(this._sinkIds.map(function createAudioElement(sinkId) { + if (!self._Audio) { + return Promise.resolve(); + } + var audioElement = self._activeEls.get(sinkId); + if (audioElement) { + return self._playAudioElement(sinkId, forceIsMuted, forceShouldLoop); + } + audioElement = new self._Audio(self.url); + // Make sure the browser always retrieves the resource using CORS. + // By default when using media tags, origin header is not sent to server + // which causes the server to not return CORS headers. When this caches + // on the CDN or browser, it causes issues to future requests that needs CORS, + // which is true when using AudioContext. Please note that we won't have to do this + // once we migrate to CloudFront. + if (typeof audioElement.setAttribute === 'function') { + audioElement.setAttribute('crossorigin', 'anonymous'); + } + /** + * (rrowland) Bug in Chrome 53 & 54 prevents us from calling Audio.setSinkId without + * crashing the tab. https://bugs.chromium.org/p/chromium/issues/detail?id=655342 + */ + return new Promise(function (resolve) { + audioElement.addEventListener('canplaythrough', resolve); + }).then(function () { + return (self._isSinkSupported + ? audioElement.setSinkId(sinkId) + : Promise.resolve()).then(function setSinkIdSuccess() { + self._activeEls.set(sinkId, audioElement); + // Stop has been called, bail out + if (!self._playPromise) { + return Promise.resolve(); + } + return self._playAudioElement(sinkId, forceIsMuted, forceShouldLoop); + }); + }); + })); + return playPromise; +}; +/** + * Stop playing the sound. + */ +Sound.prototype._stop = function _stop() { + var _this = this; + this._activeEls.forEach(function (audioEl, sinkId) { + if (_this._sinkIds.includes(sinkId)) { + audioEl.pause(); + audioEl.currentTime = 0; + } + else { + // Destroy the ones that are not used anymore + destroyAudioElement(audioEl); + _this._activeEls.delete(sinkId); + } + }); + clearTimeout(this._maxDurationTimeout); + this._playPromise = null; + this._maxDurationTimeout = null; +}; +/** + * Update the sinkIds of the audio output devices this sound should play through. + */ +Sound.prototype.setSinkIds = function setSinkIds(ids) { + if (!this._isSinkSupported) { + return; + } + ids = ids.forEach ? ids : [ids]; + [].splice.apply(this._sinkIds, [0, this._sinkIds.length].concat(ids)); +}; +/** + * Add a stop operation to the queue + */ +Sound.prototype.stop = function stop() { + var _this = this; + this._operations.enqueue(function () { + _this._stop(); + return Promise.resolve(); + }); +}; +/** + * Add a play operation to the queue + */ +Sound.prototype.play = function play() { + var _this = this; + return this._operations.enqueue(function () { return _this._play(); }); +}; +exports.default = Sound; + +},{"./asyncQueue":2,"./audioplayer/audioplayer":4,"./errors":15}],35:[function(require,module,exports){ +"use strict"; +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __spreadArrays = (this && this.__spreadArrays) || function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var events_1 = require("events"); +var errors_1 = require("./errors"); +var mos_1 = require("./rtc/mos"); +var stats_1 = require("./rtc/stats"); +var util_1 = require("./util"); +// How many samples we use when testing metric thresholds +var SAMPLE_COUNT_METRICS = 5; +// How many samples that need to cross the threshold to +// raise or clear a warning. +var SAMPLE_COUNT_CLEAR = 0; +var SAMPLE_COUNT_RAISE = 3; +var SAMPLE_INTERVAL = 1000; +var WARNING_TIMEOUT = 5 * 1000; +var DEFAULT_THRESHOLDS = { + audioInputLevel: { minStandardDeviation: 327.67, sampleCount: 10 }, + audioOutputLevel: { minStandardDeviation: 327.67, sampleCount: 10 }, + bytesReceived: { clearCount: 2, min: 1, raiseCount: 3, sampleCount: 3 }, + bytesSent: { clearCount: 2, min: 1, raiseCount: 3, sampleCount: 3 }, + jitter: { max: 30 }, + mos: { min: 3 }, + packetsLostFraction: [{ + max: 1, + }, { + clearValue: 1, + maxAverage: 3, + sampleCount: 7, + }], + rtt: { max: 400 }, +}; +/** + * Count the number of values that cross the max threshold. + * @private + * @param max - The max allowable value. + * @param values - The values to iterate over. + * @returns The amount of values in which the stat crossed the threshold. + */ +function countHigh(max, values) { + return values.reduce(function (highCount, value) { return highCount += (value > max) ? 1 : 0; }, 0); +} +/** + * Count the number of values that cross the min threshold. + * @private + * @param min - The minimum allowable value. + * @param values - The values to iterate over. + * @returns The amount of values in which the stat crossed the threshold. + */ +function countLow(min, values) { + return values.reduce(function (lowCount, value) { return lowCount += (value < min) ? 1 : 0; }, 0); +} +/** + * Calculate the standard deviation from a list of numbers. + * @private + * @param values The list of numbers to calculate the standard deviation from. + * @returns The standard deviation of a list of numbers. + */ +function calculateStandardDeviation(values) { + if (values.length <= 0) { + return null; + } + var valueAverage = values.reduce(function (partialSum, value) { return partialSum + value; }, 0) / values.length; + var diffSquared = values.map(function (value) { return Math.pow(value - valueAverage, 2); }); + var stdDev = Math.sqrt(diffSquared.reduce(function (partialSum, value) { return partialSum + value; }, 0) / diffSquared.length); + return stdDev; +} +/** + * Flatten a set of numerical sample sets into a single array of samples. + * @param sampleSets + */ +function flattenSamples(sampleSets) { + return sampleSets.reduce(function (flat, current) { return __spreadArrays(flat, current); }, []); +} +/** + * {@link StatsMonitor} polls a peerConnection via PeerConnection.getStats + * and emits warnings when stats cross the specified threshold values. + */ +var StatsMonitor = /** @class */ (function (_super) { + __extends(StatsMonitor, _super); + /** + * @constructor + * @param [options] - Optional settings + */ + function StatsMonitor(options) { + var _this = _super.call(this) || this; + /** + * A map of warnings with their raised time + */ + _this._activeWarnings = new Map(); + /** + * A map of stats with the number of exceeded thresholds + */ + _this._currentStreaks = new Map(); + /** + * Keeps track of input volumes in the last second + */ + _this._inputVolumes = []; + /** + * Keeps track of output volumes in the last second + */ + _this._outputVolumes = []; + /** + * Sample buffer. Saves most recent samples + */ + _this._sampleBuffer = []; + /** + * Keeps track of supplemental sample values. + * + * Currently used for constant audio detection. Contains an array of volume + * samples for each sample interval. + */ + _this._supplementalSampleBuffers = { + audioInputLevel: [], + audioOutputLevel: [], + }; + /** + * Whether warnings should be enabled + */ + _this._warningsEnabled = true; + options = options || {}; + _this._getRTCStats = options.getRTCStats || stats_1.getRTCStats; + _this._mos = options.Mos || mos_1.default; + _this._peerConnection = options.peerConnection; + _this._thresholds = __assign(__assign({}, DEFAULT_THRESHOLDS), options.thresholds); + var thresholdSampleCounts = Object.values(_this._thresholds) + .map(function (threshold) { return threshold.sampleCount; }) + .filter(function (sampleCount) { return !!sampleCount; }); + _this._maxSampleCount = Math.max.apply(Math, __spreadArrays([SAMPLE_COUNT_METRICS], thresholdSampleCounts)); + if (_this._peerConnection) { + _this.enable(_this._peerConnection); + } + return _this; + } + /** + * Called when a volume sample is available + * @param inputVolume - Input volume level from 0 to 32767 + * @param outputVolume - Output volume level from 0 to 32767 + */ + StatsMonitor.prototype.addVolumes = function (inputVolume, outputVolume) { + this._inputVolumes.push(inputVolume); + this._outputVolumes.push(outputVolume); + }; + /** + * Stop sampling RTC statistics for this {@link StatsMonitor}. + * @returns The current {@link StatsMonitor}. + */ + StatsMonitor.prototype.disable = function () { + if (this._sampleInterval) { + clearInterval(this._sampleInterval); + delete this._sampleInterval; + } + return this; + }; + /** + * Disable warnings for this {@link StatsMonitor}. + * @returns The current {@link StatsMonitor}. + */ + StatsMonitor.prototype.disableWarnings = function () { + if (this._warningsEnabled) { + this._activeWarnings.clear(); + } + this._warningsEnabled = false; + return this; + }; + /** + * Start sampling RTC statistics for this {@link StatsMonitor}. + * @param peerConnection - A PeerConnection to monitor. + * @returns The current {@link StatsMonitor}. + */ + StatsMonitor.prototype.enable = function (peerConnection) { + if (peerConnection) { + if (this._peerConnection && peerConnection !== this._peerConnection) { + throw new errors_1.InvalidArgumentError('Attempted to replace an existing PeerConnection in StatsMonitor.enable'); + } + this._peerConnection = peerConnection; + } + if (!this._peerConnection) { + throw new errors_1.InvalidArgumentError('Can not enable StatsMonitor without a PeerConnection'); + } + this._sampleInterval = this._sampleInterval || + setInterval(this._fetchSample.bind(this), SAMPLE_INTERVAL); + return this; + }; + /** + * Enable warnings for this {@link StatsMonitor}. + * @returns The current {@link StatsMonitor}. + */ + StatsMonitor.prototype.enableWarnings = function () { + this._warningsEnabled = true; + return this; + }; + /** + * Check if there is an active warning for a specific stat and threshold + * @param statName - The name of the stat to check + * @param thresholdName - The name of the threshold to check + * @returns Whether there is an active warning for a specific stat and threshold + */ + StatsMonitor.prototype.hasActiveWarning = function (statName, thresholdName) { + var warningId = statName + ":" + thresholdName; + return !!this._activeWarnings.get(warningId); + }; + /** + * Add a sample to our sample buffer and remove the oldest if we are over the limit. + * @param sample - Sample to add + */ + StatsMonitor.prototype._addSample = function (sample) { + var samples = this._sampleBuffer; + samples.push(sample); + // We store 1 extra sample so that we always have (current, previous) + // available for all {sampleBufferSize} threshold validations. + if (samples.length > this._maxSampleCount) { + samples.splice(0, samples.length - this._maxSampleCount); + } + }; + /** + * Clear an active warning. + * @param statName - The name of the stat to clear. + * @param thresholdName - The name of the threshold to clear + * @param [data] - Any relevant sample data. + */ + StatsMonitor.prototype._clearWarning = function (statName, thresholdName, data) { + var warningId = statName + ":" + thresholdName; + var activeWarning = this._activeWarnings.get(warningId); + if (!activeWarning || Date.now() - activeWarning.timeRaised < WARNING_TIMEOUT) { + return; + } + this._activeWarnings.delete(warningId); + this.emit('warning-cleared', __assign(__assign({}, data), { name: statName, threshold: { + name: thresholdName, + value: this._thresholds[statName][thresholdName], + } })); + }; + /** + * Create a sample object from a stats object using the previous sample, if available. + * @param stats - Stats retrieved from getStatistics + * @param [previousSample=null] - The previous sample to use to calculate deltas. + * @returns A universally-formatted version of RTC stats. + */ + StatsMonitor.prototype._createSample = function (stats, previousSample) { + var previousBytesSent = previousSample && previousSample.totals.bytesSent || 0; + var previousBytesReceived = previousSample && previousSample.totals.bytesReceived || 0; + var previousPacketsSent = previousSample && previousSample.totals.packetsSent || 0; + var previousPacketsReceived = previousSample && previousSample.totals.packetsReceived || 0; + var previousPacketsLost = previousSample && previousSample.totals.packetsLost || 0; + var currentBytesSent = stats.bytesSent - previousBytesSent; + var currentBytesReceived = stats.bytesReceived - previousBytesReceived; + var currentPacketsSent = stats.packetsSent - previousPacketsSent; + var currentPacketsReceived = stats.packetsReceived - previousPacketsReceived; + var currentPacketsLost = stats.packetsLost - previousPacketsLost; + var currentInboundPackets = currentPacketsReceived + currentPacketsLost; + var currentPacketsLostFraction = (currentInboundPackets > 0) ? + (currentPacketsLost / currentInboundPackets) * 100 : 0; + var totalInboundPackets = stats.packetsReceived + stats.packetsLost; + var totalPacketsLostFraction = (totalInboundPackets > 0) ? + (stats.packetsLost / totalInboundPackets) * 100 : 100; + var rttValue = (typeof stats.rtt === 'number' || !previousSample) ? stats.rtt : previousSample.rtt; + var audioInputLevelValues = this._inputVolumes.splice(0); + this._supplementalSampleBuffers.audioInputLevel.push(audioInputLevelValues); + var audioOutputLevelValues = this._outputVolumes.splice(0); + this._supplementalSampleBuffers.audioOutputLevel.push(audioOutputLevelValues); + return { + audioInputLevel: Math.round(util_1.average(audioInputLevelValues)), + audioOutputLevel: Math.round(util_1.average(audioOutputLevelValues)), + bytesReceived: currentBytesReceived, + bytesSent: currentBytesSent, + codecName: stats.codecName, + jitter: stats.jitter, + mos: this._mos.calculate(rttValue, stats.jitter, previousSample && currentPacketsLostFraction), + packetsLost: currentPacketsLost, + packetsLostFraction: currentPacketsLostFraction, + packetsReceived: currentPacketsReceived, + packetsSent: currentPacketsSent, + rtt: rttValue, + timestamp: stats.timestamp, + totals: { + bytesReceived: stats.bytesReceived, + bytesSent: stats.bytesSent, + packetsLost: stats.packetsLost, + packetsLostFraction: totalPacketsLostFraction, + packetsReceived: stats.packetsReceived, + packetsSent: stats.packetsSent, + }, + }; + }; + /** + * Get stats from the PeerConnection and add it to our list of samples. + */ + StatsMonitor.prototype._fetchSample = function () { + var _this = this; + this._getSample().then(function (sample) { + _this._addSample(sample); + _this._raiseWarnings(); + _this.emit('sample', sample); + }).catch(function (error) { + _this.disable(); + // We only bubble up any errors coming from pc.getStats() + // No need to attach a twilioError + _this.emit('error', error); + }); + }; + /** + * Get stats from the PeerConnection. + * @returns A universally-formatted version of RTC stats. + */ + StatsMonitor.prototype._getSample = function () { + var _this = this; + return this._getRTCStats(this._peerConnection).then(function (stats) { + var previousSample = null; + if (_this._sampleBuffer.length) { + previousSample = _this._sampleBuffer[_this._sampleBuffer.length - 1]; + } + return _this._createSample(stats, previousSample); + }); + }; + /** + * Raise a warning and log its raised time. + * @param statName - The name of the stat to raise. + * @param thresholdName - The name of the threshold to raise + * @param [data] - Any relevant sample data. + */ + StatsMonitor.prototype._raiseWarning = function (statName, thresholdName, data) { + var warningId = statName + ":" + thresholdName; + if (this._activeWarnings.has(warningId)) { + return; + } + this._activeWarnings.set(warningId, { timeRaised: Date.now() }); + var thresholds = this._thresholds[statName]; + var thresholdValue; + if (Array.isArray(thresholds)) { + var foundThreshold = thresholds.find(function (threshold) { return thresholdName in threshold; }); + if (foundThreshold) { + thresholdValue = foundThreshold[thresholdName]; + } + } + else { + thresholdValue = this._thresholds[statName][thresholdName]; + } + this.emit('warning', __assign(__assign({}, data), { name: statName, threshold: { + name: thresholdName, + value: thresholdValue, + } })); + }; + /** + * Apply our thresholds to our array of RTCStat samples. + */ + StatsMonitor.prototype._raiseWarnings = function () { + var _this = this; + if (!this._warningsEnabled) { + return; + } + Object.keys(this._thresholds).forEach(function (name) { return _this._raiseWarningsForStat(name); }); + }; + /** + * Apply thresholds for a given stat name to our array of + * RTCStat samples and raise or clear any associated warnings. + * @param statName - Name of the stat to compare. + */ + StatsMonitor.prototype._raiseWarningsForStat = function (statName) { + var _this = this; + var limits = Array.isArray(this._thresholds[statName]) + ? this._thresholds[statName] + : [this._thresholds[statName]]; + limits.forEach(function (limit) { + var samples = _this._sampleBuffer; + var clearCount = limit.clearCount || SAMPLE_COUNT_CLEAR; + var raiseCount = limit.raiseCount || SAMPLE_COUNT_RAISE; + var sampleCount = limit.sampleCount || _this._maxSampleCount; + var relevantSamples = samples.slice(-sampleCount); + var values = relevantSamples.map(function (sample) { return sample[statName]; }); + // (rrowland) If we have a bad or missing value in the set, we don't + // have enough information to throw or clear a warning. Bail out. + var containsNull = values.some(function (value) { return typeof value === 'undefined' || value === null; }); + if (containsNull) { + return; + } + var count; + if (typeof limit.max === 'number') { + count = countHigh(limit.max, values); + if (count >= raiseCount) { + _this._raiseWarning(statName, 'max', { values: values, samples: relevantSamples }); + } + else if (count <= clearCount) { + _this._clearWarning(statName, 'max', { values: values, samples: relevantSamples }); + } + } + if (typeof limit.min === 'number') { + count = countLow(limit.min, values); + if (count >= raiseCount) { + _this._raiseWarning(statName, 'min', { values: values, samples: relevantSamples }); + } + else if (count <= clearCount) { + _this._clearWarning(statName, 'min', { values: values, samples: relevantSamples }); + } + } + if (typeof limit.maxDuration === 'number' && samples.length > 1) { + relevantSamples = samples.slice(-2); + var prevValue = relevantSamples[0][statName]; + var curValue = relevantSamples[1][statName]; + var prevStreak = _this._currentStreaks.get(statName) || 0; + var streak = (prevValue === curValue) ? prevStreak + 1 : 0; + _this._currentStreaks.set(statName, streak); + if (streak >= limit.maxDuration) { + _this._raiseWarning(statName, 'maxDuration', { value: streak }); + } + else if (streak === 0) { + _this._clearWarning(statName, 'maxDuration', { value: prevStreak }); + } + } + if (typeof limit.minStandardDeviation === 'number') { + var sampleSets = _this._supplementalSampleBuffers[statName]; + if (!sampleSets || sampleSets.length < limit.sampleCount) { + return; + } + if (sampleSets.length > limit.sampleCount) { + sampleSets.splice(0, sampleSets.length - limit.sampleCount); + } + var flatSamples = flattenSamples(sampleSets.slice(-sampleCount)); + var stdDev = calculateStandardDeviation(flatSamples); + if (typeof stdDev !== 'number') { + return; + } + if (stdDev < limit.minStandardDeviation) { + _this._raiseWarning(statName, 'minStandardDeviation', { value: stdDev }); + } + else { + _this._clearWarning(statName, 'minStandardDeviation', { value: stdDev }); + } + } + [ + ['maxAverage', function (x, y) { return x > y; }], + ['minAverage', function (x, y) { return x < y; }], + ].forEach(function (_a) { + var thresholdName = _a[0], comparator = _a[1]; + if (typeof limit[thresholdName] === 'number' && values.length >= sampleCount) { + var avg = util_1.average(values); + if (comparator(avg, limit[thresholdName])) { + _this._raiseWarning(statName, thresholdName, { values: values, samples: relevantSamples }); + } + else if (!comparator(avg, limit.clearValue || limit[thresholdName])) { + _this._clearWarning(statName, thresholdName, { values: values, samples: relevantSamples }); + } + } + }); + }); + }; + return StatsMonitor; +}(events_1.EventEmitter)); +exports.default = StatsMonitor; + +},{"./errors":15,"./rtc/mos":28,"./rtc/stats":32,"./util":36,"events":39}],36:[function(require,module,exports){ +(function (global){(function (){ +"use strict"; +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +// @ts-nocheck +Object.defineProperty(exports, "__esModule", { value: true }); +exports.flatMap = exports.queryToJson = exports.isUnifiedPlanDefault = exports.isSafari = exports.isLegacyEdge = exports.isFirefox = exports.isChrome = exports.isElectron = exports.difference = exports.average = exports.Exception = void 0; +/** + * Exception class. + * @class + * @name Exception + * @exports Exception as Twilio.Exception + * @memberOf Twilio + * @param {string} message The exception message + */ +function TwilioException(message) { + if (!(this instanceof TwilioException)) { + return new TwilioException(message); + } + this.message = message; +} +/** + * Returns the exception message. + * + * @return {string} The exception message. + */ +TwilioException.prototype.toString = function () { + return "Twilio.Exception: " + this.message; +}; +function average(values) { + return values && values.length ? values.reduce(function (t, v) { return t + v; }) / values.length : 0; +} +exports.average = average; +function difference(lefts, rights, getKey) { + getKey = getKey || (function (a) { return a; }); + var rightKeys = new Set(rights.map(getKey)); + return lefts.filter(function (left) { return !rightKeys.has(getKey(left)); }); +} +exports.difference = difference; +function isElectron(navigator) { + return !!navigator.userAgent.match('Electron'); +} +exports.isElectron = isElectron; +function isChrome(window, navigator) { + var isCriOS = !!navigator.userAgent.match('CriOS'); + var isHeadlessChrome = !!navigator.userAgent.match('HeadlessChrome'); + var isGoogle = typeof window.chrome !== 'undefined' + && navigator.vendor === 'Google Inc.' + && navigator.userAgent.indexOf('OPR') === -1 + && navigator.userAgent.indexOf('Edge') === -1; + return isCriOS || isElectron(navigator) || isGoogle || isHeadlessChrome; +} +exports.isChrome = isChrome; +function isFirefox(navigator) { + navigator = navigator || (typeof window === 'undefined' + ? global.navigator : window.navigator); + return !!(navigator) && typeof navigator.userAgent === 'string' + && /firefox|fxios/i.test(navigator.userAgent); +} +exports.isFirefox = isFirefox; +function isLegacyEdge(navigator) { + navigator = navigator || (typeof window === 'undefined' + ? global.navigator : window.navigator); + return !!(navigator) && typeof navigator.userAgent === 'string' + && /edge\/\d+/i.test(navigator.userAgent); +} +exports.isLegacyEdge = isLegacyEdge; +function isSafari(navigator) { + return !!(navigator.vendor) && navigator.vendor.indexOf('Apple') !== -1 + && navigator.userAgent + && navigator.userAgent.indexOf('CriOS') === -1 + && navigator.userAgent.indexOf('FxiOS') === -1; +} +exports.isSafari = isSafari; +function isUnifiedPlanDefault(window, navigator, PeerConnection, RtpTransceiver) { + if (typeof window === 'undefined' + || typeof navigator === 'undefined' + || typeof PeerConnection === 'undefined' + || typeof RtpTransceiver === 'undefined' + || typeof PeerConnection.prototype === 'undefined' + || typeof RtpTransceiver.prototype === 'undefined') { + return false; + } + if (isChrome(window, navigator) && PeerConnection.prototype.addTransceiver) { + var pc = new PeerConnection(); + var isUnifiedPlan = true; + try { + pc.addTransceiver('audio'); + } + catch (e) { + isUnifiedPlan = false; + } + pc.close(); + return isUnifiedPlan; + } + else if (isFirefox(navigator)) { + return true; + } + else if (isSafari(navigator)) { + return 'currentDirection' in RtpTransceiver.prototype; + } + // Edge currently does not support unified plan. + // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/17733189/ + // https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/34451998-sdp-unified-plan + return false; +} +exports.isUnifiedPlanDefault = isUnifiedPlanDefault; +function queryToJson(params) { + if (!params) { + return ''; + } + return params.split('&').reduce(function (output, pair) { + var parts = pair.split('='); + var key = parts[0]; + var value = decodeURIComponent((parts[1] || '').replace(/\+/g, '%20')); + if (key) { + output[key] = value; + } + return output; + }, {}); +} +exports.queryToJson = queryToJson; +/** + * Map a list to an array of arrays, and return the flattened result. + * @param {Array<*>|Set<*>|Map<*>} list + * @param {function(*): Array<*>} [mapFn] + * @returns Array<*> + */ +function flatMap(list, mapFn) { + var listArray = list instanceof Map || list instanceof Set + ? Array.from(list.values()) + : list; + mapFn = mapFn || (function (item) { return item; }); + return listArray.reduce(function (flattened, item) { + var mapped = mapFn(item); + return flattened.concat(mapped); + }, []); +} +exports.flatMap = flatMap; +var Exception = TwilioException; +exports.Exception = Exception; + +}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],37:[function(require,module,exports){ +"use strict"; +/** + * @packageDocumentation + * @module Voice + * @internalapi + */ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.generateVoiceEventSid = void 0; +var md5Lib = require("md5"); +var errors_1 = require("../twilio/errors"); +// If imported as an ESM module, sometimes md5 is available by accessing +// the "default" property of the imported library. +// @ts-ignore +var md5 = typeof md5Lib === 'function' ? md5Lib : md5Lib.default; +function generateUuid() { + if (typeof window !== 'object') { + throw new errors_1.NotSupportedError('This platform is not supported.'); + } + var crypto = window.crypto; + if (typeof crypto !== 'object') { + throw new errors_1.NotSupportedError('The `crypto` module is not available on this platform.'); + } + if (typeof (crypto.randomUUID || crypto.getRandomValues) === 'undefined') { + throw new errors_1.NotSupportedError('Neither `crypto.randomUUID` or `crypto.getRandomValues` are available ' + + 'on this platform.'); + } + var uInt32Arr = window.Uint32Array; + if (typeof uInt32Arr === 'undefined') { + throw new errors_1.NotSupportedError('The `Uint32Array` module is not available on this platform.'); + } + var generateRandomValues = typeof crypto.randomUUID === 'function' + ? function () { return crypto.randomUUID(); } + : function () { return crypto.getRandomValues(new Uint32Array(32)).toString(); }; + return md5(generateRandomValues()); +} +function generateVoiceEventSid() { + return "KX" + generateUuid(); +} +exports.generateVoiceEventSid = generateVoiceEventSid; + +},{"../twilio/errors":15,"md5":44}],38:[function(require,module,exports){ +"use strict"; +/** + * @packageDocumentation + * @module Tools + * @internalapi + */ +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.WSTransportState = void 0; +var events_1 = require("events"); +var backoff_1 = require("./backoff"); +var errors_1 = require("./errors"); +var log_1 = require("./log"); +var WebSocket = globalThis.WebSocket; +var CONNECT_SUCCESS_TIMEOUT = 10000; +var CONNECT_TIMEOUT = 5000; +var HEARTBEAT_TIMEOUT = 15000; +var MAX_PREFERRED_DURATION = 15000; +var MAX_PRIMARY_DURATION = Infinity; +var MAX_PREFERRED_DELAY = 1000; +var MAX_PRIMARY_DELAY = 20000; +/** + * All possible states of WSTransport. + */ +var WSTransportState; +(function (WSTransportState) { + /** + * The WebSocket is not open but is trying to connect. + */ + WSTransportState["Connecting"] = "connecting"; + /** + * The WebSocket is not open and is not trying to connect. + */ + WSTransportState["Closed"] = "closed"; + /** + * The underlying WebSocket is open and active. + */ + WSTransportState["Open"] = "open"; +})(WSTransportState = exports.WSTransportState || (exports.WSTransportState = {})); +/** + * WebSocket Transport + */ +var WSTransport = /** @class */ (function (_super) { + __extends(WSTransport, _super); + /** + * @constructor + * @param uris - List of URI of the endpoints to connect to. + * @param [options] - Constructor options. + */ + function WSTransport(uris, options) { + if (options === void 0) { options = {}; } + var _this = _super.call(this) || this; + /** + * The current state of the WSTransport. + */ + _this.state = WSTransportState.Closed; + /** + * Start timestamp values for backoffs. + */ + _this._backoffStartTime = { + preferred: null, + primary: null, + }; + /** + * The URI that the transport is connecting or connected to. The value of this + * property is `null` if a connection attempt has not been made yet. + */ + _this._connectedUri = null; + /** + * An instance of Logger to use. + */ + _this._log = new log_1.default('WSTransport'); + /** + * Whether we should attempt to fallback if we receive an applicable error + * when trying to connect to a signaling endpoint. + */ + _this._shouldFallback = false; + /** + * The current uri index that the transport is connected to. + */ + _this._uriIndex = 0; + /** + * Move the uri index to the next index + * If the index is at the end, the index goes back to the first one. + */ + _this._moveUriIndex = function () { + _this._uriIndex++; + if (_this._uriIndex >= _this._uris.length) { + _this._uriIndex = 0; + } + }; + /** + * Called in response to WebSocket#close event. + */ + _this._onSocketClose = function (event) { + _this._log.error("Received websocket close event code: " + event.code + ". Reason: " + event.reason); + // 1006: Abnormal close. When the server is unreacheable + // 1015: TLS Handshake error + if (event.code === 1006 || event.code === 1015) { + _this.emit('error', { + code: 31005, + message: event.reason || + 'Websocket connection to Twilio\'s signaling servers were ' + + 'unexpectedly ended. If this is happening consistently, there may ' + + 'be an issue resolving the hostname provided. If a region or an ' + + 'edge is being specified in Device setup, ensure it is valid.', + twilioError: new errors_1.SignalingErrors.ConnectionError(), + }); + var wasConnected = ( + // Only in Safari and certain Firefox versions, on network interruption, websocket drops right away with 1006 + // Let's check current state if it's open, meaning we should not fallback + // because we're coming from a previously connected session + _this.state === WSTransportState.Open || + // But on other browsers, websocket doesn't drop + // but our heartbeat catches it, setting the internal state to "Connecting". + // With this, we should check the previous state instead. + _this._previousState === WSTransportState.Open); + // Only fallback if this is not the first error + // and if we were not connected previously + if (_this._shouldFallback || !wasConnected) { + _this._moveUriIndex(); + } + _this._shouldFallback = true; + } + _this._closeSocket(); + }; + /** + * Called in response to WebSocket#error event. + */ + _this._onSocketError = function (err) { + _this._log.error("WebSocket received error: " + err.message); + _this.emit('error', { + code: 31000, + message: err.message || 'WSTransport socket error', + twilioError: new errors_1.SignalingErrors.ConnectionDisconnected(), + }); + }; + /** + * Called in response to WebSocket#message event. + */ + _this._onSocketMessage = function (message) { + // Clear heartbeat timeout on any incoming message, as they + // all indicate an active connection. + _this._setHeartbeatTimeout(); + // Filter and respond to heartbeats + if (_this._socket && message.data === '\n') { + _this._socket.send('\n'); + return; + } + _this.emit('message', message); + }; + /** + * Called in response to WebSocket#open event. + */ + _this._onSocketOpen = function () { + _this._log.info('WebSocket opened successfully.'); + _this._timeOpened = Date.now(); + _this._shouldFallback = false; + _this._setState(WSTransportState.Open); + clearTimeout(_this._connectTimeout); + _this._resetBackoffs(); + _this._setHeartbeatTimeout(); + _this.emit('open'); + }; + _this._options = __assign(__assign({}, WSTransport.defaultConstructorOptions), options); + _this._uris = uris; + _this._backoff = _this._setupBackoffs(); + return _this; + } + /** + * Close the WebSocket, and don't try to reconnect. + */ + WSTransport.prototype.close = function () { + this._log.info('WSTransport.close() called...'); + this._close(); + }; + /** + * Attempt to open a WebSocket connection. + */ + WSTransport.prototype.open = function () { + this._log.info('WSTransport.open() called...'); + if (this._socket && + (this._socket.readyState === WebSocket.CONNECTING || + this._socket.readyState === WebSocket.OPEN)) { + this._log.info('WebSocket already open.'); + return; + } + if (this._preferredUri) { + this._connect(this._preferredUri); + } + else { + this._connect(this._uris[this._uriIndex]); + } + }; + /** + * Send a message through the WebSocket connection. + * @param message - A message to send to the endpoint. + * @returns Whether the message was sent. + */ + WSTransport.prototype.send = function (message) { + // We can't send the message if the WebSocket isn't open + if (!this._socket || this._socket.readyState !== WebSocket.OPEN) { + return false; + } + try { + this._socket.send(message); + } + catch (e) { + // Some unknown error occurred. Reset the socket to get a fresh session. + this._log.error('Error while sending message:', e.message); + this._closeSocket(); + return false; + } + return true; + }; + /** + * Update the preferred URI to connect to. Useful for Call signaling + * reconnection, which requires connecting on the same edge. If `null` is + * passed, the preferred URI is unset and the original `uris` array and + * `uriIndex` is used to determine the signaling URI to connect to. + * @param uri + */ + WSTransport.prototype.updatePreferredURI = function (uri) { + this._preferredUri = uri; + }; + /** + * Update acceptable URIs to reconnect to. Resets the URI index to 0. + */ + WSTransport.prototype.updateURIs = function (uris) { + if (typeof uris === 'string') { + uris = [uris]; + } + this._uris = uris; + this._uriIndex = 0; + }; + /** + * Close the WebSocket, and don't try to reconnect. + */ + WSTransport.prototype._close = function () { + this._setState(WSTransportState.Closed); + this._closeSocket(); + }; + /** + * Close the WebSocket and remove all event listeners. + */ + WSTransport.prototype._closeSocket = function () { + clearTimeout(this._connectTimeout); + clearTimeout(this._heartbeatTimeout); + this._log.info('Closing and cleaning up WebSocket...'); + if (!this._socket) { + this._log.info('No WebSocket to clean up.'); + return; + } + this._socket.removeEventListener('close', this._onSocketClose); + this._socket.removeEventListener('error', this._onSocketError); + this._socket.removeEventListener('message', this._onSocketMessage); + this._socket.removeEventListener('open', this._onSocketOpen); + if (this._socket.readyState === WebSocket.CONNECTING || + this._socket.readyState === WebSocket.OPEN) { + this._socket.close(); + } + // Reset backoff counter if connection was open for long enough to be considered successful + if (this._timeOpened && Date.now() - this._timeOpened > CONNECT_SUCCESS_TIMEOUT) { + this._resetBackoffs(); + } + if (this.state !== WSTransportState.Closed) { + this._performBackoff(); + } + delete this._socket; + this.emit('close'); + }; + /** + * Attempt to connect to the endpoint via WebSocket. + * @param [uri] - URI string to connect to. + * @param [retryCount] - Retry number, if this is a retry. Undefined if + * first attempt, 1+ if a retry. + */ + WSTransport.prototype._connect = function (uri, retryCount) { + var _this = this; + this._log.info(typeof retryCount === 'number' + ? "Attempting to reconnect (retry #" + retryCount + ")..." + : 'Attempting to connect...'); + this._closeSocket(); + this._setState(WSTransportState.Connecting); + this._connectedUri = uri; + try { + this._socket = new this._options.WebSocket(this._connectedUri); + } + catch (e) { + this._log.error('Could not connect to endpoint:', e.message); + this._close(); + this.emit('error', { + code: 31000, + message: e.message || "Could not connect to " + this._connectedUri, + twilioError: new errors_1.SignalingErrors.ConnectionDisconnected(), + }); + return; + } + this._socket.addEventListener('close', this._onSocketClose); + this._socket.addEventListener('error', this._onSocketError); + this._socket.addEventListener('message', this._onSocketMessage); + this._socket.addEventListener('open', this._onSocketOpen); + delete this._timeOpened; + this._connectTimeout = setTimeout(function () { + _this._log.info('WebSocket connection attempt timed out.'); + _this._moveUriIndex(); + _this._closeSocket(); + }, this._options.connectTimeoutMs); + }; + /** + * Perform a backoff. If a preferred URI is set (not null), then backoff + * using the preferred mechanism. Otherwise, use the primary mechanism. + */ + WSTransport.prototype._performBackoff = function () { + if (this._preferredUri) { + this._log.info('Preferred URI set; backing off.'); + this._backoff.preferred.backoff(); + } + else { + this._log.info('Preferred URI not set; backing off.'); + this._backoff.primary.backoff(); + } + }; + /** + * Reset both primary and preferred backoff mechanisms. + */ + WSTransport.prototype._resetBackoffs = function () { + this._backoff.preferred.reset(); + this._backoff.primary.reset(); + this._backoffStartTime.preferred = null; + this._backoffStartTime.primary = null; + }; + /** + * Set a timeout to reconnect after HEARTBEAT_TIMEOUT milliseconds + * have passed without receiving a message over the WebSocket. + */ + WSTransport.prototype._setHeartbeatTimeout = function () { + var _this = this; + clearTimeout(this._heartbeatTimeout); + this._heartbeatTimeout = setTimeout(function () { + _this._log.info("No messages received in " + HEARTBEAT_TIMEOUT / 1000 + " seconds. Reconnecting..."); + _this._shouldFallback = true; + _this._closeSocket(); + }, HEARTBEAT_TIMEOUT); + }; + /** + * Set the current and previous state + */ + WSTransport.prototype._setState = function (state) { + this._previousState = this.state; + this.state = state; + }; + /** + * Set up the primary and preferred backoff mechanisms. + */ + WSTransport.prototype._setupBackoffs = function () { + var _this = this; + var preferredBackoffConfig = { + factor: 2.0, + jitter: 0.40, + max: this._options.maxPreferredDelayMs, + min: 100, + }; + this._log.info('Initializing preferred transport backoff using config: ', preferredBackoffConfig); + var preferredBackoff = new backoff_1.default(preferredBackoffConfig); + preferredBackoff.on('backoff', function (attempt, delay) { + if (_this.state === WSTransportState.Closed) { + _this._log.info('Preferred backoff initiated but transport state is closed; not attempting a connection.'); + return; + } + _this._log.info("Will attempt to reconnect Websocket to preferred URI in " + delay + "ms"); + if (attempt === 0) { + _this._backoffStartTime.preferred = Date.now(); + _this._log.info("Preferred backoff start; " + _this._backoffStartTime.preferred); + } + }); + preferredBackoff.on('ready', function (attempt, _delay) { + if (_this.state === WSTransportState.Closed) { + _this._log.info('Preferred backoff ready but transport state is closed; not attempting a connection.'); + return; + } + if (_this._backoffStartTime.preferred === null) { + _this._log.info('Preferred backoff start time invalid; not attempting a connection.'); + return; + } + if (Date.now() - _this._backoffStartTime.preferred > _this._options.maxPreferredDurationMs) { + _this._log.info('Max preferred backoff attempt time exceeded; falling back to primary backoff.'); + _this._preferredUri = null; + _this._backoff.primary.backoff(); + return; + } + if (typeof _this._preferredUri !== 'string') { + _this._log.info('Preferred URI cleared; falling back to primary backoff.'); + _this._preferredUri = null; + _this._backoff.primary.backoff(); + return; + } + _this._connect(_this._preferredUri, attempt + 1); + }); + var primaryBackoffConfig = { + factor: 2.0, + jitter: 0.40, + max: this._options.maxPrimaryDelayMs, + // We only want a random initial delay if there are any fallback edges + // Initial delay between 1s and 5s both inclusive + min: this._uris && this._uris.length > 1 + ? Math.floor(Math.random() * (5000 - 1000 + 1)) + 1000 + : 100, + }; + this._log.info('Initializing primary transport backoff using config: ', primaryBackoffConfig); + var primaryBackoff = new backoff_1.default(primaryBackoffConfig); + primaryBackoff.on('backoff', function (attempt, delay) { + if (_this.state === WSTransportState.Closed) { + _this._log.info('Primary backoff initiated but transport state is closed; not attempting a connection.'); + return; + } + _this._log.info("Will attempt to reconnect WebSocket in " + delay + "ms"); + if (attempt === 0) { + _this._backoffStartTime.primary = Date.now(); + _this._log.info("Primary backoff start; " + _this._backoffStartTime.primary); + } + }); + primaryBackoff.on('ready', function (attempt, _delay) { + if (_this.state === WSTransportState.Closed) { + _this._log.info('Primary backoff ready but transport state is closed; not attempting a connection.'); + return; + } + if (_this._backoffStartTime.primary === null) { + _this._log.info('Primary backoff start time invalid; not attempting a connection.'); + return; + } + if (Date.now() - _this._backoffStartTime.primary > _this._options.maxPrimaryDurationMs) { + _this._log.info('Max primary backoff attempt time exceeded; not attempting a connection.'); + return; + } + _this._connect(_this._uris[_this._uriIndex], attempt + 1); + }); + return { + preferred: preferredBackoff, + primary: primaryBackoff, + }; + }; + Object.defineProperty(WSTransport.prototype, "uri", { + /** + * The uri the transport is currently connected to + */ + get: function () { + return this._connectedUri; + }, + enumerable: false, + configurable: true + }); + WSTransport.defaultConstructorOptions = { + WebSocket: WebSocket, + connectTimeoutMs: CONNECT_TIMEOUT, + maxPreferredDelayMs: MAX_PREFERRED_DELAY, + maxPreferredDurationMs: MAX_PREFERRED_DURATION, + maxPrimaryDelayMs: MAX_PRIMARY_DELAY, + maxPrimaryDurationMs: MAX_PRIMARY_DURATION, + }; + return WSTransport; +}(events_1.EventEmitter)); +exports.default = WSTransport; + +},{"./backoff":8,"./errors":15,"./log":18,"events":39}],39:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var objectCreate = Object.create || objectCreatePolyfill +var objectKeys = Object.keys || objectKeysPolyfill +var bind = Function.prototype.bind || functionBindPolyfill + +function EventEmitter() { + if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) { + this._events = objectCreate(null); + this._eventsCount = 0; + } + + this._maxListeners = this._maxListeners || undefined; +} +module.exports = EventEmitter; + +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; + +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._maxListeners = undefined; + +// By default EventEmitters will print a warning if more than 10 listeners are +// added to it. This is a useful default which helps finding memory leaks. +var defaultMaxListeners = 10; + +var hasDefineProperty; +try { + var o = {}; + if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 }); + hasDefineProperty = o.x === 0; +} catch (err) { hasDefineProperty = false } +if (hasDefineProperty) { + Object.defineProperty(EventEmitter, 'defaultMaxListeners', { + enumerable: true, + get: function() { + return defaultMaxListeners; + }, + set: function(arg) { + // check whether the input is a positive number (whose value is zero or + // greater and not a NaN). + if (typeof arg !== 'number' || arg < 0 || arg !== arg) + throw new TypeError('"defaultMaxListeners" must be a positive number'); + defaultMaxListeners = arg; + } + }); +} else { + EventEmitter.defaultMaxListeners = defaultMaxListeners; +} + +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { + if (typeof n !== 'number' || n < 0 || isNaN(n)) + throw new TypeError('"n" argument must be a positive number'); + this._maxListeners = n; + return this; +}; + +function $getMaxListeners(that) { + if (that._maxListeners === undefined) + return EventEmitter.defaultMaxListeners; + return that._maxListeners; +} + +EventEmitter.prototype.getMaxListeners = function getMaxListeners() { + return $getMaxListeners(this); +}; + +// These standalone emit* functions are used to optimize calling of event +// handlers for fast cases because emit() itself often has a variable number of +// arguments and can be deoptimized because of that. These functions always have +// the same number of arguments and thus do not get deoptimized, so the code +// inside them can execute faster. +function emitNone(handler, isFn, self) { + if (isFn) + handler.call(self); + else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + listeners[i].call(self); + } +} +function emitOne(handler, isFn, self, arg1) { + if (isFn) + handler.call(self, arg1); + else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + listeners[i].call(self, arg1); + } +} +function emitTwo(handler, isFn, self, arg1, arg2) { + if (isFn) + handler.call(self, arg1, arg2); + else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + listeners[i].call(self, arg1, arg2); + } +} +function emitThree(handler, isFn, self, arg1, arg2, arg3) { + if (isFn) + handler.call(self, arg1, arg2, arg3); + else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + listeners[i].call(self, arg1, arg2, arg3); + } +} + +function emitMany(handler, isFn, self, args) { + if (isFn) + handler.apply(self, args); + else { + var len = handler.length; + var listeners = arrayClone(handler, len); + for (var i = 0; i < len; ++i) + listeners[i].apply(self, args); + } +} + +EventEmitter.prototype.emit = function emit(type) { + var er, handler, len, args, i, events; + var doError = (type === 'error'); + + events = this._events; + if (events) + doError = (doError && events.error == null); + else if (!doError) + return false; + + // If there is no 'error' event listener then throw. + if (doError) { + if (arguments.length > 1) + er = arguments[1]; + if (er instanceof Error) { + throw er; // Unhandled 'error' event + } else { + // At least give some kind of context to the user + var err = new Error('Unhandled "error" event. (' + er + ')'); + err.context = er; + throw err; + } + return false; + } + + handler = events[type]; + + if (!handler) + return false; + + var isFn = typeof handler === 'function'; + len = arguments.length; + switch (len) { + // fast cases + case 1: + emitNone(handler, isFn, this); + break; + case 2: + emitOne(handler, isFn, this, arguments[1]); + break; + case 3: + emitTwo(handler, isFn, this, arguments[1], arguments[2]); + break; + case 4: + emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]); + break; + // slower + default: + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + emitMany(handler, isFn, this, args); + } + + return true; +}; + +function _addListener(target, type, listener, prepend) { + var m; + var events; + var existing; + + if (typeof listener !== 'function') + throw new TypeError('"listener" argument must be a function'); + + events = target._events; + if (!events) { + events = target._events = objectCreate(null); + target._eventsCount = 0; + } else { + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (events.newListener) { + target.emit('newListener', type, + listener.listener ? listener.listener : listener); + + // Re-assign `events` because a newListener handler could have caused the + // this._events to be assigned to a new object + events = target._events; + } + existing = events[type]; + } + + if (!existing) { + // Optimize the case of one listener. Don't need the extra array object. + existing = events[type] = listener; + ++target._eventsCount; + } else { + if (typeof existing === 'function') { + // Adding the second element, need to change to array. + existing = events[type] = + prepend ? [listener, existing] : [existing, listener]; + } else { + // If we've already got an array, just append. + if (prepend) { + existing.unshift(listener); + } else { + existing.push(listener); + } + } + + // Check for listener leak + if (!existing.warned) { + m = $getMaxListeners(target); + if (m && m > 0 && existing.length > m) { + existing.warned = true; + var w = new Error('Possible EventEmitter memory leak detected. ' + + existing.length + ' "' + String(type) + '" listeners ' + + 'added. Use emitter.setMaxListeners() to ' + + 'increase limit.'); + w.name = 'MaxListenersExceededWarning'; + w.emitter = target; + w.type = type; + w.count = existing.length; + if (typeof console === 'object' && console.warn) { + console.warn('%s: %s', w.name, w.message); + } + } + } + } + + return target; +} + +EventEmitter.prototype.addListener = function addListener(type, listener) { + return _addListener(this, type, listener, false); +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.prependListener = + function prependListener(type, listener) { + return _addListener(this, type, listener, true); + }; + +function onceWrapper() { + if (!this.fired) { + this.target.removeListener(this.type, this.wrapFn); + this.fired = true; + switch (arguments.length) { + case 0: + return this.listener.call(this.target); + case 1: + return this.listener.call(this.target, arguments[0]); + case 2: + return this.listener.call(this.target, arguments[0], arguments[1]); + case 3: + return this.listener.call(this.target, arguments[0], arguments[1], + arguments[2]); + default: + var args = new Array(arguments.length); + for (var i = 0; i < args.length; ++i) + args[i] = arguments[i]; + this.listener.apply(this.target, args); + } + } +} + +function _onceWrap(target, type, listener) { + var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener }; + var wrapped = bind.call(onceWrapper, state); + wrapped.listener = listener; + state.wrapFn = wrapped; + return wrapped; +} + +EventEmitter.prototype.once = function once(type, listener) { + if (typeof listener !== 'function') + throw new TypeError('"listener" argument must be a function'); + this.on(type, _onceWrap(this, type, listener)); + return this; +}; + +EventEmitter.prototype.prependOnceListener = + function prependOnceListener(type, listener) { + if (typeof listener !== 'function') + throw new TypeError('"listener" argument must be a function'); + this.prependListener(type, _onceWrap(this, type, listener)); + return this; + }; + +// Emits a 'removeListener' event if and only if the listener was removed. +EventEmitter.prototype.removeListener = + function removeListener(type, listener) { + var list, events, position, i, originalListener; + + if (typeof listener !== 'function') + throw new TypeError('"listener" argument must be a function'); + + events = this._events; + if (!events) + return this; + + list = events[type]; + if (!list) + return this; + + if (list === listener || list.listener === listener) { + if (--this._eventsCount === 0) + this._events = objectCreate(null); + else { + delete events[type]; + if (events.removeListener) + this.emit('removeListener', type, list.listener || listener); + } + } else if (typeof list !== 'function') { + position = -1; + + for (i = list.length - 1; i >= 0; i--) { + if (list[i] === listener || list[i].listener === listener) { + originalListener = list[i].listener; + position = i; + break; + } + } + + if (position < 0) + return this; + + if (position === 0) + list.shift(); + else + spliceOne(list, position); + + if (list.length === 1) + events[type] = list[0]; + + if (events.removeListener) + this.emit('removeListener', type, originalListener || listener); + } + + return this; + }; + +EventEmitter.prototype.removeAllListeners = + function removeAllListeners(type) { + var listeners, events, i; + + events = this._events; + if (!events) + return this; + + // not listening for removeListener, no need to emit + if (!events.removeListener) { + if (arguments.length === 0) { + this._events = objectCreate(null); + this._eventsCount = 0; + } else if (events[type]) { + if (--this._eventsCount === 0) + this._events = objectCreate(null); + else + delete events[type]; + } + return this; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + var keys = objectKeys(events); + var key; + for (i = 0; i < keys.length; ++i) { + key = keys[i]; + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = objectCreate(null); + this._eventsCount = 0; + return this; + } + + listeners = events[type]; + + if (typeof listeners === 'function') { + this.removeListener(type, listeners); + } else if (listeners) { + // LIFO order + for (i = listeners.length - 1; i >= 0; i--) { + this.removeListener(type, listeners[i]); + } + } + + return this; + }; + +function _listeners(target, type, unwrap) { + var events = target._events; + + if (!events) + return []; + + var evlistener = events[type]; + if (!evlistener) + return []; + + if (typeof evlistener === 'function') + return unwrap ? [evlistener.listener || evlistener] : [evlistener]; + + return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length); +} + +EventEmitter.prototype.listeners = function listeners(type) { + return _listeners(this, type, true); +}; + +EventEmitter.prototype.rawListeners = function rawListeners(type) { + return _listeners(this, type, false); +}; + +EventEmitter.listenerCount = function(emitter, type) { + if (typeof emitter.listenerCount === 'function') { + return emitter.listenerCount(type); + } else { + return listenerCount.call(emitter, type); + } +}; + +EventEmitter.prototype.listenerCount = listenerCount; +function listenerCount(type) { + var events = this._events; + + if (events) { + var evlistener = events[type]; + + if (typeof evlistener === 'function') { + return 1; + } else if (evlistener) { + return evlistener.length; + } + } + + return 0; +} + +EventEmitter.prototype.eventNames = function eventNames() { + return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : []; +}; + +// About 1.5x faster than the two-arg version of Array#splice(). +function spliceOne(list, index) { + for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) + list[i] = list[k]; + list.pop(); +} + +function arrayClone(arr, n) { + var copy = new Array(n); + for (var i = 0; i < n; ++i) + copy[i] = arr[i]; + return copy; +} + +function unwrapListeners(arr) { + var ret = new Array(arr.length); + for (var i = 0; i < ret.length; ++i) { + ret[i] = arr[i].listener || arr[i]; + } + return ret; +} + +function objectCreatePolyfill(proto) { + var F = function() {}; + F.prototype = proto; + return new F; +} +function objectKeysPolyfill(obj) { + var keys = []; + for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) { + keys.push(k); + } + return k; +} +function functionBindPolyfill(context) { + var fn = this; + return function () { + return fn.apply(context, arguments); + }; +} + +},{}],40:[function(require,module,exports){ +var charenc = { + // UTF-8 encoding + utf8: { + // Convert a string to a byte array + stringToBytes: function(str) { + return charenc.bin.stringToBytes(unescape(encodeURIComponent(str))); + }, + + // Convert a byte array to a string + bytesToString: function(bytes) { + return decodeURIComponent(escape(charenc.bin.bytesToString(bytes))); + } + }, + + // Binary encoding + bin: { + // Convert a string to a byte array + stringToBytes: function(str) { + for (var bytes = [], i = 0; i < str.length; i++) + bytes.push(str.charCodeAt(i) & 0xFF); + return bytes; + }, + + // Convert a byte array to a string + bytesToString: function(bytes) { + for (var str = [], i = 0; i < bytes.length; i++) + str.push(String.fromCharCode(bytes[i])); + return str.join(''); + } + } +}; + +module.exports = charenc; + +},{}],41:[function(require,module,exports){ +(function() { + var base64map + = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', + + crypt = { + // Bit-wise rotation left + rotl: function(n, b) { + return (n << b) | (n >>> (32 - b)); + }, + + // Bit-wise rotation right + rotr: function(n, b) { + return (n << (32 - b)) | (n >>> b); + }, + + // Swap big-endian to little-endian and vice versa + endian: function(n) { + // If number given, swap endian + if (n.constructor == Number) { + return crypt.rotl(n, 8) & 0x00FF00FF | crypt.rotl(n, 24) & 0xFF00FF00; + } + + // Else, assume array and swap all items + for (var i = 0; i < n.length; i++) + n[i] = crypt.endian(n[i]); + return n; + }, + + // Generate an array of any length of random bytes + randomBytes: function(n) { + for (var bytes = []; n > 0; n--) + bytes.push(Math.floor(Math.random() * 256)); + return bytes; + }, + + // Convert a byte array to big-endian 32-bit words + bytesToWords: function(bytes) { + for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8) + words[b >>> 5] |= bytes[i] << (24 - b % 32); + return words; + }, + + // Convert big-endian 32-bit words to a byte array + wordsToBytes: function(words) { + for (var bytes = [], b = 0; b < words.length * 32; b += 8) + bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); + return bytes; + }, + + // Convert a byte array to a hex string + bytesToHex: function(bytes) { + for (var hex = [], i = 0; i < bytes.length; i++) { + hex.push((bytes[i] >>> 4).toString(16)); + hex.push((bytes[i] & 0xF).toString(16)); + } + return hex.join(''); + }, + + // Convert a hex string to a byte array + hexToBytes: function(hex) { + for (var bytes = [], c = 0; c < hex.length; c += 2) + bytes.push(parseInt(hex.substr(c, 2), 16)); + return bytes; + }, + + // Convert a byte array to a base-64 string + bytesToBase64: function(bytes) { + for (var base64 = [], i = 0; i < bytes.length; i += 3) { + var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; + for (var j = 0; j < 4; j++) + if (i * 8 + j * 6 <= bytes.length * 8) + base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F)); + else + base64.push('='); + } + return base64.join(''); + }, + + // Convert a base-64 string to a byte array + base64ToBytes: function(base64) { + // Remove non-base-64 characters + base64 = base64.replace(/[^A-Z0-9+\/]/ig, ''); + + for (var bytes = [], i = 0, imod4 = 0; i < base64.length; + imod4 = ++i % 4) { + if (imod4 == 0) continue; + bytes.push(((base64map.indexOf(base64.charAt(i - 1)) + & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2)) + | (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2))); + } + return bytes; + } + }; + + module.exports = crypt; +})(); + +},{}],42:[function(require,module,exports){ +/*! + * Determine if an object is a Buffer + * + * @author Feross Aboukhadijeh + * @license MIT + */ + +// The _isBuffer check is for Safari 5-7 support, because it's missing +// Object.prototype.constructor. Remove this eventually +module.exports = function (obj) { + return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer) +} + +function isBuffer (obj) { + return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) +} + +// For Node v0.10 support. Remove this eventually. +function isSlowBuffer (obj) { + return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) +} + +},{}],43:[function(require,module,exports){ +/* +* loglevel - https://github.com/pimterry/loglevel +* +* Copyright (c) 2013 Tim Perry +* Licensed under the MIT license. +*/ +(function (root, definition) { + "use strict"; + if (typeof define === 'function' && define.amd) { + define(definition); + } else if (typeof module === 'object' && module.exports) { + module.exports = definition(); + } else { + root.log = definition(); + } +}(this, function () { + "use strict"; + + // Slightly dubious tricks to cut down minimized file size + var noop = function() {}; + var undefinedType = "undefined"; + var isIE = (typeof window !== undefinedType) && (typeof window.navigator !== undefinedType) && ( + /Trident\/|MSIE /.test(window.navigator.userAgent) + ); + + var logMethods = [ + "trace", + "debug", + "info", + "warn", + "error" + ]; + + // Cross-browser bind equivalent that works at least back to IE6 + function bindMethod(obj, methodName) { + var method = obj[methodName]; + if (typeof method.bind === 'function') { + return method.bind(obj); + } else { + try { + return Function.prototype.bind.call(method, obj); + } catch (e) { + // Missing bind shim or IE8 + Modernizr, fallback to wrapping + return function() { + return Function.prototype.apply.apply(method, [obj, arguments]); + }; + } + } + } + + // Trace() doesn't print the message in IE, so for that case we need to wrap it + function traceForIE() { + if (console.log) { + if (console.log.apply) { + console.log.apply(console, arguments); + } else { + // In old IE, native console methods themselves don't have apply(). + Function.prototype.apply.apply(console.log, [console, arguments]); + } + } + if (console.trace) console.trace(); + } + + // Build the best logging method possible for this env + // Wherever possible we want to bind, not wrap, to preserve stack traces + function realMethod(methodName) { + if (methodName === 'debug') { + methodName = 'log'; + } + + if (typeof console === undefinedType) { + return false; // No method possible, for now - fixed later by enableLoggingWhenConsoleArrives + } else if (methodName === 'trace' && isIE) { + return traceForIE; + } else if (console[methodName] !== undefined) { + return bindMethod(console, methodName); + } else if (console.log !== undefined) { + return bindMethod(console, 'log'); + } else { + return noop; + } + } + + // These private functions always need `this` to be set properly + + function replaceLoggingMethods(level, loggerName) { + /*jshint validthis:true */ + for (var i = 0; i < logMethods.length; i++) { + var methodName = logMethods[i]; + this[methodName] = (i < level) ? + noop : + this.methodFactory(methodName, level, loggerName); + } + + // Define log.log as an alias for log.debug + this.log = this.debug; + } + + // In old IE versions, the console isn't present until you first open it. + // We build realMethod() replacements here that regenerate logging methods + function enableLoggingWhenConsoleArrives(methodName, level, loggerName) { + return function () { + if (typeof console !== undefinedType) { + replaceLoggingMethods.call(this, level, loggerName); + this[methodName].apply(this, arguments); + } + }; + } + + // By default, we use closely bound real methods wherever possible, and + // otherwise we wait for a console to appear, and then try again. + function defaultMethodFactory(methodName, level, loggerName) { + /*jshint validthis:true */ + return realMethod(methodName) || + enableLoggingWhenConsoleArrives.apply(this, arguments); + } + + function Logger(name, defaultLevel, factory) { + var self = this; + var currentLevel; + var storageKey = "loglevel"; + if (name) { + storageKey += ":" + name; + } + + function persistLevelIfPossible(levelNum) { + var levelName = (logMethods[levelNum] || 'silent').toUpperCase(); + + if (typeof window === undefinedType) return; + + // Use localStorage if available + try { + window.localStorage[storageKey] = levelName; + return; + } catch (ignore) {} + + // Use session cookie as fallback + try { + window.document.cookie = + encodeURIComponent(storageKey) + "=" + levelName + ";"; + } catch (ignore) {} + } + + function getPersistedLevel() { + var storedLevel; + + if (typeof window === undefinedType) return; + + try { + storedLevel = window.localStorage[storageKey]; + } catch (ignore) {} + + // Fallback to cookies if local storage gives us nothing + if (typeof storedLevel === undefinedType) { + try { + var cookie = window.document.cookie; + var location = cookie.indexOf( + encodeURIComponent(storageKey) + "="); + if (location !== -1) { + storedLevel = /^([^;]+)/.exec(cookie.slice(location))[1]; + } + } catch (ignore) {} + } + + // If the stored level is not valid, treat it as if nothing was stored. + if (self.levels[storedLevel] === undefined) { + storedLevel = undefined; + } + + return storedLevel; + } + + /* + * + * Public logger API - see https://github.com/pimterry/loglevel for details + * + */ + + self.name = name; + + self.levels = { "TRACE": 0, "DEBUG": 1, "INFO": 2, "WARN": 3, + "ERROR": 4, "SILENT": 5}; + + self.methodFactory = factory || defaultMethodFactory; + + self.getLevel = function () { + return currentLevel; + }; + + self.setLevel = function (level, persist) { + if (typeof level === "string" && self.levels[level.toUpperCase()] !== undefined) { + level = self.levels[level.toUpperCase()]; + } + if (typeof level === "number" && level >= 0 && level <= self.levels.SILENT) { + currentLevel = level; + if (persist !== false) { // defaults to true + persistLevelIfPossible(level); + } + replaceLoggingMethods.call(self, level, name); + if (typeof console === undefinedType && level < self.levels.SILENT) { + return "No console available for logging"; + } + } else { + throw "log.setLevel() called with invalid level: " + level; + } + }; + + self.setDefaultLevel = function (level) { + if (!getPersistedLevel()) { + self.setLevel(level, false); + } + }; + + self.enableAll = function(persist) { + self.setLevel(self.levels.TRACE, persist); + }; + + self.disableAll = function(persist) { + self.setLevel(self.levels.SILENT, persist); + }; + + // Initialize with the right level + var initialLevel = getPersistedLevel(); + if (initialLevel == null) { + initialLevel = defaultLevel == null ? "WARN" : defaultLevel; + } + self.setLevel(initialLevel, false); + } + + /* + * + * Top-level API + * + */ + + var defaultLogger = new Logger(); + + var _loggersByName = {}; + defaultLogger.getLogger = function getLogger(name) { + if (typeof name !== "string" || name === "") { + throw new TypeError("You must supply a name when creating a logger."); + } + + var logger = _loggersByName[name]; + if (!logger) { + logger = _loggersByName[name] = new Logger( + name, defaultLogger.getLevel(), defaultLogger.methodFactory); + } + return logger; + }; + + // Grab the current global log variable in case of overwrite + var _log = (typeof window !== undefinedType) ? window.log : undefined; + defaultLogger.noConflict = function() { + if (typeof window !== undefinedType && + window.log === defaultLogger) { + window.log = _log; + } + + return defaultLogger; + }; + + defaultLogger.getLoggers = function getLoggers() { + return _loggersByName; + }; + + return defaultLogger; +})); + +},{}],44:[function(require,module,exports){ +(function(){ + var crypt = require('crypt'), + utf8 = require('charenc').utf8, + isBuffer = require('is-buffer'), + bin = require('charenc').bin, + + // The core + md5 = function (message, options) { + // Convert to byte array + if (message.constructor == String) + if (options && options.encoding === 'binary') + message = bin.stringToBytes(message); + else + message = utf8.stringToBytes(message); + else if (isBuffer(message)) + message = Array.prototype.slice.call(message, 0); + else if (!Array.isArray(message) && message.constructor !== Uint8Array) + message = message.toString(); + // else, assume byte array already + + var m = crypt.bytesToWords(message), + l = message.length * 8, + a = 1732584193, + b = -271733879, + c = -1732584194, + d = 271733878; + + // Swap endian + for (var i = 0; i < m.length; i++) { + m[i] = ((m[i] << 8) | (m[i] >>> 24)) & 0x00FF00FF | + ((m[i] << 24) | (m[i] >>> 8)) & 0xFF00FF00; + } + + // Padding + m[l >>> 5] |= 0x80 << (l % 32); + m[(((l + 64) >>> 9) << 4) + 14] = l; + + // Method shortcuts + var FF = md5._ff, + GG = md5._gg, + HH = md5._hh, + II = md5._ii; + + for (var i = 0; i < m.length; i += 16) { + + var aa = a, + bb = b, + cc = c, + dd = d; + + a = FF(a, b, c, d, m[i+ 0], 7, -680876936); + d = FF(d, a, b, c, m[i+ 1], 12, -389564586); + c = FF(c, d, a, b, m[i+ 2], 17, 606105819); + b = FF(b, c, d, a, m[i+ 3], 22, -1044525330); + a = FF(a, b, c, d, m[i+ 4], 7, -176418897); + d = FF(d, a, b, c, m[i+ 5], 12, 1200080426); + c = FF(c, d, a, b, m[i+ 6], 17, -1473231341); + b = FF(b, c, d, a, m[i+ 7], 22, -45705983); + a = FF(a, b, c, d, m[i+ 8], 7, 1770035416); + d = FF(d, a, b, c, m[i+ 9], 12, -1958414417); + c = FF(c, d, a, b, m[i+10], 17, -42063); + b = FF(b, c, d, a, m[i+11], 22, -1990404162); + a = FF(a, b, c, d, m[i+12], 7, 1804603682); + d = FF(d, a, b, c, m[i+13], 12, -40341101); + c = FF(c, d, a, b, m[i+14], 17, -1502002290); + b = FF(b, c, d, a, m[i+15], 22, 1236535329); + + a = GG(a, b, c, d, m[i+ 1], 5, -165796510); + d = GG(d, a, b, c, m[i+ 6], 9, -1069501632); + c = GG(c, d, a, b, m[i+11], 14, 643717713); + b = GG(b, c, d, a, m[i+ 0], 20, -373897302); + a = GG(a, b, c, d, m[i+ 5], 5, -701558691); + d = GG(d, a, b, c, m[i+10], 9, 38016083); + c = GG(c, d, a, b, m[i+15], 14, -660478335); + b = GG(b, c, d, a, m[i+ 4], 20, -405537848); + a = GG(a, b, c, d, m[i+ 9], 5, 568446438); + d = GG(d, a, b, c, m[i+14], 9, -1019803690); + c = GG(c, d, a, b, m[i+ 3], 14, -187363961); + b = GG(b, c, d, a, m[i+ 8], 20, 1163531501); + a = GG(a, b, c, d, m[i+13], 5, -1444681467); + d = GG(d, a, b, c, m[i+ 2], 9, -51403784); + c = GG(c, d, a, b, m[i+ 7], 14, 1735328473); + b = GG(b, c, d, a, m[i+12], 20, -1926607734); + + a = HH(a, b, c, d, m[i+ 5], 4, -378558); + d = HH(d, a, b, c, m[i+ 8], 11, -2022574463); + c = HH(c, d, a, b, m[i+11], 16, 1839030562); + b = HH(b, c, d, a, m[i+14], 23, -35309556); + a = HH(a, b, c, d, m[i+ 1], 4, -1530992060); + d = HH(d, a, b, c, m[i+ 4], 11, 1272893353); + c = HH(c, d, a, b, m[i+ 7], 16, -155497632); + b = HH(b, c, d, a, m[i+10], 23, -1094730640); + a = HH(a, b, c, d, m[i+13], 4, 681279174); + d = HH(d, a, b, c, m[i+ 0], 11, -358537222); + c = HH(c, d, a, b, m[i+ 3], 16, -722521979); + b = HH(b, c, d, a, m[i+ 6], 23, 76029189); + a = HH(a, b, c, d, m[i+ 9], 4, -640364487); + d = HH(d, a, b, c, m[i+12], 11, -421815835); + c = HH(c, d, a, b, m[i+15], 16, 530742520); + b = HH(b, c, d, a, m[i+ 2], 23, -995338651); + + a = II(a, b, c, d, m[i+ 0], 6, -198630844); + d = II(d, a, b, c, m[i+ 7], 10, 1126891415); + c = II(c, d, a, b, m[i+14], 15, -1416354905); + b = II(b, c, d, a, m[i+ 5], 21, -57434055); + a = II(a, b, c, d, m[i+12], 6, 1700485571); + d = II(d, a, b, c, m[i+ 3], 10, -1894986606); + c = II(c, d, a, b, m[i+10], 15, -1051523); + b = II(b, c, d, a, m[i+ 1], 21, -2054922799); + a = II(a, b, c, d, m[i+ 8], 6, 1873313359); + d = II(d, a, b, c, m[i+15], 10, -30611744); + c = II(c, d, a, b, m[i+ 6], 15, -1560198380); + b = II(b, c, d, a, m[i+13], 21, 1309151649); + a = II(a, b, c, d, m[i+ 4], 6, -145523070); + d = II(d, a, b, c, m[i+11], 10, -1120210379); + c = II(c, d, a, b, m[i+ 2], 15, 718787259); + b = II(b, c, d, a, m[i+ 9], 21, -343485551); + + a = (a + aa) >>> 0; + b = (b + bb) >>> 0; + c = (c + cc) >>> 0; + d = (d + dd) >>> 0; + } + + return crypt.endian([a, b, c, d]); + }; + + // Auxiliary functions + md5._ff = function (a, b, c, d, x, s, t) { + var n = a + (b & c | ~b & d) + (x >>> 0) + t; + return ((n << s) | (n >>> (32 - s))) + b; + }; + md5._gg = function (a, b, c, d, x, s, t) { + var n = a + (b & d | c & ~d) + (x >>> 0) + t; + return ((n << s) | (n >>> (32 - s))) + b; + }; + md5._hh = function (a, b, c, d, x, s, t) { + var n = a + (b ^ c ^ d) + (x >>> 0) + t; + return ((n << s) | (n >>> (32 - s))) + b; + }; + md5._ii = function (a, b, c, d, x, s, t) { + var n = a + (c ^ (b | ~d)) + (x >>> 0) + t; + return ((n << s) | (n >>> (32 - s))) + b; + }; + + // Package private blocksize + md5._blocksize = 16; + md5._digestsize = 16; + + module.exports = function (message, options) { + if (message === undefined || message === null) + throw new Error('Illegal argument ' + message); + + var digestbytes = crypt.wordsToBytes(md5(message, options)); + return options && options.asBytes ? digestbytes : + options && options.asString ? bin.bytesToString(digestbytes) : + crypt.bytesToHex(digestbytes); + }; + +})(); + +},{"charenc":40,"crypt":41,"is-buffer":42}],45:[function(require,module,exports){ +/* + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ + /* eslint-env node */ +'use strict'; + +var SDPUtils = require('sdp'); + +function writeMediaSection(transceiver, caps, type, stream, dtlsRole) { + var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps); + + // Map ICE parameters (ufrag, pwd) to SDP. + sdp += SDPUtils.writeIceParameters( + transceiver.iceGatherer.getLocalParameters()); + + // Map DTLS parameters to SDP. + sdp += SDPUtils.writeDtlsParameters( + transceiver.dtlsTransport.getLocalParameters(), + type === 'offer' ? 'actpass' : dtlsRole || 'active'); + + sdp += 'a=mid:' + transceiver.mid + '\r\n'; + + if (transceiver.rtpSender && transceiver.rtpReceiver) { + sdp += 'a=sendrecv\r\n'; + } else if (transceiver.rtpSender) { + sdp += 'a=sendonly\r\n'; + } else if (transceiver.rtpReceiver) { + sdp += 'a=recvonly\r\n'; + } else { + sdp += 'a=inactive\r\n'; + } + + if (transceiver.rtpSender) { + var trackId = transceiver.rtpSender._initialTrackId || + transceiver.rtpSender.track.id; + transceiver.rtpSender._initialTrackId = trackId; + // spec. + var msid = 'msid:' + (stream ? stream.id : '-') + ' ' + + trackId + '\r\n'; + sdp += 'a=' + msid; + // for Chrome. Legacy should no longer be required. + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + + ' ' + msid; + + // RTX + if (transceiver.sendEncodingParameters[0].rtx) { + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + + ' ' + msid; + sdp += 'a=ssrc-group:FID ' + + transceiver.sendEncodingParameters[0].ssrc + ' ' + + transceiver.sendEncodingParameters[0].rtx.ssrc + + '\r\n'; + } + } + // FIXME: this should be written by writeRtpDescription. + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + + ' cname:' + SDPUtils.localCName + '\r\n'; + if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) { + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + + ' cname:' + SDPUtils.localCName + '\r\n'; + } + return sdp; +} + +// Edge does not like +// 1) stun: filtered after 14393 unless ?transport=udp is present +// 2) turn: that does not have all of turn:host:port?transport=udp +// 3) turn: with ipv6 addresses +// 4) turn: occurring muliple times +function filterIceServers(iceServers, edgeVersion) { + var hasTurn = false; + iceServers = JSON.parse(JSON.stringify(iceServers)); + return iceServers.filter(function(server) { + if (server && (server.urls || server.url)) { + var urls = server.urls || server.url; + if (server.url && !server.urls) { + console.warn('RTCIceServer.url is deprecated! Use urls instead.'); + } + var isString = typeof urls === 'string'; + if (isString) { + urls = [urls]; + } + urls = urls.filter(function(url) { + var validTurn = url.indexOf('turn:') === 0 && + url.indexOf('transport=udp') !== -1 && + url.indexOf('turn:[') === -1 && + !hasTurn; + + if (validTurn) { + hasTurn = true; + return true; + } + return url.indexOf('stun:') === 0 && edgeVersion >= 14393 && + url.indexOf('?transport=udp') === -1; + }); + + delete server.url; + server.urls = isString ? urls[0] : urls; + return !!urls.length; + } + }); +} + +// Determines the intersection of local and remote capabilities. +function getCommonCapabilities(localCapabilities, remoteCapabilities) { + var commonCapabilities = { + codecs: [], + headerExtensions: [], + fecMechanisms: [] + }; + + var findCodecByPayloadType = function(pt, codecs) { + pt = parseInt(pt, 10); + for (var i = 0; i < codecs.length; i++) { + if (codecs[i].payloadType === pt || + codecs[i].preferredPayloadType === pt) { + return codecs[i]; + } + } + }; + + var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) { + var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs); + var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs); + return lCodec && rCodec && + lCodec.name.toLowerCase() === rCodec.name.toLowerCase(); + }; + + localCapabilities.codecs.forEach(function(lCodec) { + for (var i = 0; i < remoteCapabilities.codecs.length; i++) { + var rCodec = remoteCapabilities.codecs[i]; + if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() && + lCodec.clockRate === rCodec.clockRate) { + if (lCodec.name.toLowerCase() === 'rtx' && + lCodec.parameters && rCodec.parameters.apt) { + // for RTX we need to find the local rtx that has a apt + // which points to the same local codec as the remote one. + if (!rtxCapabilityMatches(lCodec, rCodec, + localCapabilities.codecs, remoteCapabilities.codecs)) { + continue; + } + } + rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy + // number of channels is the highest common number of channels + rCodec.numChannels = Math.min(lCodec.numChannels, + rCodec.numChannels); + // push rCodec so we reply with offerer payload type + commonCapabilities.codecs.push(rCodec); + + // determine common feedback mechanisms + rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) { + for (var j = 0; j < lCodec.rtcpFeedback.length; j++) { + if (lCodec.rtcpFeedback[j].type === fb.type && + lCodec.rtcpFeedback[j].parameter === fb.parameter) { + return true; + } + } + return false; + }); + // FIXME: also need to determine .parameters + // see https://github.com/openpeer/ortc/issues/569 + break; + } + } + }); + + localCapabilities.headerExtensions.forEach(function(lHeaderExtension) { + for (var i = 0; i < remoteCapabilities.headerExtensions.length; + i++) { + var rHeaderExtension = remoteCapabilities.headerExtensions[i]; + if (lHeaderExtension.uri === rHeaderExtension.uri) { + commonCapabilities.headerExtensions.push(rHeaderExtension); + break; + } + } + }); + + // FIXME: fecMechanisms + return commonCapabilities; +} + +// is action=setLocalDescription with type allowed in signalingState +function isActionAllowedInSignalingState(action, type, signalingState) { + return { + offer: { + setLocalDescription: ['stable', 'have-local-offer'], + setRemoteDescription: ['stable', 'have-remote-offer'] + }, + answer: { + setLocalDescription: ['have-remote-offer', 'have-local-pranswer'], + setRemoteDescription: ['have-local-offer', 'have-remote-pranswer'] + } + }[type][action].indexOf(signalingState) !== -1; +} + +function maybeAddCandidate(iceTransport, candidate) { + // Edge's internal representation adds some fields therefore + // not all fieldѕ are taken into account. + var alreadyAdded = iceTransport.getRemoteCandidates() + .find(function(remoteCandidate) { + return candidate.foundation === remoteCandidate.foundation && + candidate.ip === remoteCandidate.ip && + candidate.port === remoteCandidate.port && + candidate.priority === remoteCandidate.priority && + candidate.protocol === remoteCandidate.protocol && + candidate.type === remoteCandidate.type; + }); + if (!alreadyAdded) { + iceTransport.addRemoteCandidate(candidate); + } + return !alreadyAdded; +} + + +function makeError(name, description) { + var e = new Error(description); + e.name = name; + return e; +} + +module.exports = function(window, edgeVersion) { + // https://w3c.github.io/mediacapture-main/#mediastream + // Helper function to add the track to the stream and + // dispatch the event ourselves. + function addTrackToStreamAndFireEvent(track, stream) { + stream.addTrack(track); + stream.dispatchEvent(new window.MediaStreamTrackEvent('addtrack', + {track: track})); + } + + function removeTrackFromStreamAndFireEvent(track, stream) { + stream.removeTrack(track); + stream.dispatchEvent(new window.MediaStreamTrackEvent('removetrack', + {track: track})); + } + + function fireAddTrack(pc, track, receiver, streams) { + var trackEvent = new Event('track'); + trackEvent.track = track; + trackEvent.receiver = receiver; + trackEvent.transceiver = {receiver: receiver}; + trackEvent.streams = streams; + window.setTimeout(function() { + pc._dispatchEvent('track', trackEvent); + }); + } + + var RTCPeerConnection = function(config) { + var pc = this; + + var _eventTarget = document.createDocumentFragment(); + ['addEventListener', 'removeEventListener', 'dispatchEvent'] + .forEach(function(method) { + pc[method] = _eventTarget[method].bind(_eventTarget); + }); + + this.canTrickleIceCandidates = null; + + this.needNegotiation = false; + + this.localStreams = []; + this.remoteStreams = []; + + this.localDescription = null; + this.remoteDescription = null; + + this.signalingState = 'stable'; + this.iceConnectionState = 'new'; + this.iceGatheringState = 'new'; + + config = JSON.parse(JSON.stringify(config || {})); + + this.usingBundle = config.bundlePolicy === 'max-bundle'; + if (config.rtcpMuxPolicy === 'negotiate') { + throw(makeError('NotSupportedError', + 'rtcpMuxPolicy \'negotiate\' is not supported')); + } else if (!config.rtcpMuxPolicy) { + config.rtcpMuxPolicy = 'require'; + } + + switch (config.iceTransportPolicy) { + case 'all': + case 'relay': + break; + default: + config.iceTransportPolicy = 'all'; + break; + } + + switch (config.bundlePolicy) { + case 'balanced': + case 'max-compat': + case 'max-bundle': + break; + default: + config.bundlePolicy = 'balanced'; + break; + } + + config.iceServers = filterIceServers(config.iceServers || [], edgeVersion); + + this._iceGatherers = []; + if (config.iceCandidatePoolSize) { + for (var i = config.iceCandidatePoolSize; i > 0; i--) { + this._iceGatherers.push(new window.RTCIceGatherer({ + iceServers: config.iceServers, + gatherPolicy: config.iceTransportPolicy + })); + } + } else { + config.iceCandidatePoolSize = 0; + } + + this._config = config; + + // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ... + // everything that is needed to describe a SDP m-line. + this.transceivers = []; + + this._sdpSessionId = SDPUtils.generateSessionId(); + this._sdpSessionVersion = 0; + + this._dtlsRole = undefined; // role for a=setup to use in answers. + + this._isClosed = false; + }; + + // set up event handlers on prototype + RTCPeerConnection.prototype.onicecandidate = null; + RTCPeerConnection.prototype.onaddstream = null; + RTCPeerConnection.prototype.ontrack = null; + RTCPeerConnection.prototype.onremovestream = null; + RTCPeerConnection.prototype.onsignalingstatechange = null; + RTCPeerConnection.prototype.oniceconnectionstatechange = null; + RTCPeerConnection.prototype.onicegatheringstatechange = null; + RTCPeerConnection.prototype.onnegotiationneeded = null; + RTCPeerConnection.prototype.ondatachannel = null; + + RTCPeerConnection.prototype._dispatchEvent = function(name, event) { + if (this._isClosed) { + return; + } + this.dispatchEvent(event); + if (typeof this['on' + name] === 'function') { + this['on' + name](event); + } + }; + + RTCPeerConnection.prototype._emitGatheringStateChange = function() { + var event = new Event('icegatheringstatechange'); + this._dispatchEvent('icegatheringstatechange', event); + }; + + RTCPeerConnection.prototype.getConfiguration = function() { + return this._config; + }; + + RTCPeerConnection.prototype.getLocalStreams = function() { + return this.localStreams; + }; + + RTCPeerConnection.prototype.getRemoteStreams = function() { + return this.remoteStreams; + }; + + // internal helper to create a transceiver object. + // (whih is not yet the same as the WebRTC 1.0 transceiver) + RTCPeerConnection.prototype._createTransceiver = function(kind) { + var hasBundleTransport = this.transceivers.length > 0; + var transceiver = { + track: null, + iceGatherer: null, + iceTransport: null, + dtlsTransport: null, + localCapabilities: null, + remoteCapabilities: null, + rtpSender: null, + rtpReceiver: null, + kind: kind, + mid: null, + sendEncodingParameters: null, + recvEncodingParameters: null, + stream: null, + associatedRemoteMediaStreams: [], + wantReceive: true + }; + if (this.usingBundle && hasBundleTransport) { + transceiver.iceTransport = this.transceivers[0].iceTransport; + transceiver.dtlsTransport = this.transceivers[0].dtlsTransport; + } else { + var transports = this._createIceAndDtlsTransports(); + transceiver.iceTransport = transports.iceTransport; + transceiver.dtlsTransport = transports.dtlsTransport; + } + this.transceivers.push(transceiver); + return transceiver; + }; + + RTCPeerConnection.prototype.addTrack = function(track, stream) { + if (this._isClosed) { + throw makeError('InvalidStateError', + 'Attempted to call addTrack on a closed peerconnection.'); + } + + var alreadyExists = this.transceivers.find(function(s) { + return s.track === track; + }); + + if (alreadyExists) { + throw makeError('InvalidAccessError', 'Track already exists.'); + } + + var transceiver; + for (var i = 0; i < this.transceivers.length; i++) { + if (!this.transceivers[i].track && + this.transceivers[i].kind === track.kind) { + transceiver = this.transceivers[i]; + } + } + if (!transceiver) { + transceiver = this._createTransceiver(track.kind); + } + + this._maybeFireNegotiationNeeded(); + + if (this.localStreams.indexOf(stream) === -1) { + this.localStreams.push(stream); + } + + transceiver.track = track; + transceiver.stream = stream; + transceiver.rtpSender = new window.RTCRtpSender(track, + transceiver.dtlsTransport); + return transceiver.rtpSender; + }; + + RTCPeerConnection.prototype.addStream = function(stream) { + var pc = this; + if (edgeVersion >= 15025) { + stream.getTracks().forEach(function(track) { + pc.addTrack(track, stream); + }); + } else { + // Clone is necessary for local demos mostly, attaching directly + // to two different senders does not work (build 10547). + // Fixed in 15025 (or earlier) + var clonedStream = stream.clone(); + stream.getTracks().forEach(function(track, idx) { + var clonedTrack = clonedStream.getTracks()[idx]; + track.addEventListener('enabled', function(event) { + clonedTrack.enabled = event.enabled; + }); + }); + clonedStream.getTracks().forEach(function(track) { + pc.addTrack(track, clonedStream); + }); + } + }; + + RTCPeerConnection.prototype.removeTrack = function(sender) { + if (this._isClosed) { + throw makeError('InvalidStateError', + 'Attempted to call removeTrack on a closed peerconnection.'); + } + + if (!(sender instanceof window.RTCRtpSender)) { + throw new TypeError('Argument 1 of RTCPeerConnection.removeTrack ' + + 'does not implement interface RTCRtpSender.'); + } + + var transceiver = this.transceivers.find(function(t) { + return t.rtpSender === sender; + }); + + if (!transceiver) { + throw makeError('InvalidAccessError', + 'Sender was not created by this connection.'); + } + var stream = transceiver.stream; + + transceiver.rtpSender.stop(); + transceiver.rtpSender = null; + transceiver.track = null; + transceiver.stream = null; + + // remove the stream from the set of local streams + var localStreams = this.transceivers.map(function(t) { + return t.stream; + }); + if (localStreams.indexOf(stream) === -1 && + this.localStreams.indexOf(stream) > -1) { + this.localStreams.splice(this.localStreams.indexOf(stream), 1); + } + + this._maybeFireNegotiationNeeded(); + }; + + RTCPeerConnection.prototype.removeStream = function(stream) { + var pc = this; + stream.getTracks().forEach(function(track) { + var sender = pc.getSenders().find(function(s) { + return s.track === track; + }); + if (sender) { + pc.removeTrack(sender); + } + }); + }; + + RTCPeerConnection.prototype.getSenders = function() { + return this.transceivers.filter(function(transceiver) { + return !!transceiver.rtpSender; + }) + .map(function(transceiver) { + return transceiver.rtpSender; + }); + }; + + RTCPeerConnection.prototype.getReceivers = function() { + return this.transceivers.filter(function(transceiver) { + return !!transceiver.rtpReceiver; + }) + .map(function(transceiver) { + return transceiver.rtpReceiver; + }); + }; + + + RTCPeerConnection.prototype._createIceGatherer = function(sdpMLineIndex, + usingBundle) { + var pc = this; + if (usingBundle && sdpMLineIndex > 0) { + return this.transceivers[0].iceGatherer; + } else if (this._iceGatherers.length) { + return this._iceGatherers.shift(); + } + var iceGatherer = new window.RTCIceGatherer({ + iceServers: this._config.iceServers, + gatherPolicy: this._config.iceTransportPolicy + }); + Object.defineProperty(iceGatherer, 'state', + {value: 'new', writable: true} + ); + + this.transceivers[sdpMLineIndex].bufferedCandidateEvents = []; + this.transceivers[sdpMLineIndex].bufferCandidates = function(event) { + var end = !event.candidate || Object.keys(event.candidate).length === 0; + // polyfill since RTCIceGatherer.state is not implemented in + // Edge 10547 yet. + iceGatherer.state = end ? 'completed' : 'gathering'; + if (pc.transceivers[sdpMLineIndex].bufferedCandidateEvents !== null) { + pc.transceivers[sdpMLineIndex].bufferedCandidateEvents.push(event); + } + }; + iceGatherer.addEventListener('localcandidate', + this.transceivers[sdpMLineIndex].bufferCandidates); + return iceGatherer; + }; + + // start gathering from an RTCIceGatherer. + RTCPeerConnection.prototype._gather = function(mid, sdpMLineIndex) { + var pc = this; + var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer; + if (iceGatherer.onlocalcandidate) { + return; + } + var bufferedCandidateEvents = + this.transceivers[sdpMLineIndex].bufferedCandidateEvents; + this.transceivers[sdpMLineIndex].bufferedCandidateEvents = null; + iceGatherer.removeEventListener('localcandidate', + this.transceivers[sdpMLineIndex].bufferCandidates); + iceGatherer.onlocalcandidate = function(evt) { + if (pc.usingBundle && sdpMLineIndex > 0) { + // if we know that we use bundle we can drop candidates with + // ѕdpMLineIndex > 0. If we don't do this then our state gets + // confused since we dispose the extra ice gatherer. + return; + } + var event = new Event('icecandidate'); + event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex}; + + var cand = evt.candidate; + // Edge emits an empty object for RTCIceCandidateComplete‥ + var end = !cand || Object.keys(cand).length === 0; + if (end) { + // polyfill since RTCIceGatherer.state is not implemented in + // Edge 10547 yet. + if (iceGatherer.state === 'new' || iceGatherer.state === 'gathering') { + iceGatherer.state = 'completed'; + } + } else { + if (iceGatherer.state === 'new') { + iceGatherer.state = 'gathering'; + } + // RTCIceCandidate doesn't have a component, needs to be added + cand.component = 1; + var serializedCandidate = SDPUtils.writeCandidate(cand); + event.candidate = Object.assign(event.candidate, + SDPUtils.parseCandidate(serializedCandidate)); + event.candidate.candidate = serializedCandidate; + } + + // update local description. + var sections = SDPUtils.getMediaSections(pc.localDescription.sdp); + if (!end) { + sections[event.candidate.sdpMLineIndex] += + 'a=' + event.candidate.candidate + '\r\n'; + } else { + sections[event.candidate.sdpMLineIndex] += + 'a=end-of-candidates\r\n'; + } + pc.localDescription.sdp = + SDPUtils.getDescription(pc.localDescription.sdp) + + sections.join(''); + var complete = pc.transceivers.every(function(transceiver) { + return transceiver.iceGatherer && + transceiver.iceGatherer.state === 'completed'; + }); + + if (pc.iceGatheringState !== 'gathering') { + pc.iceGatheringState = 'gathering'; + pc._emitGatheringStateChange(); + } + + // Emit candidate. Also emit null candidate when all gatherers are + // complete. + if (!end) { + pc._dispatchEvent('icecandidate', event); + } + if (complete) { + pc._dispatchEvent('icecandidate', new Event('icecandidate')); + pc.iceGatheringState = 'complete'; + pc._emitGatheringStateChange(); + } + }; + + // emit already gathered candidates. + window.setTimeout(function() { + bufferedCandidateEvents.forEach(function(e) { + iceGatherer.onlocalcandidate(e); + }); + }, 0); + }; + + // Create ICE transport and DTLS transport. + RTCPeerConnection.prototype._createIceAndDtlsTransports = function() { + var pc = this; + var iceTransport = new window.RTCIceTransport(null); + iceTransport.onicestatechange = function() { + pc._updateConnectionState(); + }; + + var dtlsTransport = new window.RTCDtlsTransport(iceTransport); + dtlsTransport.ondtlsstatechange = function() { + pc._updateConnectionState(); + }; + dtlsTransport.onerror = function() { + // onerror does not set state to failed by itself. + Object.defineProperty(dtlsTransport, 'state', + {value: 'failed', writable: true}); + pc._updateConnectionState(); + }; + + return { + iceTransport: iceTransport, + dtlsTransport: dtlsTransport + }; + }; + + // Destroy ICE gatherer, ICE transport and DTLS transport. + // Without triggering the callbacks. + RTCPeerConnection.prototype._disposeIceAndDtlsTransports = function( + sdpMLineIndex) { + var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer; + if (iceGatherer) { + delete iceGatherer.onlocalcandidate; + delete this.transceivers[sdpMLineIndex].iceGatherer; + } + var iceTransport = this.transceivers[sdpMLineIndex].iceTransport; + if (iceTransport) { + delete iceTransport.onicestatechange; + delete this.transceivers[sdpMLineIndex].iceTransport; + } + var dtlsTransport = this.transceivers[sdpMLineIndex].dtlsTransport; + if (dtlsTransport) { + delete dtlsTransport.ondtlsstatechange; + delete dtlsTransport.onerror; + delete this.transceivers[sdpMLineIndex].dtlsTransport; + } + }; + + // Start the RTP Sender and Receiver for a transceiver. + RTCPeerConnection.prototype._transceive = function(transceiver, + send, recv) { + var params = getCommonCapabilities(transceiver.localCapabilities, + transceiver.remoteCapabilities); + if (send && transceiver.rtpSender) { + params.encodings = transceiver.sendEncodingParameters; + params.rtcp = { + cname: SDPUtils.localCName, + compound: transceiver.rtcpParameters.compound + }; + if (transceiver.recvEncodingParameters.length) { + params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc; + } + transceiver.rtpSender.send(params); + } + if (recv && transceiver.rtpReceiver && params.codecs.length > 0) { + // remove RTX field in Edge 14942 + if (transceiver.kind === 'video' + && transceiver.recvEncodingParameters + && edgeVersion < 15019) { + transceiver.recvEncodingParameters.forEach(function(p) { + delete p.rtx; + }); + } + if (transceiver.recvEncodingParameters.length) { + params.encodings = transceiver.recvEncodingParameters; + } else { + params.encodings = [{}]; + } + params.rtcp = { + compound: transceiver.rtcpParameters.compound + }; + if (transceiver.rtcpParameters.cname) { + params.rtcp.cname = transceiver.rtcpParameters.cname; + } + if (transceiver.sendEncodingParameters.length) { + params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc; + } + transceiver.rtpReceiver.receive(params); + } + }; + + RTCPeerConnection.prototype.setLocalDescription = function(description) { + var pc = this; + + // Note: pranswer is not supported. + if (['offer', 'answer'].indexOf(description.type) === -1) { + return Promise.reject(makeError('TypeError', + 'Unsupported type "' + description.type + '"')); + } + + if (!isActionAllowedInSignalingState('setLocalDescription', + description.type, pc.signalingState) || pc._isClosed) { + return Promise.reject(makeError('InvalidStateError', + 'Can not set local ' + description.type + + ' in state ' + pc.signalingState)); + } + + var sections; + var sessionpart; + if (description.type === 'offer') { + // VERY limited support for SDP munging. Limited to: + // * changing the order of codecs + sections = SDPUtils.splitSections(description.sdp); + sessionpart = sections.shift(); + sections.forEach(function(mediaSection, sdpMLineIndex) { + var caps = SDPUtils.parseRtpParameters(mediaSection); + pc.transceivers[sdpMLineIndex].localCapabilities = caps; + }); + + pc.transceivers.forEach(function(transceiver, sdpMLineIndex) { + pc._gather(transceiver.mid, sdpMLineIndex); + }); + } else if (description.type === 'answer') { + sections = SDPUtils.splitSections(pc.remoteDescription.sdp); + sessionpart = sections.shift(); + var isIceLite = SDPUtils.matchPrefix(sessionpart, + 'a=ice-lite').length > 0; + sections.forEach(function(mediaSection, sdpMLineIndex) { + var transceiver = pc.transceivers[sdpMLineIndex]; + var iceGatherer = transceiver.iceGatherer; + var iceTransport = transceiver.iceTransport; + var dtlsTransport = transceiver.dtlsTransport; + var localCapabilities = transceiver.localCapabilities; + var remoteCapabilities = transceiver.remoteCapabilities; + + // treat bundle-only as not-rejected. + var rejected = SDPUtils.isRejected(mediaSection) && + SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0; + + if (!rejected && !transceiver.isDatachannel) { + var remoteIceParameters = SDPUtils.getIceParameters( + mediaSection, sessionpart); + var remoteDtlsParameters = SDPUtils.getDtlsParameters( + mediaSection, sessionpart); + if (isIceLite) { + remoteDtlsParameters.role = 'server'; + } + + if (!pc.usingBundle || sdpMLineIndex === 0) { + pc._gather(transceiver.mid, sdpMLineIndex); + if (iceTransport.state === 'new') { + iceTransport.start(iceGatherer, remoteIceParameters, + isIceLite ? 'controlling' : 'controlled'); + } + if (dtlsTransport.state === 'new') { + dtlsTransport.start(remoteDtlsParameters); + } + } + + // Calculate intersection of capabilities. + var params = getCommonCapabilities(localCapabilities, + remoteCapabilities); + + // Start the RTCRtpSender. The RTCRtpReceiver for this + // transceiver has already been started in setRemoteDescription. + pc._transceive(transceiver, + params.codecs.length > 0, + false); + } + }); + } + + pc.localDescription = { + type: description.type, + sdp: description.sdp + }; + if (description.type === 'offer') { + pc._updateSignalingState('have-local-offer'); + } else { + pc._updateSignalingState('stable'); + } + + return Promise.resolve(); + }; + + RTCPeerConnection.prototype.setRemoteDescription = function(description) { + var pc = this; + + // Note: pranswer is not supported. + if (['offer', 'answer'].indexOf(description.type) === -1) { + return Promise.reject(makeError('TypeError', + 'Unsupported type "' + description.type + '"')); + } + + if (!isActionAllowedInSignalingState('setRemoteDescription', + description.type, pc.signalingState) || pc._isClosed) { + return Promise.reject(makeError('InvalidStateError', + 'Can not set remote ' + description.type + + ' in state ' + pc.signalingState)); + } + + var streams = {}; + pc.remoteStreams.forEach(function(stream) { + streams[stream.id] = stream; + }); + var receiverList = []; + var sections = SDPUtils.splitSections(description.sdp); + var sessionpart = sections.shift(); + var isIceLite = SDPUtils.matchPrefix(sessionpart, + 'a=ice-lite').length > 0; + var usingBundle = SDPUtils.matchPrefix(sessionpart, + 'a=group:BUNDLE ').length > 0; + pc.usingBundle = usingBundle; + var iceOptions = SDPUtils.matchPrefix(sessionpart, + 'a=ice-options:')[0]; + if (iceOptions) { + pc.canTrickleIceCandidates = iceOptions.substr(14).split(' ') + .indexOf('trickle') >= 0; + } else { + pc.canTrickleIceCandidates = false; + } + + sections.forEach(function(mediaSection, sdpMLineIndex) { + var lines = SDPUtils.splitLines(mediaSection); + var kind = SDPUtils.getKind(mediaSection); + // treat bundle-only as not-rejected. + var rejected = SDPUtils.isRejected(mediaSection) && + SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0; + var protocol = lines[0].substr(2).split(' ')[2]; + + var direction = SDPUtils.getDirection(mediaSection, sessionpart); + var remoteMsid = SDPUtils.parseMsid(mediaSection); + + var mid = SDPUtils.getMid(mediaSection) || SDPUtils.generateIdentifier(); + + // Reject datachannels which are not implemented yet. + if (kind === 'application' && protocol === 'DTLS/SCTP') { + pc.transceivers[sdpMLineIndex] = { + mid: mid, + isDatachannel: true + }; + return; + } + + var transceiver; + var iceGatherer; + var iceTransport; + var dtlsTransport; + var rtpReceiver; + var sendEncodingParameters; + var recvEncodingParameters; + var localCapabilities; + + var track; + // FIXME: ensure the mediaSection has rtcp-mux set. + var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection); + var remoteIceParameters; + var remoteDtlsParameters; + if (!rejected) { + remoteIceParameters = SDPUtils.getIceParameters(mediaSection, + sessionpart); + remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection, + sessionpart); + remoteDtlsParameters.role = 'client'; + } + recvEncodingParameters = + SDPUtils.parseRtpEncodingParameters(mediaSection); + + var rtcpParameters = SDPUtils.parseRtcpParameters(mediaSection); + + var isComplete = SDPUtils.matchPrefix(mediaSection, + 'a=end-of-candidates', sessionpart).length > 0; + var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:') + .map(function(cand) { + return SDPUtils.parseCandidate(cand); + }) + .filter(function(cand) { + return cand.component === 1; + }); + + // Check if we can use BUNDLE and dispose transports. + if ((description.type === 'offer' || description.type === 'answer') && + !rejected && usingBundle && sdpMLineIndex > 0 && + pc.transceivers[sdpMLineIndex]) { + pc._disposeIceAndDtlsTransports(sdpMLineIndex); + pc.transceivers[sdpMLineIndex].iceGatherer = + pc.transceivers[0].iceGatherer; + pc.transceivers[sdpMLineIndex].iceTransport = + pc.transceivers[0].iceTransport; + pc.transceivers[sdpMLineIndex].dtlsTransport = + pc.transceivers[0].dtlsTransport; + if (pc.transceivers[sdpMLineIndex].rtpSender) { + pc.transceivers[sdpMLineIndex].rtpSender.setTransport( + pc.transceivers[0].dtlsTransport); + } + if (pc.transceivers[sdpMLineIndex].rtpReceiver) { + pc.transceivers[sdpMLineIndex].rtpReceiver.setTransport( + pc.transceivers[0].dtlsTransport); + } + } + if (description.type === 'offer' && !rejected) { + transceiver = pc.transceivers[sdpMLineIndex] || + pc._createTransceiver(kind); + transceiver.mid = mid; + + if (!transceiver.iceGatherer) { + transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex, + usingBundle); + } + + if (cands.length && transceiver.iceTransport.state === 'new') { + if (isComplete && (!usingBundle || sdpMLineIndex === 0)) { + transceiver.iceTransport.setRemoteCandidates(cands); + } else { + cands.forEach(function(candidate) { + maybeAddCandidate(transceiver.iceTransport, candidate); + }); + } + } + + localCapabilities = window.RTCRtpReceiver.getCapabilities(kind); + + // filter RTX until additional stuff needed for RTX is implemented + // in adapter.js + if (edgeVersion < 15019) { + localCapabilities.codecs = localCapabilities.codecs.filter( + function(codec) { + return codec.name !== 'rtx'; + }); + } + + sendEncodingParameters = transceiver.sendEncodingParameters || [{ + ssrc: (2 * sdpMLineIndex + 2) * 1001 + }]; + + // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams + var isNewTrack = false; + if (direction === 'sendrecv' || direction === 'sendonly') { + isNewTrack = !transceiver.rtpReceiver; + rtpReceiver = transceiver.rtpReceiver || + new window.RTCRtpReceiver(transceiver.dtlsTransport, kind); + + if (isNewTrack) { + var stream; + track = rtpReceiver.track; + // FIXME: does not work with Plan B. + if (remoteMsid && remoteMsid.stream === '-') { + // no-op. a stream id of '-' means: no associated stream. + } else if (remoteMsid) { + if (!streams[remoteMsid.stream]) { + streams[remoteMsid.stream] = new window.MediaStream(); + Object.defineProperty(streams[remoteMsid.stream], 'id', { + get: function() { + return remoteMsid.stream; + } + }); + } + Object.defineProperty(track, 'id', { + get: function() { + return remoteMsid.track; + } + }); + stream = streams[remoteMsid.stream]; + } else { + if (!streams.default) { + streams.default = new window.MediaStream(); + } + stream = streams.default; + } + if (stream) { + addTrackToStreamAndFireEvent(track, stream); + transceiver.associatedRemoteMediaStreams.push(stream); + } + receiverList.push([track, rtpReceiver, stream]); + } + } else if (transceiver.rtpReceiver && transceiver.rtpReceiver.track) { + transceiver.associatedRemoteMediaStreams.forEach(function(s) { + var nativeTrack = s.getTracks().find(function(t) { + return t.id === transceiver.rtpReceiver.track.id; + }); + if (nativeTrack) { + removeTrackFromStreamAndFireEvent(nativeTrack, s); + } + }); + transceiver.associatedRemoteMediaStreams = []; + } + + transceiver.localCapabilities = localCapabilities; + transceiver.remoteCapabilities = remoteCapabilities; + transceiver.rtpReceiver = rtpReceiver; + transceiver.rtcpParameters = rtcpParameters; + transceiver.sendEncodingParameters = sendEncodingParameters; + transceiver.recvEncodingParameters = recvEncodingParameters; + + // Start the RTCRtpReceiver now. The RTPSender is started in + // setLocalDescription. + pc._transceive(pc.transceivers[sdpMLineIndex], + false, + isNewTrack); + } else if (description.type === 'answer' && !rejected) { + transceiver = pc.transceivers[sdpMLineIndex]; + iceGatherer = transceiver.iceGatherer; + iceTransport = transceiver.iceTransport; + dtlsTransport = transceiver.dtlsTransport; + rtpReceiver = transceiver.rtpReceiver; + sendEncodingParameters = transceiver.sendEncodingParameters; + localCapabilities = transceiver.localCapabilities; + + pc.transceivers[sdpMLineIndex].recvEncodingParameters = + recvEncodingParameters; + pc.transceivers[sdpMLineIndex].remoteCapabilities = + remoteCapabilities; + pc.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters; + + if (cands.length && iceTransport.state === 'new') { + if ((isIceLite || isComplete) && + (!usingBundle || sdpMLineIndex === 0)) { + iceTransport.setRemoteCandidates(cands); + } else { + cands.forEach(function(candidate) { + maybeAddCandidate(transceiver.iceTransport, candidate); + }); + } + } + + if (!usingBundle || sdpMLineIndex === 0) { + if (iceTransport.state === 'new') { + iceTransport.start(iceGatherer, remoteIceParameters, + 'controlling'); + } + if (dtlsTransport.state === 'new') { + dtlsTransport.start(remoteDtlsParameters); + } + } + + pc._transceive(transceiver, + direction === 'sendrecv' || direction === 'recvonly', + direction === 'sendrecv' || direction === 'sendonly'); + + // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams + if (rtpReceiver && + (direction === 'sendrecv' || direction === 'sendonly')) { + track = rtpReceiver.track; + if (remoteMsid) { + if (!streams[remoteMsid.stream]) { + streams[remoteMsid.stream] = new window.MediaStream(); + } + addTrackToStreamAndFireEvent(track, streams[remoteMsid.stream]); + receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]); + } else { + if (!streams.default) { + streams.default = new window.MediaStream(); + } + addTrackToStreamAndFireEvent(track, streams.default); + receiverList.push([track, rtpReceiver, streams.default]); + } + } else { + // FIXME: actually the receiver should be created later. + delete transceiver.rtpReceiver; + } + } + }); + + if (pc._dtlsRole === undefined) { + pc._dtlsRole = description.type === 'offer' ? 'active' : 'passive'; + } + + pc.remoteDescription = { + type: description.type, + sdp: description.sdp + }; + if (description.type === 'offer') { + pc._updateSignalingState('have-remote-offer'); + } else { + pc._updateSignalingState('stable'); + } + Object.keys(streams).forEach(function(sid) { + var stream = streams[sid]; + if (stream.getTracks().length) { + if (pc.remoteStreams.indexOf(stream) === -1) { + pc.remoteStreams.push(stream); + var event = new Event('addstream'); + event.stream = stream; + window.setTimeout(function() { + pc._dispatchEvent('addstream', event); + }); + } + + receiverList.forEach(function(item) { + var track = item[0]; + var receiver = item[1]; + if (stream.id !== item[2].id) { + return; + } + fireAddTrack(pc, track, receiver, [stream]); + }); + } + }); + receiverList.forEach(function(item) { + if (item[2]) { + return; + } + fireAddTrack(pc, item[0], item[1], []); + }); + + // check whether addIceCandidate({}) was called within four seconds after + // setRemoteDescription. + window.setTimeout(function() { + if (!(pc && pc.transceivers)) { + return; + } + pc.transceivers.forEach(function(transceiver) { + if (transceiver.iceTransport && + transceiver.iceTransport.state === 'new' && + transceiver.iceTransport.getRemoteCandidates().length > 0) { + console.warn('Timeout for addRemoteCandidate. Consider sending ' + + 'an end-of-candidates notification'); + transceiver.iceTransport.addRemoteCandidate({}); + } + }); + }, 4000); + + return Promise.resolve(); + }; + + RTCPeerConnection.prototype.close = function() { + this.transceivers.forEach(function(transceiver) { + /* not yet + if (transceiver.iceGatherer) { + transceiver.iceGatherer.close(); + } + */ + if (transceiver.iceTransport) { + transceiver.iceTransport.stop(); + } + if (transceiver.dtlsTransport) { + transceiver.dtlsTransport.stop(); + } + if (transceiver.rtpSender) { + transceiver.rtpSender.stop(); + } + if (transceiver.rtpReceiver) { + transceiver.rtpReceiver.stop(); + } + }); + // FIXME: clean up tracks, local streams, remote streams, etc + this._isClosed = true; + this._updateSignalingState('closed'); + }; + + // Update the signaling state. + RTCPeerConnection.prototype._updateSignalingState = function(newState) { + this.signalingState = newState; + var event = new Event('signalingstatechange'); + this._dispatchEvent('signalingstatechange', event); + }; + + // Determine whether to fire the negotiationneeded event. + RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() { + var pc = this; + if (this.signalingState !== 'stable' || this.needNegotiation === true) { + return; + } + this.needNegotiation = true; + window.setTimeout(function() { + if (pc.needNegotiation) { + pc.needNegotiation = false; + var event = new Event('negotiationneeded'); + pc._dispatchEvent('negotiationneeded', event); + } + }, 0); + }; + + // Update the connection state. + RTCPeerConnection.prototype._updateConnectionState = function() { + var newState; + var states = { + 'new': 0, + closed: 0, + connecting: 0, + checking: 0, + connected: 0, + completed: 0, + disconnected: 0, + failed: 0 + }; + this.transceivers.forEach(function(transceiver) { + states[transceiver.iceTransport.state]++; + states[transceiver.dtlsTransport.state]++; + }); + // ICETransport.completed and connected are the same for this purpose. + states.connected += states.completed; + + newState = 'new'; + if (states.failed > 0) { + newState = 'failed'; + } else if (states.connecting > 0 || states.checking > 0) { + newState = 'connecting'; + } else if (states.disconnected > 0) { + newState = 'disconnected'; + } else if (states.new > 0) { + newState = 'new'; + } else if (states.connected > 0 || states.completed > 0) { + newState = 'connected'; + } + + if (newState !== this.iceConnectionState) { + this.iceConnectionState = newState; + var event = new Event('iceconnectionstatechange'); + this._dispatchEvent('iceconnectionstatechange', event); + } + }; + + RTCPeerConnection.prototype.createOffer = function() { + var pc = this; + + if (pc._isClosed) { + return Promise.reject(makeError('InvalidStateError', + 'Can not call createOffer after close')); + } + + var numAudioTracks = pc.transceivers.filter(function(t) { + return t.kind === 'audio'; + }).length; + var numVideoTracks = pc.transceivers.filter(function(t) { + return t.kind === 'video'; + }).length; + + // Determine number of audio and video tracks we need to send/recv. + var offerOptions = arguments[0]; + if (offerOptions) { + // Reject Chrome legacy constraints. + if (offerOptions.mandatory || offerOptions.optional) { + throw new TypeError( + 'Legacy mandatory/optional constraints not supported.'); + } + if (offerOptions.offerToReceiveAudio !== undefined) { + if (offerOptions.offerToReceiveAudio === true) { + numAudioTracks = 1; + } else if (offerOptions.offerToReceiveAudio === false) { + numAudioTracks = 0; + } else { + numAudioTracks = offerOptions.offerToReceiveAudio; + } + } + if (offerOptions.offerToReceiveVideo !== undefined) { + if (offerOptions.offerToReceiveVideo === true) { + numVideoTracks = 1; + } else if (offerOptions.offerToReceiveVideo === false) { + numVideoTracks = 0; + } else { + numVideoTracks = offerOptions.offerToReceiveVideo; + } + } + } + + pc.transceivers.forEach(function(transceiver) { + if (transceiver.kind === 'audio') { + numAudioTracks--; + if (numAudioTracks < 0) { + transceiver.wantReceive = false; + } + } else if (transceiver.kind === 'video') { + numVideoTracks--; + if (numVideoTracks < 0) { + transceiver.wantReceive = false; + } + } + }); + + // Create M-lines for recvonly streams. + while (numAudioTracks > 0 || numVideoTracks > 0) { + if (numAudioTracks > 0) { + pc._createTransceiver('audio'); + numAudioTracks--; + } + if (numVideoTracks > 0) { + pc._createTransceiver('video'); + numVideoTracks--; + } + } + + var sdp = SDPUtils.writeSessionBoilerplate(pc._sdpSessionId, + pc._sdpSessionVersion++); + pc.transceivers.forEach(function(transceiver, sdpMLineIndex) { + // For each track, create an ice gatherer, ice transport, + // dtls transport, potentially rtpsender and rtpreceiver. + var track = transceiver.track; + var kind = transceiver.kind; + var mid = transceiver.mid || SDPUtils.generateIdentifier(); + transceiver.mid = mid; + + if (!transceiver.iceGatherer) { + transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex, + pc.usingBundle); + } + + var localCapabilities = window.RTCRtpSender.getCapabilities(kind); + // filter RTX until additional stuff needed for RTX is implemented + // in adapter.js + if (edgeVersion < 15019) { + localCapabilities.codecs = localCapabilities.codecs.filter( + function(codec) { + return codec.name !== 'rtx'; + }); + } + localCapabilities.codecs.forEach(function(codec) { + // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552 + // by adding level-asymmetry-allowed=1 + if (codec.name === 'H264' && + codec.parameters['level-asymmetry-allowed'] === undefined) { + codec.parameters['level-asymmetry-allowed'] = '1'; + } + + // for subsequent offers, we might have to re-use the payload + // type of the last offer. + if (transceiver.remoteCapabilities && + transceiver.remoteCapabilities.codecs) { + transceiver.remoteCapabilities.codecs.forEach(function(remoteCodec) { + if (codec.name.toLowerCase() === remoteCodec.name.toLowerCase() && + codec.clockRate === remoteCodec.clockRate) { + codec.preferredPayloadType = remoteCodec.payloadType; + } + }); + } + }); + localCapabilities.headerExtensions.forEach(function(hdrExt) { + var remoteExtensions = transceiver.remoteCapabilities && + transceiver.remoteCapabilities.headerExtensions || []; + remoteExtensions.forEach(function(rHdrExt) { + if (hdrExt.uri === rHdrExt.uri) { + hdrExt.id = rHdrExt.id; + } + }); + }); + + // generate an ssrc now, to be used later in rtpSender.send + var sendEncodingParameters = transceiver.sendEncodingParameters || [{ + ssrc: (2 * sdpMLineIndex + 1) * 1001 + }]; + if (track) { + // add RTX + if (edgeVersion >= 15019 && kind === 'video' && + !sendEncodingParameters[0].rtx) { + sendEncodingParameters[0].rtx = { + ssrc: sendEncodingParameters[0].ssrc + 1 + }; + } + } + + if (transceiver.wantReceive) { + transceiver.rtpReceiver = new window.RTCRtpReceiver( + transceiver.dtlsTransport, kind); + } + + transceiver.localCapabilities = localCapabilities; + transceiver.sendEncodingParameters = sendEncodingParameters; + }); + + // always offer BUNDLE and dispose on return if not supported. + if (pc._config.bundlePolicy !== 'max-compat') { + sdp += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) { + return t.mid; + }).join(' ') + '\r\n'; + } + sdp += 'a=ice-options:trickle\r\n'; + + pc.transceivers.forEach(function(transceiver, sdpMLineIndex) { + sdp += writeMediaSection(transceiver, transceiver.localCapabilities, + 'offer', transceiver.stream, pc._dtlsRole); + sdp += 'a=rtcp-rsize\r\n'; + + if (transceiver.iceGatherer && pc.iceGatheringState !== 'new' && + (sdpMLineIndex === 0 || !pc.usingBundle)) { + transceiver.iceGatherer.getLocalCandidates().forEach(function(cand) { + cand.component = 1; + sdp += 'a=' + SDPUtils.writeCandidate(cand) + '\r\n'; + }); + + if (transceiver.iceGatherer.state === 'completed') { + sdp += 'a=end-of-candidates\r\n'; + } + } + }); + + var desc = new window.RTCSessionDescription({ + type: 'offer', + sdp: sdp + }); + return Promise.resolve(desc); + }; + + RTCPeerConnection.prototype.createAnswer = function() { + var pc = this; + + if (pc._isClosed) { + return Promise.reject(makeError('InvalidStateError', + 'Can not call createAnswer after close')); + } + + var sdp = SDPUtils.writeSessionBoilerplate(pc._sdpSessionId, + pc._sdpSessionVersion++); + if (pc.usingBundle) { + sdp += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) { + return t.mid; + }).join(' ') + '\r\n'; + } + var mediaSectionsInOffer = SDPUtils.getMediaSections( + pc.remoteDescription.sdp).length; + pc.transceivers.forEach(function(transceiver, sdpMLineIndex) { + if (sdpMLineIndex + 1 > mediaSectionsInOffer) { + return; + } + if (transceiver.isDatachannel) { + sdp += 'm=application 0 DTLS/SCTP 5000\r\n' + + 'c=IN IP4 0.0.0.0\r\n' + + 'a=mid:' + transceiver.mid + '\r\n'; + return; + } + + // FIXME: look at direction. + if (transceiver.stream) { + var localTrack; + if (transceiver.kind === 'audio') { + localTrack = transceiver.stream.getAudioTracks()[0]; + } else if (transceiver.kind === 'video') { + localTrack = transceiver.stream.getVideoTracks()[0]; + } + if (localTrack) { + // add RTX + if (edgeVersion >= 15019 && transceiver.kind === 'video' && + !transceiver.sendEncodingParameters[0].rtx) { + transceiver.sendEncodingParameters[0].rtx = { + ssrc: transceiver.sendEncodingParameters[0].ssrc + 1 + }; + } + } + } + + // Calculate intersection of capabilities. + var commonCapabilities = getCommonCapabilities( + transceiver.localCapabilities, + transceiver.remoteCapabilities); + + var hasRtx = commonCapabilities.codecs.filter(function(c) { + return c.name.toLowerCase() === 'rtx'; + }).length; + if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) { + delete transceiver.sendEncodingParameters[0].rtx; + } + + sdp += writeMediaSection(transceiver, commonCapabilities, + 'answer', transceiver.stream, pc._dtlsRole); + if (transceiver.rtcpParameters && + transceiver.rtcpParameters.reducedSize) { + sdp += 'a=rtcp-rsize\r\n'; + } + }); + + var desc = new window.RTCSessionDescription({ + type: 'answer', + sdp: sdp + }); + return Promise.resolve(desc); + }; + + RTCPeerConnection.prototype.addIceCandidate = function(candidate) { + var pc = this; + var sections; + if (candidate && !(candidate.sdpMLineIndex !== undefined || + candidate.sdpMid)) { + return Promise.reject(new TypeError('sdpMLineIndex or sdpMid required')); + } + + // TODO: needs to go into ops queue. + return new Promise(function(resolve, reject) { + if (!pc.remoteDescription) { + return reject(makeError('InvalidStateError', + 'Can not add ICE candidate without a remote description')); + } else if (!candidate || candidate.candidate === '') { + for (var j = 0; j < pc.transceivers.length; j++) { + if (pc.transceivers[j].isDatachannel) { + continue; + } + pc.transceivers[j].iceTransport.addRemoteCandidate({}); + sections = SDPUtils.getMediaSections(pc.remoteDescription.sdp); + sections[j] += 'a=end-of-candidates\r\n'; + pc.remoteDescription.sdp = + SDPUtils.getDescription(pc.remoteDescription.sdp) + + sections.join(''); + if (pc.usingBundle) { + break; + } + } + } else { + var sdpMLineIndex = candidate.sdpMLineIndex; + if (candidate.sdpMid) { + for (var i = 0; i < pc.transceivers.length; i++) { + if (pc.transceivers[i].mid === candidate.sdpMid) { + sdpMLineIndex = i; + break; + } + } + } + var transceiver = pc.transceivers[sdpMLineIndex]; + if (transceiver) { + if (transceiver.isDatachannel) { + return resolve(); + } + var cand = Object.keys(candidate.candidate).length > 0 ? + SDPUtils.parseCandidate(candidate.candidate) : {}; + // Ignore Chrome's invalid candidates since Edge does not like them. + if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) { + return resolve(); + } + // Ignore RTCP candidates, we assume RTCP-MUX. + if (cand.component && cand.component !== 1) { + return resolve(); + } + // when using bundle, avoid adding candidates to the wrong + // ice transport. And avoid adding candidates added in the SDP. + if (sdpMLineIndex === 0 || (sdpMLineIndex > 0 && + transceiver.iceTransport !== pc.transceivers[0].iceTransport)) { + if (!maybeAddCandidate(transceiver.iceTransport, cand)) { + return reject(makeError('OperationError', + 'Can not add ICE candidate')); + } + } + + // update the remoteDescription. + var candidateString = candidate.candidate.trim(); + if (candidateString.indexOf('a=') === 0) { + candidateString = candidateString.substr(2); + } + sections = SDPUtils.getMediaSections(pc.remoteDescription.sdp); + sections[sdpMLineIndex] += 'a=' + + (cand.type ? candidateString : 'end-of-candidates') + + '\r\n'; + pc.remoteDescription.sdp = sections.join(''); + } else { + return reject(makeError('OperationError', + 'Can not add ICE candidate')); + } + } + resolve(); + }); + }; + + RTCPeerConnection.prototype.getStats = function() { + var promises = []; + this.transceivers.forEach(function(transceiver) { + ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport', + 'dtlsTransport'].forEach(function(method) { + if (transceiver[method]) { + promises.push(transceiver[method].getStats()); + } + }); + }); + var fixStatsType = function(stat) { + return { + inboundrtp: 'inbound-rtp', + outboundrtp: 'outbound-rtp', + candidatepair: 'candidate-pair', + localcandidate: 'local-candidate', + remotecandidate: 'remote-candidate' + }[stat.type] || stat.type; + }; + return new Promise(function(resolve) { + // shim getStats with maplike support + var results = new Map(); + Promise.all(promises).then(function(res) { + res.forEach(function(result) { + Object.keys(result).forEach(function(id) { + result[id].type = fixStatsType(result[id]); + results.set(id, result[id]); + }); + }); + resolve(results); + }); + }); + }; + + // legacy callback shims. Should be moved to adapter.js some days. + var methods = ['createOffer', 'createAnswer']; + methods.forEach(function(method) { + var nativeMethod = RTCPeerConnection.prototype[method]; + RTCPeerConnection.prototype[method] = function() { + var args = arguments; + if (typeof args[0] === 'function' || + typeof args[1] === 'function') { // legacy + return nativeMethod.apply(this, [arguments[2]]) + .then(function(description) { + if (typeof args[0] === 'function') { + args[0].apply(null, [description]); + } + }, function(error) { + if (typeof args[1] === 'function') { + args[1].apply(null, [error]); + } + }); + } + return nativeMethod.apply(this, arguments); + }; + }); + + methods = ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']; + methods.forEach(function(method) { + var nativeMethod = RTCPeerConnection.prototype[method]; + RTCPeerConnection.prototype[method] = function() { + var args = arguments; + if (typeof args[1] === 'function' || + typeof args[2] === 'function') { // legacy + return nativeMethod.apply(this, arguments) + .then(function() { + if (typeof args[1] === 'function') { + args[1].apply(null); + } + }, function(error) { + if (typeof args[2] === 'function') { + args[2].apply(null, [error]); + } + }); + } + return nativeMethod.apply(this, arguments); + }; + }); + + // getStats is special. It doesn't have a spec legacy method yet we support + // getStats(something, cb) without error callbacks. + ['getStats'].forEach(function(method) { + var nativeMethod = RTCPeerConnection.prototype[method]; + RTCPeerConnection.prototype[method] = function() { + var args = arguments; + if (typeof args[1] === 'function') { + return nativeMethod.apply(this, arguments) + .then(function() { + if (typeof args[1] === 'function') { + args[1].apply(null); + } + }); + } + return nativeMethod.apply(this, arguments); + }; + }); + + return RTCPeerConnection; +}; + +},{"sdp":46}],46:[function(require,module,exports){ +/* eslint-env node */ +'use strict'; + +// SDP helpers. +var SDPUtils = {}; + +// Generate an alphanumeric identifier for cname or mids. +// TODO: use UUIDs instead? https://gist.github.com/jed/982883 +SDPUtils.generateIdentifier = function() { + return Math.random().toString(36).substr(2, 10); +}; + +// The RTCP CNAME used by all peerconnections from the same JS. +SDPUtils.localCName = SDPUtils.generateIdentifier(); + +// Splits SDP into lines, dealing with both CRLF and LF. +SDPUtils.splitLines = function(blob) { + return blob.trim().split('\n').map(function(line) { + return line.trim(); + }); +}; +// Splits SDP into sessionpart and mediasections. Ensures CRLF. +SDPUtils.splitSections = function(blob) { + var parts = blob.split('\nm='); + return parts.map(function(part, index) { + return (index > 0 ? 'm=' + part : part).trim() + '\r\n'; + }); +}; + +// returns the session description. +SDPUtils.getDescription = function(blob) { + var sections = SDPUtils.splitSections(blob); + return sections && sections[0]; +}; + +// returns the individual media sections. +SDPUtils.getMediaSections = function(blob) { + var sections = SDPUtils.splitSections(blob); + sections.shift(); + return sections; +}; + +// Returns lines that start with a certain prefix. +SDPUtils.matchPrefix = function(blob, prefix) { + return SDPUtils.splitLines(blob).filter(function(line) { + return line.indexOf(prefix) === 0; + }); +}; + +// Parses an ICE candidate line. Sample input: +// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8 +// rport 55996" +SDPUtils.parseCandidate = function(line) { + var parts; + // Parse both variants. + if (line.indexOf('a=candidate:') === 0) { + parts = line.substring(12).split(' '); + } else { + parts = line.substring(10).split(' '); + } + + var candidate = { + foundation: parts[0], + component: parseInt(parts[1], 10), + protocol: parts[2].toLowerCase(), + priority: parseInt(parts[3], 10), + ip: parts[4], + address: parts[4], // address is an alias for ip. + port: parseInt(parts[5], 10), + // skip parts[6] == 'typ' + type: parts[7] + }; + + for (var i = 8; i < parts.length; i += 2) { + switch (parts[i]) { + case 'raddr': + candidate.relatedAddress = parts[i + 1]; + break; + case 'rport': + candidate.relatedPort = parseInt(parts[i + 1], 10); + break; + case 'tcptype': + candidate.tcpType = parts[i + 1]; + break; + case 'ufrag': + candidate.ufrag = parts[i + 1]; // for backward compability. + candidate.usernameFragment = parts[i + 1]; + break; + default: // extension handling, in particular ufrag + candidate[parts[i]] = parts[i + 1]; + break; + } + } + return candidate; +}; + +// Translates a candidate object into SDP candidate attribute. +SDPUtils.writeCandidate = function(candidate) { + var sdp = []; + sdp.push(candidate.foundation); + sdp.push(candidate.component); + sdp.push(candidate.protocol.toUpperCase()); + sdp.push(candidate.priority); + sdp.push(candidate.address || candidate.ip); + sdp.push(candidate.port); + + var type = candidate.type; + sdp.push('typ'); + sdp.push(type); + if (type !== 'host' && candidate.relatedAddress && + candidate.relatedPort) { + sdp.push('raddr'); + sdp.push(candidate.relatedAddress); + sdp.push('rport'); + sdp.push(candidate.relatedPort); + } + if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') { + sdp.push('tcptype'); + sdp.push(candidate.tcpType); + } + if (candidate.usernameFragment || candidate.ufrag) { + sdp.push('ufrag'); + sdp.push(candidate.usernameFragment || candidate.ufrag); + } + return 'candidate:' + sdp.join(' '); +}; + +// Parses an ice-options line, returns an array of option tags. +// a=ice-options:foo bar +SDPUtils.parseIceOptions = function(line) { + return line.substr(14).split(' '); +}; + +// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input: +// a=rtpmap:111 opus/48000/2 +SDPUtils.parseRtpMap = function(line) { + var parts = line.substr(9).split(' '); + var parsed = { + payloadType: parseInt(parts.shift(), 10) // was: id + }; + + parts = parts[0].split('/'); + + parsed.name = parts[0]; + parsed.clockRate = parseInt(parts[1], 10); // was: clockrate + parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1; + // legacy alias, got renamed back to channels in ORTC. + parsed.numChannels = parsed.channels; + return parsed; +}; + +// Generate an a=rtpmap line from RTCRtpCodecCapability or +// RTCRtpCodecParameters. +SDPUtils.writeRtpMap = function(codec) { + var pt = codec.payloadType; + if (codec.preferredPayloadType !== undefined) { + pt = codec.preferredPayloadType; + } + var channels = codec.channels || codec.numChannels || 1; + return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate + + (channels !== 1 ? '/' + channels : '') + '\r\n'; +}; + +// Parses an a=extmap line (headerextension from RFC 5285). Sample input: +// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset +// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset +SDPUtils.parseExtmap = function(line) { + var parts = line.substr(9).split(' '); + return { + id: parseInt(parts[0], 10), + direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv', + uri: parts[1] + }; +}; + +// Generates a=extmap line from RTCRtpHeaderExtensionParameters or +// RTCRtpHeaderExtension. +SDPUtils.writeExtmap = function(headerExtension) { + return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) + + (headerExtension.direction && headerExtension.direction !== 'sendrecv' + ? '/' + headerExtension.direction + : '') + + ' ' + headerExtension.uri + '\r\n'; +}; + +// Parses an ftmp line, returns dictionary. Sample input: +// a=fmtp:96 vbr=on;cng=on +// Also deals with vbr=on; cng=on +SDPUtils.parseFmtp = function(line) { + var parsed = {}; + var kv; + var parts = line.substr(line.indexOf(' ') + 1).split(';'); + for (var j = 0; j < parts.length; j++) { + kv = parts[j].trim().split('='); + parsed[kv[0].trim()] = kv[1]; + } + return parsed; +}; + +// Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters. +SDPUtils.writeFmtp = function(codec) { + var line = ''; + var pt = codec.payloadType; + if (codec.preferredPayloadType !== undefined) { + pt = codec.preferredPayloadType; + } + if (codec.parameters && Object.keys(codec.parameters).length) { + var params = []; + Object.keys(codec.parameters).forEach(function(param) { + if (codec.parameters[param]) { + params.push(param + '=' + codec.parameters[param]); + } else { + params.push(param); + } + }); + line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n'; + } + return line; +}; + +// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input: +// a=rtcp-fb:98 nack rpsi +SDPUtils.parseRtcpFb = function(line) { + var parts = line.substr(line.indexOf(' ') + 1).split(' '); + return { + type: parts.shift(), + parameter: parts.join(' ') + }; +}; +// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters. +SDPUtils.writeRtcpFb = function(codec) { + var lines = ''; + var pt = codec.payloadType; + if (codec.preferredPayloadType !== undefined) { + pt = codec.preferredPayloadType; + } + if (codec.rtcpFeedback && codec.rtcpFeedback.length) { + // FIXME: special handling for trr-int? + codec.rtcpFeedback.forEach(function(fb) { + lines += 'a=rtcp-fb:' + pt + ' ' + fb.type + + (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') + + '\r\n'; + }); + } + return lines; +}; + +// Parses an RFC 5576 ssrc media attribute. Sample input: +// a=ssrc:3735928559 cname:something +SDPUtils.parseSsrcMedia = function(line) { + var sp = line.indexOf(' '); + var parts = { + ssrc: parseInt(line.substr(7, sp - 7), 10) + }; + var colon = line.indexOf(':', sp); + if (colon > -1) { + parts.attribute = line.substr(sp + 1, colon - sp - 1); + parts.value = line.substr(colon + 1); + } else { + parts.attribute = line.substr(sp + 1); + } + return parts; +}; + +SDPUtils.parseSsrcGroup = function(line) { + var parts = line.substr(13).split(' '); + return { + semantics: parts.shift(), + ssrcs: parts.map(function(ssrc) { + return parseInt(ssrc, 10); + }) + }; +}; + +// Extracts the MID (RFC 5888) from a media section. +// returns the MID or undefined if no mid line was found. +SDPUtils.getMid = function(mediaSection) { + var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0]; + if (mid) { + return mid.substr(6); + } +}; + +SDPUtils.parseFingerprint = function(line) { + var parts = line.substr(14).split(' '); + return { + algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge. + value: parts[1] + }; +}; + +// Extracts DTLS parameters from SDP media section or sessionpart. +// FIXME: for consistency with other functions this should only +// get the fingerprint line as input. See also getIceParameters. +SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) { + var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, + 'a=fingerprint:'); + // Note: a=setup line is ignored since we use the 'auto' role. + // Note2: 'algorithm' is not case sensitive except in Edge. + return { + role: 'auto', + fingerprints: lines.map(SDPUtils.parseFingerprint) + }; +}; + +// Serializes DTLS parameters to SDP. +SDPUtils.writeDtlsParameters = function(params, setupType) { + var sdp = 'a=setup:' + setupType + '\r\n'; + params.fingerprints.forEach(function(fp) { + sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n'; + }); + return sdp; +}; + +// Parses a=crypto lines into +// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members +SDPUtils.parseCryptoLine = function(line) { + var parts = line.substr(9).split(' '); + return { + tag: parseInt(parts[0], 10), + cryptoSuite: parts[1], + keyParams: parts[2], + sessionParams: parts.slice(3), + }; +}; + +SDPUtils.writeCryptoLine = function(parameters) { + return 'a=crypto:' + parameters.tag + ' ' + + parameters.cryptoSuite + ' ' + + (typeof parameters.keyParams === 'object' + ? SDPUtils.writeCryptoKeyParams(parameters.keyParams) + : parameters.keyParams) + + (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') + + '\r\n'; +}; + +// Parses the crypto key parameters into +// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam* +SDPUtils.parseCryptoKeyParams = function(keyParams) { + if (keyParams.indexOf('inline:') !== 0) { + return null; + } + var parts = keyParams.substr(7).split('|'); + return { + keyMethod: 'inline', + keySalt: parts[0], + lifeTime: parts[1], + mkiValue: parts[2] ? parts[2].split(':')[0] : undefined, + mkiLength: parts[2] ? parts[2].split(':')[1] : undefined, + }; +}; + +SDPUtils.writeCryptoKeyParams = function(keyParams) { + return keyParams.keyMethod + ':' + + keyParams.keySalt + + (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') + + (keyParams.mkiValue && keyParams.mkiLength + ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength + : ''); +}; + +// Extracts all SDES paramters. +SDPUtils.getCryptoParameters = function(mediaSection, sessionpart) { + var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, + 'a=crypto:'); + return lines.map(SDPUtils.parseCryptoLine); +}; + +// Parses ICE information from SDP media section or sessionpart. +// FIXME: for consistency with other functions this should only +// get the ice-ufrag and ice-pwd lines as input. +SDPUtils.getIceParameters = function(mediaSection, sessionpart) { + var ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart, + 'a=ice-ufrag:')[0]; + var pwd = SDPUtils.matchPrefix(mediaSection + sessionpart, + 'a=ice-pwd:')[0]; + if (!(ufrag && pwd)) { + return null; + } + return { + usernameFragment: ufrag.substr(12), + password: pwd.substr(10), + }; +}; + +// Serializes ICE parameters to SDP. +SDPUtils.writeIceParameters = function(params) { + return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' + + 'a=ice-pwd:' + params.password + '\r\n'; +}; + +// Parses the SDP media section and returns RTCRtpParameters. +SDPUtils.parseRtpParameters = function(mediaSection) { + var description = { + codecs: [], + headerExtensions: [], + fecMechanisms: [], + rtcp: [] + }; + var lines = SDPUtils.splitLines(mediaSection); + var mline = lines[0].split(' '); + for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..] + var pt = mline[i]; + var rtpmapline = SDPUtils.matchPrefix( + mediaSection, 'a=rtpmap:' + pt + ' ')[0]; + if (rtpmapline) { + var codec = SDPUtils.parseRtpMap(rtpmapline); + var fmtps = SDPUtils.matchPrefix( + mediaSection, 'a=fmtp:' + pt + ' '); + // Only the first a=fmtp: is considered. + codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {}; + codec.rtcpFeedback = SDPUtils.matchPrefix( + mediaSection, 'a=rtcp-fb:' + pt + ' ') + .map(SDPUtils.parseRtcpFb); + description.codecs.push(codec); + // parse FEC mechanisms from rtpmap lines. + switch (codec.name.toUpperCase()) { + case 'RED': + case 'ULPFEC': + description.fecMechanisms.push(codec.name.toUpperCase()); + break; + default: // only RED and ULPFEC are recognized as FEC mechanisms. + break; + } + } + } + SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) { + description.headerExtensions.push(SDPUtils.parseExtmap(line)); + }); + // FIXME: parse rtcp. + return description; +}; + +// Generates parts of the SDP media section describing the capabilities / +// parameters. +SDPUtils.writeRtpDescription = function(kind, caps) { + var sdp = ''; + + // Build the mline. + sdp += 'm=' + kind + ' '; + sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs. + sdp += ' UDP/TLS/RTP/SAVPF '; + sdp += caps.codecs.map(function(codec) { + if (codec.preferredPayloadType !== undefined) { + return codec.preferredPayloadType; + } + return codec.payloadType; + }).join(' ') + '\r\n'; + + sdp += 'c=IN IP4 0.0.0.0\r\n'; + sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n'; + + // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb. + caps.codecs.forEach(function(codec) { + sdp += SDPUtils.writeRtpMap(codec); + sdp += SDPUtils.writeFmtp(codec); + sdp += SDPUtils.writeRtcpFb(codec); + }); + var maxptime = 0; + caps.codecs.forEach(function(codec) { + if (codec.maxptime > maxptime) { + maxptime = codec.maxptime; + } + }); + if (maxptime > 0) { + sdp += 'a=maxptime:' + maxptime + '\r\n'; + } + sdp += 'a=rtcp-mux\r\n'; + + if (caps.headerExtensions) { + caps.headerExtensions.forEach(function(extension) { + sdp += SDPUtils.writeExtmap(extension); + }); + } + // FIXME: write fecMechanisms. + return sdp; +}; + +// Parses the SDP media section and returns an array of +// RTCRtpEncodingParameters. +SDPUtils.parseRtpEncodingParameters = function(mediaSection) { + var encodingParameters = []; + var description = SDPUtils.parseRtpParameters(mediaSection); + var hasRed = description.fecMechanisms.indexOf('RED') !== -1; + var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1; + + // filter a=ssrc:... cname:, ignore PlanB-msid + var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') + .map(function(line) { + return SDPUtils.parseSsrcMedia(line); + }) + .filter(function(parts) { + return parts.attribute === 'cname'; + }); + var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc; + var secondarySsrc; + + var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID') + .map(function(line) { + var parts = line.substr(17).split(' '); + return parts.map(function(part) { + return parseInt(part, 10); + }); + }); + if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) { + secondarySsrc = flows[0][1]; + } + + description.codecs.forEach(function(codec) { + if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) { + var encParam = { + ssrc: primarySsrc, + codecPayloadType: parseInt(codec.parameters.apt, 10) + }; + if (primarySsrc && secondarySsrc) { + encParam.rtx = {ssrc: secondarySsrc}; + } + encodingParameters.push(encParam); + if (hasRed) { + encParam = JSON.parse(JSON.stringify(encParam)); + encParam.fec = { + ssrc: primarySsrc, + mechanism: hasUlpfec ? 'red+ulpfec' : 'red' + }; + encodingParameters.push(encParam); + } + } + }); + if (encodingParameters.length === 0 && primarySsrc) { + encodingParameters.push({ + ssrc: primarySsrc + }); + } + + // we support both b=AS and b=TIAS but interpret AS as TIAS. + var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b='); + if (bandwidth.length) { + if (bandwidth[0].indexOf('b=TIAS:') === 0) { + bandwidth = parseInt(bandwidth[0].substr(7), 10); + } else if (bandwidth[0].indexOf('b=AS:') === 0) { + // use formula from JSEP to convert b=AS to TIAS value. + bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95 + - (50 * 40 * 8); + } else { + bandwidth = undefined; + } + encodingParameters.forEach(function(params) { + params.maxBitrate = bandwidth; + }); + } + return encodingParameters; +}; + +// parses http://draft.ortc.org/#rtcrtcpparameters* +SDPUtils.parseRtcpParameters = function(mediaSection) { + var rtcpParameters = {}; + + // Gets the first SSRC. Note tha with RTX there might be multiple + // SSRCs. + var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') + .map(function(line) { + return SDPUtils.parseSsrcMedia(line); + }) + .filter(function(obj) { + return obj.attribute === 'cname'; + })[0]; + if (remoteSsrc) { + rtcpParameters.cname = remoteSsrc.value; + rtcpParameters.ssrc = remoteSsrc.ssrc; + } + + // Edge uses the compound attribute instead of reducedSize + // compound is !reducedSize + var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize'); + rtcpParameters.reducedSize = rsize.length > 0; + rtcpParameters.compound = rsize.length === 0; + + // parses the rtcp-mux attrіbute. + // Note that Edge does not support unmuxed RTCP. + var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux'); + rtcpParameters.mux = mux.length > 0; + + return rtcpParameters; +}; + +// parses either a=msid: or a=ssrc:... msid lines and returns +// the id of the MediaStream and MediaStreamTrack. +SDPUtils.parseMsid = function(mediaSection) { + var parts; + var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:'); + if (spec.length === 1) { + parts = spec[0].substr(7).split(' '); + return {stream: parts[0], track: parts[1]}; + } + var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') + .map(function(line) { + return SDPUtils.parseSsrcMedia(line); + }) + .filter(function(msidParts) { + return msidParts.attribute === 'msid'; + }); + if (planB.length > 0) { + parts = planB[0].value.split(' '); + return {stream: parts[0], track: parts[1]}; + } +}; + +// SCTP +// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back +// to draft-ietf-mmusic-sctp-sdp-05 +SDPUtils.parseSctpDescription = function(mediaSection) { + var mline = SDPUtils.parseMLine(mediaSection); + var maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:'); + var maxMessageSize; + if (maxSizeLine.length > 0) { + maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10); + } + if (isNaN(maxMessageSize)) { + maxMessageSize = 65536; + } + var sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:'); + if (sctpPort.length > 0) { + return { + port: parseInt(sctpPort[0].substr(12), 10), + protocol: mline.fmt, + maxMessageSize: maxMessageSize + }; + } + var sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:'); + if (sctpMapLines.length > 0) { + var parts = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:')[0] + .substr(10) + .split(' '); + return { + port: parseInt(parts[0], 10), + protocol: parts[1], + maxMessageSize: maxMessageSize + }; + } +}; + +// SCTP +// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers +// support by now receiving in this format, unless we originally parsed +// as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line +// protocol of DTLS/SCTP -- without UDP/ or TCP/) +SDPUtils.writeSctpDescription = function(media, sctp) { + var output = []; + if (media.protocol !== 'DTLS/SCTP') { + output = [ + 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\r\n', + 'c=IN IP4 0.0.0.0\r\n', + 'a=sctp-port:' + sctp.port + '\r\n' + ]; + } else { + output = [ + 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\r\n', + 'c=IN IP4 0.0.0.0\r\n', + 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\r\n' + ]; + } + if (sctp.maxMessageSize !== undefined) { + output.push('a=max-message-size:' + sctp.maxMessageSize + '\r\n'); + } + return output.join(''); +}; + +// Generate a session ID for SDP. +// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1 +// recommends using a cryptographically random +ve 64-bit value +// but right now this should be acceptable and within the right range +SDPUtils.generateSessionId = function() { + return Math.random().toString().substr(2, 21); +}; + +// Write boilder plate for start of SDP +// sessId argument is optional - if not supplied it will +// be generated randomly +// sessVersion is optional and defaults to 2 +// sessUser is optional and defaults to 'thisisadapterortc' +SDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) { + var sessionId; + var version = sessVer !== undefined ? sessVer : 2; + if (sessId) { + sessionId = sessId; + } else { + sessionId = SDPUtils.generateSessionId(); + } + var user = sessUser || 'thisisadapterortc'; + // FIXME: sess-id should be an NTP timestamp. + return 'v=0\r\n' + + 'o=' + user + ' ' + sessionId + ' ' + version + + ' IN IP4 127.0.0.1\r\n' + + 's=-\r\n' + + 't=0 0\r\n'; +}; + +SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) { + var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps); + + // Map ICE parameters (ufrag, pwd) to SDP. + sdp += SDPUtils.writeIceParameters( + transceiver.iceGatherer.getLocalParameters()); + + // Map DTLS parameters to SDP. + sdp += SDPUtils.writeDtlsParameters( + transceiver.dtlsTransport.getLocalParameters(), + type === 'offer' ? 'actpass' : 'active'); + + sdp += 'a=mid:' + transceiver.mid + '\r\n'; + + if (transceiver.direction) { + sdp += 'a=' + transceiver.direction + '\r\n'; + } else if (transceiver.rtpSender && transceiver.rtpReceiver) { + sdp += 'a=sendrecv\r\n'; + } else if (transceiver.rtpSender) { + sdp += 'a=sendonly\r\n'; + } else if (transceiver.rtpReceiver) { + sdp += 'a=recvonly\r\n'; + } else { + sdp += 'a=inactive\r\n'; + } + + if (transceiver.rtpSender) { + // spec. + var msid = 'msid:' + stream.id + ' ' + + transceiver.rtpSender.track.id + '\r\n'; + sdp += 'a=' + msid; + + // for Chrome. + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + + ' ' + msid; + if (transceiver.sendEncodingParameters[0].rtx) { + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + + ' ' + msid; + sdp += 'a=ssrc-group:FID ' + + transceiver.sendEncodingParameters[0].ssrc + ' ' + + transceiver.sendEncodingParameters[0].rtx.ssrc + + '\r\n'; + } + } + // FIXME: this should be written by writeRtpDescription. + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + + ' cname:' + SDPUtils.localCName + '\r\n'; + if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) { + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + + ' cname:' + SDPUtils.localCName + '\r\n'; + } + return sdp; +}; + +// Gets the direction from the mediaSection or the sessionpart. +SDPUtils.getDirection = function(mediaSection, sessionpart) { + // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv. + var lines = SDPUtils.splitLines(mediaSection); + for (var i = 0; i < lines.length; i++) { + switch (lines[i]) { + case 'a=sendrecv': + case 'a=sendonly': + case 'a=recvonly': + case 'a=inactive': + return lines[i].substr(2); + default: + // FIXME: What should happen here? + } + } + if (sessionpart) { + return SDPUtils.getDirection(sessionpart); + } + return 'sendrecv'; +}; + +SDPUtils.getKind = function(mediaSection) { + var lines = SDPUtils.splitLines(mediaSection); + var mline = lines[0].split(' '); + return mline[0].substr(2); +}; + +SDPUtils.isRejected = function(mediaSection) { + return mediaSection.split(' ', 2)[1] === '0'; +}; + +SDPUtils.parseMLine = function(mediaSection) { + var lines = SDPUtils.splitLines(mediaSection); + var parts = lines[0].substr(2).split(' '); + return { + kind: parts[0], + port: parseInt(parts[1], 10), + protocol: parts[2], + fmt: parts.slice(3).join(' ') + }; +}; + +SDPUtils.parseOLine = function(mediaSection) { + var line = SDPUtils.matchPrefix(mediaSection, 'o=')[0]; + var parts = line.substr(2).split(' '); + return { + username: parts[0], + sessionId: parts[1], + sessionVersion: parseInt(parts[2], 10), + netType: parts[3], + addressType: parts[4], + address: parts[5] + }; +}; + +// a very naive interpretation of a valid SDP. +SDPUtils.isValidSDP = function(blob) { + if (typeof blob !== 'string' || blob.length === 0) { + return false; + } + var lines = SDPUtils.splitLines(blob); + for (var i = 0; i < lines.length; i++) { + if (lines[i].length < 2 || lines[i].charAt(1) !== '=') { + return false; + } + // TODO: check the modifier a bit more. + } + return true; +}; + +// Expose public methods. +if (typeof module === 'object') { + module.exports = SDPUtils; +} + +},{}]},{},[1]); +; + var Voice = bundle(1); + /* globals define */ + if (typeof define === 'function' && define.amd) { + define([], function() { return Voice; }); + } else { + var Twilio = root.Twilio = root.Twilio || {}; + Twilio.Call = Twilio.Call || Voice.Call; + Twilio.Device = Twilio.Device || Voice.Device; + Twilio.PStream = Twilio.PStream || Voice.PStream; + Twilio.PreflightTest = Twilio.PreflightTest || Voice.PreflightTest; + Twilio.Logger = Twilio.Logger || Voice.Logger; + } +})(typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : this); diff --git a/dist/twilio.min.js b/dist/twilio.min.js new file mode 100644 index 00000000..ff5d6b9f --- /dev/null +++ b/dist/twilio.min.js @@ -0,0 +1 @@ +(function(root){var bundle=function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]0&&options.jitter<=1?options.jitter:0},_max:{value:options.max||1e4},_min:{value:options.min||100},_timeoutID:{value:null,writable:true}});return _this}Backoff.prototype.backoff=function(){var _this=this;var duration=this._duration;if(this._timeoutID){clearTimeout(this._timeoutID);this._timeoutID=null}this.emit("backoff",this._attempts,duration);this._timeoutID=setTimeout(function(){_this.emit("ready",_this._attempts,duration);_this._attempts++},duration)};Backoff.prototype.reset=function(){this._attempts=0;if(this._timeoutID){clearTimeout(this._timeoutID);this._timeoutID=null}};return Backoff}(events_1.EventEmitter);exports.default=Backoff},{events:39}],9:[function(require,module,exports){"use strict";var __extends=this&&this.__extends||function(){var extendStatics=function(d,b){extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(d,b){d.__proto__=b}||function(d,b){for(var p in b)if(Object.prototype.hasOwnProperty.call(b,p))d[p]=b[p]};return extendStatics(d,b)};return function(d,b){extendStatics(d,b);function __(){this.constructor=d}d.prototype=b===null?Object.create(b):(__.prototype=b.prototype,new __)}}();var __assign=this&&this.__assign||function(){__assign=Object.assign||function(t){for(var s,i=1,n=arguments.length;iBACKOFF_CONFIG.max){_this._log.warn("Exceeded max ICE retries");return _this._mediaHandler.onerror(MEDIA_DISCONNECT_ERROR)}try{_this._mediaReconnectBackoff.backoff()}catch(error){if(!(error.message&&error.message==="Backoff in progress.")){throw error}}}return}var pc=_this._mediaHandler.version.pc;var isIceDisconnected=pc&&pc.iceConnectionState==="disconnected";var hasLowBytesWarning=_this._monitor.hasActiveWarning("bytesSent","min")||_this._monitor.hasActiveWarning("bytesReceived","min");if(type===LowBytes&&isIceDisconnected||type===ConnectionDisconnected&&hasLowBytesWarning||isEndOfIceCycle){var mediaReconnectionError=new errors_1.MediaErrors.ConnectionError("Media connection failed.");_this._log.warn("ICE Connection disconnected.");_this._publisher.warn("connection","error",mediaReconnectionError,_this);_this._publisher.info("connection","reconnecting",null,_this);_this._mediaReconnectStartTime=Date.now();_this._status=Call.State.Reconnecting;_this._mediaStatus=Call.State.Reconnecting;_this._mediaReconnectBackoff.reset();_this._mediaReconnectBackoff.backoff();_this._log.debug("#reconnecting");_this.emit("reconnecting",mediaReconnectionError)}};_this._onMediaReconnected=function(){if(_this._mediaStatus!==Call.State.Reconnecting){return}_this._log.info("ICE Connection reestablished.");_this._mediaStatus=Call.State.Open;if(_this._signalingStatus===Call.State.Open){_this._publisher.info("connection","reconnected",null,_this);_this._log.debug("#reconnected");_this.emit("reconnected");_this._status=Call.State.Open}};_this._onMessageReceived=function(payload){var callsid=payload.callsid,content=payload.content,contenttype=payload.contenttype,messagetype=payload.messagetype,voiceeventsid=payload.voiceeventsid;if(_this.parameters.CallSid!==callsid){_this._log.warn("Received a message from a different callsid: "+callsid);return}var data={content:content,contentType:contenttype,messageType:messagetype,voiceEventSid:voiceeventsid};_this._log.debug("#messageReceived",JSON.stringify(data));_this.emit("messageReceived",data)};_this._onMessageSent=function(voiceEventSid){if(!_this._messages.has(voiceEventSid)){_this._log.warn("Received a messageSent with a voiceEventSid that doesn't exists: "+voiceEventSid);return}var message=_this._messages.get(voiceEventSid);_this._messages.delete(voiceEventSid);_this._log.debug("#messageSent",JSON.stringify(message));_this.emit("messageSent",message)};_this._onRinging=function(payload){_this._setCallSid(payload);if(_this._status!==Call.State.Connecting&&_this._status!==Call.State.Ringing){return}var hasEarlyMedia=!!payload.sdp;_this._status=Call.State.Ringing;_this._publisher.info("connection","outgoing-ringing",{hasEarlyMedia:hasEarlyMedia},_this);_this._log.debug("#ringing");_this.emit("ringing",hasEarlyMedia)};_this._onRTCSample=function(sample){var callMetrics=__assign(__assign({},sample),{inputVolume:_this._latestInputVolume,outputVolume:_this._latestOutputVolume});_this._codec=callMetrics.codecName;_this._metricsSamples.push(callMetrics);if(_this._metricsSamples.length>=METRICS_BATCH_SIZE){_this._publishMetrics()}_this.emit("sample",sample)};_this._onSignalingError=function(payload){var callsid=payload.callsid,voiceeventsid=payload.voiceeventsid;if(_this.parameters.CallSid!==callsid){_this._log.warn("Received an error from a different callsid: "+callsid);return}if(voiceeventsid&&_this._messages.has(voiceeventsid)){_this._messages.delete(voiceeventsid);_this._log.warn("Received an error while sending a message.",payload)}};_this._onSignalingReconnected=function(){if(_this._signalingStatus!==Call.State.Reconnecting){return}_this._log.info("Signaling Connection reestablished.");_this._signalingStatus=Call.State.Open;if(_this._mediaStatus===Call.State.Open){_this._publisher.info("connection","reconnected",null,_this);_this._log.debug("#reconnected");_this.emit("reconnected");_this._status=Call.State.Open}};_this._onTransportClose=function(){_this._log.error("Received transportClose from pstream");_this._log.debug("#transportClose");_this.emit("transportClose");if(_this._signalingReconnectToken){_this._status=Call.State.Reconnecting;_this._signalingStatus=Call.State.Reconnecting;_this._log.debug("#reconnecting");_this.emit("reconnecting",new errors_1.SignalingErrors.ConnectionDisconnected)}else{_this._status=Call.State.Closed;_this._signalingStatus=Call.State.Closed}};_this._reemitWarning=function(warningData,wasCleared){var groupPrefix=/^audio/.test(warningData.name)?"audio-level-":"network-quality-";var warningPrefix=WARNING_PREFIXES[warningData.threshold.name];var warningName;if(warningData.name in MULTIPLE_THRESHOLD_WARNING_NAMES){warningName=MULTIPLE_THRESHOLD_WARNING_NAMES[warningData.name][warningData.threshold.name]}else if(warningData.name in WARNING_NAMES){warningName=WARNING_NAMES[warningData.name]}var warning=warningPrefix+warningName;_this._emitWarning(groupPrefix,warning,warningData.threshold.value,warningData.values||warningData.value,wasCleared,warningData)};_this._reemitWarningCleared=function(warningData){_this._reemitWarning(warningData,true)};_this._isUnifiedPlanDefault=config.isUnifiedPlanDefault;_this._soundcache=config.soundcache;if(typeof config.onIgnore==="function"){_this._onIgnore=config.onIgnore}var message=options&&options.twimlParams||{};_this.customParameters=new Map(Object.entries(message).map(function(_a){var key=_a[0],val=_a[1];return[key,String(val)]}));Object.assign(_this._options,options);if(_this._options.callParameters){_this.parameters=_this._options.callParameters}if(_this._options.reconnectToken){_this._signalingReconnectToken=_this._options.reconnectToken}_this._voiceEventSidGenerator=_this._options.voiceEventSidGenerator||uuid_1.generateVoiceEventSid;_this._direction=_this.parameters.CallSid?Call.CallDirection.Incoming:Call.CallDirection.Outgoing;if(_this._direction===Call.CallDirection.Incoming&&_this.parameters){_this.callerInfo=_this.parameters.StirStatus?{isVerified:_this.parameters.StirStatus==="TN-Validation-Passed-A"}:null}else{_this.callerInfo=null}_this._mediaReconnectBackoff=new backoff_1.default(BACKOFF_CONFIG);_this._mediaReconnectBackoff.on("ready",function(){return _this._mediaHandler.iceRestart()});_this.outboundConnectionId=generateTempCallSid();var publisher=_this._publisher=config.publisher;if(_this._direction===Call.CallDirection.Incoming){publisher.info("connection","incoming",null,_this)}else{publisher.info("connection","outgoing",{preflight:_this._options.preflight},_this)}var monitor=_this._monitor=new(_this._options.StatsMonitor||statsMonitor_1.default);monitor.on("sample",_this._onRTCSample);monitor.disableWarnings();setTimeout(function(){return monitor.enableWarnings()},METRICS_DELAY);monitor.on("warning",function(data,wasCleared){if(data.name==="bytesSent"||data.name==="bytesReceived"){_this._onMediaFailure(Call.MediaFailure.LowBytes)}_this._reemitWarning(data,wasCleared)});monitor.on("warning-cleared",function(data){_this._reemitWarningCleared(data)});_this._mediaHandler=new _this._options.MediaHandler(config.audioHelper,config.pstream,{RTCPeerConnection:_this._options.RTCPeerConnection,codecPreferences:_this._options.codecPreferences,dscp:_this._options.dscp,forceAggressiveIceNomination:_this._options.forceAggressiveIceNomination,isUnifiedPlan:_this._isUnifiedPlanDefault,maxAverageBitrate:_this._options.maxAverageBitrate,preflight:_this._options.preflight});_this.on("volume",function(inputVolume,outputVolume){_this._inputVolumeStreak=_this._checkVolume(inputVolume,_this._inputVolumeStreak,_this._latestInputVolume,"input");_this._outputVolumeStreak=_this._checkVolume(outputVolume,_this._outputVolumeStreak,_this._latestOutputVolume,"output");_this._latestInputVolume=inputVolume;_this._latestOutputVolume=outputVolume});_this._mediaHandler.onaudio=function(remoteAudio){_this._log.debug("#audio");_this.emit("audio",remoteAudio)};_this._mediaHandler.onvolume=function(inputVolume,outputVolume,internalInputVolume,internalOutputVolume){monitor.addVolumes(internalInputVolume/255*32767,internalOutputVolume/255*32767);_this.emit("volume",inputVolume,outputVolume)};_this._mediaHandler.ondtlstransportstatechange=function(state){var level=state==="failed"?"error":"debug";_this._publisher.post(level,"dtls-transport-state",state,null,_this)};_this._mediaHandler.onpcconnectionstatechange=function(state){var level="debug";var dtlsTransport=_this._mediaHandler.getRTCDtlsTransport();if(state==="failed"){level=dtlsTransport&&dtlsTransport.state==="failed"?"error":"warning"}_this._publisher.post(level,"pc-connection-state",state,null,_this)};_this._mediaHandler.onicecandidate=function(candidate){var payload=new icecandidate_1.IceCandidate(candidate).toPayload();_this._publisher.debug("ice-candidate","ice-candidate",payload,_this)};_this._mediaHandler.onselectedcandidatepairchange=function(pair){var localCandidatePayload=new icecandidate_1.IceCandidate(pair.local).toPayload();var remoteCandidatePayload=new icecandidate_1.IceCandidate(pair.remote,true).toPayload();_this._publisher.debug("ice-candidate","selected-ice-candidate-pair",{local_candidate:localCandidatePayload,remote_candidate:remoteCandidatePayload},_this)};_this._mediaHandler.oniceconnectionstatechange=function(state){var level=state==="failed"?"error":"debug";_this._publisher.post(level,"ice-connection-state",state,null,_this)};_this._mediaHandler.onicegatheringfailure=function(type){_this._publisher.warn("ice-gathering-state",type,null,_this);_this._onMediaFailure(Call.MediaFailure.IceGatheringFailed)};_this._mediaHandler.onicegatheringstatechange=function(state){_this._publisher.debug("ice-gathering-state",state,null,_this)};_this._mediaHandler.onsignalingstatechange=function(state){_this._publisher.debug("signaling-state",state,null,_this)};_this._mediaHandler.ondisconnected=function(msg){_this._log.warn(msg);_this._publisher.warn("network-quality-warning-raised","ice-connectivity-lost",{message:msg},_this);_this._log.debug("#warning","ice-connectivity-lost");_this.emit("warning","ice-connectivity-lost");_this._onMediaFailure(Call.MediaFailure.ConnectionDisconnected)};_this._mediaHandler.onfailed=function(msg){_this._onMediaFailure(Call.MediaFailure.ConnectionFailed)};_this._mediaHandler.onconnected=function(){if(_this._status===Call.State.Reconnecting){_this._onMediaReconnected()}};_this._mediaHandler.onreconnected=function(msg){_this._log.info(msg);_this._publisher.info("network-quality-warning-cleared","ice-connectivity-lost",{message:msg},_this);_this._log.debug("#warning-cleared","ice-connectivity-lost");_this.emit("warning-cleared","ice-connectivity-lost");_this._onMediaReconnected()};_this._mediaHandler.onerror=function(e){if(e.disconnect===true){_this._disconnect(e.info&&e.info.message)}var error=e.info.twilioError||new errors_1.GeneralErrors.UnknownError(e.info.message);_this._log.error("Received an error from MediaStream:",e);_this._log.debug("#error",error);_this.emit("error",error)};_this._mediaHandler.onopen=function(){if(_this._status===Call.State.Open||_this._status===Call.State.Reconnecting){return}else if(_this._status===Call.State.Ringing||_this._status===Call.State.Connecting){_this.mute(_this._mediaHandler.isMuted);_this._mediaStatus=Call.State.Open;_this._maybeTransitionToOpen()}else{_this._mediaHandler.close()}};_this._mediaHandler.onclose=function(){_this._status=Call.State.Closed;if(_this._options.shouldPlayDisconnect&&_this._options.shouldPlayDisconnect()&&!_this._isCancelled&&!_this._isRejected){_this._soundcache.get(device_1.default.SoundName.Disconnect).play()}monitor.disable();_this._publishMetrics();if(!_this._isCancelled&&!_this._isRejected){_this._log.debug("#disconnect");_this.emit("disconnect",_this)}};_this._pstream=config.pstream;_this._pstream.on("ack",_this._onAck);_this._pstream.on("cancel",_this._onCancel);_this._pstream.on("error",_this._onSignalingError);_this._pstream.on("ringing",_this._onRinging);_this._pstream.on("transportClose",_this._onTransportClose);_this._pstream.on("connected",_this._onConnected);_this._pstream.on("message",_this._onMessageReceived);_this.on("error",function(error){_this._publisher.error("connection","error",{code:error.code,message:error.message},_this);if(_this._pstream&&_this._pstream.status==="disconnected"){_this._cleanupEventListeners()}});_this.on("disconnect",function(){_this._cleanupEventListeners()});return _this}Object.defineProperty(Call.prototype,"direction",{get:function(){return this._direction},enumerable:false,configurable:true});Object.defineProperty(Call.prototype,"codec",{get:function(){return this._codec},enumerable:false,configurable:true});Call.prototype._setInputTracksFromStream=function(stream){return this._mediaHandler.setInputTracksFromStream(stream)};Call.prototype._setSinkIds=function(sinkIds){return this._mediaHandler._setSinkIds(sinkIds)};Call.prototype.accept=function(options){var _this=this;if(this._status!==Call.State.Pending){return}this._log.debug(".accept",options);options=options||{};var rtcConfiguration=options.rtcConfiguration||this._options.rtcConfiguration;var rtcConstraints=options.rtcConstraints||this._options.rtcConstraints||{};var audioConstraints=rtcConstraints.audio||{audio:true};this._status=Call.State.Connecting;var connect=function(){if(_this._status!==Call.State.Connecting){_this._cleanupEventListeners();_this._mediaHandler.close();return}var onAnswer=function(pc,reconnectToken){var eventName=_this._direction===Call.CallDirection.Incoming?"accepted-by-local":"accepted-by-remote";_this._publisher.info("connection",eventName,null,_this);if(typeof reconnectToken==="string"){_this._signalingReconnectToken=reconnectToken}var _a=sdp_1.getPreferredCodecInfo(_this._mediaHandler.version.getSDP()),codecName=_a.codecName,codecParams=_a.codecParams;_this._publisher.info("settings","codec",{codec_params:codecParams,selected_codec:codecName},_this);_this._monitor.enable(pc)};var sinkIds=typeof _this._options.getSinkIds==="function"&&_this._options.getSinkIds();if(Array.isArray(sinkIds)){_this._mediaHandler._setSinkIds(sinkIds).catch(function(){})}_this._pstream.addListener("hangup",_this._onHangup);if(_this._direction===Call.CallDirection.Incoming){_this._isAnswered=true;_this._pstream.on("answer",_this._onAnswer);_this._mediaHandler.answerIncomingCall(_this.parameters.CallSid,_this._options.offerSdp,rtcConstraints,rtcConfiguration,onAnswer)}else{var params=Array.from(_this.customParameters.entries()).map(function(pair){return encodeURIComponent(pair[0])+"="+encodeURIComponent(pair[1])}).join("&");_this._pstream.on("answer",_this._onAnswer);_this._mediaHandler.makeOutgoingCall(_this._pstream.token,params,_this.outboundConnectionId,rtcConstraints,rtcConfiguration,onAnswer)}};if(this._options.beforeAccept){this._options.beforeAccept(this)}var inputStream=typeof this._options.getInputStream==="function"&&this._options.getInputStream();var promise=inputStream?this._mediaHandler.setInputTracksFromStream(inputStream):this._mediaHandler.openDefaultDeviceWithConstraints(audioConstraints);promise.then(function(){_this._publisher.info("get-user-media","succeeded",{data:{audioConstraints:audioConstraints}},_this);connect()},function(error){var twilioError;if(error.code===31208||["PermissionDeniedError","NotAllowedError"].indexOf(error.name)!==-1){twilioError=new errors_1.UserMediaErrors.PermissionDeniedError;_this._publisher.error("get-user-media","denied",{data:{audioConstraints:audioConstraints,error:error}},_this)}else{twilioError=new errors_1.UserMediaErrors.AcquisitionFailedError;_this._publisher.error("get-user-media","failed",{data:{audioConstraints:audioConstraints,error:error}},_this)}_this._disconnect();_this._log.debug("#error",error);_this.emit("error",twilioError)})};Call.prototype.disconnect=function(){this._log.debug(".disconnect");this._disconnect()};Call.prototype.getLocalStream=function(){return this._mediaHandler&&this._mediaHandler.stream};Call.prototype.getRemoteStream=function(){return this._mediaHandler&&this._mediaHandler._remoteStream};Call.prototype.ignore=function(){if(this._status!==Call.State.Pending){return}this._log.debug(".ignore");this._status=Call.State.Closed;this._mediaHandler.ignore(this.parameters.CallSid);this._publisher.info("connection","ignored-by-local",null,this);if(this._onIgnore){this._onIgnore()}};Call.prototype.isMuted=function(){return this._mediaHandler.isMuted};Call.prototype.mute=function(shouldMute){if(shouldMute===void 0){shouldMute=true}this._log.debug(".mute",shouldMute);var wasMuted=this._mediaHandler.isMuted;this._mediaHandler.mute(shouldMute);var isMuted=this._mediaHandler.isMuted;if(wasMuted!==isMuted){this._publisher.info("connection",isMuted?"muted":"unmuted",null,this);this._log.debug("#mute",isMuted);this.emit("mute",isMuted,this)}};Call.prototype.postFeedback=function(score,issue){if(typeof score==="undefined"||score===null){return this._postFeedbackDeclined()}if(!Object.values(Call.FeedbackScore).includes(score)){throw new errors_1.InvalidArgumentError("Feedback score must be one of: "+Object.values(Call.FeedbackScore))}if(typeof issue!=="undefined"&&issue!==null&&!Object.values(Call.FeedbackIssue).includes(issue)){throw new errors_1.InvalidArgumentError("Feedback issue must be one of: "+Object.values(Call.FeedbackIssue))}return this._publisher.info("feedback","received",{issue_name:issue,quality_score:score},this,true)};Call.prototype.reject=function(){if(this._status!==Call.State.Pending){return}this._log.debug(".reject");this._isRejected=true;this._pstream.reject(this.parameters.CallSid);this._mediaHandler.reject(this.parameters.CallSid);this._publisher.info("connection","rejected-by-local",null,this);this._cleanupEventListeners();this._mediaHandler.close();this._status=Call.State.Closed;this._log.debug("#reject");this.emit("reject")};Call.prototype.sendDigits=function(digits){var _this=this;this._log.debug(".sendDigits",digits);if(digits.match(/[^0-9*#w]/)){throw new errors_1.InvalidArgumentError("Illegal character passed into sendDigits")}var customSounds=this._options.customSounds||{};var sequence=[];digits.split("").forEach(function(digit){var dtmf=digit!=="w"?"dtmf"+digit:"";if(dtmf==="dtmf*"){dtmf="dtmfs"}if(dtmf==="dtmf#"){dtmf="dtmfh"}sequence.push(dtmf)});var playNextDigit=function(){var digit=sequence.shift();if(digit){if(_this._options.dialtonePlayer&&!customSounds[digit]){_this._options.dialtonePlayer.play(digit)}else{_this._soundcache.get(digit).play()}}if(sequence.length){setTimeout(function(){return playNextDigit()},200)}};playNextDigit();var dtmfSender=this._mediaHandler.getOrCreateDTMFSender();function insertDTMF(dtmfs){if(!dtmfs.length){return}var dtmf=dtmfs.shift();if(dtmf&&dtmf.length){dtmfSender.insertDTMF(dtmf,DTMF_TONE_DURATION,DTMF_INTER_TONE_GAP)}setTimeout(insertDTMF.bind(null,dtmfs),DTMF_PAUSE_DURATION)}if(dtmfSender){if(!("canInsertDTMF"in dtmfSender)||dtmfSender.canInsertDTMF){this._log.info("Sending digits using RTCDTMFSender");insertDTMF(digits.split("w"));return}this._log.info("RTCDTMFSender cannot insert DTMF")}this._log.info("Sending digits over PStream");if(this._pstream!==null&&this._pstream.status!=="disconnected"){this._pstream.dtmf(this.parameters.CallSid,digits)}else{var error=new errors_1.GeneralErrors.ConnectionError("Could not send DTMF: Signaling channel is disconnected");this._log.debug("#error",error);this.emit("error",error)}};Call.prototype.sendMessage=function(message){this._log.debug(".sendMessage",JSON.stringify(message));var content=message.content,contentType=message.contentType,messageType=message.messageType;if(typeof content==="undefined"||content===null){throw new errors_1.InvalidArgumentError("`content` is empty")}if(typeof messageType!=="string"){throw new errors_1.InvalidArgumentError("`messageType` must be an enumeration value of `Call.MessageType` or "+"a string.")}if(messageType.length===0){throw new errors_1.InvalidArgumentError("`messageType` must be a non-empty string.")}if(this._pstream===null){throw new errors_1.InvalidStateError("Could not send CallMessage; Signaling channel is disconnected")}var callSid=this.parameters.CallSid;if(typeof this.parameters.CallSid==="undefined"){throw new errors_1.InvalidStateError("Could not send CallMessage; Call has no CallSid")}var voiceEventSid=this._voiceEventSidGenerator();this._messages.set(voiceEventSid,{content:content,contentType:contentType,messageType:messageType,voiceEventSid:voiceEventSid});this._pstream.sendMessage(callSid,content,contentType,messageType,voiceEventSid);return voiceEventSid};Call.prototype.status=function(){return this._status};Call.prototype._checkVolume=function(currentVolume,currentStreak,lastValue,direction){var wasWarningRaised=currentStreak>=10;var newStreak=0;if(lastValue===currentVolume){newStreak=currentStreak}if(newStreak>=10){this._emitWarning("audio-level-","constant-audio-"+direction+"-level",10,newStreak,false)}else if(wasWarningRaised){this._emitWarning("audio-level-","constant-audio-"+direction+"-level",10,newStreak,true)}return newStreak};Call.prototype._cleanupEventListeners=function(){var _this=this;var cleanup=function(){if(!_this._pstream){return}_this._pstream.removeListener("ack",_this._onAck);_this._pstream.removeListener("answer",_this._onAnswer);_this._pstream.removeListener("cancel",_this._onCancel);_this._pstream.removeListener("error",_this._onSignalingError);_this._pstream.removeListener("hangup",_this._onHangup);_this._pstream.removeListener("ringing",_this._onRinging);_this._pstream.removeListener("transportClose",_this._onTransportClose);_this._pstream.removeListener("connected",_this._onConnected);_this._pstream.removeListener("message",_this._onMessageReceived)};cleanup();setTimeout(cleanup,0)};Call.prototype._createMetricPayload=function(){var payload={call_sid:this.parameters.CallSid,dscp:!!this._options.dscp,sdk_version:constants_1.RELEASE_VERSION};if(this._options.gateway){payload.gateway=this._options.gateway}payload.direction=this._direction;return payload};Call.prototype._disconnect=function(message,wasRemote){message=typeof message==="string"?message:null;if(this._status!==Call.State.Open&&this._status!==Call.State.Connecting&&this._status!==Call.State.Reconnecting&&this._status!==Call.State.Ringing){return}this._log.info("Disconnecting...");if(this._pstream!==null&&this._pstream.status!=="disconnected"&&this._shouldSendHangup){var callsid=this.parameters.CallSid||this.outboundConnectionId;if(callsid){this._pstream.hangup(callsid,message)}}this._cleanupEventListeners();this._mediaHandler.close();if(!wasRemote){this._publisher.info("connection","disconnected-by-local",null,this)}};Call.prototype._maybeTransitionToOpen=function(){var wasConnected=this._wasConnected;if(this._isAnswered){this._onSignalingReconnected();this._signalingStatus=Call.State.Open;if(this._mediaHandler&&this._mediaHandler.status==="open"){this._status=Call.State.Open;if(!this._wasConnected){this._wasConnected=true;this._log.debug("#accept");this.emit("accept",this)}}}};Call.prototype._postFeedbackDeclined=function(){return this._publisher.info("feedback","received-none",null,this,true)};Call.prototype._publishMetrics=function(){var _this=this;if(this._metricsSamples.length===0){return}this._publisher.postMetrics("quality-metrics-samples","metrics-sample",this._metricsSamples.splice(0),this._createMetricPayload(),this).catch(function(e){_this._log.warn("Unable to post metrics to Insights. Received error:",e)})};Call.prototype._setCallSid=function(payload){var callSid=payload.callsid;if(!callSid){return}this.parameters.CallSid=callSid;this._mediaHandler.callSid=callSid};Call.toString=function(){return"[Twilio.Call class]"};return Call}(events_1.EventEmitter);(function(Call){var State;(function(State){State["Closed"]="closed";State["Connecting"]="connecting";State["Open"]="open";State["Pending"]="pending";State["Reconnecting"]="reconnecting";State["Ringing"]="ringing"})(State=Call.State||(Call.State={}));var FeedbackIssue;(function(FeedbackIssue){FeedbackIssue["AudioLatency"]="audio-latency";FeedbackIssue["ChoppyAudio"]="choppy-audio";FeedbackIssue["DroppedCall"]="dropped-call";FeedbackIssue["Echo"]="echo";FeedbackIssue["NoisyCall"]="noisy-call";FeedbackIssue["OneWayAudio"]="one-way-audio"})(FeedbackIssue=Call.FeedbackIssue||(Call.FeedbackIssue={}));var FeedbackScore;(function(FeedbackScore){FeedbackScore[FeedbackScore["One"]=1]="One";FeedbackScore[FeedbackScore["Two"]=2]="Two";FeedbackScore[FeedbackScore["Three"]=3]="Three";FeedbackScore[FeedbackScore["Four"]=4]="Four";FeedbackScore[FeedbackScore["Five"]=5]="Five"})(FeedbackScore=Call.FeedbackScore||(Call.FeedbackScore={}));var CallDirection;(function(CallDirection){CallDirection["Incoming"]="INCOMING";CallDirection["Outgoing"]="OUTGOING"})(CallDirection=Call.CallDirection||(Call.CallDirection={}));var Codec;(function(Codec){Codec["Opus"]="opus";Codec["PCMU"]="pcmu"})(Codec=Call.Codec||(Call.Codec={}));var IceGatheringFailureReason;(function(IceGatheringFailureReason){IceGatheringFailureReason["None"]="none";IceGatheringFailureReason["Timeout"]="timeout"})(IceGatheringFailureReason=Call.IceGatheringFailureReason||(Call.IceGatheringFailureReason={}));var MediaFailure;(function(MediaFailure){MediaFailure["ConnectionDisconnected"]="ConnectionDisconnected";MediaFailure["ConnectionFailed"]="ConnectionFailed";MediaFailure["IceGatheringFailed"]="IceGatheringFailed";MediaFailure["LowBytes"]="LowBytes"})(MediaFailure=Call.MediaFailure||(Call.MediaFailure={}));var MessageType;(function(MessageType){MessageType["UserDefinedMessage"]="user-defined-message"})(MessageType=Call.MessageType||(Call.MessageType={}))})(Call||(Call={}));function generateTempCallSid(){return"TJSxxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(c){var r=Math.random()*16|0;var v=c==="x"?r:r&3|8;return v.toString(16)})}exports.default=Call},{"./backoff":8,"./constants":10,"./device":12,"./errors":15,"./log":18,"./rtc":26,"./rtc/icecandidate":25,"./rtc/sdp":31,"./statsMonitor":35,"./util":36,"./uuid":37,events:39}],10:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.SOUNDS_BASE_URL=exports.RELEASE_VERSION=exports.PACKAGE_NAME=exports.ECHO_TEST_DURATION=exports.COWBELL_AUDIO_URL=void 0;var PACKAGE_NAME="@twilio/voice-sdk";exports.PACKAGE_NAME=PACKAGE_NAME;var RELEASE_VERSION="2.10.0";exports.RELEASE_VERSION=RELEASE_VERSION;var SOUNDS_BASE_URL="https://sdk.twilio.com/js/client/sounds/releases/1.0.0";exports.SOUNDS_BASE_URL=SOUNDS_BASE_URL;var COWBELL_AUDIO_URL=SOUNDS_BASE_URL+"/cowbell.mp3?cache="+RELEASE_VERSION;exports.COWBELL_AUDIO_URL=COWBELL_AUDIO_URL;var ECHO_TEST_DURATION=2e4;exports.ECHO_TEST_DURATION=ECHO_TEST_DURATION},{}],11:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var Deferred=function(){function Deferred(){var _this=this;this._promise=new Promise(function(resolve,reject){_this._resolve=resolve;_this._reject=reject})}Object.defineProperty(Deferred.prototype,"promise",{get:function(){return this._promise},enumerable:false,configurable:true});Deferred.prototype.reject=function(reason){this._reject(reason)};Deferred.prototype.resolve=function(value){this._resolve(value)};return Deferred}();exports.default=Deferred},{}],12:[function(require,module,exports){"use strict";var __extends=this&&this.__extends||function(){var extendStatics=function(d,b){extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(d,b){d.__proto__=b}||function(d,b){for(var p in b)if(Object.prototype.hasOwnProperty.call(b,p))d[p]=b[p]};return extendStatics(d,b)};return function(d,b){extendStatics(d,b);function __(){this.constructor=d}d.prototype=b===null?Object.create(b):(__.prototype=b.prototype,new __)}}();var __assign=this&&this.__assign||function(){__assign=Object.assign||function(t){for(var s,i=1,n=arguments.length;i0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]0){var preferredURI=preferredURIs[0];_this._preferredURI=regions_1.createSignalingEndpointURL(preferredURI)}else{_this._log.warn("Could not parse a preferred URI from the stream#connected event.")}if(_this._shouldReRegister){_this.register()}};_this._onSignalingError=function(payload){if(typeof payload!=="object"){return}var originalError=payload.error,callsid=payload.callsid;if(typeof originalError!=="object"){return}var call=typeof callsid==="string"&&_this._findCall(callsid)||undefined;var code=originalError.code,customMessage=originalError.message;var twilioError=originalError.twilioError;if(typeof code==="number"){if(code===31201){twilioError=new errors_1.AuthorizationErrors.AuthenticationFailed(originalError)}else if(code===31204){twilioError=new errors_1.AuthorizationErrors.AccessTokenInvalid(originalError)}else if(code===31205){_this._stopRegistrationTimer();twilioError=new errors_1.AuthorizationErrors.AccessTokenExpired(originalError)}else{var errorConstructor=errors_1.getPreciseSignalingErrorByCode(!!_this._options.enableImprovedSignalingErrorPrecision,code);if(typeof errorConstructor!=="undefined"){twilioError=new errorConstructor(originalError)}}}if(!twilioError){_this._log.error("Unknown signaling error: ",originalError);twilioError=new errors_1.GeneralErrors.UnknownError(customMessage,originalError)}_this._log.error("Received error: ",twilioError);_this._log.debug("#error",originalError);_this.emit(Device.EventName.Error,twilioError,call)};_this._onSignalingInvite=function(payload){return __awaiter(_this,void 0,void 0,function(){var wasBusy,callParameters,customParameters,call,play;var _this=this;var _a;return __generator(this,function(_b){switch(_b.label){case 0:wasBusy=!!this._activeCall;if(wasBusy&&!this._options.allowIncomingWhileBusy){this._log.info("Device busy; ignoring incoming invite");return[2]}if(!payload.callsid||!payload.sdp){this._log.debug("#error",payload);this.emit(Device.EventName.Error,new errors_1.ClientErrors.BadRequest("Malformed invite from gateway"));return[2]}callParameters=payload.parameters||{};callParameters.CallSid=callParameters.CallSid||payload.callsid;customParameters=Object.assign({},util_1.queryToJson(callParameters.Params));return[4,this._makeCall(customParameters,{callParameters:callParameters,enableImprovedSignalingErrorPrecision:!!this._options.enableImprovedSignalingErrorPrecision,offerSdp:payload.sdp,reconnectToken:payload.reconnect,voiceEventSidGenerator:this._options.voiceEventSidGenerator})];case 1:call=_b.sent();this._calls.push(call);call.once("accept",function(){_this._soundcache.get(Device.SoundName.Incoming).stop();_this._publishNetworkChange()});play=((_a=this._audio)===null||_a===void 0?void 0:_a.incoming())&&!wasBusy?function(){return _this._soundcache.get(Device.SoundName.Incoming).play()}:function(){return Promise.resolve()};this._showIncomingCall(call,play);return[2]}})})};_this._onSignalingOffline=function(){_this._log.info("Stream is offline");_this._edge=null;_this._region=null;_this._shouldReRegister=_this.state!==Device.State.Unregistered;_this._setState(Device.State.Unregistered)};_this._onSignalingReady=function(){_this._log.info("Stream is ready");_this._setState(Device.State.Registered)};_this._publishNetworkChange=function(){if(!_this._activeCall){return}if(_this._networkInformation){_this._publisher.info("network-information","network-change",{connection_type:_this._networkInformation.type,downlink:_this._networkInformation.downlink,downlinkMax:_this._networkInformation.downlinkMax,effective_type:_this._networkInformation.effectiveType,rtt:_this._networkInformation.rtt},_this._activeCall)}};_this._updateInputStream=function(inputStream){var call=_this._activeCall;if(call&&!inputStream){return Promise.reject(new errors_1.InvalidStateError("Cannot unset input device while a call is in progress."))}_this._callInputStream=inputStream;return call?call._setInputTracksFromStream(inputStream):Promise.resolve()};_this._updateSinkIds=function(type,sinkIds){var promise=type==="ringtone"?_this._updateRingtoneSinkIds(sinkIds):_this._updateSpeakerSinkIds(sinkIds);return promise.then(function(){_this._publisher.info("audio",type+"-devices-set",{audio_device_ids:sinkIds},_this._activeCall)},function(error){_this._publisher.error("audio",type+"-devices-set-failed",{audio_device_ids:sinkIds,message:error.message},_this._activeCall);throw error})};_this._setupLoglevel(options.logLevel);_this._logOptions("constructor",options);_this.updateToken(token);if(util_1.isLegacyEdge()){throw new errors_1.NotSupportedError("Microsoft Edge Legacy (https://support.microsoft.com/en-us/help/4533505/what-is-microsoft-edge-legacy) "+"is deprecated and will not be able to connect to Twilio to make or receive calls after September 1st, 2020. "+"Please see this documentation for a list of supported browsers "+"https://www.twilio.com/docs/voice/client/javascript#supported-browsers")}if(!Device.isSupported&&options.ignoreBrowserSupport){if(window&&window.location&&window.location.protocol==="http:"){throw new errors_1.NotSupportedError("twilio.js wasn't able to find WebRTC browser support. This is most likely because this page is served over http rather than https, which does not support WebRTC in many browsers. Please load this page over https and try again.")}throw new errors_1.NotSupportedError("twilio.js 1.3+ SDKs require WebRTC browser support. For more information, see . If you have any questions about this announcement, please contact Twilio Support at .")}if(window){var root=window;var browser=root.msBrowser||root.browser||root.chrome;_this._isBrowserExtension=!!browser&&!!browser.runtime&&!!browser.runtime.id||!!root.safari&&!!root.safari.extension}if(_this._isBrowserExtension){_this._log.info("Running as browser extension.")}if(navigator){var n=navigator;_this._networkInformation=n.connection||n.mozConnection||n.webkitConnection}if(_this._networkInformation&&typeof _this._networkInformation.addEventListener==="function"){_this._networkInformation.addEventListener("change",_this._publishNetworkChange)}Device._getOrCreateAudioContext();if(Device._audioContext){if(!Device._dialtonePlayer){Device._dialtonePlayer=new dialtonePlayer_1.default(Device._audioContext)}}if(typeof Device._isUnifiedPlanDefault==="undefined"){Device._isUnifiedPlanDefault=typeof window!=="undefined"&&typeof RTCPeerConnection!=="undefined"&&typeof RTCRtpTransceiver!=="undefined"?util_1.isUnifiedPlanDefault(window,window.navigator,RTCPeerConnection,RTCRtpTransceiver):false}_this._boundDestroy=_this.destroy.bind(_this);_this._boundConfirmClose=_this._confirmClose.bind(_this);if(typeof window!=="undefined"&&window.addEventListener){window.addEventListener("unload",_this._boundDestroy);window.addEventListener("pagehide",_this._boundDestroy)}_this.updateOptions(options);return _this}Object.defineProperty(Device,"audioContext",{get:function(){return Device._audioContext},enumerable:false,configurable:true});Object.defineProperty(Device,"extension",{get:function(){var a=typeof document!=="undefined"?document.createElement("audio"):{canPlayType:false};var canPlayMp3;try{canPlayMp3=a.canPlayType&&!!a.canPlayType("audio/mpeg").replace(/no/,"")}catch(e){canPlayMp3=false}var canPlayVorbis;try{canPlayVorbis=a.canPlayType&&!!a.canPlayType("audio/ogg;codecs='vorbis'").replace(/no/,"")}catch(e){canPlayVorbis=false}return canPlayVorbis&&!canPlayMp3?"ogg":"mp3"},enumerable:false,configurable:true});Object.defineProperty(Device,"isSupported",{get:function(){return rtc.enabled()},enumerable:false,configurable:true});Object.defineProperty(Device,"packageName",{get:function(){return C.PACKAGE_NAME},enumerable:false,configurable:true});Device.runPreflight=function(token,options){return new preflight_1.PreflightTest(token,__assign({audioContext:Device._getOrCreateAudioContext()},options))};Device.toString=function(){return"[Twilio.Device class]"};Object.defineProperty(Device,"version",{get:function(){return C.RELEASE_VERSION},enumerable:false,configurable:true});Device._getOrCreateAudioContext=function(){if(!Device._audioContext){if(typeof AudioContext!=="undefined"){Device._audioContext=new AudioContext}else if(typeof webkitAudioContext!=="undefined"){Device._audioContext=new webkitAudioContext}}return Device._audioContext};Object.defineProperty(Device.prototype,"audio",{get:function(){return this._audio},enumerable:false,configurable:true});Device.prototype.connect=function(options){if(options===void 0){options={}}return __awaiter(this,void 0,void 0,function(){var activeCall,_a;return __generator(this,function(_b){switch(_b.label){case 0:this._log.debug(".connect",JSON.stringify(options&&options.params||{}),options);this._throwIfDestroyed();if(this._activeCall){throw new errors_1.InvalidStateError("A Call is already active")}_a=this;return[4,this._makeCall(options.params||{},{enableImprovedSignalingErrorPrecision:!!this._options.enableImprovedSignalingErrorPrecision,rtcConfiguration:options.rtcConfiguration,voiceEventSidGenerator:this._options.voiceEventSidGenerator})];case 1:activeCall=_a._activeCall=_b.sent();this._calls.splice(0).forEach(function(call){return call.ignore()});this._soundcache.get(Device.SoundName.Incoming).stop();activeCall.accept({rtcConstraints:options.rtcConstraints});this._publishNetworkChange();return[2,activeCall]}})})};Object.defineProperty(Device.prototype,"calls",{get:function(){return this._calls},enumerable:false,configurable:true});Device.prototype.destroy=function(){var _a;this._log.debug(".destroy");this.disconnectAll();this._stopRegistrationTimer();this._destroyStream();this._destroyPublisher();this._destroyAudioHelper();(_a=this._audioProcessorEventObserver)===null||_a===void 0?void 0:_a.destroy();if(this._networkInformation&&typeof this._networkInformation.removeEventListener==="function"){this._networkInformation.removeEventListener("change",this._publishNetworkChange)}if(typeof window!=="undefined"&&window.removeEventListener){window.removeEventListener("beforeunload",this._boundConfirmClose);window.removeEventListener("unload",this._boundDestroy);window.removeEventListener("pagehide",this._boundDestroy)}this._setState(Device.State.Destroyed);events_1.EventEmitter.prototype.removeAllListeners.call(this)};Device.prototype.disconnectAll=function(){this._log.debug(".disconnectAll");var calls=this._calls.splice(0);calls.forEach(function(call){return call.disconnect()});if(this._activeCall){this._activeCall.disconnect()}};Object.defineProperty(Device.prototype,"edge",{get:function(){return this._edge},enumerable:false,configurable:true});Object.defineProperty(Device.prototype,"home",{get:function(){return this._home},enumerable:false,configurable:true});Object.defineProperty(Device.prototype,"identity",{get:function(){return this._identity},enumerable:false,configurable:true});Object.defineProperty(Device.prototype,"isBusy",{get:function(){return!!this._activeCall},enumerable:false,configurable:true});Device.prototype.register=function(){return __awaiter(this,void 0,void 0,function(){var stream,streamReadyPromise;var _this=this;return __generator(this,function(_a){switch(_a.label){case 0:this._log.debug(".register");if(this.state!==Device.State.Unregistered){throw new errors_1.InvalidStateError('Attempt to register when device is in state "'+this.state+'". '+('Must be "'+Device.State.Unregistered+'".'))}this._shouldReRegister=false;this._setState(Device.State.Registering);return[4,this._streamConnectedPromise||this._setupStream()];case 1:stream=_a.sent();streamReadyPromise=new Promise(function(resolve){_this.once(Device.State.Registered,resolve)});return[4,this._sendPresence(true)];case 2:_a.sent();return[4,streamReadyPromise];case 3:_a.sent();return[2]}})})};Object.defineProperty(Device.prototype,"state",{get:function(){return this._state},enumerable:false,configurable:true});Object.defineProperty(Device.prototype,"token",{get:function(){return this._token},enumerable:false,configurable:true});Device.prototype.toString=function(){return"[Twilio.Device instance]"};Device.prototype.unregister=function(){return __awaiter(this,void 0,void 0,function(){var stream,streamOfflinePromise;return __generator(this,function(_a){switch(_a.label){case 0:this._log.debug(".unregister");if(this.state!==Device.State.Registered){throw new errors_1.InvalidStateError('Attempt to unregister when device is in state "'+this.state+'". '+('Must be "'+Device.State.Registered+'".'))}this._shouldReRegister=false;return[4,this._streamConnectedPromise];case 1:stream=_a.sent();streamOfflinePromise=new Promise(function(resolve){stream.on("offline",resolve)});return[4,this._sendPresence(false)];case 2:_a.sent();return[4,streamOfflinePromise];case 3:_a.sent();return[2]}})})};Device.prototype.updateOptions=function(options){if(options===void 0){options={}}this._logOptions("updateOptions",options);if(this.state===Device.State.Destroyed){throw new errors_1.InvalidStateError('Attempt to "updateOptions" when device is in state "'+this.state+'".')}this._options=__assign(__assign(__assign({},this._defaultOptions),this._options),options);var originalChunderURIs=new Set(this._chunderURIs);var chunderw=typeof this._options.chunderw==="string"?[this._options.chunderw]:Array.isArray(this._options.chunderw)&&this._options.chunderw;var newChunderURIs=this._chunderURIs=(chunderw||regions_1.getChunderURIs(this._options.edge)).map(regions_1.createSignalingEndpointURL);var hasChunderURIsChanged=originalChunderURIs.size!==newChunderURIs.length;if(!hasChunderURIsChanged){for(var _i=0,newChunderURIs_1=newChunderURIs;_i=0;i--){if(call===this._calls[i]){this._calls.splice(i,1)}}};Device.prototype._sendPresence=function(presence){return __awaiter(this,void 0,void 0,function(){var stream;return __generator(this,function(_a){switch(_a.label){case 0:return[4,this._streamConnectedPromise];case 1:stream=_a.sent();if(!stream){return[2]}stream.register({audio:presence});if(presence){this._startRegistrationTimer()}else{this._stopRegistrationTimer()}return[2]}})})};Device.prototype._setState=function(state){if(state===this.state){return}this._state=state;var name=this._stateEventMapping[state];this._log.debug("#"+name);this.emit(name)};Device.prototype._setupAudioHelper=function(){var _this=this;if(!this._audioProcessorEventObserver){this._audioProcessorEventObserver=new audioprocessoreventobserver_1.AudioProcessorEventObserver;this._audioProcessorEventObserver.on("event",function(_a){var name=_a.name,group=_a.group;_this._publisher.info(group,name,{},_this._activeCall)})}var audioOptions={audioContext:Device.audioContext,audioProcessorEventObserver:this._audioProcessorEventObserver,enumerateDevices:this._options.enumerateDevices,getUserMedia:this._options.getUserMedia||getusermedia_1.default};if(this._audio){this._log.info("Found existing audio helper; updating options...");this._audio._updateUserOptions(audioOptions);return}this._audio=new(this._options.AudioHelper||audiohelper_1.default)(this._updateSinkIds,this._updateInputStream,audioOptions);this._audio.on("deviceChange",function(lostActiveDevices){var activeCall=_this._activeCall;var deviceIds=lostActiveDevices.map(function(device){return device.deviceId});_this._publisher.info("audio","device-change",{lost_active_device_ids:deviceIds},activeCall);if(activeCall){activeCall["_mediaHandler"]._onInputDevicesChanged()}})};Device.prototype._setupLoglevel=function(logLevel){var level=typeof logLevel==="number"||typeof logLevel==="string"?logLevel:loglevel_1.levels.ERROR;this._log.setDefaultLevel(level);this._log.info("Set logger default level to",level)};Device.prototype._setupPublisher=function(){var _this=this;if(this._publisher){this._log.info("Found existing publisher; destroying...");this._destroyPublisher()}var publisherOptions={defaultPayload:this._createDefaultPayload,metadata:{app_name:this._options.appName,app_version:this._options.appVersion}};if(this._options.eventgw){publisherOptions.host=this._options.eventgw}if(this._home){publisherOptions.host=regions_1.createEventGatewayURI(this._home)}this._publisher=new(this._options.Publisher||eventpublisher_1.default)(PUBLISHER_PRODUCT_NAME,this.token,publisherOptions);if(this._options.publishEvents===false){this._publisher.disable()}else{this._publisher.on("error",function(error){_this._log.warn("Cannot connect to insights.",error)})}return this._publisher};Device.prototype._setupStream=function(){var _this=this;if(this._stream){this._log.info("Found existing stream; destroying...");this._destroyStream()}this._log.info("Setting up VSP");this._stream=new(this._options.PStream||pstream_1.default)(this.token,this._chunderURIs,{backoffMaxMs:this._options.backoffMaxMs,maxPreferredDurationMs:this._options.maxCallSignalingTimeoutMs});this._stream.addListener("close",this._onSignalingClose);this._stream.addListener("connected",this._onSignalingConnected);this._stream.addListener("error",this._onSignalingError);this._stream.addListener("invite",this._onSignalingInvite);this._stream.addListener("offline",this._onSignalingOffline);this._stream.addListener("ready",this._onSignalingReady);return this._streamConnectedPromise=new Promise(function(resolve){return _this._stream.once("connected",function(){resolve(_this._stream)})})};Device.prototype._showIncomingCall=function(call,play){var _this=this;var timeout;return Promise.race([play(),new Promise(function(resolve,reject){timeout=setTimeout(function(){var msg="Playing incoming ringtone took too long; it might not play. Continuing execution...";reject(new Error(msg))},RINGTONE_PLAY_TIMEOUT)})]).catch(function(reason){_this._log.warn(reason.message)}).then(function(){clearTimeout(timeout);_this._log.debug("#incoming",JSON.stringify({customParameters:call.customParameters,parameters:call.parameters}));_this.emit(Device.EventName.Incoming,call)})};Device.prototype._startRegistrationTimer=function(){var _this=this;this._stopRegistrationTimer();this._regTimer=setTimeout(function(){_this._sendPresence(true)},REGISTRATION_INTERVAL)};Device.prototype._stopRegistrationTimer=function(){if(this._regTimer){clearTimeout(this._regTimer)}};Device.prototype._throwIfDestroyed=function(){if(this.state===Device.State.Destroyed){throw new errors_1.InvalidStateError("Device has been destroyed.")}};Device.prototype._updateRingtoneSinkIds=function(sinkIds){return Promise.resolve(this._soundcache.get(Device.SoundName.Incoming).setSinkIds(sinkIds))};Device.prototype._updateSpeakerSinkIds=function(sinkIds){Array.from(this._soundcache.entries()).filter(function(entry){return entry[0]!==Device.SoundName.Incoming}).forEach(function(entry){return entry[1].setSinkIds(sinkIds)});this._callSinkIds=sinkIds;var call=this._activeCall;return call?call._setSinkIds(sinkIds):Promise.resolve()};Device._defaultSounds={disconnect:{filename:"disconnect",maxDuration:3e3},dtmf0:{filename:"dtmf-0",maxDuration:1e3},dtmf1:{filename:"dtmf-1",maxDuration:1e3},dtmf2:{filename:"dtmf-2",maxDuration:1e3},dtmf3:{filename:"dtmf-3",maxDuration:1e3},dtmf4:{filename:"dtmf-4",maxDuration:1e3},dtmf5:{filename:"dtmf-5",maxDuration:1e3},dtmf6:{filename:"dtmf-6",maxDuration:1e3},dtmf7:{filename:"dtmf-7",maxDuration:1e3},dtmf8:{filename:"dtmf-8",maxDuration:1e3},dtmf9:{filename:"dtmf-9",maxDuration:1e3},dtmfh:{filename:"dtmf-hash",maxDuration:1e3},dtmfs:{filename:"dtmf-star",maxDuration:1e3},incoming:{filename:"incoming",shouldLoop:true},outgoing:{filename:"outgoing",maxDuration:3e3}};return Device}(events_1.EventEmitter);(function(Device){var EventName;(function(EventName){EventName["Error"]="error";EventName["Incoming"]="incoming";EventName["Destroyed"]="destroyed";EventName["Unregistered"]="unregistered";EventName["Registering"]="registering";EventName["Registered"]="registered";EventName["TokenWillExpire"]="tokenWillExpire"})(EventName=Device.EventName||(Device.EventName={}));var State;(function(State){State["Destroyed"]="destroyed";State["Unregistered"]="unregistered";State["Registering"]="registering";State["Registered"]="registered"})(State=Device.State||(Device.State={}));var SoundName;(function(SoundName){SoundName["Incoming"]="incoming";SoundName["Outgoing"]="outgoing";SoundName["Disconnect"]="disconnect";SoundName["Dtmf0"]="dtmf0";SoundName["Dtmf1"]="dtmf1";SoundName["Dtmf2"]="dtmf2";SoundName["Dtmf3"]="dtmf3";SoundName["Dtmf4"]="dtmf4";SoundName["Dtmf5"]="dtmf5";SoundName["Dtmf6"]="dtmf6";SoundName["Dtmf7"]="dtmf7";SoundName["Dtmf8"]="dtmf8";SoundName["Dtmf9"]="dtmf9";SoundName["DtmfS"]="dtmfs";SoundName["DtmfH"]="dtmfh"})(SoundName=Device.SoundName||(Device.SoundName={}))})(Device||(Device={}));exports.default=Device},{"./audiohelper":3,"./audioprocessoreventobserver":7,"./call":9,"./constants":10,"./dialtonePlayer":13,"./errors":15,"./eventpublisher":17,"./log":18,"./preflight/preflight":20,"./pstream":21,"./regions":22,"./rtc":26,"./rtc/getusermedia":24,"./sound":34,"./util":36,"./uuid":37,events:39,loglevel:43}],13:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var errors_1=require("./errors");var bandFrequencies={dtmf0:[1360,960],dtmf1:[1230,720],dtmf2:[1360,720],dtmf3:[1480,720],dtmf4:[1230,790],dtmf5:[1360,790],dtmf6:[1480,790],dtmf7:[1230,870],dtmf8:[1360,870],dtmf9:[1480,870],dtmfh:[1480,960],dtmfs:[1230,960]};var DialtonePlayer=function(){function DialtonePlayer(_context){var _this=this;this._context=_context;this._gainNodes=[];this._gainNodes=[this._context.createGain(),this._context.createGain()];this._gainNodes.forEach(function(gainNode){gainNode.connect(_this._context.destination);gainNode.gain.value=.1;_this._gainNodes.push(gainNode)})}DialtonePlayer.prototype.cleanup=function(){this._gainNodes.forEach(function(gainNode){gainNode.disconnect()})};DialtonePlayer.prototype.play=function(sound){var _this=this;var frequencies=bandFrequencies[sound];if(!frequencies){throw new errors_1.InvalidArgumentError("Invalid DTMF sound name")}var oscillators=[this._context.createOscillator(),this._context.createOscillator()];oscillators.forEach(function(oscillator,i){oscillator.type="sine";oscillator.frequency.value=frequencies[i];oscillator.connect(_this._gainNodes[i]);oscillator.start();oscillator.stop(_this._context.currentTime+.1);oscillator.addEventListener("ended",function(){return oscillator.disconnect()})})};return DialtonePlayer}();exports.default=DialtonePlayer},{"./errors":15}],14:[function(require,module,exports){"use strict";var __extends=this&&this.__extends||function(){var extendStatics=function(d,b){extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(d,b){d.__proto__=b}||function(d,b){for(var p in b)if(Object.prototype.hasOwnProperty.call(b,p))d[p]=b[p]};return extendStatics(d,b)};return function(d,b){extendStatics(d,b);function __(){this.constructor=d}d.prototype=b===null?Object.create(b):(__.prototype=b.prototype,new __)}}();Object.defineProperty(exports,"__esModule",{value:true});exports.errorsByCode=exports.MediaErrors=exports.SignalingErrors=exports.UserMediaErrors=exports.MalformedRequestErrors=exports.GeneralErrors=exports.SIPServerErrors=exports.ClientErrors=exports.SignatureValidationErrors=exports.AuthorizationErrors=exports.TwilioError=void 0;var twilioError_1=require("./twilioError");exports.TwilioError=twilioError_1.default;var AuthorizationErrors;(function(AuthorizationErrors){var AccessTokenInvalid=function(_super){__extends(AccessTokenInvalid,_super);function AccessTokenInvalid(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=20101;_this.description="Invalid access token";_this.explanation="Twilio was unable to validate your Access Token";_this.name="AccessTokenInvalid";_this.solutions=[];Object.setPrototypeOf(_this,AuthorizationErrors.AccessTokenInvalid.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return AccessTokenInvalid}(twilioError_1.default);AuthorizationErrors.AccessTokenInvalid=AccessTokenInvalid;var AccessTokenExpired=function(_super){__extends(AccessTokenExpired,_super);function AccessTokenExpired(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=20104;_this.description="Access token expired or expiration date invalid";_this.explanation="The Access Token provided to the Twilio API has expired, the expiration time specified in the token was invalid, or the expiration time specified was too far in the future";_this.name="AccessTokenExpired";_this.solutions=[];Object.setPrototypeOf(_this,AuthorizationErrors.AccessTokenExpired.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return AccessTokenExpired}(twilioError_1.default);AuthorizationErrors.AccessTokenExpired=AccessTokenExpired;var AuthenticationFailed=function(_super){__extends(AuthenticationFailed,_super);function AuthenticationFailed(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=20151;_this.description="Authentication Failed";_this.explanation="The Authentication with the provided JWT failed";_this.name="AuthenticationFailed";_this.solutions=[];Object.setPrototypeOf(_this,AuthorizationErrors.AuthenticationFailed.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return AuthenticationFailed}(twilioError_1.default);AuthorizationErrors.AuthenticationFailed=AuthenticationFailed})(AuthorizationErrors=exports.AuthorizationErrors||(exports.AuthorizationErrors={}));var SignatureValidationErrors;(function(SignatureValidationErrors){var AccessTokenSignatureValidationFailed=function(_super){__extends(AccessTokenSignatureValidationFailed,_super);function AccessTokenSignatureValidationFailed(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=["The access token has an invalid Account SID, API Key, or API Key Secret."];_this.code=31202;_this.description="Signature validation failed.";_this.explanation="The provided access token failed signature validation.";_this.name="AccessTokenSignatureValidationFailed";_this.solutions=["Ensure the Account SID, API Key, and API Key Secret are valid when generating your access token."];Object.setPrototypeOf(_this,SignatureValidationErrors.AccessTokenSignatureValidationFailed.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return AccessTokenSignatureValidationFailed}(twilioError_1.default);SignatureValidationErrors.AccessTokenSignatureValidationFailed=AccessTokenSignatureValidationFailed})(SignatureValidationErrors=exports.SignatureValidationErrors||(exports.SignatureValidationErrors={}));var ClientErrors;(function(ClientErrors){var BadRequest=function(_super){__extends(BadRequest,_super);function BadRequest(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31400;_this.description="Bad Request (HTTP/SIP)";_this.explanation="The request could not be understood due to malformed syntax.";_this.name="BadRequest";_this.solutions=[];Object.setPrototypeOf(_this,ClientErrors.BadRequest.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return BadRequest}(twilioError_1.default);ClientErrors.BadRequest=BadRequest;var NotFound=function(_super){__extends(NotFound,_super);function NotFound(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=["The outbound call was made to an invalid phone number.","The TwiML application sid is missing a Voice URL."];_this.code=31404;_this.description="Not Found (HTTP/SIP)";_this.explanation="The server has not found anything matching the request.";_this.name="NotFound";_this.solutions=["Ensure the phone number dialed is valid.","Ensure the TwiML application is configured correctly with a Voice URL link."];Object.setPrototypeOf(_this,ClientErrors.NotFound.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return NotFound}(twilioError_1.default);ClientErrors.NotFound=NotFound;var TemporarilyUnavailable=function(_super){__extends(TemporarilyUnavailable,_super);function TemporarilyUnavailable(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31480;_this.description="Temporarily Unavailable (SIP)";_this.explanation="The callee is currently unavailable.";_this.name="TemporarilyUnavailable";_this.solutions=[];Object.setPrototypeOf(_this,ClientErrors.TemporarilyUnavailable.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return TemporarilyUnavailable}(twilioError_1.default);ClientErrors.TemporarilyUnavailable=TemporarilyUnavailable;var BusyHere=function(_super){__extends(BusyHere,_super);function BusyHere(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31486;_this.description="Busy Here (SIP)";_this.explanation="The callee is busy.";_this.name="BusyHere";_this.solutions=[];Object.setPrototypeOf(_this,ClientErrors.BusyHere.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return BusyHere}(twilioError_1.default);ClientErrors.BusyHere=BusyHere})(ClientErrors=exports.ClientErrors||(exports.ClientErrors={}));var SIPServerErrors;(function(SIPServerErrors){var Decline=function(_super){__extends(Decline,_super);function Decline(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31603;_this.description="Decline (SIP)";_this.explanation="The callee does not wish to participate in the call.";_this.name="Decline";_this.solutions=[];Object.setPrototypeOf(_this,SIPServerErrors.Decline.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return Decline}(twilioError_1.default);SIPServerErrors.Decline=Decline})(SIPServerErrors=exports.SIPServerErrors||(exports.SIPServerErrors={}));var GeneralErrors;(function(GeneralErrors){var UnknownError=function(_super){__extends(UnknownError,_super);function UnknownError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31e3;_this.description="Unknown Error";_this.explanation="An unknown error has occurred. See error details for more information.";_this.name="UnknownError";_this.solutions=[];Object.setPrototypeOf(_this,GeneralErrors.UnknownError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return UnknownError}(twilioError_1.default);GeneralErrors.UnknownError=UnknownError;var ApplicationNotFoundError=function(_super){__extends(ApplicationNotFoundError,_super);function ApplicationNotFoundError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31001;_this.description="Application Not Found";_this.explanation="";_this.name="ApplicationNotFoundError";_this.solutions=[];Object.setPrototypeOf(_this,GeneralErrors.ApplicationNotFoundError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return ApplicationNotFoundError}(twilioError_1.default);GeneralErrors.ApplicationNotFoundError=ApplicationNotFoundError;var ConnectionDeclinedError=function(_super){__extends(ConnectionDeclinedError,_super);function ConnectionDeclinedError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31002;_this.description="Connection Declined";_this.explanation="";_this.name="ConnectionDeclinedError";_this.solutions=[];Object.setPrototypeOf(_this,GeneralErrors.ConnectionDeclinedError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return ConnectionDeclinedError}(twilioError_1.default);GeneralErrors.ConnectionDeclinedError=ConnectionDeclinedError;var ConnectionTimeoutError=function(_super){__extends(ConnectionTimeoutError,_super);function ConnectionTimeoutError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31003;_this.description="Connection Timeout";_this.explanation="The server could not produce a response within a suitable amount of time.";_this.name="ConnectionTimeoutError";_this.solutions=[];Object.setPrototypeOf(_this,GeneralErrors.ConnectionTimeoutError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return ConnectionTimeoutError}(twilioError_1.default);GeneralErrors.ConnectionTimeoutError=ConnectionTimeoutError;var ConnectionError=function(_super){__extends(ConnectionError,_super);function ConnectionError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31005;_this.description="Connection error";_this.explanation="A connection error occurred during the call";_this.name="ConnectionError";_this.solutions=[];Object.setPrototypeOf(_this,GeneralErrors.ConnectionError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return ConnectionError}(twilioError_1.default);GeneralErrors.ConnectionError=ConnectionError;var CallCancelledError=function(_super){__extends(CallCancelledError,_super);function CallCancelledError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=["The incoming call was cancelled because it was not answered in time or it was accepted/rejected by another application instance registered with the same identity."];_this.code=31008;_this.description="Call cancelled";_this.explanation="Unable to answer because the call has ended";_this.name="CallCancelledError";_this.solutions=[];Object.setPrototypeOf(_this,GeneralErrors.CallCancelledError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return CallCancelledError}(twilioError_1.default);GeneralErrors.CallCancelledError=CallCancelledError;var TransportError=function(_super){__extends(TransportError,_super);function TransportError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31009;_this.description="Transport error";_this.explanation="No transport available to send or receive messages";_this.name="TransportError";_this.solutions=[];Object.setPrototypeOf(_this,GeneralErrors.TransportError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return TransportError}(twilioError_1.default);GeneralErrors.TransportError=TransportError})(GeneralErrors=exports.GeneralErrors||(exports.GeneralErrors={}));var MalformedRequestErrors;(function(MalformedRequestErrors){var MalformedRequestError=function(_super){__extends(MalformedRequestError,_super);function MalformedRequestError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=["Invalid content or MessageType passed to sendMessage method."];_this.code=31100;_this.description="The request had malformed syntax.";_this.explanation="The request could not be understood due to malformed syntax.";_this.name="MalformedRequestError";_this.solutions=["Ensure content and MessageType passed to sendMessage method are valid."];Object.setPrototypeOf(_this,MalformedRequestErrors.MalformedRequestError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return MalformedRequestError}(twilioError_1.default);MalformedRequestErrors.MalformedRequestError=MalformedRequestError;var MissingParameterArrayError=function(_super){__extends(MissingParameterArrayError,_super);function MissingParameterArrayError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31101;_this.description="Missing parameter array in request";_this.explanation="";_this.name="MissingParameterArrayError";_this.solutions=[];Object.setPrototypeOf(_this,MalformedRequestErrors.MissingParameterArrayError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return MissingParameterArrayError}(twilioError_1.default);MalformedRequestErrors.MissingParameterArrayError=MissingParameterArrayError;var AuthorizationTokenMissingError=function(_super){__extends(AuthorizationTokenMissingError,_super);function AuthorizationTokenMissingError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31102;_this.description="Authorization token missing in request.";_this.explanation="";_this.name="AuthorizationTokenMissingError";_this.solutions=[];Object.setPrototypeOf(_this,MalformedRequestErrors.AuthorizationTokenMissingError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return AuthorizationTokenMissingError}(twilioError_1.default);MalformedRequestErrors.AuthorizationTokenMissingError=AuthorizationTokenMissingError;var MaxParameterLengthExceededError=function(_super){__extends(MaxParameterLengthExceededError,_super);function MaxParameterLengthExceededError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31103;_this.description="Maximum parameter length has been exceeded.";_this.explanation="Length of parameters cannot exceed MAX_PARAM_LENGTH.";_this.name="MaxParameterLengthExceededError";_this.solutions=[];Object.setPrototypeOf(_this,MalformedRequestErrors.MaxParameterLengthExceededError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return MaxParameterLengthExceededError}(twilioError_1.default);MalformedRequestErrors.MaxParameterLengthExceededError=MaxParameterLengthExceededError;var InvalidBridgeTokenError=function(_super){__extends(InvalidBridgeTokenError,_super);function InvalidBridgeTokenError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31104;_this.description="Invalid bridge token";_this.explanation="";_this.name="InvalidBridgeTokenError";_this.solutions=[];Object.setPrototypeOf(_this,MalformedRequestErrors.InvalidBridgeTokenError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return InvalidBridgeTokenError}(twilioError_1.default);MalformedRequestErrors.InvalidBridgeTokenError=InvalidBridgeTokenError;var InvalidClientNameError=function(_super){__extends(InvalidClientNameError,_super);function InvalidClientNameError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=["Client name contains invalid characters."];_this.code=31105;_this.description="Invalid client name";_this.explanation="Client name should not contain control, space, delims, or unwise characters.";_this.name="InvalidClientNameError";_this.solutions=["Make sure that client name does not contain any of the invalid characters."];Object.setPrototypeOf(_this,MalformedRequestErrors.InvalidClientNameError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return InvalidClientNameError}(twilioError_1.default);MalformedRequestErrors.InvalidClientNameError=InvalidClientNameError;var ReconnectParameterInvalidError=function(_super){__extends(ReconnectParameterInvalidError,_super);function ReconnectParameterInvalidError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31107;_this.description="The reconnect parameter is invalid";_this.explanation="";_this.name="ReconnectParameterInvalidError";_this.solutions=[];Object.setPrototypeOf(_this,MalformedRequestErrors.ReconnectParameterInvalidError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return ReconnectParameterInvalidError}(twilioError_1.default);MalformedRequestErrors.ReconnectParameterInvalidError=ReconnectParameterInvalidError})(MalformedRequestErrors=exports.MalformedRequestErrors||(exports.MalformedRequestErrors={}));(function(AuthorizationErrors){var AuthorizationError=function(_super){__extends(AuthorizationError,_super);function AuthorizationError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31201;_this.description="Authorization error";_this.explanation="The request requires user authentication. The server understood the request, but is refusing to fulfill it.";_this.name="AuthorizationError";_this.solutions=[];Object.setPrototypeOf(_this,AuthorizationErrors.AuthorizationError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return AuthorizationError}(twilioError_1.default);AuthorizationErrors.AuthorizationError=AuthorizationError;var NoValidAccountError=function(_super){__extends(NoValidAccountError,_super);function NoValidAccountError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31203;_this.description="No valid account";_this.explanation="";_this.name="NoValidAccountError";_this.solutions=[];Object.setPrototypeOf(_this,AuthorizationErrors.NoValidAccountError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return NoValidAccountError}(twilioError_1.default);AuthorizationErrors.NoValidAccountError=NoValidAccountError;var InvalidJWTTokenError=function(_super){__extends(InvalidJWTTokenError,_super);function InvalidJWTTokenError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31204;_this.description="Invalid JWT token";_this.explanation="";_this.name="InvalidJWTTokenError";_this.solutions=[];Object.setPrototypeOf(_this,AuthorizationErrors.InvalidJWTTokenError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return InvalidJWTTokenError}(twilioError_1.default);AuthorizationErrors.InvalidJWTTokenError=InvalidJWTTokenError;var JWTTokenExpiredError=function(_super){__extends(JWTTokenExpiredError,_super);function JWTTokenExpiredError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31205;_this.description="JWT token expired";_this.explanation="";_this.name="JWTTokenExpiredError";_this.solutions=[];Object.setPrototypeOf(_this,AuthorizationErrors.JWTTokenExpiredError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return JWTTokenExpiredError}(twilioError_1.default);AuthorizationErrors.JWTTokenExpiredError=JWTTokenExpiredError;var RateExceededError=function(_super){__extends(RateExceededError,_super);function RateExceededError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=["Rate limit exceeded."];_this.code=31206;_this.description="Rate exceeded authorized limit.";_this.explanation="The request performed exceeds the authorized limit.";_this.name="RateExceededError";_this.solutions=["Ensure message send rate does not exceed authorized limits."];Object.setPrototypeOf(_this,AuthorizationErrors.RateExceededError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return RateExceededError}(twilioError_1.default);AuthorizationErrors.RateExceededError=RateExceededError;var JWTTokenExpirationTooLongError=function(_super){__extends(JWTTokenExpirationTooLongError,_super);function JWTTokenExpirationTooLongError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=31207;_this.description="JWT token expiration too long";_this.explanation="";_this.name="JWTTokenExpirationTooLongError";_this.solutions=[];Object.setPrototypeOf(_this,AuthorizationErrors.JWTTokenExpirationTooLongError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return JWTTokenExpirationTooLongError}(twilioError_1.default);AuthorizationErrors.JWTTokenExpirationTooLongError=JWTTokenExpirationTooLongError;var PayloadSizeExceededError=function(_super){__extends(PayloadSizeExceededError,_super);function PayloadSizeExceededError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=["The payload size of Call Message Event exceeds the authorized limit."];_this.code=31209;_this.description="Call Message Event Payload size exceeded authorized limit.";_this.explanation="The request performed to send a Call Message Event exceeds the payload size authorized limit";_this.name="PayloadSizeExceededError";_this.solutions=["Reduce payload size of Call Message Event to be within the authorized limit and try again."];Object.setPrototypeOf(_this,AuthorizationErrors.PayloadSizeExceededError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return PayloadSizeExceededError}(twilioError_1.default);AuthorizationErrors.PayloadSizeExceededError=PayloadSizeExceededError})(AuthorizationErrors=exports.AuthorizationErrors||(exports.AuthorizationErrors={}));var UserMediaErrors;(function(UserMediaErrors){var PermissionDeniedError=function(_super){__extends(PermissionDeniedError,_super);function PermissionDeniedError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=["The user denied the getUserMedia request.","The browser denied the getUserMedia request."];_this.code=31401;_this.description="UserMedia Permission Denied Error";_this.explanation="The browser or end-user denied permissions to user media. Therefore we were unable to acquire input audio.";_this.name="PermissionDeniedError";_this.solutions=["The user should accept the request next time prompted. If the browser saved the deny, the user should change that permission in their browser.","The user should to verify that the browser has permission to access the microphone at this address."];Object.setPrototypeOf(_this,UserMediaErrors.PermissionDeniedError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return PermissionDeniedError}(twilioError_1.default);UserMediaErrors.PermissionDeniedError=PermissionDeniedError;var AcquisitionFailedError=function(_super){__extends(AcquisitionFailedError,_super);function AcquisitionFailedError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=["NotFoundError - The deviceID specified was not found.","The getUserMedia constraints were overconstrained and no devices matched."];_this.code=31402;_this.description="UserMedia Acquisition Failed Error";_this.explanation="The browser and end-user allowed permissions, however getting the media failed. Usually this is due to bad constraints, but can sometimes fail due to browser, OS or hardware issues.";_this.name="AcquisitionFailedError";_this.solutions=["Ensure the deviceID being specified exists.","Try acquiring media with fewer constraints."];Object.setPrototypeOf(_this,UserMediaErrors.AcquisitionFailedError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return AcquisitionFailedError}(twilioError_1.default);UserMediaErrors.AcquisitionFailedError=AcquisitionFailedError})(UserMediaErrors=exports.UserMediaErrors||(exports.UserMediaErrors={}));var SignalingErrors;(function(SignalingErrors){var ConnectionError=function(_super){__extends(ConnectionError,_super);function ConnectionError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=[];_this.code=53e3;_this.description="Signaling connection error";_this.explanation="Raised whenever a signaling connection error occurs that is not covered by a more specific error code.";_this.name="ConnectionError";_this.solutions=[];Object.setPrototypeOf(_this,SignalingErrors.ConnectionError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return ConnectionError}(twilioError_1.default);SignalingErrors.ConnectionError=ConnectionError;var ConnectionDisconnected=function(_super){__extends(ConnectionDisconnected,_super);function ConnectionDisconnected(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=["The device running your application lost its Internet connection."];_this.code=53001;_this.description="Signaling connection disconnected";_this.explanation="Raised whenever the signaling connection is unexpectedly disconnected.";_this.name="ConnectionDisconnected";_this.solutions=["Ensure the device running your application has access to a stable Internet connection."];Object.setPrototypeOf(_this,SignalingErrors.ConnectionDisconnected.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return ConnectionDisconnected}(twilioError_1.default);SignalingErrors.ConnectionDisconnected=ConnectionDisconnected})(SignalingErrors=exports.SignalingErrors||(exports.SignalingErrors={}));var MediaErrors;(function(MediaErrors){var ClientLocalDescFailed=function(_super){__extends(ClientLocalDescFailed,_super);function ClientLocalDescFailed(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=["The Client may not be using a supported WebRTC implementation.","The Client may not have the necessary resources to create or apply a new media description."];_this.code=53400;_this.description="Client is unable to create or apply a local media description";_this.explanation="Raised whenever a Client is unable to create or apply a local media description.";_this.name="ClientLocalDescFailed";_this.solutions=["If you are experiencing this error using the JavaScript SDK, ensure you are running it with a supported WebRTC implementation."];Object.setPrototypeOf(_this,MediaErrors.ClientLocalDescFailed.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return ClientLocalDescFailed}(twilioError_1.default);MediaErrors.ClientLocalDescFailed=ClientLocalDescFailed;var ClientRemoteDescFailed=function(_super){__extends(ClientRemoteDescFailed,_super);function ClientRemoteDescFailed(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=["The Client may not be using a supported WebRTC implementation.","The Client may be connecting peer-to-peer with another Participant that is not using a supported WebRTC implementation.","The Client may not have the necessary resources to apply a new media description."];_this.code=53402;_this.description="Client is unable to apply a remote media description";_this.explanation="Raised whenever the Client receives a remote media description but is unable to apply it.";_this.name="ClientRemoteDescFailed";_this.solutions=["If you are experiencing this error using the JavaScript SDK, ensure you are running it with a supported WebRTC implementation."];Object.setPrototypeOf(_this,MediaErrors.ClientRemoteDescFailed.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return ClientRemoteDescFailed}(twilioError_1.default);MediaErrors.ClientRemoteDescFailed=ClientRemoteDescFailed;var ConnectionError=function(_super){__extends(ConnectionError,_super);function ConnectionError(messageOrError,error){var _this=_super.call(this,messageOrError,error)||this;_this.causes=["The Client was unable to establish a media connection.","A media connection which was active failed liveliness checks."];_this.code=53405;_this.description="Media connection failed";_this.explanation="Raised by the Client or Server whenever a media connection fails.";_this.name="ConnectionError";_this.solutions=["If the problem persists, try connecting to another region.","Check your Client's network connectivity.","If you've provided custom ICE Servers then ensure that the URLs and credentials are valid."];Object.setPrototypeOf(_this,MediaErrors.ConnectionError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return ConnectionError}(twilioError_1.default);MediaErrors.ConnectionError=ConnectionError})(MediaErrors=exports.MediaErrors||(exports.MediaErrors={}));exports.errorsByCode=new Map([[20101,AuthorizationErrors.AccessTokenInvalid],[20104,AuthorizationErrors.AccessTokenExpired],[20151,AuthorizationErrors.AuthenticationFailed],[31202,SignatureValidationErrors.AccessTokenSignatureValidationFailed],[31400,ClientErrors.BadRequest],[31404,ClientErrors.NotFound],[31480,ClientErrors.TemporarilyUnavailable],[31486,ClientErrors.BusyHere],[31603,SIPServerErrors.Decline],[31e3,GeneralErrors.UnknownError],[31001,GeneralErrors.ApplicationNotFoundError],[31002,GeneralErrors.ConnectionDeclinedError],[31003,GeneralErrors.ConnectionTimeoutError],[31005,GeneralErrors.ConnectionError],[31008,GeneralErrors.CallCancelledError],[31009,GeneralErrors.TransportError],[31100,MalformedRequestErrors.MalformedRequestError],[31101,MalformedRequestErrors.MissingParameterArrayError],[31102,MalformedRequestErrors.AuthorizationTokenMissingError],[31103,MalformedRequestErrors.MaxParameterLengthExceededError],[31104,MalformedRequestErrors.InvalidBridgeTokenError],[31105,MalformedRequestErrors.InvalidClientNameError],[31107,MalformedRequestErrors.ReconnectParameterInvalidError],[31201,AuthorizationErrors.AuthorizationError],[31203,AuthorizationErrors.NoValidAccountError],[31204,AuthorizationErrors.InvalidJWTTokenError],[31205,AuthorizationErrors.JWTTokenExpiredError],[31206,AuthorizationErrors.RateExceededError],[31207,AuthorizationErrors.JWTTokenExpirationTooLongError],[31209,AuthorizationErrors.PayloadSizeExceededError],[31401,UserMediaErrors.PermissionDeniedError],[31402,UserMediaErrors.AcquisitionFailedError],[53e3,SignalingErrors.ConnectionError],[53001,SignalingErrors.ConnectionDisconnected],[53400,MediaErrors.ClientLocalDescFailed],[53402,MediaErrors.ClientRemoteDescFailed],[53405,MediaErrors.ConnectionError]]);Object.freeze(exports.errorsByCode)},{"./twilioError":16}],15:[function(require,module,exports){"use strict";var __extends=this&&this.__extends||function(){var extendStatics=function(d,b){extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(d,b){d.__proto__=b}||function(d,b){for(var p in b)if(Object.prototype.hasOwnProperty.call(b,p))d[p]=b[p]};return extendStatics(d,b)};return function(d,b){extendStatics(d,b);function __(){this.constructor=d}d.prototype=b===null?Object.create(b):(__.prototype=b.prototype,new __)}}();Object.defineProperty(exports,"__esModule",{value:true});exports.UserMediaErrors=exports.TwilioError=exports.SIPServerErrors=exports.SignatureValidationErrors=exports.SignalingErrors=exports.MediaErrors=exports.MalformedRequestErrors=exports.GeneralErrors=exports.ClientErrors=exports.AuthorizationErrors=exports.hasErrorByCode=exports.getErrorByCode=exports.NotSupportedError=exports.InvalidStateError=exports.InvalidArgumentError=exports.getPreciseSignalingErrorByCode=void 0;var generated_1=require("./generated");Object.defineProperty(exports,"AuthorizationErrors",{enumerable:true,get:function(){return generated_1.AuthorizationErrors}});Object.defineProperty(exports,"ClientErrors",{enumerable:true,get:function(){return generated_1.ClientErrors}});Object.defineProperty(exports,"GeneralErrors",{enumerable:true,get:function(){return generated_1.GeneralErrors}});Object.defineProperty(exports,"MalformedRequestErrors",{enumerable:true,get:function(){return generated_1.MalformedRequestErrors}});Object.defineProperty(exports,"MediaErrors",{enumerable:true,get:function(){return generated_1.MediaErrors}});Object.defineProperty(exports,"SignalingErrors",{enumerable:true,get:function(){return generated_1.SignalingErrors}});Object.defineProperty(exports,"SignatureValidationErrors",{enumerable:true,get:function(){return generated_1.SignatureValidationErrors}});Object.defineProperty(exports,"SIPServerErrors",{enumerable:true,get:function(){return generated_1.SIPServerErrors}});Object.defineProperty(exports,"TwilioError",{enumerable:true,get:function(){return generated_1.TwilioError}});Object.defineProperty(exports,"UserMediaErrors",{enumerable:true,get:function(){return generated_1.UserMediaErrors}});var PRECISE_SIGNALING_ERROR_CODES=new Set([31001,31002,31003,31101,31102,31103,31104,31105,31107,31201,31202,31203,31204,31205,31207,31404,31480,31486,31603]);function getPreciseSignalingErrorByCode(enableImprovedSignalingErrorPrecision,errorCode){if(typeof errorCode!=="number"){return}if(!hasErrorByCode(errorCode)){return}var shouldTransform=enableImprovedSignalingErrorPrecision?true:!PRECISE_SIGNALING_ERROR_CODES.has(errorCode);if(!shouldTransform){return}return getErrorByCode(errorCode)}exports.getPreciseSignalingErrorByCode=getPreciseSignalingErrorByCode;var InvalidArgumentError=function(_super){__extends(InvalidArgumentError,_super);function InvalidArgumentError(message){var _this=_super.call(this,message)||this;_this.name="InvalidArgumentError";return _this}return InvalidArgumentError}(Error);exports.InvalidArgumentError=InvalidArgumentError;var InvalidStateError=function(_super){__extends(InvalidStateError,_super);function InvalidStateError(message){var _this=_super.call(this,message)||this;_this.name="InvalidStateError";return _this}return InvalidStateError}(Error);exports.InvalidStateError=InvalidStateError;var NotSupportedError=function(_super){__extends(NotSupportedError,_super);function NotSupportedError(message){var _this=_super.call(this,message)||this;_this.name="NotSupportedError";return _this}return NotSupportedError}(Error);exports.NotSupportedError=NotSupportedError;function getErrorByCode(code){var error=generated_1.errorsByCode.get(code);if(!error){throw new InvalidArgumentError("Error code "+code+" not found")}return error}exports.getErrorByCode=getErrorByCode;function hasErrorByCode(code){return generated_1.errorsByCode.has(code)}exports.hasErrorByCode=hasErrorByCode},{"./generated":14}],16:[function(require,module,exports){"use strict";var __extends=this&&this.__extends||function(){var extendStatics=function(d,b){extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(d,b){d.__proto__=b}||function(d,b){for(var p in b)if(Object.prototype.hasOwnProperty.call(b,p))d[p]=b[p]};return extendStatics(d,b)};return function(d,b){extendStatics(d,b);function __(){this.constructor=d}d.prototype=b===null?Object.create(b):(__.prototype=b.prototype,new __)}}();Object.defineProperty(exports,"__esModule",{value:true});var TwilioError=function(_super){__extends(TwilioError,_super);function TwilioError(messageOrError,error){var _this=_super.call(this)||this;Object.setPrototypeOf(_this,TwilioError.prototype);var message=typeof messageOrError==="string"?messageOrError:_this.explanation;var originalError=typeof messageOrError==="object"?messageOrError:error;_this.message=_this.name+" ("+_this.code+"): "+message;_this.originalError=originalError;return _this}return TwilioError}(Error);exports.default=TwilioError},{}],17:[function(require,module,exports){"use strict";var __extends=this&&this.__extends||function(){var extendStatics=function(d,b){extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(d,b){d.__proto__=b}||function(d,b){for(var p in b)if(Object.prototype.hasOwnProperty.call(b,p))d[p]=b[p]};return extendStatics(d,b)};return function(d,b){extendStatics(d,b);function __(){this.constructor=d}d.prototype=b===null?Object.create(b):(__.prototype=b.prototype,new __)}}();Object.defineProperty(exports,"__esModule",{value:true});var events_1=require("events");var log_1=require("./log");var request_1=require("./request");var EventPublisher=function(_super){__extends(EventPublisher,_super);function EventPublisher(productName,token,options){var _this=_super.call(this)||this;if(!(_this instanceof EventPublisher)){return new EventPublisher(productName,token,options)}options=Object.assign({defaultPayload:function(){return{}}},options);var defaultPayload=options.defaultPayload;if(typeof defaultPayload!=="function"){defaultPayload=function(){return Object.assign({},options.defaultPayload)}}var isEnabled=true;var metadata=Object.assign({app_name:undefined,app_version:undefined},options.metadata);Object.defineProperties(_this,{_defaultPayload:{value:defaultPayload},_host:{value:options.host,writable:true},_isEnabled:{get:function(){return isEnabled},set:function(_isEnabled){isEnabled=_isEnabled}},_log:{value:new log_1.default("EventPublisher")},_request:{value:options.request||request_1.default,writable:true},_token:{value:token,writable:true},isEnabled:{enumerable:true,get:function(){return isEnabled}},metadata:{enumerable:true,get:function(){return metadata}},productName:{enumerable:true,value:productName},token:{enumerable:true,get:function(){return this._token}}});return _this}return EventPublisher}(events_1.EventEmitter);EventPublisher.prototype._post=function _post(endpointName,level,group,name,payload,connection,force){var _this=this;if(!this.isEnabled&&!force||!this._host){this._log.debug("Publishing cancelled",JSON.stringify({isEnabled:this.isEnabled,force:force,host:this._host}));return Promise.resolve()}if(!connection||(!connection.parameters||!connection.parameters.CallSid)&&!connection.outboundConnectionId){if(!connection){this._log.debug("Publishing cancelled. Missing connection object")}else{this._log.debug("Publishing cancelled. Missing connection info",JSON.stringify({outboundConnectionId:connection.outboundConnectionId,parameters:connection.parameters}))}return Promise.resolve()}var event={group:group,level:level.toUpperCase(),name:name,payload:payload&&payload.forEach?payload.slice(0):Object.assign(this._defaultPayload(connection),payload),payload_type:"application/json",private:false,publisher:this.productName,timestamp:(new Date).toISOString()};if(this.metadata){event.publisher_metadata=this.metadata}if(endpointName==="EndpointEvents"){this._log.debug("Publishing insights",JSON.stringify({endpointName:endpointName,event:event,force:force,host:this._host}))}var requestParams={body:event,headers:{"Content-Type":"application/json","X-Twilio-Token":this.token},url:"https://"+this._host+"/v4/"+endpointName};return new Promise(function(resolve,reject){_this._request.post(requestParams,function(err){if(err){_this.emit("error",err);reject(err)}else{resolve()}})}).catch(function(e){_this._log.error("Unable to post "+group+" "+name+" event to Insights. Received error: "+e)})};EventPublisher.prototype.post=function post(level,group,name,payload,connection,force){return this._post("EndpointEvents",level,group,name,payload,connection,force)};EventPublisher.prototype.debug=function debug(group,name,payload,connection){return this.post("debug",group,name,payload,connection)};EventPublisher.prototype.info=function info(group,name,payload,connection){return this.post("info",group,name,payload,connection)};EventPublisher.prototype.warn=function warn(group,name,payload,connection){return this.post("warning",group,name,payload,connection)};EventPublisher.prototype.error=function error(group,name,payload,connection){return this.post("error",group,name,payload,connection)};EventPublisher.prototype.postMetrics=function postMetrics(group,name,metrics,customFields,connection){var _this=this;return new Promise(function(resolve){var samples=metrics.map(formatMetric).map(function(sample){return Object.assign(sample,customFields)});resolve(_this._post("EndpointMetrics","info",group,name,samples,connection))})};EventPublisher.prototype.setHost=function setHost(host){this._host=host};EventPublisher.prototype.setToken=function setToken(token){this._token=token};EventPublisher.prototype.enable=function enable(){this._isEnabled=true};EventPublisher.prototype.disable=function disable(){this._isEnabled=false};function formatMetric(sample){return{audio_codec:sample.codecName,audio_level_in:sample.audioInputLevel,audio_level_out:sample.audioOutputLevel,bytes_received:sample.bytesReceived,bytes_sent:sample.bytesSent,call_volume_input:sample.inputVolume,call_volume_output:sample.outputVolume,jitter:sample.jitter,mos:sample.mos&&Math.round(sample.mos*100)/100,packets_lost:sample.packetsLost,packets_lost_fraction:sample.packetsLostFraction&&Math.round(sample.packetsLostFraction*100)/100,packets_received:sample.packetsReceived,rtt:sample.rtt,timestamp:new Date(sample.timestamp).toISOString(),total_bytes_received:sample.totals.bytesReceived,total_bytes_sent:sample.totals.bytesSent,total_packets_lost:sample.totals.packetsLost,total_packets_received:sample.totals.packetsReceived,total_packets_sent:sample.totals.packetsSent}}exports.default=EventPublisher},{"./log":18,"./request":23,events:39}],18:[function(require,module,exports){"use strict";var __spreadArrays=this&&this.__spreadArrays||function(){for(var s=0,i=0,il=arguments.length;i0&&t[t.length-1])&&(op[0]===6||op[0]===2)){_=0;continue}if(op[0]===3&&(!t||op[1]>t[0]&&op[1]4.2){return PreflightTest.CallQuality.Excellent}else if(mos>=4.1&&mos<=4.2){return PreflightTest.CallQuality.Great}else if(mos>=3.7&&mos<=4){return PreflightTest.CallQuality.Good}else if(mos>=3.1&&mos<=3.6){return PreflightTest.CallQuality.Fair}else{return PreflightTest.CallQuality.Degraded}};PreflightTest.prototype._getReport=function(){var stats=this._getRTCStats();var testTiming={start:this._startTime};if(this._endTime){testTiming.end=this._endTime;testTiming.duration=this._endTime-this._startTime}var report={callSid:this._callSid,edge:this._edge,iceCandidateStats:this._rtcIceCandidateStatsReport.iceCandidateStats,networkTiming:this._networkTiming,samples:this._samples,selectedEdge:this._options.edge,stats:stats,testTiming:testTiming,totals:this._getRTCSampleTotals(),warnings:this._warnings};var selectedIceCandidatePairStats=this._rtcIceCandidateStatsReport.selectedIceCandidatePairStats;if(selectedIceCandidatePairStats){report.selectedIceCandidatePairStats=selectedIceCandidatePairStats;report.isTurnRequired=selectedIceCandidatePairStats.localCandidate.candidateType==="relay"||selectedIceCandidatePairStats.remoteCandidate.candidateType==="relay"}if(stats){report.callQuality=this._getCallQuality(stats.mos.average)}return report};PreflightTest.prototype._getRTCSampleTotals=function(){if(!this._latestSample){return}return __assign({},this._latestSample.totals)};PreflightTest.prototype._getRTCStats=function(){var firstMosSampleIdx=this._samples.findIndex(function(sample){return typeof sample.mos==="number"&&sample.mos>0});var samples=firstMosSampleIdx>=0?this._samples.slice(firstMosSampleIdx):[];if(!samples||!samples.length){return}return["jitter","mos","rtt"].reduce(function(statObj,stat){var _a;var values=samples.map(function(s){return s[stat]});return __assign(__assign({},statObj),(_a={},_a[stat]={average:Number((values.reduce(function(total,value){return total+value})/values.length).toPrecision(5)),max:Math.max.apply(Math,values),min:Math.min.apply(Math,values)},_a))},{})};PreflightTest.prototype._getStreamFromFile=function(){var audioContext=this._options.audioContext;if(!audioContext){throw new errors_1.NotSupportedError("Cannot fake input audio stream: AudioContext is not supported by this browser.")}var audioEl=new Audio(constants_1.COWBELL_AUDIO_URL);audioEl.addEventListener("canplaythrough",function(){return audioEl.play()});if(typeof audioEl.setAttribute==="function"){audioEl.setAttribute("crossorigin","anonymous")}var src=audioContext.createMediaElementSource(audioEl);var dest=audioContext.createMediaStreamDestination();src.connect(dest);return dest.stream};PreflightTest.prototype._initDevice=function(token,options){var _this=this;try{this._device=new(options.deviceFactory||device_1.default)(token,{codecPreferences:options.codecPreferences,edge:options.edge,fileInputStream:options.fileInputStream,logLevel:options.logLevel,preflight:true});this._device.once(device_1.default.EventName.Registered,function(){_this._onDeviceRegistered()});this._device.once(device_1.default.EventName.Error,function(error){_this._onDeviceError(error)});this._device.register()}catch(error){setTimeout(function(){_this._onFailed(error)});return}this._signalingTimeoutTimer=setTimeout(function(){_this._onDeviceError(new errors_1.SignalingErrors.ConnectionError("WebSocket Connection Timeout"))},options.signalingTimeoutMs)};PreflightTest.prototype._onDeviceError=function(error){this._device.destroy();this._onFailed(error)};PreflightTest.prototype._onDeviceRegistered=function(){return __awaiter(this,void 0,void 0,function(){var _a,audio,publisher;var _this=this;return __generator(this,function(_b){switch(_b.label){case 0:clearTimeout(this._echoTimer);clearTimeout(this._signalingTimeoutTimer);_a=this;return[4,this._device.connect({rtcConfiguration:this._options.rtcConfiguration})];case 1:_a._call=_b.sent();this._networkTiming.signaling={start:Date.now()};this._setupCallHandlers(this._call);this._edge=this._device.edge||undefined;if(this._options.fakeMicInput){this._echoTimer=setTimeout(function(){return _this._device.disconnectAll()},constants_1.ECHO_TEST_DURATION);audio=this._device.audio;if(audio){audio.disconnect(false);audio.outgoing(false)}}this._call.once("disconnect",function(){_this._device.once(device_1.default.EventName.Unregistered,function(){return _this._onUnregistered()});_this._device.destroy()});publisher=this._call["_publisher"];publisher.on("error",function(){if(!_this._hasInsightsErrored){_this._emitWarning("insights-connection-error","Received an error when attempting to connect to Insights gateway")}_this._hasInsightsErrored=true});return[2]}})})};PreflightTest.prototype._onFailed=function(error){clearTimeout(this._echoTimer);clearTimeout(this._signalingTimeoutTimer);this._releaseHandlers();this._endTime=Date.now();this._status=PreflightTest.Status.Failed;this._log.debug("#"+PreflightTest.Events.Failed,error);this.emit(PreflightTest.Events.Failed,error)};PreflightTest.prototype._onUnregistered=function(){var _this=this;setTimeout(function(){if(_this._status===PreflightTest.Status.Failed){return}clearTimeout(_this._echoTimer);clearTimeout(_this._signalingTimeoutTimer);_this._releaseHandlers();_this._endTime=Date.now();_this._status=PreflightTest.Status.Completed;_this._report=_this._getReport();_this._log.debug("#"+PreflightTest.Events.Completed,JSON.stringify(_this._report));_this.emit(PreflightTest.Events.Completed,_this._report)},10)};PreflightTest.prototype._releaseHandlers=function(){[this._device,this._call].forEach(function(emitter){if(emitter){emitter.eventNames().forEach(function(name){return emitter.removeAllListeners(name)})}})};PreflightTest.prototype._setupCallHandlers=function(call){var _this=this;if(this._options.fakeMicInput){call.once("volume",function(){call["_mediaHandler"].outputs.forEach(function(output){return output.audio.muted=true})})}call.on("warning",function(name,data){_this._emitWarning(name,"Received an RTCWarning. See .rtcWarning for the RTCWarning",data)});call.once("accept",function(){_this._callSid=call["_mediaHandler"].callSid;_this._status=PreflightTest.Status.Connected;_this._log.debug("#"+PreflightTest.Events.Connected);_this.emit(PreflightTest.Events.Connected)});call.on("sample",function(sample){return __awaiter(_this,void 0,void 0,function(){var _a;return __generator(this,function(_b){switch(_b.label){case 0:if(!!this._latestSample)return[3,2];_a=this;return[4,(this._options.getRTCIceCandidateStatsReport||stats_1.getRTCIceCandidateStatsReport)(call["_mediaHandler"].version.pc)];case 1:_a._rtcIceCandidateStatsReport=_b.sent();_b.label=2;case 2:this._latestSample=sample;this._samples.push(sample);this._log.debug("#"+PreflightTest.Events.Sample,JSON.stringify(sample));this.emit(PreflightTest.Events.Sample,sample);return[2]}})})});[{reportLabel:"peerConnection",type:"pcconnection"},{reportLabel:"ice",type:"iceconnection"},{reportLabel:"dtls",type:"dtlstransport"},{reportLabel:"signaling",type:"signaling"}].forEach(function(_a){var type=_a.type,reportLabel=_a.reportLabel;var handlerName="on"+type+"statechange";var originalHandler=call["_mediaHandler"][handlerName];call["_mediaHandler"][handlerName]=function(state){var timing=_this._networkTiming[reportLabel]=_this._networkTiming[reportLabel]||{start:0};if(state==="connecting"||state==="checking"){timing.start=Date.now()}else if((state==="connected"||state==="stable")&&!timing.duration){timing.end=Date.now();timing.duration=timing.end-timing.start}originalHandler(state)}})};Object.defineProperty(PreflightTest.prototype,"callSid",{get:function(){return this._callSid},enumerable:false,configurable:true});Object.defineProperty(PreflightTest.prototype,"endTime",{get:function(){return this._endTime},enumerable:false,configurable:true});Object.defineProperty(PreflightTest.prototype,"latestSample",{get:function(){return this._latestSample},enumerable:false,configurable:true});Object.defineProperty(PreflightTest.prototype,"report",{get:function(){return this._report},enumerable:false,configurable:true});Object.defineProperty(PreflightTest.prototype,"startTime",{get:function(){return this._startTime},enumerable:false,configurable:true});Object.defineProperty(PreflightTest.prototype,"status",{get:function(){return this._status},enumerable:false,configurable:true});return PreflightTest}(events_1.EventEmitter);exports.PreflightTest=PreflightTest;(function(PreflightTest){var CallQuality;(function(CallQuality){CallQuality["Excellent"]="excellent";CallQuality["Great"]="great";CallQuality["Good"]="good";CallQuality["Fair"]="fair";CallQuality["Degraded"]="degraded"})(CallQuality=PreflightTest.CallQuality||(PreflightTest.CallQuality={}));var Events;(function(Events){Events["Completed"]="completed";Events["Connected"]="connected";Events["Failed"]="failed";Events["Sample"]="sample";Events["Warning"]="warning"})(Events=PreflightTest.Events||(PreflightTest.Events={}));var Status;(function(Status){Status["Connecting"]="connecting";Status["Connected"]="connected";Status["Completed"]="completed";Status["Failed"]="failed"})(Status=PreflightTest.Status||(PreflightTest.Status={}))})(PreflightTest=exports.PreflightTest||(exports.PreflightTest={}));exports.PreflightTest=PreflightTest},{"../call":9,"../constants":10,"../device":12,"../errors":15,"../log":18,"../rtc/stats":32,events:39}],21:[function(require,module,exports){"use strict";var __extends=this&&this.__extends||function(){var extendStatics=function(d,b){extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(d,b){d.__proto__=b}||function(d,b){for(var p in b)if(Object.prototype.hasOwnProperty.call(b,p))d[p]=b[p]};return extendStatics(d,b)};return function(d,b){extendStatics(d,b);function __(){this.constructor=d}d.prototype=b===null?Object.create(b):(__.prototype=b.prototype,new __)}}();Object.defineProperty(exports,"__esModule",{value:true});var events_1=require("events");var C=require("./constants");var errors_1=require("./errors");var log_1=require("./log");var wstransport_1=require("./wstransport");var PSTREAM_VERSION="1.6";var PStream=function(_super){__extends(PStream,_super);function PStream(token,uris,options){var _this=_super.call(this)||this;if(!(_this instanceof PStream)){return new PStream(token,uris,options)}var defaults={TransportFactory:wstransport_1.default};options=options||{};for(var prop in defaults){if(prop in options){continue}options[prop]=defaults[prop]}_this.options=options;_this.token=token||"";_this.status="disconnected";_this.gateway=null;_this.region=null;_this._messageQueue=[];_this._preferredUri=null;_this._uris=uris;_this._handleTransportClose=_this._handleTransportClose.bind(_this);_this._handleTransportError=_this._handleTransportError.bind(_this);_this._handleTransportMessage=_this._handleTransportMessage.bind(_this);_this._handleTransportOpen=_this._handleTransportOpen.bind(_this);_this._log=new log_1.default("PStream");_this.on("error",function(){_this._log.warn("Unexpected error handled in pstream")});var self=_this;_this.addListener("ready",function(){self.status="ready"});_this.addListener("offline",function(){self.status="offline"});_this.addListener("close",function(){self._log.info('Received "close" from server. Destroying PStream...');self._destroy()});_this.transport=new _this.options.TransportFactory(_this._uris,{backoffMaxMs:_this.options.backoffMaxMs,maxPreferredDurationMs:_this.options.maxPreferredDurationMs});Object.defineProperties(_this,{uri:{enumerable:true,get:function(){return this.transport.uri}}});_this.transport.on("close",_this._handleTransportClose);_this.transport.on("error",_this._handleTransportError);_this.transport.on("message",_this._handleTransportMessage);_this.transport.on("open",_this._handleTransportOpen);_this.transport.open();return _this}return PStream}(events_1.EventEmitter);PStream.prototype._handleTransportClose=function(){this.emit("transportClose");if(this.status!=="disconnected"){if(this.status!=="offline"){this.emit("offline",this)}this.status="disconnected"}};PStream.prototype._handleTransportError=function(error){if(!error){this.emit("error",{error:{code:31e3,message:"Websocket closed without a provided reason",twilioError:new errors_1.SignalingErrors.ConnectionDisconnected}});return}this.emit("error",typeof error.code!=="undefined"?{error:error}:error)};PStream.prototype._handleTransportMessage=function(msg){if(!msg||!msg.data||typeof msg.data!=="string"){return}var _a=JSON.parse(msg.data),type=_a.type,_b=_a.payload,payload=_b===void 0?{}:_b;this.gateway=payload.gateway||this.gateway;this.region=payload.region||this.region;if(type==="error"&&payload.error){payload.error.twilioError=new errors_1.SignalingErrors.ConnectionError}this.emit(type,payload)};PStream.prototype._handleTransportOpen=function(){var _this=this;this.status="connected";this.setToken(this.token);this.emit("transportOpen");var messages=this._messageQueue.splice(0,this._messageQueue.length);messages.forEach(function(message){return _this._publish.apply(_this,message)})};PStream.toString=function(){return"[Twilio.PStream class]"};PStream.prototype.toString=function(){return"[Twilio.PStream instance]"};PStream.prototype.setToken=function(token){this._log.info("Setting token and publishing listen");this.token=token;var payload={browserinfo:getBrowserInfo(),token:token};this._publish("listen",payload)};PStream.prototype.sendMessage=function(callsid,content,contenttype,messagetype,voiceeventsid){if(contenttype===void 0){contenttype="application/json"}var payload={callsid:callsid,content:content,contenttype:contenttype,messagetype:messagetype,voiceeventsid:voiceeventsid};this._publish("message",payload,true)};PStream.prototype.register=function(mediaCapabilities){var regPayload={media:mediaCapabilities};this._publish("register",regPayload,true)};PStream.prototype.invite=function(sdp,callsid,preflight,params){var payload={callsid:callsid,preflight:!!preflight,sdp:sdp,twilio:params?{params:params}:{}};this._publish("invite",payload,true)};PStream.prototype.reconnect=function(sdp,callsid,reconnect,params){var payload={callsid:callsid,preflight:false,reconnect:reconnect,sdp:sdp,twilio:params?{params:params}:{}};this._publish("invite",payload,true)};PStream.prototype.answer=function(sdp,callsid){this._publish("answer",{sdp:sdp,callsid:callsid},true)};PStream.prototype.dtmf=function(callsid,digits){this._publish("dtmf",{callsid:callsid,dtmf:digits},true)};PStream.prototype.hangup=function(callsid,message){var payload=message?{callsid:callsid,message:message}:{callsid:callsid};this._publish("hangup",payload,true)};PStream.prototype.reject=function(callsid){this._publish("reject",{callsid:callsid},true)};PStream.prototype.reinvite=function(sdp,callsid){this._publish("reinvite",{sdp:sdp,callsid:callsid},false)};PStream.prototype._destroy=function(){this.transport.removeListener("close",this._handleTransportClose);this.transport.removeListener("error",this._handleTransportError);this.transport.removeListener("message",this._handleTransportMessage);this.transport.removeListener("open",this._handleTransportOpen);this.transport.close();this.emit("offline",this)};PStream.prototype.destroy=function(){this._log.info("PStream.destroy() called...");this._destroy();return this};PStream.prototype.updatePreferredURI=function(uri){this._preferredUri=uri;this.transport.updatePreferredURI(uri)};PStream.prototype.updateURIs=function(uris){this._uris=uris;this.transport.updateURIs(this._uris)};PStream.prototype.publish=function(type,payload){return this._publish(type,payload,true)};PStream.prototype._publish=function(type,payload,shouldRetry){var msg=JSON.stringify({payload:payload,type:type,version:PSTREAM_VERSION});var isSent=!!this.transport.send(msg);if(!isSent){this.emit("error",{error:{code:31009,message:"No transport available to send or receive messages",twilioError:new errors_1.GeneralErrors.TransportError}});if(shouldRetry){this._messageQueue.push([type,payload,true])}}};function getBrowserInfo(){var nav=typeof navigator!=="undefined"?navigator:{};var info={browser:{platform:nav.platform||"unknown",userAgent:nav.userAgent||"unknown"},p:"browser",plugin:"rtc",v:C.RELEASE_VERSION};return info}exports.default=PStream},{"./constants":10,"./errors":15,"./log":18,"./wstransport":38,events:39}],22:[function(require,module,exports){"use strict";var _a;Object.defineProperty(exports,"__esModule",{value:true});exports.getRegionShortcode=exports.getChunderURIs=exports.createSignalingEndpointURL=exports.createEventGatewayURI=exports.defaultEdge=exports.regionToEdge=exports.regionShortcodes=exports.Region=exports.Edge=void 0;var errors_1=require("./errors");var Edge;(function(Edge){Edge["Sydney"]="sydney";Edge["SaoPaulo"]="sao-paulo";Edge["Dublin"]="dublin";Edge["Frankfurt"]="frankfurt";Edge["Tokyo"]="tokyo";Edge["Singapore"]="singapore";Edge["Ashburn"]="ashburn";Edge["Umatilla"]="umatilla";Edge["Roaming"]="roaming";Edge["AshburnIx"]="ashburn-ix";Edge["SanJoseIx"]="san-jose-ix";Edge["LondonIx"]="london-ix";Edge["FrankfurtIx"]="frankfurt-ix";Edge["SingaporeIx"]="singapore-ix";Edge["SydneyIx"]="sydney-ix";Edge["TokyoIx"]="tokyo-ix"})(Edge=exports.Edge||(exports.Edge={}));var Region;(function(Region){Region["Au1"]="au1";Region["Au1Ix"]="au1-ix";Region["Br1"]="br1";Region["De1"]="de1";Region["De1Ix"]="de1-ix";Region["Gll"]="gll";Region["Ie1"]="ie1";Region["Ie1Ix"]="ie1-ix";Region["Ie1Tnx"]="ie1-tnx";Region["Jp1"]="jp1";Region["Jp1Ix"]="jp1-ix";Region["Sg1"]="sg1";Region["Sg1Ix"]="sg1-ix";Region["Sg1Tnx"]="sg1-tnx";Region["Us1"]="us1";Region["Us1Ix"]="us1-ix";Region["Us1Tnx"]="us1-tnx";Region["Us2"]="us2";Region["Us2Ix"]="us2-ix";Region["Us2Tnx"]="us2-tnx"})(Region=exports.Region||(exports.Region={}));exports.regionShortcodes={ASIAPAC_SINGAPORE:Region.Sg1,ASIAPAC_SYDNEY:Region.Au1,ASIAPAC_TOKYO:Region.Jp1,EU_FRANKFURT:Region.De1,EU_IRELAND:Region.Ie1,SOUTH_AMERICA_SAO_PAULO:Region.Br1,US_EAST_VIRGINIA:Region.Us1,US_WEST_OREGON:Region.Us2};exports.regionToEdge=(_a={},_a[Region.Au1]=Edge.Sydney,_a[Region.Br1]=Edge.SaoPaulo,_a[Region.Ie1]=Edge.Dublin,_a[Region.De1]=Edge.Frankfurt,_a[Region.Jp1]=Edge.Tokyo,_a[Region.Sg1]=Edge.Singapore,_a[Region.Us1]=Edge.Ashburn,_a[Region.Us2]=Edge.Umatilla,_a[Region.Gll]=Edge.Roaming,_a[Region.Us1Ix]=Edge.AshburnIx,_a[Region.Us2Ix]=Edge.SanJoseIx,_a[Region.Ie1Ix]=Edge.LondonIx,_a[Region.De1Ix]=Edge.FrankfurtIx,_a[Region.Sg1Ix]=Edge.SingaporeIx,_a[Region.Au1Ix]=Edge.SydneyIx,_a[Region.Jp1Ix]=Edge.TokyoIx,_a[Region.Us1Tnx]=Edge.AshburnIx,_a[Region.Us2Tnx]=Edge.AshburnIx,_a[Region.Ie1Tnx]=Edge.LondonIx,_a[Region.Sg1Tnx]=Edge.SingaporeIx,_a);exports.defaultEdge=Edge.Roaming;var defaultEventGatewayURI="eventgw.twilio.com";function createChunderEdgeURI(edge){return"voice-js."+edge+".twilio.com"}function createEventGatewayURI(region){return region?"eventgw."+region+".twilio.com":defaultEventGatewayURI}exports.createEventGatewayURI=createEventGatewayURI;function createSignalingEndpointURL(uri){return"wss://"+uri+"/signal"}exports.createSignalingEndpointURL=createSignalingEndpointURL;function getChunderURIs(edge){if(!!edge&&typeof edge!=="string"&&!Array.isArray(edge)){throw new errors_1.InvalidArgumentError("If `edge` is provided, it must be of type `string` or an array of strings.")}var uris;if(edge){var edgeParams=Array.isArray(edge)?edge:[edge];uris=edgeParams.map(function(param){return createChunderEdgeURI(param)})}else{uris=[createChunderEdgeURI(exports.defaultEdge)]}return uris}exports.getChunderURIs=getChunderURIs;function getRegionShortcode(region){return exports.regionShortcodes[region]||null}exports.getRegionShortcode=getRegionShortcode},{"./errors":15}],23:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});function request(method,params,callback){var body=JSON.stringify(params.body||{});var headers=new Headers;params.headers=params.headers||[];Object.entries(params.headers).forEach(function(_a){var headerName=_a[0],headerBody=_a[1];return headers.append(headerName,headerBody)});fetch(params.url,{body:body,headers:headers,method:method}).then(function(response){return response.text()},callback).then(function(responseText){return callback(null,responseText)},callback)}var Request=request;Request.get=function get(params,callback){return new this("GET",params,callback)};Request.post=function post(params,callback){return new this("POST",params,callback)};exports.default=Request},{}],24:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var errors_1=require("../errors");var util=require("../util");function getUserMedia(constraints,options){options=options||{};options.util=options.util||util;options.navigator=options.navigator||(typeof navigator!=="undefined"?navigator:null);return new Promise(function(resolve,reject){if(!options.navigator){throw new errors_1.NotSupportedError("getUserMedia is not supported")}switch("function"){case typeof(options.navigator.mediaDevices&&options.navigator.mediaDevices.getUserMedia):return resolve(options.navigator.mediaDevices.getUserMedia(constraints));case typeof options.navigator.webkitGetUserMedia:return options.navigator.webkitGetUserMedia(constraints,resolve,reject);case typeof options.navigator.mozGetUserMedia:return options.navigator.mozGetUserMedia(constraints,resolve,reject);case typeof options.navigator.getUserMedia:return options.navigator.getUserMedia(constraints,resolve,reject);default:throw new errors_1.NotSupportedError("getUserMedia is not supported")}}).catch(function(e){throw options.util.isFirefox()&&e.name==="NotReadableError"?new errors_1.NotSupportedError("Firefox does not currently support opening multiple audio input tracks"+"simultaneously, even across different tabs.\n"+"Related Bugzilla thread: https://bugzilla.mozilla.org/show_bug.cgi?id=1299324"):e})}exports.default=getUserMedia},{"../errors":15,"../util":36}],25:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.IceCandidate=void 0;var IceCandidate=function(){function IceCandidate(iceCandidate,isRemote){if(isRemote===void 0){isRemote=false}this.deleted=false;var cost;var parts=iceCandidate.candidate.split("network-cost ");if(parts[1]){cost=parseInt(parts[1],10)}this.candidateType=iceCandidate.type;this.ip=iceCandidate.ip||iceCandidate.address;this.isRemote=isRemote;this.networkCost=cost;this.port=iceCandidate.port;this.priority=iceCandidate.priority;this.protocol=iceCandidate.protocol;this.relatedAddress=iceCandidate.relatedAddress;this.relatedPort=iceCandidate.relatedPort;this.tcpType=iceCandidate.tcpType;this.transportId=iceCandidate.sdpMid}IceCandidate.prototype.toPayload=function(){return{candidate_type:this.candidateType,deleted:this.deleted,ip:this.ip,is_remote:this.isRemote,"network-cost":this.networkCost,port:this.port,priority:this.priority,protocol:this.protocol,related_address:this.relatedAddress,related_port:this.relatedPort,tcp_type:this.tcpType,transport_id:this.transportId}};return IceCandidate}();exports.IceCandidate=IceCandidate},{}],26:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.PeerConnection=exports.getMediaEngine=exports.enabled=void 0;var peerconnection_1=require("./peerconnection");exports.PeerConnection=peerconnection_1.default;var rtcpc_1=require("./rtcpc");function enabled(){return rtcpc_1.default.test()}exports.enabled=enabled;function getMediaEngine(){return typeof RTCIceGatherer!=="undefined"?"ORTC":"WebRTC"}exports.getMediaEngine=getMediaEngine},{"./peerconnection":29,"./rtcpc":30}],27:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var OLD_MAX_VOLUME=32767;var NativeRTCStatsReport=typeof window!=="undefined"?window.RTCStatsReport:undefined;function MockRTCStatsReport(statsMap){if(!(this instanceof MockRTCStatsReport)){return new MockRTCStatsReport(statsMap)}var self=this;Object.defineProperties(this,{_map:{value:statsMap},size:{enumerable:true,get:function(){return self._map.size}}});this[Symbol.iterator]=statsMap[Symbol.iterator]}if(NativeRTCStatsReport){MockRTCStatsReport.prototype=Object.create(NativeRTCStatsReport.prototype);MockRTCStatsReport.prototype.constructor=MockRTCStatsReport}["entries","forEach","get","has","keys","values"].forEach(function(key){MockRTCStatsReport.prototype[key]=function(){var _a;var args=[];for(var _i=0;_i=0}exports.isNonNegativeNumber=isNonNegativeNumber;exports.default={calculate:calculate,isNonNegativeNumber:isNonNegativeNumber}},{}],29:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var errors_1=require("../errors");var log_1=require("../log");var util=require("../util");var rtcpc_1=require("./rtcpc");var sdp_1=require("./sdp");var ICE_GATHERING_TIMEOUT=15e3;var ICE_GATHERING_FAIL_NONE="none";var ICE_GATHERING_FAIL_TIMEOUT="timeout";var INITIAL_ICE_CONNECTION_STATE="new";var VOLUME_INTERVAL_MS=50;function PeerConnection(audioHelper,pstream,options){if(!audioHelper||!pstream){throw new errors_1.InvalidArgumentError("Audiohelper, and pstream are required arguments")}if(!(this instanceof PeerConnection)){return new PeerConnection(audioHelper,pstream,options)}this._log=new log_1.default("PeerConnection");function noop(){this._log.warn("Unexpected noop call in peerconnection")}this.onaudio=noop;this.onopen=noop;this.onerror=noop;this.onclose=noop;this.ondisconnected=noop;this.onfailed=noop;this.onconnected=noop;this.onreconnected=noop;this.onsignalingstatechange=noop;this.ondtlstransportstatechange=noop;this.onicegatheringfailure=noop;this.onicegatheringstatechange=noop;this.oniceconnectionstatechange=noop;this.onpcconnectionstatechange=noop;this.onicecandidate=noop;this.onselectedcandidatepairchange=noop;this.onvolume=noop;this.version=null;this.pstream=pstream;this.stream=null;this.sinkIds=new Set(["default"]);this.outputs=new Map;this.status="connecting";this.callSid=null;this.isMuted=false;var AudioContext=typeof window!=="undefined"&&(window.AudioContext||window.webkitAudioContext);this._isSinkSupported=!!AudioContext&&typeof HTMLAudioElement!=="undefined"&&HTMLAudioElement.prototype.setSinkId;this._audioContext=AudioContext&&audioHelper._audioContext;this._audioHelper=audioHelper;this._hasIceCandidates=false;this._hasIceGatheringFailures=false;this._iceGatheringTimeoutId=null;this._masterAudio=null;this._masterAudioDeviceId=null;this._mediaStreamSource=null;this._dtmfSender=null;this._dtmfSenderUnsupported=false;this._callEvents=[];this._nextTimeToPublish=Date.now();this._onAnswerOrRinging=noop;this._onHangup=noop;this._remoteStream=null;this._shouldManageStream=true;this._iceState=INITIAL_ICE_CONNECTION_STATE;this._isUnifiedPlan=options.isUnifiedPlan;this.options=options=options||{};this.navigator=options.navigator||(typeof navigator!=="undefined"?navigator:null);this.util=options.util||util;this.codecPreferences=options.codecPreferences;return this}PeerConnection.prototype.uri=function(){return this._uri};PeerConnection.prototype.openDefaultDeviceWithConstraints=function(constraints){return this._audioHelper._openDefaultDeviceWithConstraints(constraints).then(this._setInputTracksFromStream.bind(this,false))};PeerConnection.prototype.setInputTracksFromStream=function(stream){var self=this;return this._setInputTracksFromStream(true,stream).then(function(){self._shouldManageStream=false})};PeerConnection.prototype._createAnalyser=function(audioContext,options){options=Object.assign({fftSize:32,smoothingTimeConstant:.3},options);var analyser=audioContext.createAnalyser();for(var field in options){analyser[field]=options[field]}return analyser};PeerConnection.prototype._setVolumeHandler=function(handler){this.onvolume=handler};PeerConnection.prototype._startPollingVolume=function(){if(!this._audioContext||!this.stream||!this._remoteStream){return}var audioContext=this._audioContext;var inputAnalyser=this._inputAnalyser=this._createAnalyser(audioContext);var inputBufferLength=inputAnalyser.frequencyBinCount;var inputDataArray=new Uint8Array(inputBufferLength);this._inputAnalyser2=this._createAnalyser(audioContext,{maxDecibels:0,minDecibels:-127,smoothingTimeConstant:0});var outputAnalyser=this._outputAnalyser=this._createAnalyser(audioContext);var outputBufferLength=outputAnalyser.frequencyBinCount;var outputDataArray=new Uint8Array(outputBufferLength);this._outputAnalyser2=this._createAnalyser(audioContext,{maxDecibels:0,minDecibels:-127,smoothingTimeConstant:0});this._updateInputStreamSource(this.stream);this._updateOutputStreamSource(this._remoteStream);var self=this;setTimeout(function emitVolume(){if(!self._audioContext){return}else if(self.status==="closed"){self._inputAnalyser.disconnect();self._outputAnalyser.disconnect();self._inputAnalyser2.disconnect();self._outputAnalyser2.disconnect();return}self._inputAnalyser.getByteFrequencyData(inputDataArray);var inputVolume=self.util.average(inputDataArray);self._inputAnalyser2.getByteFrequencyData(inputDataArray);var inputVolume2=self.util.average(inputDataArray);self._outputAnalyser.getByteFrequencyData(outputDataArray);var outputVolume=self.util.average(outputDataArray);self._outputAnalyser2.getByteFrequencyData(outputDataArray);var outputVolume2=self.util.average(outputDataArray);self.onvolume(inputVolume/255,outputVolume/255,inputVolume2,outputVolume2);setTimeout(emitVolume,VOLUME_INTERVAL_MS)},VOLUME_INTERVAL_MS)};PeerConnection.prototype._stopStream=function _stopStream(){if(!this._shouldManageStream){return}this._audioHelper._stopDefaultInputDeviceStream()};PeerConnection.prototype._updateInputStreamSource=function(stream){if(this._inputStreamSource){this._inputStreamSource.disconnect()}try{this._inputStreamSource=this._audioContext.createMediaStreamSource(stream);this._inputStreamSource.connect(this._inputAnalyser);this._inputStreamSource.connect(this._inputAnalyser2)}catch(ex){this._log.warn("Unable to update input MediaStreamSource",ex);this._inputStreamSource=null}};PeerConnection.prototype._updateOutputStreamSource=function(stream){if(this._outputStreamSource){this._outputStreamSource.disconnect()}try{this._outputStreamSource=this._audioContext.createMediaStreamSource(stream);this._outputStreamSource.connect(this._outputAnalyser);this._outputStreamSource.connect(this._outputAnalyser2)}catch(ex){this._log.warn("Unable to update output MediaStreamSource",ex);this._outputStreamSource=null}};PeerConnection.prototype._setInputTracksFromStream=function(shouldClone,newStream){return this._isUnifiedPlan?this._setInputTracksForUnifiedPlan(shouldClone,newStream):this._setInputTracksForPlanB(shouldClone,newStream)};PeerConnection.prototype._setInputTracksForPlanB=function(shouldClone,newStream){var _this=this;if(!newStream){return Promise.reject(new errors_1.InvalidArgumentError("Can not set input stream to null while in a call"))}if(!newStream.getAudioTracks().length){return Promise.reject(new errors_1.InvalidArgumentError("Supplied input stream has no audio tracks"))}var localStream=this.stream;if(!localStream){this.stream=shouldClone?cloneStream(newStream):newStream}else{this._stopStream();removeStream(this.version.pc,localStream);localStream.getAudioTracks().forEach(localStream.removeTrack,localStream);newStream.getAudioTracks().forEach(localStream.addTrack,localStream);addStream(this.version.pc,newStream);this._updateInputStreamSource(this.stream)}this.mute(this.isMuted);if(!this.version){return Promise.resolve(this.stream)}return new Promise(function(resolve,reject){_this.version.createOffer(_this.options.maxAverageBitrate,_this.codecPreferences,{audio:true},function(){_this.version.processAnswer(_this.codecPreferences,_this._answerSdp,function(){resolve(_this.stream)},reject)},reject)})};PeerConnection.prototype._setInputTracksForUnifiedPlan=function(shouldClone,newStream){var _this=this;if(!newStream){return Promise.reject(new errors_1.InvalidArgumentError("Can not set input stream to null while in a call"))}if(!newStream.getAudioTracks().length){return Promise.reject(new errors_1.InvalidArgumentError("Supplied input stream has no audio tracks"))}var localStream=this.stream;var getStreamPromise=function(){_this.mute(_this.isMuted);return Promise.resolve(_this.stream)};if(!localStream){this.stream=shouldClone?cloneStream(newStream):newStream}else{if(this._shouldManageStream){this._stopStream()}if(!this._sender){this._sender=this.version.pc.getSenders()[0]}return this._sender.replaceTrack(newStream.getAudioTracks()[0]).then(function(){_this._updateInputStreamSource(newStream);_this.stream=shouldClone?cloneStream(newStream):newStream;return getStreamPromise()})}return getStreamPromise()};PeerConnection.prototype._onInputDevicesChanged=function(){if(!this.stream){return}var activeInputWasLost=this.stream.getAudioTracks().every(function(track){return track.readyState==="ended"});if(activeInputWasLost&&this._shouldManageStream){this.openDefaultDeviceWithConstraints({audio:true})}};PeerConnection.prototype._onIceGatheringFailure=function(type){this._hasIceGatheringFailures=true;this.onicegatheringfailure(type)};PeerConnection.prototype._onMediaConnectionStateChange=function(newState){var previousState=this._iceState;if(previousState===newState||newState!=="connected"&&newState!=="disconnected"&&newState!=="failed"){return}this._iceState=newState;var message;switch(newState){case"connected":if(previousState==="disconnected"||previousState==="failed"){message="ICE liveliness check succeeded. Connection with Twilio restored";this._log.info(message);this.onreconnected(message)}else{message="Media connection established.";this._log.info(message);this.onconnected(message)}this._stopIceGatheringTimeout();this._hasIceGatheringFailures=false;break;case"disconnected":message="ICE liveliness check failed. May be having trouble connecting to Twilio";this._log.warn(message);this.ondisconnected(message);break;case"failed":message="Connection with Twilio was interrupted.";this._log.warn(message);this.onfailed(message);break}};PeerConnection.prototype._setSinkIds=function(sinkIds){if(!this._isSinkSupported){return Promise.reject(new errors_1.NotSupportedError("Audio output selection is not supported by this browser"))}this.sinkIds=new Set(sinkIds.forEach?sinkIds:[sinkIds]);return this.version?this._updateAudioOutputs():Promise.resolve()};PeerConnection.prototype._startIceGatheringTimeout=function startIceGatheringTimeout(){var _this=this;this._stopIceGatheringTimeout();this._iceGatheringTimeoutId=setTimeout(function(){_this._onIceGatheringFailure(ICE_GATHERING_FAIL_TIMEOUT)},ICE_GATHERING_TIMEOUT)};PeerConnection.prototype._stopIceGatheringTimeout=function stopIceGatheringTimeout(){clearInterval(this._iceGatheringTimeoutId)};PeerConnection.prototype._updateAudioOutputs=function updateAudioOutputs(){var addedOutputIds=Array.from(this.sinkIds).filter(function(id){return!this.outputs.has(id)},this);var removedOutputIds=Array.from(this.outputs.keys()).filter(function(id){return!this.sinkIds.has(id)},this);var self=this;var createOutputPromises=addedOutputIds.map(this._createAudioOutput,this);return Promise.all(createOutputPromises).then(function(){return Promise.all(removedOutputIds.map(self._removeAudioOutput,self))})};PeerConnection.prototype._createAudio=function createAudio(arr){var audio=new Audio(arr);this.onaudio(audio);return audio};PeerConnection.prototype._createAudioOutput=function createAudioOutput(id){var dest=null;if(this._mediaStreamSource){dest=this._audioContext.createMediaStreamDestination();this._mediaStreamSource.connect(dest)}var audio=this._createAudio();setAudioSource(audio,dest&&dest.stream?dest.stream:this.pcStream);var self=this;return audio.setSinkId(id).then(function(){return audio.play()}).then(function(){self.outputs.set(id,{audio:audio,dest:dest})})};PeerConnection.prototype._removeAudioOutputs=function removeAudioOutputs(){if(this._masterAudio&&typeof this._masterAudioDeviceId!=="undefined"){this._disableOutput(this,this._masterAudioDeviceId);this.outputs.delete(this._masterAudioDeviceId);this._masterAudioDeviceId=null;if(!this._masterAudio.paused){this._masterAudio.pause()}if(typeof this._masterAudio.srcObject!=="undefined"){this._masterAudio.srcObject=null}else{this._masterAudio.src=""}this._masterAudio=null}return Array.from(this.outputs.keys()).map(this._removeAudioOutput,this)};PeerConnection.prototype._disableOutput=function disableOutput(pc,id){var output=pc.outputs.get(id);if(!output){return}if(output.audio){output.audio.pause();output.audio.src=""}if(output.dest){output.dest.disconnect()}};PeerConnection.prototype._reassignMasterOutput=function reassignMasterOutput(pc,masterId){var masterOutput=pc.outputs.get(masterId);pc.outputs.delete(masterId);var self=this;var idToReplace=Array.from(pc.outputs.keys())[0]||"default";return masterOutput.audio.setSinkId(idToReplace).then(function(){self._disableOutput(pc,idToReplace);pc.outputs.set(idToReplace,masterOutput);pc._masterAudioDeviceId=idToReplace}).catch(function rollback(){pc.outputs.set(masterId,masterOutput);self._log.info("Could not reassign master output. Attempted to roll back.")})};PeerConnection.prototype._removeAudioOutput=function removeAudioOutput(id){if(this._masterAudioDeviceId===id){return this._reassignMasterOutput(this,id)}this._disableOutput(this,id);this.outputs.delete(id);return Promise.resolve()};PeerConnection.prototype._onAddTrack=function onAddTrack(pc,stream){var audio=pc._masterAudio=this._createAudio();setAudioSource(audio,stream);audio.play();var deviceId=Array.from(pc.outputs.keys())[0]||"default";pc._masterAudioDeviceId=deviceId;pc.outputs.set(deviceId,{audio:audio});try{pc._mediaStreamSource=pc._audioContext.createMediaStreamSource(stream)}catch(ex){this._log.warn("Unable to create a MediaStreamSource from onAddTrack",ex);this._mediaStreamSource=null}pc.pcStream=stream;pc._updateAudioOutputs()};PeerConnection.prototype._fallbackOnAddTrack=function fallbackOnAddTrack(pc,stream){var audio=document&&document.createElement("audio");audio.autoplay=true;if(!setAudioSource(audio,stream)){pc._log.info("Error attaching stream to element.")}pc.outputs.set("default",{audio:audio})};PeerConnection.prototype._setEncodingParameters=function(enableDscp){if(!enableDscp||!this._sender||typeof this._sender.getParameters!=="function"||typeof this._sender.setParameters!=="function"){return}var params=this._sender.getParameters();if(!params.priority&&!(params.encodings&¶ms.encodings.length)){return}params.priority="high";if(params.encodings&¶ms.encodings.length){params.encodings.forEach(function(encoding){encoding.priority="high";encoding.networkPriority="high"})}this._sender.setParameters(params)};PeerConnection.prototype._setupPeerConnection=function(rtcConstraints,rtcConfiguration){var _this=this;var self=this;var version=new(this.options.rtcpcFactory||rtcpc_1.default)({RTCPeerConnection:this.options.RTCPeerConnection});version.create(rtcConstraints,rtcConfiguration);addStream(version.pc,this.stream);var eventName="ontrack"in version.pc?"ontrack":"onaddstream";version.pc[eventName]=function(event){var stream=self._remoteStream=event.stream||event.streams[0];if(typeof version.pc.getSenders==="function"){_this._sender=version.pc.getSenders()[0]}if(self._isSinkSupported){self._onAddTrack(self,stream)}else{self._fallbackOnAddTrack(self,stream)}self._startPollingVolume()};return version};PeerConnection.prototype._maybeSetIceAggressiveNomination=function(sdp){return this.options.forceAggressiveIceNomination?sdp_1.setIceAggressiveNomination(sdp):sdp};PeerConnection.prototype._setupChannel=function(){var _this=this;var pc=this.version.pc;this.version.pc.onopen=function(){_this.status="open";_this.onopen()};this.version.pc.onstatechange=function(){if(_this.version.pc&&_this.version.pc.readyState==="stable"){_this.status="open";_this.onopen()}};this.version.pc.onsignalingstatechange=function(){var state=pc.signalingState;_this._log.info('signalingState is "'+state+'"');if(_this.version.pc&&_this.version.pc.signalingState==="stable"){_this.status="open";_this.onopen()}_this.onsignalingstatechange(pc.signalingState)};pc.onconnectionstatechange=function(event){var state=pc.connectionState;if(!state&&event&&event.target){var targetPc=event.target;state=targetPc.connectionState||targetPc.connectionState_;_this._log.info("pc.connectionState not detected. Using target PC. State="+state)}if(!state){_this._log.warn('onconnectionstatechange detected but state is "'+state+'"')}else{_this._log.info('pc.connectionState is "'+state+'"')}_this.onpcconnectionstatechange(state);_this._onMediaConnectionStateChange(state)};pc.onicecandidate=function(event){var candidate=event.candidate;if(candidate){_this._hasIceCandidates=true;_this.onicecandidate(candidate);_this._setupRTCIceTransportListener()}_this._log.info("ICE Candidate: "+JSON.stringify(candidate))};pc.onicegatheringstatechange=function(){var state=pc.iceGatheringState;if(state==="gathering"){_this._startIceGatheringTimeout()}else if(state==="complete"){_this._stopIceGatheringTimeout();if(!_this._hasIceCandidates){_this._onIceGatheringFailure(ICE_GATHERING_FAIL_NONE)}if(_this._hasIceCandidates&&_this._hasIceGatheringFailures){_this._startIceGatheringTimeout()}}_this._log.info('pc.iceGatheringState is "'+pc.iceGatheringState+'"');_this.onicegatheringstatechange(state)};pc.oniceconnectionstatechange=function(){_this._log.info('pc.iceConnectionState is "'+pc.iceConnectionState+'"');_this.oniceconnectionstatechange(pc.iceConnectionState);_this._onMediaConnectionStateChange(pc.iceConnectionState)}};PeerConnection.prototype._initializeMediaStream=function(rtcConstraints,rtcConfiguration){if(this.status==="open"){return false}if(this.pstream.status==="disconnected"){this.onerror({info:{code:31e3,message:"Cannot establish connection. Client is disconnected",twilioError:new errors_1.SignalingErrors.ConnectionDisconnected}});this.close();return false}this.version=this._setupPeerConnection(rtcConstraints,rtcConfiguration);this._setupChannel();return true};PeerConnection.prototype._removeReconnectionListeners=function(){if(this.pstream){this.pstream.removeListener("answer",this._onAnswerOrRinging);this.pstream.removeListener("hangup",this._onHangup)}};PeerConnection.prototype._setupRTCDtlsTransportListener=function(){var _this=this;var dtlsTransport=this.getRTCDtlsTransport();if(!dtlsTransport||dtlsTransport.onstatechange){return}var handler=function(){_this._log.info('dtlsTransportState is "'+dtlsTransport.state+'"');_this.ondtlstransportstatechange(dtlsTransport.state)};handler();dtlsTransport.onstatechange=handler};PeerConnection.prototype._setupRTCIceTransportListener=function(){var _this=this;var iceTransport=this._getRTCIceTransport();if(!iceTransport||iceTransport.onselectedcandidatepairchange){return}iceTransport.onselectedcandidatepairchange=function(){return _this.onselectedcandidatepairchange(iceTransport.getSelectedCandidatePair())}};PeerConnection.prototype.iceRestart=function(){var _this=this;this._log.info("Attempting to restart ICE...");this._hasIceCandidates=false;this.version.createOffer(this.options.maxAverageBitrate,this.codecPreferences,{iceRestart:true}).then(function(){_this._removeReconnectionListeners();_this._onAnswerOrRinging=function(payload){_this._removeReconnectionListeners();if(!payload.sdp||_this.version.pc.signalingState!=="have-local-offer"){var message="Invalid state or param during ICE Restart:"+("hasSdp:"+!!payload.sdp+", signalingState:"+_this.version.pc.signalingState);_this._log.warn(message);return}var sdp=_this._maybeSetIceAggressiveNomination(payload.sdp);_this._answerSdp=sdp;if(_this.status!=="closed"){_this.version.processAnswer(_this.codecPreferences,sdp,null,function(err){var message=err&&err.message?err.message:err;_this._log.error("Failed to process answer during ICE Restart. Error: "+message)})}};_this._onHangup=function(){_this._log.info("Received hangup during ICE Restart");_this._removeReconnectionListeners()};_this.pstream.on("answer",_this._onAnswerOrRinging);_this.pstream.on("hangup",_this._onHangup);_this.pstream.reinvite(_this.version.getSDP(),_this.callSid)}).catch(function(err){var message=err&&err.message?err.message:err;_this._log.error("Failed to createOffer during ICE Restart. Error: "+message);_this.onfailed(message)})};PeerConnection.prototype.makeOutgoingCall=function(token,params,callsid,rtcConstraints,rtcConfiguration,onMediaStarted){var _this=this;if(!this._initializeMediaStream(rtcConstraints,rtcConfiguration)){return}var self=this;this.callSid=callsid;function onAnswerSuccess(){if(self.options){self._setEncodingParameters(self.options.dscp)}onMediaStarted(self.version.pc)}function onAnswerError(err){var errMsg=err.message||err;self.onerror({info:{code:31e3,message:"Error processing answer: "+errMsg,twilioError:new errors_1.MediaErrors.ClientRemoteDescFailed}})}this._onAnswerOrRinging=function(payload){if(!payload.sdp){return}var sdp=_this._maybeSetIceAggressiveNomination(payload.sdp);self._answerSdp=sdp;if(self.status!=="closed"){self.version.processAnswer(_this.codecPreferences,sdp,onAnswerSuccess,onAnswerError)}self.pstream.removeListener("answer",self._onAnswerOrRinging);self.pstream.removeListener("ringing",self._onAnswerOrRinging)};this.pstream.on("answer",this._onAnswerOrRinging);this.pstream.on("ringing",this._onAnswerOrRinging);function onOfferSuccess(){if(self.status!=="closed"){self.pstream.invite(self.version.getSDP(),self.callSid,self.options.preflight,params);self._setupRTCDtlsTransportListener()}}function onOfferError(err){var errMsg=err.message||err;self.onerror({info:{code:31e3,message:"Error creating the offer: "+errMsg,twilioError:new errors_1.MediaErrors.ClientLocalDescFailed}})}this.version.createOffer(this.options.maxAverageBitrate,this.codecPreferences,{audio:true},onOfferSuccess,onOfferError)};PeerConnection.prototype.answerIncomingCall=function(callSid,sdp,rtcConstraints,rtcConfiguration,onMediaStarted){if(!this._initializeMediaStream(rtcConstraints,rtcConfiguration)){return}sdp=this._maybeSetIceAggressiveNomination(sdp);this._answerSdp=sdp.replace(/^a=setup:actpass$/gm,"a=setup:passive");this.callSid=callSid;var self=this;function onAnswerSuccess(){if(self.status!=="closed"){self.pstream.answer(self.version.getSDP(),callSid);if(self.options){self._setEncodingParameters(self.options.dscp)}onMediaStarted(self.version.pc);self._setupRTCDtlsTransportListener()}}function onAnswerError(err){var errMsg=err.message||err;self.onerror({info:{code:31e3,message:"Error creating the answer: "+errMsg,twilioError:new errors_1.MediaErrors.ClientRemoteDescFailed}})}this.version.processSDP(this.options.maxAverageBitrate,this.codecPreferences,sdp,{audio:true},onAnswerSuccess,onAnswerError)};PeerConnection.prototype.close=function(){if(this.version&&this.version.pc){if(this.version.pc.signalingState!=="closed"){this.version.pc.close()}this.version.pc=null}if(this.stream){this.mute(false);this._stopStream()}this.stream=null;this._removeReconnectionListeners();this._stopIceGatheringTimeout();Promise.all(this._removeAudioOutputs()).catch(function(){});if(this._mediaStreamSource){this._mediaStreamSource.disconnect()}if(this._inputAnalyser){this._inputAnalyser.disconnect()}if(this._outputAnalyser){this._outputAnalyser.disconnect()}if(this._inputAnalyser2){this._inputAnalyser2.disconnect()}if(this._outputAnalyser2){this._outputAnalyser2.disconnect()}this.status="closed";this.onclose()};PeerConnection.prototype.reject=function(callSid){this.callSid=callSid};PeerConnection.prototype.ignore=function(callSid){this.callSid=callSid};PeerConnection.prototype.mute=function(shouldMute){this.isMuted=shouldMute;if(!this.stream){return}if(this._sender&&this._sender.track){this._sender.track.enabled=!shouldMute}else{var audioTracks=typeof this.stream.getAudioTracks==="function"?this.stream.getAudioTracks():this.stream.audioTracks;audioTracks.forEach(function(track){track.enabled=!shouldMute})}};PeerConnection.prototype.getOrCreateDTMFSender=function getOrCreateDTMFSender(){if(this._dtmfSender||this._dtmfSenderUnsupported){return this._dtmfSender||null}var self=this;var pc=this.version.pc;if(!pc){this._log.warn("No RTCPeerConnection available to call createDTMFSender on");return null}if(typeof pc.getSenders==="function"&&(typeof RTCDTMFSender==="function"||typeof RTCDtmfSender==="function")){var chosenSender=pc.getSenders().find(function(sender){return sender.dtmf});if(chosenSender){this._log.info("Using RTCRtpSender#dtmf");this._dtmfSender=chosenSender.dtmf;return this._dtmfSender}}if(typeof pc.createDTMFSender==="function"&&typeof pc.getLocalStreams==="function"){var track=pc.getLocalStreams().map(function(stream){var tracks=self._getAudioTracks(stream);return tracks&&tracks[0]})[0];if(!track){this._log.warn("No local audio MediaStreamTrack available on the RTCPeerConnection to pass to createDTMFSender");return null}this._log.info("Creating RTCDTMFSender");this._dtmfSender=pc.createDTMFSender(track);return this._dtmfSender}this._log.info("RTCPeerConnection does not support RTCDTMFSender");this._dtmfSenderUnsupported=true;return null};PeerConnection.prototype.getRTCDtlsTransport=function getRTCDtlsTransport(){var sender=this.version&&this.version.pc&&typeof this.version.pc.getSenders==="function"&&this.version.pc.getSenders()[0];return sender&&sender.transport||null};PeerConnection.prototype._canStopMediaStreamTrack=function(){return typeof MediaStreamTrack.prototype.stop==="function"};PeerConnection.prototype._getAudioTracks=function(stream){return typeof stream.getAudioTracks==="function"?stream.getAudioTracks():stream.audioTracks};PeerConnection.prototype._getRTCIceTransport=function _getRTCIceTransport(){var dtlsTransport=this.getRTCDtlsTransport();return dtlsTransport&&dtlsTransport.iceTransport||null};PeerConnection.protocol=function(){return rtcpc_1.default.test()?new rtcpc_1.default:null}();function addStream(pc,stream){if(typeof pc.addTrack==="function"){stream.getAudioTracks().forEach(function(track){pc.addTrack(track,stream)})}else{pc.addStream(stream)}}function cloneStream(oldStream){var newStream=typeof MediaStream!=="undefined"?new MediaStream:new webkitMediaStream;oldStream.getAudioTracks().forEach(newStream.addTrack,newStream);return newStream}function removeStream(pc,stream){if(typeof pc.removeTrack==="function"){pc.getSenders().forEach(function(sender){pc.removeTrack(sender)})}else{pc.removeStream(stream)}}function setAudioSource(audio,stream){if(typeof audio.srcObject!=="undefined"){audio.srcObject=stream}else if(typeof audio.mozSrcObject!=="undefined"){audio.mozSrcObject=stream}else if(typeof audio.src!=="undefined"){var _window=audio.options.window||window;audio.src=(_window.URL||_window.webkitURL).createObjectURL(stream)}else{return false}return true}PeerConnection.enabled=rtcpc_1.default.test();exports.default=PeerConnection},{"../errors":15,"../log":18,"../util":36,"./rtcpc":30,"./sdp":31}],30:[function(require,module,exports){(function(global){(function(){"use strict";Object.defineProperty(exports,"__esModule",{value:true});var log_1=require("../log");var util=require("../util");var sdp_1=require("./sdp");var RTCPeerConnectionShim=require("rtcpeerconnection-shim");function RTCPC(options){if(typeof window==="undefined"){this.log.info("No RTCPeerConnection implementation available. The window object was not found.");return}if(options&&options.RTCPeerConnection){this.RTCPeerConnection=options.RTCPeerConnection}else if(util.isLegacyEdge()){this.RTCPeerConnection=new RTCPeerConnectionShim(typeof window!=="undefined"?window:global)}else if(typeof window.RTCPeerConnection==="function"){this.RTCPeerConnection=window.RTCPeerConnection}else if(typeof window.webkitRTCPeerConnection==="function"){this.RTCPeerConnection=webkitRTCPeerConnection}else if(typeof window.mozRTCPeerConnection==="function"){this.RTCPeerConnection=mozRTCPeerConnection;window.RTCSessionDescription=mozRTCSessionDescription;window.RTCIceCandidate=mozRTCIceCandidate}else{this.log.info("No RTCPeerConnection implementation available")}}RTCPC.prototype.create=function(rtcConstraints,rtcConfiguration){this.log=new log_1.default("RTCPC");this.pc=new this.RTCPeerConnection(rtcConfiguration,rtcConstraints)};RTCPC.prototype.createModernConstraints=function(c){if(typeof c==="undefined"){return null}var nc=Object.assign({},c);if(typeof webkitRTCPeerConnection!=="undefined"&&!util.isLegacyEdge()){nc.mandatory={};if(typeof c.audio!=="undefined"){nc.mandatory.OfferToReceiveAudio=c.audio}if(typeof c.video!=="undefined"){nc.mandatory.OfferToReceiveVideo=c.video}}else{if(typeof c.audio!=="undefined"){nc.offerToReceiveAudio=c.audio}if(typeof c.video!=="undefined"){nc.offerToReceiveVideo=c.video}}delete nc.audio;delete nc.video;return nc};RTCPC.prototype.createOffer=function(maxAverageBitrate,codecPreferences,constraints,onSuccess,onError){var _this=this;constraints=this.createModernConstraints(constraints);return promisifyCreate(this.pc.createOffer,this.pc)(constraints).then(function(offer){if(!_this.pc){return Promise.resolve()}var sdp=sdp_1.setMaxAverageBitrate(offer.sdp,maxAverageBitrate);return promisifySet(_this.pc.setLocalDescription,_this.pc)(new RTCSessionDescription({sdp:sdp_1.setCodecPreferences(sdp,codecPreferences),type:"offer"}))}).then(onSuccess,onError)};RTCPC.prototype.createAnswer=function(maxAverageBitrate,codecPreferences,constraints,onSuccess,onError){var _this=this;constraints=this.createModernConstraints(constraints);return promisifyCreate(this.pc.createAnswer,this.pc)(constraints).then(function(answer){if(!_this.pc){return Promise.resolve()}var sdp=sdp_1.setMaxAverageBitrate(answer.sdp,maxAverageBitrate);return promisifySet(_this.pc.setLocalDescription,_this.pc)(new RTCSessionDescription({sdp:sdp_1.setCodecPreferences(sdp,codecPreferences),type:"answer"}))}).then(onSuccess,onError)};RTCPC.prototype.processSDP=function(maxAverageBitrate,codecPreferences,sdp,constraints,onSuccess,onError){var _this=this;sdp=sdp_1.setCodecPreferences(sdp,codecPreferences);var desc=new RTCSessionDescription({sdp:sdp,type:"offer"});return promisifySet(this.pc.setRemoteDescription,this.pc)(desc).then(function(){_this.createAnswer(maxAverageBitrate,codecPreferences,constraints,onSuccess,onError)})};RTCPC.prototype.getSDP=function(){return this.pc.localDescription.sdp};RTCPC.prototype.processAnswer=function(codecPreferences,sdp,onSuccess,onError){if(!this.pc){return Promise.resolve()}sdp=sdp_1.setCodecPreferences(sdp,codecPreferences);return promisifySet(this.pc.setRemoteDescription,this.pc)(new RTCSessionDescription({sdp:sdp,type:"answer"})).then(onSuccess,onError)};RTCPC.test=function(){if(typeof navigator==="object"){var getUserMedia=navigator.mediaDevices&&navigator.mediaDevices.getUserMedia||navigator.webkitGetUserMedia||navigator.mozGetUserMedia||navigator.getUserMedia;if(util.isLegacyEdge(navigator)){return false}if(getUserMedia&&typeof window.RTCPeerConnection==="function"){return true}else if(getUserMedia&&typeof window.webkitRTCPeerConnection==="function"){return true}else if(getUserMedia&&typeof window.mozRTCPeerConnection==="function"){try{var test_1=new window.mozRTCPeerConnection;if(typeof test_1.getLocalStreams!=="function"){return false}}catch(e){return false}return true}else if(typeof RTCIceGatherer!=="undefined"){return true}}return false};function promisify(fn,ctx,areCallbacksFirst,checkRval){return function(){var args=Array.prototype.slice.call(arguments);return new Promise(function(resolve){var returnValue=fn.apply(ctx,args);if(!checkRval){resolve(returnValue);return}if(typeof returnValue==="object"&&typeof returnValue.then==="function"){resolve(returnValue)}else{throw new Error}}).catch(function(){return new Promise(function(resolve,reject){fn.apply(ctx,areCallbacksFirst?[resolve,reject].concat(args):args.concat([resolve,reject]))})})}}function promisifyCreate(fn,ctx){return promisify(fn,ctx,true,true)}function promisifySet(fn,ctx){return promisify(fn,ctx,false,false)}exports.default=RTCPC}).call(this)}).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{"../log":18,"../util":36,"./sdp":31,"rtcpeerconnection-shim":45}],31:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.setMaxAverageBitrate=exports.setIceAggressiveNomination=exports.setCodecPreferences=exports.getPreferredCodecInfo=void 0;var util=require("../util");var ptToFixedBitrateAudioCodecName={0:"PCMU",8:"PCMA"};var defaultOpusId=111;var BITRATE_MAX=51e4;var BITRATE_MIN=6e3;function getPreferredCodecInfo(sdp){var _a=/a=rtpmap:(\d+) (\S+)/m.exec(sdp)||[null,"",""],codecId=_a[1],codecName=_a[2];var regex=new RegExp("a=fmtp:"+codecId+" (\\S+)","m");var _b=regex.exec(sdp)||[null,""],codecParams=_b[1];return{codecName:codecName,codecParams:codecParams}}exports.getPreferredCodecInfo=getPreferredCodecInfo;function setIceAggressiveNomination(sdp){if(!util.isChrome(window,window.navigator)){return sdp}return sdp.split("\n").filter(function(line){return line.indexOf("a=ice-lite")===-1}).join("\n")}exports.setIceAggressiveNomination=setIceAggressiveNomination;function setMaxAverageBitrate(sdp,maxAverageBitrate){if(typeof maxAverageBitrate!=="number"||maxAverageBitrateBITRATE_MAX){return sdp}var matches=/a=rtpmap:(\d+) opus/m.exec(sdp);var opusId=matches&&matches.length?matches[1]:defaultOpusId;var regex=new RegExp("a=fmtp:"+opusId);var lines=sdp.split("\n").map(function(line){return regex.test(line)?line+(";maxaveragebitrate="+maxAverageBitrate):line});return lines.join("\n")}exports.setMaxAverageBitrate=setMaxAverageBitrate;function setCodecPreferences(sdp,preferredCodecs){var mediaSections=getMediaSections(sdp);var session=sdp.split("\r\nm=")[0];return[session].concat(mediaSections.map(function(section){if(!/^m=(audio|video)/.test(section)){return section}var kind=section.match(/^m=(audio|video)/)[1];var codecMap=createCodecMapForMediaSection(section);var payloadTypes=getReorderedPayloadTypes(codecMap,preferredCodecs);var newSection=setPayloadTypesInMediaSection(payloadTypes,section);var pcmaPayloadTypes=codecMap.get("pcma")||[];var pcmuPayloadTypes=codecMap.get("pcmu")||[];var fixedBitratePayloadTypes=kind==="audio"?new Set(pcmaPayloadTypes.concat(pcmuPayloadTypes)):new Set;return fixedBitratePayloadTypes.has(payloadTypes[0])?newSection.replace(/\r\nb=(AS|TIAS):([0-9]+)/g,""):newSection})).join("\r\n")}exports.setCodecPreferences=setCodecPreferences;function getMediaSections(sdp,kind,direction){return sdp.replace(/\r\n\r\n$/,"\r\n").split("\r\nm=").slice(1).map(function(mediaSection){return"m="+mediaSection}).filter(function(mediaSection){var kindPattern=new RegExp("m="+(kind||".*"),"gm");var directionPattern=new RegExp("a="+(direction||".*"),"gm");return kindPattern.test(mediaSection)&&directionPattern.test(mediaSection)})}function createCodecMapForMediaSection(section){return Array.from(createPtToCodecName(section)).reduce(function(codecMap,pair){var pt=pair[0];var codecName=pair[1];var pts=codecMap.get(codecName)||[];return codecMap.set(codecName,pts.concat(pt))},new Map)}function getReorderedPayloadTypes(codecMap,preferredCodecs){preferredCodecs=preferredCodecs.map(function(codecName){return codecName.toLowerCase()});var preferredPayloadTypes=util.flatMap(preferredCodecs,function(codecName){return codecMap.get(codecName)||[]});var remainingCodecs=util.difference(Array.from(codecMap.keys()),preferredCodecs);var remainingPayloadTypes=util.flatMap(remainingCodecs,function(codecName){return codecMap.get(codecName)});return preferredPayloadTypes.concat(remainingPayloadTypes)}function setPayloadTypesInMediaSection(payloadTypes,section){var lines=section.split("\r\n");var mLine=lines[0];var otherLines=lines.slice(1);mLine=mLine.replace(/([0-9]+\s?)+$/,payloadTypes.join(" "));return[mLine].concat(otherLines).join("\r\n")}function createPtToCodecName(mediaSection){return getPayloadTypesInMediaSection(mediaSection).reduce(function(ptToCodecName,pt){var rtpmapPattern=new RegExp("a=rtpmap:"+pt+" ([^/]+)");var matches=mediaSection.match(rtpmapPattern);var codecName=matches?matches[1].toLowerCase():ptToFixedBitrateAudioCodecName[pt]?ptToFixedBitrateAudioCodecName[pt].toLowerCase():"";return ptToCodecName.set(pt,codecName)},new Map)}function getPayloadTypesInMediaSection(section){var mLine=section.split("\r\n")[0];var matches=mLine.match(/([0-9]+)/g);if(!matches){return[]}return matches.slice(1).map(function(match){return parseInt(match,10)})}},{"../util":36}],32:[function(require,module,exports){"use strict";var __spreadArrays=this&&this.__spreadArrays||function(){for(var s=0,i=0,il=arguments.length;i0){this._maxDurationTimeout=setTimeout(this._stop.bind(this),this._maxDuration)}forceShouldLoop=typeof forceShouldLoop==="boolean"?forceShouldLoop:this._shouldLoop;var self=this;var playPromise=this._playPromise=Promise.all(this._sinkIds.map(function createAudioElement(sinkId){if(!self._Audio){return Promise.resolve()}var audioElement=self._activeEls.get(sinkId);if(audioElement){return self._playAudioElement(sinkId,forceIsMuted,forceShouldLoop)}audioElement=new self._Audio(self.url);if(typeof audioElement.setAttribute==="function"){audioElement.setAttribute("crossorigin","anonymous")}return new Promise(function(resolve){audioElement.addEventListener("canplaythrough",resolve)}).then(function(){return(self._isSinkSupported?audioElement.setSinkId(sinkId):Promise.resolve()).then(function setSinkIdSuccess(){self._activeEls.set(sinkId,audioElement);if(!self._playPromise){return Promise.resolve()}return self._playAudioElement(sinkId,forceIsMuted,forceShouldLoop)})})}));return playPromise};Sound.prototype._stop=function _stop(){var _this=this;this._activeEls.forEach(function(audioEl,sinkId){if(_this._sinkIds.includes(sinkId)){audioEl.pause();audioEl.currentTime=0}else{destroyAudioElement(audioEl);_this._activeEls.delete(sinkId)}});clearTimeout(this._maxDurationTimeout);this._playPromise=null;this._maxDurationTimeout=null};Sound.prototype.setSinkIds=function setSinkIds(ids){if(!this._isSinkSupported){return}ids=ids.forEach?ids:[ids];[].splice.apply(this._sinkIds,[0,this._sinkIds.length].concat(ids))};Sound.prototype.stop=function stop(){var _this=this;this._operations.enqueue(function(){_this._stop();return Promise.resolve()})};Sound.prototype.play=function play(){var _this=this;return this._operations.enqueue(function(){return _this._play()})};exports.default=Sound},{"./asyncQueue":2,"./audioplayer/audioplayer":4,"./errors":15}],35:[function(require,module,exports){"use strict";var __extends=this&&this.__extends||function(){var extendStatics=function(d,b){extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(d,b){d.__proto__=b}||function(d,b){for(var p in b)if(Object.prototype.hasOwnProperty.call(b,p))d[p]=b[p]};return extendStatics(d,b)};return function(d,b){extendStatics(d,b);function __(){this.constructor=d}d.prototype=b===null?Object.create(b):(__.prototype=b.prototype,new __)}}();var __assign=this&&this.__assign||function(){__assign=Object.assign||function(t){for(var s,i=1,n=arguments.length;imax?1:0},0)}function countLow(min,values){return values.reduce(function(lowCount,value){return lowCount+=valuethis._maxSampleCount){samples.splice(0,samples.length-this._maxSampleCount)}};StatsMonitor.prototype._clearWarning=function(statName,thresholdName,data){var warningId=statName+":"+thresholdName;var activeWarning=this._activeWarnings.get(warningId);if(!activeWarning||Date.now()-activeWarning.timeRaised0?currentPacketsLost/currentInboundPackets*100:0;var totalInboundPackets=stats.packetsReceived+stats.packetsLost;var totalPacketsLostFraction=totalInboundPackets>0?stats.packetsLost/totalInboundPackets*100:100;var rttValue=typeof stats.rtt==="number"||!previousSample?stats.rtt:previousSample.rtt;var audioInputLevelValues=this._inputVolumes.splice(0);this._supplementalSampleBuffers.audioInputLevel.push(audioInputLevelValues);var audioOutputLevelValues=this._outputVolumes.splice(0);this._supplementalSampleBuffers.audioOutputLevel.push(audioOutputLevelValues);return{audioInputLevel:Math.round(util_1.average(audioInputLevelValues)),audioOutputLevel:Math.round(util_1.average(audioOutputLevelValues)),bytesReceived:currentBytesReceived,bytesSent:currentBytesSent,codecName:stats.codecName,jitter:stats.jitter,mos:this._mos.calculate(rttValue,stats.jitter,previousSample&¤tPacketsLostFraction),packetsLost:currentPacketsLost,packetsLostFraction:currentPacketsLostFraction,packetsReceived:currentPacketsReceived,packetsSent:currentPacketsSent,rtt:rttValue,timestamp:stats.timestamp,totals:{bytesReceived:stats.bytesReceived,bytesSent:stats.bytesSent,packetsLost:stats.packetsLost,packetsLostFraction:totalPacketsLostFraction,packetsReceived:stats.packetsReceived,packetsSent:stats.packetsSent}}};StatsMonitor.prototype._fetchSample=function(){var _this=this;this._getSample().then(function(sample){_this._addSample(sample);_this._raiseWarnings();_this.emit("sample",sample)}).catch(function(error){_this.disable();_this.emit("error",error)})};StatsMonitor.prototype._getSample=function(){var _this=this;return this._getRTCStats(this._peerConnection).then(function(stats){var previousSample=null;if(_this._sampleBuffer.length){previousSample=_this._sampleBuffer[_this._sampleBuffer.length-1]}return _this._createSample(stats,previousSample)})};StatsMonitor.prototype._raiseWarning=function(statName,thresholdName,data){var warningId=statName+":"+thresholdName;if(this._activeWarnings.has(warningId)){return}this._activeWarnings.set(warningId,{timeRaised:Date.now()});var thresholds=this._thresholds[statName];var thresholdValue;if(Array.isArray(thresholds)){var foundThreshold=thresholds.find(function(threshold){return thresholdName in threshold});if(foundThreshold){thresholdValue=foundThreshold[thresholdName]}}else{thresholdValue=this._thresholds[statName][thresholdName]}this.emit("warning",__assign(__assign({},data),{name:statName,threshold:{name:thresholdName,value:thresholdValue}}))};StatsMonitor.prototype._raiseWarnings=function(){var _this=this;if(!this._warningsEnabled){return}Object.keys(this._thresholds).forEach(function(name){return _this._raiseWarningsForStat(name)})};StatsMonitor.prototype._raiseWarningsForStat=function(statName){var _this=this;var limits=Array.isArray(this._thresholds[statName])?this._thresholds[statName]:[this._thresholds[statName]];limits.forEach(function(limit){var samples=_this._sampleBuffer;var clearCount=limit.clearCount||SAMPLE_COUNT_CLEAR;var raiseCount=limit.raiseCount||SAMPLE_COUNT_RAISE;var sampleCount=limit.sampleCount||_this._maxSampleCount;var relevantSamples=samples.slice(-sampleCount);var values=relevantSamples.map(function(sample){return sample[statName]});var containsNull=values.some(function(value){return typeof value==="undefined"||value===null});if(containsNull){return}var count;if(typeof limit.max==="number"){count=countHigh(limit.max,values);if(count>=raiseCount){_this._raiseWarning(statName,"max",{values:values,samples:relevantSamples})}else if(count<=clearCount){_this._clearWarning(statName,"max",{values:values,samples:relevantSamples})}}if(typeof limit.min==="number"){count=countLow(limit.min,values);if(count>=raiseCount){_this._raiseWarning(statName,"min",{values:values,samples:relevantSamples})}else if(count<=clearCount){_this._clearWarning(statName,"min",{values:values,samples:relevantSamples})}}if(typeof limit.maxDuration==="number"&&samples.length>1){relevantSamples=samples.slice(-2);var prevValue=relevantSamples[0][statName];var curValue=relevantSamples[1][statName];var prevStreak=_this._currentStreaks.get(statName)||0;var streak=prevValue===curValue?prevStreak+1:0;_this._currentStreaks.set(statName,streak);if(streak>=limit.maxDuration){_this._raiseWarning(statName,"maxDuration",{value:streak})}else if(streak===0){_this._clearWarning(statName,"maxDuration",{value:prevStreak})}}if(typeof limit.minStandardDeviation==="number"){var sampleSets=_this._supplementalSampleBuffers[statName];if(!sampleSets||sampleSets.lengthlimit.sampleCount){sampleSets.splice(0,sampleSets.length-limit.sampleCount)}var flatSamples=flattenSamples(sampleSets.slice(-sampleCount));var stdDev=calculateStandardDeviation(flatSamples);if(typeof stdDev!=="number"){return}if(stdDevy}],["minAverage",function(x,y){return x=sampleCount){var avg=util_1.average(values);if(comparator(avg,limit[thresholdName])){_this._raiseWarning(statName,thresholdName,{values:values,samples:relevantSamples})}else if(!comparator(avg,limit.clearValue||limit[thresholdName])){_this._clearWarning(statName,thresholdName,{values:values,samples:relevantSamples})}}})})};return StatsMonitor}(events_1.EventEmitter);exports.default=StatsMonitor},{"./errors":15,"./rtc/mos":28,"./rtc/stats":32,"./util":36,events:39}],36:[function(require,module,exports){(function(global){(function(){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.flatMap=exports.queryToJson=exports.isUnifiedPlanDefault=exports.isSafari=exports.isLegacyEdge=exports.isFirefox=exports.isChrome=exports.isElectron=exports.difference=exports.average=exports.Exception=void 0;function TwilioException(message){if(!(this instanceof TwilioException)){return new TwilioException(message)}this.message=message}TwilioException.prototype.toString=function(){return"Twilio.Exception: "+this.message};function average(values){return values&&values.length?values.reduce(function(t,v){return t+v})/values.length:0}exports.average=average;function difference(lefts,rights,getKey){getKey=getKey||function(a){return a};var rightKeys=new Set(rights.map(getKey));return lefts.filter(function(left){return!rightKeys.has(getKey(left))})}exports.difference=difference;function isElectron(navigator){return!!navigator.userAgent.match("Electron")}exports.isElectron=isElectron;function isChrome(window,navigator){var isCriOS=!!navigator.userAgent.match("CriOS");var isHeadlessChrome=!!navigator.userAgent.match("HeadlessChrome");var isGoogle=typeof window.chrome!=="undefined"&&navigator.vendor==="Google Inc."&&navigator.userAgent.indexOf("OPR")===-1&&navigator.userAgent.indexOf("Edge")===-1;return isCriOS||isElectron(navigator)||isGoogle||isHeadlessChrome}exports.isChrome=isChrome;function isFirefox(navigator){navigator=navigator||(typeof window==="undefined"?global.navigator:window.navigator);return!!navigator&&typeof navigator.userAgent==="string"&&/firefox|fxios/i.test(navigator.userAgent)}exports.isFirefox=isFirefox;function isLegacyEdge(navigator){navigator=navigator||(typeof window==="undefined"?global.navigator:window.navigator);return!!navigator&&typeof navigator.userAgent==="string"&&/edge\/\d+/i.test(navigator.userAgent)}exports.isLegacyEdge=isLegacyEdge;function isSafari(navigator){return!!navigator.vendor&&navigator.vendor.indexOf("Apple")!==-1&&navigator.userAgent&&navigator.userAgent.indexOf("CriOS")===-1&&navigator.userAgent.indexOf("FxiOS")===-1}exports.isSafari=isSafari;function isUnifiedPlanDefault(window,navigator,PeerConnection,RtpTransceiver){if(typeof window==="undefined"||typeof navigator==="undefined"||typeof PeerConnection==="undefined"||typeof RtpTransceiver==="undefined"||typeof PeerConnection.prototype==="undefined"||typeof RtpTransceiver.prototype==="undefined"){return false}if(isChrome(window,navigator)&&PeerConnection.prototype.addTransceiver){var pc=new PeerConnection;var isUnifiedPlan=true;try{pc.addTransceiver("audio")}catch(e){isUnifiedPlan=false}pc.close();return isUnifiedPlan}else if(isFirefox(navigator)){return true}else if(isSafari(navigator)){return"currentDirection"in RtpTransceiver.prototype}return false}exports.isUnifiedPlanDefault=isUnifiedPlanDefault;function queryToJson(params){if(!params){return""}return params.split("&").reduce(function(output,pair){var parts=pair.split("=");var key=parts[0];var value=decodeURIComponent((parts[1]||"").replace(/\+/g,"%20"));if(key){output[key]=value}return output},{})}exports.queryToJson=queryToJson;function flatMap(list,mapFn){var listArray=list instanceof Map||list instanceof Set?Array.from(list.values()):list;mapFn=mapFn||function(item){return item};return listArray.reduce(function(flattened,item){var mapped=mapFn(item);return flattened.concat(mapped)},[])}exports.flatMap=flatMap;var Exception=TwilioException;exports.Exception=Exception}).call(this)}).call(this,typeof global!=="undefined"?global:typeof self!=="undefined"?self:typeof window!=="undefined"?window:{})},{}],37:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.generateVoiceEventSid=void 0;var md5Lib=require("md5");var errors_1=require("../twilio/errors");var md5=typeof md5Lib==="function"?md5Lib:md5Lib.default;function generateUuid(){if(typeof window!=="object"){throw new errors_1.NotSupportedError("This platform is not supported.")}var crypto=window.crypto;if(typeof crypto!=="object"){throw new errors_1.NotSupportedError("The `crypto` module is not available on this platform.")}if(typeof(crypto.randomUUID||crypto.getRandomValues)==="undefined"){throw new errors_1.NotSupportedError("Neither `crypto.randomUUID` or `crypto.getRandomValues` are available "+"on this platform.")}var uInt32Arr=window.Uint32Array;if(typeof uInt32Arr==="undefined"){throw new errors_1.NotSupportedError("The `Uint32Array` module is not available on this platform.")}var generateRandomValues=typeof crypto.randomUUID==="function"?function(){return crypto.randomUUID()}:function(){return crypto.getRandomValues(new Uint32Array(32)).toString()};return md5(generateRandomValues())}function generateVoiceEventSid(){return"KX"+generateUuid()}exports.generateVoiceEventSid=generateVoiceEventSid},{"../twilio/errors":15,md5:44}],38:[function(require,module,exports){"use strict";var __extends=this&&this.__extends||function(){var extendStatics=function(d,b){extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(d,b){d.__proto__=b}||function(d,b){for(var p in b)if(Object.prototype.hasOwnProperty.call(b,p))d[p]=b[p]};return extendStatics(d,b)};return function(d,b){extendStatics(d,b);function __(){this.constructor=d}d.prototype=b===null?Object.create(b):(__.prototype=b.prototype,new __)}}();var __assign=this&&this.__assign||function(){__assign=Object.assign||function(t){for(var s,i=1,n=arguments.length;i=_this._uris.length){_this._uriIndex=0}};_this._onSocketClose=function(event){_this._log.error("Received websocket close event code: "+event.code+". Reason: "+event.reason);if(event.code===1006||event.code===1015){_this.emit("error",{code:31005,message:event.reason||"Websocket connection to Twilio's signaling servers were "+"unexpectedly ended. If this is happening consistently, there may "+"be an issue resolving the hostname provided. If a region or an "+"edge is being specified in Device setup, ensure it is valid.",twilioError:new errors_1.SignalingErrors.ConnectionError});var wasConnected=_this.state===WSTransportState.Open||_this._previousState===WSTransportState.Open;if(_this._shouldFallback||!wasConnected){_this._moveUriIndex()}_this._shouldFallback=true}_this._closeSocket()};_this._onSocketError=function(err){_this._log.error("WebSocket received error: "+err.message);_this.emit("error",{code:31e3,message:err.message||"WSTransport socket error",twilioError:new errors_1.SignalingErrors.ConnectionDisconnected})};_this._onSocketMessage=function(message){_this._setHeartbeatTimeout();if(_this._socket&&message.data==="\n"){_this._socket.send("\n");return}_this.emit("message",message)};_this._onSocketOpen=function(){_this._log.info("WebSocket opened successfully.");_this._timeOpened=Date.now();_this._shouldFallback=false;_this._setState(WSTransportState.Open);clearTimeout(_this._connectTimeout);_this._resetBackoffs();_this._setHeartbeatTimeout();_this.emit("open")};_this._options=__assign(__assign({},WSTransport.defaultConstructorOptions),options);_this._uris=uris;_this._backoff=_this._setupBackoffs();return _this}WSTransport.prototype.close=function(){this._log.info("WSTransport.close() called...");this._close()};WSTransport.prototype.open=function(){this._log.info("WSTransport.open() called...");if(this._socket&&(this._socket.readyState===WebSocket.CONNECTING||this._socket.readyState===WebSocket.OPEN)){this._log.info("WebSocket already open.");return}if(this._preferredUri){this._connect(this._preferredUri)}else{this._connect(this._uris[this._uriIndex])}};WSTransport.prototype.send=function(message){if(!this._socket||this._socket.readyState!==WebSocket.OPEN){return false}try{this._socket.send(message)}catch(e){this._log.error("Error while sending message:",e.message);this._closeSocket();return false}return true};WSTransport.prototype.updatePreferredURI=function(uri){this._preferredUri=uri};WSTransport.prototype.updateURIs=function(uris){if(typeof uris==="string"){uris=[uris]}this._uris=uris;this._uriIndex=0};WSTransport.prototype._close=function(){this._setState(WSTransportState.Closed);this._closeSocket()};WSTransport.prototype._closeSocket=function(){clearTimeout(this._connectTimeout);clearTimeout(this._heartbeatTimeout);this._log.info("Closing and cleaning up WebSocket...");if(!this._socket){this._log.info("No WebSocket to clean up.");return}this._socket.removeEventListener("close",this._onSocketClose);this._socket.removeEventListener("error",this._onSocketError);this._socket.removeEventListener("message",this._onSocketMessage);this._socket.removeEventListener("open",this._onSocketOpen);if(this._socket.readyState===WebSocket.CONNECTING||this._socket.readyState===WebSocket.OPEN){this._socket.close()}if(this._timeOpened&&Date.now()-this._timeOpened>CONNECT_SUCCESS_TIMEOUT){this._resetBackoffs()}if(this.state!==WSTransportState.Closed){this._performBackoff()}delete this._socket;this.emit("close")};WSTransport.prototype._connect=function(uri,retryCount){var _this=this;this._log.info(typeof retryCount==="number"?"Attempting to reconnect (retry #"+retryCount+")...":"Attempting to connect...");this._closeSocket();this._setState(WSTransportState.Connecting);this._connectedUri=uri;try{this._socket=new this._options.WebSocket(this._connectedUri)}catch(e){this._log.error("Could not connect to endpoint:",e.message);this._close();this.emit("error",{code:31e3,message:e.message||"Could not connect to "+this._connectedUri,twilioError:new errors_1.SignalingErrors.ConnectionDisconnected});return}this._socket.addEventListener("close",this._onSocketClose);this._socket.addEventListener("error",this._onSocketError);this._socket.addEventListener("message",this._onSocketMessage);this._socket.addEventListener("open",this._onSocketOpen);delete this._timeOpened;this._connectTimeout=setTimeout(function(){_this._log.info("WebSocket connection attempt timed out.");_this._moveUriIndex();_this._closeSocket()},this._options.connectTimeoutMs)};WSTransport.prototype._performBackoff=function(){if(this._preferredUri){this._log.info("Preferred URI set; backing off.");this._backoff.preferred.backoff()}else{this._log.info("Preferred URI not set; backing off.");this._backoff.primary.backoff()}};WSTransport.prototype._resetBackoffs=function(){this._backoff.preferred.reset();this._backoff.primary.reset();this._backoffStartTime.preferred=null;this._backoffStartTime.primary=null};WSTransport.prototype._setHeartbeatTimeout=function(){var _this=this;clearTimeout(this._heartbeatTimeout);this._heartbeatTimeout=setTimeout(function(){_this._log.info("No messages received in "+HEARTBEAT_TIMEOUT/1e3+" seconds. Reconnecting...");_this._shouldFallback=true;_this._closeSocket()},HEARTBEAT_TIMEOUT)};WSTransport.prototype._setState=function(state){this._previousState=this.state;this.state=state};WSTransport.prototype._setupBackoffs=function(){var _this=this;var preferredBackoffConfig={factor:2,jitter:.4,max:this._options.maxPreferredDelayMs,min:100};this._log.info("Initializing preferred transport backoff using config: ",preferredBackoffConfig);var preferredBackoff=new backoff_1.default(preferredBackoffConfig);preferredBackoff.on("backoff",function(attempt,delay){if(_this.state===WSTransportState.Closed){_this._log.info("Preferred backoff initiated but transport state is closed; not attempting a connection.");return}_this._log.info("Will attempt to reconnect Websocket to preferred URI in "+delay+"ms");if(attempt===0){_this._backoffStartTime.preferred=Date.now();_this._log.info("Preferred backoff start; "+_this._backoffStartTime.preferred)}});preferredBackoff.on("ready",function(attempt,_delay){if(_this.state===WSTransportState.Closed){_this._log.info("Preferred backoff ready but transport state is closed; not attempting a connection.");return}if(_this._backoffStartTime.preferred===null){_this._log.info("Preferred backoff start time invalid; not attempting a connection.");return}if(Date.now()-_this._backoffStartTime.preferred>_this._options.maxPreferredDurationMs){_this._log.info("Max preferred backoff attempt time exceeded; falling back to primary backoff.");_this._preferredUri=null;_this._backoff.primary.backoff();return}if(typeof _this._preferredUri!=="string"){_this._log.info("Preferred URI cleared; falling back to primary backoff.");_this._preferredUri=null;_this._backoff.primary.backoff();return}_this._connect(_this._preferredUri,attempt+1)});var primaryBackoffConfig={factor:2,jitter:.4,max:this._options.maxPrimaryDelayMs,min:this._uris&&this._uris.length>1?Math.floor(Math.random()*(5e3-1e3+1))+1e3:100};this._log.info("Initializing primary transport backoff using config: ",primaryBackoffConfig);var primaryBackoff=new backoff_1.default(primaryBackoffConfig);primaryBackoff.on("backoff",function(attempt,delay){if(_this.state===WSTransportState.Closed){_this._log.info("Primary backoff initiated but transport state is closed; not attempting a connection.");return}_this._log.info("Will attempt to reconnect WebSocket in "+delay+"ms");if(attempt===0){_this._backoffStartTime.primary=Date.now();_this._log.info("Primary backoff start; "+_this._backoffStartTime.primary)}});primaryBackoff.on("ready",function(attempt,_delay){if(_this.state===WSTransportState.Closed){_this._log.info("Primary backoff ready but transport state is closed; not attempting a connection.");return}if(_this._backoffStartTime.primary===null){_this._log.info("Primary backoff start time invalid; not attempting a connection.");return}if(Date.now()-_this._backoffStartTime.primary>_this._options.maxPrimaryDurationMs){_this._log.info("Max primary backoff attempt time exceeded; not attempting a connection.");return}_this._connect(_this._uris[_this._uriIndex],attempt+1)});return{preferred:preferredBackoff,primary:primaryBackoff}};Object.defineProperty(WSTransport.prototype,"uri",{get:function(){return this._connectedUri},enumerable:false,configurable:true});WSTransport.defaultConstructorOptions={WebSocket:WebSocket,connectTimeoutMs:CONNECT_TIMEOUT,maxPreferredDelayMs:MAX_PREFERRED_DELAY,maxPreferredDurationMs:MAX_PREFERRED_DURATION,maxPrimaryDelayMs:MAX_PRIMARY_DELAY,maxPrimaryDurationMs:MAX_PRIMARY_DURATION};return WSTransport}(events_1.EventEmitter);exports.default=WSTransport},{"./backoff":8,"./errors":15,"./log":18,events:39}],39:[function(require,module,exports){var objectCreate=Object.create||objectCreatePolyfill;var objectKeys=Object.keys||objectKeysPolyfill;var bind=Function.prototype.bind||functionBindPolyfill;function EventEmitter(){if(!this._events||!Object.prototype.hasOwnProperty.call(this,"_events")){this._events=objectCreate(null);this._eventsCount=0}this._maxListeners=this._maxListeners||undefined}module.exports=EventEmitter;EventEmitter.EventEmitter=EventEmitter;EventEmitter.prototype._events=undefined;EventEmitter.prototype._maxListeners=undefined;var defaultMaxListeners=10;var hasDefineProperty;try{var o={};if(Object.defineProperty)Object.defineProperty(o,"x",{value:0});hasDefineProperty=o.x===0}catch(err){hasDefineProperty=false}if(hasDefineProperty){Object.defineProperty(EventEmitter,"defaultMaxListeners",{enumerable:true,get:function(){return defaultMaxListeners},set:function(arg){if(typeof arg!=="number"||arg<0||arg!==arg)throw new TypeError('"defaultMaxListeners" must be a positive number');defaultMaxListeners=arg}})}else{EventEmitter.defaultMaxListeners=defaultMaxListeners}EventEmitter.prototype.setMaxListeners=function setMaxListeners(n){if(typeof n!=="number"||n<0||isNaN(n))throw new TypeError('"n" argument must be a positive number');this._maxListeners=n;return this};function $getMaxListeners(that){if(that._maxListeners===undefined)return EventEmitter.defaultMaxListeners;return that._maxListeners}EventEmitter.prototype.getMaxListeners=function getMaxListeners(){return $getMaxListeners(this)};function emitNone(handler,isFn,self){if(isFn)handler.call(self);else{var len=handler.length;var listeners=arrayClone(handler,len);for(var i=0;i1)er=arguments[1];if(er instanceof Error){throw er}else{var err=new Error('Unhandled "error" event. ('+er+")");err.context=er;throw err}return false}handler=events[type];if(!handler)return false;var isFn=typeof handler==="function";len=arguments.length;switch(len){case 1:emitNone(handler,isFn,this);break;case 2:emitOne(handler,isFn,this,arguments[1]);break;case 3:emitTwo(handler,isFn,this,arguments[1],arguments[2]);break;case 4:emitThree(handler,isFn,this,arguments[1],arguments[2],arguments[3]);break;default:args=new Array(len-1);for(i=1;i0&&existing.length>m){existing.warned=true;var w=new Error("Possible EventEmitter memory leak detected. "+existing.length+' "'+String(type)+'" listeners '+"added. Use emitter.setMaxListeners() to "+"increase limit.");w.name="MaxListenersExceededWarning";w.emitter=target;w.type=type;w.count=existing.length;if(typeof console==="object"&&console.warn){console.warn("%s: %s",w.name,w.message)}}}}return target}EventEmitter.prototype.addListener=function addListener(type,listener){return _addListener(this,type,listener,false)};EventEmitter.prototype.on=EventEmitter.prototype.addListener;EventEmitter.prototype.prependListener=function prependListener(type,listener){return _addListener(this,type,listener,true)};function onceWrapper(){if(!this.fired){this.target.removeListener(this.type,this.wrapFn);this.fired=true;switch(arguments.length){case 0:return this.listener.call(this.target);case 1:return this.listener.call(this.target,arguments[0]);case 2:return this.listener.call(this.target,arguments[0],arguments[1]);case 3:return this.listener.call(this.target,arguments[0],arguments[1],arguments[2]);default:var args=new Array(arguments.length);for(var i=0;i=0;i--){if(list[i]===listener||list[i].listener===listener){originalListener=list[i].listener;position=i;break}}if(position<0)return this;if(position===0)list.shift();else spliceOne(list,position);if(list.length===1)events[type]=list[0];if(events.removeListener)this.emit("removeListener",type,originalListener||listener)}return this};EventEmitter.prototype.removeAllListeners=function removeAllListeners(type){var listeners,events,i;events=this._events;if(!events)return this;if(!events.removeListener){if(arguments.length===0){this._events=objectCreate(null);this._eventsCount=0}else if(events[type]){if(--this._eventsCount===0)this._events=objectCreate(null);else delete events[type]}return this}if(arguments.length===0){var keys=objectKeys(events);var key;for(i=0;i=0;i--){this.removeListener(type,listeners[i])}}return this};function _listeners(target,type,unwrap){var events=target._events;if(!events)return[];var evlistener=events[type];if(!evlistener)return[];if(typeof evlistener==="function")return unwrap?[evlistener.listener||evlistener]:[evlistener];return unwrap?unwrapListeners(evlistener):arrayClone(evlistener,evlistener.length)}EventEmitter.prototype.listeners=function listeners(type){return _listeners(this,type,true)};EventEmitter.prototype.rawListeners=function rawListeners(type){return _listeners(this,type,false)};EventEmitter.listenerCount=function(emitter,type){if(typeof emitter.listenerCount==="function"){return emitter.listenerCount(type)}else{return listenerCount.call(emitter,type)}};EventEmitter.prototype.listenerCount=listenerCount;function listenerCount(type){var events=this._events;if(events){var evlistener=events[type];if(typeof evlistener==="function"){return 1}else if(evlistener){return evlistener.length}}return 0}EventEmitter.prototype.eventNames=function eventNames(){return this._eventsCount>0?Reflect.ownKeys(this._events):[]};function spliceOne(list,index){for(var i=index,k=i+1,n=list.length;k>>32-b},rotr:function(n,b){return n<<32-b|n>>>b},endian:function(n){if(n.constructor==Number){return crypt.rotl(n,8)&16711935|crypt.rotl(n,24)&4278255360}for(var i=0;i0;n--)bytes.push(Math.floor(Math.random()*256));return bytes},bytesToWords:function(bytes){for(var words=[],i=0,b=0;i>>5]|=bytes[i]<<24-b%32;return words},wordsToBytes:function(words){for(var bytes=[],b=0;b>>5]>>>24-b%32&255);return bytes},bytesToHex:function(bytes){for(var hex=[],i=0;i>>4).toString(16));hex.push((bytes[i]&15).toString(16))}return hex.join("")},hexToBytes:function(hex){for(var bytes=[],c=0;c>>6*(3-j)&63));else base64.push("=")}return base64.join("")},base64ToBytes:function(base64){base64=base64.replace(/[^A-Z0-9+\/]/gi,"");for(var bytes=[],i=0,imod4=0;i>>6-imod4*2)}return bytes}};module.exports=crypt})()},{}],42:[function(require,module,exports){module.exports=function(obj){return obj!=null&&(isBuffer(obj)||isSlowBuffer(obj)||!!obj._isBuffer)};function isBuffer(obj){return!!obj.constructor&&typeof obj.constructor.isBuffer==="function"&&obj.constructor.isBuffer(obj)}function isSlowBuffer(obj){return typeof obj.readFloatLE==="function"&&typeof obj.slice==="function"&&isBuffer(obj.slice(0,0))}},{}],43:[function(require,module,exports){(function(root,definition){"use strict";if(typeof define==="function"&&define.amd){define(definition)}else if(typeof module==="object"&&module.exports){module.exports=definition()}else{root.log=definition()}})(this,function(){"use strict";var noop=function(){};var undefinedType="undefined";var isIE=typeof window!==undefinedType&&typeof window.navigator!==undefinedType&&/Trident\/|MSIE /.test(window.navigator.userAgent);var logMethods=["trace","debug","info","warn","error"];function bindMethod(obj,methodName){var method=obj[methodName];if(typeof method.bind==="function"){return method.bind(obj)}else{try{return Function.prototype.bind.call(method,obj)}catch(e){return function(){return Function.prototype.apply.apply(method,[obj,arguments])}}}}function traceForIE(){if(console.log){if(console.log.apply){console.log.apply(console,arguments)}else{Function.prototype.apply.apply(console.log,[console,arguments])}}if(console.trace)console.trace()}function realMethod(methodName){if(methodName==="debug"){methodName="log"}if(typeof console===undefinedType){return false}else if(methodName==="trace"&&isIE){return traceForIE}else if(console[methodName]!==undefined){return bindMethod(console,methodName)}else if(console.log!==undefined){return bindMethod(console,"log")}else{return noop}}function replaceLoggingMethods(level,loggerName){for(var i=0;i=0&&level<=self.levels.SILENT){currentLevel=level;if(persist!==false){persistLevelIfPossible(level)}replaceLoggingMethods.call(self,level,name);if(typeof console===undefinedType&&level>>24)&16711935|(m[i]<<24|m[i]>>>8)&4278255360}m[l>>>5]|=128<>>9<<4)+14]=l;var FF=md5._ff,GG=md5._gg,HH=md5._hh,II=md5._ii;for(var i=0;i>>0;b=b+bb>>>0;c=c+cc>>>0;d=d+dd>>>0}return crypt.endian([a,b,c,d])};md5._ff=function(a,b,c,d,x,s,t){var n=a+(b&c|~b&d)+(x>>>0)+t;return(n<>>32-s)+b};md5._gg=function(a,b,c,d,x,s,t){var n=a+(b&d|c&~d)+(x>>>0)+t;return(n<>>32-s)+b};md5._hh=function(a,b,c,d,x,s,t){var n=a+(b^c^d)+(x>>>0)+t;return(n<>>32-s)+b};md5._ii=function(a,b,c,d,x,s,t){var n=a+(c^(b|~d))+(x>>>0)+t;return(n<>>32-s)+b};md5._blocksize=16;md5._digestsize=16;module.exports=function(message,options){if(message===undefined||message===null)throw new Error("Illegal argument "+message);var digestbytes=crypt.wordsToBytes(md5(message,options));return options&&options.asBytes?digestbytes:options&&options.asString?bin.bytesToString(digestbytes):crypt.bytesToHex(digestbytes)}})()},{charenc:40,crypt:41,"is-buffer":42}],45:[function(require,module,exports){"use strict";var SDPUtils=require("sdp");function writeMediaSection(transceiver,caps,type,stream,dtlsRole){var sdp=SDPUtils.writeRtpDescription(transceiver.kind,caps);sdp+=SDPUtils.writeIceParameters(transceiver.iceGatherer.getLocalParameters());sdp+=SDPUtils.writeDtlsParameters(transceiver.dtlsTransport.getLocalParameters(),type==="offer"?"actpass":dtlsRole||"active");sdp+="a=mid:"+transceiver.mid+"\r\n";if(transceiver.rtpSender&&transceiver.rtpReceiver){sdp+="a=sendrecv\r\n"}else if(transceiver.rtpSender){sdp+="a=sendonly\r\n"}else if(transceiver.rtpReceiver){sdp+="a=recvonly\r\n"}else{sdp+="a=inactive\r\n"}if(transceiver.rtpSender){var trackId=transceiver.rtpSender._initialTrackId||transceiver.rtpSender.track.id;transceiver.rtpSender._initialTrackId=trackId;var msid="msid:"+(stream?stream.id:"-")+" "+trackId+"\r\n";sdp+="a="+msid;sdp+="a=ssrc:"+transceiver.sendEncodingParameters[0].ssrc+" "+msid;if(transceiver.sendEncodingParameters[0].rtx){sdp+="a=ssrc:"+transceiver.sendEncodingParameters[0].rtx.ssrc+" "+msid;sdp+="a=ssrc-group:FID "+transceiver.sendEncodingParameters[0].ssrc+" "+transceiver.sendEncodingParameters[0].rtx.ssrc+"\r\n"}}sdp+="a=ssrc:"+transceiver.sendEncodingParameters[0].ssrc+" cname:"+SDPUtils.localCName+"\r\n";if(transceiver.rtpSender&&transceiver.sendEncodingParameters[0].rtx){sdp+="a=ssrc:"+transceiver.sendEncodingParameters[0].rtx.ssrc+" cname:"+SDPUtils.localCName+"\r\n"}return sdp}function filterIceServers(iceServers,edgeVersion){var hasTurn=false;iceServers=JSON.parse(JSON.stringify(iceServers));return iceServers.filter(function(server){if(server&&(server.urls||server.url)){var urls=server.urls||server.url;if(server.url&&!server.urls){console.warn("RTCIceServer.url is deprecated! Use urls instead.")}var isString=typeof urls==="string";if(isString){urls=[urls]}urls=urls.filter(function(url){var validTurn=url.indexOf("turn:")===0&&url.indexOf("transport=udp")!==-1&&url.indexOf("turn:[")===-1&&!hasTurn;if(validTurn){hasTurn=true;return true}return url.indexOf("stun:")===0&&edgeVersion>=14393&&url.indexOf("?transport=udp")===-1});delete server.url;server.urls=isString?urls[0]:urls;return!!urls.length}})}function getCommonCapabilities(localCapabilities,remoteCapabilities){var commonCapabilities={codecs:[],headerExtensions:[],fecMechanisms:[]};var findCodecByPayloadType=function(pt,codecs){pt=parseInt(pt,10);for(var i=0;i0;i--){this._iceGatherers.push(new window.RTCIceGatherer({iceServers:config.iceServers,gatherPolicy:config.iceTransportPolicy}))}}else{config.iceCandidatePoolSize=0}this._config=config;this.transceivers=[];this._sdpSessionId=SDPUtils.generateSessionId();this._sdpSessionVersion=0;this._dtlsRole=undefined;this._isClosed=false};RTCPeerConnection.prototype.onicecandidate=null;RTCPeerConnection.prototype.onaddstream=null;RTCPeerConnection.prototype.ontrack=null;RTCPeerConnection.prototype.onremovestream=null;RTCPeerConnection.prototype.onsignalingstatechange=null;RTCPeerConnection.prototype.oniceconnectionstatechange=null;RTCPeerConnection.prototype.onicegatheringstatechange=null;RTCPeerConnection.prototype.onnegotiationneeded=null;RTCPeerConnection.prototype.ondatachannel=null;RTCPeerConnection.prototype._dispatchEvent=function(name,event){if(this._isClosed){return}this.dispatchEvent(event);if(typeof this["on"+name]==="function"){this["on"+name](event)}};RTCPeerConnection.prototype._emitGatheringStateChange=function(){var event=new Event("icegatheringstatechange");this._dispatchEvent("icegatheringstatechange",event)};RTCPeerConnection.prototype.getConfiguration=function(){return this._config};RTCPeerConnection.prototype.getLocalStreams=function(){return this.localStreams};RTCPeerConnection.prototype.getRemoteStreams=function(){return this.remoteStreams};RTCPeerConnection.prototype._createTransceiver=function(kind){var hasBundleTransport=this.transceivers.length>0;var transceiver={track:null,iceGatherer:null,iceTransport:null,dtlsTransport:null,localCapabilities:null,remoteCapabilities:null,rtpSender:null,rtpReceiver:null,kind:kind,mid:null,sendEncodingParameters:null,recvEncodingParameters:null,stream:null,associatedRemoteMediaStreams:[],wantReceive:true};if(this.usingBundle&&hasBundleTransport){transceiver.iceTransport=this.transceivers[0].iceTransport;transceiver.dtlsTransport=this.transceivers[0].dtlsTransport}else{var transports=this._createIceAndDtlsTransports();transceiver.iceTransport=transports.iceTransport;transceiver.dtlsTransport=transports.dtlsTransport}this.transceivers.push(transceiver);return transceiver};RTCPeerConnection.prototype.addTrack=function(track,stream){if(this._isClosed){throw makeError("InvalidStateError","Attempted to call addTrack on a closed peerconnection.")}var alreadyExists=this.transceivers.find(function(s){return s.track===track});if(alreadyExists){throw makeError("InvalidAccessError","Track already exists.")}var transceiver;for(var i=0;i=15025){stream.getTracks().forEach(function(track){pc.addTrack(track,stream)})}else{var clonedStream=stream.clone();stream.getTracks().forEach(function(track,idx){var clonedTrack=clonedStream.getTracks()[idx];track.addEventListener("enabled",function(event){clonedTrack.enabled=event.enabled})});clonedStream.getTracks().forEach(function(track){pc.addTrack(track,clonedStream)})}};RTCPeerConnection.prototype.removeTrack=function(sender){if(this._isClosed){throw makeError("InvalidStateError","Attempted to call removeTrack on a closed peerconnection.")}if(!(sender instanceof window.RTCRtpSender)){throw new TypeError("Argument 1 of RTCPeerConnection.removeTrack "+"does not implement interface RTCRtpSender.")}var transceiver=this.transceivers.find(function(t){return t.rtpSender===sender});if(!transceiver){throw makeError("InvalidAccessError","Sender was not created by this connection.")}var stream=transceiver.stream;transceiver.rtpSender.stop();transceiver.rtpSender=null;transceiver.track=null;transceiver.stream=null;var localStreams=this.transceivers.map(function(t){return t.stream});if(localStreams.indexOf(stream)===-1&&this.localStreams.indexOf(stream)>-1){this.localStreams.splice(this.localStreams.indexOf(stream),1)}this._maybeFireNegotiationNeeded()};RTCPeerConnection.prototype.removeStream=function(stream){var pc=this;stream.getTracks().forEach(function(track){var sender=pc.getSenders().find(function(s){return s.track===track});if(sender){pc.removeTrack(sender)}})};RTCPeerConnection.prototype.getSenders=function(){return this.transceivers.filter(function(transceiver){return!!transceiver.rtpSender}).map(function(transceiver){return transceiver.rtpSender})};RTCPeerConnection.prototype.getReceivers=function(){return this.transceivers.filter(function(transceiver){return!!transceiver.rtpReceiver}).map(function(transceiver){return transceiver.rtpReceiver})};RTCPeerConnection.prototype._createIceGatherer=function(sdpMLineIndex,usingBundle){var pc=this;if(usingBundle&&sdpMLineIndex>0){return this.transceivers[0].iceGatherer}else if(this._iceGatherers.length){return this._iceGatherers.shift()}var iceGatherer=new window.RTCIceGatherer({iceServers:this._config.iceServers,gatherPolicy:this._config.iceTransportPolicy});Object.defineProperty(iceGatherer,"state",{value:"new",writable:true});this.transceivers[sdpMLineIndex].bufferedCandidateEvents=[];this.transceivers[sdpMLineIndex].bufferCandidates=function(event){var end=!event.candidate||Object.keys(event.candidate).length===0;iceGatherer.state=end?"completed":"gathering";if(pc.transceivers[sdpMLineIndex].bufferedCandidateEvents!==null){pc.transceivers[sdpMLineIndex].bufferedCandidateEvents.push(event)}};iceGatherer.addEventListener("localcandidate",this.transceivers[sdpMLineIndex].bufferCandidates);return iceGatherer};RTCPeerConnection.prototype._gather=function(mid,sdpMLineIndex){var pc=this;var iceGatherer=this.transceivers[sdpMLineIndex].iceGatherer;if(iceGatherer.onlocalcandidate){return}var bufferedCandidateEvents=this.transceivers[sdpMLineIndex].bufferedCandidateEvents;this.transceivers[sdpMLineIndex].bufferedCandidateEvents=null;iceGatherer.removeEventListener("localcandidate",this.transceivers[sdpMLineIndex].bufferCandidates);iceGatherer.onlocalcandidate=function(evt){if(pc.usingBundle&&sdpMLineIndex>0){return}var event=new Event("icecandidate");event.candidate={sdpMid:mid,sdpMLineIndex:sdpMLineIndex};var cand=evt.candidate;var end=!cand||Object.keys(cand).length===0;if(end){if(iceGatherer.state==="new"||iceGatherer.state==="gathering"){iceGatherer.state="completed"}}else{if(iceGatherer.state==="new"){iceGatherer.state="gathering"}cand.component=1;var serializedCandidate=SDPUtils.writeCandidate(cand);event.candidate=Object.assign(event.candidate,SDPUtils.parseCandidate(serializedCandidate));event.candidate.candidate=serializedCandidate}var sections=SDPUtils.getMediaSections(pc.localDescription.sdp);if(!end){sections[event.candidate.sdpMLineIndex]+="a="+event.candidate.candidate+"\r\n"}else{sections[event.candidate.sdpMLineIndex]+="a=end-of-candidates\r\n"}pc.localDescription.sdp=SDPUtils.getDescription(pc.localDescription.sdp)+sections.join("");var complete=pc.transceivers.every(function(transceiver){return transceiver.iceGatherer&&transceiver.iceGatherer.state==="completed"});if(pc.iceGatheringState!=="gathering"){pc.iceGatheringState="gathering";pc._emitGatheringStateChange()}if(!end){pc._dispatchEvent("icecandidate",event)}if(complete){pc._dispatchEvent("icecandidate",new Event("icecandidate"));pc.iceGatheringState="complete";pc._emitGatheringStateChange()}};window.setTimeout(function(){bufferedCandidateEvents.forEach(function(e){iceGatherer.onlocalcandidate(e)})},0)};RTCPeerConnection.prototype._createIceAndDtlsTransports=function(){var pc=this;var iceTransport=new window.RTCIceTransport(null);iceTransport.onicestatechange=function(){pc._updateConnectionState()};var dtlsTransport=new window.RTCDtlsTransport(iceTransport);dtlsTransport.ondtlsstatechange=function(){pc._updateConnectionState()};dtlsTransport.onerror=function(){Object.defineProperty(dtlsTransport,"state",{value:"failed",writable:true});pc._updateConnectionState()};return{iceTransport:iceTransport,dtlsTransport:dtlsTransport}};RTCPeerConnection.prototype._disposeIceAndDtlsTransports=function(sdpMLineIndex){var iceGatherer=this.transceivers[sdpMLineIndex].iceGatherer;if(iceGatherer){delete iceGatherer.onlocalcandidate;delete this.transceivers[sdpMLineIndex].iceGatherer}var iceTransport=this.transceivers[sdpMLineIndex].iceTransport;if(iceTransport){delete iceTransport.onicestatechange;delete this.transceivers[sdpMLineIndex].iceTransport}var dtlsTransport=this.transceivers[sdpMLineIndex].dtlsTransport;if(dtlsTransport){delete dtlsTransport.ondtlsstatechange;delete dtlsTransport.onerror;delete this.transceivers[sdpMLineIndex].dtlsTransport}};RTCPeerConnection.prototype._transceive=function(transceiver,send,recv){var params=getCommonCapabilities(transceiver.localCapabilities,transceiver.remoteCapabilities);if(send&&transceiver.rtpSender){params.encodings=transceiver.sendEncodingParameters;params.rtcp={cname:SDPUtils.localCName,compound:transceiver.rtcpParameters.compound};if(transceiver.recvEncodingParameters.length){params.rtcp.ssrc=transceiver.recvEncodingParameters[0].ssrc}transceiver.rtpSender.send(params)}if(recv&&transceiver.rtpReceiver&¶ms.codecs.length>0){if(transceiver.kind==="video"&&transceiver.recvEncodingParameters&&edgeVersion<15019){transceiver.recvEncodingParameters.forEach(function(p){delete p.rtx})}if(transceiver.recvEncodingParameters.length){params.encodings=transceiver.recvEncodingParameters}else{params.encodings=[{}]}params.rtcp={compound:transceiver.rtcpParameters.compound};if(transceiver.rtcpParameters.cname){params.rtcp.cname=transceiver.rtcpParameters.cname}if(transceiver.sendEncodingParameters.length){params.rtcp.ssrc=transceiver.sendEncodingParameters[0].ssrc}transceiver.rtpReceiver.receive(params)}};RTCPeerConnection.prototype.setLocalDescription=function(description){var pc=this;if(["offer","answer"].indexOf(description.type)===-1){return Promise.reject(makeError("TypeError",'Unsupported type "'+description.type+'"'))}if(!isActionAllowedInSignalingState("setLocalDescription",description.type,pc.signalingState)||pc._isClosed){return Promise.reject(makeError("InvalidStateError","Can not set local "+description.type+" in state "+pc.signalingState))}var sections;var sessionpart;if(description.type==="offer"){sections=SDPUtils.splitSections(description.sdp);sessionpart=sections.shift();sections.forEach(function(mediaSection,sdpMLineIndex){var caps=SDPUtils.parseRtpParameters(mediaSection);pc.transceivers[sdpMLineIndex].localCapabilities=caps});pc.transceivers.forEach(function(transceiver,sdpMLineIndex){pc._gather(transceiver.mid,sdpMLineIndex)})}else if(description.type==="answer"){sections=SDPUtils.splitSections(pc.remoteDescription.sdp);sessionpart=sections.shift();var isIceLite=SDPUtils.matchPrefix(sessionpart,"a=ice-lite").length>0;sections.forEach(function(mediaSection,sdpMLineIndex){var transceiver=pc.transceivers[sdpMLineIndex];var iceGatherer=transceiver.iceGatherer;var iceTransport=transceiver.iceTransport;var dtlsTransport=transceiver.dtlsTransport;var localCapabilities=transceiver.localCapabilities;var remoteCapabilities=transceiver.remoteCapabilities;var rejected=SDPUtils.isRejected(mediaSection)&&SDPUtils.matchPrefix(mediaSection,"a=bundle-only").length===0;if(!rejected&&!transceiver.isDatachannel){var remoteIceParameters=SDPUtils.getIceParameters(mediaSection,sessionpart);var remoteDtlsParameters=SDPUtils.getDtlsParameters(mediaSection,sessionpart);if(isIceLite){remoteDtlsParameters.role="server"}if(!pc.usingBundle||sdpMLineIndex===0){pc._gather(transceiver.mid,sdpMLineIndex);if(iceTransport.state==="new"){iceTransport.start(iceGatherer,remoteIceParameters,isIceLite?"controlling":"controlled")}if(dtlsTransport.state==="new"){dtlsTransport.start(remoteDtlsParameters)}}var params=getCommonCapabilities(localCapabilities,remoteCapabilities);pc._transceive(transceiver,params.codecs.length>0,false)}})}pc.localDescription={type:description.type,sdp:description.sdp};if(description.type==="offer"){pc._updateSignalingState("have-local-offer")}else{pc._updateSignalingState("stable")}return Promise.resolve()};RTCPeerConnection.prototype.setRemoteDescription=function(description){var pc=this;if(["offer","answer"].indexOf(description.type)===-1){return Promise.reject(makeError("TypeError",'Unsupported type "'+description.type+'"'))}if(!isActionAllowedInSignalingState("setRemoteDescription",description.type,pc.signalingState)||pc._isClosed){return Promise.reject(makeError("InvalidStateError","Can not set remote "+description.type+" in state "+pc.signalingState))}var streams={};pc.remoteStreams.forEach(function(stream){streams[stream.id]=stream});var receiverList=[];var sections=SDPUtils.splitSections(description.sdp);var sessionpart=sections.shift();var isIceLite=SDPUtils.matchPrefix(sessionpart,"a=ice-lite").length>0;var usingBundle=SDPUtils.matchPrefix(sessionpart,"a=group:BUNDLE ").length>0;pc.usingBundle=usingBundle;var iceOptions=SDPUtils.matchPrefix(sessionpart,"a=ice-options:")[0];if(iceOptions){pc.canTrickleIceCandidates=iceOptions.substr(14).split(" ").indexOf("trickle")>=0}else{pc.canTrickleIceCandidates=false}sections.forEach(function(mediaSection,sdpMLineIndex){var lines=SDPUtils.splitLines(mediaSection);var kind=SDPUtils.getKind(mediaSection);var rejected=SDPUtils.isRejected(mediaSection)&&SDPUtils.matchPrefix(mediaSection,"a=bundle-only").length===0;var protocol=lines[0].substr(2).split(" ")[2];var direction=SDPUtils.getDirection(mediaSection,sessionpart);var remoteMsid=SDPUtils.parseMsid(mediaSection);var mid=SDPUtils.getMid(mediaSection)||SDPUtils.generateIdentifier();if(kind==="application"&&protocol==="DTLS/SCTP"){pc.transceivers[sdpMLineIndex]={mid:mid,isDatachannel:true};return}var transceiver;var iceGatherer;var iceTransport;var dtlsTransport;var rtpReceiver;var sendEncodingParameters;var recvEncodingParameters;var localCapabilities;var track;var remoteCapabilities=SDPUtils.parseRtpParameters(mediaSection);var remoteIceParameters;var remoteDtlsParameters;if(!rejected){remoteIceParameters=SDPUtils.getIceParameters(mediaSection,sessionpart);remoteDtlsParameters=SDPUtils.getDtlsParameters(mediaSection,sessionpart);remoteDtlsParameters.role="client"}recvEncodingParameters=SDPUtils.parseRtpEncodingParameters(mediaSection);var rtcpParameters=SDPUtils.parseRtcpParameters(mediaSection);var isComplete=SDPUtils.matchPrefix(mediaSection,"a=end-of-candidates",sessionpart).length>0;var cands=SDPUtils.matchPrefix(mediaSection,"a=candidate:").map(function(cand){return SDPUtils.parseCandidate(cand)}).filter(function(cand){return cand.component===1});if((description.type==="offer"||description.type==="answer")&&!rejected&&usingBundle&&sdpMLineIndex>0&&pc.transceivers[sdpMLineIndex]){pc._disposeIceAndDtlsTransports(sdpMLineIndex);pc.transceivers[sdpMLineIndex].iceGatherer=pc.transceivers[0].iceGatherer;pc.transceivers[sdpMLineIndex].iceTransport=pc.transceivers[0].iceTransport;pc.transceivers[sdpMLineIndex].dtlsTransport=pc.transceivers[0].dtlsTransport;if(pc.transceivers[sdpMLineIndex].rtpSender){pc.transceivers[sdpMLineIndex].rtpSender.setTransport(pc.transceivers[0].dtlsTransport)}if(pc.transceivers[sdpMLineIndex].rtpReceiver){pc.transceivers[sdpMLineIndex].rtpReceiver.setTransport(pc.transceivers[0].dtlsTransport)}}if(description.type==="offer"&&!rejected){transceiver=pc.transceivers[sdpMLineIndex]||pc._createTransceiver(kind);transceiver.mid=mid;if(!transceiver.iceGatherer){transceiver.iceGatherer=pc._createIceGatherer(sdpMLineIndex,usingBundle)}if(cands.length&&transceiver.iceTransport.state==="new"){if(isComplete&&(!usingBundle||sdpMLineIndex===0)){transceiver.iceTransport.setRemoteCandidates(cands)}else{cands.forEach(function(candidate){maybeAddCandidate(transceiver.iceTransport,candidate)})}}localCapabilities=window.RTCRtpReceiver.getCapabilities(kind);if(edgeVersion<15019){localCapabilities.codecs=localCapabilities.codecs.filter(function(codec){return codec.name!=="rtx"})}sendEncodingParameters=transceiver.sendEncodingParameters||[{ssrc:(2*sdpMLineIndex+2)*1001}];var isNewTrack=false;if(direction==="sendrecv"||direction==="sendonly"){isNewTrack=!transceiver.rtpReceiver;rtpReceiver=transceiver.rtpReceiver||new window.RTCRtpReceiver(transceiver.dtlsTransport,kind);if(isNewTrack){var stream;track=rtpReceiver.track;if(remoteMsid&&remoteMsid.stream==="-"){}else if(remoteMsid){if(!streams[remoteMsid.stream]){streams[remoteMsid.stream]=new window.MediaStream;Object.defineProperty(streams[remoteMsid.stream],"id",{get:function(){return remoteMsid.stream}})}Object.defineProperty(track,"id",{get:function(){return remoteMsid.track}});stream=streams[remoteMsid.stream]}else{if(!streams.default){streams.default=new window.MediaStream}stream=streams.default}if(stream){addTrackToStreamAndFireEvent(track,stream);transceiver.associatedRemoteMediaStreams.push(stream)}receiverList.push([track,rtpReceiver,stream])}}else if(transceiver.rtpReceiver&&transceiver.rtpReceiver.track){transceiver.associatedRemoteMediaStreams.forEach(function(s){var nativeTrack=s.getTracks().find(function(t){return t.id===transceiver.rtpReceiver.track.id});if(nativeTrack){removeTrackFromStreamAndFireEvent(nativeTrack,s)}});transceiver.associatedRemoteMediaStreams=[]}transceiver.localCapabilities=localCapabilities;transceiver.remoteCapabilities=remoteCapabilities;transceiver.rtpReceiver=rtpReceiver;transceiver.rtcpParameters=rtcpParameters;transceiver.sendEncodingParameters=sendEncodingParameters;transceiver.recvEncodingParameters=recvEncodingParameters;pc._transceive(pc.transceivers[sdpMLineIndex],false,isNewTrack)}else if(description.type==="answer"&&!rejected){transceiver=pc.transceivers[sdpMLineIndex];iceGatherer=transceiver.iceGatherer;iceTransport=transceiver.iceTransport;dtlsTransport=transceiver.dtlsTransport;rtpReceiver=transceiver.rtpReceiver;sendEncodingParameters=transceiver.sendEncodingParameters;localCapabilities=transceiver.localCapabilities;pc.transceivers[sdpMLineIndex].recvEncodingParameters=recvEncodingParameters;pc.transceivers[sdpMLineIndex].remoteCapabilities=remoteCapabilities;pc.transceivers[sdpMLineIndex].rtcpParameters=rtcpParameters;if(cands.length&&iceTransport.state==="new"){if((isIceLite||isComplete)&&(!usingBundle||sdpMLineIndex===0)){iceTransport.setRemoteCandidates(cands)}else{cands.forEach(function(candidate){maybeAddCandidate(transceiver.iceTransport,candidate)})}}if(!usingBundle||sdpMLineIndex===0){if(iceTransport.state==="new"){iceTransport.start(iceGatherer,remoteIceParameters,"controlling")}if(dtlsTransport.state==="new"){dtlsTransport.start(remoteDtlsParameters)}}pc._transceive(transceiver,direction==="sendrecv"||direction==="recvonly",direction==="sendrecv"||direction==="sendonly");if(rtpReceiver&&(direction==="sendrecv"||direction==="sendonly")){track=rtpReceiver.track;if(remoteMsid){if(!streams[remoteMsid.stream]){streams[remoteMsid.stream]=new window.MediaStream}addTrackToStreamAndFireEvent(track,streams[remoteMsid.stream]);receiverList.push([track,rtpReceiver,streams[remoteMsid.stream]])}else{if(!streams.default){streams.default=new window.MediaStream}addTrackToStreamAndFireEvent(track,streams.default);receiverList.push([track,rtpReceiver,streams.default])}}else{delete transceiver.rtpReceiver}}});if(pc._dtlsRole===undefined){pc._dtlsRole=description.type==="offer"?"active":"passive"}pc.remoteDescription={type:description.type,sdp:description.sdp};if(description.type==="offer"){pc._updateSignalingState("have-remote-offer")}else{pc._updateSignalingState("stable")}Object.keys(streams).forEach(function(sid){var stream=streams[sid];if(stream.getTracks().length){if(pc.remoteStreams.indexOf(stream)===-1){pc.remoteStreams.push(stream);var event=new Event("addstream");event.stream=stream;window.setTimeout(function(){pc._dispatchEvent("addstream",event)})}receiverList.forEach(function(item){var track=item[0];var receiver=item[1];if(stream.id!==item[2].id){return}fireAddTrack(pc,track,receiver,[stream])})}});receiverList.forEach(function(item){if(item[2]){return}fireAddTrack(pc,item[0],item[1],[])});window.setTimeout(function(){if(!(pc&&pc.transceivers)){return}pc.transceivers.forEach(function(transceiver){if(transceiver.iceTransport&&transceiver.iceTransport.state==="new"&&transceiver.iceTransport.getRemoteCandidates().length>0){console.warn("Timeout for addRemoteCandidate. Consider sending "+"an end-of-candidates notification");transceiver.iceTransport.addRemoteCandidate({})}})},4e3);return Promise.resolve()};RTCPeerConnection.prototype.close=function(){this.transceivers.forEach(function(transceiver){if(transceiver.iceTransport){transceiver.iceTransport.stop()}if(transceiver.dtlsTransport){transceiver.dtlsTransport.stop()}if(transceiver.rtpSender){transceiver.rtpSender.stop()}if(transceiver.rtpReceiver){transceiver.rtpReceiver.stop()}});this._isClosed=true;this._updateSignalingState("closed")};RTCPeerConnection.prototype._updateSignalingState=function(newState){this.signalingState=newState;var event=new Event("signalingstatechange");this._dispatchEvent("signalingstatechange",event)};RTCPeerConnection.prototype._maybeFireNegotiationNeeded=function(){var pc=this;if(this.signalingState!=="stable"||this.needNegotiation===true){return}this.needNegotiation=true;window.setTimeout(function(){if(pc.needNegotiation){pc.needNegotiation=false;var event=new Event("negotiationneeded");pc._dispatchEvent("negotiationneeded",event)}},0)};RTCPeerConnection.prototype._updateConnectionState=function(){var newState;var states={new:0,closed:0,connecting:0,checking:0,connected:0,completed:0,disconnected:0,failed:0};this.transceivers.forEach(function(transceiver){states[transceiver.iceTransport.state]++;states[transceiver.dtlsTransport.state]++});states.connected+=states.completed;newState="new";if(states.failed>0){newState="failed"}else if(states.connecting>0||states.checking>0){newState="connecting"}else if(states.disconnected>0){newState="disconnected"}else if(states.new>0){newState="new"}else if(states.connected>0||states.completed>0){newState="connected"}if(newState!==this.iceConnectionState){this.iceConnectionState=newState;var event=new Event("iceconnectionstatechange");this._dispatchEvent("iceconnectionstatechange",event)}};RTCPeerConnection.prototype.createOffer=function(){var pc=this;if(pc._isClosed){return Promise.reject(makeError("InvalidStateError","Can not call createOffer after close"))}var numAudioTracks=pc.transceivers.filter(function(t){return t.kind==="audio"}).length;var numVideoTracks=pc.transceivers.filter(function(t){return t.kind==="video"}).length;var offerOptions=arguments[0];if(offerOptions){if(offerOptions.mandatory||offerOptions.optional){throw new TypeError("Legacy mandatory/optional constraints not supported.")}if(offerOptions.offerToReceiveAudio!==undefined){if(offerOptions.offerToReceiveAudio===true){numAudioTracks=1}else if(offerOptions.offerToReceiveAudio===false){numAudioTracks=0}else{numAudioTracks=offerOptions.offerToReceiveAudio}}if(offerOptions.offerToReceiveVideo!==undefined){if(offerOptions.offerToReceiveVideo===true){numVideoTracks=1}else if(offerOptions.offerToReceiveVideo===false){numVideoTracks=0}else{numVideoTracks=offerOptions.offerToReceiveVideo}}}pc.transceivers.forEach(function(transceiver){if(transceiver.kind==="audio"){numAudioTracks--;if(numAudioTracks<0){transceiver.wantReceive=false}}else if(transceiver.kind==="video"){numVideoTracks--;if(numVideoTracks<0){transceiver.wantReceive=false}}});while(numAudioTracks>0||numVideoTracks>0){if(numAudioTracks>0){pc._createTransceiver("audio");numAudioTracks--}if(numVideoTracks>0){pc._createTransceiver("video");numVideoTracks--}}var sdp=SDPUtils.writeSessionBoilerplate(pc._sdpSessionId,pc._sdpSessionVersion++);pc.transceivers.forEach(function(transceiver,sdpMLineIndex){var track=transceiver.track;var kind=transceiver.kind;var mid=transceiver.mid||SDPUtils.generateIdentifier();transceiver.mid=mid;if(!transceiver.iceGatherer){transceiver.iceGatherer=pc._createIceGatherer(sdpMLineIndex,pc.usingBundle)}var localCapabilities=window.RTCRtpSender.getCapabilities(kind);if(edgeVersion<15019){localCapabilities.codecs=localCapabilities.codecs.filter(function(codec){return codec.name!=="rtx"})}localCapabilities.codecs.forEach(function(codec){if(codec.name==="H264"&&codec.parameters["level-asymmetry-allowed"]===undefined){codec.parameters["level-asymmetry-allowed"]="1"}if(transceiver.remoteCapabilities&&transceiver.remoteCapabilities.codecs){transceiver.remoteCapabilities.codecs.forEach(function(remoteCodec){if(codec.name.toLowerCase()===remoteCodec.name.toLowerCase()&&codec.clockRate===remoteCodec.clockRate){codec.preferredPayloadType=remoteCodec.payloadType}})}});localCapabilities.headerExtensions.forEach(function(hdrExt){var remoteExtensions=transceiver.remoteCapabilities&&transceiver.remoteCapabilities.headerExtensions||[];remoteExtensions.forEach(function(rHdrExt){if(hdrExt.uri===rHdrExt.uri){hdrExt.id=rHdrExt.id}})});var sendEncodingParameters=transceiver.sendEncodingParameters||[{ssrc:(2*sdpMLineIndex+1)*1001}];if(track){if(edgeVersion>=15019&&kind==="video"&&!sendEncodingParameters[0].rtx){sendEncodingParameters[0].rtx={ssrc:sendEncodingParameters[0].ssrc+1}}}if(transceiver.wantReceive){transceiver.rtpReceiver=new window.RTCRtpReceiver(transceiver.dtlsTransport,kind)}transceiver.localCapabilities=localCapabilities;transceiver.sendEncodingParameters=sendEncodingParameters});if(pc._config.bundlePolicy!=="max-compat"){sdp+="a=group:BUNDLE "+pc.transceivers.map(function(t){return t.mid}).join(" ")+"\r\n"}sdp+="a=ice-options:trickle\r\n";pc.transceivers.forEach(function(transceiver,sdpMLineIndex){sdp+=writeMediaSection(transceiver,transceiver.localCapabilities,"offer",transceiver.stream,pc._dtlsRole);sdp+="a=rtcp-rsize\r\n";if(transceiver.iceGatherer&&pc.iceGatheringState!=="new"&&(sdpMLineIndex===0||!pc.usingBundle)){transceiver.iceGatherer.getLocalCandidates().forEach(function(cand){cand.component=1;sdp+="a="+SDPUtils.writeCandidate(cand)+"\r\n"});if(transceiver.iceGatherer.state==="completed"){sdp+="a=end-of-candidates\r\n"}}});var desc=new window.RTCSessionDescription({type:"offer",sdp:sdp});return Promise.resolve(desc)};RTCPeerConnection.prototype.createAnswer=function(){var pc=this;if(pc._isClosed){return Promise.reject(makeError("InvalidStateError","Can not call createAnswer after close"))}var sdp=SDPUtils.writeSessionBoilerplate(pc._sdpSessionId,pc._sdpSessionVersion++);if(pc.usingBundle){sdp+="a=group:BUNDLE "+pc.transceivers.map(function(t){return t.mid}).join(" ")+"\r\n"}var mediaSectionsInOffer=SDPUtils.getMediaSections(pc.remoteDescription.sdp).length;pc.transceivers.forEach(function(transceiver,sdpMLineIndex){if(sdpMLineIndex+1>mediaSectionsInOffer){return}if(transceiver.isDatachannel){sdp+="m=application 0 DTLS/SCTP 5000\r\n"+"c=IN IP4 0.0.0.0\r\n"+"a=mid:"+transceiver.mid+"\r\n";return}if(transceiver.stream){var localTrack;if(transceiver.kind==="audio"){localTrack=transceiver.stream.getAudioTracks()[0]}else if(transceiver.kind==="video"){localTrack=transceiver.stream.getVideoTracks()[0]}if(localTrack){if(edgeVersion>=15019&&transceiver.kind==="video"&&!transceiver.sendEncodingParameters[0].rtx){transceiver.sendEncodingParameters[0].rtx={ssrc:transceiver.sendEncodingParameters[0].ssrc+1}}}}var commonCapabilities=getCommonCapabilities(transceiver.localCapabilities,transceiver.remoteCapabilities);var hasRtx=commonCapabilities.codecs.filter(function(c){return c.name.toLowerCase()==="rtx"}).length;if(!hasRtx&&transceiver.sendEncodingParameters[0].rtx){delete transceiver.sendEncodingParameters[0].rtx}sdp+=writeMediaSection(transceiver,commonCapabilities,"answer",transceiver.stream,pc._dtlsRole);if(transceiver.rtcpParameters&&transceiver.rtcpParameters.reducedSize){sdp+="a=rtcp-rsize\r\n"}});var desc=new window.RTCSessionDescription({type:"answer",sdp:sdp});return Promise.resolve(desc)};RTCPeerConnection.prototype.addIceCandidate=function(candidate){var pc=this;var sections;if(candidate&&!(candidate.sdpMLineIndex!==undefined||candidate.sdpMid)){return Promise.reject(new TypeError("sdpMLineIndex or sdpMid required"))}return new Promise(function(resolve,reject){if(!pc.remoteDescription){return reject(makeError("InvalidStateError","Can not add ICE candidate without a remote description"))}else if(!candidate||candidate.candidate===""){for(var j=0;j0?SDPUtils.parseCandidate(candidate.candidate):{};if(cand.protocol==="tcp"&&(cand.port===0||cand.port===9)){return resolve()}if(cand.component&&cand.component!==1){return resolve()}if(sdpMLineIndex===0||sdpMLineIndex>0&&transceiver.iceTransport!==pc.transceivers[0].iceTransport){if(!maybeAddCandidate(transceiver.iceTransport,cand)){return reject(makeError("OperationError","Can not add ICE candidate"))}}var candidateString=candidate.candidate.trim();if(candidateString.indexOf("a=")===0){candidateString=candidateString.substr(2)}sections=SDPUtils.getMediaSections(pc.remoteDescription.sdp);sections[sdpMLineIndex]+="a="+(cand.type?candidateString:"end-of-candidates")+"\r\n";pc.remoteDescription.sdp=sections.join("")}else{return reject(makeError("OperationError","Can not add ICE candidate"))}}resolve()})};RTCPeerConnection.prototype.getStats=function(){var promises=[];this.transceivers.forEach(function(transceiver){["rtpSender","rtpReceiver","iceGatherer","iceTransport","dtlsTransport"].forEach(function(method){if(transceiver[method]){promises.push(transceiver[method].getStats())}})});var fixStatsType=function(stat){return{inboundrtp:"inbound-rtp",outboundrtp:"outbound-rtp",candidatepair:"candidate-pair",localcandidate:"local-candidate",remotecandidate:"remote-candidate"}[stat.type]||stat.type};return new Promise(function(resolve){var results=new Map;Promise.all(promises).then(function(res){res.forEach(function(result){Object.keys(result).forEach(function(id){result[id].type=fixStatsType(result[id]);results.set(id,result[id])})});resolve(results)})})};var methods=["createOffer","createAnswer"];methods.forEach(function(method){var nativeMethod=RTCPeerConnection.prototype[method];RTCPeerConnection.prototype[method]=function(){var args=arguments;if(typeof args[0]==="function"||typeof args[1]==="function"){return nativeMethod.apply(this,[arguments[2]]).then(function(description){if(typeof args[0]==="function"){args[0].apply(null,[description])}},function(error){if(typeof args[1]==="function"){args[1].apply(null,[error])}})}return nativeMethod.apply(this,arguments)}});methods=["setLocalDescription","setRemoteDescription","addIceCandidate"];methods.forEach(function(method){var nativeMethod=RTCPeerConnection.prototype[method];RTCPeerConnection.prototype[method]=function(){var args=arguments;if(typeof args[1]==="function"||typeof args[2]==="function"){return nativeMethod.apply(this,arguments).then(function(){if(typeof args[1]==="function"){args[1].apply(null)}},function(error){if(typeof args[2]==="function"){args[2].apply(null,[error])}})}return nativeMethod.apply(this,arguments)}});["getStats"].forEach(function(method){var nativeMethod=RTCPeerConnection.prototype[method];RTCPeerConnection.prototype[method]=function(){var args=arguments;if(typeof args[1]==="function"){return nativeMethod.apply(this,arguments).then(function(){if(typeof args[1]==="function"){args[1].apply(null)}})}return nativeMethod.apply(this,arguments)}});return RTCPeerConnection}},{sdp:46}],46:[function(require,module,exports){"use strict";var SDPUtils={};SDPUtils.generateIdentifier=function(){return Math.random().toString(36).substr(2,10)};SDPUtils.localCName=SDPUtils.generateIdentifier();SDPUtils.splitLines=function(blob){return blob.trim().split("\n").map(function(line){return line.trim()})};SDPUtils.splitSections=function(blob){var parts=blob.split("\nm=");return parts.map(function(part,index){return(index>0?"m="+part:part).trim()+"\r\n"})};SDPUtils.getDescription=function(blob){var sections=SDPUtils.splitSections(blob);return sections&§ions[0]};SDPUtils.getMediaSections=function(blob){var sections=SDPUtils.splitSections(blob);sections.shift();return sections};SDPUtils.matchPrefix=function(blob,prefix){return SDPUtils.splitLines(blob).filter(function(line){return line.indexOf(prefix)===0})};SDPUtils.parseCandidate=function(line){var parts;if(line.indexOf("a=candidate:")===0){parts=line.substring(12).split(" ")}else{parts=line.substring(10).split(" ")}var candidate={foundation:parts[0],component:parseInt(parts[1],10),protocol:parts[2].toLowerCase(),priority:parseInt(parts[3],10),ip:parts[4],address:parts[4],port:parseInt(parts[5],10),type:parts[7]};for(var i=8;i0?parts[0].split("/")[1]:"sendrecv",uri:parts[1]}};SDPUtils.writeExtmap=function(headerExtension){return"a=extmap:"+(headerExtension.id||headerExtension.preferredId)+(headerExtension.direction&&headerExtension.direction!=="sendrecv"?"/"+headerExtension.direction:"")+" "+headerExtension.uri+"\r\n"};SDPUtils.parseFmtp=function(line){var parsed={};var kv;var parts=line.substr(line.indexOf(" ")+1).split(";");for(var j=0;j-1){parts.attribute=line.substr(sp+1,colon-sp-1);parts.value=line.substr(colon+1)}else{parts.attribute=line.substr(sp+1)}return parts};SDPUtils.parseSsrcGroup=function(line){var parts=line.substr(13).split(" ");return{semantics:parts.shift(),ssrcs:parts.map(function(ssrc){return parseInt(ssrc,10)})}};SDPUtils.getMid=function(mediaSection){var mid=SDPUtils.matchPrefix(mediaSection,"a=mid:")[0];if(mid){return mid.substr(6)}};SDPUtils.parseFingerprint=function(line){var parts=line.substr(14).split(" ");return{algorithm:parts[0].toLowerCase(),value:parts[1]}};SDPUtils.getDtlsParameters=function(mediaSection,sessionpart){var lines=SDPUtils.matchPrefix(mediaSection+sessionpart,"a=fingerprint:");return{role:"auto",fingerprints:lines.map(SDPUtils.parseFingerprint)}};SDPUtils.writeDtlsParameters=function(params,setupType){var sdp="a=setup:"+setupType+"\r\n";params.fingerprints.forEach(function(fp){sdp+="a=fingerprint:"+fp.algorithm+" "+fp.value+"\r\n"});return sdp};SDPUtils.parseCryptoLine=function(line){var parts=line.substr(9).split(" ");return{tag:parseInt(parts[0],10),cryptoSuite:parts[1],keyParams:parts[2],sessionParams:parts.slice(3)}};SDPUtils.writeCryptoLine=function(parameters){return"a=crypto:"+parameters.tag+" "+parameters.cryptoSuite+" "+(typeof parameters.keyParams==="object"?SDPUtils.writeCryptoKeyParams(parameters.keyParams):parameters.keyParams)+(parameters.sessionParams?" "+parameters.sessionParams.join(" "):"")+"\r\n"};SDPUtils.parseCryptoKeyParams=function(keyParams){if(keyParams.indexOf("inline:")!==0){return null}var parts=keyParams.substr(7).split("|");return{keyMethod:"inline",keySalt:parts[0],lifeTime:parts[1],mkiValue:parts[2]?parts[2].split(":")[0]:undefined,mkiLength:parts[2]?parts[2].split(":")[1]:undefined}};SDPUtils.writeCryptoKeyParams=function(keyParams){return keyParams.keyMethod+":"+keyParams.keySalt+(keyParams.lifeTime?"|"+keyParams.lifeTime:"")+(keyParams.mkiValue&&keyParams.mkiLength?"|"+keyParams.mkiValue+":"+keyParams.mkiLength:"")};SDPUtils.getCryptoParameters=function(mediaSection,sessionpart){var lines=SDPUtils.matchPrefix(mediaSection+sessionpart,"a=crypto:");return lines.map(SDPUtils.parseCryptoLine)};SDPUtils.getIceParameters=function(mediaSection,sessionpart){var ufrag=SDPUtils.matchPrefix(mediaSection+sessionpart,"a=ice-ufrag:")[0];var pwd=SDPUtils.matchPrefix(mediaSection+sessionpart,"a=ice-pwd:")[0];if(!(ufrag&&pwd)){return null}return{usernameFragment:ufrag.substr(12),password:pwd.substr(10)}};SDPUtils.writeIceParameters=function(params){return"a=ice-ufrag:"+params.usernameFragment+"\r\n"+"a=ice-pwd:"+params.password+"\r\n"};SDPUtils.parseRtpParameters=function(mediaSection){var description={codecs:[],headerExtensions:[],fecMechanisms:[],rtcp:[]};var lines=SDPUtils.splitLines(mediaSection);var mline=lines[0].split(" ");for(var i=3;i0?"9":"0";sdp+=" UDP/TLS/RTP/SAVPF ";sdp+=caps.codecs.map(function(codec){if(codec.preferredPayloadType!==undefined){return codec.preferredPayloadType}return codec.payloadType}).join(" ")+"\r\n";sdp+="c=IN IP4 0.0.0.0\r\n";sdp+="a=rtcp:9 IN IP4 0.0.0.0\r\n";caps.codecs.forEach(function(codec){sdp+=SDPUtils.writeRtpMap(codec);sdp+=SDPUtils.writeFmtp(codec);sdp+=SDPUtils.writeRtcpFb(codec)});var maxptime=0;caps.codecs.forEach(function(codec){if(codec.maxptime>maxptime){maxptime=codec.maxptime}});if(maxptime>0){sdp+="a=maxptime:"+maxptime+"\r\n"}sdp+="a=rtcp-mux\r\n";if(caps.headerExtensions){caps.headerExtensions.forEach(function(extension){sdp+=SDPUtils.writeExtmap(extension)})}return sdp};SDPUtils.parseRtpEncodingParameters=function(mediaSection){var encodingParameters=[];var description=SDPUtils.parseRtpParameters(mediaSection);var hasRed=description.fecMechanisms.indexOf("RED")!==-1;var hasUlpfec=description.fecMechanisms.indexOf("ULPFEC")!==-1;var ssrcs=SDPUtils.matchPrefix(mediaSection,"a=ssrc:").map(function(line){return SDPUtils.parseSsrcMedia(line)}).filter(function(parts){return parts.attribute==="cname"});var primarySsrc=ssrcs.length>0&&ssrcs[0].ssrc;var secondarySsrc;var flows=SDPUtils.matchPrefix(mediaSection,"a=ssrc-group:FID").map(function(line){var parts=line.substr(17).split(" ");return parts.map(function(part){return parseInt(part,10)})});if(flows.length>0&&flows[0].length>1&&flows[0][0]===primarySsrc){secondarySsrc=flows[0][1]}description.codecs.forEach(function(codec){if(codec.name.toUpperCase()==="RTX"&&codec.parameters.apt){var encParam={ssrc:primarySsrc,codecPayloadType:parseInt(codec.parameters.apt,10)};if(primarySsrc&&secondarySsrc){encParam.rtx={ssrc:secondarySsrc}}encodingParameters.push(encParam);if(hasRed){encParam=JSON.parse(JSON.stringify(encParam));encParam.fec={ssrc:primarySsrc,mechanism:hasUlpfec?"red+ulpfec":"red"};encodingParameters.push(encParam)}}});if(encodingParameters.length===0&&primarySsrc){encodingParameters.push({ssrc:primarySsrc})}var bandwidth=SDPUtils.matchPrefix(mediaSection,"b=");if(bandwidth.length){if(bandwidth[0].indexOf("b=TIAS:")===0){bandwidth=parseInt(bandwidth[0].substr(7),10)}else if(bandwidth[0].indexOf("b=AS:")===0){bandwidth=parseInt(bandwidth[0].substr(5),10)*1e3*.95-50*40*8}else{bandwidth=undefined}encodingParameters.forEach(function(params){params.maxBitrate=bandwidth})}return encodingParameters};SDPUtils.parseRtcpParameters=function(mediaSection){var rtcpParameters={};var remoteSsrc=SDPUtils.matchPrefix(mediaSection,"a=ssrc:").map(function(line){return SDPUtils.parseSsrcMedia(line)}).filter(function(obj){return obj.attribute==="cname"})[0];if(remoteSsrc){rtcpParameters.cname=remoteSsrc.value;rtcpParameters.ssrc=remoteSsrc.ssrc}var rsize=SDPUtils.matchPrefix(mediaSection,"a=rtcp-rsize");rtcpParameters.reducedSize=rsize.length>0;rtcpParameters.compound=rsize.length===0;var mux=SDPUtils.matchPrefix(mediaSection,"a=rtcp-mux");rtcpParameters.mux=mux.length>0;return rtcpParameters};SDPUtils.parseMsid=function(mediaSection){var parts;var spec=SDPUtils.matchPrefix(mediaSection,"a=msid:");if(spec.length===1){parts=spec[0].substr(7).split(" ");return{stream:parts[0],track:parts[1]}}var planB=SDPUtils.matchPrefix(mediaSection,"a=ssrc:").map(function(line){return SDPUtils.parseSsrcMedia(line)}).filter(function(msidParts){return msidParts.attribute==="msid"});if(planB.length>0){parts=planB[0].value.split(" ");return{stream:parts[0],track:parts[1]}}};SDPUtils.parseSctpDescription=function(mediaSection){var mline=SDPUtils.parseMLine(mediaSection);var maxSizeLine=SDPUtils.matchPrefix(mediaSection,"a=max-message-size:");var maxMessageSize;if(maxSizeLine.length>0){maxMessageSize=parseInt(maxSizeLine[0].substr(19),10)}if(isNaN(maxMessageSize)){maxMessageSize=65536}var sctpPort=SDPUtils.matchPrefix(mediaSection,"a=sctp-port:");if(sctpPort.length>0){return{port:parseInt(sctpPort[0].substr(12),10),protocol:mline.fmt,maxMessageSize:maxMessageSize}}var sctpMapLines=SDPUtils.matchPrefix(mediaSection,"a=sctpmap:");if(sctpMapLines.length>0){var parts=SDPUtils.matchPrefix(mediaSection,"a=sctpmap:")[0].substr(10).split(" ");return{port:parseInt(parts[0],10),protocol:parts[1],maxMessageSize:maxMessageSize}}};SDPUtils.writeSctpDescription=function(media,sctp){var output=[];if(media.protocol!=="DTLS/SCTP"){output=["m="+media.kind+" 9 "+media.protocol+" "+sctp.protocol+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctp-port:"+sctp.port+"\r\n"]}else{output=["m="+media.kind+" 9 "+media.protocol+" "+sctp.port+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctpmap:"+sctp.port+" "+sctp.protocol+" 65535\r\n"]}if(sctp.maxMessageSize!==undefined){output.push("a=max-message-size:"+sctp.maxMessageSize+"\r\n")}return output.join("")};SDPUtils.generateSessionId=function(){return Math.random().toString().substr(2,21)};SDPUtils.writeSessionBoilerplate=function(sessId,sessVer,sessUser){var sessionId;var version=sessVer!==undefined?sessVer:2;if(sessId){sessionId=sessId}else{sessionId=SDPUtils.generateSessionId()}var user=sessUser||"thisisadapterortc";return"v=0\r\n"+"o="+user+" "+sessionId+" "+version+" IN IP4 127.0.0.1\r\n"+"s=-\r\n"+"t=0 0\r\n"};SDPUtils.writeMediaSection=function(transceiver,caps,type,stream){var sdp=SDPUtils.writeRtpDescription(transceiver.kind,caps);sdp+=SDPUtils.writeIceParameters(transceiver.iceGatherer.getLocalParameters());sdp+=SDPUtils.writeDtlsParameters(transceiver.dtlsTransport.getLocalParameters(),type==="offer"?"actpass":"active");sdp+="a=mid:"+transceiver.mid+"\r\n";if(transceiver.direction){sdp+="a="+transceiver.direction+"\r\n"}else if(transceiver.rtpSender&&transceiver.rtpReceiver){sdp+="a=sendrecv\r\n"}else if(transceiver.rtpSender){sdp+="a=sendonly\r\n"}else if(transceiver.rtpReceiver){sdp+="a=recvonly\r\n"}else{sdp+="a=inactive\r\n"}if(transceiver.rtpSender){var msid="msid:"+stream.id+" "+transceiver.rtpSender.track.id+"\r\n";sdp+="a="+msid;sdp+="a=ssrc:"+transceiver.sendEncodingParameters[0].ssrc+" "+msid;if(transceiver.sendEncodingParameters[0].rtx){sdp+="a=ssrc:"+transceiver.sendEncodingParameters[0].rtx.ssrc+" "+msid;sdp+="a=ssrc-group:FID "+transceiver.sendEncodingParameters[0].ssrc+" "+transceiver.sendEncodingParameters[0].rtx.ssrc+"\r\n"}}sdp+="a=ssrc:"+transceiver.sendEncodingParameters[0].ssrc+" cname:"+SDPUtils.localCName+"\r\n";if(transceiver.rtpSender&&transceiver.sendEncodingParameters[0].rtx){sdp+="a=ssrc:"+transceiver.sendEncodingParameters[0].rtx.ssrc+" cname:"+SDPUtils.localCName+"\r\n"}return sdp};SDPUtils.getDirection=function(mediaSection,sessionpart){var lines=SDPUtils.splitLines(mediaSection);for(var i=0;i */ +.hljs { display: inline-block; padding: 0.5em; background: white; color: black; } + +.hljs-comment, .hljs-annotation, .hljs-template_comment, .diff .hljs-header, .hljs-chunk, .apache .hljs-cbracket { color: #008000; } + +.hljs-keyword, .hljs-id, .hljs-built_in, .css .smalltalk .hljs-class, .hljs-winutils, .bash .hljs-variable, .tex .hljs-command, .hljs-request, .hljs-status, .nginx .hljs-title { color: #00f; } + +.xml .hljs-tag { color: #00f; } +.xml .hljs-tag .hljs-value { color: #00f; } + +.hljs-string, .hljs-title, .hljs-parent, .hljs-tag .hljs-value, .hljs-rules .hljs-value { color: #a31515; } + +.ruby .hljs-symbol { color: #a31515; } +.ruby .hljs-symbol .hljs-string { color: #a31515; } + +.hljs-template_tag, .django .hljs-variable, .hljs-addition, .hljs-flow, .hljs-stream, .apache .hljs-tag, .hljs-date, .tex .hljs-formula, .coffeescript .hljs-attribute { color: #a31515; } + +.ruby .hljs-string, .hljs-decorator, .hljs-filter .hljs-argument, .hljs-localvars, .hljs-array, .hljs-attr_selector, .hljs-pseudo, .hljs-pi, .hljs-doctype, .hljs-deletion, .hljs-envvar, .hljs-shebang, .hljs-preprocessor, .hljs-pragma, .userType, .apache .hljs-sqbracket, .nginx .hljs-built_in, .tex .hljs-special, .hljs-prompt { color: #2b91af; } + +.hljs-phpdoc, .hljs-javadoc, .hljs-xmlDocTag { color: #808080; } + +.vhdl .hljs-typename { font-weight: bold; } +.vhdl .hljs-string { color: #666666; } +.vhdl .hljs-literal { color: #a31515; } +.vhdl .hljs-attribute { color: #00b0e8; } + +.xml .hljs-attribute { color: #f00; } + +.col > :first-child, .col-1 > :first-child, .col-2 > :first-child, .col-3 > :first-child, .col-4 > :first-child, .col-5 > :first-child, .col-6 > :first-child, .col-7 > :first-child, .col-8 > :first-child, .col-9 > :first-child, .col-10 > :first-child, .col-11 > :first-child, .tsd-panel > :first-child, ul.tsd-descriptions > li > :first-child, .col > :first-child > :first-child, .col-1 > :first-child > :first-child, .col-2 > :first-child > :first-child, .col-3 > :first-child > :first-child, .col-4 > :first-child > :first-child, .col-5 > :first-child > :first-child, .col-6 > :first-child > :first-child, .col-7 > :first-child > :first-child, .col-8 > :first-child > :first-child, .col-9 > :first-child > :first-child, .col-10 > :first-child > :first-child, .col-11 > :first-child > :first-child, .tsd-panel > :first-child > :first-child, ul.tsd-descriptions > li > :first-child > :first-child, .col > :first-child > :first-child > :first-child, .col-1 > :first-child > :first-child > :first-child, .col-2 > :first-child > :first-child > :first-child, .col-3 > :first-child > :first-child > :first-child, .col-4 > :first-child > :first-child > :first-child, .col-5 > :first-child > :first-child > :first-child, .col-6 > :first-child > :first-child > :first-child, .col-7 > :first-child > :first-child > :first-child, .col-8 > :first-child > :first-child > :first-child, .col-9 > :first-child > :first-child > :first-child, .col-10 > :first-child > :first-child > :first-child, .col-11 > :first-child > :first-child > :first-child, .tsd-panel > :first-child > :first-child > :first-child, ul.tsd-descriptions > li > :first-child > :first-child > :first-child { margin-top: 0; } +.col > :last-child, .col-1 > :last-child, .col-2 > :last-child, .col-3 > :last-child, .col-4 > :last-child, .col-5 > :last-child, .col-6 > :last-child, .col-7 > :last-child, .col-8 > :last-child, .col-9 > :last-child, .col-10 > :last-child, .col-11 > :last-child, .tsd-panel > :last-child, ul.tsd-descriptions > li > :last-child, .col > :last-child > :last-child, .col-1 > :last-child > :last-child, .col-2 > :last-child > :last-child, .col-3 > :last-child > :last-child, .col-4 > :last-child > :last-child, .col-5 > :last-child > :last-child, .col-6 > :last-child > :last-child, .col-7 > :last-child > :last-child, .col-8 > :last-child > :last-child, .col-9 > :last-child > :last-child, .col-10 > :last-child > :last-child, .col-11 > :last-child > :last-child, .tsd-panel > :last-child > :last-child, ul.tsd-descriptions > li > :last-child > :last-child, .col > :last-child > :last-child > :last-child, .col-1 > :last-child > :last-child > :last-child, .col-2 > :last-child > :last-child > :last-child, .col-3 > :last-child > :last-child > :last-child, .col-4 > :last-child > :last-child > :last-child, .col-5 > :last-child > :last-child > :last-child, .col-6 > :last-child > :last-child > :last-child, .col-7 > :last-child > :last-child > :last-child, .col-8 > :last-child > :last-child > :last-child, .col-9 > :last-child > :last-child > :last-child, .col-10 > :last-child > :last-child > :last-child, .col-11 > :last-child > :last-child > :last-child, .tsd-panel > :last-child > :last-child > :last-child, ul.tsd-descriptions > li > :last-child > :last-child > :last-child { margin-bottom: 0; } + +.container { max-width: 1200px; margin: 0 auto; padding: 0 40px; } +@media (max-width: 640px) { .container { padding: 0 20px; } } + +.container-main { padding-bottom: 200px; } + +.row { position: relative; margin: 0 -10px; } +.row:after { visibility: hidden; display: block; content: ""; clear: both; height: 0; } + +.col, .col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11 { box-sizing: border-box; float: left; padding: 0 10px; } + +.col-1 { width: 8.3333333333%; } + +.offset-1 { margin-left: 8.3333333333%; } + +.col-2 { width: 16.6666666667%; } + +.offset-2 { margin-left: 16.6666666667%; } + +.col-3 { width: 25%; } + +.offset-3 { margin-left: 25%; } + +.col-4 { width: 33.3333333333%; } + +.offset-4 { margin-left: 33.3333333333%; } + +.col-5 { width: 41.6666666667%; } + +.offset-5 { margin-left: 41.6666666667%; } + +.col-6 { width: 50%; } + +.offset-6 { margin-left: 50%; } + +.col-7 { width: 58.3333333333%; } + +.offset-7 { margin-left: 58.3333333333%; } + +.col-8 { width: 66.6666666667%; } + +.offset-8 { margin-left: 66.6666666667%; } + +.col-9 { width: 75%; } + +.offset-9 { margin-left: 75%; } + +.col-10 { width: 83.3333333333%; } + +.offset-10 { margin-left: 83.3333333333%; } + +.col-11 { width: 91.6666666667%; } + +.offset-11 { margin-left: 91.6666666667%; } + +.tsd-kind-icon { display: block; position: relative; padding-left: 20px; text-indent: -20px; } +.tsd-kind-icon:before { content: ''; display: inline-block; vertical-align: middle; width: 17px; height: 17px; margin: 0 3px 2px 0; background-image: url(../images/icons.png); } +@media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { .tsd-kind-icon:before { background-image: url(../images/icons@2x.png); background-size: 238px 204px; } } + +.tsd-signature.tsd-kind-icon:before { background-position: 0 -153px; } + +.tsd-kind-object-literal > .tsd-kind-icon:before { background-position: 0px -17px; } +.tsd-kind-object-literal.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -17px; } +.tsd-kind-object-literal.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -17px; } + +.tsd-kind-class > .tsd-kind-icon:before { background-position: 0px -34px; } +.tsd-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -34px; } +.tsd-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -34px; } + +.tsd-kind-class.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: 0px -51px; } +.tsd-kind-class.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -51px; } +.tsd-kind-class.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -51px; } + +.tsd-kind-interface > .tsd-kind-icon:before { background-position: 0px -68px; } +.tsd-kind-interface.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -68px; } +.tsd-kind-interface.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -68px; } + +.tsd-kind-interface.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: 0px -85px; } +.tsd-kind-interface.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -85px; } +.tsd-kind-interface.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -85px; } + +.tsd-kind-module > .tsd-kind-icon:before { background-position: 0px -102px; } +.tsd-kind-module.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -102px; } +.tsd-kind-module.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -102px; } + +.tsd-kind-external-module > .tsd-kind-icon:before { background-position: 0px -102px; } +.tsd-kind-external-module.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -102px; } +.tsd-kind-external-module.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -102px; } + +.tsd-kind-enum > .tsd-kind-icon:before { background-position: 0px -119px; } +.tsd-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -119px; } +.tsd-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -119px; } + +.tsd-kind-enum-member > .tsd-kind-icon:before { background-position: 0px -136px; } +.tsd-kind-enum-member.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -136px; } +.tsd-kind-enum-member.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -136px; } + +.tsd-kind-signature > .tsd-kind-icon:before { background-position: 0px -153px; } +.tsd-kind-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -153px; } +.tsd-kind-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -153px; } + +.tsd-kind-type-alias > .tsd-kind-icon:before { background-position: 0px -170px; } +.tsd-kind-type-alias.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -170px; } +.tsd-kind-type-alias.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -170px; } + +.tsd-kind-type-alias.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: 0px -187px; } +.tsd-kind-type-alias.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -187px; } +.tsd-kind-type-alias.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -187px; } + +.tsd-kind-variable > .tsd-kind-icon:before { background-position: -136px -0px; } +.tsd-kind-variable.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -0px; } +.tsd-kind-variable.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-variable.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-variable.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -0px; } +.tsd-kind-variable.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -0px; } +.tsd-kind-variable.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-variable.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -0px; } +.tsd-kind-variable.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -0px; } + +.tsd-kind-property > .tsd-kind-icon:before { background-position: -136px -0px; } +.tsd-kind-property.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -0px; } +.tsd-kind-property.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-property.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-property.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -0px; } +.tsd-kind-property.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -0px; } +.tsd-kind-property.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-property.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -0px; } +.tsd-kind-property.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -0px; } + +.tsd-kind-get-signature > .tsd-kind-icon:before { background-position: -136px -17px; } +.tsd-kind-get-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -17px; } +.tsd-kind-get-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -17px; } + +.tsd-kind-set-signature > .tsd-kind-icon:before { background-position: -136px -34px; } +.tsd-kind-set-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -34px; } +.tsd-kind-set-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -34px; } + +.tsd-kind-accessor > .tsd-kind-icon:before { background-position: -136px -51px; } +.tsd-kind-accessor.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -51px; } +.tsd-kind-accessor.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -51px; } +.tsd-kind-accessor.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -51px; } +.tsd-kind-accessor.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -51px; } +.tsd-kind-accessor.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -51px; } +.tsd-kind-accessor.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -51px; } +.tsd-kind-accessor.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -51px; } + +.tsd-kind-function > .tsd-kind-icon:before { background-position: -136px -68px; } +.tsd-kind-function.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -68px; } +.tsd-kind-function.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-function.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-function.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -68px; } +.tsd-kind-function.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -68px; } +.tsd-kind-function.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-function.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -68px; } +.tsd-kind-function.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -68px; } + +.tsd-kind-method > .tsd-kind-icon:before { background-position: -136px -68px; } +.tsd-kind-method.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -68px; } +.tsd-kind-method.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-method.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-method.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -68px; } +.tsd-kind-method.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -68px; } +.tsd-kind-method.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-method.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -68px; } +.tsd-kind-method.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -68px; } + +.tsd-kind-call-signature > .tsd-kind-icon:before { background-position: -136px -68px; } +.tsd-kind-call-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -68px; } +.tsd-kind-call-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -68px; } + +.tsd-kind-function.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: -136px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -85px; } + +.tsd-kind-method.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: -136px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -85px; } + +.tsd-kind-constructor > .tsd-kind-icon:before { background-position: -136px -102px; } +.tsd-kind-constructor.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -102px; } +.tsd-kind-constructor.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -102px; } +.tsd-kind-constructor.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -102px; } +.tsd-kind-constructor.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -102px; } +.tsd-kind-constructor.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -102px; } + +.tsd-kind-constructor-signature > .tsd-kind-icon:before { background-position: -136px -102px; } +.tsd-kind-constructor-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -102px; } +.tsd-kind-constructor-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -102px; } + +.tsd-kind-index-signature > .tsd-kind-icon:before { background-position: -136px -119px; } +.tsd-kind-index-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -119px; } +.tsd-kind-index-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -119px; } + +.tsd-kind-event > .tsd-kind-icon:before { background-position: -136px -136px; } +.tsd-kind-event.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -136px; } +.tsd-kind-event.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -136px; } +.tsd-kind-event.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -136px; } +.tsd-kind-event.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -136px; } +.tsd-kind-event.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -136px; } +.tsd-kind-event.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -136px; } +.tsd-kind-event.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -136px; } +.tsd-kind-event.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -136px; } + +.tsd-is-static > .tsd-kind-icon:before { background-position: -136px -153px; } +.tsd-is-static.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -153px; } +.tsd-is-static.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -153px; } +.tsd-is-static.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -153px; } +.tsd-is-static.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -153px; } +.tsd-is-static.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -153px; } +.tsd-is-static.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -153px; } +.tsd-is-static.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -153px; } +.tsd-is-static.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -153px; } + +.tsd-is-static.tsd-kind-function > .tsd-kind-icon:before { background-position: -136px -170px; } +.tsd-is-static.tsd-kind-function.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -170px; } +.tsd-is-static.tsd-kind-function.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -170px; } + +.tsd-is-static.tsd-kind-method > .tsd-kind-icon:before { background-position: -136px -170px; } +.tsd-is-static.tsd-kind-method.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -170px; } +.tsd-is-static.tsd-kind-method.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -170px; } + +.tsd-is-static.tsd-kind-call-signature > .tsd-kind-icon:before { background-position: -136px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -170px; } + +.tsd-is-static.tsd-kind-event > .tsd-kind-icon:before { background-position: -136px -187px; } +.tsd-is-static.tsd-kind-event.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -187px; } +.tsd-is-static.tsd-kind-event.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -187px; } + +.no-transition { transition: none !important; } + +@keyframes fade-in { from { opacity: 0; } + to { opacity: 1; } } +@keyframes fade-out { from { opacity: 1; visibility: visible; } + to { opacity: 0; } } +@keyframes fade-in-delayed { 0% { opacity: 0; } + 33% { opacity: 0; } + 100% { opacity: 1; } } +@keyframes fade-out-delayed { 0% { opacity: 1; visibility: visible; } + 66% { opacity: 0; } + 100% { opacity: 0; } } +@keyframes shift-to-left { from { transform: translate(0, 0); } + to { transform: translate(-25%, 0); } } +@keyframes unshift-to-left { from { transform: translate(-25%, 0); } + to { transform: translate(0, 0); } } +@keyframes pop-in-from-right { from { transform: translate(100%, 0); } + to { transform: translate(0, 0); } } +@keyframes pop-out-to-right { from { transform: translate(0, 0); visibility: visible; } + to { transform: translate(100%, 0); } } +body { background: #fdfdfd; font-family: "Segoe UI", sans-serif; font-size: 16px; color: #222; } + +a { color: #4da6ff; text-decoration: none; } +a:hover { text-decoration: underline; } + +code, pre { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; padding: 0.2em; margin: 0; font-size: 14px; background-color: rgba(0, 0, 0, 0.04); } + +pre { padding: 10px; } +pre code { padding: 0; font-size: 100%; background-color: transparent; } + +.tsd-typography { line-height: 1.333em; } +.tsd-typography ul { list-style: square; padding: 0 0 0 20px; margin: 0; } +.tsd-typography h4, .tsd-typography .tsd-index-panel h3, .tsd-index-panel .tsd-typography h3, .tsd-typography h5, .tsd-typography h6 { font-size: 1em; margin: 0; } +.tsd-typography h5, .tsd-typography h6 { font-weight: normal; } +.tsd-typography p, .tsd-typography ul, .tsd-typography ol { margin: 1em 0; } + +@media (min-width: 901px) and (max-width: 1024px) { html.default .col-content { width: 72%; } + html.default .col-menu { width: 28%; } + html.default .tsd-navigation { padding-left: 10px; } } +@media (max-width: 900px) { html.default .col-content { float: none; width: 100%; } + html.default .col-menu { position: fixed !important; overflow: auto; -webkit-overflow-scrolling: touch; overflow-scrolling: touch; z-index: 1024; top: 0 !important; bottom: 0 !important; left: auto !important; right: 0 !important; width: 100%; padding: 20px 20px 0 0; max-width: 450px; visibility: hidden; background-color: #fff; transform: translate(100%, 0); } + html.default .col-menu > *:last-child { padding-bottom: 20px; } + html.default .overlay { content: ""; display: block; position: fixed; z-index: 1023; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.75); visibility: hidden; } + html.default.to-has-menu .overlay { animation: fade-in 0.4s; } + html.default.to-has-menu header, html.default.to-has-menu footer, html.default.to-has-menu .col-content { animation: shift-to-left 0.4s; } + html.default.to-has-menu .col-menu { animation: pop-in-from-right 0.4s; } + html.default.from-has-menu .overlay { animation: fade-out 0.4s; } + html.default.from-has-menu header, html.default.from-has-menu footer, html.default.from-has-menu .col-content { animation: unshift-to-left 0.4s; } + html.default.from-has-menu .col-menu { animation: pop-out-to-right 0.4s; } + html.default.has-menu body { overflow: hidden; } + html.default.has-menu .overlay { visibility: visible; } + html.default.has-menu header, html.default.has-menu footer, html.default.has-menu .col-content { transform: translate(-25%, 0); } + html.default.has-menu .col-menu { visibility: visible; transform: translate(0, 0); } } + +.tsd-page-title { padding: 70px 0 20px 0; margin: 0 0 40px 0; background: #fff; box-shadow: 0 0 5px rgba(0, 0, 0, 0.35); } +.tsd-page-title h1 { margin: 0; } + +.tsd-breadcrumb { margin: 0; padding: 0; color: #808080; } +.tsd-breadcrumb a { color: #808080; text-decoration: none; } +.tsd-breadcrumb a:hover { text-decoration: underline; } +.tsd-breadcrumb li { display: inline; } +.tsd-breadcrumb li:after { content: " / "; } + +html.minimal .container { margin: 0; } +html.minimal .container-main { padding-top: 50px; padding-bottom: 0; } +html.minimal .content-wrap { padding-left: 300px; } +html.minimal .tsd-navigation { position: fixed !important; overflow: auto; -webkit-overflow-scrolling: touch; overflow-scrolling: touch; box-sizing: border-box; z-index: 1; left: 0; top: 40px; bottom: 0; width: 300px; padding: 20px; margin: 0; } +html.minimal .tsd-member .tsd-member { margin-left: 0; } +html.minimal .tsd-page-toolbar { position: fixed; z-index: 2; } +html.minimal #tsd-filter .tsd-filter-group { right: 0; transform: none; } +html.minimal footer { background-color: transparent; } +html.minimal footer .container { padding: 0; } +html.minimal .tsd-generator { padding: 0; } +@media (max-width: 900px) { html.minimal .tsd-navigation { display: none; } + html.minimal .content-wrap { padding-left: 0; } } + +dl.tsd-comment-tags { overflow: hidden; } +dl.tsd-comment-tags dt { float: left; padding: 1px 5px; margin: 0 10px 0 0; border-radius: 4px; border: 1px solid #808080; color: #808080; font-size: 0.8em; font-weight: normal; } +dl.tsd-comment-tags dd { margin: 0 0 10px 0; } +dl.tsd-comment-tags dd:before, dl.tsd-comment-tags dd:after { display: table; content: " "; } +dl.tsd-comment-tags dd pre, dl.tsd-comment-tags dd:after { clear: both; } +dl.tsd-comment-tags p { margin: 0; } + +.tsd-panel.tsd-comment .lead { font-size: 1.1em; line-height: 1.333em; margin-bottom: 2em; } +.tsd-panel.tsd-comment .lead:last-child { margin-bottom: 0; } + +.toggle-protected .tsd-is-private { display: none; } + +.tsd-is-private, .tsd-is-protected, .tsd-is-private-protected { display: none; } + +.toggle-inherited .tsd-is-inherited { display: none; } + +.tsd-is-not-exported { display: none; } + +.tsd-is-external { display: none; } + +#tsd-filter { position: relative; display: inline-block; height: 40px; vertical-align: bottom; } +.no-filter #tsd-filter { display: none; } +#tsd-filter .tsd-filter-group { display: inline-block; height: 40px; vertical-align: bottom; white-space: nowrap; } +#tsd-filter input { display: none; } +@media (max-width: 900px) { #tsd-filter .tsd-filter-group { display: block; position: absolute; top: 40px; right: 20px; height: auto; background-color: #fff; visibility: hidden; transform: translate(50%, 0); box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); } + .has-options #tsd-filter .tsd-filter-group { visibility: visible; } + .to-has-options #tsd-filter .tsd-filter-group { animation: fade-in 0.2s; } + .from-has-options #tsd-filter .tsd-filter-group { animation: fade-out 0.2s; } + #tsd-filter label, #tsd-filter .tsd-select { display: block; padding-right: 20px; } } + +footer { border-top: 1px solid #eee; background-color: #fff; } +footer.with-border-bottom { border-bottom: 1px solid #eee; } +footer .tsd-legend-group { font-size: 0; } +footer .tsd-legend { display: inline-block; width: 25%; padding: 0; font-size: 16px; list-style: none; line-height: 1.333em; vertical-align: top; } +@media (max-width: 900px) { footer .tsd-legend { width: 50%; } } + +.tsd-hierarchy { list-style: square; padding: 0 0 0 20px; margin: 0; } +.tsd-hierarchy .target { font-weight: bold; } + +.tsd-index-panel .tsd-index-content { margin-bottom: -30px !important; } +.tsd-index-panel .tsd-index-section { margin-bottom: 30px !important; } +.tsd-index-panel h3 { margin: 0 -20px 10px -20px; padding: 0 20px 10px 20px; border-bottom: 1px solid #eee; } +.tsd-index-panel ul.tsd-index-list { -moz-column-count: 3; -ms-column-count: 3; -o-column-count: 3; column-count: 3; -moz-column-gap: 20px; -ms-column-gap: 20px; -o-column-gap: 20px; column-gap: 20px; padding: 0; list-style: none; line-height: 1.333em; } +@media (max-width: 900px) { .tsd-index-panel ul.tsd-index-list { -moz-column-count: 1; -ms-column-count: 1; -o-column-count: 1; column-count: 1; } } +@media (min-width: 901px) and (max-width: 1024px) { .tsd-index-panel ul.tsd-index-list { -moz-column-count: 2; -ms-column-count: 2; -o-column-count: 2; column-count: 2; } } +.tsd-index-panel ul.tsd-index-list li { -webkit-column-break-inside: avoid; -moz-column-break-inside: avoid; -ms-column-break-inside: avoid; -o-column-break-inside: avoid; column-break-inside: avoid; -webkit-page-break-inside: avoid; -moz-page-break-inside: avoid; -ms-page-break-inside: avoid; -o-page-break-inside: avoid; page-break-inside: avoid; } +.tsd-index-panel a, .tsd-index-panel .tsd-parent-kind-module a { color: #9600ff; } +.tsd-index-panel .tsd-parent-kind-interface a { color: #7da01f; } +.tsd-index-panel .tsd-parent-kind-enum a { color: #cc9900; } +.tsd-index-panel .tsd-parent-kind-class a { color: #4da6ff; } +.tsd-index-panel .tsd-kind-module a { color: #9600ff; } +.tsd-index-panel .tsd-kind-interface a { color: #7da01f; } +.tsd-index-panel .tsd-kind-enum a { color: #cc9900; } +.tsd-index-panel .tsd-kind-class a { color: #4da6ff; } +.tsd-index-panel .tsd-is-private a { color: #808080; } + +.tsd-flag { display: inline-block; padding: 1px 5px; border-radius: 4px; color: #fff; background-color: #808080; text-indent: 0; font-size: 14px; font-weight: normal; } + +.tsd-anchor { position: absolute; top: -100px; } + +.tsd-member { position: relative; } +.tsd-member .tsd-anchor + h3 { margin-top: 0; margin-bottom: 0; border-bottom: none; } + +.tsd-navigation { padding: 0 0 0 40px; } +.tsd-navigation a { display: block; padding-top: 2px; padding-bottom: 2px; border-left: 2px solid transparent; color: #222; text-decoration: none; transition: border-left-color 0.1s; } +.tsd-navigation a:hover { text-decoration: underline; } +.tsd-navigation ul { margin: 0; padding: 0; list-style: none; } +.tsd-navigation li { padding: 0; } + +.tsd-navigation.primary { padding-bottom: 40px; } +.tsd-navigation.primary a { display: block; padding-top: 6px; padding-bottom: 6px; } +.tsd-navigation.primary ul li a { padding-left: 5px; } +.tsd-navigation.primary ul li li a { padding-left: 25px; } +.tsd-navigation.primary ul li li li a { padding-left: 45px; } +.tsd-navigation.primary ul li li li li a { padding-left: 65px; } +.tsd-navigation.primary ul li li li li li a { padding-left: 85px; } +.tsd-navigation.primary ul li li li li li li a { padding-left: 105px; } +.tsd-navigation.primary > ul { border-bottom: 1px solid #eee; } +.tsd-navigation.primary li { border-top: 1px solid #eee; } +.tsd-navigation.primary li.current > a { font-weight: bold; } +.tsd-navigation.primary li.label span { display: block; padding: 20px 0 6px 5px; color: #808080; } +.tsd-navigation.primary li.globals + li > span, .tsd-navigation.primary li.globals + li > a { padding-top: 20px; } + +.tsd-navigation.secondary ul { transition: opacity 0.2s; } +.tsd-navigation.secondary ul li a { padding-left: 25px; } +.tsd-navigation.secondary ul li li a { padding-left: 45px; } +.tsd-navigation.secondary ul li li li a { padding-left: 65px; } +.tsd-navigation.secondary ul li li li li a { padding-left: 85px; } +.tsd-navigation.secondary ul li li li li li a { padding-left: 105px; } +.tsd-navigation.secondary ul li li li li li li a { padding-left: 125px; } +.tsd-navigation.secondary ul.current a { border-left-color: #eee; } +.tsd-navigation.secondary li.focus > a, .tsd-navigation.secondary ul.current li.focus > a { border-left-color: #000; } +.tsd-navigation.secondary li.current { margin-top: 20px; margin-bottom: 20px; border-left-color: #eee; } +.tsd-navigation.secondary li.current > a { font-weight: bold; } + +@media (min-width: 901px) { .menu-sticky-wrap { position: static; } + .no-csspositionsticky .menu-sticky-wrap.sticky { position: fixed; } + .no-csspositionsticky .menu-sticky-wrap.sticky-current { position: fixed; } + .no-csspositionsticky .menu-sticky-wrap.sticky-current ul.before-current, .no-csspositionsticky .menu-sticky-wrap.sticky-current ul.after-current { opacity: 0; } + .no-csspositionsticky .menu-sticky-wrap.sticky-bottom { position: absolute; top: auto !important; left: auto !important; bottom: 0; right: 0; } + .csspositionsticky .menu-sticky-wrap.sticky { position: -webkit-sticky; position: sticky; } + .csspositionsticky .menu-sticky-wrap.sticky-current { position: -webkit-sticky; position: sticky; } } + +.tsd-panel { margin: 20px 0; padding: 20px; background-color: #fff; box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); } +.tsd-panel:empty { display: none; } +.tsd-panel > h1, .tsd-panel > h2, .tsd-panel > h3 { margin: 1.5em -20px 10px -20px; padding: 0 20px 10px 20px; border-bottom: 1px solid #eee; } +.tsd-panel > h1.tsd-before-signature, .tsd-panel > h2.tsd-before-signature, .tsd-panel > h3.tsd-before-signature { margin-bottom: 0; border-bottom: 0; } +.tsd-panel table { display: block; width: 100%; overflow: auto; margin-top: 10px; word-break: normal; word-break: keep-all; } +.tsd-panel table th { font-weight: bold; } +.tsd-panel table th, .tsd-panel table td { padding: 6px 13px; border: 1px solid #ddd; } +.tsd-panel table tr { background-color: #fff; border-top: 1px solid #ccc; } +.tsd-panel table tr:nth-child(2n) { background-color: #f8f8f8; } + +.tsd-panel-group { margin: 60px 0; } +.tsd-panel-group > h1, .tsd-panel-group > h2, .tsd-panel-group > h3 { padding-left: 20px; padding-right: 20px; } + +#tsd-search { transition: background-color 0.2s; } +#tsd-search .title { position: relative; z-index: 2; } +#tsd-search .field { position: absolute; left: 0; top: 0; right: 40px; height: 40px; } +#tsd-search .field input { box-sizing: border-box; position: relative; top: -50px; z-index: 1; width: 100%; padding: 0 10px; opacity: 0; outline: 0; border: 0; background: transparent; color: #222; } +#tsd-search .field label { position: absolute; overflow: hidden; right: -40px; } +#tsd-search .field input, #tsd-search .title { transition: opacity 0.2s; } +#tsd-search .results { position: absolute; visibility: hidden; top: 40px; width: 100%; margin: 0; padding: 0; list-style: none; box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); } +#tsd-search .results li { padding: 0 10px; background-color: #fdfdfd; } +#tsd-search .results li:nth-child(even) { background-color: #fff; } +#tsd-search .results li.state { display: none; } +#tsd-search .results li.current, #tsd-search .results li:hover { background-color: #eee; } +#tsd-search .results a { display: block; } +#tsd-search .results a:before { top: 10px; } +#tsd-search .results span.parent { color: #808080; font-weight: normal; } +#tsd-search.has-focus { background-color: #eee; } +#tsd-search.has-focus .field input { top: 0; opacity: 1; } +#tsd-search.has-focus .title { z-index: 0; opacity: 0; } +#tsd-search.has-focus .results { visibility: visible; } +#tsd-search.loading .results li.state.loading { display: block; } +#tsd-search.failure .results li.state.failure { display: block; } + +.tsd-signature { margin: 0 0 1em 0; padding: 10px; border: 1px solid #eee; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 14px; } +.tsd-signature.tsd-kind-icon { padding-left: 30px; } +.tsd-signature.tsd-kind-icon:before { top: 10px; left: 10px; } +.tsd-panel > .tsd-signature { margin-left: -20px; margin-right: -20px; border-width: 1px 0; } +.tsd-panel > .tsd-signature.tsd-kind-icon { padding-left: 40px; } +.tsd-panel > .tsd-signature.tsd-kind-icon:before { left: 20px; } + +.tsd-signature-symbol { color: #808080; font-weight: normal; } + +.tsd-signature-type { font-style: italic; font-weight: normal; } + +.tsd-signatures { padding: 0; margin: 0 0 1em 0; border: 1px solid #eee; } +.tsd-signatures .tsd-signature { margin: 0; border-width: 1px 0 0 0; transition: background-color 0.1s; } +.tsd-signatures .tsd-signature:first-child { border-top-width: 0; } +.tsd-signatures .tsd-signature.current { background-color: #eee; } +.tsd-signatures.active > .tsd-signature { cursor: pointer; } +.tsd-panel > .tsd-signatures { margin-left: -20px; margin-right: -20px; border-width: 1px 0; } +.tsd-panel > .tsd-signatures .tsd-signature.tsd-kind-icon { padding-left: 40px; } +.tsd-panel > .tsd-signatures .tsd-signature.tsd-kind-icon:before { left: 20px; } +.tsd-panel > a.anchor + .tsd-signatures { border-top-width: 0; margin-top: -20px; } + +ul.tsd-descriptions { position: relative; overflow: hidden; transition: height 0.3s; padding: 0; list-style: none; } +ul.tsd-descriptions.active > .tsd-description { display: none; } +ul.tsd-descriptions.active > .tsd-description.current { display: block; } +ul.tsd-descriptions.active > .tsd-description.fade-in { animation: fade-in-delayed 0.3s; } +ul.tsd-descriptions.active > .tsd-description.fade-out { animation: fade-out-delayed 0.3s; position: absolute; display: block; top: 0; left: 0; right: 0; opacity: 0; visibility: hidden; } +ul.tsd-descriptions h4, ul.tsd-descriptions .tsd-index-panel h3, .tsd-index-panel ul.tsd-descriptions h3 { font-size: 16px; margin: 1em 0 0.5em 0; } + +ul.tsd-parameters, ul.tsd-type-parameters { list-style: square; margin: 0; padding-left: 20px; } +ul.tsd-parameters > li.tsd-parameter-siganture, ul.tsd-type-parameters > li.tsd-parameter-siganture { list-style: none; margin-left: -20px; } +ul.tsd-parameters h5, ul.tsd-type-parameters h5 { font-size: 16px; margin: 1em 0 0.5em 0; } +ul.tsd-parameters .tsd-comment, ul.tsd-type-parameters .tsd-comment { margin-top: -0.5em; } + +.tsd-sources { font-size: 14px; color: #808080; margin: 0 0 1em 0; } +.tsd-sources a { color: #808080; text-decoration: underline; } +.tsd-sources ul, .tsd-sources p { margin: 0 !important; } +.tsd-sources ul { list-style: none; padding: 0; } + +.tsd-page-toolbar { position: absolute; z-index: 1; top: 0; left: 0; width: 100%; height: 40px; color: #333; background: #fff; border-bottom: 1px solid #eee; } +.tsd-page-toolbar a { color: #333; text-decoration: none; } +.tsd-page-toolbar a.title { font-weight: bold; } +.tsd-page-toolbar a.title:hover { text-decoration: underline; } +.tsd-page-toolbar .table-wrap { display: table; width: 100%; height: 40px; } +.tsd-page-toolbar .table-cell { display: table-cell; position: relative; white-space: nowrap; line-height: 40px; } +.tsd-page-toolbar .table-cell:first-child { width: 100%; } + +.tsd-widget:before, .tsd-select .tsd-select-label:before, .tsd-select .tsd-select-list li:before { content: ""; display: inline-block; width: 40px; height: 40px; margin: 0 -8px 0 0; background-image: url(../images/widgets.png); background-repeat: no-repeat; text-indent: -1024px; vertical-align: bottom; } +@media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { .tsd-widget:before, .tsd-select .tsd-select-label:before, .tsd-select .tsd-select-list li:before { background-image: url(../images/widgets@2x.png); background-size: 320px 40px; } } + +.tsd-widget { display: inline-block; overflow: hidden; opacity: 0.6; height: 40px; transition: opacity 0.1s, background-color 0.2s; vertical-align: bottom; cursor: pointer; } +.tsd-widget:hover { opacity: 0.8; } +.tsd-widget.active { opacity: 1; background-color: #eee; } +.tsd-widget.no-caption { width: 40px; } +.tsd-widget.no-caption:before { margin: 0; } +.tsd-widget.search:before { background-position: 0 0; } +.tsd-widget.menu:before { background-position: -40px 0; } +.tsd-widget.options:before { background-position: -80px 0; } +.tsd-widget.options, .tsd-widget.menu { display: none; } +@media (max-width: 900px) { .tsd-widget.options, .tsd-widget.menu { display: inline-block; } } +input[type=checkbox] + .tsd-widget:before { background-position: -120px 0; } +input[type=checkbox]:checked + .tsd-widget:before { background-position: -160px 0; } + +.tsd-select { position: relative; display: inline-block; height: 40px; transition: opacity 0.1s, background-color 0.2s; vertical-align: bottom; cursor: pointer; } +.tsd-select .tsd-select-label { opacity: 0.6; transition: opacity 0.2s; } +.tsd-select .tsd-select-label:before { background-position: -240px 0; } +.tsd-select.active .tsd-select-label { opacity: 0.8; } +.tsd-select.active .tsd-select-list { visibility: visible; opacity: 1; transition-delay: 0s; } +.tsd-select .tsd-select-list { position: absolute; visibility: hidden; top: 40px; left: 0; margin: 0; padding: 0; opacity: 0; list-style: none; box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); transition: visibility 0s 0.2s, opacity 0.2s; } +.tsd-select .tsd-select-list li { padding: 0 20px 0 0; background-color: #fdfdfd; } +.tsd-select .tsd-select-list li:before { background-position: 40px 0; } +.tsd-select .tsd-select-list li:nth-child(even) { background-color: #fff; } +.tsd-select .tsd-select-list li:hover { background-color: #eee; } +.tsd-select .tsd-select-list li.selected:before { background-position: -200px 0; } +@media (max-width: 900px) { .tsd-select .tsd-select-list { top: 0; left: auto; right: 100%; margin-right: -5px; } + .tsd-select .tsd-select-label:before { background-position: -280px 0; } } + +img { max-width: 100%; } diff --git a/docs/assets/css/main.css.map b/docs/assets/css/main.css.map new file mode 100644 index 00000000..f13c5226 --- /dev/null +++ b/docs/assets/css/main.css.map @@ -0,0 +1,7 @@ +{ +"version": 3, +"mappings": "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", +"sources": ["../../../../src/default/assets/css/vendors/_normalize.sass","../../../../src/default/assets/css/vendors/_highlight.js.sass","../../../../src/default/assets/css/setup/_mixins.sass","../../../../src/default/assets/css/setup/_grid.sass","../../../../src/default/assets/css/setup/_icons.scss","../../../../src/default/assets/css/setup/_animations.sass","../../../../src/default/assets/css/setup/_typography.sass","../../../../src/default/assets/css/_constants.sass","../../../../src/default/assets/css/layouts/_default.sass","../../../../src/default/assets/css/layouts/_minimal.sass","../../../../src/default/assets/css/elements/_comment.sass","../../../../src/default/assets/css/elements/_filter.sass","../../../../src/default/assets/css/elements/_footer.sass","../../../../src/default/assets/css/elements/_hierarchy.sass","../../../../src/default/assets/css/elements/_index.sass","../../../../src/default/assets/css/elements/_member.sass","../../../../src/default/assets/css/elements/_navigation.sass","../../../../src/default/assets/css/elements/_panel.sass","../../../../src/default/assets/css/elements/_search.sass","../../../../src/default/assets/css/elements/_signatures.sass","../../../../src/default/assets/css/elements/_sources.sass","../../../../src/default/assets/css/elements/_toolbar.sass","../../../../src/default/assets/css/elements/_images.sass"], +"names": [], +"file": "main.css" +} diff --git a/docs/assets/images/icons.png b/docs/assets/images/icons.png new file mode 100644 index 0000000000000000000000000000000000000000..3836d5fe46e48bbe186116855aae879c23935327 GIT binary patch literal 9615 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 NZmR35l_G4O{0}dcmS_L~ literal 0 HcmV?d00001 diff --git a/docs/assets/images/icons@2x.png b/docs/assets/images/icons@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5a209e2f6d7f915cc9cb6fe7a4264c8be4db87b0 GIT binary patch literal 28144 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 U&wLp6h6BX&boFyt=akR{0G%$)mH+?% literal 0 HcmV?d00001 diff --git a/docs/assets/images/widgets@2x.png b/docs/assets/images/widgets@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4bbbd57272f3b28f47527d4951ad10f950b8ad43 GIT binary patch literal 855 zcmeAS@N?(olHy`uVBq!ia0y~yU}^xe12~w0Jcmn z@(X6T|9^jgLcx21{)7exgY)a>N6m2F0<`Rqr;B4q1>>88jUdw-7W`c)zLE*mq8W2H z-<&Jl_Hco5BuC5n@AbF5GD82~-e8-v=#zCyUX0F-o}8pPfAv`!GN$ff+TL<~@kgt} z62eO?_|&+>xBmM$@p|z`tIKEdpPf8%qI>4r7@jn<=eta*{3~?g(zz{Ke9zc-G^gr? z-7foa?LcS!hmbwzru}ICvbWLlW8;+l-}!^=c32!^nV`+`C*;0-*Y%l94pC;Cb3GXz zzSf%a!{gVr{Y_lVuUj+a)*Ca+!-Hu%xmP&&X-2CuANY8^i{D7Kg6qzP zXz_ps9+lN8ESH{K4`yu&b~I>N9xGlE&;2u*b?+Go!AhN?m-bxlLvtC#MzDF2kFzfHJ1W7ybqdefSqVhbOykd*Yi%EDuhs z4wF{ft^bv2+DDnKb8gj1FuvcV`M}luS>lO<^)8x>y1#R;a=-ZKwWTQQb)ioBbi;zh zD!f5V)8581to1LL7c9!l^PSC$NBPYif!_vAZhmL4)v4U)4UsrLYiH_9rmQDd?)(e5 z^pcH>qvBg*i0dus2r*mp4;zKvu=P#s-ti;2obl`NjjwoYd>e(oo#j_uyRb<7Pv^If zzZ|mGHmV)8^tbO%^>eqMw(@7(&3g{jEp-Najo7V75xI_ZHK*FA`elF{r5}E*d7+j_R literal 0 HcmV?d00001 diff --git a/docs/assets/js/lib/backbone-1.1.2.min.js b/docs/assets/js/lib/backbone-1.1.2.min.js new file mode 100644 index 00000000..8ea4b13d --- /dev/null +++ b/docs/assets/js/lib/backbone-1.1.2.min.js @@ -0,0 +1,2 @@ +(function(t,e){if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(i,r,s){t.Backbone=e(t,s,i,r)})}else if(typeof exports!=="undefined"){var i=require("underscore");e(t,exports,i)}else{t.Backbone=e(t,{},t._,t.jQuery||t.Zepto||t.ender||t.$)}})(this,function(t,e,i,r){var s=t.Backbone;var n=[];var a=n.push;var o=n.slice;var h=n.splice;e.VERSION="1.1.2";e.$=r;e.noConflict=function(){t.Backbone=s;return this};e.emulateHTTP=false;e.emulateJSON=false;var u=e.Events={on:function(t,e,i){if(!c(this,"on",t,[e,i])||!e)return this;this._events||(this._events={});var r=this._events[t]||(this._events[t]=[]);r.push({callback:e,context:i,ctx:i||this});return this},once:function(t,e,r){if(!c(this,"once",t,[e,r])||!e)return this;var s=this;var n=i.once(function(){s.off(t,n);e.apply(this,arguments)});n._callback=e;return this.on(t,n,r)},off:function(t,e,r){var s,n,a,o,h,u,l,f;if(!this._events||!c(this,"off",t,[e,r]))return this;if(!t&&!e&&!r){this._events=void 0;return this}o=t?[t]:i.keys(this._events);for(h=0,u=o.length;h").attr(t);this.setElement(r,false)}else{this.setElement(i.result(this,"el"),false)}}});e.sync=function(t,r,s){var n=T[t];i.defaults(s||(s={}),{emulateHTTP:e.emulateHTTP,emulateJSON:e.emulateJSON});var a={type:n,dataType:"json"};if(!s.url){a.url=i.result(r,"url")||M()}if(s.data==null&&r&&(t==="create"||t==="update"||t==="patch")){a.contentType="application/json";a.data=JSON.stringify(s.attrs||r.toJSON(s))}if(s.emulateJSON){a.contentType="application/x-www-form-urlencoded";a.data=a.data?{model:a.data}:{}}if(s.emulateHTTP&&(n==="PUT"||n==="DELETE"||n==="PATCH")){a.type="POST";if(s.emulateJSON)a.data._method=n;var o=s.beforeSend;s.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",n);if(o)return o.apply(this,arguments)}}if(a.type!=="GET"&&!s.emulateJSON){a.processData=false}if(a.type==="PATCH"&&k){a.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var h=s.xhr=e.ajax(i.extend(a,s));r.trigger("request",r,h,s);return h};var k=typeof window!=="undefined"&&!!window.ActiveXObject&&!(window.XMLHttpRequest&&(new XMLHttpRequest).dispatchEvent);var T={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};e.ajax=function(){return e.$.ajax.apply(e.$,arguments)};var $=e.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var S=/\((.*?)\)/g;var H=/(\(\?)?:\w+/g;var A=/\*\w+/g;var I=/[\-{}\[\]+?.,\\\^$|#\s]/g;i.extend($.prototype,u,{initialize:function(){},route:function(t,r,s){if(!i.isRegExp(t))t=this._routeToRegExp(t);if(i.isFunction(r)){s=r;r=""}if(!s)s=this[r];var n=this;e.history.route(t,function(i){var a=n._extractParameters(t,i);n.execute(s,a);n.trigger.apply(n,["route:"+r].concat(a));n.trigger("route",r,a);e.history.trigger("route",n,r,a)});return this},execute:function(t,e){if(t)t.apply(this,e)},navigate:function(t,i){e.history.navigate(t,i);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=i.result(this,"routes");var t,e=i.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(I,"\\$&").replace(S,"(?:$1)?").replace(H,function(t,e){return e?t:"([^/?]+)"}).replace(A,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var r=t.exec(e).slice(1);return i.map(r,function(t,e){if(e===r.length-1)return t||null;return t?decodeURIComponent(t):null})}});var N=e.History=function(){this.handlers=[];i.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var R=/^[#\/]|\s+$/g;var O=/^\/+|\/+$/g;var P=/msie [\w.]+/;var C=/\/$/;var j=/#.*$/;N.started=false;i.extend(N.prototype,u,{interval:50,atRoot:function(){return this.location.pathname.replace(/[^\/]$/,"$&/")===this.root},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._wantsHashChange||e){t=decodeURI(this.location.pathname+this.location.search);var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.slice(i.length)}else{t=this.getHash()}}return t.replace(R,"")},start:function(t){if(N.started)throw new Error("Backbone.history has already been started");N.started=true;this.options=i.extend({root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var r=this.getFragment();var s=document.documentMode;var n=P.exec(navigator.userAgent.toLowerCase())&&(!s||s<=7);this.root=("/"+this.root+"/").replace(O,"/");if(n&&this._wantsHashChange){var a=e.$('