Commit a4ab9a0a6cd22b11b52200b66c0417d9379e3960

Authored by panlinlin
1 parent 2b1e5697

集成rtc播放器

src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
@@ -18,6 +18,7 @@ public class StreamInfo { @@ -18,6 +18,7 @@ public class StreamInfo {
18 private String ws_ts; 18 private String ws_ts;
19 private String rtmp; 19 private String rtmp;
20 private String rtsp; 20 private String rtsp;
  21 + private String rtc;
21 private JSONArray tracks; 22 private JSONArray tracks;
22 23
23 public String getApp() { 24 public String getApp() {
@@ -139,4 +140,12 @@ public class StreamInfo { @@ -139,4 +140,12 @@ public class StreamInfo {
139 public void setStreamId(String streamId) { 140 public void setStreamId(String streamId) {
140 this.streamId = streamId; 141 this.streamId = streamId;
141 } 142 }
  143 +
  144 + public String getRtc() {
  145 + return rtc;
  146 + }
  147 +
  148 + public void setRtc(String rtc) {
  149 + this.rtc = rtc;
  150 + }
142 } 151 }
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -4,6 +4,7 @@ import java.util.List; @@ -4,6 +4,7 @@ import java.util.List;
4 import java.util.UUID; 4 import java.util.UUID;
5 5
6 import com.alibaba.fastjson.JSON; 6 import com.alibaba.fastjson.JSON;
  7 +import com.alibaba.fastjson.JSONArray;
7 import com.genersoft.iot.vmp.common.StreamInfo; 8 import com.genersoft.iot.vmp.common.StreamInfo;
8 import com.genersoft.iot.vmp.conf.MediaServerConfig; 9 import com.genersoft.iot.vmp.conf.MediaServerConfig;
9 import com.genersoft.iot.vmp.gb28181.bean.Device; 10 import com.genersoft.iot.vmp.gb28181.bean.Device;
@@ -239,26 +240,33 @@ public class ZLMHttpHookListener { @@ -239,26 +240,33 @@ public class ZLMHttpHookListener {
239 String app = json.getString("app"); 240 String app = json.getString("app");
240 String streamId = json.getString("stream"); 241 String streamId = json.getString("stream");
241 String schema = json.getString("schema"); 242 String schema = json.getString("schema");
242 - boolean regist = json.getBoolean("regist"); 243 + JSONArray tracks = json.getJSONArray("tracks");
  244 + String regist = json.getString("regist");
  245 + if (tracks != null) {
  246 + System.out.println("222222" + schema);
  247 + }
  248 + if ("rtmp".equals(schema)){
243 249
244 - if ("rtp".equals(app) && !regist ) {  
245 - StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId);  
246 - if (streamInfo!=null){  
247 - redisCatchStorage.stopPlay(streamInfo);  
248 - storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());  
249 - }else{  
250 - streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId);  
251 - redisCatchStorage.stopPlayback(streamInfo);  
252 - }  
253 - }else {  
254 - if (!"rtp".equals(app) && "rtmp".equals(schema)){  
255 - if (regist) {  
256 - zlmMediaListManager.addMedia(app, streamId);  
257 - }else {  
258 - zlmMediaListManager.removeMedia(app, streamId); 250 + if ("rtp".equals(app) && regist != null ) {
  251 + StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId);
  252 + if (streamInfo!=null){
  253 + redisCatchStorage.stopPlay(streamInfo);
  254 + storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
  255 + }else{
  256 + streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId);
  257 + redisCatchStorage.stopPlayback(streamInfo);
  258 + }
  259 + }else {
  260 + if (!"rtp".equals(app) ){
  261 + if (regist == null) {
  262 + zlmMediaListManager.addMedia(app, streamId);
  263 + }else {
  264 + zlmMediaListManager.removeMedia(app, streamId);
  265 + }
259 } 266 }
260 } 267 }
261 } 268 }
  269 +
262 JSONObject ret = new JSONObject(); 270 JSONObject ret = new JSONObject();
263 ret.put("code", 0); 271 ret.put("code", 0);
264 ret.put("msg", "success"); 272 ret.put("msg", "success");
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
@@ -42,6 +42,8 @@ public class MediaServiceImpl implements IMediaService { @@ -42,6 +42,8 @@ public class MediaServiceImpl implements IMediaService {
42 streamInfoResult.setWs_fmp4(String.format("ws://%s:%s/%s/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app, stream)); 42 streamInfoResult.setWs_fmp4(String.format("ws://%s:%s/%s/%s.live.mp4", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app, stream));
43 streamInfoResult.setTs(String.format("http://%s:%s/%s/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app, stream)); 43 streamInfoResult.setTs(String.format("http://%s:%s/%s/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app, stream));
44 streamInfoResult.setWs_ts(String.format("ws://%s:%s/%s/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app, stream)); 44 streamInfoResult.setWs_ts(String.format("ws://%s:%s/%s/%s.live.ts", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app, stream));
  45 + streamInfoResult.setRtc(String.format("http://%s:%s/index/api/webrtc?app=%s&stream=%s&type=play", mediaInfo.getWanIp(), mediaInfo.getHttpPort(), app, stream));
  46 +
45 return streamInfoResult; 47 return streamInfoResult;
46 } 48 }
47 49
web_src/index.html
@@ -6,7 +6,8 @@ @@ -6,7 +6,8 @@
6 <title>国标28181</title> 6 <title>国标28181</title>
7 </head> 7 </head>
8 <body> 8 <body>
9 - <script type="text/javascript" src="./js/EasyWasmPlayer.js"></script> 9 + <!-- <script type="text/javascript" src="./js/EasyWasmPlayer.js"></script> -->
  10 + <script type="text/javascript" src="/static/js/ZLMRTCClient.js"></script>
10 <script type="text/javascript" src="//api.map.baidu.com/api?v=2.0&ak=rk73w8dv1rkE4UdZsataG68VarhYQzrx&s=1"></script> 11 <script type="text/javascript" src="//api.map.baidu.com/api?v=2.0&ak=rk73w8dv1rkE4UdZsataG68VarhYQzrx&s=1"></script>
11 <div id="app"></div> 12 <div id="app"></div>
12 <!-- built files will be auto injected --> 13 <!-- built files will be auto injected -->
web_src/src/components/dialog/devicePlayer.vue
@@ -136,7 +136,7 @@ @@ -136,7 +136,7 @@
136 </template> 136 </template>
137 137
138 <script> 138 <script>
139 -import player from '../dialog/player.vue' 139 +import player from '../dialog/rtcPlayer.vue'
140 export default { 140 export default {
141 name: 'devicePlayer', 141 name: 'devicePlayer',
142 props: {}, 142 props: {},
@@ -250,7 +250,7 @@ export default { @@ -250,7 +250,7 @@ export default {
250 250
251 this.hasaudio = hasAudio; 251 this.hasaudio = hasAudio;
252 this.isLoging = false; 252 this.isLoging = false;
253 - this.videoUrl = streamInfo.ws_flv; 253 + this.videoUrl = streamInfo.rtc;
254 this.streamId = streamInfo.streamId; 254 this.streamId = streamInfo.streamId;
255 this.app = streamInfo.app; 255 this.app = streamInfo.app;
256 this.playFromStreamInfo(false, streamInfo) 256 this.playFromStreamInfo(false, streamInfo)
web_src/src/components/dialog/rtcPlayer.vue 0 → 100644
  1 +<template>
  2 + <div id="rtcPlayer">
  3 + <video id='webRtcPlayerBox' controls autoplay style="text-align:left;">
  4 + Your browser is too old which doesn't support HTML5 video.
  5 + </video>
  6 + </div>
  7 +</template>
  8 +
  9 +<script>
  10 +export default {
  11 + name: 'rtcPlayer',
  12 + data() {
  13 + return {
  14 + webrtcPlayer: null
  15 + };
  16 + },
  17 + props: ['videoUrl', 'error', 'hasaudio'],
  18 + mounted () {
  19 + this.$nextTick(() =>{
  20 + console.log("初始化时的地址为: " + this.videoUrl)
  21 + this.play(this.videoUrl)
  22 + })
  23 + },
  24 + watch:{
  25 + videoUrl(newData, oldData){
  26 + this.pause();
  27 + this.play(newData);
  28 + },
  29 + immediate:true
  30 + },
  31 + methods: {
  32 + play: function (url) {
  33 + this.webrtcPlayer = new ZLMRTCClient.Endpoint({
  34 + element: document.getElementById('webRtcPlayerBox'),// video 标签
  35 + debug: true,// 是否打印日志
  36 + zlmsdpUrl: url,//流地址
  37 + simulecast: false,
  38 + useCamera: false,
  39 + audioEnable: false,
  40 + videoEnable: false,
  41 + recvOnly: true,
  42 + })
  43 + this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,(e)=>{// ICE 协商出错
  44 + console.error('ICE 协商出错')
  45 + this.eventcallbacK("ICE ERROR", "ICE 协商出错")
  46 + });
  47 +
  48 + this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,(e)=>{//获取到了远端流,可以播放
  49 + console.error('播放成功',e.streams)
  50 + this.eventcallbacK("playing", "播放成功")
  51 + });
  52 +
  53 + this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,(e)=>{// offer anwser 交换失败
  54 + console.error('offer anwser 交换失败',e)
  55 + this.eventcallbacK("OFFER ANSWER ERROR ", "offer anwser 交换失败")
  56 + if (e.code ==-400 && e.msg=="流不存在"){
  57 + console.log("111111")
  58 + setTimeout(()=>{
  59 + this.webrtcPlayer.close();
  60 + this.play(url)
  61 + }, 100)
  62 +
  63 + }
  64 + });
  65 +
  66 + this.webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,(s)=>{// 获取到了本地流
  67 +
  68 + // document.getElementById('selfVideo').srcObject=s;
  69 + this.eventcallbacK("LOCAL STREAM", "获取到了本地流")
  70 + });
  71 +
  72 + },
  73 + pause: function () {
  74 + if (this.webrtcPlayer != null) {
  75 + this.webrtcPlayer.close();
  76 + this.webrtcPlayer = null;
  77 + }
  78 +
  79 + },
  80 + eventcallbacK: function(type, message) {
  81 + console.log("player 事件回调")
  82 + console.log(type)
  83 + console.log(message)
  84 + }
  85 + },
  86 +}
  87 +</script>
  88 +
  89 +<style>
  90 + .LodingTitle {
  91 + min-width: 70px;
  92 + }
  93 + #rtcPlayer{
  94 + width: 100%;
  95 + }
  96 + #webRtcPlayerBox{
  97 + width: 100%;
  98 + max-height: 56vh;
  99 + background-color: #000;
  100 + }
  101 + /* 隐藏logo */
  102 + /* .iconqingxiLOGO {
  103 + display: none !important;
  104 + } */
  105 +
  106 +</style>
0 \ No newline at end of file 107 \ No newline at end of file
web_src/static/js/ZLMRTCClient.js 0 → 100644
  1 +var ZLMRTCClient = (function (exports) {
  2 + 'use strict';
  3 +
  4 + const Events$1 = {
  5 + WEBRTC_NOT_SUPPORT: 'WEBRTC_NOT_SUPPORT',
  6 + WEBRTC_ICE_CANDIDATE_ERROR: 'WEBRTC_ICE_CANDIDATE_ERROR',
  7 + WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED: 'WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED',
  8 + WEBRTC_ON_REMOTE_STREAMS: 'WEBRTC_ON_REMOTE_STREAMS',
  9 + WEBRTC_ON_LOCAL_STREAM: 'WEBRTC_ON_LOCAL_STREAM'
  10 + };
  11 +
  12 + const VERSION = '1.0.1';
  13 + const BUILD_DATE = 'Mon Apr 05 2021 10:22:48 GMT+0800 (中国标准时间)';
  14 +
  15 + // Copyright (C) <2018> Intel Corporation
  16 + //
  17 + // SPDX-License-Identifier: Apache-2.0
  18 + // eslint-disable-next-line require-jsdoc
  19 + function isFirefox() {
  20 + return window.navigator.userAgent.match('Firefox') !== null;
  21 + } // eslint-disable-next-line require-jsdoc
  22 +
  23 + function isChrome() {
  24 + return window.navigator.userAgent.match('Chrome') !== null;
  25 + } // eslint-disable-next-line require-jsdoc
  26 +
  27 + function isEdge() {
  28 + return window.navigator.userAgent.match(/Edge\/(\d+).(\d+)$/) !== null;
  29 + } // eslint-disable-next-line require-jsdoc
  30 +
  31 + // Copyright (C) <2018> Intel Corporation
  32 + /**
  33 + * @class AudioSourceInfo
  34 + * @classDesc Source info about an audio track. Values: 'mic', 'screen-cast', 'file', 'mixed'.
  35 + * @memberOf Owt.Base
  36 + * @readonly
  37 + * @enum {string}
  38 + */
  39 +
  40 + const AudioSourceInfo = {
  41 + MIC: 'mic',
  42 + SCREENCAST: 'screen-cast',
  43 + FILE: 'file',
  44 + MIXED: 'mixed'
  45 + };
  46 + /**
  47 + * @class VideoSourceInfo
  48 + * @classDesc Source info about a video track. Values: 'camera', 'screen-cast', 'file', 'mixed'.
  49 + * @memberOf Owt.Base
  50 + * @readonly
  51 + * @enum {string}
  52 + */
  53 +
  54 + const VideoSourceInfo = {
  55 + CAMERA: 'camera',
  56 + SCREENCAST: 'screen-cast',
  57 + FILE: 'file',
  58 + MIXED: 'mixed'
  59 + };
  60 + /**
  61 + * @class TrackKind
  62 + * @classDesc Kind of a track. Values: 'audio' for audio track, 'video' for video track, 'av' for both audio and video tracks.
  63 + * @memberOf Owt.Base
  64 + * @readonly
  65 + * @enum {string}
  66 + */
  67 +
  68 + const TrackKind = {
  69 + /**
  70 + * Audio tracks.
  71 + * @type string
  72 + */
  73 + AUDIO: 'audio',
  74 +
  75 + /**
  76 + * Video tracks.
  77 + * @type string
  78 + */
  79 + VIDEO: 'video',
  80 +
  81 + /**
  82 + * Both audio and video tracks.
  83 + * @type string
  84 + */
  85 + AUDIO_AND_VIDEO: 'av'
  86 + };
  87 + /**
  88 + * @class Resolution
  89 + * @memberOf Owt.Base
  90 + * @classDesc The Resolution defines the size of a rectangle.
  91 + * @constructor
  92 + * @param {number} width
  93 + * @param {number} height
  94 + */
  95 +
  96 + class Resolution {
  97 + // eslint-disable-next-line require-jsdoc
  98 + constructor(width, height) {
  99 + /**
  100 + * @member {number} width
  101 + * @instance
  102 + * @memberof Owt.Base.Resolution
  103 + */
  104 + this.width = width;
  105 + /**
  106 + * @member {number} height
  107 + * @instance
  108 + * @memberof Owt.Base.Resolution
  109 + */
  110 +
  111 + this.height = height;
  112 + }
  113 +
  114 + }
  115 +
  116 + /*
  117 + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  118 + *
  119 + * Use of this source code is governed by a BSD-style license
  120 + * that can be found in the LICENSE file in the root of the source
  121 + * tree.
  122 + */
  123 +
  124 + let logDisabled_ = true;
  125 + let deprecationWarnings_ = true;
  126 +
  127 + /**
  128 + * Extract browser version out of the provided user agent string.
  129 + *
  130 + * @param {!string} uastring userAgent string.
  131 + * @param {!string} expr Regular expression used as match criteria.
  132 + * @param {!number} pos position in the version string to be returned.
  133 + * @return {!number} browser version.
  134 + */
  135 + function extractVersion(uastring, expr, pos) {
  136 + const match = uastring.match(expr);
  137 + return match && match.length >= pos && parseInt(match[pos], 10);
  138 + }
  139 +
  140 + // Wraps the peerconnection event eventNameToWrap in a function
  141 + // which returns the modified event object (or false to prevent
  142 + // the event).
  143 + function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {
  144 + if (!window.RTCPeerConnection) {
  145 + return;
  146 + }
  147 + const proto = window.RTCPeerConnection.prototype;
  148 + const nativeAddEventListener = proto.addEventListener;
  149 + proto.addEventListener = function(nativeEventName, cb) {
  150 + if (nativeEventName !== eventNameToWrap) {
  151 + return nativeAddEventListener.apply(this, arguments);
  152 + }
  153 + const wrappedCallback = (e) => {
  154 + const modifiedEvent = wrapper(e);
  155 + if (modifiedEvent) {
  156 + if (cb.handleEvent) {
  157 + cb.handleEvent(modifiedEvent);
  158 + } else {
  159 + cb(modifiedEvent);
  160 + }
  161 + }
  162 + };
  163 + this._eventMap = this._eventMap || {};
  164 + if (!this._eventMap[eventNameToWrap]) {
  165 + this._eventMap[eventNameToWrap] = new Map();
  166 + }
  167 + this._eventMap[eventNameToWrap].set(cb, wrappedCallback);
  168 + return nativeAddEventListener.apply(this, [nativeEventName,
  169 + wrappedCallback]);
  170 + };
  171 +
  172 + const nativeRemoveEventListener = proto.removeEventListener;
  173 + proto.removeEventListener = function(nativeEventName, cb) {
  174 + if (nativeEventName !== eventNameToWrap || !this._eventMap
  175 + || !this._eventMap[eventNameToWrap]) {
  176 + return nativeRemoveEventListener.apply(this, arguments);
  177 + }
  178 + if (!this._eventMap[eventNameToWrap].has(cb)) {
  179 + return nativeRemoveEventListener.apply(this, arguments);
  180 + }
  181 + const unwrappedCb = this._eventMap[eventNameToWrap].get(cb);
  182 + this._eventMap[eventNameToWrap].delete(cb);
  183 + if (this._eventMap[eventNameToWrap].size === 0) {
  184 + delete this._eventMap[eventNameToWrap];
  185 + }
  186 + if (Object.keys(this._eventMap).length === 0) {
  187 + delete this._eventMap;
  188 + }
  189 + return nativeRemoveEventListener.apply(this, [nativeEventName,
  190 + unwrappedCb]);
  191 + };
  192 +
  193 + Object.defineProperty(proto, 'on' + eventNameToWrap, {
  194 + get() {
  195 + return this['_on' + eventNameToWrap];
  196 + },
  197 + set(cb) {
  198 + if (this['_on' + eventNameToWrap]) {
  199 + this.removeEventListener(eventNameToWrap,
  200 + this['_on' + eventNameToWrap]);
  201 + delete this['_on' + eventNameToWrap];
  202 + }
  203 + if (cb) {
  204 + this.addEventListener(eventNameToWrap,
  205 + this['_on' + eventNameToWrap] = cb);
  206 + }
  207 + },
  208 + enumerable: true,
  209 + configurable: true
  210 + });
  211 + }
  212 +
  213 + function disableLog(bool) {
  214 + if (typeof bool !== 'boolean') {
  215 + return new Error('Argument type: ' + typeof bool +
  216 + '. Please use a boolean.');
  217 + }
  218 + logDisabled_ = bool;
  219 + return (bool) ? 'adapter.js logging disabled' :
  220 + 'adapter.js logging enabled';
  221 + }
  222 +
  223 + /**
  224 + * Disable or enable deprecation warnings
  225 + * @param {!boolean} bool set to true to disable warnings.
  226 + */
  227 + function disableWarnings(bool) {
  228 + if (typeof bool !== 'boolean') {
  229 + return new Error('Argument type: ' + typeof bool +
  230 + '. Please use a boolean.');
  231 + }
  232 + deprecationWarnings_ = !bool;
  233 + return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');
  234 + }
  235 +
  236 + function log$1() {
  237 + if (typeof window === 'object') {
  238 + if (logDisabled_) {
  239 + return;
  240 + }
  241 + if (typeof console !== 'undefined' && typeof console.log === 'function') {
  242 + console.log.apply(console, arguments);
  243 + }
  244 + }
  245 + }
  246 +
  247 + /**
  248 + * Shows a deprecation warning suggesting the modern and spec-compatible API.
  249 + */
  250 + function deprecated(oldMethod, newMethod) {
  251 + if (!deprecationWarnings_) {
  252 + return;
  253 + }
  254 + console.warn(oldMethod + ' is deprecated, please use ' + newMethod +
  255 + ' instead.');
  256 + }
  257 +
  258 + /**
  259 + * Browser detector.
  260 + *
  261 + * @return {object} result containing browser and version
  262 + * properties.
  263 + */
  264 + function detectBrowser(window) {
  265 + // Returned result object.
  266 + const result = {browser: null, version: null};
  267 +
  268 + // Fail early if it's not a browser
  269 + if (typeof window === 'undefined' || !window.navigator) {
  270 + result.browser = 'Not a browser.';
  271 + return result;
  272 + }
  273 +
  274 + const {navigator} = window;
  275 +
  276 + if (navigator.mozGetUserMedia) { // Firefox.
  277 + result.browser = 'firefox';
  278 + result.version = extractVersion(navigator.userAgent,
  279 + /Firefox\/(\d+)\./, 1);
  280 + } else if (navigator.webkitGetUserMedia ||
  281 + (window.isSecureContext === false && window.webkitRTCPeerConnection &&
  282 + !window.RTCIceGatherer)) {
  283 + // Chrome, Chromium, Webview, Opera.
  284 + // Version matches Chrome/WebRTC version.
  285 + // Chrome 74 removed webkitGetUserMedia on http as well so we need the
  286 + // more complicated fallback to webkitRTCPeerConnection.
  287 + result.browser = 'chrome';
  288 + result.version = extractVersion(navigator.userAgent,
  289 + /Chrom(e|ium)\/(\d+)\./, 2);
  290 + } else if (navigator.mediaDevices &&
  291 + navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) { // Edge.
  292 + result.browser = 'edge';
  293 + result.version = extractVersion(navigator.userAgent,
  294 + /Edge\/(\d+).(\d+)$/, 2);
  295 + } else if (window.RTCPeerConnection &&
  296 + navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) { // Safari.
  297 + result.browser = 'safari';
  298 + result.version = extractVersion(navigator.userAgent,
  299 + /AppleWebKit\/(\d+)\./, 1);
  300 + result.supportsUnifiedPlan = window.RTCRtpTransceiver &&
  301 + 'currentDirection' in window.RTCRtpTransceiver.prototype;
  302 + } else { // Default fallthrough: not supported.
  303 + result.browser = 'Not a supported browser.';
  304 + return result;
  305 + }
  306 +
  307 + return result;
  308 + }
  309 +
  310 + /**
  311 + * Checks if something is an object.
  312 + *
  313 + * @param {*} val The something you want to check.
  314 + * @return true if val is an object, false otherwise.
  315 + */
  316 + function isObject$1(val) {
  317 + return Object.prototype.toString.call(val) === '[object Object]';
  318 + }
  319 +
  320 + /**
  321 + * Remove all empty objects and undefined values
  322 + * from a nested object -- an enhanced and vanilla version
  323 + * of Lodash's `compact`.
  324 + */
  325 + function compactObject(data) {
  326 + if (!isObject$1(data)) {
  327 + return data;
  328 + }
  329 +
  330 + return Object.keys(data).reduce(function(accumulator, key) {
  331 + const isObj = isObject$1(data[key]);
  332 + const value = isObj ? compactObject(data[key]) : data[key];
  333 + const isEmptyObject = isObj && !Object.keys(value).length;
  334 + if (value === undefined || isEmptyObject) {
  335 + return accumulator;
  336 + }
  337 + return Object.assign(accumulator, {[key]: value});
  338 + }, {});
  339 + }
  340 +
  341 + /* iterates the stats graph recursively. */
  342 + function walkStats(stats, base, resultSet) {
  343 + if (!base || resultSet.has(base.id)) {
  344 + return;
  345 + }
  346 + resultSet.set(base.id, base);
  347 + Object.keys(base).forEach(name => {
  348 + if (name.endsWith('Id')) {
  349 + walkStats(stats, stats.get(base[name]), resultSet);
  350 + } else if (name.endsWith('Ids')) {
  351 + base[name].forEach(id => {
  352 + walkStats(stats, stats.get(id), resultSet);
  353 + });
  354 + }
  355 + });
  356 + }
  357 +
  358 + /* filter getStats for a sender/receiver track. */
  359 + function filterStats(result, track, outbound) {
  360 + const streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';
  361 + const filteredResult = new Map();
  362 + if (track === null) {
  363 + return filteredResult;
  364 + }
  365 + const trackStats = [];
  366 + result.forEach(value => {
  367 + if (value.type === 'track' &&
  368 + value.trackIdentifier === track.id) {
  369 + trackStats.push(value);
  370 + }
  371 + });
  372 + trackStats.forEach(trackStat => {
  373 + result.forEach(stats => {
  374 + if (stats.type === streamStatsType && stats.trackId === trackStat.id) {
  375 + walkStats(result, stats, filteredResult);
  376 + }
  377 + });
  378 + });
  379 + return filteredResult;
  380 + }
  381 +
  382 + /*
  383 + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  384 + *
  385 + * Use of this source code is governed by a BSD-style license
  386 + * that can be found in the LICENSE file in the root of the source
  387 + * tree.
  388 + */
  389 + const logging = log$1;
  390 +
  391 + function shimGetUserMedia$3(window, browserDetails) {
  392 + const navigator = window && window.navigator;
  393 +
  394 + if (!navigator.mediaDevices) {
  395 + return;
  396 + }
  397 +
  398 + const constraintsToChrome_ = function(c) {
  399 + if (typeof c !== 'object' || c.mandatory || c.optional) {
  400 + return c;
  401 + }
  402 + const cc = {};
  403 + Object.keys(c).forEach(key => {
  404 + if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
  405 + return;
  406 + }
  407 + const r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};
  408 + if (r.exact !== undefined && typeof r.exact === 'number') {
  409 + r.min = r.max = r.exact;
  410 + }
  411 + const oldname_ = function(prefix, name) {
  412 + if (prefix) {
  413 + return prefix + name.charAt(0).toUpperCase() + name.slice(1);
  414 + }
  415 + return (name === 'deviceId') ? 'sourceId' : name;
  416 + };
  417 + if (r.ideal !== undefined) {
  418 + cc.optional = cc.optional || [];
  419 + let oc = {};
  420 + if (typeof r.ideal === 'number') {
  421 + oc[oldname_('min', key)] = r.ideal;
  422 + cc.optional.push(oc);
  423 + oc = {};
  424 + oc[oldname_('max', key)] = r.ideal;
  425 + cc.optional.push(oc);
  426 + } else {
  427 + oc[oldname_('', key)] = r.ideal;
  428 + cc.optional.push(oc);
  429 + }
  430 + }
  431 + if (r.exact !== undefined && typeof r.exact !== 'number') {
  432 + cc.mandatory = cc.mandatory || {};
  433 + cc.mandatory[oldname_('', key)] = r.exact;
  434 + } else {
  435 + ['min', 'max'].forEach(mix => {
  436 + if (r[mix] !== undefined) {
  437 + cc.mandatory = cc.mandatory || {};
  438 + cc.mandatory[oldname_(mix, key)] = r[mix];
  439 + }
  440 + });
  441 + }
  442 + });
  443 + if (c.advanced) {
  444 + cc.optional = (cc.optional || []).concat(c.advanced);
  445 + }
  446 + return cc;
  447 + };
  448 +
  449 + const shimConstraints_ = function(constraints, func) {
  450 + if (browserDetails.version >= 61) {
  451 + return func(constraints);
  452 + }
  453 + constraints = JSON.parse(JSON.stringify(constraints));
  454 + if (constraints && typeof constraints.audio === 'object') {
  455 + const remap = function(obj, a, b) {
  456 + if (a in obj && !(b in obj)) {
  457 + obj[b] = obj[a];
  458 + delete obj[a];
  459 + }
  460 + };
  461 + constraints = JSON.parse(JSON.stringify(constraints));
  462 + remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');
  463 + remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');
  464 + constraints.audio = constraintsToChrome_(constraints.audio);
  465 + }
  466 + if (constraints && typeof constraints.video === 'object') {
  467 + // Shim facingMode for mobile & surface pro.
  468 + let face = constraints.video.facingMode;
  469 + face = face && ((typeof face === 'object') ? face : {ideal: face});
  470 + const getSupportedFacingModeLies = browserDetails.version < 66;
  471 +
  472 + if ((face && (face.exact === 'user' || face.exact === 'environment' ||
  473 + face.ideal === 'user' || face.ideal === 'environment')) &&
  474 + !(navigator.mediaDevices.getSupportedConstraints &&
  475 + navigator.mediaDevices.getSupportedConstraints().facingMode &&
  476 + !getSupportedFacingModeLies)) {
  477 + delete constraints.video.facingMode;
  478 + let matches;
  479 + if (face.exact === 'environment' || face.ideal === 'environment') {
  480 + matches = ['back', 'rear'];
  481 + } else if (face.exact === 'user' || face.ideal === 'user') {
  482 + matches = ['front'];
  483 + }
  484 + if (matches) {
  485 + // Look for matches in label, or use last cam for back (typical).
  486 + return navigator.mediaDevices.enumerateDevices()
  487 + .then(devices => {
  488 + devices = devices.filter(d => d.kind === 'videoinput');
  489 + let dev = devices.find(d => matches.some(match =>
  490 + d.label.toLowerCase().includes(match)));
  491 + if (!dev && devices.length && matches.includes('back')) {
  492 + dev = devices[devices.length - 1]; // more likely the back cam
  493 + }
  494 + if (dev) {
  495 + constraints.video.deviceId = face.exact ? {exact: dev.deviceId} :
  496 + {ideal: dev.deviceId};
  497 + }
  498 + constraints.video = constraintsToChrome_(constraints.video);
  499 + logging('chrome: ' + JSON.stringify(constraints));
  500 + return func(constraints);
  501 + });
  502 + }
  503 + }
  504 + constraints.video = constraintsToChrome_(constraints.video);
  505 + }
  506 + logging('chrome: ' + JSON.stringify(constraints));
  507 + return func(constraints);
  508 + };
  509 +
  510 + const shimError_ = function(e) {
  511 + if (browserDetails.version >= 64) {
  512 + return e;
  513 + }
  514 + return {
  515 + name: {
  516 + PermissionDeniedError: 'NotAllowedError',
  517 + PermissionDismissedError: 'NotAllowedError',
  518 + InvalidStateError: 'NotAllowedError',
  519 + DevicesNotFoundError: 'NotFoundError',
  520 + ConstraintNotSatisfiedError: 'OverconstrainedError',
  521 + TrackStartError: 'NotReadableError',
  522 + MediaDeviceFailedDueToShutdown: 'NotAllowedError',
  523 + MediaDeviceKillSwitchOn: 'NotAllowedError',
  524 + TabCaptureError: 'AbortError',
  525 + ScreenCaptureError: 'AbortError',
  526 + DeviceCaptureError: 'AbortError'
  527 + }[e.name] || e.name,
  528 + message: e.message,
  529 + constraint: e.constraint || e.constraintName,
  530 + toString() {
  531 + return this.name + (this.message && ': ') + this.message;
  532 + }
  533 + };
  534 + };
  535 +
  536 + const getUserMedia_ = function(constraints, onSuccess, onError) {
  537 + shimConstraints_(constraints, c => {
  538 + navigator.webkitGetUserMedia(c, onSuccess, e => {
  539 + if (onError) {
  540 + onError(shimError_(e));
  541 + }
  542 + });
  543 + });
  544 + };
  545 + navigator.getUserMedia = getUserMedia_.bind(navigator);
  546 +
  547 + // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
  548 + // function which returns a Promise, it does not accept spec-style
  549 + // constraints.
  550 + if (navigator.mediaDevices.getUserMedia) {
  551 + const origGetUserMedia = navigator.mediaDevices.getUserMedia.
  552 + bind(navigator.mediaDevices);
  553 + navigator.mediaDevices.getUserMedia = function(cs) {
  554 + return shimConstraints_(cs, c => origGetUserMedia(c).then(stream => {
  555 + if (c.audio && !stream.getAudioTracks().length ||
  556 + c.video && !stream.getVideoTracks().length) {
  557 + stream.getTracks().forEach(track => {
  558 + track.stop();
  559 + });
  560 + throw new DOMException('', 'NotFoundError');
  561 + }
  562 + return stream;
  563 + }, e => Promise.reject(shimError_(e))));
  564 + };
  565 + }
  566 + }
  567 +
  568 + /*
  569 + * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
  570 + *
  571 + * Use of this source code is governed by a BSD-style license
  572 + * that can be found in the LICENSE file in the root of the source
  573 + * tree.
  574 + */
  575 + function shimGetDisplayMedia$2(window, getSourceId) {
  576 + if (window.navigator.mediaDevices &&
  577 + 'getDisplayMedia' in window.navigator.mediaDevices) {
  578 + return;
  579 + }
  580 + if (!(window.navigator.mediaDevices)) {
  581 + return;
  582 + }
  583 + // getSourceId is a function that returns a promise resolving with
  584 + // the sourceId of the screen/window/tab to be shared.
  585 + if (typeof getSourceId !== 'function') {
  586 + console.error('shimGetDisplayMedia: getSourceId argument is not ' +
  587 + 'a function');
  588 + return;
  589 + }
  590 + window.navigator.mediaDevices.getDisplayMedia =
  591 + function getDisplayMedia(constraints) {
  592 + return getSourceId(constraints)
  593 + .then(sourceId => {
  594 + const widthSpecified = constraints.video && constraints.video.width;
  595 + const heightSpecified = constraints.video &&
  596 + constraints.video.height;
  597 + const frameRateSpecified = constraints.video &&
  598 + constraints.video.frameRate;
  599 + constraints.video = {
  600 + mandatory: {
  601 + chromeMediaSource: 'desktop',
  602 + chromeMediaSourceId: sourceId,
  603 + maxFrameRate: frameRateSpecified || 3
  604 + }
  605 + };
  606 + if (widthSpecified) {
  607 + constraints.video.mandatory.maxWidth = widthSpecified;
  608 + }
  609 + if (heightSpecified) {
  610 + constraints.video.mandatory.maxHeight = heightSpecified;
  611 + }
  612 + return window.navigator.mediaDevices.getUserMedia(constraints);
  613 + });
  614 + };
  615 + }
  616 +
  617 + /*
  618 + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  619 + *
  620 + * Use of this source code is governed by a BSD-style license
  621 + * that can be found in the LICENSE file in the root of the source
  622 + * tree.
  623 + */
  624 +
  625 + function shimMediaStream(window) {
  626 + window.MediaStream = window.MediaStream || window.webkitMediaStream;
  627 + }
  628 +
  629 + function shimOnTrack$1(window) {
  630 + if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in
  631 + window.RTCPeerConnection.prototype)) {
  632 + Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
  633 + get() {
  634 + return this._ontrack;
  635 + },
  636 + set(f) {
  637 + if (this._ontrack) {
  638 + this.removeEventListener('track', this._ontrack);
  639 + }
  640 + this.addEventListener('track', this._ontrack = f);
  641 + },
  642 + enumerable: true,
  643 + configurable: true
  644 + });
  645 + const origSetRemoteDescription =
  646 + window.RTCPeerConnection.prototype.setRemoteDescription;
  647 + window.RTCPeerConnection.prototype.setRemoteDescription =
  648 + function setRemoteDescription() {
  649 + if (!this._ontrackpoly) {
  650 + this._ontrackpoly = (e) => {
  651 + // onaddstream does not fire when a track is added to an existing
  652 + // stream. But stream.onaddtrack is implemented so we use that.
  653 + e.stream.addEventListener('addtrack', te => {
  654 + let receiver;
  655 + if (window.RTCPeerConnection.prototype.getReceivers) {
  656 + receiver = this.getReceivers()
  657 + .find(r => r.track && r.track.id === te.track.id);
  658 + } else {
  659 + receiver = {track: te.track};
  660 + }
  661 +
  662 + const event = new Event('track');
  663 + event.track = te.track;
  664 + event.receiver = receiver;
  665 + event.transceiver = {receiver};
  666 + event.streams = [e.stream];
  667 + this.dispatchEvent(event);
  668 + });
  669 + e.stream.getTracks().forEach(track => {
  670 + let receiver;
  671 + if (window.RTCPeerConnection.prototype.getReceivers) {
  672 + receiver = this.getReceivers()
  673 + .find(r => r.track && r.track.id === track.id);
  674 + } else {
  675 + receiver = {track};
  676 + }
  677 + const event = new Event('track');
  678 + event.track = track;
  679 + event.receiver = receiver;
  680 + event.transceiver = {receiver};
  681 + event.streams = [e.stream];
  682 + this.dispatchEvent(event);
  683 + });
  684 + };
  685 + this.addEventListener('addstream', this._ontrackpoly);
  686 + }
  687 + return origSetRemoteDescription.apply(this, arguments);
  688 + };
  689 + } else {
  690 + // even if RTCRtpTransceiver is in window, it is only used and
  691 + // emitted in unified-plan. Unfortunately this means we need
  692 + // to unconditionally wrap the event.
  693 + wrapPeerConnectionEvent(window, 'track', e => {
  694 + if (!e.transceiver) {
  695 + Object.defineProperty(e, 'transceiver',
  696 + {value: {receiver: e.receiver}});
  697 + }
  698 + return e;
  699 + });
  700 + }
  701 + }
  702 +
  703 + function shimGetSendersWithDtmf(window) {
  704 + // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.
  705 + if (typeof window === 'object' && window.RTCPeerConnection &&
  706 + !('getSenders' in window.RTCPeerConnection.prototype) &&
  707 + 'createDTMFSender' in window.RTCPeerConnection.prototype) {
  708 + const shimSenderWithDtmf = function(pc, track) {
  709 + return {
  710 + track,
  711 + get dtmf() {
  712 + if (this._dtmf === undefined) {
  713 + if (track.kind === 'audio') {
  714 + this._dtmf = pc.createDTMFSender(track);
  715 + } else {
  716 + this._dtmf = null;
  717 + }
  718 + }
  719 + return this._dtmf;
  720 + },
  721 + _pc: pc
  722 + };
  723 + };
  724 +
  725 + // augment addTrack when getSenders is not available.
  726 + if (!window.RTCPeerConnection.prototype.getSenders) {
  727 + window.RTCPeerConnection.prototype.getSenders = function getSenders() {
  728 + this._senders = this._senders || [];
  729 + return this._senders.slice(); // return a copy of the internal state.
  730 + };
  731 + const origAddTrack = window.RTCPeerConnection.prototype.addTrack;
  732 + window.RTCPeerConnection.prototype.addTrack =
  733 + function addTrack(track, stream) {
  734 + let sender = origAddTrack.apply(this, arguments);
  735 + if (!sender) {
  736 + sender = shimSenderWithDtmf(this, track);
  737 + this._senders.push(sender);
  738 + }
  739 + return sender;
  740 + };
  741 +
  742 + const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
  743 + window.RTCPeerConnection.prototype.removeTrack =
  744 + function removeTrack(sender) {
  745 + origRemoveTrack.apply(this, arguments);
  746 + const idx = this._senders.indexOf(sender);
  747 + if (idx !== -1) {
  748 + this._senders.splice(idx, 1);
  749 + }
  750 + };
  751 + }
  752 + const origAddStream = window.RTCPeerConnection.prototype.addStream;
  753 + window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
  754 + this._senders = this._senders || [];
  755 + origAddStream.apply(this, [stream]);
  756 + stream.getTracks().forEach(track => {
  757 + this._senders.push(shimSenderWithDtmf(this, track));
  758 + });
  759 + };
  760 +
  761 + const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
  762 + window.RTCPeerConnection.prototype.removeStream =
  763 + function removeStream(stream) {
  764 + this._senders = this._senders || [];
  765 + origRemoveStream.apply(this, [stream]);
  766 +
  767 + stream.getTracks().forEach(track => {
  768 + const sender = this._senders.find(s => s.track === track);
  769 + if (sender) { // remove sender
  770 + this._senders.splice(this._senders.indexOf(sender), 1);
  771 + }
  772 + });
  773 + };
  774 + } else if (typeof window === 'object' && window.RTCPeerConnection &&
  775 + 'getSenders' in window.RTCPeerConnection.prototype &&
  776 + 'createDTMFSender' in window.RTCPeerConnection.prototype &&
  777 + window.RTCRtpSender &&
  778 + !('dtmf' in window.RTCRtpSender.prototype)) {
  779 + const origGetSenders = window.RTCPeerConnection.prototype.getSenders;
  780 + window.RTCPeerConnection.prototype.getSenders = function getSenders() {
  781 + const senders = origGetSenders.apply(this, []);
  782 + senders.forEach(sender => sender._pc = this);
  783 + return senders;
  784 + };
  785 +
  786 + Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
  787 + get() {
  788 + if (this._dtmf === undefined) {
  789 + if (this.track.kind === 'audio') {
  790 + this._dtmf = this._pc.createDTMFSender(this.track);
  791 + } else {
  792 + this._dtmf = null;
  793 + }
  794 + }
  795 + return this._dtmf;
  796 + }
  797 + });
  798 + }
  799 + }
  800 +
  801 + function shimGetStats(window) {
  802 + if (!window.RTCPeerConnection) {
  803 + return;
  804 + }
  805 +
  806 + const origGetStats = window.RTCPeerConnection.prototype.getStats;
  807 + window.RTCPeerConnection.prototype.getStats = function getStats() {
  808 + const [selector, onSucc, onErr] = arguments;
  809 +
  810 + // If selector is a function then we are in the old style stats so just
  811 + // pass back the original getStats format to avoid breaking old users.
  812 + if (arguments.length > 0 && typeof selector === 'function') {
  813 + return origGetStats.apply(this, arguments);
  814 + }
  815 +
  816 + // When spec-style getStats is supported, return those when called with
  817 + // either no arguments or the selector argument is null.
  818 + if (origGetStats.length === 0 && (arguments.length === 0 ||
  819 + typeof selector !== 'function')) {
  820 + return origGetStats.apply(this, []);
  821 + }
  822 +
  823 + const fixChromeStats_ = function(response) {
  824 + const standardReport = {};
  825 + const reports = response.result();
  826 + reports.forEach(report => {
  827 + const standardStats = {
  828 + id: report.id,
  829 + timestamp: report.timestamp,
  830 + type: {
  831 + localcandidate: 'local-candidate',
  832 + remotecandidate: 'remote-candidate'
  833 + }[report.type] || report.type
  834 + };
  835 + report.names().forEach(name => {
  836 + standardStats[name] = report.stat(name);
  837 + });
  838 + standardReport[standardStats.id] = standardStats;
  839 + });
  840 +
  841 + return standardReport;
  842 + };
  843 +
  844 + // shim getStats with maplike support
  845 + const makeMapStats = function(stats) {
  846 + return new Map(Object.keys(stats).map(key => [key, stats[key]]));
  847 + };
  848 +
  849 + if (arguments.length >= 2) {
  850 + const successCallbackWrapper_ = function(response) {
  851 + onSucc(makeMapStats(fixChromeStats_(response)));
  852 + };
  853 +
  854 + return origGetStats.apply(this, [successCallbackWrapper_,
  855 + selector]);
  856 + }
  857 +
  858 + // promise-support
  859 + return new Promise((resolve, reject) => {
  860 + origGetStats.apply(this, [
  861 + function(response) {
  862 + resolve(makeMapStats(fixChromeStats_(response)));
  863 + }, reject]);
  864 + }).then(onSucc, onErr);
  865 + };
  866 + }
  867 +
  868 + function shimSenderReceiverGetStats(window) {
  869 + if (!(typeof window === 'object' && window.RTCPeerConnection &&
  870 + window.RTCRtpSender && window.RTCRtpReceiver)) {
  871 + return;
  872 + }
  873 +
  874 + // shim sender stats.
  875 + if (!('getStats' in window.RTCRtpSender.prototype)) {
  876 + const origGetSenders = window.RTCPeerConnection.prototype.getSenders;
  877 + if (origGetSenders) {
  878 + window.RTCPeerConnection.prototype.getSenders = function getSenders() {
  879 + const senders = origGetSenders.apply(this, []);
  880 + senders.forEach(sender => sender._pc = this);
  881 + return senders;
  882 + };
  883 + }
  884 +
  885 + const origAddTrack = window.RTCPeerConnection.prototype.addTrack;
  886 + if (origAddTrack) {
  887 + window.RTCPeerConnection.prototype.addTrack = function addTrack() {
  888 + const sender = origAddTrack.apply(this, arguments);
  889 + sender._pc = this;
  890 + return sender;
  891 + };
  892 + }
  893 + window.RTCRtpSender.prototype.getStats = function getStats() {
  894 + const sender = this;
  895 + return this._pc.getStats().then(result =>
  896 + /* Note: this will include stats of all senders that
  897 + * send a track with the same id as sender.track as
  898 + * it is not possible to identify the RTCRtpSender.
  899 + */
  900 + filterStats(result, sender.track, true));
  901 + };
  902 + }
  903 +
  904 + // shim receiver stats.
  905 + if (!('getStats' in window.RTCRtpReceiver.prototype)) {
  906 + const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
  907 + if (origGetReceivers) {
  908 + window.RTCPeerConnection.prototype.getReceivers =
  909 + function getReceivers() {
  910 + const receivers = origGetReceivers.apply(this, []);
  911 + receivers.forEach(receiver => receiver._pc = this);
  912 + return receivers;
  913 + };
  914 + }
  915 + wrapPeerConnectionEvent(window, 'track', e => {
  916 + e.receiver._pc = e.srcElement;
  917 + return e;
  918 + });
  919 + window.RTCRtpReceiver.prototype.getStats = function getStats() {
  920 + const receiver = this;
  921 + return this._pc.getStats().then(result =>
  922 + filterStats(result, receiver.track, false));
  923 + };
  924 + }
  925 +
  926 + if (!('getStats' in window.RTCRtpSender.prototype &&
  927 + 'getStats' in window.RTCRtpReceiver.prototype)) {
  928 + return;
  929 + }
  930 +
  931 + // shim RTCPeerConnection.getStats(track).
  932 + const origGetStats = window.RTCPeerConnection.prototype.getStats;
  933 + window.RTCPeerConnection.prototype.getStats = function getStats() {
  934 + if (arguments.length > 0 &&
  935 + arguments[0] instanceof window.MediaStreamTrack) {
  936 + const track = arguments[0];
  937 + let sender;
  938 + let receiver;
  939 + let err;
  940 + this.getSenders().forEach(s => {
  941 + if (s.track === track) {
  942 + if (sender) {
  943 + err = true;
  944 + } else {
  945 + sender = s;
  946 + }
  947 + }
  948 + });
  949 + this.getReceivers().forEach(r => {
  950 + if (r.track === track) {
  951 + if (receiver) {
  952 + err = true;
  953 + } else {
  954 + receiver = r;
  955 + }
  956 + }
  957 + return r.track === track;
  958 + });
  959 + if (err || (sender && receiver)) {
  960 + return Promise.reject(new DOMException(
  961 + 'There are more than one sender or receiver for the track.',
  962 + 'InvalidAccessError'));
  963 + } else if (sender) {
  964 + return sender.getStats();
  965 + } else if (receiver) {
  966 + return receiver.getStats();
  967 + }
  968 + return Promise.reject(new DOMException(
  969 + 'There is no sender or receiver for the track.',
  970 + 'InvalidAccessError'));
  971 + }
  972 + return origGetStats.apply(this, arguments);
  973 + };
  974 + }
  975 +
  976 + function shimAddTrackRemoveTrackWithNative(window) {
  977 + // shim addTrack/removeTrack with native variants in order to make
  978 + // the interactions with legacy getLocalStreams behave as in other browsers.
  979 + // Keeps a mapping stream.id => [stream, rtpsenders...]
  980 + window.RTCPeerConnection.prototype.getLocalStreams =
  981 + function getLocalStreams() {
  982 + this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  983 + return Object.keys(this._shimmedLocalStreams)
  984 + .map(streamId => this._shimmedLocalStreams[streamId][0]);
  985 + };
  986 +
  987 + const origAddTrack = window.RTCPeerConnection.prototype.addTrack;
  988 + window.RTCPeerConnection.prototype.addTrack =
  989 + function addTrack(track, stream) {
  990 + if (!stream) {
  991 + return origAddTrack.apply(this, arguments);
  992 + }
  993 + this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  994 +
  995 + const sender = origAddTrack.apply(this, arguments);
  996 + if (!this._shimmedLocalStreams[stream.id]) {
  997 + this._shimmedLocalStreams[stream.id] = [stream, sender];
  998 + } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) {
  999 + this._shimmedLocalStreams[stream.id].push(sender);
  1000 + }
  1001 + return sender;
  1002 + };
  1003 +
  1004 + const origAddStream = window.RTCPeerConnection.prototype.addStream;
  1005 + window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
  1006 + this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  1007 +
  1008 + stream.getTracks().forEach(track => {
  1009 + const alreadyExists = this.getSenders().find(s => s.track === track);
  1010 + if (alreadyExists) {
  1011 + throw new DOMException('Track already exists.',
  1012 + 'InvalidAccessError');
  1013 + }
  1014 + });
  1015 + const existingSenders = this.getSenders();
  1016 + origAddStream.apply(this, arguments);
  1017 + const newSenders = this.getSenders()
  1018 + .filter(newSender => existingSenders.indexOf(newSender) === -1);
  1019 + this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders);
  1020 + };
  1021 +
  1022 + const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
  1023 + window.RTCPeerConnection.prototype.removeStream =
  1024 + function removeStream(stream) {
  1025 + this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  1026 + delete this._shimmedLocalStreams[stream.id];
  1027 + return origRemoveStream.apply(this, arguments);
  1028 + };
  1029 +
  1030 + const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
  1031 + window.RTCPeerConnection.prototype.removeTrack =
  1032 + function removeTrack(sender) {
  1033 + this._shimmedLocalStreams = this._shimmedLocalStreams || {};
  1034 + if (sender) {
  1035 + Object.keys(this._shimmedLocalStreams).forEach(streamId => {
  1036 + const idx = this._shimmedLocalStreams[streamId].indexOf(sender);
  1037 + if (idx !== -1) {
  1038 + this._shimmedLocalStreams[streamId].splice(idx, 1);
  1039 + }
  1040 + if (this._shimmedLocalStreams[streamId].length === 1) {
  1041 + delete this._shimmedLocalStreams[streamId];
  1042 + }
  1043 + });
  1044 + }
  1045 + return origRemoveTrack.apply(this, arguments);
  1046 + };
  1047 + }
  1048 +
  1049 + function shimAddTrackRemoveTrack(window, browserDetails) {
  1050 + if (!window.RTCPeerConnection) {
  1051 + return;
  1052 + }
  1053 + // shim addTrack and removeTrack.
  1054 + if (window.RTCPeerConnection.prototype.addTrack &&
  1055 + browserDetails.version >= 65) {
  1056 + return shimAddTrackRemoveTrackWithNative(window);
  1057 + }
  1058 +
  1059 + // also shim pc.getLocalStreams when addTrack is shimmed
  1060 + // to return the original streams.
  1061 + const origGetLocalStreams = window.RTCPeerConnection.prototype
  1062 + .getLocalStreams;
  1063 + window.RTCPeerConnection.prototype.getLocalStreams =
  1064 + function getLocalStreams() {
  1065 + const nativeStreams = origGetLocalStreams.apply(this);
  1066 + this._reverseStreams = this._reverseStreams || {};
  1067 + return nativeStreams.map(stream => this._reverseStreams[stream.id]);
  1068 + };
  1069 +
  1070 + const origAddStream = window.RTCPeerConnection.prototype.addStream;
  1071 + window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
  1072 + this._streams = this._streams || {};
  1073 + this._reverseStreams = this._reverseStreams || {};
  1074 +
  1075 + stream.getTracks().forEach(track => {
  1076 + const alreadyExists = this.getSenders().find(s => s.track === track);
  1077 + if (alreadyExists) {
  1078 + throw new DOMException('Track already exists.',
  1079 + 'InvalidAccessError');
  1080 + }
  1081 + });
  1082 + // Add identity mapping for consistency with addTrack.
  1083 + // Unless this is being used with a stream from addTrack.
  1084 + if (!this._reverseStreams[stream.id]) {
  1085 + const newStream = new window.MediaStream(stream.getTracks());
  1086 + this._streams[stream.id] = newStream;
  1087 + this._reverseStreams[newStream.id] = stream;
  1088 + stream = newStream;
  1089 + }
  1090 + origAddStream.apply(this, [stream]);
  1091 + };
  1092 +
  1093 + const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
  1094 + window.RTCPeerConnection.prototype.removeStream =
  1095 + function removeStream(stream) {
  1096 + this._streams = this._streams || {};
  1097 + this._reverseStreams = this._reverseStreams || {};
  1098 +
  1099 + origRemoveStream.apply(this, [(this._streams[stream.id] || stream)]);
  1100 + delete this._reverseStreams[(this._streams[stream.id] ?
  1101 + this._streams[stream.id].id : stream.id)];
  1102 + delete this._streams[stream.id];
  1103 + };
  1104 +
  1105 + window.RTCPeerConnection.prototype.addTrack =
  1106 + function addTrack(track, stream) {
  1107 + if (this.signalingState === 'closed') {
  1108 + throw new DOMException(
  1109 + 'The RTCPeerConnection\'s signalingState is \'closed\'.',
  1110 + 'InvalidStateError');
  1111 + }
  1112 + const streams = [].slice.call(arguments, 1);
  1113 + if (streams.length !== 1 ||
  1114 + !streams[0].getTracks().find(t => t === track)) {
  1115 + // this is not fully correct but all we can manage without
  1116 + // [[associated MediaStreams]] internal slot.
  1117 + throw new DOMException(
  1118 + 'The adapter.js addTrack polyfill only supports a single ' +
  1119 + ' stream which is associated with the specified track.',
  1120 + 'NotSupportedError');
  1121 + }
  1122 +
  1123 + const alreadyExists = this.getSenders().find(s => s.track === track);
  1124 + if (alreadyExists) {
  1125 + throw new DOMException('Track already exists.',
  1126 + 'InvalidAccessError');
  1127 + }
  1128 +
  1129 + this._streams = this._streams || {};
  1130 + this._reverseStreams = this._reverseStreams || {};
  1131 + const oldStream = this._streams[stream.id];
  1132 + if (oldStream) {
  1133 + // this is using odd Chrome behaviour, use with caution:
  1134 + // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815
  1135 + // Note: we rely on the high-level addTrack/dtmf shim to
  1136 + // create the sender with a dtmf sender.
  1137 + oldStream.addTrack(track);
  1138 +
  1139 + // Trigger ONN async.
  1140 + Promise.resolve().then(() => {
  1141 + this.dispatchEvent(new Event('negotiationneeded'));
  1142 + });
  1143 + } else {
  1144 + const newStream = new window.MediaStream([track]);
  1145 + this._streams[stream.id] = newStream;
  1146 + this._reverseStreams[newStream.id] = stream;
  1147 + this.addStream(newStream);
  1148 + }
  1149 + return this.getSenders().find(s => s.track === track);
  1150 + };
  1151 +
  1152 + // replace the internal stream id with the external one and
  1153 + // vice versa.
  1154 + function replaceInternalStreamId(pc, description) {
  1155 + let sdp = description.sdp;
  1156 + Object.keys(pc._reverseStreams || []).forEach(internalId => {
  1157 + const externalStream = pc._reverseStreams[internalId];
  1158 + const internalStream = pc._streams[externalStream.id];
  1159 + sdp = sdp.replace(new RegExp(internalStream.id, 'g'),
  1160 + externalStream.id);
  1161 + });
  1162 + return new RTCSessionDescription({
  1163 + type: description.type,
  1164 + sdp
  1165 + });
  1166 + }
  1167 + function replaceExternalStreamId(pc, description) {
  1168 + let sdp = description.sdp;
  1169 + Object.keys(pc._reverseStreams || []).forEach(internalId => {
  1170 + const externalStream = pc._reverseStreams[internalId];
  1171 + const internalStream = pc._streams[externalStream.id];
  1172 + sdp = sdp.replace(new RegExp(externalStream.id, 'g'),
  1173 + internalStream.id);
  1174 + });
  1175 + return new RTCSessionDescription({
  1176 + type: description.type,
  1177 + sdp
  1178 + });
  1179 + }
  1180 + ['createOffer', 'createAnswer'].forEach(function(method) {
  1181 + const nativeMethod = window.RTCPeerConnection.prototype[method];
  1182 + const methodObj = {[method]() {
  1183 + const args = arguments;
  1184 + const isLegacyCall = arguments.length &&
  1185 + typeof arguments[0] === 'function';
  1186 + if (isLegacyCall) {
  1187 + return nativeMethod.apply(this, [
  1188 + (description) => {
  1189 + const desc = replaceInternalStreamId(this, description);
  1190 + args[0].apply(null, [desc]);
  1191 + },
  1192 + (err) => {
  1193 + if (args[1]) {
  1194 + args[1].apply(null, err);
  1195 + }
  1196 + }, arguments[2]
  1197 + ]);
  1198 + }
  1199 + return nativeMethod.apply(this, arguments)
  1200 + .then(description => replaceInternalStreamId(this, description));
  1201 + }};
  1202 + window.RTCPeerConnection.prototype[method] = methodObj[method];
  1203 + });
  1204 +
  1205 + const origSetLocalDescription =
  1206 + window.RTCPeerConnection.prototype.setLocalDescription;
  1207 + window.RTCPeerConnection.prototype.setLocalDescription =
  1208 + function setLocalDescription() {
  1209 + if (!arguments.length || !arguments[0].type) {
  1210 + return origSetLocalDescription.apply(this, arguments);
  1211 + }
  1212 + arguments[0] = replaceExternalStreamId(this, arguments[0]);
  1213 + return origSetLocalDescription.apply(this, arguments);
  1214 + };
  1215 +
  1216 + // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier
  1217 +
  1218 + const origLocalDescription = Object.getOwnPropertyDescriptor(
  1219 + window.RTCPeerConnection.prototype, 'localDescription');
  1220 + Object.defineProperty(window.RTCPeerConnection.prototype,
  1221 + 'localDescription', {
  1222 + get() {
  1223 + const description = origLocalDescription.get.apply(this);
  1224 + if (description.type === '') {
  1225 + return description;
  1226 + }
  1227 + return replaceInternalStreamId(this, description);
  1228 + }
  1229 + });
  1230 +
  1231 + window.RTCPeerConnection.prototype.removeTrack =
  1232 + function removeTrack(sender) {
  1233 + if (this.signalingState === 'closed') {
  1234 + throw new DOMException(
  1235 + 'The RTCPeerConnection\'s signalingState is \'closed\'.',
  1236 + 'InvalidStateError');
  1237 + }
  1238 + // We can not yet check for sender instanceof RTCRtpSender
  1239 + // since we shim RTPSender. So we check if sender._pc is set.
  1240 + if (!sender._pc) {
  1241 + throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' +
  1242 + 'does not implement interface RTCRtpSender.', 'TypeError');
  1243 + }
  1244 + const isLocal = sender._pc === this;
  1245 + if (!isLocal) {
  1246 + throw new DOMException('Sender was not created by this connection.',
  1247 + 'InvalidAccessError');
  1248 + }
  1249 +
  1250 + // Search for the native stream the senders track belongs to.
  1251 + this._streams = this._streams || {};
  1252 + let stream;
  1253 + Object.keys(this._streams).forEach(streamid => {
  1254 + const hasTrack = this._streams[streamid].getTracks()
  1255 + .find(track => sender.track === track);
  1256 + if (hasTrack) {
  1257 + stream = this._streams[streamid];
  1258 + }
  1259 + });
  1260 +
  1261 + if (stream) {
  1262 + if (stream.getTracks().length === 1) {
  1263 + // if this is the last track of the stream, remove the stream. This
  1264 + // takes care of any shimmed _senders.
  1265 + this.removeStream(this._reverseStreams[stream.id]);
  1266 + } else {
  1267 + // relying on the same odd chrome behaviour as above.
  1268 + stream.removeTrack(sender.track);
  1269 + }
  1270 + this.dispatchEvent(new Event('negotiationneeded'));
  1271 + }
  1272 + };
  1273 + }
  1274 +
  1275 + function shimPeerConnection$2(window, browserDetails) {
  1276 + if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) {
  1277 + // very basic support for old versions.
  1278 + window.RTCPeerConnection = window.webkitRTCPeerConnection;
  1279 + }
  1280 + if (!window.RTCPeerConnection) {
  1281 + return;
  1282 + }
  1283 +
  1284 + // shim implicit creation of RTCSessionDescription/RTCIceCandidate
  1285 + if (browserDetails.version < 53) {
  1286 + ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
  1287 + .forEach(function(method) {
  1288 + const nativeMethod = window.RTCPeerConnection.prototype[method];
  1289 + const methodObj = {[method]() {
  1290 + arguments[0] = new ((method === 'addIceCandidate') ?
  1291 + window.RTCIceCandidate :
  1292 + window.RTCSessionDescription)(arguments[0]);
  1293 + return nativeMethod.apply(this, arguments);
  1294 + }};
  1295 + window.RTCPeerConnection.prototype[method] = methodObj[method];
  1296 + });
  1297 + }
  1298 + }
  1299 +
  1300 + // Attempt to fix ONN in plan-b mode.
  1301 + function fixNegotiationNeeded(window, browserDetails) {
  1302 + wrapPeerConnectionEvent(window, 'negotiationneeded', e => {
  1303 + const pc = e.target;
  1304 + if (browserDetails.version < 72 || (pc.getConfiguration &&
  1305 + pc.getConfiguration().sdpSemantics === 'plan-b')) {
  1306 + if (pc.signalingState !== 'stable') {
  1307 + return;
  1308 + }
  1309 + }
  1310 + return e;
  1311 + });
  1312 + }
  1313 +
  1314 + var chromeShim = /*#__PURE__*/Object.freeze({
  1315 + __proto__: null,
  1316 + shimMediaStream: shimMediaStream,
  1317 + shimOnTrack: shimOnTrack$1,
  1318 + shimGetSendersWithDtmf: shimGetSendersWithDtmf,
  1319 + shimGetStats: shimGetStats,
  1320 + shimSenderReceiverGetStats: shimSenderReceiverGetStats,
  1321 + shimAddTrackRemoveTrackWithNative: shimAddTrackRemoveTrackWithNative,
  1322 + shimAddTrackRemoveTrack: shimAddTrackRemoveTrack,
  1323 + shimPeerConnection: shimPeerConnection$2,
  1324 + fixNegotiationNeeded: fixNegotiationNeeded,
  1325 + shimGetUserMedia: shimGetUserMedia$3,
  1326 + shimGetDisplayMedia: shimGetDisplayMedia$2
  1327 + });
  1328 +
  1329 + /*
  1330 + * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
  1331 + *
  1332 + * Use of this source code is governed by a BSD-style license
  1333 + * that can be found in the LICENSE file in the root of the source
  1334 + * tree.
  1335 + */
  1336 + // Edge does not like
  1337 + // 1) stun: filtered after 14393 unless ?transport=udp is present
  1338 + // 2) turn: that does not have all of turn:host:port?transport=udp
  1339 + // 3) turn: with ipv6 addresses
  1340 + // 4) turn: occurring muliple times
  1341 + function filterIceServers$1(iceServers, edgeVersion) {
  1342 + let hasTurn = false;
  1343 + iceServers = JSON.parse(JSON.stringify(iceServers));
  1344 + return iceServers.filter(server => {
  1345 + if (server && (server.urls || server.url)) {
  1346 + let urls = server.urls || server.url;
  1347 + if (server.url && !server.urls) {
  1348 + deprecated('RTCIceServer.url', 'RTCIceServer.urls');
  1349 + }
  1350 + const isString = typeof urls === 'string';
  1351 + if (isString) {
  1352 + urls = [urls];
  1353 + }
  1354 + urls = urls.filter(url => {
  1355 + // filter STUN unconditionally.
  1356 + if (url.indexOf('stun:') === 0) {
  1357 + return false;
  1358 + }
  1359 +
  1360 + const validTurn = url.startsWith('turn') &&
  1361 + !url.startsWith('turn:[') &&
  1362 + url.includes('transport=udp');
  1363 + if (validTurn && !hasTurn) {
  1364 + hasTurn = true;
  1365 + return true;
  1366 + }
  1367 + return validTurn && !hasTurn;
  1368 + });
  1369 +
  1370 + delete server.url;
  1371 + server.urls = isString ? urls[0] : urls;
  1372 + return !!urls.length;
  1373 + }
  1374 + });
  1375 + }
  1376 +
  1377 + function createCommonjsModule(fn) {
  1378 + var module = { exports: {} };
  1379 + return fn(module, module.exports), module.exports;
  1380 + }
  1381 +
  1382 + /* eslint-env node */
  1383 +
  1384 + var sdp = createCommonjsModule(function (module) {
  1385 +
  1386 + // SDP helpers.
  1387 + var SDPUtils = {};
  1388 +
  1389 + // Generate an alphanumeric identifier for cname or mids.
  1390 + // TODO: use UUIDs instead? https://gist.github.com/jed/982883
  1391 + SDPUtils.generateIdentifier = function() {
  1392 + return Math.random().toString(36).substr(2, 10);
  1393 + };
  1394 +
  1395 + // The RTCP CNAME used by all peerconnections from the same JS.
  1396 + SDPUtils.localCName = SDPUtils.generateIdentifier();
  1397 +
  1398 + // Splits SDP into lines, dealing with both CRLF and LF.
  1399 + SDPUtils.splitLines = function(blob) {
  1400 + return blob.trim().split('\n').map(function(line) {
  1401 + return line.trim();
  1402 + });
  1403 + };
  1404 + // Splits SDP into sessionpart and mediasections. Ensures CRLF.
  1405 + SDPUtils.splitSections = function(blob) {
  1406 + var parts = blob.split('\nm=');
  1407 + return parts.map(function(part, index) {
  1408 + return (index > 0 ? 'm=' + part : part).trim() + '\r\n';
  1409 + });
  1410 + };
  1411 +
  1412 + // returns the session description.
  1413 + SDPUtils.getDescription = function(blob) {
  1414 + var sections = SDPUtils.splitSections(blob);
  1415 + return sections && sections[0];
  1416 + };
  1417 +
  1418 + // returns the individual media sections.
  1419 + SDPUtils.getMediaSections = function(blob) {
  1420 + var sections = SDPUtils.splitSections(blob);
  1421 + sections.shift();
  1422 + return sections;
  1423 + };
  1424 +
  1425 + // Returns lines that start with a certain prefix.
  1426 + SDPUtils.matchPrefix = function(blob, prefix) {
  1427 + return SDPUtils.splitLines(blob).filter(function(line) {
  1428 + return line.indexOf(prefix) === 0;
  1429 + });
  1430 + };
  1431 +
  1432 + // Parses an ICE candidate line. Sample input:
  1433 + // candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8
  1434 + // rport 55996"
  1435 + SDPUtils.parseCandidate = function(line) {
  1436 + var parts;
  1437 + // Parse both variants.
  1438 + if (line.indexOf('a=candidate:') === 0) {
  1439 + parts = line.substring(12).split(' ');
  1440 + } else {
  1441 + parts = line.substring(10).split(' ');
  1442 + }
  1443 +
  1444 + var candidate = {
  1445 + foundation: parts[0],
  1446 + component: parseInt(parts[1], 10),
  1447 + protocol: parts[2].toLowerCase(),
  1448 + priority: parseInt(parts[3], 10),
  1449 + ip: parts[4],
  1450 + address: parts[4], // address is an alias for ip.
  1451 + port: parseInt(parts[5], 10),
  1452 + // skip parts[6] == 'typ'
  1453 + type: parts[7]
  1454 + };
  1455 +
  1456 + for (var i = 8; i < parts.length; i += 2) {
  1457 + switch (parts[i]) {
  1458 + case 'raddr':
  1459 + candidate.relatedAddress = parts[i + 1];
  1460 + break;
  1461 + case 'rport':
  1462 + candidate.relatedPort = parseInt(parts[i + 1], 10);
  1463 + break;
  1464 + case 'tcptype':
  1465 + candidate.tcpType = parts[i + 1];
  1466 + break;
  1467 + case 'ufrag':
  1468 + candidate.ufrag = parts[i + 1]; // for backward compability.
  1469 + candidate.usernameFragment = parts[i + 1];
  1470 + break;
  1471 + default: // extension handling, in particular ufrag
  1472 + candidate[parts[i]] = parts[i + 1];
  1473 + break;
  1474 + }
  1475 + }
  1476 + return candidate;
  1477 + };
  1478 +
  1479 + // Translates a candidate object into SDP candidate attribute.
  1480 + SDPUtils.writeCandidate = function(candidate) {
  1481 + var sdp = [];
  1482 + sdp.push(candidate.foundation);
  1483 + sdp.push(candidate.component);
  1484 + sdp.push(candidate.protocol.toUpperCase());
  1485 + sdp.push(candidate.priority);
  1486 + sdp.push(candidate.address || candidate.ip);
  1487 + sdp.push(candidate.port);
  1488 +
  1489 + var type = candidate.type;
  1490 + sdp.push('typ');
  1491 + sdp.push(type);
  1492 + if (type !== 'host' && candidate.relatedAddress &&
  1493 + candidate.relatedPort) {
  1494 + sdp.push('raddr');
  1495 + sdp.push(candidate.relatedAddress);
  1496 + sdp.push('rport');
  1497 + sdp.push(candidate.relatedPort);
  1498 + }
  1499 + if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
  1500 + sdp.push('tcptype');
  1501 + sdp.push(candidate.tcpType);
  1502 + }
  1503 + if (candidate.usernameFragment || candidate.ufrag) {
  1504 + sdp.push('ufrag');
  1505 + sdp.push(candidate.usernameFragment || candidate.ufrag);
  1506 + }
  1507 + return 'candidate:' + sdp.join(' ');
  1508 + };
  1509 +
  1510 + // Parses an ice-options line, returns an array of option tags.
  1511 + // a=ice-options:foo bar
  1512 + SDPUtils.parseIceOptions = function(line) {
  1513 + return line.substr(14).split(' ');
  1514 + };
  1515 +
  1516 + // Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:
  1517 + // a=rtpmap:111 opus/48000/2
  1518 + SDPUtils.parseRtpMap = function(line) {
  1519 + var parts = line.substr(9).split(' ');
  1520 + var parsed = {
  1521 + payloadType: parseInt(parts.shift(), 10) // was: id
  1522 + };
  1523 +
  1524 + parts = parts[0].split('/');
  1525 +
  1526 + parsed.name = parts[0];
  1527 + parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
  1528 + parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;
  1529 + // legacy alias, got renamed back to channels in ORTC.
  1530 + parsed.numChannels = parsed.channels;
  1531 + return parsed;
  1532 + };
  1533 +
  1534 + // Generate an a=rtpmap line from RTCRtpCodecCapability or
  1535 + // RTCRtpCodecParameters.
  1536 + SDPUtils.writeRtpMap = function(codec) {
  1537 + var pt = codec.payloadType;
  1538 + if (codec.preferredPayloadType !== undefined) {
  1539 + pt = codec.preferredPayloadType;
  1540 + }
  1541 + var channels = codec.channels || codec.numChannels || 1;
  1542 + return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +
  1543 + (channels !== 1 ? '/' + channels : '') + '\r\n';
  1544 + };
  1545 +
  1546 + // Parses an a=extmap line (headerextension from RFC 5285). Sample input:
  1547 + // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
  1548 + // a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset
  1549 + SDPUtils.parseExtmap = function(line) {
  1550 + var parts = line.substr(9).split(' ');
  1551 + return {
  1552 + id: parseInt(parts[0], 10),
  1553 + direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',
  1554 + uri: parts[1]
  1555 + };
  1556 + };
  1557 +
  1558 + // Generates a=extmap line from RTCRtpHeaderExtensionParameters or
  1559 + // RTCRtpHeaderExtension.
  1560 + SDPUtils.writeExtmap = function(headerExtension) {
  1561 + return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +
  1562 + (headerExtension.direction && headerExtension.direction !== 'sendrecv'
  1563 + ? '/' + headerExtension.direction
  1564 + : '') +
  1565 + ' ' + headerExtension.uri + '\r\n';
  1566 + };
  1567 +
  1568 + // Parses an ftmp line, returns dictionary. Sample input:
  1569 + // a=fmtp:96 vbr=on;cng=on
  1570 + // Also deals with vbr=on; cng=on
  1571 + SDPUtils.parseFmtp = function(line) {
  1572 + var parsed = {};
  1573 + var kv;
  1574 + var parts = line.substr(line.indexOf(' ') + 1).split(';');
  1575 + for (var j = 0; j < parts.length; j++) {
  1576 + kv = parts[j].trim().split('=');
  1577 + parsed[kv[0].trim()] = kv[1];
  1578 + }
  1579 + return parsed;
  1580 + };
  1581 +
  1582 + // Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
  1583 + SDPUtils.writeFmtp = function(codec) {
  1584 + var line = '';
  1585 + var pt = codec.payloadType;
  1586 + if (codec.preferredPayloadType !== undefined) {
  1587 + pt = codec.preferredPayloadType;
  1588 + }
  1589 + if (codec.parameters && Object.keys(codec.parameters).length) {
  1590 + var params = [];
  1591 + Object.keys(codec.parameters).forEach(function(param) {
  1592 + if (codec.parameters[param]) {
  1593 + params.push(param + '=' + codec.parameters[param]);
  1594 + } else {
  1595 + params.push(param);
  1596 + }
  1597 + });
  1598 + line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
  1599 + }
  1600 + return line;
  1601 + };
  1602 +
  1603 + // Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
  1604 + // a=rtcp-fb:98 nack rpsi
  1605 + SDPUtils.parseRtcpFb = function(line) {
  1606 + var parts = line.substr(line.indexOf(' ') + 1).split(' ');
  1607 + return {
  1608 + type: parts.shift(),
  1609 + parameter: parts.join(' ')
  1610 + };
  1611 + };
  1612 + // Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
  1613 + SDPUtils.writeRtcpFb = function(codec) {
  1614 + var lines = '';
  1615 + var pt = codec.payloadType;
  1616 + if (codec.preferredPayloadType !== undefined) {
  1617 + pt = codec.preferredPayloadType;
  1618 + }
  1619 + if (codec.rtcpFeedback && codec.rtcpFeedback.length) {
  1620 + // FIXME: special handling for trr-int?
  1621 + codec.rtcpFeedback.forEach(function(fb) {
  1622 + lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +
  1623 + (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +
  1624 + '\r\n';
  1625 + });
  1626 + }
  1627 + return lines;
  1628 + };
  1629 +
  1630 + // Parses an RFC 5576 ssrc media attribute. Sample input:
  1631 + // a=ssrc:3735928559 cname:something
  1632 + SDPUtils.parseSsrcMedia = function(line) {
  1633 + var sp = line.indexOf(' ');
  1634 + var parts = {
  1635 + ssrc: parseInt(line.substr(7, sp - 7), 10)
  1636 + };
  1637 + var colon = line.indexOf(':', sp);
  1638 + if (colon > -1) {
  1639 + parts.attribute = line.substr(sp + 1, colon - sp - 1);
  1640 + parts.value = line.substr(colon + 1);
  1641 + } else {
  1642 + parts.attribute = line.substr(sp + 1);
  1643 + }
  1644 + return parts;
  1645 + };
  1646 +
  1647 + SDPUtils.parseSsrcGroup = function(line) {
  1648 + var parts = line.substr(13).split(' ');
  1649 + return {
  1650 + semantics: parts.shift(),
  1651 + ssrcs: parts.map(function(ssrc) {
  1652 + return parseInt(ssrc, 10);
  1653 + })
  1654 + };
  1655 + };
  1656 +
  1657 + // Extracts the MID (RFC 5888) from a media section.
  1658 + // returns the MID or undefined if no mid line was found.
  1659 + SDPUtils.getMid = function(mediaSection) {
  1660 + var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];
  1661 + if (mid) {
  1662 + return mid.substr(6);
  1663 + }
  1664 + };
  1665 +
  1666 + SDPUtils.parseFingerprint = function(line) {
  1667 + var parts = line.substr(14).split(' ');
  1668 + return {
  1669 + algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.
  1670 + value: parts[1]
  1671 + };
  1672 + };
  1673 +
  1674 + // Extracts DTLS parameters from SDP media section or sessionpart.
  1675 + // FIXME: for consistency with other functions this should only
  1676 + // get the fingerprint line as input. See also getIceParameters.
  1677 + SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {
  1678 + var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,
  1679 + 'a=fingerprint:');
  1680 + // Note: a=setup line is ignored since we use the 'auto' role.
  1681 + // Note2: 'algorithm' is not case sensitive except in Edge.
  1682 + return {
  1683 + role: 'auto',
  1684 + fingerprints: lines.map(SDPUtils.parseFingerprint)
  1685 + };
  1686 + };
  1687 +
  1688 + // Serializes DTLS parameters to SDP.
  1689 + SDPUtils.writeDtlsParameters = function(params, setupType) {
  1690 + var sdp = 'a=setup:' + setupType + '\r\n';
  1691 + params.fingerprints.forEach(function(fp) {
  1692 + sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
  1693 + });
  1694 + return sdp;
  1695 + };
  1696 +
  1697 + // Parses a=crypto lines into
  1698 + // https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members
  1699 + SDPUtils.parseCryptoLine = function(line) {
  1700 + var parts = line.substr(9).split(' ');
  1701 + return {
  1702 + tag: parseInt(parts[0], 10),
  1703 + cryptoSuite: parts[1],
  1704 + keyParams: parts[2],
  1705 + sessionParams: parts.slice(3),
  1706 + };
  1707 + };
  1708 +
  1709 + SDPUtils.writeCryptoLine = function(parameters) {
  1710 + return 'a=crypto:' + parameters.tag + ' ' +
  1711 + parameters.cryptoSuite + ' ' +
  1712 + (typeof parameters.keyParams === 'object'
  1713 + ? SDPUtils.writeCryptoKeyParams(parameters.keyParams)
  1714 + : parameters.keyParams) +
  1715 + (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') +
  1716 + '\r\n';
  1717 + };
  1718 +
  1719 + // Parses the crypto key parameters into
  1720 + // https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*
  1721 + SDPUtils.parseCryptoKeyParams = function(keyParams) {
  1722 + if (keyParams.indexOf('inline:') !== 0) {
  1723 + return null;
  1724 + }
  1725 + var parts = keyParams.substr(7).split('|');
  1726 + return {
  1727 + keyMethod: 'inline',
  1728 + keySalt: parts[0],
  1729 + lifeTime: parts[1],
  1730 + mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,
  1731 + mkiLength: parts[2] ? parts[2].split(':')[1] : undefined,
  1732 + };
  1733 + };
  1734 +
  1735 + SDPUtils.writeCryptoKeyParams = function(keyParams) {
  1736 + return keyParams.keyMethod + ':'
  1737 + + keyParams.keySalt +
  1738 + (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') +
  1739 + (keyParams.mkiValue && keyParams.mkiLength
  1740 + ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength
  1741 + : '');
  1742 + };
  1743 +
  1744 + // Extracts all SDES paramters.
  1745 + SDPUtils.getCryptoParameters = function(mediaSection, sessionpart) {
  1746 + var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,
  1747 + 'a=crypto:');
  1748 + return lines.map(SDPUtils.parseCryptoLine);
  1749 + };
  1750 +
  1751 + // Parses ICE information from SDP media section or sessionpart.
  1752 + // FIXME: for consistency with other functions this should only
  1753 + // get the ice-ufrag and ice-pwd lines as input.
  1754 + SDPUtils.getIceParameters = function(mediaSection, sessionpart) {
  1755 + var ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart,
  1756 + 'a=ice-ufrag:')[0];
  1757 + var pwd = SDPUtils.matchPrefix(mediaSection + sessionpart,
  1758 + 'a=ice-pwd:')[0];
  1759 + if (!(ufrag && pwd)) {
  1760 + return null;
  1761 + }
  1762 + return {
  1763 + usernameFragment: ufrag.substr(12),
  1764 + password: pwd.substr(10),
  1765 + };
  1766 + };
  1767 +
  1768 + // Serializes ICE parameters to SDP.
  1769 + SDPUtils.writeIceParameters = function(params) {
  1770 + return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' +
  1771 + 'a=ice-pwd:' + params.password + '\r\n';
  1772 + };
  1773 +
  1774 + // Parses the SDP media section and returns RTCRtpParameters.
  1775 + SDPUtils.parseRtpParameters = function(mediaSection) {
  1776 + var description = {
  1777 + codecs: [],
  1778 + headerExtensions: [],
  1779 + fecMechanisms: [],
  1780 + rtcp: []
  1781 + };
  1782 + var lines = SDPUtils.splitLines(mediaSection);
  1783 + var mline = lines[0].split(' ');
  1784 + for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]
  1785 + var pt = mline[i];
  1786 + var rtpmapline = SDPUtils.matchPrefix(
  1787 + mediaSection, 'a=rtpmap:' + pt + ' ')[0];
  1788 + if (rtpmapline) {
  1789 + var codec = SDPUtils.parseRtpMap(rtpmapline);
  1790 + var fmtps = SDPUtils.matchPrefix(
  1791 + mediaSection, 'a=fmtp:' + pt + ' ');
  1792 + // Only the first a=fmtp:<pt> is considered.
  1793 + codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
  1794 + codec.rtcpFeedback = SDPUtils.matchPrefix(
  1795 + mediaSection, 'a=rtcp-fb:' + pt + ' ')
  1796 + .map(SDPUtils.parseRtcpFb);
  1797 + description.codecs.push(codec);
  1798 + // parse FEC mechanisms from rtpmap lines.
  1799 + switch (codec.name.toUpperCase()) {
  1800 + case 'RED':
  1801 + case 'ULPFEC':
  1802 + description.fecMechanisms.push(codec.name.toUpperCase());
  1803 + break;
  1804 + }
  1805 + }
  1806 + }
  1807 + SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) {
  1808 + description.headerExtensions.push(SDPUtils.parseExtmap(line));
  1809 + });
  1810 + // FIXME: parse rtcp.
  1811 + return description;
  1812 + };
  1813 +
  1814 + // Generates parts of the SDP media section describing the capabilities /
  1815 + // parameters.
  1816 + SDPUtils.writeRtpDescription = function(kind, caps) {
  1817 + var sdp = '';
  1818 +
  1819 + // Build the mline.
  1820 + sdp += 'm=' + kind + ' ';
  1821 + sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
  1822 + sdp += ' UDP/TLS/RTP/SAVPF ';
  1823 + sdp += caps.codecs.map(function(codec) {
  1824 + if (codec.preferredPayloadType !== undefined) {
  1825 + return codec.preferredPayloadType;
  1826 + }
  1827 + return codec.payloadType;
  1828 + }).join(' ') + '\r\n';
  1829 +
  1830 + sdp += 'c=IN IP4 0.0.0.0\r\n';
  1831 + sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n';
  1832 +
  1833 + // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.
  1834 + caps.codecs.forEach(function(codec) {
  1835 + sdp += SDPUtils.writeRtpMap(codec);
  1836 + sdp += SDPUtils.writeFmtp(codec);
  1837 + sdp += SDPUtils.writeRtcpFb(codec);
  1838 + });
  1839 + var maxptime = 0;
  1840 + caps.codecs.forEach(function(codec) {
  1841 + if (codec.maxptime > maxptime) {
  1842 + maxptime = codec.maxptime;
  1843 + }
  1844 + });
  1845 + if (maxptime > 0) {
  1846 + sdp += 'a=maxptime:' + maxptime + '\r\n';
  1847 + }
  1848 + sdp += 'a=rtcp-mux\r\n';
  1849 +
  1850 + if (caps.headerExtensions) {
  1851 + caps.headerExtensions.forEach(function(extension) {
  1852 + sdp += SDPUtils.writeExtmap(extension);
  1853 + });
  1854 + }
  1855 + // FIXME: write fecMechanisms.
  1856 + return sdp;
  1857 + };
  1858 +
  1859 + // Parses the SDP media section and returns an array of
  1860 + // RTCRtpEncodingParameters.
  1861 + SDPUtils.parseRtpEncodingParameters = function(mediaSection) {
  1862 + var encodingParameters = [];
  1863 + var description = SDPUtils.parseRtpParameters(mediaSection);
  1864 + var hasRed = description.fecMechanisms.indexOf('RED') !== -1;
  1865 + var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;
  1866 +
  1867 + // filter a=ssrc:... cname:, ignore PlanB-msid
  1868 + var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
  1869 + .map(function(line) {
  1870 + return SDPUtils.parseSsrcMedia(line);
  1871 + })
  1872 + .filter(function(parts) {
  1873 + return parts.attribute === 'cname';
  1874 + });
  1875 + var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;
  1876 + var secondarySsrc;
  1877 +
  1878 + var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')
  1879 + .map(function(line) {
  1880 + var parts = line.substr(17).split(' ');
  1881 + return parts.map(function(part) {
  1882 + return parseInt(part, 10);
  1883 + });
  1884 + });
  1885 + if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {
  1886 + secondarySsrc = flows[0][1];
  1887 + }
  1888 +
  1889 + description.codecs.forEach(function(codec) {
  1890 + if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {
  1891 + var encParam = {
  1892 + ssrc: primarySsrc,
  1893 + codecPayloadType: parseInt(codec.parameters.apt, 10)
  1894 + };
  1895 + if (primarySsrc && secondarySsrc) {
  1896 + encParam.rtx = {ssrc: secondarySsrc};
  1897 + }
  1898 + encodingParameters.push(encParam);
  1899 + if (hasRed) {
  1900 + encParam = JSON.parse(JSON.stringify(encParam));
  1901 + encParam.fec = {
  1902 + ssrc: primarySsrc,
  1903 + mechanism: hasUlpfec ? 'red+ulpfec' : 'red'
  1904 + };
  1905 + encodingParameters.push(encParam);
  1906 + }
  1907 + }
  1908 + });
  1909 + if (encodingParameters.length === 0 && primarySsrc) {
  1910 + encodingParameters.push({
  1911 + ssrc: primarySsrc
  1912 + });
  1913 + }
  1914 +
  1915 + // we support both b=AS and b=TIAS but interpret AS as TIAS.
  1916 + var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');
  1917 + if (bandwidth.length) {
  1918 + if (bandwidth[0].indexOf('b=TIAS:') === 0) {
  1919 + bandwidth = parseInt(bandwidth[0].substr(7), 10);
  1920 + } else if (bandwidth[0].indexOf('b=AS:') === 0) {
  1921 + // use formula from JSEP to convert b=AS to TIAS value.
  1922 + bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95
  1923 + - (50 * 40 * 8);
  1924 + } else {
  1925 + bandwidth = undefined;
  1926 + }
  1927 + encodingParameters.forEach(function(params) {
  1928 + params.maxBitrate = bandwidth;
  1929 + });
  1930 + }
  1931 + return encodingParameters;
  1932 + };
  1933 +
  1934 + // parses http://draft.ortc.org/#rtcrtcpparameters*
  1935 + SDPUtils.parseRtcpParameters = function(mediaSection) {
  1936 + var rtcpParameters = {};
  1937 +
  1938 + // Gets the first SSRC. Note tha with RTX there might be multiple
  1939 + // SSRCs.
  1940 + var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
  1941 + .map(function(line) {
  1942 + return SDPUtils.parseSsrcMedia(line);
  1943 + })
  1944 + .filter(function(obj) {
  1945 + return obj.attribute === 'cname';
  1946 + })[0];
  1947 + if (remoteSsrc) {
  1948 + rtcpParameters.cname = remoteSsrc.value;
  1949 + rtcpParameters.ssrc = remoteSsrc.ssrc;
  1950 + }
  1951 +
  1952 + // Edge uses the compound attribute instead of reducedSize
  1953 + // compound is !reducedSize
  1954 + var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');
  1955 + rtcpParameters.reducedSize = rsize.length > 0;
  1956 + rtcpParameters.compound = rsize.length === 0;
  1957 +
  1958 + // parses the rtcp-mux attrіbute.
  1959 + // Note that Edge does not support unmuxed RTCP.
  1960 + var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');
  1961 + rtcpParameters.mux = mux.length > 0;
  1962 +
  1963 + return rtcpParameters;
  1964 + };
  1965 +
  1966 + // parses either a=msid: or a=ssrc:... msid lines and returns
  1967 + // the id of the MediaStream and MediaStreamTrack.
  1968 + SDPUtils.parseMsid = function(mediaSection) {
  1969 + var parts;
  1970 + var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');
  1971 + if (spec.length === 1) {
  1972 + parts = spec[0].substr(7).split(' ');
  1973 + return {stream: parts[0], track: parts[1]};
  1974 + }
  1975 + var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')
  1976 + .map(function(line) {
  1977 + return SDPUtils.parseSsrcMedia(line);
  1978 + })
  1979 + .filter(function(msidParts) {
  1980 + return msidParts.attribute === 'msid';
  1981 + });
  1982 + if (planB.length > 0) {
  1983 + parts = planB[0].value.split(' ');
  1984 + return {stream: parts[0], track: parts[1]};
  1985 + }
  1986 + };
  1987 +
  1988 + // SCTP
  1989 + // parses draft-ietf-mmusic-sctp-sdp-26 first and falls back
  1990 + // to draft-ietf-mmusic-sctp-sdp-05
  1991 + SDPUtils.parseSctpDescription = function(mediaSection) {
  1992 + var mline = SDPUtils.parseMLine(mediaSection);
  1993 + var maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');
  1994 + var maxMessageSize;
  1995 + if (maxSizeLine.length > 0) {
  1996 + maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10);
  1997 + }
  1998 + if (isNaN(maxMessageSize)) {
  1999 + maxMessageSize = 65536;
  2000 + }
  2001 + var sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');
  2002 + if (sctpPort.length > 0) {
  2003 + return {
  2004 + port: parseInt(sctpPort[0].substr(12), 10),
  2005 + protocol: mline.fmt,
  2006 + maxMessageSize: maxMessageSize
  2007 + };
  2008 + }
  2009 + var sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');
  2010 + if (sctpMapLines.length > 0) {
  2011 + var parts = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:')[0]
  2012 + .substr(10)
  2013 + .split(' ');
  2014 + return {
  2015 + port: parseInt(parts[0], 10),
  2016 + protocol: parts[1],
  2017 + maxMessageSize: maxMessageSize
  2018 + };
  2019 + }
  2020 + };
  2021 +
  2022 + // SCTP
  2023 + // outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers
  2024 + // support by now receiving in this format, unless we originally parsed
  2025 + // as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line
  2026 + // protocol of DTLS/SCTP -- without UDP/ or TCP/)
  2027 + SDPUtils.writeSctpDescription = function(media, sctp) {
  2028 + var output = [];
  2029 + if (media.protocol !== 'DTLS/SCTP') {
  2030 + output = [
  2031 + 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\r\n',
  2032 + 'c=IN IP4 0.0.0.0\r\n',
  2033 + 'a=sctp-port:' + sctp.port + '\r\n'
  2034 + ];
  2035 + } else {
  2036 + output = [
  2037 + 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\r\n',
  2038 + 'c=IN IP4 0.0.0.0\r\n',
  2039 + 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\r\n'
  2040 + ];
  2041 + }
  2042 + if (sctp.maxMessageSize !== undefined) {
  2043 + output.push('a=max-message-size:' + sctp.maxMessageSize + '\r\n');
  2044 + }
  2045 + return output.join('');
  2046 + };
  2047 +
  2048 + // Generate a session ID for SDP.
  2049 + // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1
  2050 + // recommends using a cryptographically random +ve 64-bit value
  2051 + // but right now this should be acceptable and within the right range
  2052 + SDPUtils.generateSessionId = function() {
  2053 + return Math.random().toString().substr(2, 21);
  2054 + };
  2055 +
  2056 + // Write boilder plate for start of SDP
  2057 + // sessId argument is optional - if not supplied it will
  2058 + // be generated randomly
  2059 + // sessVersion is optional and defaults to 2
  2060 + // sessUser is optional and defaults to 'thisisadapterortc'
  2061 + SDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) {
  2062 + var sessionId;
  2063 + var version = sessVer !== undefined ? sessVer : 2;
  2064 + if (sessId) {
  2065 + sessionId = sessId;
  2066 + } else {
  2067 + sessionId = SDPUtils.generateSessionId();
  2068 + }
  2069 + var user = sessUser || 'thisisadapterortc';
  2070 + // FIXME: sess-id should be an NTP timestamp.
  2071 + return 'v=0\r\n' +
  2072 + 'o=' + user + ' ' + sessionId + ' ' + version +
  2073 + ' IN IP4 127.0.0.1\r\n' +
  2074 + 's=-\r\n' +
  2075 + 't=0 0\r\n';
  2076 + };
  2077 +
  2078 + SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {
  2079 + var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);
  2080 +
  2081 + // Map ICE parameters (ufrag, pwd) to SDP.
  2082 + sdp += SDPUtils.writeIceParameters(
  2083 + transceiver.iceGatherer.getLocalParameters());
  2084 +
  2085 + // Map DTLS parameters to SDP.
  2086 + sdp += SDPUtils.writeDtlsParameters(
  2087 + transceiver.dtlsTransport.getLocalParameters(),
  2088 + type === 'offer' ? 'actpass' : 'active');
  2089 +
  2090 + sdp += 'a=mid:' + transceiver.mid + '\r\n';
  2091 +
  2092 + if (transceiver.direction) {
  2093 + sdp += 'a=' + transceiver.direction + '\r\n';
  2094 + } else if (transceiver.rtpSender && transceiver.rtpReceiver) {
  2095 + sdp += 'a=sendrecv\r\n';
  2096 + } else if (transceiver.rtpSender) {
  2097 + sdp += 'a=sendonly\r\n';
  2098 + } else if (transceiver.rtpReceiver) {
  2099 + sdp += 'a=recvonly\r\n';
  2100 + } else {
  2101 + sdp += 'a=inactive\r\n';
  2102 + }
  2103 +
  2104 + if (transceiver.rtpSender) {
  2105 + // spec.
  2106 + var msid = 'msid:' + stream.id + ' ' +
  2107 + transceiver.rtpSender.track.id + '\r\n';
  2108 + sdp += 'a=' + msid;
  2109 +
  2110 + // for Chrome.
  2111 + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
  2112 + ' ' + msid;
  2113 + if (transceiver.sendEncodingParameters[0].rtx) {
  2114 + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
  2115 + ' ' + msid;
  2116 + sdp += 'a=ssrc-group:FID ' +
  2117 + transceiver.sendEncodingParameters[0].ssrc + ' ' +
  2118 + transceiver.sendEncodingParameters[0].rtx.ssrc +
  2119 + '\r\n';
  2120 + }
  2121 + }
  2122 + // FIXME: this should be written by writeRtpDescription.
  2123 + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
  2124 + ' cname:' + SDPUtils.localCName + '\r\n';
  2125 + if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {
  2126 + sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
  2127 + ' cname:' + SDPUtils.localCName + '\r\n';
  2128 + }
  2129 + return sdp;
  2130 + };
  2131 +
  2132 + // Gets the direction from the mediaSection or the sessionpart.
  2133 + SDPUtils.getDirection = function(mediaSection, sessionpart) {
  2134 + // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
  2135 + var lines = SDPUtils.splitLines(mediaSection);
  2136 + for (var i = 0; i < lines.length; i++) {
  2137 + switch (lines[i]) {
  2138 + case 'a=sendrecv':
  2139 + case 'a=sendonly':
  2140 + case 'a=recvonly':
  2141 + case 'a=inactive':
  2142 + return lines[i].substr(2);
  2143 + // FIXME: What should happen here?
  2144 + }
  2145 + }
  2146 + if (sessionpart) {
  2147 + return SDPUtils.getDirection(sessionpart);
  2148 + }
  2149 + return 'sendrecv';
  2150 + };
  2151 +
  2152 + SDPUtils.getKind = function(mediaSection) {
  2153 + var lines = SDPUtils.splitLines(mediaSection);
  2154 + var mline = lines[0].split(' ');
  2155 + return mline[0].substr(2);
  2156 + };
  2157 +
  2158 + SDPUtils.isRejected = function(mediaSection) {
  2159 + return mediaSection.split(' ', 2)[1] === '0';
  2160 + };
  2161 +
  2162 + SDPUtils.parseMLine = function(mediaSection) {
  2163 + var lines = SDPUtils.splitLines(mediaSection);
  2164 + var parts = lines[0].substr(2).split(' ');
  2165 + return {
  2166 + kind: parts[0],
  2167 + port: parseInt(parts[1], 10),
  2168 + protocol: parts[2],
  2169 + fmt: parts.slice(3).join(' ')
  2170 + };
  2171 + };
  2172 +
  2173 + SDPUtils.parseOLine = function(mediaSection) {
  2174 + var line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];
  2175 + var parts = line.substr(2).split(' ');
  2176 + return {
  2177 + username: parts[0],
  2178 + sessionId: parts[1],
  2179 + sessionVersion: parseInt(parts[2], 10),
  2180 + netType: parts[3],
  2181 + addressType: parts[4],
  2182 + address: parts[5]
  2183 + };
  2184 + };
  2185 +
  2186 + // a very naive interpretation of a valid SDP.
  2187 + SDPUtils.isValidSDP = function(blob) {
  2188 + if (typeof blob !== 'string' || blob.length === 0) {
  2189 + return false;
  2190 + }
  2191 + var lines = SDPUtils.splitLines(blob);
  2192 + for (var i = 0; i < lines.length; i++) {
  2193 + if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {
  2194 + return false;
  2195 + }
  2196 + // TODO: check the modifier a bit more.
  2197 + }
  2198 + return true;
  2199 + };
  2200 +
  2201 + // Expose public methods.
  2202 + {
  2203 + module.exports = SDPUtils;
  2204 + }
  2205 + });
  2206 +
  2207 + /*
  2208 + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
  2209 + *
  2210 + * Use of this source code is governed by a BSD-style license
  2211 + * that can be found in the LICENSE file in the root of the source
  2212 + * tree.
  2213 + */
  2214 +
  2215 +
  2216 +
  2217 + function fixStatsType(stat) {
  2218 + return {
  2219 + inboundrtp: 'inbound-rtp',
  2220 + outboundrtp: 'outbound-rtp',
  2221 + candidatepair: 'candidate-pair',
  2222 + localcandidate: 'local-candidate',
  2223 + remotecandidate: 'remote-candidate'
  2224 + }[stat.type] || stat.type;
  2225 + }
  2226 +
  2227 + function writeMediaSection(transceiver, caps, type, stream, dtlsRole) {
  2228 + var sdp$1 = sdp.writeRtpDescription(transceiver.kind, caps);
  2229 +
  2230 + // Map ICE parameters (ufrag, pwd) to SDP.
  2231 + sdp$1 += sdp.writeIceParameters(
  2232 + transceiver.iceGatherer.getLocalParameters());
  2233 +
  2234 + // Map DTLS parameters to SDP.
  2235 + sdp$1 += sdp.writeDtlsParameters(
  2236 + transceiver.dtlsTransport.getLocalParameters(),
  2237 + type === 'offer' ? 'actpass' : dtlsRole || 'active');
  2238 +
  2239 + sdp$1 += 'a=mid:' + transceiver.mid + '\r\n';
  2240 +
  2241 + if (transceiver.rtpSender && transceiver.rtpReceiver) {
  2242 + sdp$1 += 'a=sendrecv\r\n';
  2243 + } else if (transceiver.rtpSender) {
  2244 + sdp$1 += 'a=sendonly\r\n';
  2245 + } else if (transceiver.rtpReceiver) {
  2246 + sdp$1 += 'a=recvonly\r\n';
  2247 + } else {
  2248 + sdp$1 += 'a=inactive\r\n';
  2249 + }
  2250 +
  2251 + if (transceiver.rtpSender) {
  2252 + var trackId = transceiver.rtpSender._initialTrackId ||
  2253 + transceiver.rtpSender.track.id;
  2254 + transceiver.rtpSender._initialTrackId = trackId;
  2255 + // spec.
  2256 + var msid = 'msid:' + (stream ? stream.id : '-') + ' ' +
  2257 + trackId + '\r\n';
  2258 + sdp$1 += 'a=' + msid;
  2259 + // for Chrome. Legacy should no longer be required.
  2260 + sdp$1 += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
  2261 + ' ' + msid;
  2262 +
  2263 + // RTX
  2264 + if (transceiver.sendEncodingParameters[0].rtx) {
  2265 + sdp$1 += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
  2266 + ' ' + msid;
  2267 + sdp$1 += 'a=ssrc-group:FID ' +
  2268 + transceiver.sendEncodingParameters[0].ssrc + ' ' +
  2269 + transceiver.sendEncodingParameters[0].rtx.ssrc +
  2270 + '\r\n';
  2271 + }
  2272 + }
  2273 + // FIXME: this should be written by writeRtpDescription.
  2274 + sdp$1 += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +
  2275 + ' cname:' + sdp.localCName + '\r\n';
  2276 + if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {
  2277 + sdp$1 += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +
  2278 + ' cname:' + sdp.localCName + '\r\n';
  2279 + }
  2280 + return sdp$1;
  2281 + }
  2282 +
  2283 + // Edge does not like
  2284 + // 1) stun: filtered after 14393 unless ?transport=udp is present
  2285 + // 2) turn: that does not have all of turn:host:port?transport=udp
  2286 + // 3) turn: with ipv6 addresses
  2287 + // 4) turn: occurring muliple times
  2288 + function filterIceServers(iceServers, edgeVersion) {
  2289 + var hasTurn = false;
  2290 + iceServers = JSON.parse(JSON.stringify(iceServers));
  2291 + return iceServers.filter(function(server) {
  2292 + if (server && (server.urls || server.url)) {
  2293 + var urls = server.urls || server.url;
  2294 + if (server.url && !server.urls) {
  2295 + console.warn('RTCIceServer.url is deprecated! Use urls instead.');
  2296 + }
  2297 + var isString = typeof urls === 'string';
  2298 + if (isString) {
  2299 + urls = [urls];
  2300 + }
  2301 + urls = urls.filter(function(url) {
  2302 + var validTurn = url.indexOf('turn:') === 0 &&
  2303 + url.indexOf('transport=udp') !== -1 &&
  2304 + url.indexOf('turn:[') === -1 &&
  2305 + !hasTurn;
  2306 +
  2307 + if (validTurn) {
  2308 + hasTurn = true;
  2309 + return true;
  2310 + }
  2311 + return url.indexOf('stun:') === 0 && edgeVersion >= 14393 &&
  2312 + url.indexOf('?transport=udp') === -1;
  2313 + });
  2314 +
  2315 + delete server.url;
  2316 + server.urls = isString ? urls[0] : urls;
  2317 + return !!urls.length;
  2318 + }
  2319 + });
  2320 + }
  2321 +
  2322 + // Determines the intersection of local and remote capabilities.
  2323 + function getCommonCapabilities(localCapabilities, remoteCapabilities) {
  2324 + var commonCapabilities = {
  2325 + codecs: [],
  2326 + headerExtensions: [],
  2327 + fecMechanisms: []
  2328 + };
  2329 +
  2330 + var findCodecByPayloadType = function(pt, codecs) {
  2331 + pt = parseInt(pt, 10);
  2332 + for (var i = 0; i < codecs.length; i++) {
  2333 + if (codecs[i].payloadType === pt ||
  2334 + codecs[i].preferredPayloadType === pt) {
  2335 + return codecs[i];
  2336 + }
  2337 + }
  2338 + };
  2339 +
  2340 + var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) {
  2341 + var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs);
  2342 + var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs);
  2343 + return lCodec && rCodec &&
  2344 + lCodec.name.toLowerCase() === rCodec.name.toLowerCase();
  2345 + };
  2346 +
  2347 + localCapabilities.codecs.forEach(function(lCodec) {
  2348 + for (var i = 0; i < remoteCapabilities.codecs.length; i++) {
  2349 + var rCodec = remoteCapabilities.codecs[i];
  2350 + if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&
  2351 + lCodec.clockRate === rCodec.clockRate) {
  2352 + if (lCodec.name.toLowerCase() === 'rtx' &&
  2353 + lCodec.parameters && rCodec.parameters.apt) {
  2354 + // for RTX we need to find the local rtx that has a apt
  2355 + // which points to the same local codec as the remote one.
  2356 + if (!rtxCapabilityMatches(lCodec, rCodec,
  2357 + localCapabilities.codecs, remoteCapabilities.codecs)) {
  2358 + continue;
  2359 + }
  2360 + }
  2361 + rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy
  2362 + // number of channels is the highest common number of channels
  2363 + rCodec.numChannels = Math.min(lCodec.numChannels,
  2364 + rCodec.numChannels);
  2365 + // push rCodec so we reply with offerer payload type
  2366 + commonCapabilities.codecs.push(rCodec);
  2367 +
  2368 + // determine common feedback mechanisms
  2369 + rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) {
  2370 + for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {
  2371 + if (lCodec.rtcpFeedback[j].type === fb.type &&
  2372 + lCodec.rtcpFeedback[j].parameter === fb.parameter) {
  2373 + return true;
  2374 + }
  2375 + }
  2376 + return false;
  2377 + });
  2378 + // FIXME: also need to determine .parameters
  2379 + // see https://github.com/openpeer/ortc/issues/569
  2380 + break;
  2381 + }
  2382 + }
  2383 + });
  2384 +
  2385 + localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {
  2386 + for (var i = 0; i < remoteCapabilities.headerExtensions.length;
  2387 + i++) {
  2388 + var rHeaderExtension = remoteCapabilities.headerExtensions[i];
  2389 + if (lHeaderExtension.uri === rHeaderExtension.uri) {
  2390 + commonCapabilities.headerExtensions.push(rHeaderExtension);
  2391 + break;
  2392 + }
  2393 + }
  2394 + });
  2395 +
  2396 + // FIXME: fecMechanisms
  2397 + return commonCapabilities;
  2398 + }
  2399 +
  2400 + // is action=setLocalDescription with type allowed in signalingState
  2401 + function isActionAllowedInSignalingState(action, type, signalingState) {
  2402 + return {
  2403 + offer: {
  2404 + setLocalDescription: ['stable', 'have-local-offer'],
  2405 + setRemoteDescription: ['stable', 'have-remote-offer']
  2406 + },
  2407 + answer: {
  2408 + setLocalDescription: ['have-remote-offer', 'have-local-pranswer'],
  2409 + setRemoteDescription: ['have-local-offer', 'have-remote-pranswer']
  2410 + }
  2411 + }[type][action].indexOf(signalingState) !== -1;
  2412 + }
  2413 +
  2414 + function maybeAddCandidate(iceTransport, candidate) {
  2415 + // Edge's internal representation adds some fields therefore
  2416 + // not all fieldѕ are taken into account.
  2417 + var alreadyAdded = iceTransport.getRemoteCandidates()
  2418 + .find(function(remoteCandidate) {
  2419 + return candidate.foundation === remoteCandidate.foundation &&
  2420 + candidate.ip === remoteCandidate.ip &&
  2421 + candidate.port === remoteCandidate.port &&
  2422 + candidate.priority === remoteCandidate.priority &&
  2423 + candidate.protocol === remoteCandidate.protocol &&
  2424 + candidate.type === remoteCandidate.type;
  2425 + });
  2426 + if (!alreadyAdded) {
  2427 + iceTransport.addRemoteCandidate(candidate);
  2428 + }
  2429 + return !alreadyAdded;
  2430 + }
  2431 +
  2432 +
  2433 + function makeError(name, description) {
  2434 + var e = new Error(description);
  2435 + e.name = name;
  2436 + // legacy error codes from https://heycam.github.io/webidl/#idl-DOMException-error-names
  2437 + e.code = {
  2438 + NotSupportedError: 9,
  2439 + InvalidStateError: 11,
  2440 + InvalidAccessError: 15,
  2441 + TypeError: undefined,
  2442 + OperationError: undefined
  2443 + }[name];
  2444 + return e;
  2445 + }
  2446 +
  2447 + var rtcpeerconnection = function(window, edgeVersion) {
  2448 + // https://w3c.github.io/mediacapture-main/#mediastream
  2449 + // Helper function to add the track to the stream and
  2450 + // dispatch the event ourselves.
  2451 + function addTrackToStreamAndFireEvent(track, stream) {
  2452 + stream.addTrack(track);
  2453 + stream.dispatchEvent(new window.MediaStreamTrackEvent('addtrack',
  2454 + {track: track}));
  2455 + }
  2456 +
  2457 + function removeTrackFromStreamAndFireEvent(track, stream) {
  2458 + stream.removeTrack(track);
  2459 + stream.dispatchEvent(new window.MediaStreamTrackEvent('removetrack',
  2460 + {track: track}));
  2461 + }
  2462 +
  2463 + function fireAddTrack(pc, track, receiver, streams) {
  2464 + var trackEvent = new Event('track');
  2465 + trackEvent.track = track;
  2466 + trackEvent.receiver = receiver;
  2467 + trackEvent.transceiver = {receiver: receiver};
  2468 + trackEvent.streams = streams;
  2469 + window.setTimeout(function() {
  2470 + pc._dispatchEvent('track', trackEvent);
  2471 + });
  2472 + }
  2473 +
  2474 + var RTCPeerConnection = function(config) {
  2475 + var pc = this;
  2476 +
  2477 + var _eventTarget = document.createDocumentFragment();
  2478 + ['addEventListener', 'removeEventListener', 'dispatchEvent']
  2479 + .forEach(function(method) {
  2480 + pc[method] = _eventTarget[method].bind(_eventTarget);
  2481 + });
  2482 +
  2483 + this.canTrickleIceCandidates = null;
  2484 +
  2485 + this.needNegotiation = false;
  2486 +
  2487 + this.localStreams = [];
  2488 + this.remoteStreams = [];
  2489 +
  2490 + this._localDescription = null;
  2491 + this._remoteDescription = null;
  2492 +
  2493 + this.signalingState = 'stable';
  2494 + this.iceConnectionState = 'new';
  2495 + this.connectionState = 'new';
  2496 + this.iceGatheringState = 'new';
  2497 +
  2498 + config = JSON.parse(JSON.stringify(config || {}));
  2499 +
  2500 + this.usingBundle = config.bundlePolicy === 'max-bundle';
  2501 + if (config.rtcpMuxPolicy === 'negotiate') {
  2502 + throw(makeError('NotSupportedError',
  2503 + 'rtcpMuxPolicy \'negotiate\' is not supported'));
  2504 + } else if (!config.rtcpMuxPolicy) {
  2505 + config.rtcpMuxPolicy = 'require';
  2506 + }
  2507 +
  2508 + switch (config.iceTransportPolicy) {
  2509 + case 'all':
  2510 + case 'relay':
  2511 + break;
  2512 + default:
  2513 + config.iceTransportPolicy = 'all';
  2514 + break;
  2515 + }
  2516 +
  2517 + switch (config.bundlePolicy) {
  2518 + case 'balanced':
  2519 + case 'max-compat':
  2520 + case 'max-bundle':
  2521 + break;
  2522 + default:
  2523 + config.bundlePolicy = 'balanced';
  2524 + break;
  2525 + }
  2526 +
  2527 + config.iceServers = filterIceServers(config.iceServers || [], edgeVersion);
  2528 +
  2529 + this._iceGatherers = [];
  2530 + if (config.iceCandidatePoolSize) {
  2531 + for (var i = config.iceCandidatePoolSize; i > 0; i--) {
  2532 + this._iceGatherers.push(new window.RTCIceGatherer({
  2533 + iceServers: config.iceServers,
  2534 + gatherPolicy: config.iceTransportPolicy
  2535 + }));
  2536 + }
  2537 + } else {
  2538 + config.iceCandidatePoolSize = 0;
  2539 + }
  2540 +
  2541 + this._config = config;
  2542 +
  2543 + // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...
  2544 + // everything that is needed to describe a SDP m-line.
  2545 + this.transceivers = [];
  2546 +
  2547 + this._sdpSessionId = sdp.generateSessionId();
  2548 + this._sdpSessionVersion = 0;
  2549 +
  2550 + this._dtlsRole = undefined; // role for a=setup to use in answers.
  2551 +
  2552 + this._isClosed = false;
  2553 + };
  2554 +
  2555 + Object.defineProperty(RTCPeerConnection.prototype, 'localDescription', {
  2556 + configurable: true,
  2557 + get: function() {
  2558 + return this._localDescription;
  2559 + }
  2560 + });
  2561 + Object.defineProperty(RTCPeerConnection.prototype, 'remoteDescription', {
  2562 + configurable: true,
  2563 + get: function() {
  2564 + return this._remoteDescription;
  2565 + }
  2566 + });
  2567 +
  2568 + // set up event handlers on prototype
  2569 + RTCPeerConnection.prototype.onicecandidate = null;
  2570 + RTCPeerConnection.prototype.onaddstream = null;
  2571 + RTCPeerConnection.prototype.ontrack = null;
  2572 + RTCPeerConnection.prototype.onremovestream = null;
  2573 + RTCPeerConnection.prototype.onsignalingstatechange = null;
  2574 + RTCPeerConnection.prototype.oniceconnectionstatechange = null;
  2575 + RTCPeerConnection.prototype.onconnectionstatechange = null;
  2576 + RTCPeerConnection.prototype.onicegatheringstatechange = null;
  2577 + RTCPeerConnection.prototype.onnegotiationneeded = null;
  2578 + RTCPeerConnection.prototype.ondatachannel = null;
  2579 +
  2580 + RTCPeerConnection.prototype._dispatchEvent = function(name, event) {
  2581 + if (this._isClosed) {
  2582 + return;
  2583 + }
  2584 + this.dispatchEvent(event);
  2585 + if (typeof this['on' + name] === 'function') {
  2586 + this['on' + name](event);
  2587 + }
  2588 + };
  2589 +
  2590 + RTCPeerConnection.prototype._emitGatheringStateChange = function() {
  2591 + var event = new Event('icegatheringstatechange');
  2592 + this._dispatchEvent('icegatheringstatechange', event);
  2593 + };
  2594 +
  2595 + RTCPeerConnection.prototype.getConfiguration = function() {
  2596 + return this._config;
  2597 + };
  2598 +
  2599 + RTCPeerConnection.prototype.getLocalStreams = function() {
  2600 + return this.localStreams;
  2601 + };
  2602 +
  2603 + RTCPeerConnection.prototype.getRemoteStreams = function() {
  2604 + return this.remoteStreams;
  2605 + };
  2606 +
  2607 + // internal helper to create a transceiver object.
  2608 + // (which is not yet the same as the WebRTC 1.0 transceiver)
  2609 + RTCPeerConnection.prototype._createTransceiver = function(kind, doNotAdd) {
  2610 + var hasBundleTransport = this.transceivers.length > 0;
  2611 + var transceiver = {
  2612 + track: null,
  2613 + iceGatherer: null,
  2614 + iceTransport: null,
  2615 + dtlsTransport: null,
  2616 + localCapabilities: null,
  2617 + remoteCapabilities: null,
  2618 + rtpSender: null,
  2619 + rtpReceiver: null,
  2620 + kind: kind,
  2621 + mid: null,
  2622 + sendEncodingParameters: null,
  2623 + recvEncodingParameters: null,
  2624 + stream: null,
  2625 + associatedRemoteMediaStreams: [],
  2626 + wantReceive: true
  2627 + };
  2628 + if (this.usingBundle && hasBundleTransport) {
  2629 + transceiver.iceTransport = this.transceivers[0].iceTransport;
  2630 + transceiver.dtlsTransport = this.transceivers[0].dtlsTransport;
  2631 + } else {
  2632 + var transports = this._createIceAndDtlsTransports();
  2633 + transceiver.iceTransport = transports.iceTransport;
  2634 + transceiver.dtlsTransport = transports.dtlsTransport;
  2635 + }
  2636 + if (!doNotAdd) {
  2637 + this.transceivers.push(transceiver);
  2638 + }
  2639 + return transceiver;
  2640 + };
  2641 +
  2642 + RTCPeerConnection.prototype.addTrack = function(track, stream) {
  2643 + if (this._isClosed) {
  2644 + throw makeError('InvalidStateError',
  2645 + 'Attempted to call addTrack on a closed peerconnection.');
  2646 + }
  2647 +
  2648 + var alreadyExists = this.transceivers.find(function(s) {
  2649 + return s.track === track;
  2650 + });
  2651 +
  2652 + if (alreadyExists) {
  2653 + throw makeError('InvalidAccessError', 'Track already exists.');
  2654 + }
  2655 +
  2656 + var transceiver;
  2657 + for (var i = 0; i < this.transceivers.length; i++) {
  2658 + if (!this.transceivers[i].track &&
  2659 + this.transceivers[i].kind === track.kind) {
  2660 + transceiver = this.transceivers[i];
  2661 + }
  2662 + }
  2663 + if (!transceiver) {
  2664 + transceiver = this._createTransceiver(track.kind);
  2665 + }
  2666 +
  2667 + this._maybeFireNegotiationNeeded();
  2668 +
  2669 + if (this.localStreams.indexOf(stream) === -1) {
  2670 + this.localStreams.push(stream);
  2671 + }
  2672 +
  2673 + transceiver.track = track;
  2674 + transceiver.stream = stream;
  2675 + transceiver.rtpSender = new window.RTCRtpSender(track,
  2676 + transceiver.dtlsTransport);
  2677 + return transceiver.rtpSender;
  2678 + };
  2679 +
  2680 + RTCPeerConnection.prototype.addStream = function(stream) {
  2681 + var pc = this;
  2682 + if (edgeVersion >= 15025) {
  2683 + stream.getTracks().forEach(function(track) {
  2684 + pc.addTrack(track, stream);
  2685 + });
  2686 + } else {
  2687 + // Clone is necessary for local demos mostly, attaching directly
  2688 + // to two different senders does not work (build 10547).
  2689 + // Fixed in 15025 (or earlier)
  2690 + var clonedStream = stream.clone();
  2691 + stream.getTracks().forEach(function(track, idx) {
  2692 + var clonedTrack = clonedStream.getTracks()[idx];
  2693 + track.addEventListener('enabled', function(event) {
  2694 + clonedTrack.enabled = event.enabled;
  2695 + });
  2696 + });
  2697 + clonedStream.getTracks().forEach(function(track) {
  2698 + pc.addTrack(track, clonedStream);
  2699 + });
  2700 + }
  2701 + };
  2702 +
  2703 + RTCPeerConnection.prototype.removeTrack = function(sender) {
  2704 + if (this._isClosed) {
  2705 + throw makeError('InvalidStateError',
  2706 + 'Attempted to call removeTrack on a closed peerconnection.');
  2707 + }
  2708 +
  2709 + if (!(sender instanceof window.RTCRtpSender)) {
  2710 + throw new TypeError('Argument 1 of RTCPeerConnection.removeTrack ' +
  2711 + 'does not implement interface RTCRtpSender.');
  2712 + }
  2713 +
  2714 + var transceiver = this.transceivers.find(function(t) {
  2715 + return t.rtpSender === sender;
  2716 + });
  2717 +
  2718 + if (!transceiver) {
  2719 + throw makeError('InvalidAccessError',
  2720 + 'Sender was not created by this connection.');
  2721 + }
  2722 + var stream = transceiver.stream;
  2723 +
  2724 + transceiver.rtpSender.stop();
  2725 + transceiver.rtpSender = null;
  2726 + transceiver.track = null;
  2727 + transceiver.stream = null;
  2728 +
  2729 + // remove the stream from the set of local streams
  2730 + var localStreams = this.transceivers.map(function(t) {
  2731 + return t.stream;
  2732 + });
  2733 + if (localStreams.indexOf(stream) === -1 &&
  2734 + this.localStreams.indexOf(stream) > -1) {
  2735 + this.localStreams.splice(this.localStreams.indexOf(stream), 1);
  2736 + }
  2737 +
  2738 + this._maybeFireNegotiationNeeded();
  2739 + };
  2740 +
  2741 + RTCPeerConnection.prototype.removeStream = function(stream) {
  2742 + var pc = this;
  2743 + stream.getTracks().forEach(function(track) {
  2744 + var sender = pc.getSenders().find(function(s) {
  2745 + return s.track === track;
  2746 + });
  2747 + if (sender) {
  2748 + pc.removeTrack(sender);
  2749 + }
  2750 + });
  2751 + };
  2752 +
  2753 + RTCPeerConnection.prototype.getSenders = function() {
  2754 + return this.transceivers.filter(function(transceiver) {
  2755 + return !!transceiver.rtpSender;
  2756 + })
  2757 + .map(function(transceiver) {
  2758 + return transceiver.rtpSender;
  2759 + });
  2760 + };
  2761 +
  2762 + RTCPeerConnection.prototype.getReceivers = function() {
  2763 + return this.transceivers.filter(function(transceiver) {
  2764 + return !!transceiver.rtpReceiver;
  2765 + })
  2766 + .map(function(transceiver) {
  2767 + return transceiver.rtpReceiver;
  2768 + });
  2769 + };
  2770 +
  2771 +
  2772 + RTCPeerConnection.prototype._createIceGatherer = function(sdpMLineIndex,
  2773 + usingBundle) {
  2774 + var pc = this;
  2775 + if (usingBundle && sdpMLineIndex > 0) {
  2776 + return this.transceivers[0].iceGatherer;
  2777 + } else if (this._iceGatherers.length) {
  2778 + return this._iceGatherers.shift();
  2779 + }
  2780 + var iceGatherer = new window.RTCIceGatherer({
  2781 + iceServers: this._config.iceServers,
  2782 + gatherPolicy: this._config.iceTransportPolicy
  2783 + });
  2784 + Object.defineProperty(iceGatherer, 'state',
  2785 + {value: 'new', writable: true}
  2786 + );
  2787 +
  2788 + this.transceivers[sdpMLineIndex].bufferedCandidateEvents = [];
  2789 + this.transceivers[sdpMLineIndex].bufferCandidates = function(event) {
  2790 + var end = !event.candidate || Object.keys(event.candidate).length === 0;
  2791 + // polyfill since RTCIceGatherer.state is not implemented in
  2792 + // Edge 10547 yet.
  2793 + iceGatherer.state = end ? 'completed' : 'gathering';
  2794 + if (pc.transceivers[sdpMLineIndex].bufferedCandidateEvents !== null) {
  2795 + pc.transceivers[sdpMLineIndex].bufferedCandidateEvents.push(event);
  2796 + }
  2797 + };
  2798 + iceGatherer.addEventListener('localcandidate',
  2799 + this.transceivers[sdpMLineIndex].bufferCandidates);
  2800 + return iceGatherer;
  2801 + };
  2802 +
  2803 + // start gathering from an RTCIceGatherer.
  2804 + RTCPeerConnection.prototype._gather = function(mid, sdpMLineIndex) {
  2805 + var pc = this;
  2806 + var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;
  2807 + if (iceGatherer.onlocalcandidate) {
  2808 + return;
  2809 + }
  2810 + var bufferedCandidateEvents =
  2811 + this.transceivers[sdpMLineIndex].bufferedCandidateEvents;
  2812 + this.transceivers[sdpMLineIndex].bufferedCandidateEvents = null;
  2813 + iceGatherer.removeEventListener('localcandidate',
  2814 + this.transceivers[sdpMLineIndex].bufferCandidates);
  2815 + iceGatherer.onlocalcandidate = function(evt) {
  2816 + if (pc.usingBundle && sdpMLineIndex > 0) {
  2817 + // if we know that we use bundle we can drop candidates with
  2818 + // ѕdpMLineIndex > 0. If we don't do this then our state gets
  2819 + // confused since we dispose the extra ice gatherer.
  2820 + return;
  2821 + }
  2822 + var event = new Event('icecandidate');
  2823 + event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};
  2824 +
  2825 + var cand = evt.candidate;
  2826 + // Edge emits an empty object for RTCIceCandidateComplete‥
  2827 + var end = !cand || Object.keys(cand).length === 0;
  2828 + if (end) {
  2829 + // polyfill since RTCIceGatherer.state is not implemented in
  2830 + // Edge 10547 yet.
  2831 + if (iceGatherer.state === 'new' || iceGatherer.state === 'gathering') {
  2832 + iceGatherer.state = 'completed';
  2833 + }
  2834 + } else {
  2835 + if (iceGatherer.state === 'new') {
  2836 + iceGatherer.state = 'gathering';
  2837 + }
  2838 + // RTCIceCandidate doesn't have a component, needs to be added
  2839 + cand.component = 1;
  2840 + // also the usernameFragment. TODO: update SDP to take both variants.
  2841 + cand.ufrag = iceGatherer.getLocalParameters().usernameFragment;
  2842 +
  2843 + var serializedCandidate = sdp.writeCandidate(cand);
  2844 + event.candidate = Object.assign(event.candidate,
  2845 + sdp.parseCandidate(serializedCandidate));
  2846 +
  2847 + event.candidate.candidate = serializedCandidate;
  2848 + event.candidate.toJSON = function() {
  2849 + return {
  2850 + candidate: event.candidate.candidate,
  2851 + sdpMid: event.candidate.sdpMid,
  2852 + sdpMLineIndex: event.candidate.sdpMLineIndex,
  2853 + usernameFragment: event.candidate.usernameFragment
  2854 + };
  2855 + };
  2856 + }
  2857 +
  2858 + // update local description.
  2859 + var sections = sdp.getMediaSections(pc._localDescription.sdp);
  2860 + if (!end) {
  2861 + sections[event.candidate.sdpMLineIndex] +=
  2862 + 'a=' + event.candidate.candidate + '\r\n';
  2863 + } else {
  2864 + sections[event.candidate.sdpMLineIndex] +=
  2865 + 'a=end-of-candidates\r\n';
  2866 + }
  2867 + pc._localDescription.sdp =
  2868 + sdp.getDescription(pc._localDescription.sdp) +
  2869 + sections.join('');
  2870 + var complete = pc.transceivers.every(function(transceiver) {
  2871 + return transceiver.iceGatherer &&
  2872 + transceiver.iceGatherer.state === 'completed';
  2873 + });
  2874 +
  2875 + if (pc.iceGatheringState !== 'gathering') {
  2876 + pc.iceGatheringState = 'gathering';
  2877 + pc._emitGatheringStateChange();
  2878 + }
  2879 +
  2880 + // Emit candidate. Also emit null candidate when all gatherers are
  2881 + // complete.
  2882 + if (!end) {
  2883 + pc._dispatchEvent('icecandidate', event);
  2884 + }
  2885 + if (complete) {
  2886 + pc._dispatchEvent('icecandidate', new Event('icecandidate'));
  2887 + pc.iceGatheringState = 'complete';
  2888 + pc._emitGatheringStateChange();
  2889 + }
  2890 + };
  2891 +
  2892 + // emit already gathered candidates.
  2893 + window.setTimeout(function() {
  2894 + bufferedCandidateEvents.forEach(function(e) {
  2895 + iceGatherer.onlocalcandidate(e);
  2896 + });
  2897 + }, 0);
  2898 + };
  2899 +
  2900 + // Create ICE transport and DTLS transport.
  2901 + RTCPeerConnection.prototype._createIceAndDtlsTransports = function() {
  2902 + var pc = this;
  2903 + var iceTransport = new window.RTCIceTransport(null);
  2904 + iceTransport.onicestatechange = function() {
  2905 + pc._updateIceConnectionState();
  2906 + pc._updateConnectionState();
  2907 + };
  2908 +
  2909 + var dtlsTransport = new window.RTCDtlsTransport(iceTransport);
  2910 + dtlsTransport.ondtlsstatechange = function() {
  2911 + pc._updateConnectionState();
  2912 + };
  2913 + dtlsTransport.onerror = function() {
  2914 + // onerror does not set state to failed by itself.
  2915 + Object.defineProperty(dtlsTransport, 'state',
  2916 + {value: 'failed', writable: true});
  2917 + pc._updateConnectionState();
  2918 + };
  2919 +
  2920 + return {
  2921 + iceTransport: iceTransport,
  2922 + dtlsTransport: dtlsTransport
  2923 + };
  2924 + };
  2925 +
  2926 + // Destroy ICE gatherer, ICE transport and DTLS transport.
  2927 + // Without triggering the callbacks.
  2928 + RTCPeerConnection.prototype._disposeIceAndDtlsTransports = function(
  2929 + sdpMLineIndex) {
  2930 + var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;
  2931 + if (iceGatherer) {
  2932 + delete iceGatherer.onlocalcandidate;
  2933 + delete this.transceivers[sdpMLineIndex].iceGatherer;
  2934 + }
  2935 + var iceTransport = this.transceivers[sdpMLineIndex].iceTransport;
  2936 + if (iceTransport) {
  2937 + delete iceTransport.onicestatechange;
  2938 + delete this.transceivers[sdpMLineIndex].iceTransport;
  2939 + }
  2940 + var dtlsTransport = this.transceivers[sdpMLineIndex].dtlsTransport;
  2941 + if (dtlsTransport) {
  2942 + delete dtlsTransport.ondtlsstatechange;
  2943 + delete dtlsTransport.onerror;
  2944 + delete this.transceivers[sdpMLineIndex].dtlsTransport;
  2945 + }
  2946 + };
  2947 +
  2948 + // Start the RTP Sender and Receiver for a transceiver.
  2949 + RTCPeerConnection.prototype._transceive = function(transceiver,
  2950 + send, recv) {
  2951 + var params = getCommonCapabilities(transceiver.localCapabilities,
  2952 + transceiver.remoteCapabilities);
  2953 + if (send && transceiver.rtpSender) {
  2954 + params.encodings = transceiver.sendEncodingParameters;
  2955 + params.rtcp = {
  2956 + cname: sdp.localCName,
  2957 + compound: transceiver.rtcpParameters.compound
  2958 + };
  2959 + if (transceiver.recvEncodingParameters.length) {
  2960 + params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;
  2961 + }
  2962 + transceiver.rtpSender.send(params);
  2963 + }
  2964 + if (recv && transceiver.rtpReceiver && params.codecs.length > 0) {
  2965 + // remove RTX field in Edge 14942
  2966 + if (transceiver.kind === 'video'
  2967 + && transceiver.recvEncodingParameters
  2968 + && edgeVersion < 15019) {
  2969 + transceiver.recvEncodingParameters.forEach(function(p) {
  2970 + delete p.rtx;
  2971 + });
  2972 + }
  2973 + if (transceiver.recvEncodingParameters.length) {
  2974 + params.encodings = transceiver.recvEncodingParameters;
  2975 + } else {
  2976 + params.encodings = [{}];
  2977 + }
  2978 + params.rtcp = {
  2979 + compound: transceiver.rtcpParameters.compound
  2980 + };
  2981 + if (transceiver.rtcpParameters.cname) {
  2982 + params.rtcp.cname = transceiver.rtcpParameters.cname;
  2983 + }
  2984 + if (transceiver.sendEncodingParameters.length) {
  2985 + params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;
  2986 + }
  2987 + transceiver.rtpReceiver.receive(params);
  2988 + }
  2989 + };
  2990 +
  2991 + RTCPeerConnection.prototype.setLocalDescription = function(description) {
  2992 + var pc = this;
  2993 +
  2994 + // Note: pranswer is not supported.
  2995 + if (['offer', 'answer'].indexOf(description.type) === -1) {
  2996 + return Promise.reject(makeError('TypeError',
  2997 + 'Unsupported type "' + description.type + '"'));
  2998 + }
  2999 +
  3000 + if (!isActionAllowedInSignalingState('setLocalDescription',
  3001 + description.type, pc.signalingState) || pc._isClosed) {
  3002 + return Promise.reject(makeError('InvalidStateError',
  3003 + 'Can not set local ' + description.type +
  3004 + ' in state ' + pc.signalingState));
  3005 + }
  3006 +
  3007 + var sections;
  3008 + var sessionpart;
  3009 + if (description.type === 'offer') {
  3010 + // VERY limited support for SDP munging. Limited to:
  3011 + // * changing the order of codecs
  3012 + sections = sdp.splitSections(description.sdp);
  3013 + sessionpart = sections.shift();
  3014 + sections.forEach(function(mediaSection, sdpMLineIndex) {
  3015 + var caps = sdp.parseRtpParameters(mediaSection);
  3016 + pc.transceivers[sdpMLineIndex].localCapabilities = caps;
  3017 + });
  3018 +
  3019 + pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {
  3020 + pc._gather(transceiver.mid, sdpMLineIndex);
  3021 + });
  3022 + } else if (description.type === 'answer') {
  3023 + sections = sdp.splitSections(pc._remoteDescription.sdp);
  3024 + sessionpart = sections.shift();
  3025 + var isIceLite = sdp.matchPrefix(sessionpart,
  3026 + 'a=ice-lite').length > 0;
  3027 + sections.forEach(function(mediaSection, sdpMLineIndex) {
  3028 + var transceiver = pc.transceivers[sdpMLineIndex];
  3029 + var iceGatherer = transceiver.iceGatherer;
  3030 + var iceTransport = transceiver.iceTransport;
  3031 + var dtlsTransport = transceiver.dtlsTransport;
  3032 + var localCapabilities = transceiver.localCapabilities;
  3033 + var remoteCapabilities = transceiver.remoteCapabilities;
  3034 +
  3035 + // treat bundle-only as not-rejected.
  3036 + var rejected = sdp.isRejected(mediaSection) &&
  3037 + sdp.matchPrefix(mediaSection, 'a=bundle-only').length === 0;
  3038 +
  3039 + if (!rejected && !transceiver.rejected) {
  3040 + var remoteIceParameters = sdp.getIceParameters(
  3041 + mediaSection, sessionpart);
  3042 + var remoteDtlsParameters = sdp.getDtlsParameters(
  3043 + mediaSection, sessionpart);
  3044 + if (isIceLite) {
  3045 + remoteDtlsParameters.role = 'server';
  3046 + }
  3047 +
  3048 + if (!pc.usingBundle || sdpMLineIndex === 0) {
  3049 + pc._gather(transceiver.mid, sdpMLineIndex);
  3050 + if (iceTransport.state === 'new') {
  3051 + iceTransport.start(iceGatherer, remoteIceParameters,
  3052 + isIceLite ? 'controlling' : 'controlled');
  3053 + }
  3054 + if (dtlsTransport.state === 'new') {
  3055 + dtlsTransport.start(remoteDtlsParameters);
  3056 + }
  3057 + }
  3058 +
  3059 + // Calculate intersection of capabilities.
  3060 + var params = getCommonCapabilities(localCapabilities,
  3061 + remoteCapabilities);
  3062 +
  3063 + // Start the RTCRtpSender. The RTCRtpReceiver for this
  3064 + // transceiver has already been started in setRemoteDescription.
  3065 + pc._transceive(transceiver,
  3066 + params.codecs.length > 0,
  3067 + false);
  3068 + }
  3069 + });
  3070 + }
  3071 +
  3072 + pc._localDescription = {
  3073 + type: description.type,
  3074 + sdp: description.sdp
  3075 + };
  3076 + if (description.type === 'offer') {
  3077 + pc._updateSignalingState('have-local-offer');
  3078 + } else {
  3079 + pc._updateSignalingState('stable');
  3080 + }
  3081 +
  3082 + return Promise.resolve();
  3083 + };
  3084 +
  3085 + RTCPeerConnection.prototype.setRemoteDescription = function(description) {
  3086 + var pc = this;
  3087 +
  3088 + // Note: pranswer is not supported.
  3089 + if (['offer', 'answer'].indexOf(description.type) === -1) {
  3090 + return Promise.reject(makeError('TypeError',
  3091 + 'Unsupported type "' + description.type + '"'));
  3092 + }
  3093 +
  3094 + if (!isActionAllowedInSignalingState('setRemoteDescription',
  3095 + description.type, pc.signalingState) || pc._isClosed) {
  3096 + return Promise.reject(makeError('InvalidStateError',
  3097 + 'Can not set remote ' + description.type +
  3098 + ' in state ' + pc.signalingState));
  3099 + }
  3100 +
  3101 + var streams = {};
  3102 + pc.remoteStreams.forEach(function(stream) {
  3103 + streams[stream.id] = stream;
  3104 + });
  3105 + var receiverList = [];
  3106 + var sections = sdp.splitSections(description.sdp);
  3107 + var sessionpart = sections.shift();
  3108 + var isIceLite = sdp.matchPrefix(sessionpart,
  3109 + 'a=ice-lite').length > 0;
  3110 + var usingBundle = sdp.matchPrefix(sessionpart,
  3111 + 'a=group:BUNDLE ').length > 0;
  3112 + pc.usingBundle = usingBundle;
  3113 + var iceOptions = sdp.matchPrefix(sessionpart,
  3114 + 'a=ice-options:')[0];
  3115 + if (iceOptions) {
  3116 + pc.canTrickleIceCandidates = iceOptions.substr(14).split(' ')
  3117 + .indexOf('trickle') >= 0;
  3118 + } else {
  3119 + pc.canTrickleIceCandidates = false;
  3120 + }
  3121 +
  3122 + sections.forEach(function(mediaSection, sdpMLineIndex) {
  3123 + var lines = sdp.splitLines(mediaSection);
  3124 + var kind = sdp.getKind(mediaSection);
  3125 + // treat bundle-only as not-rejected.
  3126 + var rejected = sdp.isRejected(mediaSection) &&
  3127 + sdp.matchPrefix(mediaSection, 'a=bundle-only').length === 0;
  3128 + var protocol = lines[0].substr(2).split(' ')[2];
  3129 +
  3130 + var direction = sdp.getDirection(mediaSection, sessionpart);
  3131 + var remoteMsid = sdp.parseMsid(mediaSection);
  3132 +
  3133 + var mid = sdp.getMid(mediaSection) || sdp.generateIdentifier();
  3134 +
  3135 + // Reject datachannels which are not implemented yet.
  3136 + if (rejected || (kind === 'application' && (protocol === 'DTLS/SCTP' ||
  3137 + protocol === 'UDP/DTLS/SCTP'))) {
  3138 + // TODO: this is dangerous in the case where a non-rejected m-line
  3139 + // becomes rejected.
  3140 + pc.transceivers[sdpMLineIndex] = {
  3141 + mid: mid,
  3142 + kind: kind,
  3143 + protocol: protocol,
  3144 + rejected: true
  3145 + };
  3146 + return;
  3147 + }
  3148 +
  3149 + if (!rejected && pc.transceivers[sdpMLineIndex] &&
  3150 + pc.transceivers[sdpMLineIndex].rejected) {
  3151 + // recycle a rejected transceiver.
  3152 + pc.transceivers[sdpMLineIndex] = pc._createTransceiver(kind, true);
  3153 + }
  3154 +
  3155 + var transceiver;
  3156 + var iceGatherer;
  3157 + var iceTransport;
  3158 + var dtlsTransport;
  3159 + var rtpReceiver;
  3160 + var sendEncodingParameters;
  3161 + var recvEncodingParameters;
  3162 + var localCapabilities;
  3163 +
  3164 + var track;
  3165 + // FIXME: ensure the mediaSection has rtcp-mux set.
  3166 + var remoteCapabilities = sdp.parseRtpParameters(mediaSection);
  3167 + var remoteIceParameters;
  3168 + var remoteDtlsParameters;
  3169 + if (!rejected) {
  3170 + remoteIceParameters = sdp.getIceParameters(mediaSection,
  3171 + sessionpart);
  3172 + remoteDtlsParameters = sdp.getDtlsParameters(mediaSection,
  3173 + sessionpart);
  3174 + remoteDtlsParameters.role = 'client';
  3175 + }
  3176 + recvEncodingParameters =
  3177 + sdp.parseRtpEncodingParameters(mediaSection);
  3178 +
  3179 + var rtcpParameters = sdp.parseRtcpParameters(mediaSection);
  3180 +
  3181 + var isComplete = sdp.matchPrefix(mediaSection,
  3182 + 'a=end-of-candidates', sessionpart).length > 0;
  3183 + var cands = sdp.matchPrefix(mediaSection, 'a=candidate:')
  3184 + .map(function(cand) {
  3185 + return sdp.parseCandidate(cand);
  3186 + })
  3187 + .filter(function(cand) {
  3188 + return cand.component === 1;
  3189 + });
  3190 +
  3191 + // Check if we can use BUNDLE and dispose transports.
  3192 + if ((description.type === 'offer' || description.type === 'answer') &&
  3193 + !rejected && usingBundle && sdpMLineIndex > 0 &&
  3194 + pc.transceivers[sdpMLineIndex]) {
  3195 + pc._disposeIceAndDtlsTransports(sdpMLineIndex);
  3196 + pc.transceivers[sdpMLineIndex].iceGatherer =
  3197 + pc.transceivers[0].iceGatherer;
  3198 + pc.transceivers[sdpMLineIndex].iceTransport =
  3199 + pc.transceivers[0].iceTransport;
  3200 + pc.transceivers[sdpMLineIndex].dtlsTransport =
  3201 + pc.transceivers[0].dtlsTransport;
  3202 + if (pc.transceivers[sdpMLineIndex].rtpSender) {
  3203 + pc.transceivers[sdpMLineIndex].rtpSender.setTransport(
  3204 + pc.transceivers[0].dtlsTransport);
  3205 + }
  3206 + if (pc.transceivers[sdpMLineIndex].rtpReceiver) {
  3207 + pc.transceivers[sdpMLineIndex].rtpReceiver.setTransport(
  3208 + pc.transceivers[0].dtlsTransport);
  3209 + }
  3210 + }
  3211 + if (description.type === 'offer' && !rejected) {
  3212 + transceiver = pc.transceivers[sdpMLineIndex] ||
  3213 + pc._createTransceiver(kind);
  3214 + transceiver.mid = mid;
  3215 +
  3216 + if (!transceiver.iceGatherer) {
  3217 + transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex,
  3218 + usingBundle);
  3219 + }
  3220 +
  3221 + if (cands.length && transceiver.iceTransport.state === 'new') {
  3222 + if (isComplete && (!usingBundle || sdpMLineIndex === 0)) {
  3223 + transceiver.iceTransport.setRemoteCandidates(cands);
  3224 + } else {
  3225 + cands.forEach(function(candidate) {
  3226 + maybeAddCandidate(transceiver.iceTransport, candidate);
  3227 + });
  3228 + }
  3229 + }
  3230 +
  3231 + localCapabilities = window.RTCRtpReceiver.getCapabilities(kind);
  3232 +
  3233 + // filter RTX until additional stuff needed for RTX is implemented
  3234 + // in adapter.js
  3235 + if (edgeVersion < 15019) {
  3236 + localCapabilities.codecs = localCapabilities.codecs.filter(
  3237 + function(codec) {
  3238 + return codec.name !== 'rtx';
  3239 + });
  3240 + }
  3241 +
  3242 + sendEncodingParameters = transceiver.sendEncodingParameters || [{
  3243 + ssrc: (2 * sdpMLineIndex + 2) * 1001
  3244 + }];
  3245 +
  3246 + // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams
  3247 + var isNewTrack = false;
  3248 + if (direction === 'sendrecv' || direction === 'sendonly') {
  3249 + isNewTrack = !transceiver.rtpReceiver;
  3250 + rtpReceiver = transceiver.rtpReceiver ||
  3251 + new window.RTCRtpReceiver(transceiver.dtlsTransport, kind);
  3252 +
  3253 + if (isNewTrack) {
  3254 + var stream;
  3255 + track = rtpReceiver.track;
  3256 + // FIXME: does not work with Plan B.
  3257 + if (remoteMsid && remoteMsid.stream === '-') ; else if (remoteMsid) {
  3258 + if (!streams[remoteMsid.stream]) {
  3259 + streams[remoteMsid.stream] = new window.MediaStream();
  3260 + Object.defineProperty(streams[remoteMsid.stream], 'id', {
  3261 + get: function() {
  3262 + return remoteMsid.stream;
  3263 + }
  3264 + });
  3265 + }
  3266 + Object.defineProperty(track, 'id', {
  3267 + get: function() {
  3268 + return remoteMsid.track;
  3269 + }
  3270 + });
  3271 + stream = streams[remoteMsid.stream];
  3272 + } else {
  3273 + if (!streams.default) {
  3274 + streams.default = new window.MediaStream();
  3275 + }
  3276 + stream = streams.default;
  3277 + }
  3278 + if (stream) {
  3279 + addTrackToStreamAndFireEvent(track, stream);
  3280 + transceiver.associatedRemoteMediaStreams.push(stream);
  3281 + }
  3282 + receiverList.push([track, rtpReceiver, stream]);
  3283 + }
  3284 + } else if (transceiver.rtpReceiver && transceiver.rtpReceiver.track) {
  3285 + transceiver.associatedRemoteMediaStreams.forEach(function(s) {
  3286 + var nativeTrack = s.getTracks().find(function(t) {
  3287 + return t.id === transceiver.rtpReceiver.track.id;
  3288 + });
  3289 + if (nativeTrack) {
  3290 + removeTrackFromStreamAndFireEvent(nativeTrack, s);
  3291 + }
  3292 + });
  3293 + transceiver.associatedRemoteMediaStreams = [];
  3294 + }
  3295 +
  3296 + transceiver.localCapabilities = localCapabilities;
  3297 + transceiver.remoteCapabilities = remoteCapabilities;
  3298 + transceiver.rtpReceiver = rtpReceiver;
  3299 + transceiver.rtcpParameters = rtcpParameters;
  3300 + transceiver.sendEncodingParameters = sendEncodingParameters;
  3301 + transceiver.recvEncodingParameters = recvEncodingParameters;
  3302 +
  3303 + // Start the RTCRtpReceiver now. The RTPSender is started in
  3304 + // setLocalDescription.
  3305 + pc._transceive(pc.transceivers[sdpMLineIndex],
  3306 + false,
  3307 + isNewTrack);
  3308 + } else if (description.type === 'answer' && !rejected) {
  3309 + transceiver = pc.transceivers[sdpMLineIndex];
  3310 + iceGatherer = transceiver.iceGatherer;
  3311 + iceTransport = transceiver.iceTransport;
  3312 + dtlsTransport = transceiver.dtlsTransport;
  3313 + rtpReceiver = transceiver.rtpReceiver;
  3314 + sendEncodingParameters = transceiver.sendEncodingParameters;
  3315 + localCapabilities = transceiver.localCapabilities;
  3316 +
  3317 + pc.transceivers[sdpMLineIndex].recvEncodingParameters =
  3318 + recvEncodingParameters;
  3319 + pc.transceivers[sdpMLineIndex].remoteCapabilities =
  3320 + remoteCapabilities;
  3321 + pc.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters;
  3322 +
  3323 + if (cands.length && iceTransport.state === 'new') {
  3324 + if ((isIceLite || isComplete) &&
  3325 + (!usingBundle || sdpMLineIndex === 0)) {
  3326 + iceTransport.setRemoteCandidates(cands);
  3327 + } else {
  3328 + cands.forEach(function(candidate) {
  3329 + maybeAddCandidate(transceiver.iceTransport, candidate);
  3330 + });
  3331 + }
  3332 + }
  3333 +
  3334 + if (!usingBundle || sdpMLineIndex === 0) {
  3335 + if (iceTransport.state === 'new') {
  3336 + iceTransport.start(iceGatherer, remoteIceParameters,
  3337 + 'controlling');
  3338 + }
  3339 + if (dtlsTransport.state === 'new') {
  3340 + dtlsTransport.start(remoteDtlsParameters);
  3341 + }
  3342 + }
  3343 +
  3344 + // If the offer contained RTX but the answer did not,
  3345 + // remove RTX from sendEncodingParameters.
  3346 + var commonCapabilities = getCommonCapabilities(
  3347 + transceiver.localCapabilities,
  3348 + transceiver.remoteCapabilities);
  3349 +
  3350 + var hasRtx = commonCapabilities.codecs.filter(function(c) {
  3351 + return c.name.toLowerCase() === 'rtx';
  3352 + }).length;
  3353 + if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {
  3354 + delete transceiver.sendEncodingParameters[0].rtx;
  3355 + }
  3356 +
  3357 + pc._transceive(transceiver,
  3358 + direction === 'sendrecv' || direction === 'recvonly',
  3359 + direction === 'sendrecv' || direction === 'sendonly');
  3360 +
  3361 + // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams
  3362 + if (rtpReceiver &&
  3363 + (direction === 'sendrecv' || direction === 'sendonly')) {
  3364 + track = rtpReceiver.track;
  3365 + if (remoteMsid) {
  3366 + if (!streams[remoteMsid.stream]) {
  3367 + streams[remoteMsid.stream] = new window.MediaStream();
  3368 + }
  3369 + addTrackToStreamAndFireEvent(track, streams[remoteMsid.stream]);
  3370 + receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]);
  3371 + } else {
  3372 + if (!streams.default) {
  3373 + streams.default = new window.MediaStream();
  3374 + }
  3375 + addTrackToStreamAndFireEvent(track, streams.default);
  3376 + receiverList.push([track, rtpReceiver, streams.default]);
  3377 + }
  3378 + } else {
  3379 + // FIXME: actually the receiver should be created later.
  3380 + delete transceiver.rtpReceiver;
  3381 + }
  3382 + }
  3383 + });
  3384 +
  3385 + if (pc._dtlsRole === undefined) {
  3386 + pc._dtlsRole = description.type === 'offer' ? 'active' : 'passive';
  3387 + }
  3388 +
  3389 + pc._remoteDescription = {
  3390 + type: description.type,
  3391 + sdp: description.sdp
  3392 + };
  3393 + if (description.type === 'offer') {
  3394 + pc._updateSignalingState('have-remote-offer');
  3395 + } else {
  3396 + pc._updateSignalingState('stable');
  3397 + }
  3398 + Object.keys(streams).forEach(function(sid) {
  3399 + var stream = streams[sid];
  3400 + if (stream.getTracks().length) {
  3401 + if (pc.remoteStreams.indexOf(stream) === -1) {
  3402 + pc.remoteStreams.push(stream);
  3403 + var event = new Event('addstream');
  3404 + event.stream = stream;
  3405 + window.setTimeout(function() {
  3406 + pc._dispatchEvent('addstream', event);
  3407 + });
  3408 + }
  3409 +
  3410 + receiverList.forEach(function(item) {
  3411 + var track = item[0];
  3412 + var receiver = item[1];
  3413 + if (stream.id !== item[2].id) {
  3414 + return;
  3415 + }
  3416 + fireAddTrack(pc, track, receiver, [stream]);
  3417 + });
  3418 + }
  3419 + });
  3420 + receiverList.forEach(function(item) {
  3421 + if (item[2]) {
  3422 + return;
  3423 + }
  3424 + fireAddTrack(pc, item[0], item[1], []);
  3425 + });
  3426 +
  3427 + // check whether addIceCandidate({}) was called within four seconds after
  3428 + // setRemoteDescription.
  3429 + window.setTimeout(function() {
  3430 + if (!(pc && pc.transceivers)) {
  3431 + return;
  3432 + }
  3433 + pc.transceivers.forEach(function(transceiver) {
  3434 + if (transceiver.iceTransport &&
  3435 + transceiver.iceTransport.state === 'new' &&
  3436 + transceiver.iceTransport.getRemoteCandidates().length > 0) {
  3437 + console.warn('Timeout for addRemoteCandidate. Consider sending ' +
  3438 + 'an end-of-candidates notification');
  3439 + transceiver.iceTransport.addRemoteCandidate({});
  3440 + }
  3441 + });
  3442 + }, 4000);
  3443 +
  3444 + return Promise.resolve();
  3445 + };
  3446 +
  3447 + RTCPeerConnection.prototype.close = function() {
  3448 + this.transceivers.forEach(function(transceiver) {
  3449 + /* not yet
  3450 + if (transceiver.iceGatherer) {
  3451 + transceiver.iceGatherer.close();
  3452 + }
  3453 + */
  3454 + if (transceiver.iceTransport) {
  3455 + transceiver.iceTransport.stop();
  3456 + }
  3457 + if (transceiver.dtlsTransport) {
  3458 + transceiver.dtlsTransport.stop();
  3459 + }
  3460 + if (transceiver.rtpSender) {
  3461 + transceiver.rtpSender.stop();
  3462 + }
  3463 + if (transceiver.rtpReceiver) {
  3464 + transceiver.rtpReceiver.stop();
  3465 + }
  3466 + });
  3467 + // FIXME: clean up tracks, local streams, remote streams, etc
  3468 + this._isClosed = true;
  3469 + this._updateSignalingState('closed');
  3470 + };
  3471 +
  3472 + // Update the signaling state.
  3473 + RTCPeerConnection.prototype._updateSignalingState = function(newState) {
  3474 + this.signalingState = newState;
  3475 + var event = new Event('signalingstatechange');
  3476 + this._dispatchEvent('signalingstatechange', event);
  3477 + };
  3478 +
  3479 + // Determine whether to fire the negotiationneeded event.
  3480 + RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() {
  3481 + var pc = this;
  3482 + if (this.signalingState !== 'stable' || this.needNegotiation === true) {
  3483 + return;
  3484 + }
  3485 + this.needNegotiation = true;
  3486 + window.setTimeout(function() {
  3487 + if (pc.needNegotiation) {
  3488 + pc.needNegotiation = false;
  3489 + var event = new Event('negotiationneeded');
  3490 + pc._dispatchEvent('negotiationneeded', event);
  3491 + }
  3492 + }, 0);
  3493 + };
  3494 +
  3495 + // Update the ice connection state.
  3496 + RTCPeerConnection.prototype._updateIceConnectionState = function() {
  3497 + var newState;
  3498 + var states = {
  3499 + 'new': 0,
  3500 + closed: 0,
  3501 + checking: 0,
  3502 + connected: 0,
  3503 + completed: 0,
  3504 + disconnected: 0,
  3505 + failed: 0
  3506 + };
  3507 + this.transceivers.forEach(function(transceiver) {
  3508 + if (transceiver.iceTransport && !transceiver.rejected) {
  3509 + states[transceiver.iceTransport.state]++;
  3510 + }
  3511 + });
  3512 +
  3513 + newState = 'new';
  3514 + if (states.failed > 0) {
  3515 + newState = 'failed';
  3516 + } else if (states.checking > 0) {
  3517 + newState = 'checking';
  3518 + } else if (states.disconnected > 0) {
  3519 + newState = 'disconnected';
  3520 + } else if (states.new > 0) {
  3521 + newState = 'new';
  3522 + } else if (states.connected > 0) {
  3523 + newState = 'connected';
  3524 + } else if (states.completed > 0) {
  3525 + newState = 'completed';
  3526 + }
  3527 +
  3528 + if (newState !== this.iceConnectionState) {
  3529 + this.iceConnectionState = newState;
  3530 + var event = new Event('iceconnectionstatechange');
  3531 + this._dispatchEvent('iceconnectionstatechange', event);
  3532 + }
  3533 + };
  3534 +
  3535 + // Update the connection state.
  3536 + RTCPeerConnection.prototype._updateConnectionState = function() {
  3537 + var newState;
  3538 + var states = {
  3539 + 'new': 0,
  3540 + closed: 0,
  3541 + connecting: 0,
  3542 + connected: 0,
  3543 + completed: 0,
  3544 + disconnected: 0,
  3545 + failed: 0
  3546 + };
  3547 + this.transceivers.forEach(function(transceiver) {
  3548 + if (transceiver.iceTransport && transceiver.dtlsTransport &&
  3549 + !transceiver.rejected) {
  3550 + states[transceiver.iceTransport.state]++;
  3551 + states[transceiver.dtlsTransport.state]++;
  3552 + }
  3553 + });
  3554 + // ICETransport.completed and connected are the same for this purpose.
  3555 + states.connected += states.completed;
  3556 +
  3557 + newState = 'new';
  3558 + if (states.failed > 0) {
  3559 + newState = 'failed';
  3560 + } else if (states.connecting > 0) {
  3561 + newState = 'connecting';
  3562 + } else if (states.disconnected > 0) {
  3563 + newState = 'disconnected';
  3564 + } else if (states.new > 0) {
  3565 + newState = 'new';
  3566 + } else if (states.connected > 0) {
  3567 + newState = 'connected';
  3568 + }
  3569 +
  3570 + if (newState !== this.connectionState) {
  3571 + this.connectionState = newState;
  3572 + var event = new Event('connectionstatechange');
  3573 + this._dispatchEvent('connectionstatechange', event);
  3574 + }
  3575 + };
  3576 +
  3577 + RTCPeerConnection.prototype.createOffer = function() {
  3578 + var pc = this;
  3579 +
  3580 + if (pc._isClosed) {
  3581 + return Promise.reject(makeError('InvalidStateError',
  3582 + 'Can not call createOffer after close'));
  3583 + }
  3584 +
  3585 + var numAudioTracks = pc.transceivers.filter(function(t) {
  3586 + return t.kind === 'audio';
  3587 + }).length;
  3588 + var numVideoTracks = pc.transceivers.filter(function(t) {
  3589 + return t.kind === 'video';
  3590 + }).length;
  3591 +
  3592 + // Determine number of audio and video tracks we need to send/recv.
  3593 + var offerOptions = arguments[0];
  3594 + if (offerOptions) {
  3595 + // Reject Chrome legacy constraints.
  3596 + if (offerOptions.mandatory || offerOptions.optional) {
  3597 + throw new TypeError(
  3598 + 'Legacy mandatory/optional constraints not supported.');
  3599 + }
  3600 + if (offerOptions.offerToReceiveAudio !== undefined) {
  3601 + if (offerOptions.offerToReceiveAudio === true) {
  3602 + numAudioTracks = 1;
  3603 + } else if (offerOptions.offerToReceiveAudio === false) {
  3604 + numAudioTracks = 0;
  3605 + } else {
  3606 + numAudioTracks = offerOptions.offerToReceiveAudio;
  3607 + }
  3608 + }
  3609 + if (offerOptions.offerToReceiveVideo !== undefined) {
  3610 + if (offerOptions.offerToReceiveVideo === true) {
  3611 + numVideoTracks = 1;
  3612 + } else if (offerOptions.offerToReceiveVideo === false) {
  3613 + numVideoTracks = 0;
  3614 + } else {
  3615 + numVideoTracks = offerOptions.offerToReceiveVideo;
  3616 + }
  3617 + }
  3618 + }
  3619 +
  3620 + pc.transceivers.forEach(function(transceiver) {
  3621 + if (transceiver.kind === 'audio') {
  3622 + numAudioTracks--;
  3623 + if (numAudioTracks < 0) {
  3624 + transceiver.wantReceive = false;
  3625 + }
  3626 + } else if (transceiver.kind === 'video') {
  3627 + numVideoTracks--;
  3628 + if (numVideoTracks < 0) {
  3629 + transceiver.wantReceive = false;
  3630 + }
  3631 + }
  3632 + });
  3633 +
  3634 + // Create M-lines for recvonly streams.
  3635 + while (numAudioTracks > 0 || numVideoTracks > 0) {
  3636 + if (numAudioTracks > 0) {
  3637 + pc._createTransceiver('audio');
  3638 + numAudioTracks--;
  3639 + }
  3640 + if (numVideoTracks > 0) {
  3641 + pc._createTransceiver('video');
  3642 + numVideoTracks--;
  3643 + }
  3644 + }
  3645 +
  3646 + var sdp$1 = sdp.writeSessionBoilerplate(pc._sdpSessionId,
  3647 + pc._sdpSessionVersion++);
  3648 + pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {
  3649 + // For each track, create an ice gatherer, ice transport,
  3650 + // dtls transport, potentially rtpsender and rtpreceiver.
  3651 + var track = transceiver.track;
  3652 + var kind = transceiver.kind;
  3653 + var mid = transceiver.mid || sdp.generateIdentifier();
  3654 + transceiver.mid = mid;
  3655 +
  3656 + if (!transceiver.iceGatherer) {
  3657 + transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex,
  3658 + pc.usingBundle);
  3659 + }
  3660 +
  3661 + var localCapabilities = window.RTCRtpSender.getCapabilities(kind);
  3662 + // filter RTX until additional stuff needed for RTX is implemented
  3663 + // in adapter.js
  3664 + if (edgeVersion < 15019) {
  3665 + localCapabilities.codecs = localCapabilities.codecs.filter(
  3666 + function(codec) {
  3667 + return codec.name !== 'rtx';
  3668 + });
  3669 + }
  3670 + localCapabilities.codecs.forEach(function(codec) {
  3671 + // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552
  3672 + // by adding level-asymmetry-allowed=1
  3673 + if (codec.name === 'H264' &&
  3674 + codec.parameters['level-asymmetry-allowed'] === undefined) {
  3675 + codec.parameters['level-asymmetry-allowed'] = '1';
  3676 + }
  3677 +
  3678 + // for subsequent offers, we might have to re-use the payload
  3679 + // type of the last offer.
  3680 + if (transceiver.remoteCapabilities &&
  3681 + transceiver.remoteCapabilities.codecs) {
  3682 + transceiver.remoteCapabilities.codecs.forEach(function(remoteCodec) {
  3683 + if (codec.name.toLowerCase() === remoteCodec.name.toLowerCase() &&
  3684 + codec.clockRate === remoteCodec.clockRate) {
  3685 + codec.preferredPayloadType = remoteCodec.payloadType;
  3686 + }
  3687 + });
  3688 + }
  3689 + });
  3690 + localCapabilities.headerExtensions.forEach(function(hdrExt) {
  3691 + var remoteExtensions = transceiver.remoteCapabilities &&
  3692 + transceiver.remoteCapabilities.headerExtensions || [];
  3693 + remoteExtensions.forEach(function(rHdrExt) {
  3694 + if (hdrExt.uri === rHdrExt.uri) {
  3695 + hdrExt.id = rHdrExt.id;
  3696 + }
  3697 + });
  3698 + });
  3699 +
  3700 + // generate an ssrc now, to be used later in rtpSender.send
  3701 + var sendEncodingParameters = transceiver.sendEncodingParameters || [{
  3702 + ssrc: (2 * sdpMLineIndex + 1) * 1001
  3703 + }];
  3704 + if (track) {
  3705 + // add RTX
  3706 + if (edgeVersion >= 15019 && kind === 'video' &&
  3707 + !sendEncodingParameters[0].rtx) {
  3708 + sendEncodingParameters[0].rtx = {
  3709 + ssrc: sendEncodingParameters[0].ssrc + 1
  3710 + };
  3711 + }
  3712 + }
  3713 +
  3714 + if (transceiver.wantReceive) {
  3715 + transceiver.rtpReceiver = new window.RTCRtpReceiver(
  3716 + transceiver.dtlsTransport, kind);
  3717 + }
  3718 +
  3719 + transceiver.localCapabilities = localCapabilities;
  3720 + transceiver.sendEncodingParameters = sendEncodingParameters;
  3721 + });
  3722 +
  3723 + // always offer BUNDLE and dispose on return if not supported.
  3724 + if (pc._config.bundlePolicy !== 'max-compat') {
  3725 + sdp$1 += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) {
  3726 + return t.mid;
  3727 + }).join(' ') + '\r\n';
  3728 + }
  3729 + sdp$1 += 'a=ice-options:trickle\r\n';
  3730 +
  3731 + pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {
  3732 + sdp$1 += writeMediaSection(transceiver, transceiver.localCapabilities,
  3733 + 'offer', transceiver.stream, pc._dtlsRole);
  3734 + sdp$1 += 'a=rtcp-rsize\r\n';
  3735 +
  3736 + if (transceiver.iceGatherer && pc.iceGatheringState !== 'new' &&
  3737 + (sdpMLineIndex === 0 || !pc.usingBundle)) {
  3738 + transceiver.iceGatherer.getLocalCandidates().forEach(function(cand) {
  3739 + cand.component = 1;
  3740 + sdp$1 += 'a=' + sdp.writeCandidate(cand) + '\r\n';
  3741 + });
  3742 +
  3743 + if (transceiver.iceGatherer.state === 'completed') {
  3744 + sdp$1 += 'a=end-of-candidates\r\n';
  3745 + }
  3746 + }
  3747 + });
  3748 +
  3749 + var desc = new window.RTCSessionDescription({
  3750 + type: 'offer',
  3751 + sdp: sdp$1
  3752 + });
  3753 + return Promise.resolve(desc);
  3754 + };
  3755 +
  3756 + RTCPeerConnection.prototype.createAnswer = function() {
  3757 + var pc = this;
  3758 +
  3759 + if (pc._isClosed) {
  3760 + return Promise.reject(makeError('InvalidStateError',
  3761 + 'Can not call createAnswer after close'));
  3762 + }
  3763 +
  3764 + if (!(pc.signalingState === 'have-remote-offer' ||
  3765 + pc.signalingState === 'have-local-pranswer')) {
  3766 + return Promise.reject(makeError('InvalidStateError',
  3767 + 'Can not call createAnswer in signalingState ' + pc.signalingState));
  3768 + }
  3769 +
  3770 + var sdp$1 = sdp.writeSessionBoilerplate(pc._sdpSessionId,
  3771 + pc._sdpSessionVersion++);
  3772 + if (pc.usingBundle) {
  3773 + sdp$1 += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) {
  3774 + return t.mid;
  3775 + }).join(' ') + '\r\n';
  3776 + }
  3777 + sdp$1 += 'a=ice-options:trickle\r\n';
  3778 +
  3779 + var mediaSectionsInOffer = sdp.getMediaSections(
  3780 + pc._remoteDescription.sdp).length;
  3781 + pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {
  3782 + if (sdpMLineIndex + 1 > mediaSectionsInOffer) {
  3783 + return;
  3784 + }
  3785 + if (transceiver.rejected) {
  3786 + if (transceiver.kind === 'application') {
  3787 + if (transceiver.protocol === 'DTLS/SCTP') { // legacy fmt
  3788 + sdp$1 += 'm=application 0 DTLS/SCTP 5000\r\n';
  3789 + } else {
  3790 + sdp$1 += 'm=application 0 ' + transceiver.protocol +
  3791 + ' webrtc-datachannel\r\n';
  3792 + }
  3793 + } else if (transceiver.kind === 'audio') {
  3794 + sdp$1 += 'm=audio 0 UDP/TLS/RTP/SAVPF 0\r\n' +
  3795 + 'a=rtpmap:0 PCMU/8000\r\n';
  3796 + } else if (transceiver.kind === 'video') {
  3797 + sdp$1 += 'm=video 0 UDP/TLS/RTP/SAVPF 120\r\n' +
  3798 + 'a=rtpmap:120 VP8/90000\r\n';
  3799 + }
  3800 + sdp$1 += 'c=IN IP4 0.0.0.0\r\n' +
  3801 + 'a=inactive\r\n' +
  3802 + 'a=mid:' + transceiver.mid + '\r\n';
  3803 + return;
  3804 + }
  3805 +
  3806 + // FIXME: look at direction.
  3807 + if (transceiver.stream) {
  3808 + var localTrack;
  3809 + if (transceiver.kind === 'audio') {
  3810 + localTrack = transceiver.stream.getAudioTracks()[0];
  3811 + } else if (transceiver.kind === 'video') {
  3812 + localTrack = transceiver.stream.getVideoTracks()[0];
  3813 + }
  3814 + if (localTrack) {
  3815 + // add RTX
  3816 + if (edgeVersion >= 15019 && transceiver.kind === 'video' &&
  3817 + !transceiver.sendEncodingParameters[0].rtx) {
  3818 + transceiver.sendEncodingParameters[0].rtx = {
  3819 + ssrc: transceiver.sendEncodingParameters[0].ssrc + 1
  3820 + };
  3821 + }
  3822 + }
  3823 + }
  3824 +
  3825 + // Calculate intersection of capabilities.
  3826 + var commonCapabilities = getCommonCapabilities(
  3827 + transceiver.localCapabilities,
  3828 + transceiver.remoteCapabilities);
  3829 +
  3830 + var hasRtx = commonCapabilities.codecs.filter(function(c) {
  3831 + return c.name.toLowerCase() === 'rtx';
  3832 + }).length;
  3833 + if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {
  3834 + delete transceiver.sendEncodingParameters[0].rtx;
  3835 + }
  3836 +
  3837 + sdp$1 += writeMediaSection(transceiver, commonCapabilities,
  3838 + 'answer', transceiver.stream, pc._dtlsRole);
  3839 + if (transceiver.rtcpParameters &&
  3840 + transceiver.rtcpParameters.reducedSize) {
  3841 + sdp$1 += 'a=rtcp-rsize\r\n';
  3842 + }
  3843 + });
  3844 +
  3845 + var desc = new window.RTCSessionDescription({
  3846 + type: 'answer',
  3847 + sdp: sdp$1
  3848 + });
  3849 + return Promise.resolve(desc);
  3850 + };
  3851 +
  3852 + RTCPeerConnection.prototype.addIceCandidate = function(candidate) {
  3853 + var pc = this;
  3854 + var sections;
  3855 + if (candidate && !(candidate.sdpMLineIndex !== undefined ||
  3856 + candidate.sdpMid)) {
  3857 + return Promise.reject(new TypeError('sdpMLineIndex or sdpMid required'));
  3858 + }
  3859 +
  3860 + // TODO: needs to go into ops queue.
  3861 + return new Promise(function(resolve, reject) {
  3862 + if (!pc._remoteDescription) {
  3863 + return reject(makeError('InvalidStateError',
  3864 + 'Can not add ICE candidate without a remote description'));
  3865 + } else if (!candidate || candidate.candidate === '') {
  3866 + for (var j = 0; j < pc.transceivers.length; j++) {
  3867 + if (pc.transceivers[j].rejected) {
  3868 + continue;
  3869 + }
  3870 + pc.transceivers[j].iceTransport.addRemoteCandidate({});
  3871 + sections = sdp.getMediaSections(pc._remoteDescription.sdp);
  3872 + sections[j] += 'a=end-of-candidates\r\n';
  3873 + pc._remoteDescription.sdp =
  3874 + sdp.getDescription(pc._remoteDescription.sdp) +
  3875 + sections.join('');
  3876 + if (pc.usingBundle) {
  3877 + break;
  3878 + }
  3879 + }
  3880 + } else {
  3881 + var sdpMLineIndex = candidate.sdpMLineIndex;
  3882 + if (candidate.sdpMid) {
  3883 + for (var i = 0; i < pc.transceivers.length; i++) {
  3884 + if (pc.transceivers[i].mid === candidate.sdpMid) {
  3885 + sdpMLineIndex = i;
  3886 + break;
  3887 + }
  3888 + }
  3889 + }
  3890 + var transceiver = pc.transceivers[sdpMLineIndex];
  3891 + if (transceiver) {
  3892 + if (transceiver.rejected) {
  3893 + return resolve();
  3894 + }
  3895 + var cand = Object.keys(candidate.candidate).length > 0 ?
  3896 + sdp.parseCandidate(candidate.candidate) : {};
  3897 + // Ignore Chrome's invalid candidates since Edge does not like them.
  3898 + if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {
  3899 + return resolve();
  3900 + }
  3901 + // Ignore RTCP candidates, we assume RTCP-MUX.
  3902 + if (cand.component && cand.component !== 1) {
  3903 + return resolve();
  3904 + }
  3905 + // when using bundle, avoid adding candidates to the wrong
  3906 + // ice transport. And avoid adding candidates added in the SDP.
  3907 + if (sdpMLineIndex === 0 || (sdpMLineIndex > 0 &&
  3908 + transceiver.iceTransport !== pc.transceivers[0].iceTransport)) {
  3909 + if (!maybeAddCandidate(transceiver.iceTransport, cand)) {
  3910 + return reject(makeError('OperationError',
  3911 + 'Can not add ICE candidate'));
  3912 + }
  3913 + }
  3914 +
  3915 + // update the remoteDescription.
  3916 + var candidateString = candidate.candidate.trim();
  3917 + if (candidateString.indexOf('a=') === 0) {
  3918 + candidateString = candidateString.substr(2);
  3919 + }
  3920 + sections = sdp.getMediaSections(pc._remoteDescription.sdp);
  3921 + sections[sdpMLineIndex] += 'a=' +
  3922 + (cand.type ? candidateString : 'end-of-candidates')
  3923 + + '\r\n';
  3924 + pc._remoteDescription.sdp =
  3925 + sdp.getDescription(pc._remoteDescription.sdp) +
  3926 + sections.join('');
  3927 + } else {
  3928 + return reject(makeError('OperationError',
  3929 + 'Can not add ICE candidate'));
  3930 + }
  3931 + }
  3932 + resolve();
  3933 + });
  3934 + };
  3935 +
  3936 + RTCPeerConnection.prototype.getStats = function(selector) {
  3937 + if (selector && selector instanceof window.MediaStreamTrack) {
  3938 + var senderOrReceiver = null;
  3939 + this.transceivers.forEach(function(transceiver) {
  3940 + if (transceiver.rtpSender &&
  3941 + transceiver.rtpSender.track === selector) {
  3942 + senderOrReceiver = transceiver.rtpSender;
  3943 + } else if (transceiver.rtpReceiver &&
  3944 + transceiver.rtpReceiver.track === selector) {
  3945 + senderOrReceiver = transceiver.rtpReceiver;
  3946 + }
  3947 + });
  3948 + if (!senderOrReceiver) {
  3949 + throw makeError('InvalidAccessError', 'Invalid selector.');
  3950 + }
  3951 + return senderOrReceiver.getStats();
  3952 + }
  3953 +
  3954 + var promises = [];
  3955 + this.transceivers.forEach(function(transceiver) {
  3956 + ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',
  3957 + 'dtlsTransport'].forEach(function(method) {
  3958 + if (transceiver[method]) {
  3959 + promises.push(transceiver[method].getStats());
  3960 + }
  3961 + });
  3962 + });
  3963 + return Promise.all(promises).then(function(allStats) {
  3964 + var results = new Map();
  3965 + allStats.forEach(function(stats) {
  3966 + stats.forEach(function(stat) {
  3967 + results.set(stat.id, stat);
  3968 + });
  3969 + });
  3970 + return results;
  3971 + });
  3972 + };
  3973 +
  3974 + // fix low-level stat names and return Map instead of object.
  3975 + var ortcObjects = ['RTCRtpSender', 'RTCRtpReceiver', 'RTCIceGatherer',
  3976 + 'RTCIceTransport', 'RTCDtlsTransport'];
  3977 + ortcObjects.forEach(function(ortcObjectName) {
  3978 + var obj = window[ortcObjectName];
  3979 + if (obj && obj.prototype && obj.prototype.getStats) {
  3980 + var nativeGetstats = obj.prototype.getStats;
  3981 + obj.prototype.getStats = function() {
  3982 + return nativeGetstats.apply(this)
  3983 + .then(function(nativeStats) {
  3984 + var mapStats = new Map();
  3985 + Object.keys(nativeStats).forEach(function(id) {
  3986 + nativeStats[id].type = fixStatsType(nativeStats[id]);
  3987 + mapStats.set(id, nativeStats[id]);
  3988 + });
  3989 + return mapStats;
  3990 + });
  3991 + };
  3992 + }
  3993 + });
  3994 +
  3995 + // legacy callback shims. Should be moved to adapter.js some days.
  3996 + var methods = ['createOffer', 'createAnswer'];
  3997 + methods.forEach(function(method) {
  3998 + var nativeMethod = RTCPeerConnection.prototype[method];
  3999 + RTCPeerConnection.prototype[method] = function() {
  4000 + var args = arguments;
  4001 + if (typeof args[0] === 'function' ||
  4002 + typeof args[1] === 'function') { // legacy
  4003 + return nativeMethod.apply(this, [arguments[2]])
  4004 + .then(function(description) {
  4005 + if (typeof args[0] === 'function') {
  4006 + args[0].apply(null, [description]);
  4007 + }
  4008 + }, function(error) {
  4009 + if (typeof args[1] === 'function') {
  4010 + args[1].apply(null, [error]);
  4011 + }
  4012 + });
  4013 + }
  4014 + return nativeMethod.apply(this, arguments);
  4015 + };
  4016 + });
  4017 +
  4018 + methods = ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'];
  4019 + methods.forEach(function(method) {
  4020 + var nativeMethod = RTCPeerConnection.prototype[method];
  4021 + RTCPeerConnection.prototype[method] = function() {
  4022 + var args = arguments;
  4023 + if (typeof args[1] === 'function' ||
  4024 + typeof args[2] === 'function') { // legacy
  4025 + return nativeMethod.apply(this, arguments)
  4026 + .then(function() {
  4027 + if (typeof args[1] === 'function') {
  4028 + args[1].apply(null);
  4029 + }
  4030 + }, function(error) {
  4031 + if (typeof args[2] === 'function') {
  4032 + args[2].apply(null, [error]);
  4033 + }
  4034 + });
  4035 + }
  4036 + return nativeMethod.apply(this, arguments);
  4037 + };
  4038 + });
  4039 +
  4040 + // getStats is special. It doesn't have a spec legacy method yet we support
  4041 + // getStats(something, cb) without error callbacks.
  4042 + ['getStats'].forEach(function(method) {
  4043 + var nativeMethod = RTCPeerConnection.prototype[method];
  4044 + RTCPeerConnection.prototype[method] = function() {
  4045 + var args = arguments;
  4046 + if (typeof args[1] === 'function') {
  4047 + return nativeMethod.apply(this, arguments)
  4048 + .then(function() {
  4049 + if (typeof args[1] === 'function') {
  4050 + args[1].apply(null);
  4051 + }
  4052 + });
  4053 + }
  4054 + return nativeMethod.apply(this, arguments);
  4055 + };
  4056 + });
  4057 +
  4058 + return RTCPeerConnection;
  4059 + };
  4060 +
  4061 + /*
  4062 + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  4063 + *
  4064 + * Use of this source code is governed by a BSD-style license
  4065 + * that can be found in the LICENSE file in the root of the source
  4066 + * tree.
  4067 + */
  4068 +
  4069 + function shimGetUserMedia$2(window) {
  4070 + const navigator = window && window.navigator;
  4071 +
  4072 + const shimError_ = function(e) {
  4073 + return {
  4074 + name: {PermissionDeniedError: 'NotAllowedError'}[e.name] || e.name,
  4075 + message: e.message,
  4076 + constraint: e.constraint,
  4077 + toString() {
  4078 + return this.name;
  4079 + }
  4080 + };
  4081 + };
  4082 +
  4083 + // getUserMedia error shim.
  4084 + const origGetUserMedia = navigator.mediaDevices.getUserMedia.
  4085 + bind(navigator.mediaDevices);
  4086 + navigator.mediaDevices.getUserMedia = function(c) {
  4087 + return origGetUserMedia(c).catch(e => Promise.reject(shimError_(e)));
  4088 + };
  4089 + }
  4090 +
  4091 + /*
  4092 + * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
  4093 + *
  4094 + * Use of this source code is governed by a BSD-style license
  4095 + * that can be found in the LICENSE file in the root of the source
  4096 + * tree.
  4097 + */
  4098 +
  4099 + function shimGetDisplayMedia$1(window) {
  4100 + if (!('getDisplayMedia' in window.navigator)) {
  4101 + return;
  4102 + }
  4103 + if (!(window.navigator.mediaDevices)) {
  4104 + return;
  4105 + }
  4106 + if (window.navigator.mediaDevices &&
  4107 + 'getDisplayMedia' in window.navigator.mediaDevices) {
  4108 + return;
  4109 + }
  4110 + window.navigator.mediaDevices.getDisplayMedia =
  4111 + window.navigator.getDisplayMedia.bind(window.navigator);
  4112 + }
  4113 +
  4114 + /*
  4115 + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  4116 + *
  4117 + * Use of this source code is governed by a BSD-style license
  4118 + * that can be found in the LICENSE file in the root of the source
  4119 + * tree.
  4120 + */
  4121 +
  4122 + function shimPeerConnection$1(window, browserDetails) {
  4123 + if (window.RTCIceGatherer) {
  4124 + if (!window.RTCIceCandidate) {
  4125 + window.RTCIceCandidate = function RTCIceCandidate(args) {
  4126 + return args;
  4127 + };
  4128 + }
  4129 + if (!window.RTCSessionDescription) {
  4130 + window.RTCSessionDescription = function RTCSessionDescription(args) {
  4131 + return args;
  4132 + };
  4133 + }
  4134 + // this adds an additional event listener to MediaStrackTrack that signals
  4135 + // when a tracks enabled property was changed. Workaround for a bug in
  4136 + // addStream, see below. No longer required in 15025+
  4137 + if (browserDetails.version < 15025) {
  4138 + const origMSTEnabled = Object.getOwnPropertyDescriptor(
  4139 + window.MediaStreamTrack.prototype, 'enabled');
  4140 + Object.defineProperty(window.MediaStreamTrack.prototype, 'enabled', {
  4141 + set(value) {
  4142 + origMSTEnabled.set.call(this, value);
  4143 + const ev = new Event('enabled');
  4144 + ev.enabled = value;
  4145 + this.dispatchEvent(ev);
  4146 + }
  4147 + });
  4148 + }
  4149 + }
  4150 +
  4151 + // ORTC defines the DTMF sender a bit different.
  4152 + // https://github.com/w3c/ortc/issues/714
  4153 + if (window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) {
  4154 + Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
  4155 + get() {
  4156 + if (this._dtmf === undefined) {
  4157 + if (this.track.kind === 'audio') {
  4158 + this._dtmf = new window.RTCDtmfSender(this);
  4159 + } else if (this.track.kind === 'video') {
  4160 + this._dtmf = null;
  4161 + }
  4162 + }
  4163 + return this._dtmf;
  4164 + }
  4165 + });
  4166 + }
  4167 + // Edge currently only implements the RTCDtmfSender, not the
  4168 + // RTCDTMFSender alias. See http://draft.ortc.org/#rtcdtmfsender2*
  4169 + if (window.RTCDtmfSender && !window.RTCDTMFSender) {
  4170 + window.RTCDTMFSender = window.RTCDtmfSender;
  4171 + }
  4172 +
  4173 + const RTCPeerConnectionShim = rtcpeerconnection(window,
  4174 + browserDetails.version);
  4175 + window.RTCPeerConnection = function RTCPeerConnection(config) {
  4176 + if (config && config.iceServers) {
  4177 + config.iceServers = filterIceServers$1(config.iceServers,
  4178 + browserDetails.version);
  4179 + log$1('ICE servers after filtering:', config.iceServers);
  4180 + }
  4181 + return new RTCPeerConnectionShim(config);
  4182 + };
  4183 + window.RTCPeerConnection.prototype = RTCPeerConnectionShim.prototype;
  4184 + }
  4185 +
  4186 + function shimReplaceTrack(window) {
  4187 + // ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614
  4188 + if (window.RTCRtpSender &&
  4189 + !('replaceTrack' in window.RTCRtpSender.prototype)) {
  4190 + window.RTCRtpSender.prototype.replaceTrack =
  4191 + window.RTCRtpSender.prototype.setTrack;
  4192 + }
  4193 + }
  4194 +
  4195 + var edgeShim = /*#__PURE__*/Object.freeze({
  4196 + __proto__: null,
  4197 + shimPeerConnection: shimPeerConnection$1,
  4198 + shimReplaceTrack: shimReplaceTrack,
  4199 + shimGetUserMedia: shimGetUserMedia$2,
  4200 + shimGetDisplayMedia: shimGetDisplayMedia$1
  4201 + });
  4202 +
  4203 + /*
  4204 + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  4205 + *
  4206 + * Use of this source code is governed by a BSD-style license
  4207 + * that can be found in the LICENSE file in the root of the source
  4208 + * tree.
  4209 + */
  4210 +
  4211 + function shimGetUserMedia$1(window, browserDetails) {
  4212 + const navigator = window && window.navigator;
  4213 + const MediaStreamTrack = window && window.MediaStreamTrack;
  4214 +
  4215 + navigator.getUserMedia = function(constraints, onSuccess, onError) {
  4216 + // Replace Firefox 44+'s deprecation warning with unprefixed version.
  4217 + deprecated('navigator.getUserMedia',
  4218 + 'navigator.mediaDevices.getUserMedia');
  4219 + navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
  4220 + };
  4221 +
  4222 + if (!(browserDetails.version > 55 &&
  4223 + 'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {
  4224 + const remap = function(obj, a, b) {
  4225 + if (a in obj && !(b in obj)) {
  4226 + obj[b] = obj[a];
  4227 + delete obj[a];
  4228 + }
  4229 + };
  4230 +
  4231 + const nativeGetUserMedia = navigator.mediaDevices.getUserMedia.
  4232 + bind(navigator.mediaDevices);
  4233 + navigator.mediaDevices.getUserMedia = function(c) {
  4234 + if (typeof c === 'object' && typeof c.audio === 'object') {
  4235 + c = JSON.parse(JSON.stringify(c));
  4236 + remap(c.audio, 'autoGainControl', 'mozAutoGainControl');
  4237 + remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');
  4238 + }
  4239 + return nativeGetUserMedia(c);
  4240 + };
  4241 +
  4242 + if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {
  4243 + const nativeGetSettings = MediaStreamTrack.prototype.getSettings;
  4244 + MediaStreamTrack.prototype.getSettings = function() {
  4245 + const obj = nativeGetSettings.apply(this, arguments);
  4246 + remap(obj, 'mozAutoGainControl', 'autoGainControl');
  4247 + remap(obj, 'mozNoiseSuppression', 'noiseSuppression');
  4248 + return obj;
  4249 + };
  4250 + }
  4251 +
  4252 + if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {
  4253 + const nativeApplyConstraints =
  4254 + MediaStreamTrack.prototype.applyConstraints;
  4255 + MediaStreamTrack.prototype.applyConstraints = function(c) {
  4256 + if (this.kind === 'audio' && typeof c === 'object') {
  4257 + c = JSON.parse(JSON.stringify(c));
  4258 + remap(c, 'autoGainControl', 'mozAutoGainControl');
  4259 + remap(c, 'noiseSuppression', 'mozNoiseSuppression');
  4260 + }
  4261 + return nativeApplyConstraints.apply(this, [c]);
  4262 + };
  4263 + }
  4264 + }
  4265 + }
  4266 +
  4267 + /*
  4268 + * Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
  4269 + *
  4270 + * Use of this source code is governed by a BSD-style license
  4271 + * that can be found in the LICENSE file in the root of the source
  4272 + * tree.
  4273 + */
  4274 +
  4275 + function shimGetDisplayMedia(window, preferredMediaSource) {
  4276 + if (window.navigator.mediaDevices &&
  4277 + 'getDisplayMedia' in window.navigator.mediaDevices) {
  4278 + return;
  4279 + }
  4280 + if (!(window.navigator.mediaDevices)) {
  4281 + return;
  4282 + }
  4283 + window.navigator.mediaDevices.getDisplayMedia =
  4284 + function getDisplayMedia(constraints) {
  4285 + if (!(constraints && constraints.video)) {
  4286 + const err = new DOMException('getDisplayMedia without video ' +
  4287 + 'constraints is undefined');
  4288 + err.name = 'NotFoundError';
  4289 + // from https://heycam.github.io/webidl/#idl-DOMException-error-names
  4290 + err.code = 8;
  4291 + return Promise.reject(err);
  4292 + }
  4293 + if (constraints.video === true) {
  4294 + constraints.video = {mediaSource: preferredMediaSource};
  4295 + } else {
  4296 + constraints.video.mediaSource = preferredMediaSource;
  4297 + }
  4298 + return window.navigator.mediaDevices.getUserMedia(constraints);
  4299 + };
  4300 + }
  4301 +
  4302 + /*
  4303 + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  4304 + *
  4305 + * Use of this source code is governed by a BSD-style license
  4306 + * that can be found in the LICENSE file in the root of the source
  4307 + * tree.
  4308 + */
  4309 +
  4310 + function shimOnTrack(window) {
  4311 + if (typeof window === 'object' && window.RTCTrackEvent &&
  4312 + ('receiver' in window.RTCTrackEvent.prototype) &&
  4313 + !('transceiver' in window.RTCTrackEvent.prototype)) {
  4314 + Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
  4315 + get() {
  4316 + return {receiver: this.receiver};
  4317 + }
  4318 + });
  4319 + }
  4320 + }
  4321 +
  4322 + function shimPeerConnection(window, browserDetails) {
  4323 + if (typeof window !== 'object' ||
  4324 + !(window.RTCPeerConnection || window.mozRTCPeerConnection)) {
  4325 + return; // probably media.peerconnection.enabled=false in about:config
  4326 + }
  4327 + if (!window.RTCPeerConnection && window.mozRTCPeerConnection) {
  4328 + // very basic support for old versions.
  4329 + window.RTCPeerConnection = window.mozRTCPeerConnection;
  4330 + }
  4331 +
  4332 + if (browserDetails.version < 53) {
  4333 + // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
  4334 + ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
  4335 + .forEach(function(method) {
  4336 + const nativeMethod = window.RTCPeerConnection.prototype[method];
  4337 + const methodObj = {[method]() {
  4338 + arguments[0] = new ((method === 'addIceCandidate') ?
  4339 + window.RTCIceCandidate :
  4340 + window.RTCSessionDescription)(arguments[0]);
  4341 + return nativeMethod.apply(this, arguments);
  4342 + }};
  4343 + window.RTCPeerConnection.prototype[method] = methodObj[method];
  4344 + });
  4345 + }
  4346 +
  4347 + const modernStatsTypes = {
  4348 + inboundrtp: 'inbound-rtp',
  4349 + outboundrtp: 'outbound-rtp',
  4350 + candidatepair: 'candidate-pair',
  4351 + localcandidate: 'local-candidate',
  4352 + remotecandidate: 'remote-candidate'
  4353 + };
  4354 +
  4355 + const nativeGetStats = window.RTCPeerConnection.prototype.getStats;
  4356 + window.RTCPeerConnection.prototype.getStats = function getStats() {
  4357 + const [selector, onSucc, onErr] = arguments;
  4358 + return nativeGetStats.apply(this, [selector || null])
  4359 + .then(stats => {
  4360 + if (browserDetails.version < 53 && !onSucc) {
  4361 + // Shim only promise getStats with spec-hyphens in type names
  4362 + // Leave callback version alone; misc old uses of forEach before Map
  4363 + try {
  4364 + stats.forEach(stat => {
  4365 + stat.type = modernStatsTypes[stat.type] || stat.type;
  4366 + });
  4367 + } catch (e) {
  4368 + if (e.name !== 'TypeError') {
  4369 + throw e;
  4370 + }
  4371 + // Avoid TypeError: "type" is read-only, in old versions. 34-43ish
  4372 + stats.forEach((stat, i) => {
  4373 + stats.set(i, Object.assign({}, stat, {
  4374 + type: modernStatsTypes[stat.type] || stat.type
  4375 + }));
  4376 + });
  4377 + }
  4378 + }
  4379 + return stats;
  4380 + })
  4381 + .then(onSucc, onErr);
  4382 + };
  4383 + }
  4384 +
  4385 + function shimSenderGetStats(window) {
  4386 + if (!(typeof window === 'object' && window.RTCPeerConnection &&
  4387 + window.RTCRtpSender)) {
  4388 + return;
  4389 + }
  4390 + if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) {
  4391 + return;
  4392 + }
  4393 + const origGetSenders = window.RTCPeerConnection.prototype.getSenders;
  4394 + if (origGetSenders) {
  4395 + window.RTCPeerConnection.prototype.getSenders = function getSenders() {
  4396 + const senders = origGetSenders.apply(this, []);
  4397 + senders.forEach(sender => sender._pc = this);
  4398 + return senders;
  4399 + };
  4400 + }
  4401 +
  4402 + const origAddTrack = window.RTCPeerConnection.prototype.addTrack;
  4403 + if (origAddTrack) {
  4404 + window.RTCPeerConnection.prototype.addTrack = function addTrack() {
  4405 + const sender = origAddTrack.apply(this, arguments);
  4406 + sender._pc = this;
  4407 + return sender;
  4408 + };
  4409 + }
  4410 + window.RTCRtpSender.prototype.getStats = function getStats() {
  4411 + return this.track ? this._pc.getStats(this.track) :
  4412 + Promise.resolve(new Map());
  4413 + };
  4414 + }
  4415 +
  4416 + function shimReceiverGetStats(window) {
  4417 + if (!(typeof window === 'object' && window.RTCPeerConnection &&
  4418 + window.RTCRtpSender)) {
  4419 + return;
  4420 + }
  4421 + if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) {
  4422 + return;
  4423 + }
  4424 + const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
  4425 + if (origGetReceivers) {
  4426 + window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {
  4427 + const receivers = origGetReceivers.apply(this, []);
  4428 + receivers.forEach(receiver => receiver._pc = this);
  4429 + return receivers;
  4430 + };
  4431 + }
  4432 + wrapPeerConnectionEvent(window, 'track', e => {
  4433 + e.receiver._pc = e.srcElement;
  4434 + return e;
  4435 + });
  4436 + window.RTCRtpReceiver.prototype.getStats = function getStats() {
  4437 + return this._pc.getStats(this.track);
  4438 + };
  4439 + }
  4440 +
  4441 + function shimRemoveStream(window) {
  4442 + if (!window.RTCPeerConnection ||
  4443 + 'removeStream' in window.RTCPeerConnection.prototype) {
  4444 + return;
  4445 + }
  4446 + window.RTCPeerConnection.prototype.removeStream =
  4447 + function removeStream(stream) {
  4448 + deprecated('removeStream', 'removeTrack');
  4449 + this.getSenders().forEach(sender => {
  4450 + if (sender.track && stream.getTracks().includes(sender.track)) {
  4451 + this.removeTrack(sender);
  4452 + }
  4453 + });
  4454 + };
  4455 + }
  4456 +
  4457 + function shimRTCDataChannel(window) {
  4458 + // rename DataChannel to RTCDataChannel (native fix in FF60):
  4459 + // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851
  4460 + if (window.DataChannel && !window.RTCDataChannel) {
  4461 + window.RTCDataChannel = window.DataChannel;
  4462 + }
  4463 + }
  4464 +
  4465 + function shimAddTransceiver(window) {
  4466 + // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
  4467 + // Firefox ignores the init sendEncodings options passed to addTransceiver
  4468 + // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
  4469 + if (!(typeof window === 'object' && window.RTCPeerConnection)) {
  4470 + return;
  4471 + }
  4472 + const origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver;
  4473 + if (origAddTransceiver) {
  4474 + window.RTCPeerConnection.prototype.addTransceiver =
  4475 + function addTransceiver() {
  4476 + this.setParametersPromises = [];
  4477 + const initParameters = arguments[1];
  4478 + const shouldPerformCheck = initParameters &&
  4479 + 'sendEncodings' in initParameters;
  4480 + if (shouldPerformCheck) {
  4481 + // If sendEncodings params are provided, validate grammar
  4482 + initParameters.sendEncodings.forEach((encodingParam) => {
  4483 + if ('rid' in encodingParam) {
  4484 + const ridRegex = /^[a-z0-9]{0,16}$/i;
  4485 + if (!ridRegex.test(encodingParam.rid)) {
  4486 + throw new TypeError('Invalid RID value provided.');
  4487 + }
  4488 + }
  4489 + if ('scaleResolutionDownBy' in encodingParam) {
  4490 + if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) {
  4491 + throw new RangeError('scale_resolution_down_by must be >= 1.0');
  4492 + }
  4493 + }
  4494 + if ('maxFramerate' in encodingParam) {
  4495 + if (!(parseFloat(encodingParam.maxFramerate) >= 0)) {
  4496 + throw new RangeError('max_framerate must be >= 0.0');
  4497 + }
  4498 + }
  4499 + });
  4500 + }
  4501 + const transceiver = origAddTransceiver.apply(this, arguments);
  4502 + if (shouldPerformCheck) {
  4503 + // Check if the init options were applied. If not we do this in an
  4504 + // asynchronous way and save the promise reference in a global object.
  4505 + // This is an ugly hack, but at the same time is way more robust than
  4506 + // checking the sender parameters before and after the createOffer
  4507 + // Also note that after the createoffer we are not 100% sure that
  4508 + // the params were asynchronously applied so we might miss the
  4509 + // opportunity to recreate offer.
  4510 + const {sender} = transceiver;
  4511 + const params = sender.getParameters();
  4512 + if (!('encodings' in params) ||
  4513 + // Avoid being fooled by patched getParameters() below.
  4514 + (params.encodings.length === 1 &&
  4515 + Object.keys(params.encodings[0]).length === 0)) {
  4516 + params.encodings = initParameters.sendEncodings;
  4517 + sender.sendEncodings = initParameters.sendEncodings;
  4518 + this.setParametersPromises.push(sender.setParameters(params)
  4519 + .then(() => {
  4520 + delete sender.sendEncodings;
  4521 + }).catch(() => {
  4522 + delete sender.sendEncodings;
  4523 + })
  4524 + );
  4525 + }
  4526 + }
  4527 + return transceiver;
  4528 + };
  4529 + }
  4530 + }
  4531 +
  4532 + function shimGetParameters(window) {
  4533 + if (!(typeof window === 'object' && window.RTCRtpSender)) {
  4534 + return;
  4535 + }
  4536 + const origGetParameters = window.RTCRtpSender.prototype.getParameters;
  4537 + if (origGetParameters) {
  4538 + window.RTCRtpSender.prototype.getParameters =
  4539 + function getParameters() {
  4540 + const params = origGetParameters.apply(this, arguments);
  4541 + if (!('encodings' in params)) {
  4542 + params.encodings = [].concat(this.sendEncodings || [{}]);
  4543 + }
  4544 + return params;
  4545 + };
  4546 + }
  4547 + }
  4548 +
  4549 + function shimCreateOffer(window) {
  4550 + // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
  4551 + // Firefox ignores the init sendEncodings options passed to addTransceiver
  4552 + // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
  4553 + if (!(typeof window === 'object' && window.RTCPeerConnection)) {
  4554 + return;
  4555 + }
  4556 + const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
  4557 + window.RTCPeerConnection.prototype.createOffer = function createOffer() {
  4558 + if (this.setParametersPromises && this.setParametersPromises.length) {
  4559 + return Promise.all(this.setParametersPromises)
  4560 + .then(() => {
  4561 + return origCreateOffer.apply(this, arguments);
  4562 + })
  4563 + .finally(() => {
  4564 + this.setParametersPromises = [];
  4565 + });
  4566 + }
  4567 + return origCreateOffer.apply(this, arguments);
  4568 + };
  4569 + }
  4570 +
  4571 + function shimCreateAnswer(window) {
  4572 + // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
  4573 + // Firefox ignores the init sendEncodings options passed to addTransceiver
  4574 + // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
  4575 + if (!(typeof window === 'object' && window.RTCPeerConnection)) {
  4576 + return;
  4577 + }
  4578 + const origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer;
  4579 + window.RTCPeerConnection.prototype.createAnswer = function createAnswer() {
  4580 + if (this.setParametersPromises && this.setParametersPromises.length) {
  4581 + return Promise.all(this.setParametersPromises)
  4582 + .then(() => {
  4583 + return origCreateAnswer.apply(this, arguments);
  4584 + })
  4585 + .finally(() => {
  4586 + this.setParametersPromises = [];
  4587 + });
  4588 + }
  4589 + return origCreateAnswer.apply(this, arguments);
  4590 + };
  4591 + }
  4592 +
  4593 + var firefoxShim = /*#__PURE__*/Object.freeze({
  4594 + __proto__: null,
  4595 + shimOnTrack: shimOnTrack,
  4596 + shimPeerConnection: shimPeerConnection,
  4597 + shimSenderGetStats: shimSenderGetStats,
  4598 + shimReceiverGetStats: shimReceiverGetStats,
  4599 + shimRemoveStream: shimRemoveStream,
  4600 + shimRTCDataChannel: shimRTCDataChannel,
  4601 + shimAddTransceiver: shimAddTransceiver,
  4602 + shimGetParameters: shimGetParameters,
  4603 + shimCreateOffer: shimCreateOffer,
  4604 + shimCreateAnswer: shimCreateAnswer,
  4605 + shimGetUserMedia: shimGetUserMedia$1,
  4606 + shimGetDisplayMedia: shimGetDisplayMedia
  4607 + });
  4608 +
  4609 + /*
  4610 + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  4611 + *
  4612 + * Use of this source code is governed by a BSD-style license
  4613 + * that can be found in the LICENSE file in the root of the source
  4614 + * tree.
  4615 + */
  4616 +
  4617 + function shimLocalStreamsAPI(window) {
  4618 + if (typeof window !== 'object' || !window.RTCPeerConnection) {
  4619 + return;
  4620 + }
  4621 + if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {
  4622 + window.RTCPeerConnection.prototype.getLocalStreams =
  4623 + function getLocalStreams() {
  4624 + if (!this._localStreams) {
  4625 + this._localStreams = [];
  4626 + }
  4627 + return this._localStreams;
  4628 + };
  4629 + }
  4630 + if (!('addStream' in window.RTCPeerConnection.prototype)) {
  4631 + const _addTrack = window.RTCPeerConnection.prototype.addTrack;
  4632 + window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
  4633 + if (!this._localStreams) {
  4634 + this._localStreams = [];
  4635 + }
  4636 + if (!this._localStreams.includes(stream)) {
  4637 + this._localStreams.push(stream);
  4638 + }
  4639 + // Try to emulate Chrome's behaviour of adding in audio-video order.
  4640 + // Safari orders by track id.
  4641 + stream.getAudioTracks().forEach(track => _addTrack.call(this, track,
  4642 + stream));
  4643 + stream.getVideoTracks().forEach(track => _addTrack.call(this, track,
  4644 + stream));
  4645 + };
  4646 +
  4647 + window.RTCPeerConnection.prototype.addTrack =
  4648 + function addTrack(track, ...streams) {
  4649 + if (streams) {
  4650 + streams.forEach((stream) => {
  4651 + if (!this._localStreams) {
  4652 + this._localStreams = [stream];
  4653 + } else if (!this._localStreams.includes(stream)) {
  4654 + this._localStreams.push(stream);
  4655 + }
  4656 + });
  4657 + }
  4658 + return _addTrack.apply(this, arguments);
  4659 + };
  4660 + }
  4661 + if (!('removeStream' in window.RTCPeerConnection.prototype)) {
  4662 + window.RTCPeerConnection.prototype.removeStream =
  4663 + function removeStream(stream) {
  4664 + if (!this._localStreams) {
  4665 + this._localStreams = [];
  4666 + }
  4667 + const index = this._localStreams.indexOf(stream);
  4668 + if (index === -1) {
  4669 + return;
  4670 + }
  4671 + this._localStreams.splice(index, 1);
  4672 + const tracks = stream.getTracks();
  4673 + this.getSenders().forEach(sender => {
  4674 + if (tracks.includes(sender.track)) {
  4675 + this.removeTrack(sender);
  4676 + }
  4677 + });
  4678 + };
  4679 + }
  4680 + }
  4681 +
  4682 + function shimRemoteStreamsAPI(window) {
  4683 + if (typeof window !== 'object' || !window.RTCPeerConnection) {
  4684 + return;
  4685 + }
  4686 + if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {
  4687 + window.RTCPeerConnection.prototype.getRemoteStreams =
  4688 + function getRemoteStreams() {
  4689 + return this._remoteStreams ? this._remoteStreams : [];
  4690 + };
  4691 + }
  4692 + if (!('onaddstream' in window.RTCPeerConnection.prototype)) {
  4693 + Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {
  4694 + get() {
  4695 + return this._onaddstream;
  4696 + },
  4697 + set(f) {
  4698 + if (this._onaddstream) {
  4699 + this.removeEventListener('addstream', this._onaddstream);
  4700 + this.removeEventListener('track', this._onaddstreampoly);
  4701 + }
  4702 + this.addEventListener('addstream', this._onaddstream = f);
  4703 + this.addEventListener('track', this._onaddstreampoly = (e) => {
  4704 + e.streams.forEach(stream => {
  4705 + if (!this._remoteStreams) {
  4706 + this._remoteStreams = [];
  4707 + }
  4708 + if (this._remoteStreams.includes(stream)) {
  4709 + return;
  4710 + }
  4711 + this._remoteStreams.push(stream);
  4712 + const event = new Event('addstream');
  4713 + event.stream = stream;
  4714 + this.dispatchEvent(event);
  4715 + });
  4716 + });
  4717 + }
  4718 + });
  4719 + const origSetRemoteDescription =
  4720 + window.RTCPeerConnection.prototype.setRemoteDescription;
  4721 + window.RTCPeerConnection.prototype.setRemoteDescription =
  4722 + function setRemoteDescription() {
  4723 + const pc = this;
  4724 + if (!this._onaddstreampoly) {
  4725 + this.addEventListener('track', this._onaddstreampoly = function(e) {
  4726 + e.streams.forEach(stream => {
  4727 + if (!pc._remoteStreams) {
  4728 + pc._remoteStreams = [];
  4729 + }
  4730 + if (pc._remoteStreams.indexOf(stream) >= 0) {
  4731 + return;
  4732 + }
  4733 + pc._remoteStreams.push(stream);
  4734 + const event = new Event('addstream');
  4735 + event.stream = stream;
  4736 + pc.dispatchEvent(event);
  4737 + });
  4738 + });
  4739 + }
  4740 + return origSetRemoteDescription.apply(pc, arguments);
  4741 + };
  4742 + }
  4743 + }
  4744 +
  4745 + function shimCallbacksAPI(window) {
  4746 + if (typeof window !== 'object' || !window.RTCPeerConnection) {
  4747 + return;
  4748 + }
  4749 + const prototype = window.RTCPeerConnection.prototype;
  4750 + const origCreateOffer = prototype.createOffer;
  4751 + const origCreateAnswer = prototype.createAnswer;
  4752 + const setLocalDescription = prototype.setLocalDescription;
  4753 + const setRemoteDescription = prototype.setRemoteDescription;
  4754 + const addIceCandidate = prototype.addIceCandidate;
  4755 +
  4756 + prototype.createOffer =
  4757 + function createOffer(successCallback, failureCallback) {
  4758 + const options = (arguments.length >= 2) ? arguments[2] : arguments[0];
  4759 + const promise = origCreateOffer.apply(this, [options]);
  4760 + if (!failureCallback) {
  4761 + return promise;
  4762 + }
  4763 + promise.then(successCallback, failureCallback);
  4764 + return Promise.resolve();
  4765 + };
  4766 +
  4767 + prototype.createAnswer =
  4768 + function createAnswer(successCallback, failureCallback) {
  4769 + const options = (arguments.length >= 2) ? arguments[2] : arguments[0];
  4770 + const promise = origCreateAnswer.apply(this, [options]);
  4771 + if (!failureCallback) {
  4772 + return promise;
  4773 + }
  4774 + promise.then(successCallback, failureCallback);
  4775 + return Promise.resolve();
  4776 + };
  4777 +
  4778 + let withCallback = function(description, successCallback, failureCallback) {
  4779 + const promise = setLocalDescription.apply(this, [description]);
  4780 + if (!failureCallback) {
  4781 + return promise;
  4782 + }
  4783 + promise.then(successCallback, failureCallback);
  4784 + return Promise.resolve();
  4785 + };
  4786 + prototype.setLocalDescription = withCallback;
  4787 +
  4788 + withCallback = function(description, successCallback, failureCallback) {
  4789 + const promise = setRemoteDescription.apply(this, [description]);
  4790 + if (!failureCallback) {
  4791 + return promise;
  4792 + }
  4793 + promise.then(successCallback, failureCallback);
  4794 + return Promise.resolve();
  4795 + };
  4796 + prototype.setRemoteDescription = withCallback;
  4797 +
  4798 + withCallback = function(candidate, successCallback, failureCallback) {
  4799 + const promise = addIceCandidate.apply(this, [candidate]);
  4800 + if (!failureCallback) {
  4801 + return promise;
  4802 + }
  4803 + promise.then(successCallback, failureCallback);
  4804 + return Promise.resolve();
  4805 + };
  4806 + prototype.addIceCandidate = withCallback;
  4807 + }
  4808 +
  4809 + function shimGetUserMedia(window) {
  4810 + const navigator = window && window.navigator;
  4811 +
  4812 + if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
  4813 + // shim not needed in Safari 12.1
  4814 + const mediaDevices = navigator.mediaDevices;
  4815 + const _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices);
  4816 + navigator.mediaDevices.getUserMedia = (constraints) => {
  4817 + return _getUserMedia(shimConstraints(constraints));
  4818 + };
  4819 + }
  4820 +
  4821 + if (!navigator.getUserMedia && navigator.mediaDevices &&
  4822 + navigator.mediaDevices.getUserMedia) {
  4823 + navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) {
  4824 + navigator.mediaDevices.getUserMedia(constraints)
  4825 + .then(cb, errcb);
  4826 + }.bind(navigator);
  4827 + }
  4828 + }
  4829 +
  4830 + function shimConstraints(constraints) {
  4831 + if (constraints && constraints.video !== undefined) {
  4832 + return Object.assign({},
  4833 + constraints,
  4834 + {video: compactObject(constraints.video)}
  4835 + );
  4836 + }
  4837 +
  4838 + return constraints;
  4839 + }
  4840 +
  4841 + function shimRTCIceServerUrls(window) {
  4842 + if (!window.RTCPeerConnection) {
  4843 + return;
  4844 + }
  4845 + // migrate from non-spec RTCIceServer.url to RTCIceServer.urls
  4846 + const OrigPeerConnection = window.RTCPeerConnection;
  4847 + window.RTCPeerConnection =
  4848 + function RTCPeerConnection(pcConfig, pcConstraints) {
  4849 + if (pcConfig && pcConfig.iceServers) {
  4850 + const newIceServers = [];
  4851 + for (let i = 0; i < pcConfig.iceServers.length; i++) {
  4852 + let server = pcConfig.iceServers[i];
  4853 + if (!server.hasOwnProperty('urls') &&
  4854 + server.hasOwnProperty('url')) {
  4855 + deprecated('RTCIceServer.url', 'RTCIceServer.urls');
  4856 + server = JSON.parse(JSON.stringify(server));
  4857 + server.urls = server.url;
  4858 + delete server.url;
  4859 + newIceServers.push(server);
  4860 + } else {
  4861 + newIceServers.push(pcConfig.iceServers[i]);
  4862 + }
  4863 + }
  4864 + pcConfig.iceServers = newIceServers;
  4865 + }
  4866 + return new OrigPeerConnection(pcConfig, pcConstraints);
  4867 + };
  4868 + window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
  4869 + // wrap static methods. Currently just generateCertificate.
  4870 + if ('generateCertificate' in OrigPeerConnection) {
  4871 + Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
  4872 + get() {
  4873 + return OrigPeerConnection.generateCertificate;
  4874 + }
  4875 + });
  4876 + }
  4877 + }
  4878 +
  4879 + function shimTrackEventTransceiver(window) {
  4880 + // Add event.transceiver member over deprecated event.receiver
  4881 + if (typeof window === 'object' && window.RTCTrackEvent &&
  4882 + 'receiver' in window.RTCTrackEvent.prototype &&
  4883 + !('transceiver' in window.RTCTrackEvent.prototype)) {
  4884 + Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
  4885 + get() {
  4886 + return {receiver: this.receiver};
  4887 + }
  4888 + });
  4889 + }
  4890 + }
  4891 +
  4892 + function shimCreateOfferLegacy(window) {
  4893 + const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
  4894 + window.RTCPeerConnection.prototype.createOffer =
  4895 + function createOffer(offerOptions) {
  4896 + if (offerOptions) {
  4897 + if (typeof offerOptions.offerToReceiveAudio !== 'undefined') {
  4898 + // support bit values
  4899 + offerOptions.offerToReceiveAudio =
  4900 + !!offerOptions.offerToReceiveAudio;
  4901 + }
  4902 + const audioTransceiver = this.getTransceivers().find(transceiver =>
  4903 + transceiver.receiver.track.kind === 'audio');
  4904 + if (offerOptions.offerToReceiveAudio === false && audioTransceiver) {
  4905 + if (audioTransceiver.direction === 'sendrecv') {
  4906 + if (audioTransceiver.setDirection) {
  4907 + audioTransceiver.setDirection('sendonly');
  4908 + } else {
  4909 + audioTransceiver.direction = 'sendonly';
  4910 + }
  4911 + } else if (audioTransceiver.direction === 'recvonly') {
  4912 + if (audioTransceiver.setDirection) {
  4913 + audioTransceiver.setDirection('inactive');
  4914 + } else {
  4915 + audioTransceiver.direction = 'inactive';
  4916 + }
  4917 + }
  4918 + } else if (offerOptions.offerToReceiveAudio === true &&
  4919 + !audioTransceiver) {
  4920 + this.addTransceiver('audio');
  4921 + }
  4922 +
  4923 + if (typeof offerOptions.offerToReceiveVideo !== 'undefined') {
  4924 + // support bit values
  4925 + offerOptions.offerToReceiveVideo =
  4926 + !!offerOptions.offerToReceiveVideo;
  4927 + }
  4928 + const videoTransceiver = this.getTransceivers().find(transceiver =>
  4929 + transceiver.receiver.track.kind === 'video');
  4930 + if (offerOptions.offerToReceiveVideo === false && videoTransceiver) {
  4931 + if (videoTransceiver.direction === 'sendrecv') {
  4932 + if (videoTransceiver.setDirection) {
  4933 + videoTransceiver.setDirection('sendonly');
  4934 + } else {
  4935 + videoTransceiver.direction = 'sendonly';
  4936 + }
  4937 + } else if (videoTransceiver.direction === 'recvonly') {
  4938 + if (videoTransceiver.setDirection) {
  4939 + videoTransceiver.setDirection('inactive');
  4940 + } else {
  4941 + videoTransceiver.direction = 'inactive';
  4942 + }
  4943 + }
  4944 + } else if (offerOptions.offerToReceiveVideo === true &&
  4945 + !videoTransceiver) {
  4946 + this.addTransceiver('video');
  4947 + }
  4948 + }
  4949 + return origCreateOffer.apply(this, arguments);
  4950 + };
  4951 + }
  4952 +
  4953 + function shimAudioContext(window) {
  4954 + if (typeof window !== 'object' || window.AudioContext) {
  4955 + return;
  4956 + }
  4957 + window.AudioContext = window.webkitAudioContext;
  4958 + }
  4959 +
  4960 + var safariShim = /*#__PURE__*/Object.freeze({
  4961 + __proto__: null,
  4962 + shimLocalStreamsAPI: shimLocalStreamsAPI,
  4963 + shimRemoteStreamsAPI: shimRemoteStreamsAPI,
  4964 + shimCallbacksAPI: shimCallbacksAPI,
  4965 + shimGetUserMedia: shimGetUserMedia,
  4966 + shimConstraints: shimConstraints,
  4967 + shimRTCIceServerUrls: shimRTCIceServerUrls,
  4968 + shimTrackEventTransceiver: shimTrackEventTransceiver,
  4969 + shimCreateOfferLegacy: shimCreateOfferLegacy,
  4970 + shimAudioContext: shimAudioContext
  4971 + });
  4972 +
  4973 + /*
  4974 + * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
  4975 + *
  4976 + * Use of this source code is governed by a BSD-style license
  4977 + * that can be found in the LICENSE file in the root of the source
  4978 + * tree.
  4979 + */
  4980 +
  4981 + function shimRTCIceCandidate(window) {
  4982 + // foundation is arbitrarily chosen as an indicator for full support for
  4983 + // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface
  4984 + if (!window.RTCIceCandidate || (window.RTCIceCandidate && 'foundation' in
  4985 + window.RTCIceCandidate.prototype)) {
  4986 + return;
  4987 + }
  4988 +
  4989 + const NativeRTCIceCandidate = window.RTCIceCandidate;
  4990 + window.RTCIceCandidate = function RTCIceCandidate(args) {
  4991 + // Remove the a= which shouldn't be part of the candidate string.
  4992 + if (typeof args === 'object' && args.candidate &&
  4993 + args.candidate.indexOf('a=') === 0) {
  4994 + args = JSON.parse(JSON.stringify(args));
  4995 + args.candidate = args.candidate.substr(2);
  4996 + }
  4997 +
  4998 + if (args.candidate && args.candidate.length) {
  4999 + // Augment the native candidate with the parsed fields.
  5000 + const nativeCandidate = new NativeRTCIceCandidate(args);
  5001 + const parsedCandidate = sdp.parseCandidate(args.candidate);
  5002 + const augmentedCandidate = Object.assign(nativeCandidate,
  5003 + parsedCandidate);
  5004 +
  5005 + // Add a serializer that does not serialize the extra attributes.
  5006 + augmentedCandidate.toJSON = function toJSON() {
  5007 + return {
  5008 + candidate: augmentedCandidate.candidate,
  5009 + sdpMid: augmentedCandidate.sdpMid,
  5010 + sdpMLineIndex: augmentedCandidate.sdpMLineIndex,
  5011 + usernameFragment: augmentedCandidate.usernameFragment,
  5012 + };
  5013 + };
  5014 + return augmentedCandidate;
  5015 + }
  5016 + return new NativeRTCIceCandidate(args);
  5017 + };
  5018 + window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype;
  5019 +
  5020 + // Hook up the augmented candidate in onicecandidate and
  5021 + // addEventListener('icecandidate', ...)
  5022 + wrapPeerConnectionEvent(window, 'icecandidate', e => {
  5023 + if (e.candidate) {
  5024 + Object.defineProperty(e, 'candidate', {
  5025 + value: new window.RTCIceCandidate(e.candidate),
  5026 + writable: 'false'
  5027 + });
  5028 + }
  5029 + return e;
  5030 + });
  5031 + }
  5032 +
  5033 + function shimMaxMessageSize(window, browserDetails) {
  5034 + if (!window.RTCPeerConnection) {
  5035 + return;
  5036 + }
  5037 +
  5038 + if (!('sctp' in window.RTCPeerConnection.prototype)) {
  5039 + Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', {
  5040 + get() {
  5041 + return typeof this._sctp === 'undefined' ? null : this._sctp;
  5042 + }
  5043 + });
  5044 + }
  5045 +
  5046 + const sctpInDescription = function(description) {
  5047 + if (!description || !description.sdp) {
  5048 + return false;
  5049 + }
  5050 + const sections = sdp.splitSections(description.sdp);
  5051 + sections.shift();
  5052 + return sections.some(mediaSection => {
  5053 + const mLine = sdp.parseMLine(mediaSection);
  5054 + return mLine && mLine.kind === 'application'
  5055 + && mLine.protocol.indexOf('SCTP') !== -1;
  5056 + });
  5057 + };
  5058 +
  5059 + const getRemoteFirefoxVersion = function(description) {
  5060 + // TODO: Is there a better solution for detecting Firefox?
  5061 + const match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/);
  5062 + if (match === null || match.length < 2) {
  5063 + return -1;
  5064 + }
  5065 + const version = parseInt(match[1], 10);
  5066 + // Test for NaN (yes, this is ugly)
  5067 + return version !== version ? -1 : version;
  5068 + };
  5069 +
  5070 + const getCanSendMaxMessageSize = function(remoteIsFirefox) {
  5071 + // Every implementation we know can send at least 64 KiB.
  5072 + // Note: Although Chrome is technically able to send up to 256 KiB, the
  5073 + // data does not reach the other peer reliably.
  5074 + // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419
  5075 + let canSendMaxMessageSize = 65536;
  5076 + if (browserDetails.browser === 'firefox') {
  5077 + if (browserDetails.version < 57) {
  5078 + if (remoteIsFirefox === -1) {
  5079 + // FF < 57 will send in 16 KiB chunks using the deprecated PPID
  5080 + // fragmentation.
  5081 + canSendMaxMessageSize = 16384;
  5082 + } else {
  5083 + // However, other FF (and RAWRTC) can reassemble PPID-fragmented
  5084 + // messages. Thus, supporting ~2 GiB when sending.
  5085 + canSendMaxMessageSize = 2147483637;
  5086 + }
  5087 + } else if (browserDetails.version < 60) {
  5088 + // Currently, all FF >= 57 will reset the remote maximum message size
  5089 + // to the default value when a data channel is created at a later
  5090 + // stage. :(
  5091 + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
  5092 + canSendMaxMessageSize =
  5093 + browserDetails.version === 57 ? 65535 : 65536;
  5094 + } else {
  5095 + // FF >= 60 supports sending ~2 GiB
  5096 + canSendMaxMessageSize = 2147483637;
  5097 + }
  5098 + }
  5099 + return canSendMaxMessageSize;
  5100 + };
  5101 +
  5102 + const getMaxMessageSize = function(description, remoteIsFirefox) {
  5103 + // Note: 65536 bytes is the default value from the SDP spec. Also,
  5104 + // every implementation we know supports receiving 65536 bytes.
  5105 + let maxMessageSize = 65536;
  5106 +
  5107 + // FF 57 has a slightly incorrect default remote max message size, so
  5108 + // we need to adjust it here to avoid a failure when sending.
  5109 + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697
  5110 + if (browserDetails.browser === 'firefox'
  5111 + && browserDetails.version === 57) {
  5112 + maxMessageSize = 65535;
  5113 + }
  5114 +
  5115 + const match = sdp.matchPrefix(description.sdp,
  5116 + 'a=max-message-size:');
  5117 + if (match.length > 0) {
  5118 + maxMessageSize = parseInt(match[0].substr(19), 10);
  5119 + } else if (browserDetails.browser === 'firefox' &&
  5120 + remoteIsFirefox !== -1) {
  5121 + // If the maximum message size is not present in the remote SDP and
  5122 + // both local and remote are Firefox, the remote peer can receive
  5123 + // ~2 GiB.
  5124 + maxMessageSize = 2147483637;
  5125 + }
  5126 + return maxMessageSize;
  5127 + };
  5128 +
  5129 + const origSetRemoteDescription =
  5130 + window.RTCPeerConnection.prototype.setRemoteDescription;
  5131 + window.RTCPeerConnection.prototype.setRemoteDescription =
  5132 + function setRemoteDescription() {
  5133 + this._sctp = null;
  5134 + // Chrome decided to not expose .sctp in plan-b mode.
  5135 + // As usual, adapter.js has to do an 'ugly worakaround'
  5136 + // to cover up the mess.
  5137 + if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) {
  5138 + const {sdpSemantics} = this.getConfiguration();
  5139 + if (sdpSemantics === 'plan-b') {
  5140 + Object.defineProperty(this, 'sctp', {
  5141 + get() {
  5142 + return typeof this._sctp === 'undefined' ? null : this._sctp;
  5143 + },
  5144 + enumerable: true,
  5145 + configurable: true,
  5146 + });
  5147 + }
  5148 + }
  5149 +
  5150 + if (sctpInDescription(arguments[0])) {
  5151 + // Check if the remote is FF.
  5152 + const isFirefox = getRemoteFirefoxVersion(arguments[0]);
  5153 +
  5154 + // Get the maximum message size the local peer is capable of sending
  5155 + const canSendMMS = getCanSendMaxMessageSize(isFirefox);
  5156 +
  5157 + // Get the maximum message size of the remote peer.
  5158 + const remoteMMS = getMaxMessageSize(arguments[0], isFirefox);
  5159 +
  5160 + // Determine final maximum message size
  5161 + let maxMessageSize;
  5162 + if (canSendMMS === 0 && remoteMMS === 0) {
  5163 + maxMessageSize = Number.POSITIVE_INFINITY;
  5164 + } else if (canSendMMS === 0 || remoteMMS === 0) {
  5165 + maxMessageSize = Math.max(canSendMMS, remoteMMS);
  5166 + } else {
  5167 + maxMessageSize = Math.min(canSendMMS, remoteMMS);
  5168 + }
  5169 +
  5170 + // Create a dummy RTCSctpTransport object and the 'maxMessageSize'
  5171 + // attribute.
  5172 + const sctp = {};
  5173 + Object.defineProperty(sctp, 'maxMessageSize', {
  5174 + get() {
  5175 + return maxMessageSize;
  5176 + }
  5177 + });
  5178 + this._sctp = sctp;
  5179 + }
  5180 +
  5181 + return origSetRemoteDescription.apply(this, arguments);
  5182 + };
  5183 + }
  5184 +
  5185 + function shimSendThrowTypeError(window) {
  5186 + if (!(window.RTCPeerConnection &&
  5187 + 'createDataChannel' in window.RTCPeerConnection.prototype)) {
  5188 + return;
  5189 + }
  5190 +
  5191 + // Note: Although Firefox >= 57 has a native implementation, the maximum
  5192 + // message size can be reset for all data channels at a later stage.
  5193 + // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
  5194 +
  5195 + function wrapDcSend(dc, pc) {
  5196 + const origDataChannelSend = dc.send;
  5197 + dc.send = function send() {
  5198 + const data = arguments[0];
  5199 + const length = data.length || data.size || data.byteLength;
  5200 + if (dc.readyState === 'open' &&
  5201 + pc.sctp && length > pc.sctp.maxMessageSize) {
  5202 + throw new TypeError('Message too large (can send a maximum of ' +
  5203 + pc.sctp.maxMessageSize + ' bytes)');
  5204 + }
  5205 + return origDataChannelSend.apply(dc, arguments);
  5206 + };
  5207 + }
  5208 + const origCreateDataChannel =
  5209 + window.RTCPeerConnection.prototype.createDataChannel;
  5210 + window.RTCPeerConnection.prototype.createDataChannel =
  5211 + function createDataChannel() {
  5212 + const dataChannel = origCreateDataChannel.apply(this, arguments);
  5213 + wrapDcSend(dataChannel, this);
  5214 + return dataChannel;
  5215 + };
  5216 + wrapPeerConnectionEvent(window, 'datachannel', e => {
  5217 + wrapDcSend(e.channel, e.target);
  5218 + return e;
  5219 + });
  5220 + }
  5221 +
  5222 +
  5223 + /* shims RTCConnectionState by pretending it is the same as iceConnectionState.
  5224 + * See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12
  5225 + * for why this is a valid hack in Chrome. In Firefox it is slightly incorrect
  5226 + * since DTLS failures would be hidden. See
  5227 + * https://bugzilla.mozilla.org/show_bug.cgi?id=1265827
  5228 + * for the Firefox tracking bug.
  5229 + */
  5230 + function shimConnectionState(window) {
  5231 + if (!window.RTCPeerConnection ||
  5232 + 'connectionState' in window.RTCPeerConnection.prototype) {
  5233 + return;
  5234 + }
  5235 + const proto = window.RTCPeerConnection.prototype;
  5236 + Object.defineProperty(proto, 'connectionState', {
  5237 + get() {
  5238 + return {
  5239 + completed: 'connected',
  5240 + checking: 'connecting'
  5241 + }[this.iceConnectionState] || this.iceConnectionState;
  5242 + },
  5243 + enumerable: true,
  5244 + configurable: true
  5245 + });
  5246 + Object.defineProperty(proto, 'onconnectionstatechange', {
  5247 + get() {
  5248 + return this._onconnectionstatechange || null;
  5249 + },
  5250 + set(cb) {
  5251 + if (this._onconnectionstatechange) {
  5252 + this.removeEventListener('connectionstatechange',
  5253 + this._onconnectionstatechange);
  5254 + delete this._onconnectionstatechange;
  5255 + }
  5256 + if (cb) {
  5257 + this.addEventListener('connectionstatechange',
  5258 + this._onconnectionstatechange = cb);
  5259 + }
  5260 + },
  5261 + enumerable: true,
  5262 + configurable: true
  5263 + });
  5264 +
  5265 + ['setLocalDescription', 'setRemoteDescription'].forEach((method) => {
  5266 + const origMethod = proto[method];
  5267 + proto[method] = function() {
  5268 + if (!this._connectionstatechangepoly) {
  5269 + this._connectionstatechangepoly = e => {
  5270 + const pc = e.target;
  5271 + if (pc._lastConnectionState !== pc.connectionState) {
  5272 + pc._lastConnectionState = pc.connectionState;
  5273 + const newEvent = new Event('connectionstatechange', e);
  5274 + pc.dispatchEvent(newEvent);
  5275 + }
  5276 + return e;
  5277 + };
  5278 + this.addEventListener('iceconnectionstatechange',
  5279 + this._connectionstatechangepoly);
  5280 + }
  5281 + return origMethod.apply(this, arguments);
  5282 + };
  5283 + });
  5284 + }
  5285 +
  5286 + function removeExtmapAllowMixed(window, browserDetails) {
  5287 + /* remove a=extmap-allow-mixed for webrtc.org < M71 */
  5288 + if (!window.RTCPeerConnection) {
  5289 + return;
  5290 + }
  5291 + if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {
  5292 + return;
  5293 + }
  5294 + if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {
  5295 + return;
  5296 + }
  5297 + const nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;
  5298 + window.RTCPeerConnection.prototype.setRemoteDescription =
  5299 + function setRemoteDescription(desc) {
  5300 + if (desc && desc.sdp && desc.sdp.indexOf('\na=extmap-allow-mixed') !== -1) {
  5301 + const sdp = desc.sdp.split('\n').filter((line) => {
  5302 + return line.trim() !== 'a=extmap-allow-mixed';
  5303 + }).join('\n');
  5304 + // Safari enforces read-only-ness of RTCSessionDescription fields.
  5305 + if (window.RTCSessionDescription &&
  5306 + desc instanceof window.RTCSessionDescription) {
  5307 + arguments[0] = new window.RTCSessionDescription({
  5308 + type: desc.type,
  5309 + sdp,
  5310 + });
  5311 + } else {
  5312 + desc.sdp = sdp;
  5313 + }
  5314 + }
  5315 + return nativeSRD.apply(this, arguments);
  5316 + };
  5317 + }
  5318 +
  5319 + function shimAddIceCandidateNullOrEmpty(window, browserDetails) {
  5320 + // Support for addIceCandidate(null or undefined)
  5321 + // as well as addIceCandidate({candidate: "", ...})
  5322 + // https://bugs.chromium.org/p/chromium/issues/detail?id=978582
  5323 + // Note: must be called before other polyfills which change the signature.
  5324 + if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {
  5325 + return;
  5326 + }
  5327 + const nativeAddIceCandidate =
  5328 + window.RTCPeerConnection.prototype.addIceCandidate;
  5329 + if (!nativeAddIceCandidate || nativeAddIceCandidate.length === 0) {
  5330 + return;
  5331 + }
  5332 + window.RTCPeerConnection.prototype.addIceCandidate =
  5333 + function addIceCandidate() {
  5334 + if (!arguments[0]) {
  5335 + if (arguments[1]) {
  5336 + arguments[1].apply(null);
  5337 + }
  5338 + return Promise.resolve();
  5339 + }
  5340 + // Firefox 68+ emits and processes {candidate: "", ...}, ignore
  5341 + // in older versions.
  5342 + // Native support for ignoring exists for Chrome M77+.
  5343 + // Safari ignores as well, exact version unknown but works in the same
  5344 + // version that also ignores addIceCandidate(null).
  5345 + if (((browserDetails.browser === 'chrome' && browserDetails.version < 78)
  5346 + || (browserDetails.browser === 'firefox'
  5347 + && browserDetails.version < 68)
  5348 + || (browserDetails.browser === 'safari'))
  5349 + && arguments[0] && arguments[0].candidate === '') {
  5350 + return Promise.resolve();
  5351 + }
  5352 + return nativeAddIceCandidate.apply(this, arguments);
  5353 + };
  5354 + }
  5355 +
  5356 + var commonShim = /*#__PURE__*/Object.freeze({
  5357 + __proto__: null,
  5358 + shimRTCIceCandidate: shimRTCIceCandidate,
  5359 + shimMaxMessageSize: shimMaxMessageSize,
  5360 + shimSendThrowTypeError: shimSendThrowTypeError,
  5361 + shimConnectionState: shimConnectionState,
  5362 + removeExtmapAllowMixed: removeExtmapAllowMixed,
  5363 + shimAddIceCandidateNullOrEmpty: shimAddIceCandidateNullOrEmpty
  5364 + });
  5365 +
  5366 + /*
  5367 + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  5368 + *
  5369 + * Use of this source code is governed by a BSD-style license
  5370 + * that can be found in the LICENSE file in the root of the source
  5371 + * tree.
  5372 + */
  5373 +
  5374 + // Shimming starts here.
  5375 + function adapterFactory({window} = {}, options = {
  5376 + shimChrome: true,
  5377 + shimFirefox: true,
  5378 + shimEdge: true,
  5379 + shimSafari: true,
  5380 + }) {
  5381 + // Utils.
  5382 + const logging = log$1;
  5383 + const browserDetails = detectBrowser(window);
  5384 +
  5385 + const adapter = {
  5386 + browserDetails,
  5387 + commonShim,
  5388 + extractVersion: extractVersion,
  5389 + disableLog: disableLog,
  5390 + disableWarnings: disableWarnings
  5391 + };
  5392 +
  5393 + // Shim browser if found.
  5394 + switch (browserDetails.browser) {
  5395 + case 'chrome':
  5396 + if (!chromeShim || !shimPeerConnection$2 ||
  5397 + !options.shimChrome) {
  5398 + logging('Chrome shim is not included in this adapter release.');
  5399 + return adapter;
  5400 + }
  5401 + if (browserDetails.version === null) {
  5402 + logging('Chrome shim can not determine version, not shimming.');
  5403 + return adapter;
  5404 + }
  5405 + logging('adapter.js shimming chrome.');
  5406 + // Export to the adapter global object visible in the browser.
  5407 + adapter.browserShim = chromeShim;
  5408 +
  5409 + // Must be called before shimPeerConnection.
  5410 + shimAddIceCandidateNullOrEmpty(window, browserDetails);
  5411 +
  5412 + shimGetUserMedia$3(window, browserDetails);
  5413 + shimMediaStream(window);
  5414 + shimPeerConnection$2(window, browserDetails);
  5415 + shimOnTrack$1(window);
  5416 + shimAddTrackRemoveTrack(window, browserDetails);
  5417 + shimGetSendersWithDtmf(window);
  5418 + shimGetStats(window);
  5419 + shimSenderReceiverGetStats(window);
  5420 + fixNegotiationNeeded(window, browserDetails);
  5421 +
  5422 + shimRTCIceCandidate(window);
  5423 + shimConnectionState(window);
  5424 + shimMaxMessageSize(window, browserDetails);
  5425 + shimSendThrowTypeError(window);
  5426 + removeExtmapAllowMixed(window, browserDetails);
  5427 + break;
  5428 + case 'firefox':
  5429 + if (!firefoxShim || !shimPeerConnection ||
  5430 + !options.shimFirefox) {
  5431 + logging('Firefox shim is not included in this adapter release.');
  5432 + return adapter;
  5433 + }
  5434 + logging('adapter.js shimming firefox.');
  5435 + // Export to the adapter global object visible in the browser.
  5436 + adapter.browserShim = firefoxShim;
  5437 +
  5438 + // Must be called before shimPeerConnection.
  5439 + shimAddIceCandidateNullOrEmpty(window, browserDetails);
  5440 +
  5441 + shimGetUserMedia$1(window, browserDetails);
  5442 + shimPeerConnection(window, browserDetails);
  5443 + shimOnTrack(window);
  5444 + shimRemoveStream(window);
  5445 + shimSenderGetStats(window);
  5446 + shimReceiverGetStats(window);
  5447 + shimRTCDataChannel(window);
  5448 + shimAddTransceiver(window);
  5449 + shimGetParameters(window);
  5450 + shimCreateOffer(window);
  5451 + shimCreateAnswer(window);
  5452 +
  5453 + shimRTCIceCandidate(window);
  5454 + shimConnectionState(window);
  5455 + shimMaxMessageSize(window, browserDetails);
  5456 + shimSendThrowTypeError(window);
  5457 + break;
  5458 + case 'edge':
  5459 + if (!edgeShim || !shimPeerConnection$1 || !options.shimEdge) {
  5460 + logging('MS edge shim is not included in this adapter release.');
  5461 + return adapter;
  5462 + }
  5463 + logging('adapter.js shimming edge.');
  5464 + // Export to the adapter global object visible in the browser.
  5465 + adapter.browserShim = edgeShim;
  5466 +
  5467 + shimGetUserMedia$2(window);
  5468 + shimGetDisplayMedia$1(window);
  5469 + shimPeerConnection$1(window, browserDetails);
  5470 + shimReplaceTrack(window);
  5471 +
  5472 + // the edge shim implements the full RTCIceCandidate object.
  5473 +
  5474 + shimMaxMessageSize(window, browserDetails);
  5475 + shimSendThrowTypeError(window);
  5476 + break;
  5477 + case 'safari':
  5478 + if (!safariShim || !options.shimSafari) {
  5479 + logging('Safari shim is not included in this adapter release.');
  5480 + return adapter;
  5481 + }
  5482 + logging('adapter.js shimming safari.');
  5483 + // Export to the adapter global object visible in the browser.
  5484 + adapter.browserShim = safariShim;
  5485 +
  5486 + // Must be called before shimCallbackAPI.
  5487 + shimAddIceCandidateNullOrEmpty(window, browserDetails);
  5488 +
  5489 + shimRTCIceServerUrls(window);
  5490 + shimCreateOfferLegacy(window);
  5491 + shimCallbacksAPI(window);
  5492 + shimLocalStreamsAPI(window);
  5493 + shimRemoteStreamsAPI(window);
  5494 + shimTrackEventTransceiver(window);
  5495 + shimGetUserMedia(window);
  5496 + shimAudioContext(window);
  5497 +
  5498 + shimRTCIceCandidate(window);
  5499 + shimMaxMessageSize(window, browserDetails);
  5500 + shimSendThrowTypeError(window);
  5501 + removeExtmapAllowMixed(window, browserDetails);
  5502 + break;
  5503 + default:
  5504 + logging('Unsupported browser!');
  5505 + break;
  5506 + }
  5507 +
  5508 + return adapter;
  5509 + }
  5510 +
  5511 + /*
  5512 + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
  5513 + *
  5514 + * Use of this source code is governed by a BSD-style license
  5515 + * that can be found in the LICENSE file in the root of the source
  5516 + * tree.
  5517 + */
  5518 +
  5519 + adapterFactory({window: typeof window === 'undefined' ? undefined : window});
  5520 +
  5521 + /**
  5522 + * @class AudioTrackConstraints
  5523 + * @classDesc Constraints for creating an audio MediaStreamTrack.
  5524 + * @memberof Owt.Base
  5525 + * @constructor
  5526 + * @param {Owt.Base.AudioSourceInfo} source Source info of this audio track.
  5527 + */
  5528 +
  5529 + class AudioTrackConstraints {
  5530 + // eslint-disable-next-line require-jsdoc
  5531 + constructor(source) {
  5532 + if (!Object.values(AudioSourceInfo).some(v => v === source)) {
  5533 + throw new TypeError('Invalid source.');
  5534 + }
  5535 + /**
  5536 + * @member {string} source
  5537 + * @memberof Owt.Base.AudioTrackConstraints
  5538 + * @desc Values could be "mic", "screen-cast", "file" or "mixed".
  5539 + * @instance
  5540 + */
  5541 +
  5542 +
  5543 + this.source = source;
  5544 + /**
  5545 + * @member {string} deviceId
  5546 + * @memberof Owt.Base.AudioTrackConstraints
  5547 + * @desc Do not provide deviceId if source is not "mic".
  5548 + * @instance
  5549 + * @see https://w3c.github.io/mediacapture-main/#def-constraint-deviceId
  5550 + */
  5551 +
  5552 + this.deviceId = undefined;
  5553 + }
  5554 +
  5555 + }
  5556 + /**
  5557 + * @class VideoTrackConstraints
  5558 + * @classDesc Constraints for creating a video MediaStreamTrack.
  5559 + * @memberof Owt.Base
  5560 + * @constructor
  5561 + * @param {Owt.Base.VideoSourceInfo} source Source info of this video track.
  5562 + */
  5563 +
  5564 + class VideoTrackConstraints {
  5565 + // eslint-disable-next-line require-jsdoc
  5566 + constructor(source) {
  5567 + if (!Object.values(VideoSourceInfo).some(v => v === source)) {
  5568 + throw new TypeError('Invalid source.');
  5569 + }
  5570 + /**
  5571 + * @member {string} source
  5572 + * @memberof Owt.Base.VideoTrackConstraints
  5573 + * @desc Values could be "camera", "screen-cast", "file" or "mixed".
  5574 + * @instance
  5575 + */
  5576 +
  5577 +
  5578 + this.source = source;
  5579 + /**
  5580 + * @member {string} deviceId
  5581 + * @memberof Owt.Base.VideoTrackConstraints
  5582 + * @desc Do not provide deviceId if source is not "camera".
  5583 + * @instance
  5584 + * @see https://w3c.github.io/mediacapture-main/#def-constraint-deviceId
  5585 + */
  5586 +
  5587 + this.deviceId = undefined;
  5588 + /**
  5589 + * @member {Owt.Base.Resolution} resolution
  5590 + * @memberof Owt.Base.VideoTrackConstraints
  5591 + * @instance
  5592 + */
  5593 +
  5594 + this.resolution = undefined;
  5595 + /**
  5596 + * @member {number} frameRate
  5597 + * @memberof Owt.Base.VideoTrackConstraints
  5598 + * @instance
  5599 + */
  5600 +
  5601 + this.frameRate = undefined;
  5602 + }
  5603 +
  5604 + }
  5605 + /**
  5606 + * @class StreamConstraints
  5607 + * @classDesc Constraints for creating a MediaStream from screen mic and camera.
  5608 + * @memberof Owt.Base
  5609 + * @constructor
  5610 + * @param {?Owt.Base.AudioTrackConstraints} audioConstraints
  5611 + * @param {?Owt.Base.VideoTrackConstraints} videoConstraints
  5612 + */
  5613 +
  5614 + class StreamConstraints {
  5615 + // eslint-disable-next-line require-jsdoc
  5616 + constructor(audioConstraints = false, videoConstraints = false) {
  5617 + /**
  5618 + * @member {Owt.Base.MediaStreamTrackDeviceConstraintsForAudio} audio
  5619 + * @memberof Owt.Base.MediaStreamDeviceConstraints
  5620 + * @instance
  5621 + */
  5622 + this.audio = audioConstraints;
  5623 + /**
  5624 + * @member {Owt.Base.MediaStreamTrackDeviceConstraintsForVideo} Video
  5625 + * @memberof Owt.Base.MediaStreamDeviceConstraints
  5626 + * @instance
  5627 + */
  5628 +
  5629 + this.video = videoConstraints;
  5630 + }
  5631 +
  5632 + } // eslint-disable-next-line require-jsdoc
  5633 +
  5634 + function isVideoConstrainsForScreenCast(constraints) {
  5635 + return typeof constraints.video === 'object' && constraints.video.source === VideoSourceInfo.SCREENCAST;
  5636 + }
  5637 + /**
  5638 + * @class MediaStreamFactory
  5639 + * @classDesc A factory to create MediaStream. You can also create MediaStream by yourself.
  5640 + * @memberof Owt.Base
  5641 + */
  5642 +
  5643 +
  5644 + class MediaStreamFactory {
  5645 + /**
  5646 + * @function createMediaStream
  5647 + * @static
  5648 + * @desc Create a MediaStream with given constraints. If you want to create a MediaStream for screen cast, please make sure both audio and video's source are "screen-cast".
  5649 + * @memberof Owt.Base.MediaStreamFactory
  5650 + * @return {Promise<MediaStream, Error>} Return a promise that is resolved when stream is successfully created, or rejected if one of the following error happened:
  5651 + * - One or more parameters cannot be satisfied.
  5652 + * - Specified device is busy.
  5653 + * - Cannot obtain necessary permission or operation is canceled by user.
  5654 + * - Video source is screen cast, while audio source is not.
  5655 + * - Audio source is screen cast, while video source is disabled.
  5656 + * @param {Owt.Base.StreamConstraints} constraints
  5657 + */
  5658 + static createMediaStream(constraints) {
  5659 + if (typeof constraints !== 'object' || !constraints.audio && !constraints.video) {
  5660 + return Promise.reject(new TypeError('Invalid constrains'));
  5661 + }
  5662 +
  5663 + if (!isVideoConstrainsForScreenCast(constraints) && typeof constraints.audio === 'object' && constraints.audio.source === AudioSourceInfo.SCREENCAST) {
  5664 + return Promise.reject(new TypeError('Cannot share screen without video.'));
  5665 + }
  5666 +
  5667 + if (isVideoConstrainsForScreenCast(constraints) && !isChrome() && !isFirefox()) {
  5668 + return Promise.reject(new TypeError('Screen sharing only supports Chrome and Firefox.'));
  5669 + }
  5670 +
  5671 + if (isVideoConstrainsForScreenCast(constraints) && typeof constraints.audio === 'object' && constraints.audio.source !== AudioSourceInfo.SCREENCAST) {
  5672 + return Promise.reject(new TypeError('Cannot capture video from screen cast while capture audio from' + ' other source.'));
  5673 + } // Check and convert constraints.
  5674 +
  5675 +
  5676 + if (!constraints.audio && !constraints.video) {
  5677 + return Promise.reject(new TypeError('At least one of audio and video must be requested.'));
  5678 + }
  5679 +
  5680 + const mediaConstraints = Object.create({});
  5681 +
  5682 + if (typeof constraints.audio === 'object' && constraints.audio.source === AudioSourceInfo.MIC) {
  5683 + mediaConstraints.audio = Object.create({});
  5684 +
  5685 + if (isEdge()) {
  5686 + mediaConstraints.audio.deviceId = constraints.audio.deviceId;
  5687 + } else {
  5688 + mediaConstraints.audio.deviceId = {
  5689 + exact: constraints.audio.deviceId
  5690 + };
  5691 + }
  5692 + } else {
  5693 + if (constraints.audio.source === AudioSourceInfo.SCREENCAST) {
  5694 + mediaConstraints.audio = true;
  5695 + } else {
  5696 + mediaConstraints.audio = constraints.audio;
  5697 + }
  5698 + }
  5699 +
  5700 + if (typeof constraints.video === 'object') {
  5701 + mediaConstraints.video = Object.create({});
  5702 +
  5703 + if (typeof constraints.video.frameRate === 'number') {
  5704 + mediaConstraints.video.frameRate = constraints.video.frameRate;
  5705 + }
  5706 +
  5707 + if (constraints.video.resolution && constraints.video.resolution.width && constraints.video.resolution.height) {
  5708 + if (constraints.video.source === VideoSourceInfo.SCREENCAST) {
  5709 + mediaConstraints.video.width = constraints.video.resolution.width;
  5710 + mediaConstraints.video.height = constraints.video.resolution.height;
  5711 + } else {
  5712 + mediaConstraints.video.width = Object.create({});
  5713 + mediaConstraints.video.width.exact = constraints.video.resolution.width;
  5714 + mediaConstraints.video.height = Object.create({});
  5715 + mediaConstraints.video.height.exact = constraints.video.resolution.height;
  5716 + }
  5717 + }
  5718 +
  5719 + if (typeof constraints.video.deviceId === 'string') {
  5720 + mediaConstraints.video.deviceId = {
  5721 + exact: constraints.video.deviceId
  5722 + };
  5723 + }
  5724 +
  5725 + if (isFirefox() && constraints.video.source === VideoSourceInfo.SCREENCAST) {
  5726 + mediaConstraints.video.mediaSource = 'screen';
  5727 + }
  5728 + } else {
  5729 + mediaConstraints.video = constraints.video;
  5730 + }
  5731 +
  5732 + if (isVideoConstrainsForScreenCast(constraints)) {
  5733 + return navigator.mediaDevices.getDisplayMedia(mediaConstraints);
  5734 + } else {
  5735 + return navigator.mediaDevices.getUserMedia(mediaConstraints);
  5736 + }
  5737 + }
  5738 +
  5739 + }
  5740 +
  5741 + // Copyright (C) <2018> Intel Corporation
  5742 +
  5743 + var media = /*#__PURE__*/Object.freeze({
  5744 + __proto__: null,
  5745 + AudioTrackConstraints: AudioTrackConstraints,
  5746 + VideoTrackConstraints: VideoTrackConstraints,
  5747 + StreamConstraints: StreamConstraints,
  5748 + MediaStreamFactory: MediaStreamFactory,
  5749 + AudioSourceInfo: AudioSourceInfo,
  5750 + VideoSourceInfo: VideoSourceInfo,
  5751 + TrackKind: TrackKind,
  5752 + Resolution: Resolution
  5753 + });
  5754 +
  5755 + let logger;
  5756 + let errorLogger;
  5757 + function setLogger() {
  5758 + /*eslint-disable */
  5759 + logger = console.log;
  5760 + errorLogger = console.error;
  5761 + /*eslint-enable */
  5762 + }
  5763 + function log(message, ...optionalParams) {
  5764 + if (logger) {
  5765 + logger(message, ...optionalParams);
  5766 + }
  5767 + }
  5768 + function error(message, ...optionalParams) {
  5769 + if (errorLogger) {
  5770 + errorLogger(message, ...optionalParams);
  5771 + }
  5772 + }
  5773 +
  5774 + class Event$1 {
  5775 + constructor(type) {
  5776 + this.listener = {};
  5777 + this.type = type | '';
  5778 + }
  5779 +
  5780 + on(event, fn) {
  5781 + if (!this.listener[event]) {
  5782 + this.listener[event] = [];
  5783 + }
  5784 +
  5785 + this.listener[event].push(fn);
  5786 + return true;
  5787 + }
  5788 +
  5789 + off(event, fn) {
  5790 + if (this.listener[event]) {
  5791 + var index = this.listener[event].indexOf(fn);
  5792 +
  5793 + if (index > -1) {
  5794 + this.listener[event].splice(index, 1);
  5795 + }
  5796 +
  5797 + return true;
  5798 + }
  5799 +
  5800 + return false;
  5801 + }
  5802 +
  5803 + offAll() {
  5804 + this.listener = {};
  5805 + }
  5806 +
  5807 + dispatch(event, data) {
  5808 + if (this.listener[event]) {
  5809 + this.listener[event].map(each => {
  5810 + each.apply(null, [data]);
  5811 + });
  5812 + return true;
  5813 + }
  5814 +
  5815 + return false;
  5816 + }
  5817 +
  5818 + }
  5819 +
  5820 + var bind = function bind(fn, thisArg) {
  5821 + return function wrap() {
  5822 + var args = new Array(arguments.length);
  5823 + for (var i = 0; i < args.length; i++) {
  5824 + args[i] = arguments[i];
  5825 + }
  5826 + return fn.apply(thisArg, args);
  5827 + };
  5828 + };
  5829 +
  5830 + /*global toString:true*/
  5831 +
  5832 + // utils is a library of generic helper functions non-specific to axios
  5833 +
  5834 + var toString = Object.prototype.toString;
  5835 +
  5836 + /**
  5837 + * Determine if a value is an Array
  5838 + *
  5839 + * @param {Object} val The value to test
  5840 + * @returns {boolean} True if value is an Array, otherwise false
  5841 + */
  5842 + function isArray(val) {
  5843 + return toString.call(val) === '[object Array]';
  5844 + }
  5845 +
  5846 + /**
  5847 + * Determine if a value is undefined
  5848 + *
  5849 + * @param {Object} val The value to test
  5850 + * @returns {boolean} True if the value is undefined, otherwise false
  5851 + */
  5852 + function isUndefined(val) {
  5853 + return typeof val === 'undefined';
  5854 + }
  5855 +
  5856 + /**
  5857 + * Determine if a value is a Buffer
  5858 + *
  5859 + * @param {Object} val The value to test
  5860 + * @returns {boolean} True if value is a Buffer, otherwise false
  5861 + */
  5862 + function isBuffer(val) {
  5863 + return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor)
  5864 + && typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val);
  5865 + }
  5866 +
  5867 + /**
  5868 + * Determine if a value is an ArrayBuffer
  5869 + *
  5870 + * @param {Object} val The value to test
  5871 + * @returns {boolean} True if value is an ArrayBuffer, otherwise false
  5872 + */
  5873 + function isArrayBuffer(val) {
  5874 + return toString.call(val) === '[object ArrayBuffer]';
  5875 + }
  5876 +
  5877 + /**
  5878 + * Determine if a value is a FormData
  5879 + *
  5880 + * @param {Object} val The value to test
  5881 + * @returns {boolean} True if value is an FormData, otherwise false
  5882 + */
  5883 + function isFormData(val) {
  5884 + return (typeof FormData !== 'undefined') && (val instanceof FormData);
  5885 + }
  5886 +
  5887 + /**
  5888 + * Determine if a value is a view on an ArrayBuffer
  5889 + *
  5890 + * @param {Object} val The value to test
  5891 + * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false
  5892 + */
  5893 + function isArrayBufferView(val) {
  5894 + var result;
  5895 + if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {
  5896 + result = ArrayBuffer.isView(val);
  5897 + } else {
  5898 + result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer);
  5899 + }
  5900 + return result;
  5901 + }
  5902 +
  5903 + /**
  5904 + * Determine if a value is a String
  5905 + *
  5906 + * @param {Object} val The value to test
  5907 + * @returns {boolean} True if value is a String, otherwise false
  5908 + */
  5909 + function isString(val) {
  5910 + return typeof val === 'string';
  5911 + }
  5912 +
  5913 + /**
  5914 + * Determine if a value is a Number
  5915 + *
  5916 + * @param {Object} val The value to test
  5917 + * @returns {boolean} True if value is a Number, otherwise false
  5918 + */
  5919 + function isNumber(val) {
  5920 + return typeof val === 'number';
  5921 + }
  5922 +
  5923 + /**
  5924 + * Determine if a value is an Object
  5925 + *
  5926 + * @param {Object} val The value to test
  5927 + * @returns {boolean} True if value is an Object, otherwise false
  5928 + */
  5929 + function isObject(val) {
  5930 + return val !== null && typeof val === 'object';
  5931 + }
  5932 +
  5933 + /**
  5934 + * Determine if a value is a plain Object
  5935 + *
  5936 + * @param {Object} val The value to test
  5937 + * @return {boolean} True if value is a plain Object, otherwise false
  5938 + */
  5939 + function isPlainObject(val) {
  5940 + if (toString.call(val) !== '[object Object]') {
  5941 + return false;
  5942 + }
  5943 +
  5944 + var prototype = Object.getPrototypeOf(val);
  5945 + return prototype === null || prototype === Object.prototype;
  5946 + }
  5947 +
  5948 + /**
  5949 + * Determine if a value is a Date
  5950 + *
  5951 + * @param {Object} val The value to test
  5952 + * @returns {boolean} True if value is a Date, otherwise false
  5953 + */
  5954 + function isDate(val) {
  5955 + return toString.call(val) === '[object Date]';
  5956 + }
  5957 +
  5958 + /**
  5959 + * Determine if a value is a File
  5960 + *
  5961 + * @param {Object} val The value to test
  5962 + * @returns {boolean} True if value is a File, otherwise false
  5963 + */
  5964 + function isFile(val) {
  5965 + return toString.call(val) === '[object File]';
  5966 + }
  5967 +
  5968 + /**
  5969 + * Determine if a value is a Blob
  5970 + *
  5971 + * @param {Object} val The value to test
  5972 + * @returns {boolean} True if value is a Blob, otherwise false
  5973 + */
  5974 + function isBlob(val) {
  5975 + return toString.call(val) === '[object Blob]';
  5976 + }
  5977 +
  5978 + /**
  5979 + * Determine if a value is a Function
  5980 + *
  5981 + * @param {Object} val The value to test
  5982 + * @returns {boolean} True if value is a Function, otherwise false
  5983 + */
  5984 + function isFunction(val) {
  5985 + return toString.call(val) === '[object Function]';
  5986 + }
  5987 +
  5988 + /**
  5989 + * Determine if a value is a Stream
  5990 + *
  5991 + * @param {Object} val The value to test
  5992 + * @returns {boolean} True if value is a Stream, otherwise false
  5993 + */
  5994 + function isStream(val) {
  5995 + return isObject(val) && isFunction(val.pipe);
  5996 + }
  5997 +
  5998 + /**
  5999 + * Determine if a value is a URLSearchParams object
  6000 + *
  6001 + * @param {Object} val The value to test
  6002 + * @returns {boolean} True if value is a URLSearchParams object, otherwise false
  6003 + */
  6004 + function isURLSearchParams(val) {
  6005 + return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams;
  6006 + }
  6007 +
  6008 + /**
  6009 + * Trim excess whitespace off the beginning and end of a string
  6010 + *
  6011 + * @param {String} str The String to trim
  6012 + * @returns {String} The String freed of excess whitespace
  6013 + */
  6014 + function trim(str) {
  6015 + return str.replace(/^\s*/, '').replace(/\s*$/, '');
  6016 + }
  6017 +
  6018 + /**
  6019 + * Determine if we're running in a standard browser environment
  6020 + *
  6021 + * This allows axios to run in a web worker, and react-native.
  6022 + * Both environments support XMLHttpRequest, but not fully standard globals.
  6023 + *
  6024 + * web workers:
  6025 + * typeof window -> undefined
  6026 + * typeof document -> undefined
  6027 + *
  6028 + * react-native:
  6029 + * navigator.product -> 'ReactNative'
  6030 + * nativescript
  6031 + * navigator.product -> 'NativeScript' or 'NS'
  6032 + */
  6033 + function isStandardBrowserEnv() {
  6034 + if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' ||
  6035 + navigator.product === 'NativeScript' ||
  6036 + navigator.product === 'NS')) {
  6037 + return false;
  6038 + }
  6039 + return (
  6040 + typeof window !== 'undefined' &&
  6041 + typeof document !== 'undefined'
  6042 + );
  6043 + }
  6044 +
  6045 + /**
  6046 + * Iterate over an Array or an Object invoking a function for each item.
  6047 + *
  6048 + * If `obj` is an Array callback will be called passing
  6049 + * the value, index, and complete array for each item.
  6050 + *
  6051 + * If 'obj' is an Object callback will be called passing
  6052 + * the value, key, and complete object for each property.
  6053 + *
  6054 + * @param {Object|Array} obj The object to iterate
  6055 + * @param {Function} fn The callback to invoke for each item
  6056 + */
  6057 + function forEach(obj, fn) {
  6058 + // Don't bother if no value provided
  6059 + if (obj === null || typeof obj === 'undefined') {
  6060 + return;
  6061 + }
  6062 +
  6063 + // Force an array if not already something iterable
  6064 + if (typeof obj !== 'object') {
  6065 + /*eslint no-param-reassign:0*/
  6066 + obj = [obj];
  6067 + }
  6068 +
  6069 + if (isArray(obj)) {
  6070 + // Iterate over array values
  6071 + for (var i = 0, l = obj.length; i < l; i++) {
  6072 + fn.call(null, obj[i], i, obj);
  6073 + }
  6074 + } else {
  6075 + // Iterate over object keys
  6076 + for (var key in obj) {
  6077 + if (Object.prototype.hasOwnProperty.call(obj, key)) {
  6078 + fn.call(null, obj[key], key, obj);
  6079 + }
  6080 + }
  6081 + }
  6082 + }
  6083 +
  6084 + /**
  6085 + * Accepts varargs expecting each argument to be an object, then
  6086 + * immutably merges the properties of each object and returns result.
  6087 + *
  6088 + * When multiple objects contain the same key the later object in
  6089 + * the arguments list will take precedence.
  6090 + *
  6091 + * Example:
  6092 + *
  6093 + * ```js
  6094 + * var result = merge({foo: 123}, {foo: 456});
  6095 + * console.log(result.foo); // outputs 456
  6096 + * ```
  6097 + *
  6098 + * @param {Object} obj1 Object to merge
  6099 + * @returns {Object} Result of all merge properties
  6100 + */
  6101 + function merge(/* obj1, obj2, obj3, ... */) {
  6102 + var result = {};
  6103 + function assignValue(val, key) {
  6104 + if (isPlainObject(result[key]) && isPlainObject(val)) {
  6105 + result[key] = merge(result[key], val);
  6106 + } else if (isPlainObject(val)) {
  6107 + result[key] = merge({}, val);
  6108 + } else if (isArray(val)) {
  6109 + result[key] = val.slice();
  6110 + } else {
  6111 + result[key] = val;
  6112 + }
  6113 + }
  6114 +
  6115 + for (var i = 0, l = arguments.length; i < l; i++) {
  6116 + forEach(arguments[i], assignValue);
  6117 + }
  6118 + return result;
  6119 + }
  6120 +
  6121 + /**
  6122 + * Extends object a by mutably adding to it the properties of object b.
  6123 + *
  6124 + * @param {Object} a The object to be extended
  6125 + * @param {Object} b The object to copy properties from
  6126 + * @param {Object} thisArg The object to bind function to
  6127 + * @return {Object} The resulting value of object a
  6128 + */
  6129 + function extend(a, b, thisArg) {
  6130 + forEach(b, function assignValue(val, key) {
  6131 + if (thisArg && typeof val === 'function') {
  6132 + a[key] = bind(val, thisArg);
  6133 + } else {
  6134 + a[key] = val;
  6135 + }
  6136 + });
  6137 + return a;
  6138 + }
  6139 +
  6140 + /**
  6141 + * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
  6142 + *
  6143 + * @param {string} content with BOM
  6144 + * @return {string} content value without BOM
  6145 + */
  6146 + function stripBOM(content) {
  6147 + if (content.charCodeAt(0) === 0xFEFF) {
  6148 + content = content.slice(1);
  6149 + }
  6150 + return content;
  6151 + }
  6152 +
  6153 + var utils = {
  6154 + isArray: isArray,
  6155 + isArrayBuffer: isArrayBuffer,
  6156 + isBuffer: isBuffer,
  6157 + isFormData: isFormData,
  6158 + isArrayBufferView: isArrayBufferView,
  6159 + isString: isString,
  6160 + isNumber: isNumber,
  6161 + isObject: isObject,
  6162 + isPlainObject: isPlainObject,
  6163 + isUndefined: isUndefined,
  6164 + isDate: isDate,
  6165 + isFile: isFile,
  6166 + isBlob: isBlob,
  6167 + isFunction: isFunction,
  6168 + isStream: isStream,
  6169 + isURLSearchParams: isURLSearchParams,
  6170 + isStandardBrowserEnv: isStandardBrowserEnv,
  6171 + forEach: forEach,
  6172 + merge: merge,
  6173 + extend: extend,
  6174 + trim: trim,
  6175 + stripBOM: stripBOM
  6176 + };
  6177 +
  6178 + function encode(val) {
  6179 + return encodeURIComponent(val).
  6180 + replace(/%3A/gi, ':').
  6181 + replace(/%24/g, '$').
  6182 + replace(/%2C/gi, ',').
  6183 + replace(/%20/g, '+').
  6184 + replace(/%5B/gi, '[').
  6185 + replace(/%5D/gi, ']');
  6186 + }
  6187 +
  6188 + /**
  6189 + * Build a URL by appending params to the end
  6190 + *
  6191 + * @param {string} url The base of the url (e.g., http://www.google.com)
  6192 + * @param {object} [params] The params to be appended
  6193 + * @returns {string} The formatted url
  6194 + */
  6195 + var buildURL = function buildURL(url, params, paramsSerializer) {
  6196 + /*eslint no-param-reassign:0*/
  6197 + if (!params) {
  6198 + return url;
  6199 + }
  6200 +
  6201 + var serializedParams;
  6202 + if (paramsSerializer) {
  6203 + serializedParams = paramsSerializer(params);
  6204 + } else if (utils.isURLSearchParams(params)) {
  6205 + serializedParams = params.toString();
  6206 + } else {
  6207 + var parts = [];
  6208 +
  6209 + utils.forEach(params, function serialize(val, key) {
  6210 + if (val === null || typeof val === 'undefined') {
  6211 + return;
  6212 + }
  6213 +
  6214 + if (utils.isArray(val)) {
  6215 + key = key + '[]';
  6216 + } else {
  6217 + val = [val];
  6218 + }
  6219 +
  6220 + utils.forEach(val, function parseValue(v) {
  6221 + if (utils.isDate(v)) {
  6222 + v = v.toISOString();
  6223 + } else if (utils.isObject(v)) {
  6224 + v = JSON.stringify(v);
  6225 + }
  6226 + parts.push(encode(key) + '=' + encode(v));
  6227 + });
  6228 + });
  6229 +
  6230 + serializedParams = parts.join('&');
  6231 + }
  6232 +
  6233 + if (serializedParams) {
  6234 + var hashmarkIndex = url.indexOf('#');
  6235 + if (hashmarkIndex !== -1) {
  6236 + url = url.slice(0, hashmarkIndex);
  6237 + }
  6238 +
  6239 + url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;
  6240 + }
  6241 +
  6242 + return url;
  6243 + };
  6244 +
  6245 + function InterceptorManager() {
  6246 + this.handlers = [];
  6247 + }
  6248 +
  6249 + /**
  6250 + * Add a new interceptor to the stack
  6251 + *
  6252 + * @param {Function} fulfilled The function to handle `then` for a `Promise`
  6253 + * @param {Function} rejected The function to handle `reject` for a `Promise`
  6254 + *
  6255 + * @return {Number} An ID used to remove interceptor later
  6256 + */
  6257 + InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  6258 + this.handlers.push({
  6259 + fulfilled: fulfilled,
  6260 + rejected: rejected
  6261 + });
  6262 + return this.handlers.length - 1;
  6263 + };
  6264 +
  6265 + /**
  6266 + * Remove an interceptor from the stack
  6267 + *
  6268 + * @param {Number} id The ID that was returned by `use`
  6269 + */
  6270 + InterceptorManager.prototype.eject = function eject(id) {
  6271 + if (this.handlers[id]) {
  6272 + this.handlers[id] = null;
  6273 + }
  6274 + };
  6275 +
  6276 + /**
  6277 + * Iterate over all the registered interceptors
  6278 + *
  6279 + * This method is particularly useful for skipping over any
  6280 + * interceptors that may have become `null` calling `eject`.
  6281 + *
  6282 + * @param {Function} fn The function to call for each interceptor
  6283 + */
  6284 + InterceptorManager.prototype.forEach = function forEach(fn) {
  6285 + utils.forEach(this.handlers, function forEachHandler(h) {
  6286 + if (h !== null) {
  6287 + fn(h);
  6288 + }
  6289 + });
  6290 + };
  6291 +
  6292 + var InterceptorManager_1 = InterceptorManager;
  6293 +
  6294 + /**
  6295 + * Transform the data for a request or a response
  6296 + *
  6297 + * @param {Object|String} data The data to be transformed
  6298 + * @param {Array} headers The headers for the request or response
  6299 + * @param {Array|Function} fns A single function or Array of functions
  6300 + * @returns {*} The resulting transformed data
  6301 + */
  6302 + var transformData = function transformData(data, headers, fns) {
  6303 + /*eslint no-param-reassign:0*/
  6304 + utils.forEach(fns, function transform(fn) {
  6305 + data = fn(data, headers);
  6306 + });
  6307 +
  6308 + return data;
  6309 + };
  6310 +
  6311 + var isCancel = function isCancel(value) {
  6312 + return !!(value && value.__CANCEL__);
  6313 + };
  6314 +
  6315 + var normalizeHeaderName = function normalizeHeaderName(headers, normalizedName) {
  6316 + utils.forEach(headers, function processHeader(value, name) {
  6317 + if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {
  6318 + headers[normalizedName] = value;
  6319 + delete headers[name];
  6320 + }
  6321 + });
  6322 + };
  6323 +
  6324 + /**
  6325 + * Update an Error with the specified config, error code, and response.
  6326 + *
  6327 + * @param {Error} error The error to update.
  6328 + * @param {Object} config The config.
  6329 + * @param {string} [code] The error code (for example, 'ECONNABORTED').
  6330 + * @param {Object} [request] The request.
  6331 + * @param {Object} [response] The response.
  6332 + * @returns {Error} The error.
  6333 + */
  6334 + var enhanceError = function enhanceError(error, config, code, request, response) {
  6335 + error.config = config;
  6336 + if (code) {
  6337 + error.code = code;
  6338 + }
  6339 +
  6340 + error.request = request;
  6341 + error.response = response;
  6342 + error.isAxiosError = true;
  6343 +
  6344 + error.toJSON = function toJSON() {
  6345 + return {
  6346 + // Standard
  6347 + message: this.message,
  6348 + name: this.name,
  6349 + // Microsoft
  6350 + description: this.description,
  6351 + number: this.number,
  6352 + // Mozilla
  6353 + fileName: this.fileName,
  6354 + lineNumber: this.lineNumber,
  6355 + columnNumber: this.columnNumber,
  6356 + stack: this.stack,
  6357 + // Axios
  6358 + config: this.config,
  6359 + code: this.code
  6360 + };
  6361 + };
  6362 + return error;
  6363 + };
  6364 +
  6365 + /**
  6366 + * Create an Error with the specified message, config, error code, request and response.
  6367 + *
  6368 + * @param {string} message The error message.
  6369 + * @param {Object} config The config.
  6370 + * @param {string} [code] The error code (for example, 'ECONNABORTED').
  6371 + * @param {Object} [request] The request.
  6372 + * @param {Object} [response] The response.
  6373 + * @returns {Error} The created error.
  6374 + */
  6375 + var createError = function createError(message, config, code, request, response) {
  6376 + var error = new Error(message);
  6377 + return enhanceError(error, config, code, request, response);
  6378 + };
  6379 +
  6380 + /**
  6381 + * Resolve or reject a Promise based on response status.
  6382 + *
  6383 + * @param {Function} resolve A function that resolves the promise.
  6384 + * @param {Function} reject A function that rejects the promise.
  6385 + * @param {object} response The response.
  6386 + */
  6387 + var settle = function settle(resolve, reject, response) {
  6388 + var validateStatus = response.config.validateStatus;
  6389 + if (!response.status || !validateStatus || validateStatus(response.status)) {
  6390 + resolve(response);
  6391 + } else {
  6392 + reject(createError(
  6393 + 'Request failed with status code ' + response.status,
  6394 + response.config,
  6395 + null,
  6396 + response.request,
  6397 + response
  6398 + ));
  6399 + }
  6400 + };
  6401 +
  6402 + var cookies = (
  6403 + utils.isStandardBrowserEnv() ?
  6404 +
  6405 + // Standard browser envs support document.cookie
  6406 + (function standardBrowserEnv() {
  6407 + return {
  6408 + write: function write(name, value, expires, path, domain, secure) {
  6409 + var cookie = [];
  6410 + cookie.push(name + '=' + encodeURIComponent(value));
  6411 +
  6412 + if (utils.isNumber(expires)) {
  6413 + cookie.push('expires=' + new Date(expires).toGMTString());
  6414 + }
  6415 +
  6416 + if (utils.isString(path)) {
  6417 + cookie.push('path=' + path);
  6418 + }
  6419 +
  6420 + if (utils.isString(domain)) {
  6421 + cookie.push('domain=' + domain);
  6422 + }
  6423 +
  6424 + if (secure === true) {
  6425 + cookie.push('secure');
  6426 + }
  6427 +
  6428 + document.cookie = cookie.join('; ');
  6429 + },
  6430 +
  6431 + read: function read(name) {
  6432 + var match = document.cookie.match(new RegExp('(^|;\\s*)(' + name + ')=([^;]*)'));
  6433 + return (match ? decodeURIComponent(match[3]) : null);
  6434 + },
  6435 +
  6436 + remove: function remove(name) {
  6437 + this.write(name, '', Date.now() - 86400000);
  6438 + }
  6439 + };
  6440 + })() :
  6441 +
  6442 + // Non standard browser env (web workers, react-native) lack needed support.
  6443 + (function nonStandardBrowserEnv() {
  6444 + return {
  6445 + write: function write() {},
  6446 + read: function read() { return null; },
  6447 + remove: function remove() {}
  6448 + };
  6449 + })()
  6450 + );
  6451 +
  6452 + /**
  6453 + * Determines whether the specified URL is absolute
  6454 + *
  6455 + * @param {string} url The URL to test
  6456 + * @returns {boolean} True if the specified URL is absolute, otherwise false
  6457 + */
  6458 + var isAbsoluteURL = function isAbsoluteURL(url) {
  6459 + // A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
  6460 + // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
  6461 + // by any combination of letters, digits, plus, period, or hyphen.
  6462 + return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url);
  6463 + };
  6464 +
  6465 + /**
  6466 + * Creates a new URL by combining the specified URLs
  6467 + *
  6468 + * @param {string} baseURL The base URL
  6469 + * @param {string} relativeURL The relative URL
  6470 + * @returns {string} The combined URL
  6471 + */
  6472 + var combineURLs = function combineURLs(baseURL, relativeURL) {
  6473 + return relativeURL
  6474 + ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
  6475 + : baseURL;
  6476 + };
  6477 +
  6478 + /**
  6479 + * Creates a new URL by combining the baseURL with the requestedURL,
  6480 + * only when the requestedURL is not already an absolute URL.
  6481 + * If the requestURL is absolute, this function returns the requestedURL untouched.
  6482 + *
  6483 + * @param {string} baseURL The base URL
  6484 + * @param {string} requestedURL Absolute or relative URL to combine
  6485 + * @returns {string} The combined full path
  6486 + */
  6487 + var buildFullPath = function buildFullPath(baseURL, requestedURL) {
  6488 + if (baseURL && !isAbsoluteURL(requestedURL)) {
  6489 + return combineURLs(baseURL, requestedURL);
  6490 + }
  6491 + return requestedURL;
  6492 + };
  6493 +
  6494 + // Headers whose duplicates are ignored by node
  6495 + // c.f. https://nodejs.org/api/http.html#http_message_headers
  6496 + var ignoreDuplicateOf = [
  6497 + 'age', 'authorization', 'content-length', 'content-type', 'etag',
  6498 + 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since',
  6499 + 'last-modified', 'location', 'max-forwards', 'proxy-authorization',
  6500 + 'referer', 'retry-after', 'user-agent'
  6501 + ];
  6502 +
  6503 + /**
  6504 + * Parse headers into an object
  6505 + *
  6506 + * ```
  6507 + * Date: Wed, 27 Aug 2014 08:58:49 GMT
  6508 + * Content-Type: application/json
  6509 + * Connection: keep-alive
  6510 + * Transfer-Encoding: chunked
  6511 + * ```
  6512 + *
  6513 + * @param {String} headers Headers needing to be parsed
  6514 + * @returns {Object} Headers parsed into an object
  6515 + */
  6516 + var parseHeaders = function parseHeaders(headers) {
  6517 + var parsed = {};
  6518 + var key;
  6519 + var val;
  6520 + var i;
  6521 +
  6522 + if (!headers) { return parsed; }
  6523 +
  6524 + utils.forEach(headers.split('\n'), function parser(line) {
  6525 + i = line.indexOf(':');
  6526 + key = utils.trim(line.substr(0, i)).toLowerCase();
  6527 + val = utils.trim(line.substr(i + 1));
  6528 +
  6529 + if (key) {
  6530 + if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) {
  6531 + return;
  6532 + }
  6533 + if (key === 'set-cookie') {
  6534 + parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]);
  6535 + } else {
  6536 + parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
  6537 + }
  6538 + }
  6539 + });
  6540 +
  6541 + return parsed;
  6542 + };
  6543 +
  6544 + var isURLSameOrigin = (
  6545 + utils.isStandardBrowserEnv() ?
  6546 +
  6547 + // Standard browser envs have full support of the APIs needed to test
  6548 + // whether the request URL is of the same origin as current location.
  6549 + (function standardBrowserEnv() {
  6550 + var msie = /(msie|trident)/i.test(navigator.userAgent);
  6551 + var urlParsingNode = document.createElement('a');
  6552 + var originURL;
  6553 +
  6554 + /**
  6555 + * Parse a URL to discover it's components
  6556 + *
  6557 + * @param {String} url The URL to be parsed
  6558 + * @returns {Object}
  6559 + */
  6560 + function resolveURL(url) {
  6561 + var href = url;
  6562 +
  6563 + if (msie) {
  6564 + // IE needs attribute set twice to normalize properties
  6565 + urlParsingNode.setAttribute('href', href);
  6566 + href = urlParsingNode.href;
  6567 + }
  6568 +
  6569 + urlParsingNode.setAttribute('href', href);
  6570 +
  6571 + // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
  6572 + return {
  6573 + href: urlParsingNode.href,
  6574 + protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
  6575 + host: urlParsingNode.host,
  6576 + search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
  6577 + hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
  6578 + hostname: urlParsingNode.hostname,
  6579 + port: urlParsingNode.port,
  6580 + pathname: (urlParsingNode.pathname.charAt(0) === '/') ?
  6581 + urlParsingNode.pathname :
  6582 + '/' + urlParsingNode.pathname
  6583 + };
  6584 + }
  6585 +
  6586 + originURL = resolveURL(window.location.href);
  6587 +
  6588 + /**
  6589 + * Determine if a URL shares the same origin as the current location
  6590 + *
  6591 + * @param {String} requestURL The URL to test
  6592 + * @returns {boolean} True if URL shares the same origin, otherwise false
  6593 + */
  6594 + return function isURLSameOrigin(requestURL) {
  6595 + var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL;
  6596 + return (parsed.protocol === originURL.protocol &&
  6597 + parsed.host === originURL.host);
  6598 + };
  6599 + })() :
  6600 +
  6601 + // Non standard browser envs (web workers, react-native) lack needed support.
  6602 + (function nonStandardBrowserEnv() {
  6603 + return function isURLSameOrigin() {
  6604 + return true;
  6605 + };
  6606 + })()
  6607 + );
  6608 +
  6609 + var xhr = function xhrAdapter(config) {
  6610 + return new Promise(function dispatchXhrRequest(resolve, reject) {
  6611 + var requestData = config.data;
  6612 + var requestHeaders = config.headers;
  6613 +
  6614 + if (utils.isFormData(requestData)) {
  6615 + delete requestHeaders['Content-Type']; // Let the browser set it
  6616 + }
  6617 +
  6618 + var request = new XMLHttpRequest();
  6619 +
  6620 + // HTTP basic authentication
  6621 + if (config.auth) {
  6622 + var username = config.auth.username || '';
  6623 + var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : '';
  6624 + requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
  6625 + }
  6626 +
  6627 + var fullPath = buildFullPath(config.baseURL, config.url);
  6628 + request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
  6629 +
  6630 + // Set the request timeout in MS
  6631 + request.timeout = config.timeout;
  6632 +
  6633 + // Listen for ready state
  6634 + request.onreadystatechange = function handleLoad() {
  6635 + if (!request || request.readyState !== 4) {
  6636 + return;
  6637 + }
  6638 +
  6639 + // The request errored out and we didn't get a response, this will be
  6640 + // handled by onerror instead
  6641 + // With one exception: request that using file: protocol, most browsers
  6642 + // will return status as 0 even though it's a successful request
  6643 + if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
  6644 + return;
  6645 + }
  6646 +
  6647 + // Prepare the response
  6648 + var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
  6649 + var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;
  6650 + var response = {
  6651 + data: responseData,
  6652 + status: request.status,
  6653 + statusText: request.statusText,
  6654 + headers: responseHeaders,
  6655 + config: config,
  6656 + request: request
  6657 + };
  6658 +
  6659 + settle(resolve, reject, response);
  6660 +
  6661 + // Clean up request
  6662 + request = null;
  6663 + };
  6664 +
  6665 + // Handle browser request cancellation (as opposed to a manual cancellation)
  6666 + request.onabort = function handleAbort() {
  6667 + if (!request) {
  6668 + return;
  6669 + }
  6670 +
  6671 + reject(createError('Request aborted', config, 'ECONNABORTED', request));
  6672 +
  6673 + // Clean up request
  6674 + request = null;
  6675 + };
  6676 +
  6677 + // Handle low level network errors
  6678 + request.onerror = function handleError() {
  6679 + // Real errors are hidden from us by the browser
  6680 + // onerror should only fire if it's a network error
  6681 + reject(createError('Network Error', config, null, request));
  6682 +
  6683 + // Clean up request
  6684 + request = null;
  6685 + };
  6686 +
  6687 + // Handle timeout
  6688 + request.ontimeout = function handleTimeout() {
  6689 + var timeoutErrorMessage = 'timeout of ' + config.timeout + 'ms exceeded';
  6690 + if (config.timeoutErrorMessage) {
  6691 + timeoutErrorMessage = config.timeoutErrorMessage;
  6692 + }
  6693 + reject(createError(timeoutErrorMessage, config, 'ECONNABORTED',
  6694 + request));
  6695 +
  6696 + // Clean up request
  6697 + request = null;
  6698 + };
  6699 +
  6700 + // Add xsrf header
  6701 + // This is only done if running in a standard browser environment.
  6702 + // Specifically not if we're in a web worker, or react-native.
  6703 + if (utils.isStandardBrowserEnv()) {
  6704 + // Add xsrf header
  6705 + var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?
  6706 + cookies.read(config.xsrfCookieName) :
  6707 + undefined;
  6708 +
  6709 + if (xsrfValue) {
  6710 + requestHeaders[config.xsrfHeaderName] = xsrfValue;
  6711 + }
  6712 + }
  6713 +
  6714 + // Add headers to the request
  6715 + if ('setRequestHeader' in request) {
  6716 + utils.forEach(requestHeaders, function setRequestHeader(val, key) {
  6717 + if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
  6718 + // Remove Content-Type if data is undefined
  6719 + delete requestHeaders[key];
  6720 + } else {
  6721 + // Otherwise add header to the request
  6722 + request.setRequestHeader(key, val);
  6723 + }
  6724 + });
  6725 + }
  6726 +
  6727 + // Add withCredentials to request if needed
  6728 + if (!utils.isUndefined(config.withCredentials)) {
  6729 + request.withCredentials = !!config.withCredentials;
  6730 + }
  6731 +
  6732 + // Add responseType to request if needed
  6733 + if (config.responseType) {
  6734 + try {
  6735 + request.responseType = config.responseType;
  6736 + } catch (e) {
  6737 + // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.
  6738 + // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.
  6739 + if (config.responseType !== 'json') {
  6740 + throw e;
  6741 + }
  6742 + }
  6743 + }
  6744 +
  6745 + // Handle progress if needed
  6746 + if (typeof config.onDownloadProgress === 'function') {
  6747 + request.addEventListener('progress', config.onDownloadProgress);
  6748 + }
  6749 +
  6750 + // Not all browsers support upload events
  6751 + if (typeof config.onUploadProgress === 'function' && request.upload) {
  6752 + request.upload.addEventListener('progress', config.onUploadProgress);
  6753 + }
  6754 +
  6755 + if (config.cancelToken) {
  6756 + // Handle cancellation
  6757 + config.cancelToken.promise.then(function onCanceled(cancel) {
  6758 + if (!request) {
  6759 + return;
  6760 + }
  6761 +
  6762 + request.abort();
  6763 + reject(cancel);
  6764 + // Clean up request
  6765 + request = null;
  6766 + });
  6767 + }
  6768 +
  6769 + if (!requestData) {
  6770 + requestData = null;
  6771 + }
  6772 +
  6773 + // Send the request
  6774 + request.send(requestData);
  6775 + });
  6776 + };
  6777 +
  6778 + var DEFAULT_CONTENT_TYPE = {
  6779 + 'Content-Type': 'application/x-www-form-urlencoded'
  6780 + };
  6781 +
  6782 + function setContentTypeIfUnset(headers, value) {
  6783 + if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {
  6784 + headers['Content-Type'] = value;
  6785 + }
  6786 + }
  6787 +
  6788 + function getDefaultAdapter() {
  6789 + var adapter;
  6790 + if (typeof XMLHttpRequest !== 'undefined') {
  6791 + // For browsers use XHR adapter
  6792 + adapter = xhr;
  6793 + } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
  6794 + // For node use HTTP adapter
  6795 + adapter = xhr;
  6796 + }
  6797 + return adapter;
  6798 + }
  6799 +
  6800 + var defaults = {
  6801 + adapter: getDefaultAdapter(),
  6802 +
  6803 + transformRequest: [function transformRequest(data, headers) {
  6804 + normalizeHeaderName(headers, 'Accept');
  6805 + normalizeHeaderName(headers, 'Content-Type');
  6806 + if (utils.isFormData(data) ||
  6807 + utils.isArrayBuffer(data) ||
  6808 + utils.isBuffer(data) ||
  6809 + utils.isStream(data) ||
  6810 + utils.isFile(data) ||
  6811 + utils.isBlob(data)
  6812 + ) {
  6813 + return data;
  6814 + }
  6815 + if (utils.isArrayBufferView(data)) {
  6816 + return data.buffer;
  6817 + }
  6818 + if (utils.isURLSearchParams(data)) {
  6819 + setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
  6820 + return data.toString();
  6821 + }
  6822 + if (utils.isObject(data)) {
  6823 + setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
  6824 + return JSON.stringify(data);
  6825 + }
  6826 + return data;
  6827 + }],
  6828 +
  6829 + transformResponse: [function transformResponse(data) {
  6830 + /*eslint no-param-reassign:0*/
  6831 + if (typeof data === 'string') {
  6832 + try {
  6833 + data = JSON.parse(data);
  6834 + } catch (e) { /* Ignore */ }
  6835 + }
  6836 + return data;
  6837 + }],
  6838 +
  6839 + /**
  6840 + * A timeout in milliseconds to abort a request. If set to 0 (default) a
  6841 + * timeout is not created.
  6842 + */
  6843 + timeout: 0,
  6844 +
  6845 + xsrfCookieName: 'XSRF-TOKEN',
  6846 + xsrfHeaderName: 'X-XSRF-TOKEN',
  6847 +
  6848 + maxContentLength: -1,
  6849 + maxBodyLength: -1,
  6850 +
  6851 + validateStatus: function validateStatus(status) {
  6852 + return status >= 200 && status < 300;
  6853 + }
  6854 + };
  6855 +
  6856 + defaults.headers = {
  6857 + common: {
  6858 + 'Accept': 'application/json, text/plain, */*'
  6859 + }
  6860 + };
  6861 +
  6862 + utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {
  6863 + defaults.headers[method] = {};
  6864 + });
  6865 +
  6866 + utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  6867 + defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
  6868 + });
  6869 +
  6870 + var defaults_1 = defaults;
  6871 +
  6872 + /**
  6873 + * Throws a `Cancel` if cancellation has been requested.
  6874 + */
  6875 + function throwIfCancellationRequested(config) {
  6876 + if (config.cancelToken) {
  6877 + config.cancelToken.throwIfRequested();
  6878 + }
  6879 + }
  6880 +
  6881 + /**
  6882 + * Dispatch a request to the server using the configured adapter.
  6883 + *
  6884 + * @param {object} config The config that is to be used for the request
  6885 + * @returns {Promise} The Promise to be fulfilled
  6886 + */
  6887 + var dispatchRequest = function dispatchRequest(config) {
  6888 + throwIfCancellationRequested(config);
  6889 +
  6890 + // Ensure headers exist
  6891 + config.headers = config.headers || {};
  6892 +
  6893 + // Transform request data
  6894 + config.data = transformData(
  6895 + config.data,
  6896 + config.headers,
  6897 + config.transformRequest
  6898 + );
  6899 +
  6900 + // Flatten headers
  6901 + config.headers = utils.merge(
  6902 + config.headers.common || {},
  6903 + config.headers[config.method] || {},
  6904 + config.headers
  6905 + );
  6906 +
  6907 + utils.forEach(
  6908 + ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
  6909 + function cleanHeaderConfig(method) {
  6910 + delete config.headers[method];
  6911 + }
  6912 + );
  6913 +
  6914 + var adapter = config.adapter || defaults_1.adapter;
  6915 +
  6916 + return adapter(config).then(function onAdapterResolution(response) {
  6917 + throwIfCancellationRequested(config);
  6918 +
  6919 + // Transform response data
  6920 + response.data = transformData(
  6921 + response.data,
  6922 + response.headers,
  6923 + config.transformResponse
  6924 + );
  6925 +
  6926 + return response;
  6927 + }, function onAdapterRejection(reason) {
  6928 + if (!isCancel(reason)) {
  6929 + throwIfCancellationRequested(config);
  6930 +
  6931 + // Transform response data
  6932 + if (reason && reason.response) {
  6933 + reason.response.data = transformData(
  6934 + reason.response.data,
  6935 + reason.response.headers,
  6936 + config.transformResponse
  6937 + );
  6938 + }
  6939 + }
  6940 +
  6941 + return Promise.reject(reason);
  6942 + });
  6943 + };
  6944 +
  6945 + /**
  6946 + * Config-specific merge-function which creates a new config-object
  6947 + * by merging two configuration objects together.
  6948 + *
  6949 + * @param {Object} config1
  6950 + * @param {Object} config2
  6951 + * @returns {Object} New object resulting from merging config2 to config1
  6952 + */
  6953 + var mergeConfig = function mergeConfig(config1, config2) {
  6954 + // eslint-disable-next-line no-param-reassign
  6955 + config2 = config2 || {};
  6956 + var config = {};
  6957 +
  6958 + var valueFromConfig2Keys = ['url', 'method', 'data'];
  6959 + var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy', 'params'];
  6960 + var defaultToConfig2Keys = [
  6961 + 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer',
  6962 + 'timeout', 'timeoutMessage', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',
  6963 + 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'decompress',
  6964 + 'maxContentLength', 'maxBodyLength', 'maxRedirects', 'transport', 'httpAgent',
  6965 + 'httpsAgent', 'cancelToken', 'socketPath', 'responseEncoding'
  6966 + ];
  6967 + var directMergeKeys = ['validateStatus'];
  6968 +
  6969 + function getMergedValue(target, source) {
  6970 + if (utils.isPlainObject(target) && utils.isPlainObject(source)) {
  6971 + return utils.merge(target, source);
  6972 + } else if (utils.isPlainObject(source)) {
  6973 + return utils.merge({}, source);
  6974 + } else if (utils.isArray(source)) {
  6975 + return source.slice();
  6976 + }
  6977 + return source;
  6978 + }
  6979 +
  6980 + function mergeDeepProperties(prop) {
  6981 + if (!utils.isUndefined(config2[prop])) {
  6982 + config[prop] = getMergedValue(config1[prop], config2[prop]);
  6983 + } else if (!utils.isUndefined(config1[prop])) {
  6984 + config[prop] = getMergedValue(undefined, config1[prop]);
  6985 + }
  6986 + }
  6987 +
  6988 + utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) {
  6989 + if (!utils.isUndefined(config2[prop])) {
  6990 + config[prop] = getMergedValue(undefined, config2[prop]);
  6991 + }
  6992 + });
  6993 +
  6994 + utils.forEach(mergeDeepPropertiesKeys, mergeDeepProperties);
  6995 +
  6996 + utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) {
  6997 + if (!utils.isUndefined(config2[prop])) {
  6998 + config[prop] = getMergedValue(undefined, config2[prop]);
  6999 + } else if (!utils.isUndefined(config1[prop])) {
  7000 + config[prop] = getMergedValue(undefined, config1[prop]);
  7001 + }
  7002 + });
  7003 +
  7004 + utils.forEach(directMergeKeys, function merge(prop) {
  7005 + if (prop in config2) {
  7006 + config[prop] = getMergedValue(config1[prop], config2[prop]);
  7007 + } else if (prop in config1) {
  7008 + config[prop] = getMergedValue(undefined, config1[prop]);
  7009 + }
  7010 + });
  7011 +
  7012 + var axiosKeys = valueFromConfig2Keys
  7013 + .concat(mergeDeepPropertiesKeys)
  7014 + .concat(defaultToConfig2Keys)
  7015 + .concat(directMergeKeys);
  7016 +
  7017 + var otherKeys = Object
  7018 + .keys(config1)
  7019 + .concat(Object.keys(config2))
  7020 + .filter(function filterAxiosKeys(key) {
  7021 + return axiosKeys.indexOf(key) === -1;
  7022 + });
  7023 +
  7024 + utils.forEach(otherKeys, mergeDeepProperties);
  7025 +
  7026 + return config;
  7027 + };
  7028 +
  7029 + /**
  7030 + * Create a new instance of Axios
  7031 + *
  7032 + * @param {Object} instanceConfig The default config for the instance
  7033 + */
  7034 + function Axios(instanceConfig) {
  7035 + this.defaults = instanceConfig;
  7036 + this.interceptors = {
  7037 + request: new InterceptorManager_1(),
  7038 + response: new InterceptorManager_1()
  7039 + };
  7040 + }
  7041 +
  7042 + /**
  7043 + * Dispatch a request
  7044 + *
  7045 + * @param {Object} config The config specific for this request (merged with this.defaults)
  7046 + */
  7047 + Axios.prototype.request = function request(config) {
  7048 + /*eslint no-param-reassign:0*/
  7049 + // Allow for axios('example/url'[, config]) a la fetch API
  7050 + if (typeof config === 'string') {
  7051 + config = arguments[1] || {};
  7052 + config.url = arguments[0];
  7053 + } else {
  7054 + config = config || {};
  7055 + }
  7056 +
  7057 + config = mergeConfig(this.defaults, config);
  7058 +
  7059 + // Set config.method
  7060 + if (config.method) {
  7061 + config.method = config.method.toLowerCase();
  7062 + } else if (this.defaults.method) {
  7063 + config.method = this.defaults.method.toLowerCase();
  7064 + } else {
  7065 + config.method = 'get';
  7066 + }
  7067 +
  7068 + // Hook up interceptors middleware
  7069 + var chain = [dispatchRequest, undefined];
  7070 + var promise = Promise.resolve(config);
  7071 +
  7072 + this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
  7073 + chain.unshift(interceptor.fulfilled, interceptor.rejected);
  7074 + });
  7075 +
  7076 + this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
  7077 + chain.push(interceptor.fulfilled, interceptor.rejected);
  7078 + });
  7079 +
  7080 + while (chain.length) {
  7081 + promise = promise.then(chain.shift(), chain.shift());
  7082 + }
  7083 +
  7084 + return promise;
  7085 + };
  7086 +
  7087 + Axios.prototype.getUri = function getUri(config) {
  7088 + config = mergeConfig(this.defaults, config);
  7089 + return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, '');
  7090 + };
  7091 +
  7092 + // Provide aliases for supported request methods
  7093 + utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  7094 + /*eslint func-names:0*/
  7095 + Axios.prototype[method] = function(url, config) {
  7096 + return this.request(mergeConfig(config || {}, {
  7097 + method: method,
  7098 + url: url,
  7099 + data: (config || {}).data
  7100 + }));
  7101 + };
  7102 + });
  7103 +
  7104 + utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  7105 + /*eslint func-names:0*/
  7106 + Axios.prototype[method] = function(url, data, config) {
  7107 + return this.request(mergeConfig(config || {}, {
  7108 + method: method,
  7109 + url: url,
  7110 + data: data
  7111 + }));
  7112 + };
  7113 + });
  7114 +
  7115 + var Axios_1 = Axios;
  7116 +
  7117 + /**
  7118 + * A `Cancel` is an object that is thrown when an operation is canceled.
  7119 + *
  7120 + * @class
  7121 + * @param {string=} message The message.
  7122 + */
  7123 + function Cancel(message) {
  7124 + this.message = message;
  7125 + }
  7126 +
  7127 + Cancel.prototype.toString = function toString() {
  7128 + return 'Cancel' + (this.message ? ': ' + this.message : '');
  7129 + };
  7130 +
  7131 + Cancel.prototype.__CANCEL__ = true;
  7132 +
  7133 + var Cancel_1 = Cancel;
  7134 +
  7135 + /**
  7136 + * A `CancelToken` is an object that can be used to request cancellation of an operation.
  7137 + *
  7138 + * @class
  7139 + * @param {Function} executor The executor function.
  7140 + */
  7141 + function CancelToken(executor) {
  7142 + if (typeof executor !== 'function') {
  7143 + throw new TypeError('executor must be a function.');
  7144 + }
  7145 +
  7146 + var resolvePromise;
  7147 + this.promise = new Promise(function promiseExecutor(resolve) {
  7148 + resolvePromise = resolve;
  7149 + });
  7150 +
  7151 + var token = this;
  7152 + executor(function cancel(message) {
  7153 + if (token.reason) {
  7154 + // Cancellation has already been requested
  7155 + return;
  7156 + }
  7157 +
  7158 + token.reason = new Cancel_1(message);
  7159 + resolvePromise(token.reason);
  7160 + });
  7161 + }
  7162 +
  7163 + /**
  7164 + * Throws a `Cancel` if cancellation has been requested.
  7165 + */
  7166 + CancelToken.prototype.throwIfRequested = function throwIfRequested() {
  7167 + if (this.reason) {
  7168 + throw this.reason;
  7169 + }
  7170 + };
  7171 +
  7172 + /**
  7173 + * Returns an object that contains a new `CancelToken` and a function that, when called,
  7174 + * cancels the `CancelToken`.
  7175 + */
  7176 + CancelToken.source = function source() {
  7177 + var cancel;
  7178 + var token = new CancelToken(function executor(c) {
  7179 + cancel = c;
  7180 + });
  7181 + return {
  7182 + token: token,
  7183 + cancel: cancel
  7184 + };
  7185 + };
  7186 +
  7187 + var CancelToken_1 = CancelToken;
  7188 +
  7189 + /**
  7190 + * Syntactic sugar for invoking a function and expanding an array for arguments.
  7191 + *
  7192 + * Common use case would be to use `Function.prototype.apply`.
  7193 + *
  7194 + * ```js
  7195 + * function f(x, y, z) {}
  7196 + * var args = [1, 2, 3];
  7197 + * f.apply(null, args);
  7198 + * ```
  7199 + *
  7200 + * With `spread` this example can be re-written.
  7201 + *
  7202 + * ```js
  7203 + * spread(function(x, y, z) {})([1, 2, 3]);
  7204 + * ```
  7205 + *
  7206 + * @param {Function} callback
  7207 + * @returns {Function}
  7208 + */
  7209 + var spread = function spread(callback) {
  7210 + return function wrap(arr) {
  7211 + return callback.apply(null, arr);
  7212 + };
  7213 + };
  7214 +
  7215 + /**
  7216 + * Determines whether the payload is an error thrown by Axios
  7217 + *
  7218 + * @param {*} payload The value to test
  7219 + * @returns {boolean} True if the payload is an error thrown by Axios, otherwise false
  7220 + */
  7221 + var isAxiosError = function isAxiosError(payload) {
  7222 + return (typeof payload === 'object') && (payload.isAxiosError === true);
  7223 + };
  7224 +
  7225 + /**
  7226 + * Create an instance of Axios
  7227 + *
  7228 + * @param {Object} defaultConfig The default config for the instance
  7229 + * @return {Axios} A new instance of Axios
  7230 + */
  7231 + function createInstance(defaultConfig) {
  7232 + var context = new Axios_1(defaultConfig);
  7233 + var instance = bind(Axios_1.prototype.request, context);
  7234 +
  7235 + // Copy axios.prototype to instance
  7236 + utils.extend(instance, Axios_1.prototype, context);
  7237 +
  7238 + // Copy context to instance
  7239 + utils.extend(instance, context);
  7240 +
  7241 + return instance;
  7242 + }
  7243 +
  7244 + // Create the default instance to be exported
  7245 + var axios$1 = createInstance(defaults_1);
  7246 +
  7247 + // Expose Axios class to allow class inheritance
  7248 + axios$1.Axios = Axios_1;
  7249 +
  7250 + // Factory for creating new instances
  7251 + axios$1.create = function create(instanceConfig) {
  7252 + return createInstance(mergeConfig(axios$1.defaults, instanceConfig));
  7253 + };
  7254 +
  7255 + // Expose Cancel & CancelToken
  7256 + axios$1.Cancel = Cancel_1;
  7257 + axios$1.CancelToken = CancelToken_1;
  7258 + axios$1.isCancel = isCancel;
  7259 +
  7260 + // Expose all/spread
  7261 + axios$1.all = function all(promises) {
  7262 + return Promise.all(promises);
  7263 + };
  7264 + axios$1.spread = spread;
  7265 +
  7266 + // Expose isAxiosError
  7267 + axios$1.isAxiosError = isAxiosError;
  7268 +
  7269 + var axios_1 = axios$1;
  7270 +
  7271 + // Allow use of default import syntax in TypeScript
  7272 + var _default = axios$1;
  7273 + axios_1.default = _default;
  7274 +
  7275 + var axios = axios_1;
  7276 +
  7277 + class RTCEndpoint extends Event$1 {
  7278 + constructor(options) {
  7279 + super('RTCPusherPlayer');
  7280 + this.TAG = '[RTCPusherPlayer]';
  7281 + let defaults = {
  7282 + element: '',
  7283 + // html video element
  7284 + debug: false,
  7285 + // if output debug log
  7286 + zlmsdpUrl: '',
  7287 + simulecast: false,
  7288 + useCamera: true,
  7289 + audioEnable: true,
  7290 + videoEnable: true,
  7291 + recvOnly: false
  7292 + };
  7293 + this.options = Object.assign({}, defaults, options);
  7294 +
  7295 + if (this.options.debug) {
  7296 + setLogger();
  7297 + }
  7298 +
  7299 + this.e = {
  7300 + onicecandidate: this._onIceCandidate.bind(this),
  7301 + ontrack: this._onTrack.bind(this),
  7302 + onicecandidateerror: this._onIceCandidateError.bind(this)
  7303 + };
  7304 + this._remoteStream = null;
  7305 + this._localStream = null;
  7306 + this.pc = new RTCPeerConnection(null);
  7307 + this.pc.onicecandidate = this.e.onicecandidate;
  7308 + this.pc.onicecandidateerror = this.e.onicecandidateerror;
  7309 + this.pc.ontrack = this.e.ontrack;
  7310 + if (!this.options.recvOnly && (this.options.audioEnable || this.options.videoEnable)) this.start();else this.receive();
  7311 + }
  7312 +
  7313 + receive() {
  7314 +
  7315 + const AudioTransceiverInit = {
  7316 + direction: 'recvonly',
  7317 + sendEncodings: []
  7318 + };
  7319 + const VideoTransceiverInit = {
  7320 + direction: 'recvonly',
  7321 + sendEncodings: []
  7322 + };
  7323 + this.pc.addTransceiver('audio', AudioTransceiverInit);
  7324 + this.pc.addTransceiver('video', VideoTransceiverInit);
  7325 + this.pc.createOffer().then(desc => {
  7326 + log(this.TAG, 'offer:', desc.sdp);
  7327 + this.pc.setLocalDescription(desc).then(() => {
  7328 + axios({
  7329 + method: 'post',
  7330 + url: this.options.zlmsdpUrl,
  7331 + responseType: 'json',
  7332 + data: desc.sdp,
  7333 + headers: {
  7334 + 'Content-Type': 'text/plain;charset=utf-8'
  7335 + }
  7336 + }).then(response => {
  7337 + let ret = response.data; //JSON.parse(response.data);
  7338 +
  7339 + if (ret.code != 0) {
  7340 + // mean failed for offer/anwser exchange
  7341 + this.dispatch(Events$1.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, ret);
  7342 + return;
  7343 + }
  7344 +
  7345 + let anwser = {};
  7346 + anwser.sdp = ret.sdp;
  7347 + anwser.type = 'answer';
  7348 + log(this.TAG, 'answer:', ret.sdp);
  7349 + this.pc.setRemoteDescription(anwser).then(() => {
  7350 + log(this.TAG, 'set remote sucess');
  7351 + }).catch(e => {
  7352 + error(this.TAG, e);
  7353 + });
  7354 + });
  7355 + });
  7356 + }).catch(e => {
  7357 + error(this.TAG, e);
  7358 + });
  7359 + }
  7360 +
  7361 + start() {
  7362 + let videoConstraints = false;
  7363 + let audioConstraints = false;
  7364 +
  7365 + if (this.options.useCamera) {
  7366 + if (this.options.videoEnable) videoConstraints = new VideoTrackConstraints(VideoSourceInfo.CAMERA);
  7367 + if (this.options.audioEnable) audioConstraints = new AudioTrackConstraints(AudioSourceInfo.MIC);
  7368 + } else {
  7369 + if (this.options.videoEnable) {
  7370 + videoConstraints = new VideoTrackConstraints(VideoSourceInfo.SCREENCAST);
  7371 + if (this.options.audioEnable) audioConstraints = new AudioTrackConstraints(AudioSourceInfo.SCREENCAST);
  7372 + } else {
  7373 + if (this.options.audioEnable) audioConstraints = new AudioTrackConstraints(AudioSourceInfo.MIC);else {
  7374 + // error shared display media not only audio
  7375 + error(this.TAG, 'error paramter');
  7376 + }
  7377 + }
  7378 + }
  7379 +
  7380 + MediaStreamFactory.createMediaStream(new StreamConstraints(audioConstraints, videoConstraints)).then(stream => {
  7381 + this._localStream = stream;
  7382 + this.dispatch(Events$1.WEBRTC_ON_LOCAL_STREAM, stream);
  7383 + const AudioTransceiverInit = {
  7384 + direction: 'sendrecv',
  7385 + sendEncodings: []
  7386 + };
  7387 + const VideoTransceiverInit = {
  7388 + direction: 'sendrecv',
  7389 + sendEncodings: []
  7390 + };
  7391 +
  7392 + if (this.options.simulecast && stream.getVideoTracks().length > 0) {
  7393 + VideoTransceiverInit.sendEncodings = [{
  7394 + rid: 'q',
  7395 + active: true,
  7396 + scaleResolutionDownBy: 4.0
  7397 + }, {
  7398 + rid: 'h',
  7399 + active: true,
  7400 + scaleResolutionDownBy: 2.0
  7401 + }, {
  7402 + rid: 'f',
  7403 + active: true
  7404 + }];
  7405 + }
  7406 +
  7407 + if (stream.getAudioTracks().length > 0) {
  7408 + this.pc.addTransceiver(stream.getAudioTracks()[0], AudioTransceiverInit);
  7409 + } else {
  7410 + AudioTransceiverInit.direction = 'recvonly';
  7411 + this.pc.addTransceiver('audio', AudioTransceiverInit);
  7412 + }
  7413 +
  7414 + if (stream.getVideoTracks().length > 0) {
  7415 + this.pc.addTransceiver(stream.getVideoTracks()[0], VideoTransceiverInit);
  7416 + } else {
  7417 + VideoTransceiverInit.direction = 'recvonly';
  7418 + this.pc.addTransceiver('video', VideoTransceiverInit);
  7419 + }
  7420 + /*
  7421 + stream.getTracks().forEach((track,idx)=>{
  7422 + debug.log(this.TAG,track);
  7423 + this.pc.addTrack(track);
  7424 + });
  7425 + */
  7426 +
  7427 +
  7428 + this.pc.createOffer().then(desc => {
  7429 + log(this.TAG, 'offer:', desc.sdp);
  7430 + this.pc.setLocalDescription(desc).then(() => {
  7431 + axios({
  7432 + method: 'post',
  7433 + url: this.options.zlmsdpUrl,
  7434 + responseType: 'json',
  7435 + data: desc.sdp,
  7436 + headers: {
  7437 + 'Content-Type': 'text/plain;charset=utf-8'
  7438 + }
  7439 + }).then(response => {
  7440 + let ret = response.data; //JSON.parse(response.data);
  7441 +
  7442 + if (ret.code != 0) {
  7443 + // mean failed for offer/anwser exchange
  7444 + this.dispatch(Events$1.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, ret);
  7445 + return;
  7446 + }
  7447 +
  7448 + let anwser = {};
  7449 + anwser.sdp = ret.sdp;
  7450 + anwser.type = 'answer';
  7451 + log(this.TAG, 'answer:', ret.sdp);
  7452 + this.pc.setRemoteDescription(anwser).then(() => {
  7453 + log(this.TAG, 'set remote sucess');
  7454 + }).catch(e => {
  7455 + error(this.TAG, e);
  7456 + });
  7457 + });
  7458 + });
  7459 + }).catch(e => {
  7460 + error(this.TAG, e);
  7461 + });
  7462 + }).catch(e => {
  7463 + error(this.TAG, e);
  7464 + }); //const offerOptions = {};
  7465 +
  7466 + /*
  7467 + if (typeof this.pc.addTransceiver === 'function') {
  7468 + // |direction| seems not working on Safari.
  7469 + this.pc.addTransceiver('audio', { direction: 'recvonly' });
  7470 + this.pc.addTransceiver('video', { direction: 'recvonly' });
  7471 + } else {
  7472 + offerOptions.offerToReceiveAudio = true;
  7473 + offerOptions.offerToReceiveVideo = true;
  7474 + }
  7475 + */
  7476 + }
  7477 +
  7478 + _onIceCandidate(event) {
  7479 + if (event.candidate) {
  7480 + log('Remote ICE candidate: \n ' + event.candidate.candidate); // Send the candidate to the remote peer
  7481 + }
  7482 + }
  7483 +
  7484 + _onTrack(event) {
  7485 + if (this.options.element && event.streams && event.streams.length > 0) {
  7486 + this.options.element.srcObject = event.streams[0];
  7487 + this._remoteStream = event.streams[0];
  7488 + this.dispatch(Events$1.WEBRTC_ON_REMOTE_STREAMS, event);
  7489 + } else {
  7490 + error('element pararm is failed');
  7491 + }
  7492 + }
  7493 +
  7494 + _onIceCandidateError(event) {
  7495 + this.dispatch(Events$1.WEBRTC_ICE_CANDIDATE_ERROR, event);
  7496 + }
  7497 +
  7498 + close() {
  7499 + if (this.pc) {
  7500 + this.pc.close();
  7501 + this.pc = null;
  7502 + }
  7503 +
  7504 + if (this.options) {
  7505 + this.options = null;
  7506 + }
  7507 +
  7508 + if (this._localStream) {
  7509 + this._localStream.getTracks().forEach((track, idx) => {
  7510 + track.stop();
  7511 + });
  7512 + }
  7513 +
  7514 + if (this._remoteStream) {
  7515 + this._remoteStream.getTracks().forEach((track, idx) => {
  7516 + track.stop();
  7517 + });
  7518 + }
  7519 + }
  7520 +
  7521 + get remoteStream() {
  7522 + return this._remoteStream;
  7523 + }
  7524 +
  7525 + get localStream() {
  7526 + return this._localStream;
  7527 + }
  7528 +
  7529 + }
  7530 +
  7531 + console.log('build date:', BUILD_DATE);
  7532 + console.log('version:', VERSION);
  7533 + const Events = Events$1;
  7534 + const Media = media;
  7535 + const Endpoint = RTCEndpoint;
  7536 +
  7537 + exports.Endpoint = Endpoint;
  7538 + exports.Events = Events;
  7539 + exports.Media = Media;
  7540 +
  7541 + Object.defineProperty(exports, '__esModule', { value: true });
  7542 +
  7543 + return exports;
  7544 +
  7545 +}({}));
  7546 +//# sourceMappingURL=ZLMRTCClient.js.map